diff options
author | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2011-06-04 08:51:11 +0200 |
---|---|---|
committer | Nikos Mavrogiannopoulos <nmav@gnutls.org> | 2011-06-04 08:59:45 +0200 |
commit | 0d66f1fc47ec0dec5bfe56928cc46ed47842b360 (patch) | |
tree | 59e323c895b9ce5340b28f55109aed00bad70e8e /lib | |
parent | b9f7a3d505d4be7521c3a6927b776ba02be33e5c (diff) | |
download | gnutls-0d66f1fc47ec0dec5bfe56928cc46ed47842b360.tar.gz |
Split pubkey_verify_sig() to pubkey_verify_hashed_data() and pubkey_verify_data().
Added gnutls_pubkey_verify_data2() to allow verification of a signature when the
signature algorithm cannot be determined by the signature and the public key only.
Diffstat (limited to 'lib')
-rw-r--r-- | lib/abstract_int.h | 16 | ||||
-rw-r--r-- | lib/gnutls_pubkey.c | 310 | ||||
-rw-r--r-- | lib/gnutls_sig.c | 155 | ||||
-rw-r--r-- | lib/gnutls_sig.h | 5 | ||||
-rw-r--r-- | lib/includes/gnutls/abstract.h | 6 | ||||
-rw-r--r-- | lib/includes/gnutls/compat.h | 10 | ||||
-rw-r--r-- | lib/libgnutls.map | 2 | ||||
-rw-r--r-- | lib/x509/common.c | 37 | ||||
-rw-r--r-- | lib/x509/common.h | 11 | ||||
-rw-r--r-- | lib/x509/crq.c | 15 | ||||
-rw-r--r-- | lib/x509/privkey.c | 39 | ||||
-rw-r--r-- | lib/x509/verify.c | 368 | ||||
-rw-r--r-- | lib/x509/x509.c | 32 | ||||
-rw-r--r-- | lib/x509/x509_int.h | 15 |
14 files changed, 568 insertions, 453 deletions
diff --git a/lib/abstract_int.h b/lib/abstract_int.h index 1060679797..06318d3791 100644 --- a/lib/abstract_int.h +++ b/lib/abstract_int.h @@ -14,4 +14,20 @@ int _gnutls_pubkey_get_mpis (gnutls_pubkey_t key, gnutls_pk_params_st * params); +int pubkey_verify_hashed_data (gnutls_pk_algorithm_t pk, + const gnutls_datum_t * hash, + const gnutls_datum_t * signature, + gnutls_pk_params_st * issuer_params); + +int pubkey_verify_data (gnutls_pk_algorithm_t pk, + gnutls_digest_algorithm_t algo, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_pk_params_st * issuer_params); + + + +gnutls_digest_algorithm_t _gnutls_dsa_q_to_hash (gnutls_pk_algorithm_t algo, + const gnutls_pk_params_st* params, int* hash_len); + #endif diff --git a/lib/gnutls_pubkey.c b/lib/gnutls_pubkey.c index 5dddae2b1c..6352bbbeed 100644 --- a/lib/gnutls_pubkey.c +++ b/lib/gnutls_pubkey.c @@ -30,6 +30,7 @@ #include <gnutls_datum.h> #include <pkcs11_int.h> #include <gnutls/abstract.h> +#include <gnutls_sig.h> #include <gnutls_pk.h> #include <x509_int.h> #include <openpgp/openpgp_int.h> @@ -1119,7 +1120,49 @@ gnutls_pubkey_verify_data (gnutls_pubkey_t pubkey, unsigned int flags, return GNUTLS_E_INVALID_REQUEST; } - ret = pubkey_verify_sig( data, NULL, signature, pubkey->pk_algorithm, + ret = pubkey_verify_data( pubkey->pk_algorithm, GNUTLS_DIG_UNKNOWN, data, signature, + &pubkey->params); + if (ret < 0) + { + gnutls_assert(); + } + + return ret; +} + +/** + * gnutls_pubkey_verify_data2: + * @pubkey: Holds the public key + * @algo: The signature algorithm used + * @flags: should be 0 for now + * @data: holds the signed data + * @signature: contains the signature + * + * This function will verify the given signed data, using the + * parameters from the certificate. + * + * Returns: In case of a verification failure + * %GNUTLS_E_PK_SIG_VERIFY_FAILED is returned, and a positive code + * on success. + * + * Since: 2.12.0 + **/ +int +gnutls_pubkey_verify_data2 (gnutls_pubkey_t pubkey, + gnutls_sign_algorithm_t algo, + unsigned int flags, + const gnutls_datum_t * data, + const gnutls_datum_t * signature) +{ + int ret; + + if (pubkey == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = pubkey_verify_data( pubkey->pk_algorithm, _gnutls_sign_get_hash_algorithm(algo), data, signature, &pubkey->params); if (ret < 0) { @@ -1158,7 +1201,7 @@ gnutls_pubkey_verify_hash (gnutls_pubkey_t key, unsigned int flags, return _gnutls_rsa_verify (hash, signature, &key->params, 1); else { - return pubkey_verify_sig (NULL, hash, signature, key->pk_algorithm, + return pubkey_verify_hashed_data (key->pk_algorithm, hash, signature, &key->params); } } @@ -1250,3 +1293,266 @@ _gnutls_pubkey_get_mpis (gnutls_pubkey_t key, { return _gnutls_pk_params_copy(params, &key->params); } + +/* if hash==MD5 then we do RSA-MD5 + * if hash==SHA then we do RSA-SHA + * params[0] is modulus + * params[1] is public key + */ +static int +_pkcs1_rsa_verify_sig (const gnutls_datum_t * text, + const gnutls_datum_t * prehash, + const gnutls_datum_t * signature, + gnutls_pk_params_st * params) +{ + gnutls_mac_algorithm_t hash = GNUTLS_MAC_UNKNOWN; + int ret; + opaque digest[MAX_HASH_SIZE], md[MAX_HASH_SIZE], *cmp; + int digest_size; + digest_hd_st hd; + gnutls_datum_t decrypted; + + ret = + _gnutls_pkcs1_rsa_decrypt (&decrypted, signature, params, 1); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + /* decrypted is a BER encoded data of type DigestInfo + */ + + digest_size = sizeof (digest); + if ((ret = + decode_ber_digest_info (&decrypted, &hash, digest, &digest_size)) != 0) + { + gnutls_assert (); + _gnutls_free_datum (&decrypted); + return ret; + } + + _gnutls_free_datum (&decrypted); + + if (digest_size != _gnutls_hash_get_algo_len (hash)) + { + gnutls_assert (); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + if (prehash && prehash->data && prehash->size == digest_size) + { + cmp = prehash->data; + } + else + { + if (!text) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + ret = _gnutls_hash_init (&hd, hash); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_hash (&hd, text->data, text->size); + _gnutls_hash_deinit (&hd, md); + + cmp = md; + } + + if (memcmp (cmp, digest, digest_size) != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 0; +} + +/* Hashes input data and verifies a signature. + */ +static int +dsa_verify_hashed_data (const gnutls_datum_t * hash, + const gnutls_datum_t * signature, + gnutls_pk_algorithm_t pk, + gnutls_pk_params_st* params) +{ + gnutls_datum_t digest; + gnutls_digest_algorithm_t algo; + int hash_len; + + algo = _gnutls_dsa_q_to_hash (pk, params, &hash_len); + + /* SHA1 or better allowed */ + if (!hash->data || hash->size < hash_len) + { + gnutls_assert(); + _gnutls_debug_log("Hash size (%d) does not correspond to hash %s(%d) or better.\n", (int)hash->size, gnutls_mac_get_name(algo), hash_len); + + if (hash->size != 20) /* SHA1 is allowed */ + return gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); + } + + digest.data = hash->data; + digest.size = hash->size; + + return _gnutls_pk_verify (pk, &digest, signature, params); +} + +static int +dsa_verify_data (gnutls_pk_algorithm_t pk, + gnutls_digest_algorithm_t algo, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_pk_params_st* params) +{ + int ret; + opaque _digest[MAX_HASH_SIZE]; + gnutls_datum_t digest; + digest_hd_st hd; + + if (algo == GNUTLS_DIG_UNKNOWN) + algo = _gnutls_dsa_q_to_hash (pk, params, NULL); + + ret = _gnutls_hash_init (&hd, algo); + if (ret < 0) + return gnutls_assert_val(ret); + + _gnutls_hash (&hd, data->data, data->size); + _gnutls_hash_deinit (&hd, _digest); + + digest.data = _digest; + digest.size = _gnutls_hash_get_algo_len(algo); + + return _gnutls_pk_verify (pk, &digest, signature, params); +} + +/* Verifies the signature data, and returns GNUTLS_E_PK_SIG_VERIFY_FAILED if + * not verified, or 1 otherwise. + */ +int +pubkey_verify_hashed_data (gnutls_pk_algorithm_t pk, + const gnutls_datum_t * hash, + const gnutls_datum_t * signature, + gnutls_pk_params_st * issuer_params) +{ + + switch (pk) + { + case GNUTLS_PK_RSA: + + if (_pkcs1_rsa_verify_sig + (NULL, hash, signature, issuer_params) != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 1; + break; + + case GNUTLS_PK_ECC: + case GNUTLS_PK_DSA: + if (dsa_verify_hashed_data(hash, signature, pk, issuer_params) != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 1; + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + + } +} + +/* Verifies the signature data, and returns GNUTLS_E_PK_SIG_VERIFY_FAILED if + * not verified, or 1 otherwise. + */ +int +pubkey_verify_data (gnutls_pk_algorithm_t pk, + gnutls_digest_algorithm_t algo, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_pk_params_st * issuer_params) +{ + + switch (pk) + { + case GNUTLS_PK_RSA: + + if (_pkcs1_rsa_verify_sig + (data, NULL, signature, issuer_params) != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 1; + break; + + case GNUTLS_PK_ECC: + case GNUTLS_PK_DSA: + if (dsa_verify_data(pk, algo, data, signature, issuer_params) != 0) + { + gnutls_assert (); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + + return 1; + break; + default: + gnutls_assert (); + return GNUTLS_E_INTERNAL_ERROR; + + } +} + +gnutls_digest_algorithm_t +_gnutls_dsa_q_to_hash (gnutls_pk_algorithm_t algo, const gnutls_pk_params_st* params, int* hash_len) +{ + int bits = 0; + + if (algo == GNUTLS_PK_DSA) + bits = _gnutls_mpi_get_nbits (params->params[1]); + else if (algo == GNUTLS_PK_ECC) + bits = gnutls_ecc_curve_get_size(params->flags)*8; + + if (bits <= 160) + { + if (hash_len) *hash_len = 20; + return GNUTLS_DIG_SHA1; + } + else if (bits <= 192) + { + if (hash_len) *hash_len = 24; + return GNUTLS_DIG_SHA256; + } + else if (bits <= 224) + { + if (hash_len) *hash_len = 28; + return GNUTLS_DIG_SHA256; + } + else if (bits <= 256) + { + if (hash_len) *hash_len = 32; + return GNUTLS_DIG_SHA256; + } + else if (bits <= 384) + { + if (hash_len) *hash_len = 48; + return GNUTLS_DIG_SHA384; + } + else + { + if (hash_len) *hash_len = 64; + return GNUTLS_DIG_SHA512; + } +} diff --git a/lib/gnutls_sig.c b/lib/gnutls_sig.c index b93aa40a8a..db8a071d22 100644 --- a/lib/gnutls_sig.c +++ b/lib/gnutls_sig.c @@ -48,6 +48,11 @@ sign_tls_hash (gnutls_session_t session, gnutls_digest_algorithm_t hash_algo, const gnutls_datum_t * hash_concat, gnutls_datum_t * signature); +static int +encode_ber_digest_info (gnutls_digest_algorithm_t hash, + const gnutls_datum_t * digest, + gnutls_datum_t * output); + /* While this is currently equal to the length of RSA/SHA512 * signature, it should also be sufficient for DSS signature and any * other RSA signatures including one with the old MD5/SHA1-combined @@ -732,6 +737,120 @@ cleanup: return ret; } + +/* + * This function will do RSA PKCS #1 1.5 encoding + * on the given digest. The given digest must be allocated + * and will be freed if replacement is required. + */ +int +pk_prepare_hash (gnutls_pk_algorithm_t pk, + gnutls_digest_algorithm_t hash, gnutls_datum_t * digest) +{ + int ret; + gnutls_datum_t old_digest = { digest->data, digest->size }; + + switch (pk) + { + case GNUTLS_PK_RSA: + /* Encode the digest as a DigestInfo + */ + if ((ret = encode_ber_digest_info (hash, &old_digest, digest)) != 0) + { + gnutls_assert (); + return ret; + } + + _gnutls_free_datum (&old_digest); + break; + case GNUTLS_PK_DSA: + case GNUTLS_PK_ECC: + break; + default: + gnutls_assert (); + return GNUTLS_E_UNIMPLEMENTED_FEATURE; + } + + return 0; +} + +/* Reads the digest information. + * we use DER here, although we should use BER. It works fine + * anyway. + */ +int +decode_ber_digest_info (const gnutls_datum_t * info, + gnutls_mac_algorithm_t * hash, + opaque * digest, int *digest_size) +{ + ASN1_TYPE dinfo = ASN1_TYPE_EMPTY; + int result; + char str[1024]; + int len; + + if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), + "GNUTLS.DigestInfo", + &dinfo)) != ASN1_SUCCESS) + { + gnutls_assert (); + return _gnutls_asn2err (result); + } + + result = asn1_der_decoding (&dinfo, info->data, info->size, NULL); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + len = sizeof (str) - 1; + result = asn1_read_value (dinfo, "digestAlgorithm.algorithm", str, &len); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + *hash = _gnutls_x509_oid2mac_algorithm (str); + + if (*hash == GNUTLS_MAC_UNKNOWN) + { + + _gnutls_debug_log ("verify.c: HASH OID: %s\n", str); + + gnutls_assert (); + asn1_delete_structure (&dinfo); + return GNUTLS_E_UNKNOWN_ALGORITHM; + } + + len = sizeof (str) - 1; + result = asn1_read_value (dinfo, "digestAlgorithm.parameters", str, &len); + /* To avoid permitting garbage in the parameters field, either the + parameters field is not present, or it contains 0x05 0x00. */ + if (!(result == ASN1_ELEMENT_NOT_FOUND || + (result == ASN1_SUCCESS && len == ASN1_NULL_SIZE && + memcmp (str, ASN1_NULL, ASN1_NULL_SIZE) == 0))) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return GNUTLS_E_ASN1_GENERIC_ERROR; + } + + result = asn1_read_value (dinfo, "digest", digest, digest_size); + if (result != ASN1_SUCCESS) + { + gnutls_assert (); + asn1_delete_structure (&dinfo); + return _gnutls_asn2err (result); + } + + asn1_delete_structure (&dinfo); + + return 0; +} + /* Writes the digest information and the digest in a DER encoded * structure. The digest info is allocated and stored into the info structure. */ @@ -818,39 +937,3 @@ encode_ber_digest_info (gnutls_digest_algorithm_t hash, return 0; } - -/* - * This function will do RSA PKCS #1 1.5 encoding - * on the given digest. The given digest must be allocated - * and will be freed if replacement is required. - */ -int -pk_prepare_hash (gnutls_pk_algorithm_t pk, - gnutls_digest_algorithm_t hash, gnutls_datum_t * digest) -{ - int ret; - gnutls_datum_t old_digest = { digest->data, digest->size }; - - switch (pk) - { - case GNUTLS_PK_RSA: - /* Encode the digest as a DigestInfo - */ - if ((ret = encode_ber_digest_info (hash, &old_digest, digest)) != 0) - { - gnutls_assert (); - return ret; - } - - _gnutls_free_datum (&old_digest); - break; - case GNUTLS_PK_DSA: - case GNUTLS_PK_ECC: - break; - default: - gnutls_assert (); - return GNUTLS_E_UNIMPLEMENTED_FEATURE; - } - - return 0; -} diff --git a/lib/gnutls_sig.h b/lib/gnutls_sig.h index 3b6957ac58..e3865b9a2a 100644 --- a/lib/gnutls_sig.h +++ b/lib/gnutls_sig.h @@ -67,4 +67,9 @@ _gnutls_privkey_sign_hash (gnutls_privkey_t key, const gnutls_datum_t * hash, gnutls_datum_t * signature); +int +decode_ber_digest_info (const gnutls_datum_t * info, + gnutls_mac_algorithm_t * hash, + opaque * digest, int *digest_size); + #endif diff --git a/lib/includes/gnutls/abstract.h b/lib/includes/gnutls/abstract.h index eec013cffc..078ab60897 100644 --- a/lib/includes/gnutls/abstract.h +++ b/lib/includes/gnutls/abstract.h @@ -95,6 +95,12 @@ int gnutls_pubkey_verify_data (gnutls_pubkey_t pubkey, unsigned int flags, const gnutls_datum_t * data, const gnutls_datum_t * signature); +int +gnutls_pubkey_verify_data2 (gnutls_pubkey_t pubkey, + gnutls_sign_algorithm_t algo, + unsigned int flags, + const gnutls_datum_t * data, + const gnutls_datum_t * signature); /* Private key operations */ diff --git a/lib/includes/gnutls/compat.h b/lib/includes/gnutls/compat.h index e6b92776a7..fb88179c52 100644 --- a/lib/includes/gnutls/compat.h +++ b/lib/includes/gnutls/compat.h @@ -202,16 +202,6 @@ gnutls_sign_callback_get (gnutls_session_t session, void **userdata) gnutls_datum_t * signature) _GNUTLS_GCC_ATTR_DEPRECATED; - -/* Deprecated because verify_* functions are moved to public - * keys. Check abstract.h for similar functionality. - */ - int gnutls_x509_privkey_verify_data (gnutls_x509_privkey_t key, - unsigned int flags, - const gnutls_datum_t * data, - const gnutls_datum_t * signature) - _GNUTLS_GCC_ATTR_DEPRECATED; - /* we support the gnutls_privkey_sign_data() instead. */ int gnutls_x509_privkey_sign_data (gnutls_x509_privkey_t key, diff --git a/lib/libgnutls.map b/lib/libgnutls.map index 042b2f95cb..5c47ea3b57 100644 --- a/lib/libgnutls.map +++ b/lib/libgnutls.map @@ -508,7 +508,6 @@ GNUTLS_1_4 gnutls_x509_privkey_init; gnutls_x509_privkey_sign_data; gnutls_x509_privkey_sign_hash; - gnutls_x509_privkey_verify_data; gnutls_x509_rdn_get; gnutls_x509_rdn_get_by_oid; gnutls_x509_rdn_get_oid; @@ -710,6 +709,7 @@ GNUTLS_3_0_0 { gnutls_pubkey_get_pk_ecc_raw; gnutls_x509_privkey_export_ecc_raw; gnutls_x509_privkey_import_ecc_raw; + gnutls_pubkey_verify_data2; } GNUTLS_2_12; GNUTLS_PRIVATE { diff --git a/lib/x509/common.c b/lib/x509/common.c index 788b335030..16cd3e42f5 100644 --- a/lib/x509/common.c +++ b/lib/x509/common.c @@ -1344,6 +1344,43 @@ cleanup: return result; } +/** + * gnutls_x509_crt_get_signature_algorithm: + * @cert: should contain a #gnutls_x509_crt_t structure + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm that has been used to + * sign this certificate. + * + * Returns: a #gnutls_sign_algorithm_t value, or a negative value on + * error. + **/ +int +_gnutls_x509_get_signature_algorithm (ASN1_TYPE src, const char *src_name) +{ + int result; + gnutls_datum_t sa; + + /* 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, 0); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_oid2sign_algorithm (sa.data); + + _gnutls_free_datum (&sa); + + return result; +} + + /* Reads the DER signature from the certificate and allocates space and * returns them into signed_data. */ diff --git a/lib/x509/common.h b/lib/x509/common.h index 0e96337e72..aebe338641 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -128,6 +128,9 @@ int _gnutls_x509_decode_and_read_attribute (ASN1_TYPE asn1_struct, int _gnutls_x509_get_pk_algorithm (ASN1_TYPE src, const char *src_name, unsigned int *bits); +int +_gnutls_x509_get_signature_algorithm (ASN1_TYPE src, const char *src_name); + int _gnutls_x509_encode_and_copy_PKI_params (ASN1_TYPE dst, const char *dst_name, gnutls_pk_algorithm_t @@ -140,8 +143,6 @@ int _gnutls_x509_get_signed_data (ASN1_TYPE src, const char *src_name, int _gnutls_x509_get_signature (ASN1_TYPE src, const char *src_name, gnutls_datum_t * signature); -gnutls_digest_algorithm_t _gnutls_dsa_q_to_hash (gnutls_pk_algorithm_t algo, - const gnutls_pk_params_st* params, int* hash_len); int _gnutls_get_asn_mpis (ASN1_TYPE asn, const char *root, gnutls_pk_params_st * params); @@ -153,12 +154,6 @@ int _gnutls_get_key_id (gnutls_pk_algorithm_t pk, gnutls_pk_params_st*, void _asnstr_append_name (char *name, size_t name_size, const char *part1, const char *part2); -int pubkey_verify_sig (const gnutls_datum_t * tbs, - const gnutls_datum_t * hash, - const gnutls_datum_t * signature, - gnutls_pk_algorithm_t pk, - gnutls_pk_params_st * issuer_params); - int check_if_same_cert (gnutls_x509_crt_t cert1, gnutls_x509_crt_t cert2); diff --git a/lib/x509/crq.c b/lib/x509/crq.c index c61b3c4043..27dee3ed8e 100644 --- a/lib/x509/crq.c +++ b/lib/x509/crq.c @@ -2523,6 +2523,7 @@ gnutls_x509_crq_verify (gnutls_x509_crq_t crq, gnutls_datum data = { NULL, 0 }; gnutls_datum signature = { NULL, 0 }; gnutls_pk_params_st params; +gnutls_digest_algorithm_t algo; int ret; gnutls_pk_params_init(¶ms); @@ -2534,6 +2535,15 @@ int ret; gnutls_assert (); return ret; } + + ret = _gnutls_x509_get_signature_algorithm(crq->crq, "signatureAlgorithm.algorithm"); + if (ret < 0) + { + gnutls_assert (); + goto cleanup; + } + + algo = _gnutls_sign_get_hash_algorithm(ret); ret = _gnutls_x509_get_signature (crq->crq, "signature", &signature); if (ret < 0) @@ -2550,9 +2560,8 @@ int ret; goto cleanup; } - ret = pubkey_verify_sig(&data, NULL, &signature, - gnutls_x509_crq_get_pk_algorithm (crq, NULL), - ¶ms); + ret = pubkey_verify_data(gnutls_x509_crq_get_pk_algorithm (crq, NULL), algo, + &data, &signature, ¶ms); if (ret < 0) { gnutls_assert (); diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c index 08d89b5621..99f626f1d6 100644 --- a/lib/x509/privkey.c +++ b/lib/x509/privkey.c @@ -1641,45 +1641,6 @@ gnutls_x509_privkey_sign_data (gnutls_x509_privkey_t key, /** - * gnutls_x509_privkey_verify_data: - * @key: Holds the key - * @flags: should be 0 for now - * @data: holds the data to be signed - * @signature: contains the signature - * - * This function will verify the given signed data, using the - * parameters in the private key. - * - * Returns: In case of a verification failure %GNUTLS_E_PK_SIG_VERIFY_FAILED - * is returned, and a positive code on success. - * - * Deprecated: Use gnutls_pubkey_verify_data(). - */ -int -gnutls_x509_privkey_verify_data (gnutls_x509_privkey_t key, - unsigned int flags, - const gnutls_datum_t * data, - const gnutls_datum_t * signature) -{ - int result; - - if (key == NULL) - { - gnutls_assert (); - return GNUTLS_E_INVALID_REQUEST; - } - - result = _gnutls_x509_privkey_verify_signature (data, signature, key); - if (result < 0) - { - gnutls_assert (); - return result; - } - - return result; -} - -/** * gnutls_x509_privkey_fix: * @key: Holds the key * diff --git a/lib/x509/verify.c b/lib/x509/verify.c index 4ae9706776..2adb3dd5f6 100644 --- a/lib/x509/verify.c +++ b/lib/x509/verify.c @@ -369,7 +369,7 @@ _gnutls_verify_certificate2 (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, result; + int issuer_version, result, hash_algo; unsigned int out = 0; if (output) @@ -442,8 +442,17 @@ _gnutls_verify_certificate2 (gnutls_x509_crt_t cert, goto cleanup; } + result = _gnutls_x509_get_signature_algorithm(cert->cert, "signatureAlgorithm.algorithm"); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + hash_algo = _gnutls_sign_get_hash_algorithm(result); + result = - _gnutls_x509_verify_signature (&cert_signed_data, NULL, &cert_signature, + _gnutls_x509_verify_data (hash_algo, &cert_signed_data, &cert_signature, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { @@ -666,301 +675,6 @@ _gnutls_x509_verify_certificate (const gnutls_x509_crt_t * certificate_list, return 0; } - -/* Reads the digest information. - * we use DER here, although we should use BER. It works fine - * anyway. - */ -static int -decode_ber_digest_info (const gnutls_datum_t * info, - gnutls_mac_algorithm_t * hash, - opaque * digest, int *digest_size) -{ - ASN1_TYPE dinfo = ASN1_TYPE_EMPTY; - int result; - char str[1024]; - int len; - - if ((result = asn1_create_element (_gnutls_get_gnutls_asn (), - "GNUTLS.DigestInfo", - &dinfo)) != ASN1_SUCCESS) - { - gnutls_assert (); - return _gnutls_asn2err (result); - } - - result = asn1_der_decoding (&dinfo, info->data, info->size, NULL); - if (result != ASN1_SUCCESS) - { - gnutls_assert (); - asn1_delete_structure (&dinfo); - return _gnutls_asn2err (result); - } - - len = sizeof (str) - 1; - result = asn1_read_value (dinfo, "digestAlgorithm.algorithm", str, &len); - if (result != ASN1_SUCCESS) - { - gnutls_assert (); - asn1_delete_structure (&dinfo); - return _gnutls_asn2err (result); - } - - *hash = _gnutls_x509_oid2mac_algorithm (str); - - if (*hash == GNUTLS_MAC_UNKNOWN) - { - - _gnutls_debug_log ("verify.c: HASH OID: %s\n", str); - - gnutls_assert (); - asn1_delete_structure (&dinfo); - return GNUTLS_E_UNKNOWN_ALGORITHM; - } - - len = sizeof (str) - 1; - result = asn1_read_value (dinfo, "digestAlgorithm.parameters", str, &len); - /* To avoid permitting garbage in the parameters field, either the - parameters field is not present, or it contains 0x05 0x00. */ - if (!(result == ASN1_ELEMENT_NOT_FOUND || - (result == ASN1_SUCCESS && len == ASN1_NULL_SIZE && - memcmp (str, ASN1_NULL, ASN1_NULL_SIZE) == 0))) - { - gnutls_assert (); - asn1_delete_structure (&dinfo); - return GNUTLS_E_ASN1_GENERIC_ERROR; - } - - result = asn1_read_value (dinfo, "digest", digest, digest_size); - if (result != ASN1_SUCCESS) - { - gnutls_assert (); - asn1_delete_structure (&dinfo); - return _gnutls_asn2err (result); - } - - asn1_delete_structure (&dinfo); - - return 0; -} - -/* if hash==MD5 then we do RSA-MD5 - * if hash==SHA then we do RSA-SHA - * params[0] is modulus - * params[1] is public key - */ -static int -_pkcs1_rsa_verify_sig (const gnutls_datum_t * text, - const gnutls_datum_t * prehash, - const gnutls_datum_t * signature, - gnutls_pk_params_st * params) -{ - gnutls_mac_algorithm_t hash = GNUTLS_MAC_UNKNOWN; - int ret; - opaque digest[MAX_HASH_SIZE], md[MAX_HASH_SIZE], *cmp; - int digest_size; - digest_hd_st hd; - gnutls_datum_t decrypted; - - ret = - _gnutls_pkcs1_rsa_decrypt (&decrypted, signature, params, 1); - if (ret < 0) - { - gnutls_assert (); - return ret; - } - - /* decrypted is a BER encoded data of type DigestInfo - */ - - digest_size = sizeof (digest); - if ((ret = - decode_ber_digest_info (&decrypted, &hash, digest, &digest_size)) != 0) - { - gnutls_assert (); - _gnutls_free_datum (&decrypted); - return ret; - } - - _gnutls_free_datum (&decrypted); - - if (digest_size != _gnutls_hash_get_algo_len (hash)) - { - gnutls_assert (); - return GNUTLS_E_ASN1_GENERIC_ERROR; - } - - if (prehash && prehash->data && prehash->size == digest_size) - { - cmp = prehash->data; - } - else - { - if (!text) - { - gnutls_assert (); - return GNUTLS_E_INVALID_REQUEST; - } - - ret = _gnutls_hash_init (&hd, hash); - if (ret < 0) - { - gnutls_assert (); - return ret; - } - - _gnutls_hash (&hd, text->data, text->size); - _gnutls_hash_deinit (&hd, md); - - cmp = md; - } - - if (memcmp (cmp, digest, digest_size) != 0) - { - gnutls_assert (); - return GNUTLS_E_PK_SIG_VERIFY_FAILED; - } - - return 0; -} - -/* Hashes input data and verifies a signature. - */ -static int -dsa_verify_sig (const gnutls_datum_t * text, - const gnutls_datum_t * hash, - const gnutls_datum_t * signature, - gnutls_pk_algorithm_t pk, - gnutls_pk_params_st* params) -{ - int ret; - opaque _digest[MAX_HASH_SIZE]; - gnutls_datum_t digest; - digest_hd_st hd; - gnutls_digest_algorithm_t algo; - int hash_len; - - algo = _gnutls_dsa_q_to_hash (pk, params, &hash_len); - if (hash) - { - /* SHA1 or better allowed */ - if (!hash->data || hash->size < hash_len) - { - gnutls_assert(); - _gnutls_debug_log("Hash size (%d) does not correspond to hash %s(%d) or better.\n", (int)hash->size, gnutls_mac_get_name(algo), hash_len); - - if (hash->size != 20) /* SHA1 is allowed */ - return gnutls_assert_val(GNUTLS_E_PK_SIG_VERIFY_FAILED); - } - - digest.data = hash->data; - digest.size = hash->size; - } - else - { - ret = _gnutls_hash_init (&hd, algo); - if (ret < 0) - { - gnutls_assert (); - return ret; - } - - _gnutls_hash (&hd, text->data, text->size); - _gnutls_hash_deinit (&hd, _digest); - - digest.data = _digest; - digest.size = _gnutls_hash_get_algo_len(algo); - } - - ret = _gnutls_pk_verify (pk, &digest, signature, params); - - return ret; -} - -/* Verifies the signature data, and returns GNUTLS_E_PK_SIG_VERIFY_FAILED if - * not verified, or 1 otherwise. - */ -int -pubkey_verify_sig (const gnutls_datum_t * tbs, - const gnutls_datum_t * hash, - const gnutls_datum_t * signature, - gnutls_pk_algorithm_t pk, - gnutls_pk_params_st * issuer_params) -{ - - switch (pk) - { - case GNUTLS_PK_RSA: - - if (_pkcs1_rsa_verify_sig - (tbs, hash, signature, issuer_params) != 0) - { - gnutls_assert (); - return GNUTLS_E_PK_SIG_VERIFY_FAILED; - } - - return 1; - break; - - case GNUTLS_PK_ECC: - case GNUTLS_PK_DSA: - if (dsa_verify_sig(tbs, hash, signature, pk, issuer_params) != 0) - { - gnutls_assert (); - return GNUTLS_E_PK_SIG_VERIFY_FAILED; - } - - return 1; - break; - default: - gnutls_assert (); - return GNUTLS_E_INTERNAL_ERROR; - - } -} - -gnutls_digest_algorithm_t -_gnutls_dsa_q_to_hash (gnutls_pk_algorithm_t algo, const gnutls_pk_params_st* params, int* hash_len) -{ - int bits = 0; - - if (algo == GNUTLS_PK_DSA) - bits = _gnutls_mpi_get_nbits (params->params[1]); - else if (algo == GNUTLS_PK_ECC) - bits = gnutls_ecc_curve_get_size(params->flags)*8; - - if (bits <= 160) - { - if (hash_len) *hash_len = 20; - return GNUTLS_DIG_SHA1; - } - else if (bits <= 192) - { - if (hash_len) *hash_len = 24; - return GNUTLS_DIG_SHA256; - } - else if (bits <= 224) - { - if (hash_len) *hash_len = 28; - return GNUTLS_DIG_SHA256; - } - else if (bits <= 256) - { - if (hash_len) *hash_len = 32; - return GNUTLS_DIG_SHA256; - } - else if (bits <= 384) - { - if (hash_len) *hash_len = 48; - return GNUTLS_DIG_SHA384; - } - else - { - if (hash_len) *hash_len = 64; - return GNUTLS_DIG_SHA512; - } -} - /* This will return the appropriate hash to verify the given signature. * If signature is NULL it will return an (or the) appropriate hash for * the given parameters. @@ -1040,14 +754,14 @@ cleanup: /* verifies if the certificate is properly signed. * returns GNUTLS_E_PK_VERIFY_SIG_FAILED on failure and 1 on success. * - * 'tbs' is the signed data + * 'data' is the signed data * 'signature' is the signature! */ int -_gnutls_x509_verify_signature (const gnutls_datum_t * tbs, - const gnutls_datum_t * hash, - const gnutls_datum_t * signature, - gnutls_x509_crt_t issuer) +_gnutls_x509_verify_data (gnutls_digest_algorithm_t algo, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_x509_crt_t issuer) { gnutls_pk_params_st issuer_params; int ret; @@ -1063,9 +777,8 @@ _gnutls_x509_verify_signature (const gnutls_datum_t * tbs, } ret = - pubkey_verify_sig (tbs, hash, signature, - gnutls_x509_crt_get_pk_algorithm (issuer, NULL), - &issuer_params); + pubkey_verify_data (gnutls_x509_crt_get_pk_algorithm (issuer, NULL), algo, + data, signature, &issuer_params); if (ret < 0) { gnutls_assert (); @@ -1078,26 +791,36 @@ _gnutls_x509_verify_signature (const gnutls_datum_t * tbs, return ret; } -/* verifies if the certificate is properly signed. - * returns GNUTLS_E_PK_VERIFY_SIG_FAILED on failure and 1 on success. - * - * 'tbs' is the signed data - * 'signature' is the signature! - */ int -_gnutls_x509_privkey_verify_signature (const gnutls_datum_t * tbs, - const gnutls_datum_t * signature, - gnutls_x509_privkey_t issuer) +_gnutls_x509_verify_hashed_data (const gnutls_datum_t * hash, + const gnutls_datum_t * signature, + gnutls_x509_crt_t issuer) { + gnutls_pk_params_st issuer_params; int ret; - ret = pubkey_verify_sig (tbs, NULL, signature, issuer->pk_algorithm, - &issuer->params); + /* Read the MPI parameters from the issuer's certificate. + */ + ret = + _gnutls_x509_crt_get_mpis (issuer, &issuer_params); + if (ret < 0) + { + gnutls_assert (); + return ret; + } + + ret = + pubkey_verify_hashed_data (gnutls_x509_crt_get_pk_algorithm (issuer, NULL), + hash, signature, &issuer_params); if (ret < 0) { gnutls_assert (); } + /* release all allocated MPIs + */ + gnutls_pk_params_release(&issuer_params); + return ret; } @@ -1329,7 +1052,7 @@ _gnutls_verify_crl2 (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; - int result; + int result, hash_algo; if (output) *output = 0; @@ -1381,8 +1104,17 @@ _gnutls_verify_crl2 (gnutls_x509_crl_t crl, goto cleanup; } + result = _gnutls_x509_get_signature_algorithm(crl->crl, "signatureAlgorithm.algorithm"); + if (result < 0) + { + gnutls_assert (); + goto cleanup; + } + + hash_algo = _gnutls_sign_get_hash_algorithm(result); + result = - _gnutls_x509_verify_signature (&crl_signed_data, NULL, &crl_signature, + _gnutls_x509_verify_data (hash_algo, &crl_signed_data, &crl_signature, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { diff --git a/lib/x509/x509.c b/lib/x509/x509.c index 810057a049..582968f8e6 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -475,33 +475,7 @@ gnutls_x509_crt_get_dn_oid (gnutls_x509_crt_t cert, int gnutls_x509_crt_get_signature_algorithm (gnutls_x509_crt_t cert) { - int result; - gnutls_datum_t sa; - - if (cert == 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 (cert->cert, "signatureAlgorithm.algorithm", - &sa, 0); - - if (result < 0) - { - gnutls_assert (); - return result; - } - - result = _gnutls_x509_oid2sign_algorithm (sa.data); - - _gnutls_free_datum (&sa); - - return result; + return _gnutls_x509_get_signature_algorithm(cert->cert, "signatureAlgorithm.algorithm"); } /** @@ -2613,7 +2587,7 @@ gnutls_x509_crt_verify_data (gnutls_x509_crt_t crt, unsigned int flags, return GNUTLS_E_INVALID_REQUEST; } - result = _gnutls_x509_verify_signature (data, NULL, signature, crt); + result = _gnutls_x509_verify_data (GNUTLS_DIG_UNKNOWN, data, signature, crt); if (result < 0) { gnutls_assert (); @@ -2651,7 +2625,7 @@ gnutls_x509_crt_verify_hash (gnutls_x509_crt_t crt, unsigned int flags, return GNUTLS_E_INVALID_REQUEST; } - result = _gnutls_x509_verify_signature (NULL, hash, signature, crt); + result = _gnutls_x509_verify_hashed_data (hash, signature, crt); if (result < 0) { gnutls_assert (); diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 4df028befe..41ed58277b 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -182,13 +182,14 @@ _gnutls_x509_verify_algorithm (gnutls_mac_algorithm_t * hash, gnutls_pk_algorithm_t pk, gnutls_pk_params_st * issuer_params); -int _gnutls_x509_verify_signature (const gnutls_datum_t * tbs, - const gnutls_datum_t * hash, - const gnutls_datum_t * signature, - gnutls_x509_crt_t issuer); -int _gnutls_x509_privkey_verify_signature (const gnutls_datum_t * tbs, - const gnutls_datum_t * signature, - gnutls_x509_privkey_t issuer); +int _gnutls_x509_verify_data (gnutls_digest_algorithm_t algo, + const gnutls_datum_t * data, + const gnutls_datum_t * signature, + gnutls_x509_crt_t issuer); + +int _gnutls_x509_verify_hashed_data (const gnutls_datum_t * hash, + const gnutls_datum_t * signature, + gnutls_x509_crt_t issuer); /* privkey.h */ ASN1_TYPE _gnutls_privkey_decode_pkcs1_rsa_key (const gnutls_datum_t * |