From 5e4e73d6e904bb64060423b319713875d04b4d71 Mon Sep 17 00:00:00 2001 From: Hayden Roche Date: Tue, 25 May 2021 16:07:35 -0700 Subject: [PATCH 1/3] Add an API function wc_EncryptPKCS8Key to handle encrypting a DER, PKCS#8-formatted key. There's already a function wc_CreatePKCS8Key, but this only creates the unencrypted PKCS#8 key. TraditionalEnc exists, which takes a non-PKCS#8 key, converts it to PKCS#8 format, and encrypts it, but this function isn't in the public-facing API. I've modified TraditionalEnc to use wc_EncryptPKCS8Key after wc_CreatePKCS8Key. wc_EncryptPKCS8Key is essentially the encryption portion of TraditionalEnc moved out into its own function. wc_EncryptPKCS8Key will be in the API going forward so that users can do PKCS#8 encryption without relying on the non-API TraditionalEnc. Next, I'll be adding a corresponding wc_DecryptPKCS8Key to handle decryption. --- wolfcrypt/src/asn.c | 289 ++++++++++++++++++++------------- wolfssl/wolfcrypt/asn_public.h | 2 + 2 files changed, 174 insertions(+), 117 deletions(-) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 51ef95c16..fe20f4bc1 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -3738,43 +3738,75 @@ static int GetAlgoV2(int encAlgId, const byte** oid, int *len, int* id, return ret; } -/* Converts Encrypted PKCS#8 to 'traditional' (i.e. PKCS#8 removed from - * decrypted key.) +/* PKCS#8 encryption from RFC 5208 + * This function takes in an unencrypted PKCS#8 DER key and converts it to + * PKCS#8 encrypted format. The resulting encrypted key can be decrypted using + * wc_DecryptPKCS8Key. + * + * EncryptedPrivateKeyInfo ::= SEQUENCE { + * encryptionAlgorithm EncryptionAlgorithmIdentifier, + * encryptedData EncryptedData } + * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * EncryptedData ::= OCTET STRING + * + * key DER buffer containing the unencrypted PKCS#8 key. + * keySz The size of the key buffer. + * out The buffer to place the encrypted key in. + * outSz The size of the out buffer. + * password The password to use for the password-based encryption algorithm. + * passwordSz The length of the password (not including the NULL terminator). + * vPKCS The PKCS version to use. Can be 1 for PKCS12 or PKCS5. + * pbeOid The OID of the PBE scheme to use (e.g. PBES2 or one of the OIDs + for PBES1 in RFC 2898 A.3) + * encAlgId The encryption algorithm ID to use (e.g. AES256CBCb). + * salt The salt buffer to use. If NULL, a random salt will be used. + * saltSz The length of the salt buffer. Can be 0 if passing NULL for salt. + * itt The number of iterations to use for the KDF. + * rng A pointer to an initialized WC_RNG object. + * heap A pointer to the heap use for dynamic allocation. Can be NULL. + * + * Returns the size of the encrypted key placed in out. In error cases, returns + * negative values. */ -int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz, - const char* password, int passwordSz, int vPKCS, int vAlgo, +int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32 outSz, + const char* password, int passwordSz, int vPKCS, int pbeOid, int encAlgId, byte* salt, word32 saltSz, int itt, WC_RNG* rng, void* heap) { - int ret = 0; - int version, blockSz, id; - word32 idx = 0, encIdx; #ifdef WOLFSSL_SMALL_STACK byte* saltTmp = NULL; #else byte saltTmp[MAX_SALT_SIZE]; #endif - byte cbcIv[MAX_IV_SIZE]; - byte *pkcs8Key = NULL; - word32 pkcs8KeySz = 0, padSz = 0; - int algId = 0; - const byte* curveOid = NULL; - word32 curveOidSz = 0; - const byte* pbeOid = NULL; - word32 pbeOidSz = 0; + int ret = 0; + int version = 0; + int pbeId = 0; + int blockSz = 0; const byte* encOid = NULL; int encOidSz = 0; - word32 pbeLen = 0, kdfLen = 0, encLen = 0; - word32 innerLen = 0, outerLen; + word32 padSz = 0; + word32 innerLen = 0; + word32 outerLen = 0; + const byte* pbeOidBuf = NULL; + word32 pbeOidBufSz = 0; + word32 pbeLen = 0; + word32 kdfLen = 0; + word32 encLen = 0; + byte cbcIv[MAX_IV_SIZE]; + word32 idx = 0; + word32 encIdx = 0; - ret = CheckAlgo(vPKCS, vAlgo, &id, &version, &blockSz); - /* create random salt if one not provided */ + (void)heap; + + WOLFSSL_ENTER("wc_EncryptPKCS8Key"); + + ret = CheckAlgo(vPKCS, pbeOid, &pbeId, &version, &blockSz); if (ret == 0 && (salt == NULL || saltSz == 0)) { saltSz = 8; #ifdef WOLFSSL_SMALL_STACK saltTmp = (byte*)XMALLOC(saltSz, heap, DYNAMIC_TYPE_TMP_BUFFER); if (saltTmp == NULL) - return MEMORY_E; + ret = MEMORY_E; #endif salt = saltTmp; @@ -3783,10 +3815,126 @@ int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz, #ifdef WOLFSSL_SMALL_STACK XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); #endif - return ret; } } + if (ret == 0 && version == PKCS5v2) + ret = GetAlgoV2(encAlgId, &encOid, &encOidSz, &pbeId, &blockSz); + + if (ret == 0) { + padSz = (blockSz - (keySz & (blockSz - 1))) & (blockSz - 1); + /* inner = OCT salt INT itt */ + innerLen = 2 + saltSz + 2 + (itt < 256 ? 1 : 2); + + if (version != PKCS5v2) { + pbeOidBuf = OidFromId(pbeId, oidPBEType, &pbeOidBufSz); + /* pbe = OBJ pbse1 SEQ [ inner ] */ + pbeLen = 2 + pbeOidBufSz + 2 + innerLen; + } + else { + pbeOidBuf = pbes2; + pbeOidBufSz = sizeof(pbes2); + /* kdf = OBJ pbkdf2 [ SEQ innerLen ] */ + kdfLen = 2 + sizeof(pbkdf2Oid) + 2 + innerLen; + /* enc = OBJ enc_alg OCT iv */ + encLen = 2 + encOidSz + 2 + blockSz; + /* pbe = OBJ pbse2 SEQ [ SEQ [ kdf ] SEQ [ enc ] ] */ + pbeLen = 2 + sizeof(pbes2) + 2 + 2 + kdfLen + 2 + encLen; + + ret = wc_RNG_GenerateBlock(rng, cbcIv, blockSz); + } + } + if (ret == 0) { + /* outer = SEQ [ pbe ] OCT encrypted_PKCS#8_key */ + outerLen = 2 + pbeLen; + outerLen += SetOctetString(keySz + padSz, out); + outerLen += keySz + padSz; + + idx += SetSequence(outerLen, out + idx); + + encIdx = idx + outerLen - keySz - padSz; + /* Put Encrypted content in place. */ + XMEMCPY(out + encIdx, key, keySz); + if (padSz > 0) { + XMEMSET(out + encIdx + keySz, padSz, padSz); + keySz += padSz; + } + ret = wc_CryptKey(password, passwordSz, salt, saltSz, itt, pbeId, + out + encIdx, keySz, version, cbcIv, 1, 0); + } + if (ret == 0) { + if (version != PKCS5v2) { + /* PBE algorithm */ + idx += SetSequence(pbeLen, out + idx); + idx += SetObjectId(pbeOidBufSz, out + idx); + XMEMCPY(out + idx, pbeOidBuf, pbeOidBufSz); + idx += pbeOidBufSz; + } + else { + /* PBES2 algorithm identifier */ + idx += SetSequence(pbeLen, out + idx); + idx += SetObjectId(pbeOidBufSz, out + idx); + XMEMCPY(out + idx, pbeOidBuf, pbeOidBufSz); + idx += pbeOidBufSz; + /* PBES2 Parameters: SEQ [ kdf ] SEQ [ enc ] */ + idx += SetSequence(2 + kdfLen + 2 + encLen, out + idx); + /* KDF Algorithm Identifier */ + idx += SetSequence(kdfLen, out + idx); + idx += SetObjectId(sizeof(pbkdf2Oid), out + idx); + XMEMCPY(out + idx, pbkdf2Oid, sizeof(pbkdf2Oid)); + idx += sizeof(pbkdf2Oid); + } + idx += SetSequence(innerLen, out + idx); + idx += SetOctetString(saltSz, out + idx); + XMEMCPY(out + idx, salt, saltSz); idx += saltSz; + ret = SetShortInt(out, &idx, itt, outSz); + if (ret > 0) + ret = 0; + } + if (ret == 0) { + if (version == PKCS5v2) { + /* Encryption Algorithm Identifier */ + idx += SetSequence(encLen, out + idx); + idx += SetObjectId(encOidSz, out + idx); + XMEMCPY(out + idx, encOid, encOidSz); + idx += encOidSz; + /* Encryption Algorithm Parameter: CBC IV */ + idx += SetOctetString(blockSz, out + idx); + XMEMCPY(out + idx, cbcIv, blockSz); + idx += blockSz; + } + idx += SetOctetString(keySz, out + idx); + /* Default PRF - no need to write out OID */ + idx += keySz; + + ret = idx; + } + +#ifdef WOLFSSL_SMALL_STACK + if (saltTmp != NULL) { + XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); + } +#endif + + WOLFSSL_LEAVE("wc_EncryptPKCS8Key", ret); + + return ret; +} + +/* Takes an unencrypted, traditional DER-encoded key and converts it to a PKCS#8 + * encrypted key. */ +int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz, + const char* password, int passwordSz, int vPKCS, int vAlgo, + int encAlgId, byte* salt, word32 saltSz, int itt, WC_RNG* rng, + void* heap) +{ + int ret = 0; + byte *pkcs8Key = NULL; + word32 pkcs8KeySz = 0; + int algId = 0; + const byte* curveOid = NULL; + word32 curveOidSz = 0; + if (ret == 0) { /* check key type and get OID if ECC */ ret = wc_GetKeyOID(key, keySz, &curveOid, &curveOidSz, &algId, heap); @@ -3806,114 +3954,21 @@ int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz, } if (ret == 0) { ret = wc_CreatePKCS8Key(pkcs8Key, &pkcs8KeySz, key, keySz, algId, - curveOid, curveOidSz); + curveOid, curveOidSz); if (ret >= 0) { pkcs8KeySz = ret; ret = 0; } } - - if (ret == 0 && version == PKCS5v2) - ret = GetAlgoV2(encAlgId, &encOid, &encOidSz, &id, &blockSz); - if (ret == 0) { - padSz = (blockSz - (pkcs8KeySz & (blockSz - 1))) & (blockSz - 1); - /* inner = OCT salt INT itt */ - innerLen = 2 + saltSz + 2 + (itt < 256 ? 1 : 2); - - if (version != PKCS5v2) { - pbeOid = OidFromId(id, oidPBEType, &pbeOidSz); - /* pbe = OBJ pbse1 SEQ [ inner ] */ - pbeLen = 2 + pbeOidSz + 2 + innerLen; - } - else { - pbeOid = pbes2; - pbeOidSz = sizeof(pbes2); - /* kdf = OBJ pbkdf2 [ SEQ innerLen ] */ - kdfLen = 2 + sizeof(pbkdf2Oid) + 2 + innerLen; - /* enc = OBJ enc_alg OCT iv */ - encLen = 2 + encOidSz + 2 + blockSz; - /* pbe = OBJ pbse2 SEQ [ SEQ [ kdf ] SEQ [ enc ] ] */ - pbeLen = 2 + sizeof(pbes2) + 2 + 2 + kdfLen + 2 + encLen; - - ret = wc_RNG_GenerateBlock(rng, cbcIv, blockSz); - } - } - if (ret == 0) { - /* outer = SEQ [ pbe ] OCT encrypted_PKCS#8_key */ - outerLen = 2 + pbeLen; - outerLen += SetOctetString(pkcs8KeySz + padSz, out); - outerLen += pkcs8KeySz + padSz; - - idx += SetSequence(outerLen, out + idx); - - encIdx = idx + outerLen - pkcs8KeySz - padSz; - /* Put Encrypted content in place. */ - XMEMCPY(out + encIdx, pkcs8Key, pkcs8KeySz); - if (padSz > 0) { - XMEMSET(out + encIdx + pkcs8KeySz, padSz, padSz); - pkcs8KeySz += padSz; - } - ret = wc_CryptKey(password, passwordSz, salt, saltSz, itt, id, - out + encIdx, pkcs8KeySz, version, cbcIv, 1, 0); - } - if (ret == 0) { - if (version != PKCS5v2) { - /* PBE algorithm */ - idx += SetSequence(pbeLen, out + idx); - idx += SetObjectId(pbeOidSz, out + idx); - XMEMCPY(out + idx, pbeOid, pbeOidSz); - idx += pbeOidSz; - } - else { - /* PBES2 algorithm identifier */ - idx += SetSequence(pbeLen, out + idx); - idx += SetObjectId(pbeOidSz, out + idx); - XMEMCPY(out + idx, pbeOid, pbeOidSz); - idx += pbeOidSz; - /* PBES2 Parameters: SEQ [ kdf ] SEQ [ enc ] */ - idx += SetSequence(2 + kdfLen + 2 + encLen, out + idx); - /* KDF Algorithm Identifier */ - idx += SetSequence(kdfLen, out + idx); - idx += SetObjectId(sizeof(pbkdf2Oid), out + idx); - XMEMCPY(out + idx, pbkdf2Oid, sizeof(pbkdf2Oid)); - idx += sizeof(pbkdf2Oid); - } - idx += SetSequence(innerLen, out + idx); - idx += SetOctetString(saltSz, out + idx); - XMEMCPY(out + idx, salt, saltSz); idx += saltSz; - ret = SetShortInt(out, &idx, itt, *outSz); - if (ret > 0) - ret = 0; - } - if (ret == 0) { - if (version == PKCS5v2) { - /* Encryption Algorithm Identifier */ - idx += SetSequence(encLen, out + idx); - idx += SetObjectId(encOidSz, out + idx); - XMEMCPY(out + idx, encOid, encOidSz); - idx += encOidSz; - /* Encryption Algorithm Parameter: CBC IV */ - idx += SetOctetString(blockSz, out + idx); - XMEMCPY(out + idx, cbcIv, blockSz); - idx += blockSz; - } - idx += SetOctetString(pkcs8KeySz, out + idx); - /* Default PRF - no need to write out OID */ - idx += pkcs8KeySz; - - ret = idx; + ret = wc_EncryptPKCS8Key(pkcs8Key, pkcs8KeySz, out, *outSz, password, + passwordSz, vPKCS, vAlgo, encAlgId, salt, saltSz, itt, rng, heap); } if (pkcs8Key != NULL) { ForceZero(pkcs8Key, pkcs8KeySz); XFREE(pkcs8Key, NULL, DYNAMIC_TYPE_TMP_BUFFER); } -#ifdef WOLFSSL_SMALL_STACK - if (saltTmp != NULL) { - XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER); - } -#endif (void)rng; diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index ed1775d91..2e539c358 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -583,6 +583,8 @@ WOLFSSL_API int wc_GetPkcs8TraditionalOffset(byte* input, word32* inOutIdx, word32 sz); WOLFSSL_API int wc_CreatePKCS8Key(byte* out, word32* outSz, byte* key, word32 keySz, int algoID, const byte* curveOID, word32 oidSz); +WOLFSSL_API int wc_EncryptPKCS8Key(byte*, word32, byte*, word32, const char*, + int, int, int, int, byte*, word32, int, WC_RNG*, void*); #ifndef NO_ASN_TIME /* Time */ From 88370285cc95835c8e37e8100e783a0ec8611c03 Mon Sep 17 00:00:00 2001 From: Hayden Roche Date: Wed, 26 May 2021 10:26:39 -0700 Subject: [PATCH 2/3] Add an API function wc_DecryptPKCS8Key to handle decrypting a DER, PKCS#8 encrypted key. --- wolfcrypt/src/asn.c | 61 ++++++++++++++++++++++++++-------- wolfssl/wolfcrypt/asn_public.h | 1 + 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index fe20f4bc1..e322a5999 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -3921,6 +3921,48 @@ int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32 outSz, return ret; } +/* PKCS#8 decryption from RFC 5208 + * + * NOTE: input buffer is overwritten with decrypted data! + * + * This function takes an encrypted PKCS#8 DER key and decrypts it to PKCS#8 + * unencrypted DER. Undoes the encryption done by wc_EncryptPKCS8Key. Returns + * the length of the decrypted buffer or a negative value if there was an error. + */ +int wc_DecryptPKCS8Key(byte* input, word32 sz, const char* password, + int passwordSz) +{ + int ret; + int length; + word32 inOutIdx = 0; + + if (GetSequence(input, &inOutIdx, &length, sz) < 0) { + ret = ASN_PARSE_E; + } + else { + ret = DecryptContent(input + inOutIdx, sz - inOutIdx, password, + passwordSz); + if (ret > 0) { + XMEMMOVE(input, input + inOutIdx, ret); + } + } + + if (ret > 0) { + /* DecryptContent will decrypt the data, but it will leave any padding + * bytes intact. This code calculates the length without the padding + * and we return that to the user. */ + inOutIdx = 0; + if (GetSequence(input, &inOutIdx, &length, ret) < 0) { + ret = ASN_PARSE_E; + } + else { + ret = inOutIdx + length; + } + } + + return ret; +} + /* Takes an unencrypted, traditional DER-encoded key and converts it to a PKCS#8 * encrypted key. */ int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz, @@ -4146,25 +4188,16 @@ exit_dc: return ret; } - /* Remove Encrypted PKCS8 header, move beginning of traditional to beginning of input */ -int ToTraditionalEnc(byte* input, word32 sz,const char* password, +int ToTraditionalEnc(byte* input, word32 sz, const char* password, int passwordSz, word32* algId) { - int ret, length; - word32 inOutIdx = 0; + int ret; - if (GetSequence(input, &inOutIdx, &length, sz) < 0) { - ret = ASN_PARSE_E; - } - else { - ret = DecryptContent(input + inOutIdx, sz - inOutIdx, password, - passwordSz); - if (ret > 0) { - XMEMMOVE(input, input + inOutIdx, ret); - ret = ToTraditional_ex(input, ret, algId); - } + ret = wc_DecryptPKCS8Key(input, sz, password, passwordSz); + if (ret > 0) { + ret = ToTraditional_ex(input, ret, algId); } return ret; diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 2e539c358..289f3620e 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -585,6 +585,7 @@ WOLFSSL_API int wc_CreatePKCS8Key(byte* out, word32* outSz, byte* key, word32 keySz, int algoID, const byte* curveOID, word32 oidSz); WOLFSSL_API int wc_EncryptPKCS8Key(byte*, word32, byte*, word32, const char*, int, int, int, int, byte*, word32, int, WC_RNG*, void*); +WOLFSSL_API int wc_DecryptPKCS8Key(byte*, word32, const char*, int); #ifndef NO_ASN_TIME /* Time */ From 66c7acb0766d597d5942058a9194c87e4dc6ed57 Mon Sep 17 00:00:00 2001 From: Jacob Barthelmeh Date: Thu, 3 Jun 2021 23:38:30 +0700 Subject: [PATCH 3/3] add use of heap hint for malloc --- wolfcrypt/src/asn.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index e322a5999..84aacdce4 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -3990,7 +3990,7 @@ int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz, ret = 0; } if (ret == 0) { - pkcs8Key = (byte*)XMALLOC(pkcs8KeySz, NULL, DYNAMIC_TYPE_TMP_BUFFER); + pkcs8Key = (byte*)XMALLOC(pkcs8KeySz, heap, DYNAMIC_TYPE_TMP_BUFFER); if (pkcs8Key == NULL) ret = MEMORY_E; } @@ -4009,7 +4009,7 @@ int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz, if (pkcs8Key != NULL) { ForceZero(pkcs8Key, pkcs8KeySz); - XFREE(pkcs8Key, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(pkcs8Key, heap, DYNAMIC_TYPE_TMP_BUFFER); } (void)rng;