Reimplement PKCS5_v2_PBE_keyivgen. This gets us closer to decoupling from crypto/asn1. BUG=54 Change-Id: I06ec04ed3cb47c2f56a94c6defa97398bfd0e013 Reviewed-on: https://boringssl-review.googlesource.com/13066 Reviewed-by: Adam Langley <alangley@gmail.com>
diff --git a/crypto/pkcs8/internal.h b/crypto/pkcs8/internal.h index e777467..26f7559 100644 --- a/crypto/pkcs8/internal.h +++ b/crypto/pkcs8/internal.h
@@ -71,21 +71,7 @@ ASN1_INTEGER *iter; } PBEPARAM; -typedef struct { - X509_ALGOR *keyfunc; - X509_ALGOR *encryption; -} PBE2PARAM; - -typedef struct { - ASN1_TYPE *salt; /* Usually OCTET STRING but could be anything */ - ASN1_INTEGER *iter; - ASN1_INTEGER *keylength; - X509_ALGOR *prf; -} PBKDF2PARAM; - DECLARE_ASN1_FUNCTIONS(PBEPARAM) -DECLARE_ASN1_FUNCTIONS(PBE2PARAM) -DECLARE_ASN1_FUNCTIONS(PBKDF2PARAM) /* PKCS5_v2_PBE_keyivgen intializes the supplied |ctx| for PBKDF v2, which must * be specified by |param|. The password is specified by |pass_raw| and
diff --git a/crypto/pkcs8/p5_pbev2.c b/crypto/pkcs8/p5_pbev2.c index 5111238..e48191d 100644 --- a/crypto/pkcs8/p5_pbev2.c +++ b/crypto/pkcs8/p5_pbev2.c
@@ -53,17 +53,17 @@ * (eay@cryptsoft.com). This product includes software written by Tim * Hudson (tjh@cryptsoft.com). */ -#include <assert.h> +#include <openssl/pkcs8.h> + #include <limits.h> #include <string.h> -#include <openssl/asn1t.h> +#include <openssl/asn1.h> #include <openssl/bytestring.h> #include <openssl/cipher.h> #include <openssl/err.h> #include <openssl/mem.h> #include <openssl/obj.h> -#include <openssl/pkcs8.h> #include <openssl/rand.h> #include <openssl/x509.h> @@ -71,24 +71,6 @@ #include "../internal.h" -/* PKCS#5 v2.0 password based encryption structures */ - -ASN1_SEQUENCE(PBE2PARAM) = { - ASN1_SIMPLE(PBE2PARAM, keyfunc, X509_ALGOR), - ASN1_SIMPLE(PBE2PARAM, encryption, X509_ALGOR) -} ASN1_SEQUENCE_END(PBE2PARAM) - -IMPLEMENT_ASN1_FUNCTIONS(PBE2PARAM) - -ASN1_SEQUENCE(PBKDF2PARAM) = { - ASN1_SIMPLE(PBKDF2PARAM, salt, ASN1_ANY), - ASN1_SIMPLE(PBKDF2PARAM, iter, ASN1_INTEGER), - ASN1_OPT(PBKDF2PARAM, keylength, ASN1_INTEGER), - ASN1_OPT(PBKDF2PARAM, prf, X509_ALGOR) -} ASN1_SEQUENCE_END(PBKDF2PARAM) - -IMPLEMENT_ASN1_FUNCTIONS(PBKDF2PARAM) - X509_ALGOR *PKCS5_pbe2_set(const EVP_CIPHER *cipher, int iter, const uint8_t *salt, size_t salt_len) { int cipher_nid = EVP_CIPHER_nid(cipher); @@ -167,138 +149,114 @@ return ret; } -static int PKCS5_v2_PBKDF2_keyivgen(EVP_CIPHER_CTX *ctx, - const uint8_t *pass_raw, - size_t pass_raw_len, const ASN1_TYPE *param, - const ASN1_TYPE *iv, int enc) { - int rv = 0; - PBKDF2PARAM *pbkdf2param = NULL; - - if (EVP_CIPHER_CTX_cipher(ctx) == NULL) { - OPENSSL_PUT_ERROR(PKCS8, CIPHER_R_NO_CIPHER_SET); - goto err; - } - - /* Decode parameters. */ - if (param == NULL || param->type != V_ASN1_SEQUENCE) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR); - goto err; - } - - const uint8_t *pbuf = param->value.sequence->data; - int plen = param->value.sequence->length; - pbkdf2param = d2i_PBKDF2PARAM(NULL, &pbuf, plen); - if (pbkdf2param == NULL || pbuf != param->value.sequence->data + plen) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR); - goto err; - } - - /* Now check the parameters. */ - uint8_t key[EVP_MAX_KEY_LENGTH]; - const size_t key_len = EVP_CIPHER_CTX_key_length(ctx); - assert(key_len <= sizeof(key)); - - if (pbkdf2param->keylength != NULL && - ASN1_INTEGER_get(pbkdf2param->keylength) != (int) key_len) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEYLENGTH); - goto err; - } - - if (pbkdf2param->prf != NULL && - OBJ_obj2nid(pbkdf2param->prf->algorithm) != NID_hmacWithSHA1) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF); - goto err; - } - - if (pbkdf2param->salt->type != V_ASN1_OCTET_STRING) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_SALT_TYPE); - goto err; - } - - if (pbkdf2param->iter->type != V_ASN1_INTEGER) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT); - goto err; - } - long iterations = ASN1_INTEGER_get(pbkdf2param->iter); - if (iterations <= 0 || - (sizeof(long) > sizeof(unsigned) && iterations > (long)UINT_MAX)) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT); - goto err; - } - - if (iv->type != V_ASN1_OCTET_STRING || iv->value.octet_string == NULL) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS); - goto err; - } - - const size_t iv_len = EVP_CIPHER_CTX_iv_length(ctx); - if ((size_t) iv->value.octet_string->length != iv_len) { - OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS); - goto err; - } - - if (!PKCS5_PBKDF2_HMAC_SHA1((const char *) pass_raw, pass_raw_len, - pbkdf2param->salt->value.octet_string->data, - pbkdf2param->salt->value.octet_string->length, - iterations, key_len, key)) { - goto err; - } - - rv = EVP_CipherInit_ex(ctx, NULL /* cipher */, NULL /* engine */, key, - iv->value.octet_string->data, enc); - - err: - PBKDF2PARAM_free(pbkdf2param); - return rv; -} - int PKCS5_v2_PBE_keyivgen(EVP_CIPHER_CTX *ctx, const uint8_t *pass_raw, size_t pass_raw_len, ASN1_TYPE *param, const EVP_CIPHER *unused, const EVP_MD *unused2, int enc) { - PBE2PARAM *pbe2param = NULL; - int rv = 0; - if (param == NULL || param->type != V_ASN1_SEQUENCE || param->value.sequence == NULL) { OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR); - goto err; + return 0; } - const uint8_t *pbuf = param->value.sequence->data; - int plen = param->value.sequence->length; - pbe2param = d2i_PBE2PARAM(NULL, &pbuf, plen); - if (pbe2param == NULL || pbuf != param->value.sequence->data + plen) { + CBS cbs, pbe_param, kdf, kdf_obj, enc_scheme, enc_obj; + CBS_init(&cbs, param->value.sequence->data, param->value.sequence->length); + if (!CBS_get_asn1(&cbs, &pbe_param, CBS_ASN1_SEQUENCE) || + CBS_len(&cbs) != 0 || + !CBS_get_asn1(&pbe_param, &kdf, CBS_ASN1_SEQUENCE) || + !CBS_get_asn1(&pbe_param, &enc_scheme, CBS_ASN1_SEQUENCE) || + CBS_len(&pbe_param) != 0 || + !CBS_get_asn1(&kdf, &kdf_obj, CBS_ASN1_OBJECT) || + !CBS_get_asn1(&enc_scheme, &enc_obj, CBS_ASN1_OBJECT)) { OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR); - goto err; + return 0; } /* Check that the key derivation function is PBKDF2. */ - if (OBJ_obj2nid(pbe2param->keyfunc->algorithm) != NID_id_pbkdf2) { + if (OBJ_cbs2nid(&kdf_obj) != NID_id_pbkdf2) { OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEY_DERIVATION_FUNCTION); - goto err; + return 0; } /* See if we recognise the encryption algorithm. */ - const EVP_CIPHER *cipher = - EVP_get_cipherbynid(OBJ_obj2nid(pbe2param->encryption->algorithm)); + const EVP_CIPHER *cipher = EVP_get_cipherbynid(OBJ_cbs2nid(&enc_obj)); if (cipher == NULL) { OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_CIPHER); - goto err; + return 0; } - /* Fixup cipher based on AlgorithmIdentifier. */ - if (!EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, NULL /* key */, - NULL /* iv */, enc)) { - goto err; + /* Parse the KDF parameters. */ + CBS pbkdf2_params, salt; + uint64_t iterations; + if (!CBS_get_asn1(&kdf, &pbkdf2_params, CBS_ASN1_SEQUENCE) || + CBS_len(&kdf) != 0 || + !CBS_get_asn1(&pbkdf2_params, &salt, CBS_ASN1_OCTETSTRING) || + !CBS_get_asn1_uint64(&pbkdf2_params, &iterations)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR); + return 0; } - rv = PKCS5_v2_PBKDF2_keyivgen(ctx, pass_raw, pass_raw_len, - pbe2param->keyfunc->parameter, - pbe2param->encryption->parameter, enc); + if (iterations == 0 || iterations > UINT_MAX) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_ITERATION_COUNT); + return 0; + } - err: - PBE2PARAM_free(pbe2param); - return rv; + /* The optional keyLength parameter, if present, must match the key length of + * the cipher. */ + if (CBS_peek_asn1_tag(&pbkdf2_params, CBS_ASN1_INTEGER)) { + uint64_t key_len; + if (!CBS_get_asn1_uint64(&pbkdf2_params, &key_len)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR); + return 0; + } + + if (key_len != EVP_CIPHER_key_length(cipher)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_KEYLENGTH); + return 0; + } + } + + if (CBS_len(&pbkdf2_params) != 0) { + CBS prf; + if (!CBS_get_asn1(&pbkdf2_params, &prf, CBS_ASN1_OBJECT) || + CBS_len(&pbkdf2_params) != 0) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR); + return 0; + } + + /* We only support hmacWithSHA1. It is the DEFAULT, so DER requires it be + * omitted, but we match OpenSSL in tolerating it being present. */ + if (OBJ_cbs2nid(&prf) != NID_hmacWithSHA1) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF); + return 0; + } + } + + /* Parse the encryption scheme parameters. Note OpenSSL does not match the + * specification. Per RFC 2898, this should depend on the encryption scheme. + * In particular, RC2-CBC and RC5-CBC-Pad use a SEQUENCE with version and IV. + * We align with OpenSSL. */ + CBS iv; + if (!CBS_get_asn1(&enc_scheme, &iv, CBS_ASN1_OCTETSTRING) || + CBS_len(&enc_scheme) != 0) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_UNSUPPORTED_PRF); + return 0; + } + + if (CBS_len(&iv) != EVP_CIPHER_iv_length(cipher)) { + OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_ERROR_SETTING_CIPHER_PARAMS); + return 0; + } + + uint8_t key[EVP_MAX_KEY_LENGTH]; + if (!PKCS5_PBKDF2_HMAC_SHA1( + (const char *)pass_raw, pass_raw_len, CBS_data(&salt), CBS_len(&salt), + (unsigned)iterations, EVP_CIPHER_key_length(cipher), key) || + !EVP_CipherInit_ex(ctx, cipher, NULL /* engine */, key, CBS_data(&iv), + enc)) { + return 0; + } + + return 1; }