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.
This commit is contained in:
Hayden Roche
2021-05-25 16:07:35 -07:00
parent 41af3da0e3
commit 5e4e73d6e9
2 changed files with 174 additions and 117 deletions

View File

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

View File

@@ -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 */