diff options
50 files changed, 2449 insertions, 189 deletions
diff --git a/doc/credentials/x509/cert-rsa-pss.pem b/doc/credentials/x509/cert-rsa-pss.pem new file mode 100644 index 0000000000..8354a45d71 --- /dev/null +++ b/doc/credentials/x509/cert-rsa-pss.pem @@ -0,0 +1,26 @@ +-----BEGIN CERTIFICATE----- +MIIEUTCCAtmgAwIBAgIIWP3fgCMnlg4wPQYJKoZIhvcNAQEKMDCgDTALBglghkgB +ZQMEAgGhGjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCASAwGTEXMBUGA1UE +AxMOR251VExTIFRlc3QgQ0EwIBcNMTcwNDI0MTEyMDM1WhgPOTk5OTEyMzEyMzU5 +NTlaMDMxMTAvBgNVBAMTKEdudVRMUyBUZXN0IFNlcnZlciAoUlNBLVBTUyBjZXJ0 +aWZpY2F0ZSkwggGCMD0GCSqGSIb3DQEBCjAwoA0wCwYJYIZIAWUDBAIBoRowGAYJ +KoZIhvcNAQEIMAsGCWCGSAFlAwQCAaIDAgEgA4IBPwAwggE6AoIBMQC0ayeYJa/B +/x7KsH702LztQ4ZnVF3atB7CkF+DPAIR/BNyhbKIpGVBC3ZfI76Kn/55S3M7LsdL +PL8WyZdVNRfzoXJLMMLgJ5QS81YA5s6CSxFdpB6b+vq5GypNGLW6peYMx6iooW2q +iITclg6ybBw1qufHlD351cfCog1Ls2569whfxQnNFZMa95jfKkxmiSTtH9AWY4Fl +pVg7oc0lYpuZgVQIFxjsfC8IojsoVzKdF0cKhvtisUGZ5vveqOogfvMb7rrqmiFk +KZLyrXPlGQWdN1PiEZ8YXyK64osNAIyeL6eHPUC+SqKlkggMLmHAWHyameHWrIM5 +Jc8+G+3ro22dy8U43sHHbps0FL4wPoKQHrlKmnbk7zMMRqIxcvbDYQv4qmeJ9KXl +djehKZ+Aeap1AgMBAAGjgZAwgY0wDwYDVR0TAQH/BAUwAwEB/zAUBgNVHREEDTAL +gglsb2NhbGhvc3QwEwYDVR0lBAwwCgYIKwYBBQUHAwEwDwYDVR0PAQH/BAUDAweg +ADAdBgNVHQ4EFgQUSCM0UwqJMThKWurKttKm3s4dKxgwHwYDVR0jBBgwFoAUTVa3 +agBY8WeS9KZ1VRuOUwED788wPQYJKoZIhvcNAQEKMDCgDTALBglghkgBZQMEAgGh +GjAYBgkqhkiG9w0BAQgwCwYJYIZIAWUDBAIBogMCASADggExAHk5sqHOGPZPGMHM +ZTseLwXOHmQUhd4B4dczk0F3Dd+/QI0EE3gn3Mm3Flhs4QeqV7oNtgjDjLTbmGzY +qz0kecBfN83JYT/WtTHQTXdHXc2QT8GD5kO9jgRcHcnNbA+B+JFhFU2JJoAO/GlS +LmqW+dYWZmBLZcYM7k3xebytPsik/cDVLqAC/TosubYcfRtXaELWKrXio750nw4f +YPIgvP2wavXZAP5nlZxua5CH/zfswQ+ztf6J1UKK5nk6yfAQp/zWm2wCv4OuwaAF +E4Kppl2/hj6IkMsCRKO3FrlLAPHDhBJWE/LFm7boRWM9CeBHTgQ2afq7Zz0Jl3Pn +IfQuo71L8sl4BXKAuChOl5XukPqKr7V+d+JiCHijNJoOjj+gR2bKTv14kKaP6+7u ++Pdg/c4= +-----END CERTIFICATE----- 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; +} diff --git a/src/certtool-args.def b/src/certtool-args.def index f7889ccde6..ba5a7f0eeb 100644 --- a/src/certtool-args.def +++ b/src/certtool-args.def @@ -458,6 +458,18 @@ flag = { }; flag = { + name = rsa-pss; + descrip = "Generate RSA-PSS key or certificate"; + doc = "This option can be combined with --generate-privkey or --generate-certificate, to generate private key or certificate restricted to the use with the RSA-PSS padding scheme."; +}; + +flag = { + name = rsa-pss-sign; + descrip = "Sign certificate with RSA-PSS"; + doc = "This option can be combined with --generate-certificate, to sign the certificate with the RSA-PSS padding scheme."; +}; + +flag = { name = hash; arg-type = string; descrip = "Hash algorithm to use for signing"; diff --git a/src/certtool-common.c b/src/certtool-common.c index 9c223ec94d..b48ff743e8 100644 --- a/src/certtool-common.c +++ b/src/certtool-common.c @@ -1110,24 +1110,46 @@ static void privkey_info_int(FILE *outfile, common_info_st * cinfo, unsigned int bits = 0; size_t size; const char *cprint; + gnutls_x509_spki_t spki; /* Public key algorithm */ fprintf(outfile, "Public Key Info:\n"); - ret = gnutls_x509_privkey_get_pk_algorithm2(key, &bits); + ret = gnutls_x509_spki_init(&spki); + if (ret < 0) { + fprintf(stderr, "spki_init: %s\n", gnutls_strerror(ret)); + } + ret = gnutls_x509_privkey_get_pk_algorithm3(key, spki, &bits); fprintf(outfile, "\tPublic Key Algorithm: "); key_type = ret; cprint = gnutls_pk_algorithm_get_name(key_type); fprintf(outfile, "%s\n", cprint ? cprint : "Unknown"); + if (spki && key_type == GNUTLS_PK_RSA_PSS) { + ret = gnutls_x509_spki_get_digest_algorithm(spki); + if (ret < 0) { + fprintf(stderr, "spki_get_digest_algorithm: %s\n", + gnutls_strerror(ret)); + } else { + fprintf(outfile, "\t\tHash Algorithm: %s\n", + gnutls_digest_get_name(ret)); + } + ret = gnutls_x509_spki_get_salt_size(spki); + if (ret < 0) { + fprintf(stderr, "spki_get_salt_size: %s\n", + gnutls_strerror(ret)); + } else + fprintf(outfile, "\t\tSalt Length: %d\n", ret); + } + gnutls_x509_spki_deinit(spki); fprintf(outfile, "\tKey Security Level: %s (%u bits)\n\n", gnutls_sec_param_get_name(gnutls_x509_privkey_sec_param (key)), bits); /* Print the raw public and private keys */ - if (key_type == GNUTLS_PK_RSA) { + if (GNUTLS_PK_IS_RSA(key_type)) { gnutls_datum_t m, e, d, p, q, u, exp1, exp2; ret = diff --git a/src/certtool-common.h b/src/certtool-common.h index e48396029f..91a4c6d1e9 100644 --- a/src/certtool-common.h +++ b/src/certtool-common.h @@ -73,6 +73,8 @@ typedef struct common_info { /* set to non zero when no compatibility structs need to be exported */ unsigned no_compat; + + unsigned rsa_pss_sign; } common_info_st; /* this must be provided by the app */ @@ -142,4 +144,6 @@ void fix_lbuffer(unsigned long); void decode_seed(gnutls_datum_t *seed, const char *hex, unsigned hex_size); +#define GNUTLS_PK_IS_RSA(pk) ((pk) == GNUTLS_PK_RSA || (pk) == GNUTLS_PK_RSA_PSS) + #endif diff --git a/src/certtool.c b/src/certtool.c index 1a8ccf8a0c..5f08937cc9 100644 --- a/src/certtool.c +++ b/src/certtool.c @@ -86,6 +86,7 @@ static void verify_certificate(common_info_st * cinfo); static void pubkey_keyid(common_info_st * cinfo); static void certificate_fpr(common_info_st * cinfo); +static gnutls_digest_algorithm_t get_dig(gnutls_x509_crt_t crt); FILE *outfile; static const char *outfile_name = NULL; /* to delete on exit */ @@ -165,7 +166,7 @@ generate_private_key_int(common_info_st * cinfo) bits, gnutls_pk_algorithm_get_name(key_type)); } - if (provable && (key_type != GNUTLS_PK_RSA && key_type != GNUTLS_PK_DSA)) { + if (provable && (!GNUTLS_PK_IS_RSA(key_type) && key_type != GNUTLS_PK_DSA)) { fprintf(stderr, "The --provable parameter cannot be used with ECDSA keys.\n"); app_exit(1); @@ -175,7 +176,7 @@ generate_private_key_int(common_info_st * cinfo) fprintf(stderr, "Note that DSA keys with size over 1024 may cause incompatibility problems when used with earlier than TLS 1.2 versions.\n\n"); - if ((HAVE_OPT(SEED) || provable) && key_type == GNUTLS_PK_RSA) { + if ((HAVE_OPT(SEED) || provable) && GNUTLS_PK_IS_RSA(key_type)) { if (bits != 2048 && bits != 3072) { fprintf(stderr, "Note that the FIPS 186-4 key generation restricts keys to 2048 and 3072 bits\n"); } @@ -188,7 +189,7 @@ generate_private_key_int(common_info_st * cinfo) data.data = (void*)cinfo->seed; data.size = cinfo->seed_size; - if (key_type == GNUTLS_PK_RSA) { + if (GNUTLS_PK_IS_RSA(key_type)) { if ((bits == 3072 && cinfo->seed_size != 32) || (bits == 2048 && cinfo->seed_size != 28)) { fprintf(stderr, "The seed size (%d) doesn't match the size of the request security level; use -d 2 for more information.\n", (int)cinfo->seed_size); } @@ -200,9 +201,14 @@ generate_private_key_int(common_info_st * cinfo) ret = gnutls_x509_privkey_generate2(key, key_type, bits, GNUTLS_PRIVKEY_FLAG_PROVABLE, &data, 1); } else { + gnutls_keygen_data_st data; + + data.type = GNUTLS_KEYGEN_DIGEST; + data.size = default_dig; + if (provable) flags |= GNUTLS_PRIVKEY_FLAG_PROVABLE; - ret = gnutls_x509_privkey_generate(key, key_type, bits, flags); + ret = gnutls_x509_privkey_generate2(key, key_type, bits, flags, &data, 1); } if (ret < 0) { fprintf(stderr, "privkey_generate: %s\n", @@ -672,6 +678,30 @@ generate_certificate(gnutls_privkey_t * ret_key, app_exit(1); } + /* Algorithm restriction. + */ + if (req_key_type == GNUTLS_PK_RSA_PSS) { + gnutls_x509_spki_t spki; + + result = gnutls_x509_spki_init(&spki); + if (result < 0) { + fprintf(stderr, "spki_init: %s\n", + gnutls_strerror(result)); + app_exit(1); + } + + gnutls_x509_spki_set_pk_algorithm(spki, GNUTLS_PK_RSA_PSS); + gnutls_x509_spki_set_digest_algorithm(spki, get_dig(crt)); + + result = gnutls_x509_crt_set_pk_algorithm(crt, spki, 0); + gnutls_x509_spki_deinit(spki); + if (result < 0) { + fprintf(stderr, "error setting signing algorithm: %s\n", + gnutls_strerror(result)); + app_exit(1); + } + } + *ret_key = key; return crt; @@ -840,6 +870,7 @@ void generate_self_signed(common_info_st * cinfo) gnutls_privkey_t key; size_t size; int result; + unsigned int flags = 0; fprintf(stdlog, "Generating a self signed certificate...\n"); @@ -854,8 +885,11 @@ void generate_self_signed(common_info_st * cinfo) fprintf(stdlog, "\n\nSigning certificate...\n"); + if (cinfo->rsa_pss_sign) + flags |= GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS; + result = - gnutls_x509_crt_privkey_sign(crt, crt, key, get_dig(crt), 0); + gnutls_x509_crt_privkey_sign(crt, crt, key, get_dig(crt), flags); if (result < 0) { fprintf(stderr, "crt_sign: %s\n", gnutls_strerror(result)); app_exit(1); @@ -883,6 +917,7 @@ static void generate_signed_certificate(common_info_st * cinfo) int result; gnutls_privkey_t ca_key; gnutls_x509_crt_t ca_crt; + unsigned int flags = 0; fprintf(stdlog, "Generating a signed certificate...\n"); @@ -901,9 +936,12 @@ static void generate_signed_certificate(common_info_st * cinfo) fprintf(stdlog, "\n\nSigning certificate...\n"); + if (cinfo->rsa_pss_sign) + flags |= GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS; + result = gnutls_x509_crt_privkey_sign(crt, ca_crt, ca_key, - get_dig(ca_crt), 0); + get_dig(ca_crt), flags); if (result < 0) { fprintf(stderr, "crt_sign: %s\n", gnutls_strerror(result)); app_exit(1); @@ -931,6 +969,7 @@ static void generate_proxy_certificate(common_info_st * cinfo) gnutls_privkey_t key, eekey; size_t size; int result; + unsigned int flags = 0; fprintf(stdlog, "Generating a proxy certificate...\n"); @@ -943,9 +982,12 @@ static void generate_proxy_certificate(common_info_st * cinfo) fprintf(stdlog, "\n\nSigning certificate...\n"); + if (cinfo->rsa_pss_sign) + flags = GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS; + result = gnutls_x509_crt_privkey_sign(crt, eecrt, eekey, get_dig(eecrt), - 0); + flags); if (result < 0) { fprintf(stderr, "crt_sign: %s\n", gnutls_strerror(result)); app_exit(1); @@ -1005,6 +1047,7 @@ static void update_signed_certificate(common_info_st * cinfo) gnutls_privkey_t ca_key; gnutls_x509_crt_t ca_crt; time_t tim; + unsigned int flags = 0; fprintf(stdlog, "Generating a signed certificate...\n"); @@ -1033,9 +1076,12 @@ static void update_signed_certificate(common_info_st * cinfo) fprintf(stderr, "\n\nSigning certificate...\n"); + if (cinfo->rsa_pss_sign) + flags = GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS; + result = gnutls_x509_crt_privkey_sign(crt, ca_crt, ca_key, - get_dig(ca_crt), 0); + get_dig(ca_crt), flags); if (result < 0) { fprintf(stderr, "crt_sign: %s\n", gnutls_strerror(result)); app_exit(1); @@ -1116,6 +1162,8 @@ static void cmd_parser(int argc, char **argv) req_key_type = GNUTLS_PK_DSA; else if (HAVE_OPT(ECC)) req_key_type = GNUTLS_PK_ECC; + else if (HAVE_OPT(RSA_PSS)) + req_key_type = GNUTLS_PK_RSA_PSS; else req_key_type = GNUTLS_PK_RSA; @@ -1279,6 +1327,9 @@ static void cmd_parser(int argc, char **argv) cinfo.password = ""; } + if (HAVE_OPT(RSA_PSS_SIGN)) + cinfo.rsa_pss_sign = 1; + if (HAVE_OPT(GENERATE_SELF_SIGNED)) generate_self_signed(&cinfo); else if (HAVE_OPT(GENERATE_CERTIFICATE)) diff --git a/tests/cert-tests/Makefile.am b/tests/cert-tests/Makefile.am index e155fef509..90641679fb 100644 --- a/tests/cert-tests/Makefile.am +++ b/tests/cert-tests/Makefile.am @@ -76,7 +76,7 @@ EXTRA_DIST = data/ca-no-pathlen.pem data/no-ca-or-pathlen.pem data/aki-cert.pem data/invalid-date-secs.der data/invalid-date-month.der data/invalid-date-day.der \ data/mem-leak.p12 data/alt-chain-new-ca.pem data/alt-chain-old-ca.pem \ data/alt-chain.pem data/pkcs7-chain.pem data/pkcs7-chain-root.pem \ - data/pkcs7-chain-endcert-key.pem + data/pkcs7-chain-endcert-key.pem data/cert-rsa-pss.pem dist_check_SCRIPTS = pathlen aki certtool invalid-sig email \ pkcs7 pkcs7-broken-sigs privkey-import name-constraints certtool-long-cn crl provable-privkey \ @@ -104,13 +104,15 @@ dist_check_SCRIPTS += certtool-utf8 crq if !WINDOWS dist_check_SCRIPTS += template-test pem-decoding othername-test krb5-test sha3-test md5-test \ - tlsfeature-test template-exts-test pkcs1-pad pkcs12-utf8 + tlsfeature-test template-exts-test pkcs1-pad pkcs12-utf8 rsa-pss-pad endif if ENABLE_DANE dist_check_SCRIPTS += dane endif +dist_check_SCRIPTS += certtool-rsa-pss + TESTS = $(dist_check_SCRIPTS) # Set detect_leaks=0 to ASAN. It seems it is detecting many leaks in tools diff --git a/tests/cert-tests/certtool-rsa-pss b/tests/cert-tests/certtool-rsa-pss new file mode 100755 index 0000000000..a4c65dc0b7 --- /dev/null +++ b/tests/cert-tests/certtool-rsa-pss @@ -0,0 +1,125 @@ +#!/bin/sh + +# Copyright (C) 2014 Nikos Mavrogiannopoulos +# +# This file is part of GnuTLS. +# +# GnuTLS is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at +# your option) any later version. +# +# GnuTLS 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GnuTLS; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#set -e + +srcdir="${srcdir:-.}" +CERTTOOL="${CERTTOOL:-../../src/certtool${EXEEXT}}" +DIFF="${DIFF:-diff -b -B}" +OUTFILE=pss-privkey.tmp +TMPFILE=pss.$$.tmp + +if ! test -x "${CERTTOOL}"; then + exit 77 +fi + +if ! test -z "${VALGRIND}"; then + VALGRIND="${LIBTOOL:-libtool} --mode=execute ${VALGRIND}" +fi + +for i in sha256 sha384 sha512;do +if test "${GNUTLS_FORCE_FIPS_MODE}" = 1 && test "$i" != sha384;then + continue +fi + +# Create an RSA-PSS private key, restricted to the use with RSA-PSS +${VALGRIND} "${CERTTOOL}" --generate-privkey --pkcs8 --password '' \ + --rsa-pss --hash $i --outfile "$OUTFILE" +rc=$? + +if test "${rc}" != "0"; then + echo "Could not generate an RSA-PSS key ($i)" + exit 1 +fi + +# Create an RSA-PSS certificate from an RSA-PSS private key +${VALGRIND} "${CERTTOOL}" --generate-self-signed \ + --pkcs8 --load-privkey "$OUTFILE" --password '' \ + --template "${srcdir}/templates/template-test.tmpl" \ + --outfile "${TMPFILE}" --hash $i 2>/dev/null +rc=$? + +if test "${rc}" != "0"; then + echo "Could not generate an RSA-PSS certificate from an RSA-PSS key ($i)" + exit 1 +fi + +# Create an RSA-PSS certificate from an RSA-PSS private key, with +# mismatched parameters +for j in sha256 sha384 sha512;do +${VALGRIND} "${CERTTOOL}" --generate-self-signed \ + --pkcs8 --load-privkey "$OUTFILE" --password '' \ + --template "${srcdir}/templates/template-test.tmpl" \ + --outfile "${TMPFILE}" --hash $j 2>/dev/null +rc=$? + +if test "$j" != "$j" && "${rc}" = "0"; then + echo "Unexpectedly succeeded to generate an RSA-PSS certificate ($j != $i)" + exit 1 +fi +done + +# Create an RSA-PSS certificate from an RSA key +${VALGRIND} "${CERTTOOL}" --generate-certificate --rsa-pss \ + --load-privkey "${srcdir}/../../doc/credentials/x509/key-rsa.pem" \ + --load-ca-privkey "${srcdir}/../../doc/credentials/x509/ca-key.pem" \ + --load-ca-certificate "${srcdir}/../../doc/credentials/x509/ca.pem" \ + --template "${srcdir}/templates/template-test.tmpl" \ + --outfile "${TMPFILE}" --hash $i 2>/dev/null +rc=$? + +if test "${rc}" != "0"; then + echo "Could not generate an RSA-PSS certificate $i" + exit 1 +fi + +# Create an RSA certificate from an RSA key, and sign it with RSA-PSS +${VALGRIND} "${CERTTOOL}" --generate-certificate --rsa --rsa-pss-sign \ + --load-privkey "${srcdir}/../../doc/credentials/x509/key-rsa.pem" \ + --load-ca-privkey "${srcdir}/../../doc/credentials/x509/ca-key.pem" \ + --load-ca-certificate "${srcdir}/../../doc/credentials/x509/ca.pem" \ + --template "${srcdir}/templates/template-test.tmpl" \ + --outfile "${TMPFILE}" --hash $i 2>/dev/null +rc=$? + +if test "${rc}" != "0"; then + echo "Could not generate an RSA-PSS certificate" + exit 1 +fi +done + +export TZ="UTC" + +. ${srcdir}/../scripts/common.sh + +check_for_datefudge + +datefudge "2012-11-22" \ +${VALGRIND} "${CERTTOOL}" --verify --load-ca-certificate "${srcdir}/data/cert-rsa-pss.pem" --infile "${srcdir}/data/cert-rsa-pss.pem" +rc=$? + +if test "${rc}" != "0"; then + echo "There was an issue verifying the certificate" + exit 1 +fi + +rm -f "${TMPFILE}" + +exit 0 diff --git a/tests/cert-tests/data/cert-rsa-pss.pem b/tests/cert-tests/data/cert-rsa-pss.pem new file mode 100644 index 0000000000..ffaee16c20 --- /dev/null +++ b/tests/cert-tests/data/cert-rsa-pss.pem @@ -0,0 +1,29 @@ +-----BEGIN CERTIFICATE----- +MIIE8jCCAyagAwIBAgIBdDBBBgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAQUA +oRwwGgYJKoZIhvcNAQEIMA0GCWCGSAFlAwQCAQUAogMCASAwVzELMAkGA1UEBhMC +Q1oxFzAVBgNVBAoMDkN6ZWNoIFJlcHVibGljMR0wGwYDVQQLDBRNaW5pc3RyeSBv +ZiBJbnRlcmlvcjEQMA4GA1UEAwwHQ1NDQV9DWjAeFw0xMTAzMjUwMDAwMDBaFw0y +NjA2MjUwMDAwMDBaMFcxCzAJBgNVBAYTAkNaMRcwFQYDVQQKDA5DemVjaCBSZXB1 +YmxpYzEdMBsGA1UECwwUTWluaXN0cnkgb2YgSW50ZXJpb3IxEDAOBgNVBAMMB0NT +Q0FfQ1owggGiMA0GCSqGSIb3DQEBAQUAA4IBjwAwggGKAoIBgQCdFhq+ScQXepAA +0kp0IwF/nEv+Md3Wx41Y6WRJkTVVyU6CFPlvr/F6XLGX/ILJtM8QL97CgojDVQbE +ccNxUsZ+yjtB4n52ttWYLBN5nktJksP2aBVMu1fqoyTqBhaf0JtkpQjWKNVJYbUH +k2pXkzGUJ+YHG04jOEYIKNclY82f1Ho1Wd7di4gZ1LCBRTzIU9JVPEMOZdmLx1qQ +InOWf8deZ4Gmkj5UqzXt7vGQF/TbIedhmfnbulvyHN3UEDMZgVgzkF2fIOaBwu5s +kfo8e08/J155hZIJtUjXk/moLF8U/4ETo7qER1EkoJ9KIRIvoPwPk2QAI4JP0Hd6 +GgUMEsz0dmTxKHcJZAWXcDaKcwTrG7/xhPTAffdOZnTnOpYQPcKmjUvogqY839VQ +mwYyo+s51tVrAIe2YcdHhIdBY5SLOhHsDNadpRrBYIa8vzUgtfUH50US7dZuwUu6 +WbUiQu5vmDjvGEUa7F6eehCCf9xXkbPJZoE63t/NJYvHSFa1wDcCAwEAAaNhMF8w +HQYDVR0OBBYEFOuhT488aYrathCbEjUoztRlSghZMA4GA1UdDwEB/wQEAwIBBjAa +BgNVHSAEEzARMA8GDSqBS7cYAQEBAYnKmXwwEgYDVR0TAQH/BAgwBgEB/wIBADBB +BgkqhkiG9w0BAQowNKAPMA0GCWCGSAFlAwQCAQUAoRwwGgYJKoZIhvcNAQEIMA0G +CWCGSAFlAwQCAQUAogMCASADggGBAEcppLLH0xAkWXbBMXDa1hwqOZfKVdoUVDNT +G20V3VbDT74R2yFCLWBge9rr7S2LfF/w4Xrl3kyZ2Tn0dYoOmBcqVzH6nCYrniGx +apPmSkAexI/bjW55BOMe6CVI9qdKayqm99om/2+R+VSAKaopwOKn9IQ+4itNA0VI +0ZDHbI/pdJClRZ0OPA8DREz7+hZWMNb7S4aAYGdd1fLo0uDKf/siFkUUfkpBtkgr +4Enb1e8NMU0a5abpI25NgURB+OWhfArLB9jFmnlql6TZADLZfw49bEmr4KOddxyk +toBmFgu4/uQXcukNWB487iREvEPPIL7A90W+W3ymtlol76SChKKBGexaaNA1JW+O +sRxoN5FnXTBhyRm96lZOfvF7SjCQHMCaIeYaQ4ajjPJgxB0EbyyJMeSrjh4yJPEH +pKc3AM9MUCxC0LO/qb5oBio8TQqHYnpyvp2CsvPSezPGh/40hxsKTL8GqTxB5m9G +g6qpdcDQxM6VtrnTh08nMjaKnfXVJQ== +-----END CERTIFICATE----- diff --git a/tests/cert-tests/pkcs7 b/tests/cert-tests/pkcs7 index 8267d2e33d..f32dc46766 100755 --- a/tests/cert-tests/pkcs7 +++ b/tests/cert-tests/pkcs7 @@ -261,6 +261,24 @@ if test "${rc}" != "0"; then exit ${rc} fi +FILE="rsa-pss-signing" +${VALGRIND} "${CERTTOOL}" --p7-sign --load-privkey "${srcdir}/../../doc/credentials/x509/key-rsa.pem" --load-certificate "${srcdir}/../../doc/credentials/x509/cert-rsa-pss.pem" --infile "${srcdir}/data/pkcs7-detached.txt" >"${OUTFILE}" +rc=$? + +if test "${rc}" != "0"; then + echo "${FILE}: PKCS7 struct signing failed" + exit ${rc} +fi + +FILE="rsa-pss-signing-verify" +${VALGRIND} "${CERTTOOL}" --p7-verify --load-certificate "${srcdir}/../../doc/credentials/x509/cert-rsa-pss.pem" <"${OUTFILE}" +rc=$? + +if test "${rc}" != "0"; then + echo "${FILE}: PKCS7 struct signing failed verification" + exit ${rc} +fi + rm -f "${OUTFILE}" rm -f "${OUTFILE2}" diff --git a/tests/cert-tests/rsa-pss-pad b/tests/cert-tests/rsa-pss-pad new file mode 100755 index 0000000000..8129d9adcb --- /dev/null +++ b/tests/cert-tests/rsa-pss-pad @@ -0,0 +1,74 @@ +#!/bin/sh + +# Copyright (C) 2006-2012 Free Software Foundation, Inc. +# +# This file is part of GnuTLS. +# +# GnuTLS is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation; either version 3 of the License, or (at +# your option) any later version. +# +# GnuTLS 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 +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with GnuTLS; if not, write to the Free Software Foundation, +# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#set -e + +srcdir="${srcdir:-.}" +CERTTOOL="${CERTTOOL:-../../src/certtool${EXEEXT}}" +DIFF="${DIFF:-diff}" +TMPFILE=pss.$$.tmp + +if ! test -x "${CERTTOOL}"; then + exit 77 +fi + +export TZ="UTC" + +. ${srcdir}/../scripts/common.sh + +check_for_datefudge + +# Note that in rare cases this test may fail because the +# time set using datefudge could have changed since the generation +# (if example the system was busy) + +# Test PSS signatures on certificate + +for i in sha256 sha384 sha512;do +datefudge -s "2007-04-22" \ +"${CERTTOOL}" --generate-self-signed --rsa-pss \ + --load-privkey "${srcdir}/data/template-test.key" \ + --template "${srcdir}/templates/template-test.tmpl" \ + --outfile "${TMPFILE}" --hash $i 2>/dev/null +rc=$? + +if test -f "${srcdir}/data/template-rsa-$i.pem";then +${DIFF} "${srcdir}/data/template-rsa-$i.pem" "${TMPFILE}" >/dev/null 2>&1 +rc=$? +fi + +# We're done. +if test "${rc}" != "0"; then + echo "Test (RSA-PSS-$i) failed" + exit ${rc} +fi + +datefudge -s "2007-04-25" \ + "${CERTTOOL}" --load-ca-certificate "${TMPFILE}" --verify --infile "${TMPFILE}" >/dev/null 2>&1 +rc=$? +if test "${rc}" != "0"; then + echo "Test (verification of RSA-PSS-$i) failed" + exit ${rc} +fi +done + +rm -f "${TMPFILE}" + +exit 0 diff --git a/tests/privkey-keygen.c b/tests/privkey-keygen.c index 72201f4b18..9696392aef 100644 --- a/tests/privkey-keygen.c +++ b/tests/privkey-keygen.c @@ -103,9 +103,10 @@ void doit(void) gnutls_global_set_log_level(4711); for (i = 0; i < MAX_TRIES; i++) { - for (algorithm = GNUTLS_PK_RSA; algorithm <= GNUTLS_PK_EC; + for (algorithm = GNUTLS_PK_RSA; algorithm <= GNUTLS_PK_RSA_PSS; algorithm++) { - if (algorithm == GNUTLS_PK_DH) + if (algorithm == GNUTLS_PK_DH || + algorithm == GNUTLS_PK_ECDHX) continue; ret = gnutls_x509_privkey_init(&pkey); diff --git a/tests/privkey-verify-broken.c b/tests/privkey-verify-broken.c index 7b41e6ffba..7d7c84c7a0 100644 --- a/tests/privkey-verify-broken.c +++ b/tests/privkey-verify-broken.c @@ -45,12 +45,13 @@ const gnutls_datum_t raw_data = { 11 }; -static int sign_verify_data(gnutls_x509_privkey_t pkey, unsigned algo, unsigned vflags) +static int sign_verify_data2(gnutls_x509_privkey_t pkey, unsigned algo, unsigned sflags, unsigned vflags) { int ret; gnutls_privkey_t privkey; gnutls_pubkey_t pubkey; gnutls_datum_t signature; + gnutls_pk_algorithm_t pk; /* sign arbitrary data */ assert(gnutls_privkey_init(&privkey) >= 0); @@ -59,7 +60,7 @@ static int sign_verify_data(gnutls_x509_privkey_t pkey, unsigned algo, unsigned if (ret < 0) fail("gnutls_pubkey_import_x509\n"); - ret = gnutls_privkey_sign_data(privkey, algo, 0, + ret = gnutls_privkey_sign_data(privkey, algo, sflags, &raw_data, &signature); if (ret < 0) { ret = -1; @@ -73,7 +74,12 @@ static int sign_verify_data(gnutls_x509_privkey_t pkey, unsigned algo, unsigned if (ret < 0) fail("gnutls_pubkey_import_privkey\n"); - ret = gnutls_pubkey_verify_data2(pubkey, gnutls_pk_to_sign(gnutls_pubkey_get_pk_algorithm(pubkey, NULL),algo), + if (sflags & GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS) + pk = GNUTLS_PK_RSA_PSS; + else + pk = gnutls_pubkey_get_pk_algorithm(pubkey, NULL); + + ret = gnutls_pubkey_verify_data2(pubkey, gnutls_pk_to_sign(pk, algo), vflags, &raw_data, &signature); if (ret < 0) { ret = -1; @@ -89,6 +95,11 @@ static int sign_verify_data(gnutls_x509_privkey_t pkey, unsigned algo, unsigned return ret; } +static int sign_verify_data(gnutls_x509_privkey_t pkey, unsigned algo, unsigned vflags) +{ + return sign_verify_data2(pkey, algo, 0, vflags); +} + void doit(void) { gnutls_x509_privkey_t pkey; @@ -123,7 +134,7 @@ void doit(void) fail("failed verification with SHA1 and override flags2!\n"); if (sign_verify_data(pkey, GNUTLS_DIG_MD5, 0) >= 0) - fail("succeeded verification with SHA1!\n"); + fail("succeeded verification with MD5!\n"); if (!gnutls_fips140_mode_enabled()) { if (sign_verify_data(pkey, GNUTLS_DIG_MD5, GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5) < 0) @@ -142,6 +153,9 @@ void doit(void) if (sign_verify_data(pkey, GNUTLS_DIG_SHA3_256, 0) < 0) fail("failed verification with SHA3-256!\n"); + if (sign_verify_data2(pkey, GNUTLS_DIG_SHA256, GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS, GNUTLS_VERIFY_USE_RSA_PSS) < 0) + fail("failed verification with SHA256 with PSS!\n"); + gnutls_x509_privkey_deinit(pkey); gnutls_global_deinit(); diff --git a/tests/x509sign-verify-rsa.c b/tests/x509sign-verify-rsa.c index c0f1be1813..f527606e8d 100644 --- a/tests/x509sign-verify-rsa.c +++ b/tests/x509sign-verify-rsa.c @@ -62,6 +62,7 @@ void doit(void) test_sig(GNUTLS_PK_RSA, GNUTLS_DIG_SHA1, rsa_size1); test_sig(GNUTLS_PK_RSA, GNUTLS_DIG_SHA256, rsa_size2); + test_sig(GNUTLS_PK_RSA_PSS, GNUTLS_DIG_SHA256, rsa_size2); gnutls_global_deinit(); } |