Skip to content

Commit

Permalink
Decouple PKCS8_encrypt and PKCS8_decrypt's core from crypto/asn1.
Browse files Browse the repository at this point in the history
These will be used by Chromium's crypto::ECPrivateKey to work with
EncryptedPrivateKeyInfo structures.

Note this comes with a behavior change: PKCS8_encrypt and PKCS8_decrypt
will no longer preserve PKCS#8 PrivateKeyInfo attributes. However, those
functions are only called by Chromium which does not care. They are also
called by the PEM code, but not in a way which exposes attributes.

The PKCS#12 PFX code is made to use PKCS8_parse_encrypted_private_key
because it's cleaner (no more tossing X509_SIG around) and to ease
decoupling that in the future.

crypto/pkcs8's dependency on the legacy ASN.1 stack is now limited to
pkcs8_x509.c.

BUG=54

Change-Id: I173e605d175e982c6b0250dd22187b73aca15b1a
Reviewed-on: https://boringssl-review.googlesource.com/14215
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
  • Loading branch information
davidben authored and CQ bot account: commit-bot@chromium.org committed Mar 26, 2017
1 parent 3cb1246 commit 02084ea
Show file tree
Hide file tree
Showing 3 changed files with 137 additions and 109 deletions.
123 changes: 37 additions & 86 deletions crypto/pkcs8/pkcs8.c
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,13 @@
#include <limits.h>
#include <string.h>

#include <openssl/buf.h>
#include <openssl/bytestring.h>
#include <openssl/cipher.h>
#include <openssl/digest.h>
#include <openssl/err.h>
#include <openssl/hmac.h>
#include <openssl/mem.h>
#include <openssl/nid.h>
#include <openssl/rand.h>
#include <openssl/x509.h>

#include "internal.h"
#include "../internal.h"
Expand Down Expand Up @@ -412,80 +409,41 @@ int pkcs8_pbe_decrypt(uint8_t **out, size_t *out_len, CBS *algorithm,
return ret;
}

PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8, const char *pass,
int pass_len_i) {
size_t pass_len;
if (pass_len_i == -1 && pass != NULL) {
pass_len = strlen(pass);
} else {
pass_len = (size_t)pass_len_i;
}

PKCS8_PRIV_KEY_INFO *ret = NULL;
uint8_t *in = NULL, *out = NULL;
size_t out_len = 0;

/* Convert the legacy ASN.1 object to a byte string. */
int in_len = i2d_X509_SIG(pkcs8, &in);
if (in_len < 0) {
goto err;
}

EVP_PKEY *PKCS8_parse_encrypted_private_key(CBS *cbs, const char *pass,
size_t pass_len) {
/* See RFC 5208, section 6. */
CBS cbs, epki, algorithm, ciphertext;
CBS_init(&cbs, in, in_len);
if (!CBS_get_asn1(&cbs, &epki, CBS_ASN1_SEQUENCE) ||
CBS epki, algorithm, ciphertext;
if (!CBS_get_asn1(cbs, &epki, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&epki, &algorithm, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&epki, &ciphertext, CBS_ASN1_OCTETSTRING) ||
CBS_len(&epki) != 0 ||
CBS_len(&cbs) != 0) {
CBS_len(&epki) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
goto err;
return 0;
}

uint8_t *out;
size_t out_len;
if (!pkcs8_pbe_decrypt(&out, &out_len, &algorithm, pass, pass_len,
CBS_data(&ciphertext), CBS_len(&ciphertext))) {
goto err;
}

if (out_len > LONG_MAX) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
goto err;
}

/* Convert back to legacy ASN.1 objects. */
const uint8_t *ptr = out;
ret = d2i_PKCS8_PRIV_KEY_INFO(NULL, &ptr, (long)out_len);
OPENSSL_cleanse(out, out_len);
if (ret == NULL || ptr != out + out_len) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_DECODE_ERROR);
PKCS8_PRIV_KEY_INFO_free(ret);
ret = NULL;
return 0;
}

err:
OPENSSL_free(in);
CBS pki;
CBS_init(&pki, out, out_len);
EVP_PKEY *ret = EVP_parse_private_key(&pki);
OPENSSL_cleanse(out, out_len);
OPENSSL_free(out);
return ret;
}

X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
int pass_len_i, const uint8_t *salt, size_t salt_len,
int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
size_t pass_len;
if (pass_len_i == -1 && pass != NULL) {
pass_len = strlen(pass);
} else {
pass_len = (size_t)pass_len_i;
}

X509_SIG *ret = NULL;
uint8_t *plaintext = NULL, *salt_buf = NULL, *der = NULL;
int plaintext_len = -1;
size_t der_len;
CBB cbb;
CBB_zero(&cbb);
int PKCS8_marshal_encrypted_private_key(CBB *out, int pbe_nid,
const EVP_CIPHER *cipher,
const char *pass, size_t pass_len,
const uint8_t *salt, size_t salt_len,
int iterations, const EVP_PKEY *pkey) {
int ret = 0;
uint8_t *plaintext = NULL, *salt_buf = NULL;
size_t plaintext_len = 0;
EVP_CIPHER_CTX ctx;
EVP_CIPHER_CTX_init(&ctx);

Expand All @@ -508,15 +466,17 @@ X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
iterations = PKCS5_DEFAULT_ITERATIONS;
}

/* Convert the input from the legacy ASN.1 format. */
plaintext_len = i2d_PKCS8_PRIV_KEY_INFO(p8inf, &plaintext);
if (plaintext_len < 0) {
/* Serialize the input key. */
CBB plaintext_cbb;
if (!CBB_init(&plaintext_cbb, 128) ||
!EVP_marshal_private_key(&plaintext_cbb, pkey) ||
!CBB_finish(&plaintext_cbb, &plaintext, &plaintext_len)) {
CBB_cleanup(&plaintext_cbb);
goto err;
}

CBB epki;
if (!CBB_init(&cbb, 128) ||
!CBB_add_asn1(&cbb, &epki, CBS_ASN1_SEQUENCE)) {
if (!CBB_add_asn1(out, &epki, CBS_ASN1_SEQUENCE)) {
goto err;
}

Expand All @@ -532,41 +492,32 @@ X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
goto err;
}

size_t max_out = (size_t)plaintext_len + EVP_CIPHER_CTX_block_size(&ctx);
if (max_out < (size_t)plaintext_len) {
size_t max_out = plaintext_len + EVP_CIPHER_CTX_block_size(&ctx);
if (max_out < plaintext_len) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_TOO_LONG);
goto err;
}

CBB ciphertext;
uint8_t *out;
uint8_t *ptr;
int n1, n2;
if (!CBB_add_asn1(&epki, &ciphertext, CBS_ASN1_OCTETSTRING) ||
!CBB_reserve(&ciphertext, &out, max_out) ||
!EVP_CipherUpdate(&ctx, out, &n1, plaintext, plaintext_len) ||
!EVP_CipherFinal_ex(&ctx, out + n1, &n2) ||
!CBB_reserve(&ciphertext, &ptr, max_out) ||
!EVP_CipherUpdate(&ctx, ptr, &n1, plaintext, plaintext_len) ||
!EVP_CipherFinal_ex(&ctx, ptr + n1, &n2) ||
!CBB_did_write(&ciphertext, n1 + n2) ||
!CBB_finish(&cbb, &der, &der_len)) {
!CBB_flush(out)) {
goto err;
}

/* Convert back to legacy ASN.1 objects. */
const uint8_t *ptr = der;
ret = d2i_X509_SIG(NULL, &ptr, der_len);
if (ret == NULL || ptr != der + der_len) {
OPENSSL_PUT_ERROR(PKCS8, ERR_R_INTERNAL_ERROR);
X509_SIG_free(ret);
ret = NULL;
}
ret = 1;

err:
if (plaintext_len > 0) {
if (plaintext != NULL) {
OPENSSL_cleanse(plaintext, plaintext_len);
OPENSSL_free(plaintext);
}
OPENSSL_free(plaintext);
OPENSSL_free(salt_buf);
OPENSSL_free(der);
CBB_cleanup(&cbb);
EVP_CIPHER_CTX_cleanup(&ctx);
return ret;
}
108 changes: 85 additions & 23 deletions crypto/pkcs8/pkcs8_x509.c
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,84 @@ PKCS8_PRIV_KEY_INFO *EVP_PKEY2PKCS8(EVP_PKEY *pkey) {
return NULL;
}

PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8, const char *pass,
int pass_len_in) {
size_t pass_len;
if (pass_len_in == -1 && pass != NULL) {
pass_len = strlen(pass);
} else {
pass_len = (size_t)pass_len_in;
}

PKCS8_PRIV_KEY_INFO *ret = NULL;
EVP_PKEY *pkey = NULL;
uint8_t *in = NULL;

/* Convert the legacy ASN.1 object to a byte string. */
int in_len = i2d_X509_SIG(pkcs8, &in);
if (in_len < 0) {
goto err;
}

CBS cbs;
CBS_init(&cbs, in, in_len);
pkey = PKCS8_parse_encrypted_private_key(&cbs, pass, pass_len);
if (pkey == NULL || CBS_len(&cbs) != 0) {
goto err;
}

ret = EVP_PKEY2PKCS8(pkey);

err:
OPENSSL_free(in);
EVP_PKEY_free(pkey);
return ret;
}

X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
int pass_len_in, const uint8_t *salt, size_t salt_len,
int iterations, PKCS8_PRIV_KEY_INFO *p8inf) {
size_t pass_len;
if (pass_len_in == -1 && pass != NULL) {
pass_len = strlen(pass);
} else {
pass_len = (size_t)pass_len_in;
}

/* Parse out the private key. */
EVP_PKEY *pkey = EVP_PKCS82PKEY(p8inf);
if (pkey == NULL) {
return NULL;
}

X509_SIG *ret = NULL;
uint8_t *der = NULL;
size_t der_len;
CBB cbb;
if (!CBB_init(&cbb, 128) ||
!PKCS8_marshal_encrypted_private_key(&cbb, pbe_nid, cipher, pass,
pass_len, salt, salt_len, iterations,
pkey) ||
!CBB_finish(&cbb, &der, &der_len)) {
CBB_cleanup(&cbb);
goto err;
}

/* Convert back to legacy ASN.1 objects. */
const uint8_t *ptr = der;
ret = d2i_X509_SIG(NULL, &ptr, der_len);
if (ret == NULL || ptr != der + der_len) {
OPENSSL_PUT_ERROR(PKCS8, ERR_R_INTERNAL_ERROR);
X509_SIG_free(ret);
ret = NULL;
}

err:
OPENSSL_free(der);
EVP_PKEY_free(pkey);
return ret;
}

struct pkcs12_context {
EVP_PKEY **out_key;
STACK_OF(X509) *out_certs;
Expand Down Expand Up @@ -239,36 +317,20 @@ static int PKCS12_handle_safe_bag(CBS *safe_bag, struct pkcs12_context *ctx) {
return 0;
}

if (CBS_len(&wrapped_value) > LONG_MAX) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
EVP_PKEY *pkey = PKCS8_parse_encrypted_private_key(
&wrapped_value, ctx->password, ctx->password_len);
if (pkey == NULL) {
return 0;
}

/* |encrypted| isn't actually an X.509 signature, but it has the same
* structure as one and so |X509_SIG| is reused to store it. */
const uint8_t *inp = CBS_data(&wrapped_value);
X509_SIG *encrypted =
d2i_X509_SIG(NULL, &inp, (long)CBS_len(&wrapped_value));
if (encrypted == NULL) {
if (CBS_len(&wrapped_value) != 0) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
return 0;
}
if (inp != CBS_data(&wrapped_value) + CBS_len(&wrapped_value)) {
OPENSSL_PUT_ERROR(PKCS8, PKCS8_R_BAD_PKCS12_DATA);
X509_SIG_free(encrypted);
EVP_PKEY_free(pkey);
return 0;
}

PKCS8_PRIV_KEY_INFO *pki =
PKCS8_decrypt(encrypted, ctx->password, ctx->password_len);
X509_SIG_free(encrypted);
if (pki == NULL) {
return 0;
}

*ctx->out_key = EVP_PKCS82PKEY(pki);
PKCS8_PRIV_KEY_INFO_free(pki);
return ctx->out_key != NULL;
*ctx->out_key = pkey;
return 1;
}

if (CBS_mem_equal(&bag_id, kCertBag, sizeof(kCertBag))) {
Expand Down
15 changes: 15 additions & 0 deletions include/openssl/pkcs8.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,14 @@ OPENSSL_EXPORT X509_SIG *PKCS8_encrypt(int pbe_nid, const EVP_CIPHER *cipher,
int iterations,
PKCS8_PRIV_KEY_INFO *p8inf);

/* PKCS8_marshal_encrypted_private_key behaves like |PKCS8_encrypt| but encrypts
* an |EVP_PKEY| and writes the serialized EncryptedPrivateKeyInfo to |out|. It
* returns one on success and zero on error. */
OPENSSL_EXPORT int PKCS8_marshal_encrypted_private_key(
CBB *out, int pbe_nid, const EVP_CIPHER *cipher, const char *pass,
size_t pass_len, const uint8_t *salt, size_t salt_len, int iterations,
const EVP_PKEY *pkey);

/* PKCS8_decrypt decrypts and decodes a PKCS8_PRIV_KEY_INFO with PBES1 or PBES2
* as defined in PKCS #5. Only pbeWithSHAAnd128BitRC4,
* pbeWithSHAAnd3-KeyTripleDES-CBC and pbeWithSHA1And40BitRC2, and PBES2,
Expand All @@ -103,6 +111,13 @@ OPENSSL_EXPORT PKCS8_PRIV_KEY_INFO *PKCS8_decrypt(X509_SIG *pkcs8,
const char *pass,
int pass_len);

/* PKCS8_parse_encrypted_private_key behaves like |PKCS8_decrypt| but it parses
* the EncryptedPrivateKeyInfo structure from |cbs| and advances |cbs|. It
* returns a newly-allocated |EVP_PKEY| on success and zero on error. */
OPENSSL_EXPORT EVP_PKEY *PKCS8_parse_encrypted_private_key(CBS *cbs,
const char *pass,
size_t pass_len);

/* PKCS12_get_key_and_certs parses a PKCS#12 structure from |in|, authenticates
* and decrypts it using |password|, sets |*out_key| to the included private
* key and appends the included certificates to |out_certs|. It returns one on
Expand Down

0 comments on commit 02084ea

Please sign in to comment.