diff options
Diffstat (limited to 'lib')
37 files changed, 2052 insertions, 171 deletions
diff --git a/lib/abstract_int.h b/lib/abstract_int.h index 3693d00074..ac582180d7 100644 --- a/lib/abstract_int.h +++ b/lib/abstract_int.h @@ -82,8 +82,25 @@ struct gnutls_pubkey_st { int _gnutls_privkey_get_public_mpis(gnutls_privkey_t key, gnutls_pk_params_st *); +int _gnutls_privkey_get_sign_params(gnutls_privkey_t key, + gnutls_x509_spki_st * params); +int _gnutls_privkey_find_sign_params(gnutls_privkey_t key, + gnutls_pk_algorithm_t pk, + gnutls_digest_algorithm_t dig, + unsigned flags, + gnutls_x509_spki_st *params); + void _gnutls_privkey_cleanup(gnutls_privkey_t key); +int privkey_sign_data(gnutls_privkey_t signer, + const gnutls_datum_t * data, + gnutls_datum_t * signature, + gnutls_x509_spki_st *params); +int privkey_sign_hash(gnutls_privkey_t signer, + const gnutls_datum_t * hash_data, + gnutls_datum_t * signature, + gnutls_x509_spki_st * params); + unsigned pubkey_to_bits(gnutls_pk_algorithm_t pk, gnutls_pk_params_st * params); int _gnutls_pubkey_compatible_with_sig(gnutls_session_t, gnutls_pubkey_t pubkey, @@ -97,13 +114,15 @@ pubkey_verify_hashed_data(gnutls_pk_algorithm_t pk, const mac_entry_st * algo, const gnutls_datum_t * hash, const gnutls_datum_t * signature, - gnutls_pk_params_st * issuer_params); + gnutls_pk_params_st * params, + gnutls_x509_spki_st * sign_params); int pubkey_verify_data(gnutls_pk_algorithm_t pk, const mac_entry_st * algo, const gnutls_datum_t * data, const gnutls_datum_t * signature, - gnutls_pk_params_st * issuer_params); + gnutls_pk_params_st * params, + gnutls_x509_spki_st * sign_params); diff --git a/lib/algorithms/publickey.c b/lib/algorithms/publickey.c index 5ef780986f..aba3896004 100644 --- a/lib/algorithms/publickey.c +++ b/lib/algorithms/publickey.c @@ -89,6 +89,7 @@ static const gnutls_pk_entry pk_algorithms[] = { * we want to return OID from is first */ {"UNKNOWN", NULL, GNUTLS_PK_UNKNOWN}, {"RSA", PK_PKIX1_RSA_OID, GNUTLS_PK_RSA}, + {"RSA-PSS", PK_PKIX1_RSA_PSS_OID, GNUTLS_PK_RSA_PSS}, {"RSA (X.509)", PK_X509_RSA_OID, GNUTLS_PK_RSA}, /* some certificates use this OID for RSA */ {"RSA-MD5", SIG_RSA_MD5_OID, GNUTLS_PK_RSA}, /* some other broken certificates set RSA with MD5 as an indicator of RSA */ {"RSA-SHA1", SIG_RSA_SHA1_OID, GNUTLS_PK_RSA}, /* some other broken certificates set RSA with SHA1 as an indicator of RSA */ diff --git a/lib/algorithms/sign.c b/lib/algorithms/sign.c index 6107c44435..614b17155e 100644 --- a/lib/algorithms/sign.c +++ b/lib/algorithms/sign.c @@ -123,6 +123,17 @@ static const gnutls_sign_entry sign_algorithms[] = { GNUTLS_PK_DSA, GNUTLS_DIG_SHA3_384, TLS_SIGN_AID_UNKNOWN}, {"DSA-SHA3-512", SIG_DSA_SHA3_512_OID, GNUTLS_SIGN_DSA_SHA3_512, GNUTLS_PK_DSA, GNUTLS_DIG_SHA3_512, TLS_SIGN_AID_UNKNOWN}, + + {"RSA-PSS-SHA256", PK_PKIX1_RSA_PSS_OID, GNUTLS_SIGN_RSA_PSS_SHA256, + GNUTLS_PK_RSA_PSS, + GNUTLS_DIG_SHA256, {8, 4}}, + {"RSA-PSS-SHA384", PK_PKIX1_RSA_PSS_OID, GNUTLS_SIGN_RSA_PSS_SHA384, + GNUTLS_PK_RSA_PSS, + GNUTLS_DIG_SHA384, {8, 5}}, + {"RSA-PSS-SHA512", PK_PKIX1_RSA_PSS_OID, GNUTLS_SIGN_RSA_PSS_SHA512, + GNUTLS_PK_RSA_PSS, + GNUTLS_DIG_SHA512, {8, 6}}, + {0, 0, 0, 0, 0, TLS_SIGN_AID_UNKNOWN} }; diff --git a/lib/crypto-backend.h b/lib/crypto-backend.h index 1f0b85fb11..d60a5745c2 100644 --- a/lib/crypto-backend.h +++ b/lib/crypto-backend.h @@ -166,6 +166,20 @@ typedef struct gnutls_crypto_bigint { gnutls_bigint_format_t format); } gnutls_crypto_bigint_st; +/* additional information about the public key + */ +typedef struct gnutls_x509_spki_st { + gnutls_pk_algorithm_t pk; + gnutls_digest_algorithm_t dig; + + /* the size of salt used by RSA-PSS */ + unsigned int salt_size; + + /* if non-zero, the legacy value for PKCS#7 signatures will be + * written for RSA signatures. */ + unsigned int legacy; +} gnutls_x509_spki_st; + #define GNUTLS_MAX_PK_PARAMS 16 typedef struct { @@ -178,6 +192,7 @@ typedef struct { unsigned int seed_size; uint8_t seed[MAX_PVP_SEED_SIZE]; gnutls_digest_algorithm_t palgo; + gnutls_x509_spki_st sign; gnutls_pk_algorithm_t algo; } gnutls_pk_params_st; @@ -314,10 +329,12 @@ typedef struct gnutls_crypto_pk { int (*sign) (gnutls_pk_algorithm_t, gnutls_datum_t * signature, const gnutls_datum_t * data, - const gnutls_pk_params_st * priv); + const gnutls_pk_params_st *priv, + const gnutls_x509_spki_st *sign); int (*verify) (gnutls_pk_algorithm_t, const gnutls_datum_t * data, const gnutls_datum_t * sig, - const gnutls_pk_params_st * pub); + const gnutls_pk_params_st *pub, + const gnutls_x509_spki_st *sign); /* sanity checks the public key parameters */ int (*verify_priv_params) (gnutls_pk_algorithm_t, const gnutls_pk_params_st * priv); diff --git a/lib/gnutls.asn b/lib/gnutls.asn index cce748d52b..e006b6d5b7 100644 --- a/lib/gnutls.asn +++ b/lib/gnutls.asn @@ -118,4 +118,12 @@ KRB5PrincipalName ::= SEQUENCE { principalName [1] PrincipalName } +-- from RFC4055 +RSAPSSParameters ::= SEQUENCE { + hashAlgorithm [0] AlgorithmIdentifier OPTIONAL, -- sha1Identifier + maskGenAlgorithm [1] AlgorithmIdentifier OPTIONAL, -- mgf1SHA1Identifier + saltLength [2] INTEGER DEFAULT 20, + trailerField [3] INTEGER DEFAULT 1 +} + END diff --git a/lib/gnutls_asn1_tab.c b/lib/gnutls_asn1_tab.c index 4d69728163..587e54ed36 100644 --- a/lib/gnutls_asn1_tab.c +++ b/lib/gnutls_asn1_tab.c @@ -79,10 +79,21 @@ const asn1_static_node gnutls_asn1_tab[] = { { "name-string", 536879115, NULL }, { NULL, 1073743880, "1"}, { NULL, 27, NULL }, - { "KRB5PrincipalName", 536870917, NULL }, + { "KRB5PrincipalName", 1610612741, NULL }, { "realm", 1610620955, NULL }, { NULL, 2056, "0"}, { "principalName", 536879106, "PrincipalName"}, { NULL, 2056, "1"}, + { "RSAPSSParameters", 536870917, NULL }, + { "hashAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "0"}, + { "maskGenAlgorithm", 1610637314, "AlgorithmIdentifier"}, + { NULL, 2056, "1"}, + { "saltLength", 1610653699, NULL }, + { NULL, 1073741833, "20"}, + { NULL, 2056, "2"}, + { "trailerField", 536911875, NULL }, + { NULL, 1073741833, "1"}, + { NULL, 2056, "3"}, { NULL, 0, NULL } }; diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 5d013c83c2..28adae80a8 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -293,6 +293,8 @@ typedef enum content_type_t { #define GNUTLS_PK_ANY (gnutls_pk_algorithm_t)-1 #define GNUTLS_PK_NONE (gnutls_pk_algorithm_t)-2 +#define GNUTLS_PK_IS_RSA(pk) ((pk) == GNUTLS_PK_RSA || (pk) == GNUTLS_PK_RSA_PSS) + /* Message buffers (mbuffers) structures */ /* this is actually the maximum number of distinct handshake diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index e4c3efd42c..dec5db4e3f 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -52,6 +52,7 @@ typedef enum gnutls_pubkey_flags { } gnutls_pubkey_flags_t; #define GNUTLS_PUBKEY_VERIFY_FLAG_TLS1_RSA GNUTLS_VERIFY_USE_TLS1_RSA +#define GNUTLS_PUBKEY_VERIFY_FLAG_RSA_PSS GNUTLS_VERIFY_USE_RSA_PSS typedef int (*gnutls_privkey_sign_func) (gnutls_privkey_t key, void *userdata, @@ -267,6 +268,8 @@ int gnutls_privkey_status(gnutls_privkey_t key); /** * gnutls_privkey_flags: * @GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA: Make an RSA signature on the hashed data as in the TLS protocol. + * @GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS: Make an RSA signature on the hashed data with the PSS padding. + * @GNUTLS_PRIVKEY_SIGN_FLAG_REPRODUCIBLE: Make an RSA-PSS signature on the hashed data with reproducible parameters (zero salt). * @GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE: When importing a private key, automatically * release it when the structure it was imported is released. * @GNUTLS_PRIVKEY_IMPORT_COPY: Copy required values during import. @@ -285,7 +288,9 @@ typedef enum gnutls_privkey_flags { GNUTLS_PRIVKEY_DISABLE_CALLBACKS = 1 << 2, GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA = 1 << 4, GNUTLS_PRIVKEY_FLAG_PROVABLE = 1 << 5, - GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT = 1 << 6 + GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT = 1 << 6, + GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS = 1 << 7, + GNUTLS_PRIVKEY_SIGN_FLAG_REPRODUCIBLE = GNUTLS_PRIVKEY_FLAG_PROVABLE /* save a flag, they are not overlapping */ } gnutls_privkey_flags_t; int gnutls_privkey_import_pkcs11(gnutls_privkey_t pkey, diff --git a/lib/includes/gnutls/gnutls.h.in b/lib/includes/gnutls/gnutls.h.in index 580cf9f00c..19dc4f252b 100644 --- a/lib/includes/gnutls/gnutls.h.in +++ b/lib/includes/gnutls/gnutls.h.in @@ -701,6 +701,7 @@ typedef enum gnutls_certificate_print_formats { * gnutls_pk_algorithm_t: * @GNUTLS_PK_UNKNOWN: Unknown public-key algorithm. * @GNUTLS_PK_RSA: RSA public-key algorithm. + * @GNUTLS_PK_RSA_PSS: RSA public-key algorithm, with PSS padding. * @GNUTLS_PK_DSA: DSA public-key algorithm. * @GNUTLS_PK_DH: Diffie-Hellman algorithm. Used to generate parameters. * @GNUTLS_PK_ECDSA: Elliptic curve algorithm. These parameters are compatible with the ECDSA and ECDH algorithm. @@ -715,7 +716,8 @@ typedef enum { GNUTLS_PK_DH = 3, GNUTLS_PK_ECDSA = 4, GNUTLS_PK_ECDHX = 5, - GNUTLS_PK_MAX = GNUTLS_PK_ECDHX + GNUTLS_PK_RSA_PSS = 6, + GNUTLS_PK_MAX = GNUTLS_PK_RSA_PSS } gnutls_pk_algorithm_t; @@ -756,6 +758,9 @@ const char *gnutls_pk_algorithm_get_name(gnutls_pk_algorithm_t algorithm); * @GNUTLS_SIGN_RSA_SHA3_256: Digital signature algorithm RSA with SHA3-256. * @GNUTLS_SIGN_RSA_SHA3_384: Digital signature algorithm RSA with SHA3-384. * @GNUTLS_SIGN_RSA_SHA3_512: Digital signature algorithm RSA with SHA3-512. + * @GNUTLS_SIGN_RSA_PSS_SHA256: Digital signature algorithm RSA with SHA-256, with PSS padding. + * @GNUTLS_SIGN_RSA_PSS_SHA384: Digital signature algorithm RSA with SHA-384, with PSS padding. + * @GNUTLS_SIGN_RSA_PSS_SHA512: Digital signature algorithm RSA with SHA-512, with PSS padding. * * Enumeration of different digital signature algorithms. */ @@ -794,7 +799,11 @@ typedef enum { GNUTLS_SIGN_RSA_SHA3_256 = 29, GNUTLS_SIGN_RSA_SHA3_384 = 30, GNUTLS_SIGN_RSA_SHA3_512 = 31, - GNUTLS_SIGN_MAX = GNUTLS_SIGN_RSA_SHA3_512 + + GNUTLS_SIGN_RSA_PSS_SHA256 = 32, + GNUTLS_SIGN_RSA_PSS_SHA384 = 33, + GNUTLS_SIGN_RSA_PSS_SHA512 = 34, + GNUTLS_SIGN_MAX = GNUTLS_SIGN_RSA_PSS_SHA512 } gnutls_sign_algorithm_t; /** diff --git a/lib/includes/gnutls/x509.h b/lib/includes/gnutls/x509.h index 4646299368..cc30a5fd6c 100644 --- a/lib/includes/gnutls/x509.h +++ b/lib/includes/gnutls/x509.h @@ -413,8 +413,28 @@ time_t gnutls_x509_crt_get_expiration_time(gnutls_x509_crt_t cert); int gnutls_x509_crt_get_serial(gnutls_x509_crt_t cert, void *result, size_t * result_size); +typedef struct gnutls_x509_spki_st *gnutls_x509_spki_t; + +int gnutls_x509_spki_init(gnutls_x509_spki_t *spki); +void gnutls_x509_spki_deinit(gnutls_x509_spki_t spki); +int gnutls_x509_spki_get_pk_algorithm(gnutls_x509_spki_t spki); +void gnutls_x509_spki_set_pk_algorithm(gnutls_x509_spki_t spki, + gnutls_pk_algorithm_t pk); +int gnutls_x509_spki_get_digest_algorithm(gnutls_x509_spki_t spki); +void gnutls_x509_spki_set_digest_algorithm(gnutls_x509_spki_t spki, + gnutls_digest_algorithm_t dig); +int gnutls_x509_spki_get_salt_size(gnutls_x509_spki_t spki); +void gnutls_x509_spki_set_salt_size(gnutls_x509_spki_t spki, + unsigned int salt_size); + int gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert, unsigned int *bits); +int gnutls_x509_crt_set_pk_algorithm(gnutls_x509_crt_t crt, + gnutls_x509_spki_t spki, + unsigned int flags); +int gnutls_x509_crt_get_pk_algorithm2(gnutls_x509_crt_t cert, + gnutls_x509_spki_t spki, + unsigned int *bits); int gnutls_x509_crt_get_pk_rsa_raw(gnutls_x509_crt_t crt, gnutls_datum_t * m, gnutls_datum_t * e); int gnutls_x509_crt_get_pk_dsa_raw(gnutls_x509_crt_t crt, @@ -945,6 +965,7 @@ typedef enum gnutls_certificate_verify_flags { GNUTLS_VERIFY_USE_TLS1_RSA = 1 << 13, GNUTLS_VERIFY_IGNORE_UNKNOWN_CRIT_EXTENSIONS = 1 << 14, GNUTLS_VERIFY_ALLOW_SIGN_WITH_SHA1 = 1 << 15, + GNUTLS_VERIFY_USE_RSA_PSS = 1 << 16 /* cannot exceed 2^24 due to GNUTLS_PROFILE_TO_VFLAGS() */ } gnutls_certificate_verify_flags; @@ -1167,6 +1188,9 @@ int gnutls_x509_privkey_import_dsa_raw(gnutls_x509_privkey_t key, int gnutls_x509_privkey_get_pk_algorithm(gnutls_x509_privkey_t key); int gnutls_x509_privkey_get_pk_algorithm2(gnutls_x509_privkey_t key, unsigned int *bits); +int gnutls_x509_privkey_get_pk_algorithm3(gnutls_x509_privkey_t key, + gnutls_x509_spki_t spki, + unsigned int *bits); int gnutls_x509_privkey_get_key_id(gnutls_x509_privkey_t key, unsigned int flags, unsigned char *output_data, @@ -1391,6 +1415,12 @@ int gnutls_x509_crq_get_attribute_info(gnutls_x509_crq_t crq, size_t * sizeof_oid); int gnutls_x509_crq_get_pk_algorithm(gnutls_x509_crq_t crq, unsigned int *bits); +int gnutls_x509_crq_get_pk_algorithm2(gnutls_x509_crq_t crq, + gnutls_x509_spki_t spki, + unsigned int *bits); +int gnutls_x509_crq_set_pk_algorithm(gnutls_x509_crq_t crq, + gnutls_x509_spki_t spki, + unsigned int flags); int gnutls_x509_crq_get_signature_oid(gnutls_x509_crq_t crq, char *oid, size_t *oid_size); int gnutls_x509_crq_get_pk_oid(gnutls_x509_crq_t crq, char *oid, size_t *oid_size); diff --git a/lib/libgnutls.map b/lib/libgnutls.map index af7a151ff4..fb6fb36620 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -1142,6 +1142,19 @@ GNUTLS_3_4 gnutls_x509_crt_set_inhibit_anypolicy; gnutls_decode_rs_value; gnutls_encode_rs_value; + gnutls_x509_spki_init; + gnutls_x509_spki_deinit; + gnutls_x509_spki_get_pk_algorithm; + gnutls_x509_spki_set_pk_algorithm; + gnutls_x509_spki_get_digest_algorithm; + gnutls_x509_spki_set_digest_algorithm; + gnutls_x509_spki_get_salt_size; + gnutls_x509_spki_set_salt_size; + gnutls_x509_crt_get_pk_algorithm2; + gnutls_x509_crt_set_pk_algorithm; + gnutls_x509_crq_get_pk_algorithm2; + gnutls_x509_crq_set_pk_algorithm; + gnutls_x509_privkey_get_pk_algorithm3; local: *; }; diff --git a/lib/nettle/pk.c b/lib/nettle/pk.c index 0bbe45f126..b635c645ff 100644 --- a/lib/nettle/pk.c +++ b/lib/nettle/pk.c @@ -50,6 +50,9 @@ #include <nettle/curve25519.h> #include <gnettle.h> #include <fips.h> +#ifndef HAVE_NETTLE_RSA_PSS +#include "rsa-pss.h" +#endif static inline const struct ecc_curve *get_supported_nist_curve(int curve); @@ -484,13 +487,80 @@ _wrap_nettle_pk_decrypt(gnutls_pk_algorithm_t algo, return ret; } +static int +_rsa_pss_sign_digest_tr(gnutls_digest_algorithm_t dig, + const struct rsa_public_key *pub, + const struct rsa_private_key *priv, + void *rnd_ctx, nettle_random_func *rnd_func, + size_t salt_size, + const uint8_t *digest, + mpz_t s) +{ + int (*sign_func)(const struct rsa_public_key *, + const struct rsa_private_key *, + void *, nettle_random_func *, + size_t, const uint8_t *, + const uint8_t *, + mpz_t); + uint8_t *salt = NULL; + size_t hash_size; + int ret; + + switch (dig) { + case GNUTLS_DIG_SHA256: + sign_func = rsa_pss_sha256_sign_digest_tr; + hash_size = 32; + break; + case GNUTLS_DIG_SHA384: + sign_func = rsa_pss_sha384_sign_digest_tr; + hash_size = 48; + break; + case GNUTLS_DIG_SHA512: + sign_func = rsa_pss_sha512_sign_digest_tr; + hash_size = 64; + break; + default: + gnutls_assert(); + return GNUTLS_E_UNKNOWN_ALGORITHM; + } + + /* This is also checked in pss_encode_mgf1, but error out earlier. */ + if (hash_size + salt_size + 2 > pub->size) + return gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER); + + if (salt_size > 0) { + salt = gnutls_malloc(salt_size); + if (salt == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + ret = gnutls_rnd(GNUTLS_RND_NONCE, salt, salt_size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } + + ret = sign_func(pub, priv, rnd_ctx, rnd_func, salt_size, salt, + digest, s); + if (ret == 0) { + gnutls_assert(); + ret = GNUTLS_E_PK_SIGN_FAILED; + } else + ret = 0; + + cleanup: + gnutls_free(salt); + return ret; +} + /* in case of DSA puts into data, r,s */ static int _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, gnutls_datum_t * signature, const gnutls_datum_t * vdata, - const gnutls_pk_params_st * pk_params) + const gnutls_pk_params_st * pk_params, + const gnutls_x509_spki_st * sign_params) { int ret; unsigned int hash_len; @@ -636,6 +706,46 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, break; } + case GNUTLS_PK_RSA_PSS: + { + struct rsa_private_key priv; + struct rsa_public_key pub; + mpz_t s; + + _rsa_params_to_privkey(pk_params, &priv); + ret = _rsa_params_to_pubkey(pk_params, &pub); + if (ret < 0) + return + gnutls_assert_val(ret); + + mpz_init(s); + + ret = + _rsa_pss_sign_digest_tr(sign_params->dig, + &pub, &priv, + NULL, rnd_nonce_func, + sign_params->salt_size, + vdata->data, s); + if (ret < 0) { + gnutls_assert(); + ret = GNUTLS_E_PK_SIGN_FAILED; + goto rsa_pss_fail; + } + + ret = + _gnutls_mpi_dprint_size(s, signature, + pub.size); + + rsa_pss_fail: + mpz_clear(s); + + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + break; + } default: gnutls_assert(); ret = GNUTLS_E_INTERNAL_ERROR; @@ -651,10 +761,49 @@ _wrap_nettle_pk_sign(gnutls_pk_algorithm_t algo, } static int +_rsa_pss_verify_digest(gnutls_digest_algorithm_t dig, + const struct rsa_public_key *pub, + size_t salt_size, + const uint8_t *digest, + size_t digest_size, + const mpz_t s) +{ + int (*verify_func) (const struct rsa_public_key *, + size_t, + const uint8_t *, + const mpz_t); + size_t hash_size; + + switch (dig) { + case GNUTLS_DIG_SHA256: + verify_func = rsa_pss_sha256_verify_digest; + hash_size = 32; + break; + case GNUTLS_DIG_SHA384: + verify_func = rsa_pss_sha384_verify_digest; + hash_size = 48; + break; + case GNUTLS_DIG_SHA512: + verify_func = rsa_pss_sha512_verify_digest; + hash_size = 64; + break; + default: + gnutls_assert(); + return 0; + } + + if (digest_size != hash_size) + return 0; + + return verify_func(pub, salt_size, digest, s); +} + +static int _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, const gnutls_datum_t * vdata, const gnutls_datum_t * signature, - const gnutls_pk_params_st * pk_params) + const gnutls_pk_params_st * pk_params, + const gnutls_x509_spki_st * sign_params) { int ret; unsigned int hash_len; @@ -776,6 +925,42 @@ _wrap_nettle_pk_verify(gnutls_pk_algorithm_t algo, break; } + case GNUTLS_PK_RSA_PSS: + { + struct rsa_public_key pub; + + ret = _rsa_params_to_pubkey(pk_params, &pub); + if (ret < 0) + return + gnutls_assert_val(ret); + + if (signature->size != pub.size) + return + gnutls_assert_val + (GNUTLS_E_PK_SIG_VERIFY_FAILED); + + ret = + _gnutls_mpi_init_scan_nz(&tmp[0], signature->data, + signature->size); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _rsa_pss_verify_digest(sign_params->dig, + &pub, + sign_params->salt_size, + vdata->data, vdata->size, + TOMPZ(tmp[0])); + if (ret == 0) + ret = + gnutls_assert_val + (GNUTLS_E_PK_SIG_VERIFY_FAILED); + else + ret = 0; + + break; + } default: gnutls_assert(); ret = GNUTLS_E_INTERNAL_ERROR; @@ -934,6 +1119,7 @@ wrap_nettle_pk_generate_params(gnutls_pk_algorithm_t algo, break; } + case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_RSA: case GNUTLS_PK_EC: break; @@ -1204,6 +1390,9 @@ static int pct_test(gnutls_pk_algorithm_t algo, const gnutls_pk_params_st* param int ret; gnutls_datum_t sig = {NULL, 0}; const char const_data[20] = "onetwothreefourfive"; +const char const_data_sha256[32] = "onetwothreefourfivesixseveneight"; +const char const_data_sha384[48] = "onetwothreefourfivesixseveneightnineteneleventwe"; +const char const_data_sha512[64] = "onetwothreefourfivesixseveneightnineteneleventwelvethirteenfourt"; gnutls_datum_t ddata, tmp = {NULL,0}; char* gen_data = NULL; @@ -1216,6 +1405,24 @@ char* gen_data = NULL; ddata.data = (void*)gen_data; ddata.size = hash_len; + } else if (algo == GNUTLS_PK_RSA_PSS) { + switch (params->sign.dig) { + case GNUTLS_DIG_SHA256: + ddata.data = (void*)const_data_sha256; + ddata.size = sizeof(const_data_sha256); + break; + case GNUTLS_DIG_SHA384: + ddata.data = (void*)const_data_sha384; + ddata.size = sizeof(const_data_sha384); + break; + case GNUTLS_DIG_SHA512: + ddata.data = (void*)const_data_sha512; + ddata.size = sizeof(const_data_sha512); + break; + default: + ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); + goto cleanup; + } } else { ddata.data = (void*)const_data; ddata.size = sizeof(const_data); @@ -1256,13 +1463,14 @@ char* gen_data = NULL; */ case GNUTLS_PK_EC: /* we only do keys for ECDSA */ case GNUTLS_PK_DSA: - ret = _gnutls_pk_sign(algo, &sig, &ddata, params); + case GNUTLS_PK_RSA_PSS: + ret = _gnutls_pk_sign(algo, &sig, &ddata, params, ¶ms->sign); if (ret < 0) { ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); goto cleanup; } - ret = _gnutls_pk_verify(algo, &ddata, &sig, params); + ret = _gnutls_pk_verify(algo, &ddata, &sig, params, ¶ms->sign); if (ret < 0) { ret = gnutls_assert_val(GNUTLS_E_PK_GENERATION_ERROR); gnutls_assert(); @@ -1436,6 +1644,7 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo, break; } + case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_RSA: { struct rsa_public_key pub; @@ -1628,6 +1837,7 @@ wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo, switch (algo) { case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: { bigint_t t1 = NULL, t2 = NULL; @@ -1833,6 +2043,7 @@ wrap_nettle_pk_verify_pub_params(gnutls_pk_algorithm_t algo, switch (algo) { case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_DSA: return 0; case GNUTLS_PK_EC: diff --git a/lib/opencdk/pubkey.c b/lib/opencdk/pubkey.c index 1c73433fd6..6e3285e65e 100644 --- a/lib/opencdk/pubkey.c +++ b/lib/opencdk/pubkey.c @@ -116,7 +116,7 @@ cdk_pk_verify(cdk_pubkey_t pk, cdk_pkt_signature_t sig, const byte * md) for (i = 0; i < params.params_nr; i++) params.params[i] = pk->mpi[i]; params.flags = 0; - ret = _gnutls_pk_verify(algo, &di, &s_sig, ¶ms); + ret = _gnutls_pk_verify(algo, &di, &s_sig, ¶ms, ¶ms.sign); if (ret < 0) { gnutls_assert(); diff --git a/lib/openpgp/privkey.c b/lib/openpgp/privkey.c index a90541dae8..6e88a3c5fb 100644 --- a/lib/openpgp/privkey.c +++ b/lib/openpgp/privkey.c @@ -1353,7 +1353,7 @@ gnutls_openpgp_privkey_sign_hash(gnutls_openpgp_privkey_t key, } - result = _gnutls_pk_sign(pk_algorithm, signature, hash, ¶ms); + result = _gnutls_pk_sign(pk_algorithm, signature, hash, ¶ms, ¶ms.sign); gnutls_pk_params_clear(¶ms); gnutls_pk_params_release(¶ms); @@ -332,6 +332,8 @@ int _gnutls_pk_params_copy(gnutls_pk_params_st * dst, } dst->palgo = src->palgo; + memcpy(&dst->sign, &src->sign, sizeof(gnutls_x509_spki_st)); + return 0; fail: @@ -374,6 +376,24 @@ void gnutls_pk_params_clear(gnutls_pk_params_st * p) } } +unsigned +_gnutls_find_rsa_pss_salt_size(unsigned bits, const mac_entry_st *me, + unsigned salt_size) +{ + unsigned max_salt_size, digest_size; + + digest_size = _gnutls_hash_get_algo_len(me); + max_salt_size = (bits + 7) / 8 - digest_size - 2; + + if (salt_size < digest_size) + salt_size = digest_size; + + if (salt_size > max_salt_size) + salt_size = max_salt_size; + + return salt_size; +} + /* Writes the digest information and the digest in a DER encoded * structure. The digest info is allocated and stored into the info structure. */ @@ -596,7 +616,7 @@ _gnutls_params_get_rsa_raw(const gnutls_pk_params_st* params, return GNUTLS_E_INVALID_REQUEST; } - if (params->algo != GNUTLS_PK_RSA) { + if (!GNUTLS_PK_IS_RSA(params->algo)) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } @@ -890,6 +910,7 @@ pk_prepare_hash(gnutls_pk_algorithm_t pk, _gnutls_free_datum(&old_digest); break; + case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_DSA: case GNUTLS_PK_EC: break; @@ -28,8 +28,8 @@ extern gnutls_crypto_pk_st _gnutls_pk_ops; #define _gnutls_pk_encrypt( algo, ciphertext, plaintext, params) _gnutls_pk_ops.encrypt( algo, ciphertext, plaintext, params) #define _gnutls_pk_decrypt( algo, ciphertext, plaintext, params) _gnutls_pk_ops.decrypt( algo, ciphertext, plaintext, params) -#define _gnutls_pk_sign( algo, sig, data, params) _gnutls_pk_ops.sign( algo, sig, data, params) -#define _gnutls_pk_verify( algo, data, sig, params) _gnutls_pk_ops.verify( algo, data, sig, params) +#define _gnutls_pk_sign( algo, sig, data, params, sign_params) _gnutls_pk_ops.sign( algo, sig, data, params, sign_params) +#define _gnutls_pk_verify( algo, data, sig, params, sign_params) _gnutls_pk_ops.verify( algo, data, sig, params, sign_params) #define _gnutls_pk_verify_priv_params( algo, params) _gnutls_pk_ops.verify_priv_params( algo, params) #define _gnutls_pk_verify_pub_params( algo, params) _gnutls_pk_ops.verify_pub_params( algo, params) #define _gnutls_pk_derive( algo, out, pub, priv) _gnutls_pk_ops.derive( algo, out, pub, priv) @@ -101,4 +101,7 @@ int pk_hash_data(gnutls_pk_algorithm_t pk, const mac_entry_st * hash, gnutls_pk_params_st * params, const gnutls_datum_t * data, gnutls_datum_t * digest); +unsigned _gnutls_find_rsa_pss_salt_size(unsigned bits, const mac_entry_st *me, + unsigned salt_size); + #endif /* GNUTLS_PK_H */ diff --git a/lib/privkey.c b/lib/privkey.c index 1b4eb48208..1bfca03a8d 100644 --- a/lib/privkey.c +++ b/lib/privkey.c @@ -40,9 +40,9 @@ static int _gnutls_privkey_sign_raw_data(gnutls_privkey_t key, - unsigned flags, const gnutls_datum_t * data, - gnutls_datum_t * signature); + gnutls_datum_t * signature, + gnutls_x509_spki_st * params); /** * gnutls_privkey_get_type: @@ -158,8 +158,10 @@ privkey_to_pubkey(gnutls_pk_algorithm_t pk, pub->algo = priv->algo; pub->flags = priv->flags; + memcpy(&pub->sign, &priv->sign, sizeof(gnutls_x509_spki_st)); switch (pk) { + case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_RSA: pub->params[0] = _gnutls_mpi_copy(priv->params[0]); pub->params[1] = _gnutls_mpi_copy(priv->params[1]); @@ -296,6 +298,69 @@ _gnutls_privkey_get_public_mpis(gnutls_privkey_t key, return ret; } +/* This function retrieves default sign parameters from KEY. */ +int +_gnutls_privkey_get_sign_params(gnutls_privkey_t key, + gnutls_x509_spki_st * params) +{ + switch (key->type) { +#ifdef ENABLE_OPENPGP + case GNUTLS_PRIVKEY_OPENPGP: + break; +#endif +#ifdef ENABLE_PKCS11 + case GNUTLS_PRIVKEY_PKCS11: + break; +#endif + case GNUTLS_PRIVKEY_X509: + return _gnutls_x509_privkey_get_sign_params(key->key.x509, + params); + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(params, 0, sizeof(gnutls_x509_spki_st)); + + return 0; +} + +/* This function fills in PARAMS with the necessary parameters to sign + * with PK and DIG. PARAMS must be initialized with + * _gnutls_privkey_get_sign_params in advance. */ +int +_gnutls_privkey_find_sign_params(gnutls_privkey_t key, + gnutls_pk_algorithm_t pk, + gnutls_digest_algorithm_t dig, + unsigned flags, + gnutls_x509_spki_st *params) +{ + switch (key->type) { +#ifdef ENABLE_OPENPGP + case GNUTLS_PRIVKEY_OPENPGP: + break; +#endif +#ifdef ENABLE_PKCS11 + case GNUTLS_PRIVKEY_PKCS11: + break; +#endif + case GNUTLS_PRIVKEY_X509: + return _gnutls_x509_privkey_find_sign_params(key->key.x509, + pk, + dig, + flags, + params); + default: + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + params->pk = pk; + params->dig = dig; + + return 0; +} + /** * gnutls_privkey_init: * @key: A pointer to the type to be initialized @@ -1084,25 +1149,53 @@ gnutls_privkey_sign_data(gnutls_privkey_t signer, gnutls_datum_t * signature) { int ret; - gnutls_datum_t digest; - const mac_entry_st *me = hash_to_entry(hash); + gnutls_x509_spki_st params; if (flags & GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); - ret = pk_hash_data(signer->pk_algorithm, me, NULL, data, &digest); + ret = _gnutls_privkey_get_sign_params(signer, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_privkey_find_sign_params(signer, signer->pk_algorithm, + hash, flags, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + return privkey_sign_data(signer, data, signature, ¶ms); +} + +int +privkey_sign_data(gnutls_privkey_t signer, + const gnutls_datum_t * data, + gnutls_datum_t * signature, + gnutls_x509_spki_st * params) +{ + int ret; + gnutls_datum_t digest; + const mac_entry_st *me = hash_to_entry(params->dig); + + if (me == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + ret = pk_hash_data(params->pk, me, NULL, data, &digest); if (ret < 0) { gnutls_assert(); return ret; } - ret = pk_prepare_hash(signer->pk_algorithm, me, &digest); + ret = pk_prepare_hash(params->pk, me, &digest); if (ret < 0) { gnutls_assert(); goto cleanup; } - ret = _gnutls_privkey_sign_raw_data(signer, flags, &digest, signature); + ret = _gnutls_privkey_sign_raw_data(signer, &digest, signature, params); _gnutls_free_datum(&digest); if (ret < 0) { @@ -1149,11 +1242,37 @@ gnutls_privkey_sign_hash(gnutls_privkey_t signer, gnutls_datum_t * signature) { int ret; - gnutls_datum_t digest; + gnutls_x509_spki_st params; + + ret = _gnutls_privkey_get_sign_params(signer, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } + + ret = _gnutls_privkey_find_sign_params(signer, signer->pk_algorithm, + hash_algo, 0, ¶ms); + if (ret < 0) { + gnutls_assert(); + return ret; + } if (flags & GNUTLS_PRIVKEY_SIGN_FLAG_TLS1_RSA) - return _gnutls_privkey_sign_raw_data(signer, flags, - hash_data, signature); + return _gnutls_privkey_sign_raw_data(signer, + hash_data, signature, + ¶ms); + + return privkey_sign_hash(signer, hash_data, signature, ¶ms); +} + +int +privkey_sign_hash(gnutls_privkey_t signer, + const gnutls_datum_t * hash_data, + gnutls_datum_t * signature, + gnutls_x509_spki_st * params) +{ + int ret; + gnutls_datum_t digest; digest.data = gnutls_malloc(hash_data->size); if (digest.data == NULL) { @@ -1163,15 +1282,15 @@ gnutls_privkey_sign_hash(gnutls_privkey_t signer, digest.size = hash_data->size; memcpy(digest.data, hash_data->data, digest.size); - ret = - pk_prepare_hash(signer->pk_algorithm, hash_to_entry(hash_algo), - &digest); + ret = pk_prepare_hash(params->pk, hash_to_entry(params->dig), &digest); if (ret < 0) { gnutls_assert(); goto cleanup; } - ret = _gnutls_privkey_sign_raw_data(signer, flags, &digest, signature); + ret = _gnutls_privkey_sign_raw_data(signer, + &digest, signature, + params); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -1187,9 +1306,9 @@ gnutls_privkey_sign_hash(gnutls_privkey_t signer, /*- * gnutls_privkey_sign_raw_data: * @key: Holds the key - * @flags: should be zero * @data: holds the data to be signed * @signature: will contain the signature allocated with gnutls_malloc() + * @params: holds the signing parameters * * This function will sign the given data using a signature algorithm * supported by the private key. Note that this is a low-level function @@ -1207,9 +1326,9 @@ gnutls_privkey_sign_hash(gnutls_privkey_t signer, -*/ static int _gnutls_privkey_sign_raw_data(gnutls_privkey_t key, - unsigned flags, const gnutls_datum_t * data, - gnutls_datum_t * signature) + gnutls_datum_t * signature, + gnutls_x509_spki_st * params) { switch (key->type) { #ifdef ENABLE_OPENPGP @@ -1223,8 +1342,8 @@ _gnutls_privkey_sign_raw_data(gnutls_privkey_t key, data, signature); #endif case GNUTLS_PRIVKEY_X509: - return _gnutls_pk_sign(key->key.x509->pk_algorithm, - signature, data, &key->key.x509->params); + return _gnutls_pk_sign(params->pk, signature, data, + &key->key.x509->params, params); case GNUTLS_PRIVKEY_EXT: if (key->key.ext.sign_func == NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); diff --git a/lib/pubkey.c b/lib/pubkey.c index 7c42f67805..e4cb1c92f1 100644 --- a/lib/pubkey.c +++ b/lib/pubkey.c @@ -43,6 +43,7 @@ unsigned pubkey_to_bits(gnutls_pk_algorithm_t pk, gnutls_pk_params_st * params) { switch (pk) { case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: return _gnutls_mpi_get_nbits(params->params[RSA_MODULUS]); case GNUTLS_PK_DSA: return _gnutls_mpi_get_nbits(params->params[DSA_P]); @@ -294,6 +295,7 @@ gnutls_pubkey_get_preferred_hash_algorithm(gnutls_pubkey_t key, ret = 0; break; case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: if (hash) *hash = GNUTLS_DIG_SHA256; ret = 0; @@ -371,6 +373,7 @@ gnutls_pubkey_import_pkcs11(gnutls_pubkey_t key, switch (obj->pk_algorithm) { case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: ret = gnutls_pubkey_import_rsa_raw(key, &obj->pubkey[0], &obj->pubkey[1]); break; @@ -838,7 +841,7 @@ gnutls_pubkey_export_rsa_raw(gnutls_pubkey_t key, return GNUTLS_E_INVALID_REQUEST; } - if (key->pk_algorithm != GNUTLS_PK_RSA) { + if (!GNUTLS_PK_IS_RSA(key->pk_algorithm)) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } @@ -1611,6 +1614,7 @@ gnutls_pubkey_verify_data2(gnutls_pubkey_t pubkey, { int ret; const mac_entry_st *me; + gnutls_x509_spki_st params; if (pubkey == NULL) { gnutls_assert(); @@ -1620,12 +1624,32 @@ gnutls_pubkey_verify_data2(gnutls_pubkey_t pubkey, if (flags & OLD_PUBKEY_VERIFY_FLAG_TLS1_RSA || flags & GNUTLS_VERIFY_USE_TLS1_RSA) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); - me = hash_to_entry(gnutls_sign_get_hash_algorithm(algo)); + memcpy(¶ms, &pubkey->params.sign, sizeof(gnutls_x509_spki_st)); + + params.pk = pubkey->pk_algorithm; + params.dig = gnutls_sign_get_hash_algorithm(algo); + me = hash_to_entry(params.dig); if (me == NULL) return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); - ret = pubkey_verify_data(pubkey->pk_algorithm, me, - data, signature, &pubkey->params); + if (flags & GNUTLS_VERIFY_USE_RSA_PSS) { + unsigned bits; + + if (!GNUTLS_PK_IS_RSA(pubkey->pk_algorithm)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + /* The requested sign algorithm is RSA-PSS, while the + * pubkey doesn't include parameter information. Fill + * it with the same way as gnutls_privkey_sign*. */ + if (pubkey->pk_algorithm == GNUTLS_PK_RSA) { + gnutls_pubkey_get_pk_algorithm(pubkey, &bits); + params.salt_size = _gnutls_find_rsa_pss_salt_size(bits, me, 0); + } + params.pk = GNUTLS_PK_RSA_PSS; + } + + ret = pubkey_verify_data(params.pk, me, data, signature, &pubkey->params, + ¶ms); if (ret < 0) { gnutls_assert(); return ret; @@ -1664,20 +1688,54 @@ gnutls_pubkey_verify_hash2(gnutls_pubkey_t key, const gnutls_datum_t * signature) { const mac_entry_st *me; + gnutls_x509_spki_st params; if (key == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } + memcpy(¶ms, &key->params.sign, sizeof(gnutls_x509_spki_st)); + if (flags & OLD_PUBKEY_VERIFY_FLAG_TLS1_RSA || flags & GNUTLS_VERIFY_USE_TLS1_RSA) { - return _gnutls_pk_verify(GNUTLS_PK_RSA, hash, signature, - &key->params); + params.pk = GNUTLS_PK_RSA; + return _gnutls_pk_verify(params.pk, hash, signature, + &key->params, ¶ms); } else { - me = hash_to_entry(gnutls_sign_get_hash_algorithm(algo)); - return pubkey_verify_hashed_data(key->pk_algorithm, me, + params.pk = key->pk_algorithm; + params.dig = gnutls_sign_get_hash_algorithm(algo); + /* This can be NULL here, if pubkey is DSA. For RSA it + * is checked below. */ + me = hash_to_entry(params.dig); + + if (flags & GNUTLS_VERIFY_USE_RSA_PSS) { + int ret; + gnutls_pk_algorithm_t pk; + unsigned bits; + + ret = gnutls_pubkey_get_pk_algorithm(key, &bits); + if (ret < 0) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + pk = ret; + + /* The requested sign algorithm is RSA-PSS, while the + * pubkey doesn't include parameter information */ + if (pk == GNUTLS_PK_RSA) { + if (me == NULL) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + params.salt_size = + _gnutls_find_rsa_pss_salt_size(bits, me, 0); + } + params.pk = GNUTLS_PK_RSA_PSS; + } + + return pubkey_verify_hashed_data(params.pk, me, hash, signature, - &key->params); + &key->params, + ¶ms); } } @@ -1785,7 +1843,8 @@ _pkcs1_rsa_verify_sig(const mac_entry_st * me, const gnutls_datum_t * text, const gnutls_datum_t * prehash, const gnutls_datum_t * signature, - gnutls_pk_params_st * params) + gnutls_pk_params_st * params, + gnutls_x509_spki_st * sign_params) { int ret; uint8_t md[MAX_HASH_SIZE], *cmp; @@ -1823,7 +1882,8 @@ _pkcs1_rsa_verify_sig(const mac_entry_st * me, if (ret < 0) return gnutls_assert_val(ret); - ret = _gnutls_pk_verify(GNUTLS_PK_RSA, &di, signature, params); + ret = _gnutls_pk_verify(GNUTLS_PK_RSA, &di, signature, params, + sign_params); _gnutls_free_datum(&di); return ret; @@ -1836,7 +1896,8 @@ dsa_verify_hashed_data(gnutls_pk_algorithm_t pk, const mac_entry_st * algo, const gnutls_datum_t * hash, const gnutls_datum_t * signature, - gnutls_pk_params_st * params) + gnutls_pk_params_st * params, + gnutls_x509_spki_st * sign_params) { gnutls_datum_t digest; unsigned int hash_len; @@ -1863,7 +1924,7 @@ dsa_verify_hashed_data(gnutls_pk_algorithm_t pk, digest.data = hash->data; digest.size = hash->size; - return _gnutls_pk_verify(pk, &digest, signature, params); + return _gnutls_pk_verify(pk, &digest, signature, params, sign_params); } static int @@ -1871,7 +1932,8 @@ dsa_verify_data(gnutls_pk_algorithm_t pk, const mac_entry_st * algo, const gnutls_datum_t * data, const gnutls_datum_t * signature, - gnutls_pk_params_st * params) + gnutls_pk_params_st * params, + gnutls_x509_spki_st * sign_params) { int ret; uint8_t _digest[MAX_HASH_SIZE]; @@ -1888,7 +1950,7 @@ dsa_verify_data(gnutls_pk_algorithm_t pk, digest.data = _digest; digest.size = _gnutls_hash_get_algo_len(algo); - return _gnutls_pk_verify(pk, &digest, signature, params); + return _gnutls_pk_verify(pk, &digest, signature, params, sign_params); } /* Verifies the signature data, and returns GNUTLS_E_PK_SIG_VERIFY_FAILED if @@ -1896,17 +1958,16 @@ dsa_verify_data(gnutls_pk_algorithm_t pk, */ int pubkey_verify_hashed_data(gnutls_pk_algorithm_t pk, - const mac_entry_st * hash_algo, + const mac_entry_st *hash_algo, const gnutls_datum_t * hash, const gnutls_datum_t * signature, - gnutls_pk_params_st * issuer_params) + gnutls_pk_params_st * params, + gnutls_x509_spki_st * sign_params) { - switch (pk) { case GNUTLS_PK_RSA: - if (_pkcs1_rsa_verify_sig - (hash_algo, NULL, hash, signature, issuer_params) != 0) + (hash_algo, NULL, hash, signature, params, sign_params) != 0) { gnutls_assert(); return GNUTLS_E_PK_SIG_VERIFY_FAILED; @@ -1915,10 +1976,11 @@ pubkey_verify_hashed_data(gnutls_pk_algorithm_t pk, return 1; break; + case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_EC: case GNUTLS_PK_DSA: if (dsa_verify_hashed_data - (pk, hash_algo, hash, signature, issuer_params) != 0) { + (pk, hash_algo, hash, signature, params, sign_params) != 0) { gnutls_assert(); return GNUTLS_E_PK_SIG_VERIFY_FAILED; } @@ -1940,14 +2002,13 @@ pubkey_verify_data(gnutls_pk_algorithm_t pk, const mac_entry_st * me, const gnutls_datum_t * data, const gnutls_datum_t * signature, - gnutls_pk_params_st * issuer_params) + gnutls_pk_params_st * params, + gnutls_x509_spki_st * sign_params) { - switch (pk) { case GNUTLS_PK_RSA: - if (_pkcs1_rsa_verify_sig - (me, data, NULL, signature, issuer_params) != 0) { + (me, data, NULL, signature, params, sign_params) != 0) { gnutls_assert(); return GNUTLS_E_PK_SIG_VERIFY_FAILED; } @@ -1955,10 +2016,11 @@ pubkey_verify_data(gnutls_pk_algorithm_t pk, return 1; break; + case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_EC: case GNUTLS_PK_DSA: - if (dsa_verify_data(pk, me, data, signature, issuer_params) - != 0) { + if (dsa_verify_data + (pk, me, data, signature, params, sign_params) != 0) { gnutls_assert(); return GNUTLS_E_PK_SIG_VERIFY_FAILED; } diff --git a/lib/x509/Makefile.am b/lib/x509/Makefile.am index f4d9463c55..46d567ed95 100644 --- a/lib/x509/Makefile.am +++ b/lib/x509/Makefile.am @@ -71,6 +71,7 @@ libgnutls_x509_la_SOURCES = \ pkcs7-output.c \ virt-san.c \ virt-san.h \ + spki.c \ x509_ext_int.h \ tls_features.c \ krb5.c krb5.h \ diff --git a/lib/x509/common.c b/lib/x509/common.c index 38425bde4c..a07b0ec5ed 100644 --- a/lib/x509/common.c +++ b/lib/x509/common.c @@ -1240,19 +1240,48 @@ int _gnutls_x509_get_signature_algorithm(ASN1_TYPE src, const char *src_name) { int result; + char name[128]; gnutls_datum_t sa = {NULL, 0}; - /* Read the signature algorithm. Note that parameters are not - * read. They will be read from the issuer's certificate if needed. - */ - result = _gnutls_x509_read_value(src, src_name, &sa); + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".algorithm"); + + /* Read the signature algorithm */ + result = _gnutls_x509_read_value(src, name, &sa); if (result < 0) { gnutls_assert(); return result; } - result = gnutls_oid_to_sign((char *) sa.data); + /* Read the signature parameters. Unless the algorithm is + * RSA-PSS, parameters are not read. They will be read from + * the issuer's certificate if needed. + */ + if (sa.data && strcmp ((char *) sa.data, PK_PKIX1_RSA_PSS_OID) == 0) { + gnutls_datum_t der = {NULL, 0}; + gnutls_x509_spki_st params; + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".parameters"); + + result = _gnutls_x509_read_value(src, name, &der); + if (result < 0) { + _gnutls_free_datum(&sa); + return gnutls_assert_val(result); + } + + result = _gnutls_x509_read_rsa_pss_params(der.data, der.size, + ¶ms); + _gnutls_free_datum(&der); + + if (result == 0) + result = gnutls_pk_to_sign(params.pk, params.dig); + else if (result == GNUTLS_E_UNKNOWN_ALGORITHM) + result = GNUTLS_SIGN_UNKNOWN; + } else { + result = gnutls_oid_to_sign((char *) sa.data); + } _gnutls_free_datum(&sa); diff --git a/lib/x509/common.h b/lib/x509/common.h index b0c1c5e29f..0cca5272b8 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -46,6 +46,7 @@ /* public key algorithm's OIDs */ #define PK_PKIX1_RSA_OID "1.2.840.113549.1.1.1" +#define PK_PKIX1_RSA_PSS_OID "1.2.840.113549.1.1.10" #define PK_X509_RSA_OID "2.5.8.1.1" #define PK_DSA_OID "1.2.840.10040.4.1" #define PK_GOST_R3410_94_OID "1.2.643.2.2.20" @@ -89,6 +90,7 @@ #define XMPP_OID "1.3.6.1.5.5.7.8.5" #define KRB5_PRINCIPAL_OID "1.3.6.1.5.2.2" +#define PKIX1_RSA_PSS_MGF1_OID "1.2.840.113549.1.1.8" #define ASN1_NULL "\x05\x00" #define ASN1_NULL_SIZE 2 diff --git a/lib/x509/crl.c b/lib/x509/crl.c index bd307ca42a..928f6c05c9 100644 --- a/lib/x509/crl.c +++ b/lib/x509/crl.c @@ -373,32 +373,13 @@ gnutls_x509_crl_get_issuer_dn3(gnutls_x509_crl_t crl, gnutls_datum_t * dn, unsig **/ int gnutls_x509_crl_get_signature_algorithm(gnutls_x509_crl_t crl) { - int result; - gnutls_datum_t sa; - if (crl == NULL) { gnutls_assert(); return GNUTLS_E_INVALID_REQUEST; } - /* Read the signature algorithm. Note that parameters are not - * read. They will be read from the issuer's certificate if needed. - */ - - result = - _gnutls_x509_read_value(crl->crl, - "signatureAlgorithm.algorithm", &sa); - - if (result < 0) { - gnutls_assert(); - return result; - } - - result = gnutls_oid_to_sign((const char *) sa.data); - - _gnutls_free_datum(&sa); - - return result; + return _gnutls_x509_get_signature_algorithm(crl->crl, + "signatureAlgorithm"); } /** diff --git a/lib/x509/crl_write.c b/lib/x509/crl_write.c index 6e5cfc9f28..a5930a45f9 100644 --- a/lib/x509/crl_write.c +++ b/lib/x509/crl_write.c @@ -499,7 +499,7 @@ gnutls_x509_crl_privkey_sign(gnutls_x509_crl_t crl, disable_optional_stuff(crl); result = _gnutls_x509_pkix_sign(crl->crl, "tbsCertList", - dig, issuer, issuer_key); + dig, 0, issuer, issuer_key); if (result < 0) { gnutls_assert(); return result; diff --git a/lib/x509/crq.c b/lib/x509/crq.c index 13978baa40..e6f774d1f7 100644 --- a/lib/x509/crq.c +++ b/lib/x509/crq.c @@ -36,6 +36,7 @@ #include <gnutls/x509-ext.h> #include "x509_int.h" #include <libtasn1.h> +#include <pk.h> /** * gnutls_x509_crq_init: @@ -175,7 +176,7 @@ gnutls_x509_crq_import(gnutls_x509_crq_t crq, int gnutls_x509_crq_get_signature_algorithm(gnutls_x509_crq_t crq) { return _gnutls_x509_get_signature_algorithm(crq->crq, - "signatureAlgorithm.algorithm"); + "signatureAlgorithm"); } /** @@ -1278,6 +1279,33 @@ gnutls_x509_crq_export2(gnutls_x509_crq_t crq, int gnutls_x509_crq_get_pk_algorithm(gnutls_x509_crq_t crq, unsigned int *bits) { + return gnutls_x509_crq_get_pk_algorithm2(crq, NULL, bits); +} + +/** + * gnutls_x509_crq_get_pk_algorithm2: + * @crq: should contain a #gnutls_x509_crq_t type + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @bits: if bits is non-%NULL it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of a PKCS#10 + * certificate request. + * + * If @spki is non null, it should have enough size to hold the + * parameters. + * + * If @bits is non-%NULL, it should have enough size to hold the + * parameters size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + **/ +int +gnutls_x509_crq_get_pk_algorithm2(gnutls_x509_crq_t crq, + gnutls_x509_spki_t spki, + unsigned int *bits) +{ int result; if (crq == NULL) { @@ -1289,6 +1317,24 @@ gnutls_x509_crq_get_pk_algorithm(gnutls_x509_crq_t crq, unsigned int *bits) (crq->crq, "certificationRequestInfo.subjectPKInfo", bits); if (result < 0) { gnutls_assert(); + return result; + } + + if (spki) { + gnutls_x509_spki_st params; + + spki->pk = result; + + result = _gnutls_x509_crq_read_sign_params(crq, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + spki->dig = params.dig; + spki->salt_size = params.salt_size; + + return spki->pk; } return result; @@ -2774,6 +2820,8 @@ gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key, int result; gnutls_datum_t signature; gnutls_datum_t tbs; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; if (crq == NULL) { gnutls_assert(); @@ -2790,6 +2838,19 @@ gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key, } } + result = _gnutls_privkey_get_sign_params(key, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + pk = gnutls_privkey_get_pk_algorithm(key, NULL); + result = _gnutls_privkey_find_sign_params(key, pk, dig, 0, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + /* Step 1. Self sign the request. */ result = @@ -2801,7 +2862,7 @@ gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key, return result; } - result = gnutls_privkey_sign_data(key, dig, 0, &tbs, &signature); + result = privkey_sign_data(key, &tbs, &signature, ¶ms); gnutls_free(tbs.data); if (result < 0) { @@ -2825,9 +2886,8 @@ gnutls_x509_crq_privkey_sign(gnutls_x509_crq_t crq, gnutls_privkey_t key, /* Step 3. Write the signatureAlgorithm field. */ result = - _gnutls_x509_write_sig_params(crq->crq, "signatureAlgorithm", - gnutls_privkey_get_pk_algorithm - (key, NULL), dig, 0); + _gnutls_x509_write_sign_params(crq->crq, "signatureAlgorithm", + ¶ms); if (result < 0) { gnutls_assert(); return result; @@ -2856,6 +2916,7 @@ int gnutls_x509_crq_verify(gnutls_x509_crq_t crq, unsigned int flags) gnutls_datum_t signature = { NULL, 0 }; gnutls_pk_params_st params; gnutls_digest_algorithm_t algo; + gnutls_x509_spki_st sign_params; int ret; gnutls_pk_params_init(¶ms); @@ -2871,7 +2932,7 @@ int gnutls_x509_crq_verify(gnutls_x509_crq_t crq, unsigned int flags) ret = _gnutls_x509_get_signature_algorithm(crq->crq, - "signatureAlgorithm.algorithm"); + "signatureAlgorithm"); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -2892,10 +2953,18 @@ int gnutls_x509_crq_verify(gnutls_x509_crq_t crq, unsigned int flags) goto cleanup; } + ret = _gnutls_x509_read_sign_params(crq->crq, + "signatureAlgorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + ret = - pubkey_verify_data(gnutls_x509_crq_get_pk_algorithm(crq, NULL), + pubkey_verify_data(sign_params.pk, hash_to_entry(algo), &data, &signature, - ¶ms); + ¶ms, &sign_params); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -3120,3 +3189,102 @@ gnutls_x509_crq_set_extension_by_oid(gnutls_x509_crq_t crq, return 0; } + +/** + * gnutls_x509_crq_set_pk_algorithm: + * @crq: a certificate request of type #gnutls_x509_crq_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will set the certificate request's subject public key + * information explicitly. This is intended to be used in the cases + * where a single public key (e.g., RSA) can be used for multiple + * signature algorithms (RSA PKCS1-1.5, and RSA-PSS). + * + * To export the public key (i.e., the SubjectPublicKeyInfo part), check + * gnutls_pubkey_import_x509(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crq_set_pk_algorithm(gnutls_x509_crq_t crq, + gnutls_x509_spki_t spki, + unsigned int flags) +{ + int result; + gnutls_pk_algorithm_t crq_pk; + gnutls_x509_spki_st params; + unsigned bits; + + if (crq == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_crq_get_pk_algorithm(crq, &bits); + if (result < 0) { + gnutls_assert(); + return result; + } + + crq_pk = result; + + if (spki->pk != GNUTLS_PK_RSA_PSS) { + if (crq_pk == spki->pk) + return 0; + + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (crq_pk == GNUTLS_PK_RSA) { + const mac_entry_st *me; + + me = hash_to_entry(spki->dig); + if (unlikely(me == NULL)) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(¶ms, 0, sizeof(gnutls_x509_spki_st)); + params.pk = spki->pk; + params.dig = spki->dig; + + /* If salt size is zero, find the optimal salt size. */ + if (spki->salt_size == 0) { + params.salt_size = + _gnutls_find_rsa_pss_salt_size(bits, me, + spki->salt_size); + } else + params.salt_size = spki->salt_size; + } else if (crq_pk == GNUTLS_PK_RSA_PSS) { + result = _gnutls_x509_crq_read_sign_params(crq, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (params.dig != spki->dig || + params.salt_size > spki->salt_size) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + params.salt_size = spki->salt_size; + } + + result = _gnutls_x509_write_sign_params(crq->crq, + "certificationRequestInfo." + "subjectPKInfo." + "algorithm", + ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} diff --git a/lib/x509/key_decode.c b/lib/x509/key_decode.c index 1e5cc43c3a..8d929f7315 100644 --- a/lib/x509/key_decode.c +++ b/lib/x509/key_decode.c @@ -234,6 +234,144 @@ _gnutls_x509_read_ecc_params(uint8_t * der, int dersize, } +/* Reads RSA-PSS parameters. + */ +int +_gnutls_x509_read_rsa_pss_params(uint8_t * der, int dersize, + gnutls_x509_spki_st * params) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + gnutls_digest_algorithm_t digest; + char oid[MAX_OID_SIZE]; + int size; + unsigned int trailer; + gnutls_datum_t value = { NULL, 0 }; + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPSSParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&spk, der, dersize, NULL); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + size = sizeof(oid); + result = asn1_read_value(spk, "hashAlgorithm.algorithm", oid, &size); + if (result == ASN1_SUCCESS) + digest = gnutls_oid_to_digest(oid); + else if (result == ASN1_ELEMENT_NOT_FOUND) + /* The default hash algorithm is SHA-1 */ + digest = GNUTLS_DIG_SHA1; + else { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + size = sizeof(oid); + result = asn1_read_value(spk, "maskGenAlgorithm.algorithm", oid, &size); + if (result == ASN1_SUCCESS) { + gnutls_digest_algorithm_t digest2; + + /* Error out if algorithm other than mgf1 is specified */ + if (strcmp(oid, PKIX1_RSA_PSS_MGF1_OID) != 0) { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + /* Check if maskGenAlgorithm.parameters does exist and + * is identical to hashAlgorithm */ + result = _gnutls_x509_read_value(spk, "maskGenAlgorithm.parameters", &value); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.AlgorithmIdentifier", &c2)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _asn1_strict_der_decode(&c2, value.data, value.size, NULL); + if (result != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + size = sizeof(oid); + result = asn1_read_value(c2, "algorithm", oid, &size); + if (result == ASN1_SUCCESS) + digest2 = gnutls_oid_to_digest(oid); + else if (result == ASN1_ELEMENT_NOT_FOUND) + /* The default hash algorithm for mgf1 is SHA-1 */ + digest2 = GNUTLS_DIG_SHA1; + else { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if (digest != digest2) { + gnutls_assert(); + result = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + } else if (result != ASN1_ELEMENT_NOT_FOUND) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + memset(params, 0, sizeof(gnutls_x509_spki_st)); + params->pk = GNUTLS_PK_RSA_PSS; + params->dig = digest; + + result = _gnutls_x509_read_uint(spk, "saltLength", ¶ms->salt_size); + if (result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND || + result == GNUTLS_E_ASN1_VALUE_NOT_FOUND) + params->salt_size = 20; + else if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_read_uint(spk, "trailerField", &trailer); + if (result == GNUTLS_E_ASN1_VALUE_NOT_FOUND || + result == GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) + trailer = 1; + else if (result < 0) { + gnutls_assert(); + goto cleanup; + } + if (trailer != 1) { + gnutls_assert(); + result = GNUTLS_E_CERTIFICATE_ERROR; + goto cleanup; + } + + result = 0; + cleanup: + _gnutls_free_datum(&value); + asn1_delete_structure(&c2); + asn1_delete_structure(&spk); + return result; +} + /* This function must be called after _gnutls_x509_read_params() */ int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der, @@ -243,9 +381,10 @@ int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t algo, uint8_t * der, switch (algo) { case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: ret = _gnutls_x509_read_rsa_pubkey(der, dersize, params); if (ret >= 0) { - params->algo = GNUTLS_PK_RSA; + params->algo = algo; params->params_nr = RSA_PUBLIC_PARAMS; } break; @@ -282,6 +421,8 @@ int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t algo, switch (algo) { case GNUTLS_PK_RSA: return 0; + case GNUTLS_PK_RSA_PSS: + return _gnutls_x509_read_rsa_pss_params(der, dersize, ¶ms->sign); case GNUTLS_PK_DSA: return _gnutls_x509_read_dsa_params(der, dersize, params); case GNUTLS_PK_EC: @@ -291,6 +432,34 @@ int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t algo, } } +/* This function must be called after _gnutls_x509_read_pubkey() + */ +int _gnutls_x509_check_pubkey_params(gnutls_pk_algorithm_t algo, + gnutls_pk_params_st * params) +{ + switch (algo) { + case GNUTLS_PK_RSA_PSS: { + unsigned bits = pubkey_to_bits(algo, params); + const mac_entry_st *me = hash_to_entry(params->sign.dig); + size_t hash_size; + + if (unlikely(me == NULL)) + return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); + + hash_size = _gnutls_hash_get_algo_len(me); + if (hash_size + params->sign.salt_size + 2 > (bits + 7) / 8) + return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); + return 0; + } + case GNUTLS_PK_RSA: + case GNUTLS_PK_DSA: + case GNUTLS_PK_EC: + return 0; + default: + return gnutls_assert_val(GNUTLS_E_UNIMPLEMENTED_FEATURE); + } +} + /* reads DSA's Y * from the certificate * only sets params[3] diff --git a/lib/x509/key_encode.c b/lib/x509/key_encode.c index 3277ca2476..724f7402d9 100644 --- a/lib/x509/key_encode.c +++ b/lib/x509/key_encode.c @@ -144,6 +144,8 @@ _gnutls_x509_write_pubkey_params(gnutls_pk_algorithm_t algo, memcpy(der->data, ASN1_NULL, ASN1_NULL_SIZE); der->size = ASN1_NULL_SIZE; return 0; + case GNUTLS_PK_RSA_PSS: + return _gnutls_x509_write_rsa_pss_params(¶ms->sign, der); case GNUTLS_PK_EC: return _gnutls_x509_write_ecc_params(params->flags, der); default: @@ -160,6 +162,7 @@ _gnutls_x509_write_pubkey(gnutls_pk_algorithm_t algo, case GNUTLS_PK_DSA: return _gnutls_x509_write_dsa_pubkey(params, der); case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: return _gnutls_x509_write_rsa_pubkey(params, der); case GNUTLS_PK_EC: return _gnutls_x509_write_ecc_pubkey(params, der); @@ -285,6 +288,117 @@ _gnutls_x509_write_ecc_params(gnutls_ecc_curve_t curve, return result; } +int +_gnutls_x509_write_rsa_pss_params(gnutls_x509_spki_st *params, + gnutls_datum_t *der) +{ + int result; + ASN1_TYPE spk = ASN1_TYPE_EMPTY; + ASN1_TYPE c2 = ASN1_TYPE_EMPTY; + const char *oid; + gnutls_datum_t tmp = { NULL, 0 }; + + der->data = NULL; + der->size = 0; + + if ((result = asn1_create_element + (_gnutls_get_gnutls_asn(), "GNUTLS.RSAPSSParameters", &spk)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + oid = gnutls_digest_get_oid(params->dig); + + if ((result = asn1_write_value(spk, "hashAlgorithm.algorithm", oid, 1)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = asn1_write_value(spk, "hashAlgorithm.parameters", NULL, 0)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = + asn1_write_value(spk, "maskGenAlgorithm.algorithm", + PKIX1_RSA_PSS_MGF1_OID, 1)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = asn1_create_element + (_gnutls_get_pkix(), "PKIX1.AlgorithmIdentifier", &c2)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = asn1_write_value(c2, "algorithm", oid, 1)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + if ((result = asn1_write_value(c2, "parameters", NULL, 0)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _gnutls_x509_der_encode(c2, "", &tmp, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + if ((result = + asn1_write_value(spk, "maskGenAlgorithm.parameters", + tmp.data, tmp.size)) + != ASN1_SUCCESS) { + gnutls_assert(); + result = _gnutls_asn2err(result); + goto cleanup; + } + + result = _gnutls_x509_write_uint32(spk, "saltLength", + params->salt_size); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_write_uint32(spk, "trailerField", 1); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_x509_der_encode(spk, "", der, 0); + if (result < 0) { + gnutls_assert(); + goto cleanup; + } + + result = 0; + + cleanup: + _gnutls_free_datum(&tmp); + asn1_delete_structure(&c2); + asn1_delete_structure(&spk); + return result; +} + /* * This function writes the public parameters for DSS keys. * Needs 1 parameter (y). @@ -681,6 +795,7 @@ int _gnutls_asn1_encode_privkey(gnutls_pk_algorithm_t pk, ASN1_TYPE * c2, { switch (pk) { case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: return _gnutls_asn1_encode_rsa(c2, params, compat); case GNUTLS_PK_DSA: return _gnutls_asn1_encode_dsa(c2, params, compat); diff --git a/lib/x509/mpi.c b/lib/x509/mpi.c index 2bb3e54aaa..bd47d3244c 100644 --- a/lib/x509/mpi.c +++ b/lib/x509/mpi.c @@ -134,7 +134,7 @@ _gnutls_get_asn_mpis(ASN1_TYPE asn, const char *root, * then the issuer's parameters should be used. This is not * done yet. */ - if (pk_algorithm != GNUTLS_PK_RSA) { /* RSA doesn't use parameters */ + if (pk_algorithm != GNUTLS_PK_RSA) { /* RSA doesn't use parameters */ result = _gnutls_x509_read_value(asn, name, &tmp); if (result < 0) { gnutls_assert(); @@ -169,6 +169,12 @@ _gnutls_get_asn_mpis(ASN1_TYPE asn, const char *root, goto error; } + result = _gnutls_x509_check_pubkey_params(pk_algorithm, params); + if (result < 0) { + gnutls_assert(); + goto error; + } + result = 0; error: @@ -205,16 +211,89 @@ _gnutls_x509_crq_get_mpis(gnutls_x509_crq_t cert, } /* - * This function writes and encodes the parameters for DSS or RSA keys. + * This function reads and decodes the parameters for DSS or RSA keys. * This is the "signatureAlgorithm" fields. - * - * If @legacy is non-zero then the legacy value for PKCS#7 signatures - * will be written for RSA signatures. */ int -_gnutls_x509_write_sig_params(ASN1_TYPE dst, const char *dst_name, - gnutls_pk_algorithm_t pk_algorithm, - gnutls_digest_algorithm_t dig, unsigned legacy) +_gnutls_x509_read_sign_params(ASN1_TYPE src, const char *src_name, + gnutls_x509_spki_st *params) +{ + int result; + char name[128]; + char oid[MAX_OID_SIZE]; + int oid_size; + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".algorithm"); + + oid_size = sizeof(oid); + result = asn1_read_value(src, name, oid, &oid_size); + + if (result != ASN1_SUCCESS) { + gnutls_assert(); + return _gnutls_asn2err(result); + } + + if (strcmp (oid, PK_PKIX1_RSA_PSS_OID) == 0) { + gnutls_datum_t tmp = { NULL, 0 }; + + _gnutls_str_cpy(name, sizeof(name), src_name); + _gnutls_str_cat(name, sizeof(name), ".parameters"); + + result = _gnutls_x509_read_value(src, name, &tmp); + if (result < 0 && + result != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND && + result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + _gnutls_free_datum(&tmp); + return gnutls_assert_val(result); + } + + result = _gnutls_x509_read_rsa_pss_params(tmp.data, tmp.size, + params); + _gnutls_free_datum(&tmp); + + if (result < 0) + gnutls_assert(); + + return result; + } else { + memset(params, 0, sizeof(gnutls_x509_spki_st)); + + result = gnutls_oid_to_sign(oid); + if (result != GNUTLS_SIGN_UNKNOWN) { + params->pk = gnutls_sign_get_pk_algorithm(result); + params->dig = gnutls_sign_get_hash_algorithm(result); + } + } + + return 0; +} + +int +_gnutls_x509_crt_read_sign_params(gnutls_x509_crt_t crt, + gnutls_x509_spki_st *params) +{ + return _gnutls_x509_read_sign_params(crt->cert, + "tbsCertificate." + "subjectPublicKeyInfo." + "algorithm", + params); +} + +int +_gnutls_x509_crq_read_sign_params(gnutls_x509_crq_t crt, + gnutls_x509_spki_st *params) +{ + return _gnutls_x509_read_sign_params(crt->crq, + "certificationRequestInfo." + "subjectPKInfo." + "algorithm", + params); +} + +int +_gnutls_x509_write_sign_params(ASN1_TYPE dst, const char *dst_name, + gnutls_x509_spki_st *params) { int result; char name[128]; @@ -223,15 +302,18 @@ _gnutls_x509_write_sig_params(ASN1_TYPE dst, const char *dst_name, _gnutls_str_cpy(name, sizeof(name), dst_name); _gnutls_str_cat(name, sizeof(name), ".algorithm"); - if (legacy && pk_algorithm == GNUTLS_PK_RSA) + if (params->legacy && params->pk == GNUTLS_PK_RSA) oid = PK_PKIX1_RSA_OID; + else if (params->pk == GNUTLS_PK_RSA_PSS) + oid = PK_PKIX1_RSA_PSS_OID; else - oid = gnutls_sign_get_oid(gnutls_pk_to_sign(pk_algorithm, dig)); + oid = gnutls_sign_get_oid(gnutls_pk_to_sign(params->pk, + params->dig)); if (oid == NULL) { gnutls_assert(); _gnutls_debug_log ("Cannot find OID for sign algorithm pk: %d dig: %d\n", - (int) pk_algorithm, (int) dig); + (int) params->pk, (int) params->dig); return GNUTLS_E_INVALID_REQUEST; } @@ -247,10 +329,24 @@ _gnutls_x509_write_sig_params(ASN1_TYPE dst, const char *dst_name, _gnutls_str_cpy(name, sizeof(name), dst_name); _gnutls_str_cat(name, sizeof(name), ".parameters"); - if (pk_algorithm == GNUTLS_PK_RSA) + if (params->pk == GNUTLS_PK_RSA) result = asn1_write_value(dst, name, ASN1_NULL, ASN1_NULL_SIZE); - else + else if (params->pk == GNUTLS_PK_RSA_PSS) { + gnutls_datum_t tmp = { NULL, 0 }; + + if (params == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = _gnutls_x509_write_rsa_pss_params(params, &tmp); + if (result < 0) + return gnutls_assert_val(result); + + result = asn1_write_value(dst, name, tmp.data, tmp.size); + _gnutls_free_datum(&tmp); + } else result = asn1_write_value(dst, name, NULL, 0); if (result != ASN1_SUCCESS && result != ASN1_ELEMENT_NOT_FOUND) { diff --git a/lib/x509/output.c b/lib/x509/output.c index 8ebb998ed2..6bcf68275d 100644 --- a/lib/x509/output.c +++ b/lib/x509/output.c @@ -1231,6 +1231,7 @@ print_pubkey(gnutls_buffer_st * str, const char *key_name, (err, bits)), bits); switch (pk) { case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: { gnutls_datum_t m, e; @@ -1394,6 +1395,59 @@ print_pubkey(gnutls_buffer_st * str, const char *key_name, } static int +print_crt_sig_params(gnutls_buffer_st * str, gnutls_x509_crt_t crt, + gnutls_certificate_print_formats_t format) +{ + int ret; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; + gnutls_sign_algorithm_t sign; + + sign = gnutls_x509_crt_get_signature_algorithm(crt); + pk = gnutls_sign_get_pk_algorithm(sign); + if (pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_read_sign_params(crt->cert, + "signatureAlgorithm", + ¶ms); + if (ret < 0) { + addf(str, "error: read_pss_params: %s\n", + gnutls_strerror(ret)); + } else + addf(str, "\t\tSalt Length: %d\n", params.salt_size); + } + + return 0; +} + +static int +print_crt_pubkey_params(gnutls_buffer_st * str, const char *key_name, + gnutls_x509_crt_t crt, + gnutls_certificate_print_formats_t format) +{ + int ret; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; + + ret = gnutls_x509_crt_get_pk_algorithm(crt, NULL); + if (ret < 0) + return ret; + + pk = ret; + + if (pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_crt_read_sign_params(crt, ¶ms); + if (ret < 0) + return ret; + addf(str, _("\t%sPublic Key Parameters:\n"), key_name); + addf(str, "\t\tHash Algorithm: %s\n", + gnutls_digest_get_name(params.dig)); + addf(str, "\t\tSalt Length: %d\n", params.salt_size); + } + + return 0; +} + +static int print_crt_pubkey(gnutls_buffer_st * str, gnutls_x509_crt_t crt, gnutls_certificate_print_formats_t format) { @@ -1409,6 +1463,7 @@ print_crt_pubkey(gnutls_buffer_st * str, gnutls_x509_crt_t crt, goto cleanup; print_pubkey(str, _("Subject "), pubkey, format); + print_crt_pubkey_params(str, _("Subject "), crt, format); ret = 0; cleanup: @@ -1573,6 +1628,8 @@ print_cert(gnutls_buffer_st * str, gnutls_x509_crt_t cert, addf(str, _("\tSignature Algorithm: %s\n"), p); gnutls_free(name); + print_crt_sig_params(str, cert, format); + if (err != GNUTLS_SIGN_UNKNOWN && gnutls_sign_is_secure(err) == 0) { adds(str, _("warning: signed using a broken signature " @@ -2287,6 +2344,58 @@ gnutls_x509_crl_print(gnutls_x509_crl_t crl, } static int +print_crq_sig_params(gnutls_buffer_st * str, gnutls_x509_crq_t crt, + gnutls_certificate_print_formats_t format) +{ + int ret; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; + gnutls_sign_algorithm_t sign; + + sign = gnutls_x509_crq_get_signature_algorithm(crt); + pk = gnutls_sign_get_pk_algorithm(sign); + if (pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_read_sign_params(crt->crq, + "signatureAlgorithm", + ¶ms); + if (ret < 0) { + addf(str, "error: read_pss_params: %s\n", + gnutls_strerror(ret)); + } else + addf(str, "\t\tSalt Length: %d\n", params.salt_size); + } + + return 0; +} +static int +print_crq_pubkey_params(gnutls_buffer_st * str, const char *key_name, + gnutls_x509_crq_t crt, + gnutls_certificate_print_formats_t format) +{ + int ret; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st params; + + ret = gnutls_x509_crq_get_pk_algorithm(crt, NULL); + if (ret < 0) + return ret; + + pk = ret; + + if (pk == GNUTLS_PK_RSA_PSS) { + ret = _gnutls_x509_crq_read_sign_params(crt, ¶ms); + if (ret < 0) + return ret; + addf(str, _("\t%sPublic Key Parameters:\n"), key_name); + addf(str, "\t\tHash Algorithm: %s\n", + gnutls_digest_get_name(params.dig)); + addf(str, "\t\tSalt Length: %d\n", params.salt_size); + } + + return 0; +} + +static int print_crq_pubkey(gnutls_buffer_st * str, gnutls_x509_crq_t crq, gnutls_certificate_print_formats_t format) { @@ -2302,6 +2411,7 @@ print_crq_pubkey(gnutls_buffer_st * str, gnutls_x509_crq_t crq, goto cleanup; print_pubkey(str, _("Subject "), pubkey, format); + print_crq_pubkey_params(str, _("Subject "), crq, format); ret = 0; cleanup: @@ -2369,6 +2479,8 @@ print_crq(gnutls_buffer_st * str, gnutls_x509_crq_t cert, addf(str, _("\tSignature Algorithm: %s\n"), p); gnutls_free(name); + + print_crq_sig_params(str, cert, format); } /* parse attributes */ diff --git a/lib/x509/pkcs7.c b/lib/x509/pkcs7.c index e285546060..9222af652e 100644 --- a/lib/x509/pkcs7.c +++ b/lib/x509/pkcs7.c @@ -2348,6 +2348,7 @@ int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, gnutls_datum_t signature = { NULL, 0 }; const mac_entry_st *me = hash_to_entry(dig); unsigned pk, sigalgo; + gnutls_x509_spki_st key_params, params; if (pkcs7 == NULL || me == NULL) return GNUTLS_E_INVALID_REQUEST; @@ -2485,15 +2486,35 @@ int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, /* write the signature algorithm */ pk = gnutls_x509_crt_get_pk_algorithm(signer, NULL); + ret = _gnutls_privkey_get_sign_params(signer_key, &key_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_crt_get_sign_params(signer, &key_params, ¶ms); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + result = _gnutls_privkey_find_sign_params(signer_key, pk, dig, 0, + ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + /* RFC5652 is silent on what the values would be and initially I assumed that * typical signature algorithms should be set. However RFC2315 (PKCS#7) mentions * that a generic RSA OID should be used. We switch to this "unexpected" value * because some implementations cannot cope with the "expected" signature values. */ + params.legacy = 1; ret = - _gnutls_x509_write_sig_params(pkcs7->signed_data, - "signerInfos.?LAST.signatureAlgorithm", - pk, dig, 1); + _gnutls_x509_write_sign_params(pkcs7->signed_data, + "signerInfos.?LAST.signatureAlgorithm", + ¶ms); if (ret < 0) { gnutls_assert(); goto cleanup; @@ -2515,8 +2536,7 @@ int gnutls_pkcs7_sign(gnutls_pkcs7_t pkcs7, goto cleanup; } - ret = - gnutls_privkey_sign_data(signer_key, dig, 0, &sigdata, &signature); + ret = privkey_sign_data(signer_key, &sigdata, &signature, ¶ms); if (ret < 0) { gnutls_assert(); goto cleanup; diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c index c88f4fa681..b8e6092c34 100644 --- a/lib/x509/privkey.c +++ b/lib/x509/privkey.c @@ -1202,6 +1202,26 @@ int gnutls_x509_privkey_get_pk_algorithm2(gnutls_x509_privkey_t key, unsigned int *bits) { + return gnutls_x509_privkey_get_pk_algorithm3(key, NULL, bits); +} + +/** + * gnutls_x509_privkey_get_pk_algorithm3: + * @key: should contain a #gnutls_x509_privkey_t type + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @bits: The number of bits in the public key algorithm + * + * This function will return the public key algorithm of a private + * key. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + **/ +int +gnutls_x509_privkey_get_pk_algorithm3(gnutls_x509_privkey_t key, + gnutls_x509_spki_t spki, + unsigned int *bits) +{ int ret; if (key == NULL) { @@ -1209,6 +1229,10 @@ gnutls_x509_privkey_get_pk_algorithm2(gnutls_x509_privkey_t key, return GNUTLS_E_INVALID_REQUEST; } + if (spki) { + memcpy(spki, &key->params.sign, sizeof (gnutls_x509_spki_st)); + } + if (bits) { ret = pubkey_to_bits(key->pk_algorithm, &key->params); if (ret < 0) @@ -1221,7 +1245,7 @@ gnutls_x509_privkey_get_pk_algorithm2(gnutls_x509_privkey_t key, static const char *set_msg(gnutls_x509_privkey_t key) { - if (key->pk_algorithm == GNUTLS_PK_RSA) { + if (GNUTLS_PK_IS_RSA(key->pk_algorithm)) { if (key->params.seed_size > 0 && !(key->flags&GNUTLS_PRIVKEY_FLAG_EXPORT_COMPAT)) return PEM_KEY_RSA_PROVABLE; else @@ -1565,6 +1589,30 @@ gnutls_x509_privkey_generate2(gnutls_x509_privkey_t key, return ret; } + if (algo == GNUTLS_PK_RSA_PSS) { + const mac_entry_st *me; + + key->params.sign.pk = GNUTLS_PK_RSA_PSS; + if (key->params.palgo != GNUTLS_DIG_UNKNOWN) + key->params.sign.dig = key->params.palgo; + else + key->params.sign.dig = GNUTLS_DIG_SHA256; + + me = hash_to_entry(key->params.sign.dig); + if (unlikely(me == NULL)) { + gnutls_assert(); + ret = GNUTLS_E_INVALID_REQUEST; + goto cleanup; + } + + if (flags & GNUTLS_PRIVKEY_FLAG_PROVABLE) + key->params.sign.salt_size = 0; + else { + key->params.sign.salt_size = + _gnutls_find_rsa_pss_salt_size(bits, me, 0); + } + } + ret = _gnutls_pk_generate_keys(algo, bits, &key->params, 0); if (ret < 0) { gnutls_assert(); @@ -1930,7 +1978,7 @@ _gnutls_x509_privkey_sign_hash2(gnutls_x509_privkey_t signer, ret = _gnutls_pk_sign(signer->pk_algorithm, signature, &digest, - &signer->params); + &signer->params, &signer->params.sign); if (ret < 0) { gnutls_assert(); @@ -1974,7 +2022,7 @@ gnutls_x509_privkey_sign_hash(gnutls_x509_privkey_t key, result = _gnutls_pk_sign(key->pk_algorithm, signature, hash, - &key->params); + &key->params, &key->params.sign); if (result < 0) { gnutls_assert(); @@ -2130,3 +2178,66 @@ void gnutls_x509_privkey_set_flags(gnutls_x509_privkey_t key, { key->flags |= flags; } + +int +_gnutls_x509_privkey_get_sign_params(gnutls_x509_privkey_t key, + gnutls_x509_spki_st *params) +{ + memcpy(params, &key->params.sign, sizeof(gnutls_x509_spki_st)); + params->pk = gnutls_x509_privkey_get_pk_algorithm2(key, NULL); + return 0; +} + +int +_gnutls_x509_privkey_find_sign_params(gnutls_x509_privkey_t key, + gnutls_pk_algorithm_t pk, + gnutls_digest_algorithm_t dig, + unsigned flags, + gnutls_x509_spki_st *params) +{ + unsigned salt_size = 0; + gnutls_pk_algorithm_t key_pk; + unsigned bits; + + if (flags & GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS) { + if (!GNUTLS_PK_IS_RSA(pk)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + pk = GNUTLS_PK_RSA_PSS; + } + + key_pk = gnutls_x509_privkey_get_pk_algorithm2(key, &bits); + if (!(key_pk == pk || + (key_pk == GNUTLS_PK_RSA && pk == GNUTLS_PK_RSA_PSS))) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (pk == GNUTLS_PK_RSA_PSS) { + const mac_entry_st *me; + + me = hash_to_entry(dig); + if (unlikely(me == NULL)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + + if (params->pk == GNUTLS_PK_RSA) + salt_size = 0; + else if (params->pk == GNUTLS_PK_RSA_PSS) { + if (dig != params->dig) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + salt_size = params->salt_size; + } + + if ((flags & GNUTLS_PRIVKEY_SIGN_FLAG_REPRODUCIBLE) == 0) + salt_size = _gnutls_find_rsa_pss_salt_size(bits, me, + salt_size); + } + + params->pk = pk; + params->dig = dig; + params->salt_size = salt_size; + + return 0; +} diff --git a/lib/x509/privkey_pkcs8.c b/lib/x509/privkey_pkcs8.c index 0f1863d160..827794bec2 100644 --- a/lib/x509/privkey_pkcs8.c +++ b/lib/x509/privkey_pkcs8.c @@ -67,6 +67,7 @@ _encode_privkey(gnutls_x509_privkey_t pkey, gnutls_datum_t * raw) switch (pkey->pk_algorithm) { case GNUTLS_PK_RSA: + case GNUTLS_PK_RSA_PSS: case GNUTLS_PK_EC: ret = gnutls_x509_privkey_export2(pkey, GNUTLS_X509_FMT_DER, @@ -939,6 +940,44 @@ _decode_pkcs8_rsa_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey) return ret; } +/* Decodes an RSA-PSS privateKey from a PKCS8 structure. + */ +static int +_decode_pkcs8_rsa_pss_key(ASN1_TYPE pkcs8_asn, gnutls_x509_privkey_t pkey) +{ + int ret; + gnutls_datum_t tmp; + gnutls_x509_spki_st params; + + ret = _gnutls_x509_read_value(pkcs8_asn, + "privateKeyAlgorithm.parameters", &tmp); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = _gnutls_x509_read_rsa_pss_params(tmp.data, tmp.size, ¶ms); + _gnutls_free_key_datum(&tmp); + + if (ret < 0) { + gnutls_assert(); + goto error; + } + + ret = _decode_pkcs8_rsa_key(pkcs8_asn, pkey); + if (ret < 0) { + gnutls_assert(); + goto error; + } + + memcpy(&pkey->params.sign, ¶ms, sizeof(gnutls_x509_spki_st)); + + ret = 0; + + error: + return ret; +} + /* Decodes an ECC privateKey from a PKCS8 structure. */ static int @@ -1120,6 +1159,8 @@ decode_private_key_info(const gnutls_datum_t * der, if (pkey->pk_algorithm == GNUTLS_PK_RSA) result = _decode_pkcs8_rsa_key(pkcs8_asn, pkey); + else if (pkey->pk_algorithm == GNUTLS_PK_RSA_PSS) + result = _decode_pkcs8_rsa_pss_key(pkcs8_asn, pkey); else if (pkey->pk_algorithm == GNUTLS_PK_DSA) result = _decode_pkcs8_dsa_key(pkcs8_asn, pkey); else if (pkey->pk_algorithm == GNUTLS_PK_EC) diff --git a/lib/x509/sign.c b/lib/x509/sign.c index 35e57f01a2..0abe92a3b4 100644 --- a/lib/x509/sign.c +++ b/lib/x509/sign.c @@ -36,6 +36,7 @@ #include <x509_int.h> #include <common.h> #include <gnutls/abstract.h> +#include <pk.h> /* This is the same as the _gnutls_x509_sign, but this one will decode * the ASN1_TYPE given, and sign the DER data. Actually used to get the DER @@ -48,6 +49,43 @@ _gnutls_x509_get_tbs(ASN1_TYPE cert, const char *tbs_name, return _gnutls_x509_der_encode(cert, tbs_name, tbs, 0); } +int +_gnutls_x509_crt_get_sign_params(gnutls_x509_crt_t crt, + const gnutls_x509_spki_st *key_params, + gnutls_x509_spki_st *params) +{ + int result; + gnutls_x509_spki_st crt_params; + + result = _gnutls_x509_crt_read_sign_params(crt, &crt_params); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (crt_params.pk == GNUTLS_PK_RSA_PSS) { + if (key_params->pk == GNUTLS_PK_RSA_PSS) { + if (crt_params.dig != key_params->dig) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + + if (crt_params.salt_size < key_params->salt_size) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + } else if (key_params->pk != GNUTLS_PK_RSA) { + gnutls_assert(); + return GNUTLS_E_CERTIFICATE_ERROR; + } + memcpy(params, &crt_params, sizeof(gnutls_x509_spki_st)); + } else { + memcpy(params, key_params, sizeof(gnutls_x509_spki_st)); + } + + return 0; +} + /*- * _gnutls_x509_pkix_sign - This function will sign a CRL or a certificate with a key * @src: should contain an ASN1_TYPE @@ -63,6 +101,7 @@ _gnutls_x509_get_tbs(ASN1_TYPE cert, const char *tbs_name, int _gnutls_x509_pkix_sign(ASN1_TYPE src, const char *src_name, gnutls_digest_algorithm_t dig, + unsigned int flags, gnutls_x509_crt_t issuer, gnutls_privkey_t issuer_key) { @@ -70,6 +109,31 @@ _gnutls_x509_pkix_sign(ASN1_TYPE src, const char *src_name, gnutls_datum_t signature; gnutls_datum_t tbs; char name[128]; + gnutls_pk_algorithm_t pk; + gnutls_x509_spki_st key_params, params; + + pk = gnutls_x509_crt_get_pk_algorithm(issuer, NULL); + if (pk == GNUTLS_PK_UNKNOWN) + pk = gnutls_privkey_get_pk_algorithm(issuer_key, NULL); + + result = _gnutls_privkey_get_sign_params(issuer_key, &key_params); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_x509_crt_get_sign_params(issuer, &key_params, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + result = _gnutls_privkey_find_sign_params(issuer_key, pk, dig, flags, + ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } /* Step 1. Copy the issuer's name into the certificate. */ @@ -89,9 +153,7 @@ _gnutls_x509_pkix_sign(ASN1_TYPE src, const char *src_name, _gnutls_str_cpy(name, sizeof(name), src_name); _gnutls_str_cat(name, sizeof(name), ".signature"); - result = _gnutls_x509_write_sig_params(src, name, - gnutls_privkey_get_pk_algorithm - (issuer_key, NULL), dig, 0); + result = _gnutls_x509_write_sign_params(src, name, ¶ms); if (result < 0) { gnutls_assert(); return result; @@ -106,8 +168,7 @@ _gnutls_x509_pkix_sign(ASN1_TYPE src, const char *src_name, return result; } - result = - gnutls_privkey_sign_data(issuer_key, dig, 0, &tbs, &signature); + result = privkey_sign_data(issuer_key, &tbs, &signature, ¶ms); gnutls_free(tbs.data); if (result < 0) { @@ -132,9 +193,8 @@ _gnutls_x509_pkix_sign(ASN1_TYPE src, const char *src_name, * the same. */ - result = _gnutls_x509_write_sig_params(src, "signatureAlgorithm", - gnutls_privkey_get_pk_algorithm - (issuer_key, NULL), dig, 0); + result = _gnutls_x509_write_sign_params(src, "signatureAlgorithm", + ¶ms); if (result < 0) { gnutls_assert(); return result; diff --git a/lib/x509/spki.c b/lib/x509/spki.c new file mode 100644 index 0000000000..af94ea4a2a --- /dev/null +++ b/lib/x509/spki.c @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2017 Red Hat, Inc. + * + * Authors: Daiki Ueno + * + * This file is part of GnuTLS. + * + * The GnuTLS is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 of + * the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +#include "gnutls_int.h" +#include "errors.h" +#include <common.h> +#include <x509.h> +#include <x509_int.h> + +/** + * gnutls_x509_spki_init: + * @spki: A pointer to the type to be initialized + * + * This function will initialize a SubjectPublicKeyInfo structure used + * in PKIX. The structure is used to set additional parameters + * in the public key information field of a certificate. + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + * + **/ +int +gnutls_x509_spki_init(gnutls_x509_spki_t *spki) +{ + gnutls_x509_spki_t tmp; + + FAIL_IF_LIB_ERROR; + + tmp = + gnutls_calloc(1, sizeof(gnutls_x509_spki_st)); + + if (!tmp) + return GNUTLS_E_MEMORY_ERROR; + + *spki = tmp; + + return 0; /* success */ +} + +/** + * gnutls_x509_spki_deinit: + * @spki: the SubjectPublicKeyInfo structure + * + * This function will deinitialize a SubjectPublicKeyInfo structure. + * + * Since: 3.6.0 + * + **/ +void +gnutls_x509_spki_deinit(gnutls_x509_spki_t spki) +{ + gnutls_free(spki); +} + +/** + * gnutls_x509_spki_set_pk_algorithm: + * @spki: the SubjectPublicKeyInfo structure + * @pk: the public key algorithm of type #gnutls_pk_algorithm_t + * + * This function will set the public key algorithm of a + * SubjectPublicKeyInfo structure. + * + * Since: 3.6.0 + * + **/ +void +gnutls_x509_spki_set_pk_algorithm(gnutls_x509_spki_t spki, + gnutls_pk_algorithm_t pk) +{ + spki->pk = pk; +} + +/** + * gnutls_x509_spki_get_pk_algorithm: + * @spki: the SubjectPublicKeyInfo structure + * + * This function will get the public key algorithm of a + * SubjectPublicKeyInfo structure. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or %GNUTLS_PK_UNKNOWN on error. + * + * Since: 3.6.0 + * + **/ +int +gnutls_x509_spki_get_pk_algorithm(gnutls_x509_spki_t spki) +{ + return spki->pk; +} + +/** + * gnutls_x509_spki_set_digest_algorithm: + * @spki: the SubjectPublicKeyInfo structure + * @dig: the digest algorithm of type #gnutls_digest_algorithm_t + * + * This function will set the digest algorithm of a + * SubjectPublicKeyInfo structure. + * + * Since: 3.6.0 + * + **/ +void +gnutls_x509_spki_set_digest_algorithm(gnutls_x509_spki_t spki, + gnutls_digest_algorithm_t dig) +{ + spki->dig = dig; +} + +/** + * gnutls_x509_spki_get_digest_algorithm: + * @spki: the SubjectPublicKeyInfo structure + * + * This function will get the digest algorithm of a + * SubjectPublicKeyInfo structure. + * + * Returns: a member of the #gnutls_digest_algorithm_t enumeration on + * success, or a %GNUTLS_DIG_UNKNOWN on error. + * + * Since: 3.6.0 + * + **/ +int +gnutls_x509_spki_get_digest_algorithm(gnutls_x509_spki_t spki) +{ + return spki->dig; +} + +/** + * gnutls_x509_spki_set_salt_size: + * @spki: the SubjectPublicKeyInfo structure + * @salt_size: the size of salt string + * + * This function will set the salt size parameter of a + * SubjectPublicKeyInfo structure. + * + * The salt is used in the RSA-PSS signature scheme. + * + * Since: 3.6.0 + * + **/ +void +gnutls_x509_spki_set_salt_size(gnutls_x509_spki_t spki, + unsigned int salt_size) +{ + spki->salt_size = salt_size; +} + +/** + * gnutls_x509_spki_get_salt_size: + * @spki: the SubjectPublicKeyInfo structure + * + * This function will get the salt size parameter of a + * SubjectPublicKeyInfo structure. + * + * The salt is used in the RSA-PSS signature scheme. + * + * Returns: salt size as a positive integer, or zero. + * + * Since: 3.6.0 + * + **/ +int +gnutls_x509_spki_get_salt_size(gnutls_x509_spki_t spki) +{ + return spki->salt_size; +} diff --git a/lib/x509/verify.c b/lib/x509/verify.c index 041c22066b..e27c5dfdaa 100644 --- a/lib/x509/verify.c +++ b/lib/x509/verify.c @@ -579,6 +579,12 @@ typedef struct verify_state_st { out |= (x|GNUTLS_CERT_INVALID); \ result = 0; } +static int _gnutls_x509_verify_data(gnutls_sign_algorithm_t sign, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_x509_crt_t cert, + gnutls_x509_crt_t issuer); + /* * Verifies the given certificate against a certificate list of * trusted CAs. @@ -602,9 +608,8 @@ verify_crt(gnutls_x509_crt_t cert, gnutls_datum_t cert_signed_data = { NULL, 0 }; gnutls_datum_t cert_signature = { NULL, 0 }; gnutls_x509_crt_t issuer = NULL; - int issuer_version, hash_algo; + int issuer_version; unsigned result = 1; - const mac_entry_st * me; unsigned int out = 0, usage; int sigalg, ret; @@ -639,7 +644,7 @@ verify_crt(gnutls_x509_crt_t cert, ret = _gnutls_x509_get_signature_algorithm(cert->cert, - "signatureAlgorithm.algorithm"); + "signatureAlgorithm"); if (ret < 0) { MARK_INVALID(0); } @@ -733,22 +738,17 @@ verify_crt(gnutls_x509_crt_t cert, } } - if (sigalg >= 0) { - hash_algo = gnutls_sign_get_hash_algorithm(sigalg); - me = mac_to_entry(hash_algo); - } else { - me = NULL; - } - - if (me == NULL) { + if (sigalg < 0) { MARK_INVALID(0); } else if (cert_signed_data.data != NULL && - cert_signature.data != NULL) { + cert_signature.data != NULL) { ret = - _gnutls_x509_verify_data(me, + _gnutls_x509_verify_data(sigalg, &cert_signed_data, &cert_signature, + cert, issuer); + if (ret == GNUTLS_E_PK_SIG_VERIFY_FAILED) { MARK_INVALID(GNUTLS_CERT_SIGNATURE_FAILURE); } else if (ret < 0) { @@ -1273,6 +1273,45 @@ cleanup: } #endif +static int +_gnutls_x509_validate_sign_params(gnutls_pk_algorithm_t pk_algorithm, + ASN1_TYPE cert, + const char *name, + gnutls_x509_spki_st *sig_params) +{ + /* The signature parameter validation is only needed for RSA-PSS */ + if (pk_algorithm == GNUTLS_PK_RSA_PSS) { + int result; + gnutls_x509_spki_st params; + + result = _gnutls_x509_read_sign_params(cert, name, ¶ms); + if (result < 0) { + /* If parameters field is absent, no parameter + * validation is needed */ + if (result != GNUTLS_E_ASN1_ELEMENT_NOT_FOUND && + result != GNUTLS_E_ASN1_VALUE_NOT_FOUND) { + gnutls_assert(); + return result; + } + } else { + /* Check if the underlying hash algorithms are same. */ + if (sig_params->dig != params.dig) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + /* The salt length used to generate the + * signature must be equal to or larger than + * the one in the key parameter. */ + if (sig_params->salt_size < params.salt_size) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + } + } + return 0; +} + /* verifies if the certificate is properly signed. * returns GNUTLS_E_PK_VERIFY_SIG_FAILED on failure and 1 on success. * @@ -1280,33 +1319,71 @@ cleanup: * 'signature' is the signature! */ int -_gnutls_x509_verify_data(const mac_entry_st * me, +_gnutls_x509_verify_data(gnutls_sign_algorithm_t sign, const gnutls_datum_t * data, const gnutls_datum_t * signature, + gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer) { - gnutls_pk_params_st issuer_params; + gnutls_pk_params_st params; + gnutls_pk_algorithm_t issuer_pk; int ret; + gnutls_x509_spki_st sign_params; + const mac_entry_st * me; /* Read the MPI parameters from the issuer's certificate. */ - ret = _gnutls_x509_crt_get_mpis(issuer, &issuer_params); + ret = _gnutls_x509_crt_get_mpis(issuer, ¶ms); if (ret < 0) { gnutls_assert(); return ret; } - ret = - pubkey_verify_data(gnutls_x509_crt_get_pk_algorithm - (issuer, NULL), me, data, signature, - &issuer_params); + issuer_pk = gnutls_x509_crt_get_pk_algorithm(issuer, NULL); + + if (cert != NULL) { + ret = _gnutls_x509_read_sign_params(cert->cert, + "signatureAlgorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + + ret = _gnutls_x509_validate_sign_params(issuer_pk, + issuer->cert, + "tbsCertificate." + "subjectPublicKeyInfo." + "algorithm", + &sign_params); + if (ret < 0) { + gnutls_assert(); + goto cleanup; + } + } else { + memcpy(&sign_params, ¶ms.sign, + sizeof(gnutls_x509_spki_st)); + sign_params.pk = gnutls_sign_get_pk_algorithm(sign); + sign_params.dig = gnutls_sign_get_hash_algorithm(sign); + } + + me = hash_to_entry(sign_params.dig); + if (unlikely(me == NULL)) { + gnutls_assert(); + ret = GNUTLS_E_CERTIFICATE_ERROR; + goto cleanup; + } + + ret = pubkey_verify_data(sign_params.pk, me, data, signature, ¶ms, + &sign_params); if (ret < 0) { gnutls_assert(); } + cleanup: /* release all allocated MPIs */ - gnutls_pk_params_release(&issuer_params); + gnutls_pk_params_release(¶ms); return ret; } @@ -1477,7 +1554,7 @@ gnutls_x509_crl_verify(gnutls_x509_crl_t crl, gnutls_datum_t crl_signed_data = { NULL, 0 }; gnutls_datum_t crl_signature = { NULL, 0 }; gnutls_x509_crt_t issuer = NULL; - int result, hash_algo; + int result, sigalg; time_t now = gnutls_time(0); unsigned int usage; @@ -1507,18 +1584,16 @@ gnutls_x509_crl_verify(gnutls_x509_crl_t crl, goto cleanup; } - result = + sigalg = _gnutls_x509_get_signature_algorithm(crl->crl, - "signatureAlgorithm.algorithm"); - if (result < 0) { + "signatureAlgorithm"); + if (sigalg < 0) { gnutls_assert(); if (verify) *verify |= GNUTLS_CERT_INVALID; goto cleanup; } - hash_algo = gnutls_sign_get_hash_algorithm(result); - /* issuer is not in trusted certificate * authorities. */ @@ -1556,8 +1631,9 @@ gnutls_x509_crl_verify(gnutls_x509_crl_t crl, } result = - _gnutls_x509_verify_data(mac_to_entry(hash_algo), + _gnutls_x509_verify_data(sigalg, &crl_signed_data, &crl_signature, + NULL, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { gnutls_assert(); @@ -1576,8 +1652,6 @@ gnutls_x509_crl_verify(gnutls_x509_crl_t crl, } { - int sigalg; - sigalg = gnutls_x509_crl_get_signature_algorithm(crl); if (((sigalg == GNUTLS_SIGN_RSA_MD2) && diff --git a/lib/x509/x509.c b/lib/x509/x509.c index 93e79cb298..f346c93b3b 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -303,14 +303,14 @@ static int compare_sig_algorithm(gnutls_x509_crt_t cert) unsigned empty1 = 0, empty2 = 0; ret = _gnutls_x509_get_signature_algorithm(cert->cert, - "signatureAlgorithm.algorithm"); + "signatureAlgorithm"); if (ret < 0) { gnutls_assert(); return ret; } s2 = _gnutls_x509_get_signature_algorithm(cert->cert, - "tbsCertificate.signature.algorithm"); + "tbsCertificate.signature"); if (ret != s2) { _gnutls_debug_log("signatureAlgorithm.algorithm differs from tbsCertificate.signature.algorithm: %s, %s\n", gnutls_sign_get_name(ret), gnutls_sign_get_name(s2)); @@ -982,7 +982,7 @@ gnutls_x509_crt_get_dn_oid(gnutls_x509_crt_t cert, int gnutls_x509_crt_get_signature_algorithm(gnutls_x509_crt_t cert) { return _gnutls_x509_get_signature_algorithm(cert->cert, - "signatureAlgorithm.algorithm"); + "signatureAlgorithm"); } /** @@ -1548,6 +1548,37 @@ int gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert, unsigned int *bits) { + return gnutls_x509_crt_get_pk_algorithm2(cert, NULL, bits); +} + +/** + * gnutls_x509_crt_get_pk_algorithm2: + * @cert: a certificate of type #gnutls_x509_crt_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @bits: if bits is non null it will hold the size of the parameters' in bits + * + * This function will return the public key algorithm of an X.509 + * certificate. + * + * If @spki is non null, it should have enough size to hold the + * parameters. + * + * If @bits is non null, it should have enough size to hold the + * parameters size in bits. For RSA the bits returned is the modulus. + * For DSA the bits returned are of the public exponent. + * + * Unknown/unsupported algorithms are mapped to %GNUTLS_PK_UNKNOWN. + * + * Returns: a member of the #gnutls_pk_algorithm_t enumeration on + * success, or a negative error code on error. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crt_get_pk_algorithm2(gnutls_x509_crt_t cert, + gnutls_x509_spki_t spki, + unsigned int *bits) +{ int result; if (cert == NULL) { @@ -1568,8 +1599,24 @@ gnutls_x509_crt_get_pk_algorithm(gnutls_x509_crt_t cert, return result; } - return result; + if (spki) { + gnutls_x509_spki_st params; + spki->pk = result; + + result = _gnutls_x509_crt_read_sign_params(cert, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + spki->dig = params.dig; + spki->salt_size = params.salt_size; + + return spki->pk; + } + + return result; } /* returns the type and the name on success. @@ -4255,3 +4302,4 @@ void gnutls_x509_crt_set_flags(gnutls_x509_crt_t cert, { cert->flags = flags; } + diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 38f07d6ed8..7b2d38457d 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -156,8 +156,12 @@ int _gnutls_x509_get_tbs(ASN1_TYPE cert, const char *tbs_name, gnutls_datum_t * tbs); int _gnutls_x509_pkix_sign(ASN1_TYPE src, const char *src_name, gnutls_digest_algorithm_t, + unsigned int flags, gnutls_x509_crt_t issuer, gnutls_privkey_t issuer_key); +int _gnutls_x509_crt_get_sign_params(gnutls_x509_crt_t issuer, + const gnutls_x509_spki_st *key_params, + gnutls_x509_spki_st *params); /* dn.c */ #define OID_X520_COUNTRY_NAME "2.5.4.6" @@ -229,11 +233,6 @@ _gnutls_x509_verify_algorithm(gnutls_digest_algorithm_t * hash, gnutls_pk_algorithm_t pk, gnutls_pk_params_st * issuer_params); -int _gnutls_x509_verify_data(const mac_entry_st * me, - const gnutls_datum_t * data, - const gnutls_datum_t * signature, - gnutls_x509_crt_t issuer); - /* privkey.h */ void _gnutls_x509_privkey_reinit(gnutls_x509_privkey_t key); @@ -252,6 +251,19 @@ _gnutls_x509_read_ecc_params(uint8_t * der, int dersize, int _gnutls_asn1_encode_privkey(gnutls_pk_algorithm_t pk, ASN1_TYPE * c2, gnutls_pk_params_st * params, unsigned compat); +int _gnutls_x509_privkey_get_sign_params(gnutls_x509_privkey_t key, + gnutls_x509_spki_st * params); +int _gnutls_x509_privkey_find_sign_params(gnutls_x509_privkey_t key, + gnutls_pk_algorithm_t pk, + gnutls_digest_algorithm_t dig, + unsigned flags, + gnutls_x509_spki_st *params); + +int _gnutls_x509_read_rsa_pss_params(uint8_t * der, int dersize, + gnutls_x509_spki_st * params); +int _gnutls_x509_write_rsa_pss_params(gnutls_x509_spki_st * params, + gnutls_datum_t * der); + /* extensions.c */ int _gnutls_x509_crl_get_extension_oid(gnutls_x509_crl_t crl, int indx, void *oid, @@ -312,13 +324,24 @@ int _gnutls_x509_crq_get_mpis(gnutls_x509_crq_t cert, int _gnutls_x509_crt_get_mpis(gnutls_x509_crt_t cert, gnutls_pk_params_st * params); +int _gnutls_x509_crt_read_sign_params(gnutls_x509_crt_t crt, + gnutls_x509_spki_st *params); +int _gnutls_x509_crq_read_sign_params(gnutls_x509_crq_t crt, + gnutls_x509_spki_st *params); + int _gnutls_x509_read_pubkey_params(gnutls_pk_algorithm_t, uint8_t * der, int dersize, gnutls_pk_params_st * params); +int _gnutls_x509_check_pubkey_params(gnutls_pk_algorithm_t algo, + gnutls_pk_params_st * params); int _gnutls_x509_read_pubkey(gnutls_pk_algorithm_t, uint8_t * der, int dersize, gnutls_pk_params_st * params); +int _gnutls_x509_read_pubkey_signature_params(gnutls_pk_algorithm_t algo, + uint8_t * der, int dersize, + gnutls_pk_params_st * params); + int _gnutls_x509_write_ecc_params(gnutls_ecc_curve_t curve, gnutls_datum_t * der); int _gnutls_x509_write_ecc_pubkey(gnutls_pk_params_st * params, @@ -352,9 +375,10 @@ int _gnutls_x509_read_key_int(ASN1_TYPE node, const char *value, int _gnutls_x509_write_key_int(ASN1_TYPE node, const char *value, bigint_t mpi, int lz); -int _gnutls_x509_write_sig_params(ASN1_TYPE dst, const char *dst_name, - gnutls_pk_algorithm_t pk_algorithm, - gnutls_digest_algorithm_t, unsigned legacy); +int _gnutls_x509_read_sign_params(ASN1_TYPE src, const char *src_name, + gnutls_x509_spki_st *params); +int _gnutls_x509_write_sign_params(ASN1_TYPE dst, const char *dst_name, + gnutls_x509_spki_st *params); /* pkcs12.h */ #include <gnutls/pkcs12.h> diff --git a/lib/x509/x509_write.c b/lib/x509/x509_write.c index 70e9cc0b1d..da12905c71 100644 --- a/lib/x509/x509_write.c +++ b/lib/x509/x509_write.c @@ -35,6 +35,7 @@ #include <x509_b64.h> #include "x509_int.h" #include <libtasn1.h> +#include <pk.h> static void disable_optional_stuff(gnutls_x509_crt_t cert); @@ -1815,7 +1816,7 @@ gnutls_x509_crt_privkey_sign(gnutls_x509_crt_t crt, } result = _gnutls_x509_pkix_sign(crt->cert, "tbsCertificate", - dig, issuer, issuer_key); + dig, flags, issuer, issuer_key); if (result < 0) { gnutls_assert(); return result; @@ -1989,3 +1990,102 @@ gnutls_x509_crt_set_policy(gnutls_x509_crt_t crt, return ret; } +/** + * gnutls_x509_crt_set_pk_algorithm: + * @crt: a certificate of type #gnutls_x509_crt_t + * @spki: a SubjectPublicKeyInfo structure of type #gnutls_x509_spki_t + * @flags: must be zero + * + * This function will set the certificate's subject public key + * information explicitly. This is intended to be used in the cases + * where a single public key (e.g., RSA) can be used for multiple + * signature algorithms (RSA PKCS1-1.5, and RSA-PSS). + * + * To export the public key (i.e., the SubjectPublicKeyInfo part), check + * gnutls_pubkey_import_x509(). + * + * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise a + * negative error value. + * + * Since: 3.6.0 + **/ +int +gnutls_x509_crt_set_pk_algorithm(gnutls_x509_crt_t crt, + gnutls_x509_spki_t spki, + unsigned int flags) +{ + int result; + gnutls_pk_algorithm_t crt_pk; + gnutls_x509_spki_st params; + unsigned bits; + + if (crt == NULL) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + result = gnutls_x509_crt_get_pk_algorithm(crt, &bits); + if (result < 0) { + gnutls_assert(); + return result; + } + + crt_pk = result; + + if (spki->pk != GNUTLS_PK_RSA_PSS) { + if (crt_pk == spki->pk) + return 0; + + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + if (crt_pk == GNUTLS_PK_RSA) { + const mac_entry_st *me; + + me = hash_to_entry(spki->dig); + if (unlikely(me == NULL)) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + memset(¶ms, 0, sizeof(gnutls_x509_spki_st)); + params.pk = spki->pk; + params.dig = spki->dig; + + /* If salt size is zero, find the optimal salt size. */ + if (spki->salt_size == 0) { + params.salt_size = + _gnutls_find_rsa_pss_salt_size(bits, me, + spki->salt_size); + } else + params.salt_size = spki->salt_size; + } else if (crt_pk == GNUTLS_PK_RSA_PSS) { + result = _gnutls_x509_crt_read_sign_params(crt, ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + if (params.dig != spki->dig || + params.salt_size > spki->salt_size) { + gnutls_assert(); + return GNUTLS_E_INVALID_REQUEST; + } + + params.salt_size = spki->salt_size; + } + + MODIFIED(crt); + + result = _gnutls_x509_write_sign_params(crt->cert, + "tbsCertificate." + "subjectPublicKeyInfo.algorithm", + ¶ms); + if (result < 0) { + gnutls_assert(); + return result; + } + + return 0; +} |