diff options
author | Daiki Ueno <dueno@redhat.com> | 2017-03-16 11:38:58 +0100 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@redhat.com> | 2017-05-29 08:23:49 +0200 |
commit | 9e5452193c3510102801fd86b6e65d37b5dc1012 (patch) | |
tree | 1c401b3900c8a6f3ffac58ad839266e8c228f941 /lib/x509/verify.c | |
parent | 03c811b7f9a280182b486473567a0b93fe1dc291 (diff) | |
download | gnutls-9e5452193c3510102801fd86b6e65d37b5dc1012.tar.gz |
x509: implement RSA-PSS signature scheme
This patch enables RSA-PSS signature scheme in the X.509 functions and
certtool.
When creating RSA-PSS signature, there are 3 different scenarios:
a. both a private key and a certificate are RSA-PSS
b. the private key is RSA, while the certificate is RSA-PSS
c. both the private key and the certificate are RSA
For (a) and (b), the RSA-PSS parameters are read from the certificate.
Any conflicts in parameters between the private key and the certificate
are reported as an error.
For (c), the sign functions, such as gnutls_x509_crt_privkey_sign() or
gnutls_privkey_sign_data(), shall be instructed to generate an RSA-PSS
signature. This can be done with the new flag
GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS.
Verification is similar to signing, except for the case (c), use the
flag GNUTLS_VERIFY_USE_RSA_PSS instead of
GNUTLS_PRIVKEY_SIGN_FLAG_RSA_PSS.
From the command line, certtool has a couple of new options: --rsa-pss
and --rsa-pss-sign. The --rsa-pss option indicates that the generated
private key or certificate is restricted to RSA-PSS, while the
--rsa-pss-sign option indicates that the generated certificate is signed
with RSA-PSS.
For simplicity, there is no means of choosing arbitrary salt length.
When it is not given by a private key or a certificate, it is
automatically calculated from the underlying hash algorithm and the
RSA modulus bits.
[minor naming changes by nmav]
Signed-off-by: Daiki Ueno <dueno@redhat.com>
Signed-off-by: Nikos Mavrogiannopoulos <nmav@redhat.com>
Diffstat (limited to 'lib/x509/verify.c')
-rw-r--r-- | lib/x509/verify.c | 134 |
1 files changed, 104 insertions, 30 deletions
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) && |