Decouple PKCS#12 hash lookup from the OID table. This isn't strictly necessary for Chromium yet, but we already have a decoupled version of hash algorithm parsing available. For now, don't export it but eventually we may wish to use it for OCSP. BUG=54 Change-Id: If460d38d48bd47a2b4a853779f210c0cf7ee236b Reviewed-on: https://boringssl-review.googlesource.com/14211 Reviewed-by: Steven Valdez <svaldez@chromium.org> Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Steven Valdez <svaldez@chromium.org> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/digest/digests.c b/crypto/digest/digests.c index fd2a939..9ad2d49 100644 --- a/crypto/digest/digests.c +++ b/crypto/digest/digests.c
@@ -60,6 +60,7 @@ #include <string.h> #include <openssl/asn1.h> +#include <openssl/bytestring.h> #include <openssl/md4.h> #include <openssl/md5.h> #include <openssl/nid.h> @@ -328,20 +329,58 @@ { {0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04}, 9, EVP_sha224 }, }; +static const EVP_MD *cbs_to_md(const CBS *cbs) { + for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kMDOIDs); i++) { + if (CBS_len(cbs) == kMDOIDs[i].oid_len && + OPENSSL_memcmp(CBS_data(cbs), kMDOIDs[i].oid, kMDOIDs[i].oid_len) == + 0) { + return kMDOIDs[i].md_func(); + } + } + + return NULL; +} + const EVP_MD *EVP_get_digestbyobj(const ASN1_OBJECT *obj) { /* Handle objects with no corresponding OID. */ if (obj->nid != NID_undef) { return EVP_get_digestbynid(obj->nid); } - for (size_t i = 0; i < OPENSSL_ARRAY_SIZE(kMDOIDs); i++) { - if (obj->length == kMDOIDs[i].oid_len && - memcmp(obj->data, kMDOIDs[i].oid, obj->length) == 0) { - return kMDOIDs[i].md_func(); + CBS cbs; + CBS_init(&cbs, obj->data, obj->length); + return cbs_to_md(&cbs); +} + +const EVP_MD *EVP_parse_digest_algorithm(CBS *cbs) { + CBS algorithm, oid; + if (!CBS_get_asn1(cbs, &algorithm, CBS_ASN1_SEQUENCE) || + !CBS_get_asn1(&algorithm, &oid, CBS_ASN1_OBJECT)) { + OPENSSL_PUT_ERROR(DIGEST, DIGEST_R_DECODE_ERROR); + return NULL; + } + + const EVP_MD *ret = cbs_to_md(&oid); + if (ret == NULL) { + OPENSSL_PUT_ERROR(DIGEST, DIGEST_R_UNKNOWN_HASH); + return NULL; + } + + /* The parameters, if present, must be NULL. Historically, whether the NULL + * was included or omitted was not well-specified. When parsing an + * AlgorithmIdentifier, we allow both. (Note this code is not used when + * verifying RSASSA-PKCS1-v1_5 signatures.) */ + if (CBS_len(&algorithm) > 0) { + CBS param; + if (!CBS_get_asn1(&algorithm, ¶m, CBS_ASN1_NULL) || + CBS_len(¶m) != 0 || + CBS_len(&algorithm) != 0) { + OPENSSL_PUT_ERROR(DIGEST, DIGEST_R_DECODE_ERROR); + return NULL; } } - return NULL; + return ret; } const EVP_MD *EVP_get_digestbyname(const char *name) {
diff --git a/crypto/digest/internal.h b/crypto/digest/internal.h index e3d812a..9f83bcb 100644 --- a/crypto/digest/internal.h +++ b/crypto/digest/internal.h
@@ -104,6 +104,8 @@ EVP_PKEY_CTX* (*dup) (EVP_PKEY_CTX *pctx); }; +const EVP_MD *EVP_parse_digest_algorithm(CBS *cbs); + #if defined(__cplusplus) } /* extern C */
diff --git a/crypto/err/digest.errordata b/crypto/err/digest.errordata index 411e778..bbcd9cd 100644 --- a/crypto/err/digest.errordata +++ b/crypto/err/digest.errordata
@@ -1 +1,3 @@ +DIGEST,101,DECODE_ERROR DIGEST,100,INPUT_NOT_INITIALIZED +DIGEST,102,UNKNOWN_HASH
diff --git a/crypto/pkcs8/pkcs8.c b/crypto/pkcs8/pkcs8.c index 9c8054e..c86ad23 100644 --- a/crypto/pkcs8/pkcs8.c +++ b/crypto/pkcs8/pkcs8.c
@@ -74,6 +74,7 @@ #include "internal.h" #include "../internal.h" #include "../bytestring/internal.h" +#include "../digest/internal.h" #define PKCS12_KEY_ID 1 @@ -1032,25 +1033,25 @@ /* Verify the MAC. */ { - CBS mac, hash_type_seq, hash_oid, salt, expected_mac; - uint64_t iterations; - int hash_nid; - const EVP_MD *md; - uint8_t hmac_key[EVP_MAX_MD_SIZE]; - uint8_t hmac[EVP_MAX_MD_SIZE]; - unsigned hmac_len; + CBS mac, salt, expected_mac; + if (!CBS_get_asn1(&mac_data, &mac, CBS_ASN1_SEQUENCE)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA); + goto err; + } - if (!CBS_get_asn1(&mac_data, &mac, CBS_ASN1_SEQUENCE) || - !CBS_get_asn1(&mac, &hash_type_seq, CBS_ASN1_SEQUENCE) || - !CBS_get_asn1(&hash_type_seq, &hash_oid, CBS_ASN1_OBJECT) || - !CBS_get_asn1(&mac, &expected_mac, CBS_ASN1_OCTETSTRING) || + const EVP_MD *md = EVP_parse_digest_algorithm(&mac); + if (md == NULL) { + goto err; + } + + if (!CBS_get_asn1(&mac, &expected_mac, CBS_ASN1_OCTETSTRING) || !CBS_get_asn1(&mac_data, &salt, CBS_ASN1_OCTETSTRING)) { OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA); goto err; } /* The iteration count is optional and the default is one. */ - iterations = 1; + uint64_t iterations = 1; if (CBS_len(&mac_data) > 0) { if (!CBS_get_asn1_uint64(&mac_data, &iterations) || iterations > UINT_MAX) { @@ -1059,19 +1060,15 @@ } } - hash_nid = OBJ_cbs2nid(&hash_oid); - if (hash_nid == NID_undef || - (md = EVP_get_digestbynid(hash_nid)) == NULL) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNKNOWN_HASH); - goto err; - } - + uint8_t hmac_key[EVP_MAX_MD_SIZE]; if (!pkcs12_key_gen_raw(ctx.password, ctx.password_len, CBS_data(&salt), CBS_len(&salt), PKCS12_MAC_ID, iterations, EVP_MD_size(md), hmac_key, md)) { goto err; } + uint8_t hmac[EVP_MAX_MD_SIZE]; + unsigned hmac_len; if (NULL == HMAC(md, hmac_key, EVP_MD_size(md), CBS_data(&authsafes), CBS_len(&authsafes), hmac, &hmac_len)) { goto err;
diff --git a/include/openssl/digest.h b/include/openssl/digest.h index 87de3df..2de84f7 100644 --- a/include/openssl/digest.h +++ b/include/openssl/digest.h
@@ -283,5 +283,7 @@ #endif #define DIGEST_R_INPUT_NOT_INITIALIZED 100 +#define DIGEST_R_DECODE_ERROR 101 +#define DIGEST_R_UNKNOWN_HASH 102 #endif /* OPENSSL_HEADER_DIGEST_H */