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;
}