Make a bunch of PKCS#8 improvements.

- Add doxygen documentation for wc_GetPkcs8TraditionalOffset, wc_CreatePKCS8Key,
wc_EncryptPKCS8Key, and wc_DecryptPKCS8Key.
- Add a new API function, wc_CreateEncryptedPKCS8Key, which handles both
creation of an unencrypted PKCS#8 key and the subsequent encrypting of said key.
This is a wrapper around TraditionalEnc, which does the same thing. This may
become a first-class function at some point (i.e. not a wrapper). TraditionalEnc
is left as is since it is used in the wild.
- Added a unit test which exercises wc_CreateEncryptedPKCS8Key and
wc_DecryptPKCS8Key. Testing wc_CreateEncryptedPKCS8Key inherently also tests
TraditionalEnc, wc_CreatePKCS8Key, and wc_EncryptPKCS8Key.
- Modified wc_EncryptPKCS8Key to be able to return the required output buffer
size via LENGTH_ONLY_E idiom.
- Added parameter checking to wc_EncryptPKCS8Key and wc_DecryptPKCS8Key.
This commit is contained in:
Hayden Roche 2021-06-11 11:45:26 -07:00
parent 2923d812bd
commit b3401bd102
4 changed files with 360 additions and 319 deletions

View File

@ -1629,3 +1629,245 @@ WOLFSSL_API int wc_GetCTC_HashOID(int type);
*/
WOLFSSL_API void wc_SetCert_Free(Cert* cert);
/*!
\ingroup ASN
\brief This function finds the beginning of the traditional private key
inside a PKCS#8 unencrypted buffer.
\return Length of traditional private key on success.
\return Negative values on failure.
\param input Buffer containing unencrypted PKCS#8 private key.
\param inOutIdx Index into the input buffer. On input, it should be a byte
offset to the beginning of the the PKCS#8 buffer. On output, it will be the
byte offset to the traditional private key within the input buffer.
\param sz The number of bytes in the input buffer.
_Example_
\code
byte* pkcs8Buf; // Buffer containing PKCS#8 key.
word32 idx = 0;
word32 sz; // Size of pkcs8Buf.
...
ret = wc_GetPkcs8TraditionalOffset(pkcs8Buf, &idx, sz);
// pkcs8Buf + idx is now the beginning of the traditional private key bytes.
\endcode
\sa wc_CreatePKCS8Key
\sa wc_EncryptPKCS8Key
\sa wc_DecryptPKCS8Key
\sa wc_CreateEncryptedPKCS8Key
*/
WOLFSSL_API int wc_GetPkcs8TraditionalOffset(byte* input,
word32* inOutIdx, word32 sz);
/*!
\ingroup ASN
\brief This function takes in a DER private key and converts it to PKCS#8
format. Also used in creating PKCS#12 shrouded key bags. See RFC 5208.
\return The size of the PKCS#8 key placed into out on success.
\return LENGTH_ONLY_E if out is NULL, with required output buffer size in
outSz.
\return Other negative values on failure.
\param out Buffer to place result in. If NULL, required out buffer size
returned in outSz.
\param outSz Size of out buffer.
\param key Buffer with traditional DER key.
\param keySz Size of key buffer.
\param algoID Algorithm ID (e.g. RSAk).
\param curveOID ECC curve OID if used. Should be NULL for RSA keys.
\param oidSz Size of curve OID. Is set to 0 if curveOID is NULL.
_Example_
\code
ecc_key eccKey; // wolfSSL ECC key object.
byte* der; // DER-encoded ECC key.
word32 derSize; // Size of der.
const byte* curveOid = NULL; // OID of curve used by eccKey.
word32 curveOidSz = 0; // Size of curve OID.
byte* pkcs8; // Output buffer for PKCS#8 key.
word32 pkcs8Sz; // Size of output buffer.
derSize = wc_EccKeyDerSize(&eccKey, 1);
...
derSize = wc_EccKeyToDer(&eccKey, der, derSize);
...
ret = wc_ecc_get_oid(eccKey.dp->oidSum, &curveOid, &curveOidSz);
...
ret = wc_CreatePKCS8Key(NULL, &pkcs8Sz, der,
derSize, ECDSAk, curveOid, curveOidSz); // Get size needed in pkcs8Sz.
...
ret = wc_CreatePKCS8Key(pkcs8, &pkcs8Sz, der,
derSize, ECDSAk, curveOid, curveOidSz);
\endcode
\sa wc_GetPkcs8TraditionalOffset
\sa wc_EncryptPKCS8Key
\sa wc_DecryptPKCS8Key
\sa wc_CreateEncryptedPKCS8Key
*/
WOLFSSL_API int wc_CreatePKCS8Key(byte* out, word32* outSz,
byte* key, word32 keySz, int algoID, const byte* curveOID,
word32 oidSz);
/*!
\ingroup ASN
\brief This function takes in an unencrypted PKCS#8 DER key (e.g. one
created by wc_CreatePKCS8Key) and converts it to PKCS#8 encrypted format.
The resulting encrypted key can be decrypted using wc_DecryptPKCS8Key. See
RFC 5208.
\return The size of the encrypted key placed in out on success.
\return LENGTH_ONLY_E if out is NULL, with required output buffer size in
outSz.
\return Other negative values on failure.
\param key Buffer with traditional DER key.
\param keySz Size of key buffer.
\param out Buffer to place result in. If NULL, required out buffer size
returned in outSz.
\param outSz Size of out buffer.
\param password The password to use for the password-based encryption
algorithm.
\param passwordSz The length of the password (not including the NULL
terminator).
\param vPKCS The PKCS version to use. Can be 1 for PKCS12 or PKCS5.
\param pbeOid The OID of the PBE scheme to use (e.g. PBES2 or one of the
OIDs for PBES1 in RFC 2898 A.3).
\param encAlgId The encryption algorithm ID to use (e.g. AES256CBCb).
\param salt The salt buffer to use. If NULL, a random salt will be used.
\param saltSz The length of the salt buffer. Can be 0 if passing NULL for
salt.
\param itt The number of iterations to use for the KDF.
\param rng A pointer to an initialized WC_RNG object.
\param heap A pointer to the heap used for dynamic allocation. Can be NULL.
_Example_
\code
byte* pkcs8; // Unencrypted PKCS#8 key.
word32 pkcs8Sz; // Size of pkcs8.
byte* pkcs8Enc; // Encrypted PKCS#8 key.
word32 pkcs8EncSz; // Size of pkcs8Enc.
const char* password; // Password to use for encryption.
int passwordSz; // Length of password (not including NULL terminator).
WC_RNG rng;
// The following produces an encrypted version of pkcs8 in pkcs8Enc. The
// encryption uses password-based encryption scheme 2 (PBE2) from PKCS#5 and
// the AES cipher in CBC mode with a 256-bit key. See RFC 8018 for more on
// PKCS#5.
ret = wc_EncryptPKCS8Key(pkcs8, pkcs8Sz, pkcs8Enc, &pkcs8EncSz, password,
passwordSz, PKCS5, PBES2, AES256CBCb, NULL, 0,
WC_PKCS12_ITT_DEFAULT, &rng, NULL);
\endcode
\sa wc_GetPkcs8TraditionalOffset
\sa wc_CreatePKCS8Key
\sa wc_DecryptPKCS8Key
\sa wc_CreateEncryptedPKCS8Key
*/
WOLFSSL_API 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);
/*!
\ingroup ASN
\brief This function takes an encrypted PKCS#8 DER key and decrypts it to
PKCS#8 unencrypted DER. Undoes the encryption done by wc_EncryptPKCS8Key.
See RFC5208. The input buffer is overwritten with the decrypted data.
\return The length of the decrypted buffer on success.
\return Negative values on failure.
\param input On input, buffer containing encrypted PKCS#8 key. On successful
output, contains the decrypted key.
\param sz Size of the input buffer.
\param password The password used to encrypt the key.
\param passwordSz The length of the password (not including NULL
terminator).
_Example_
\code
byte* pkcs8Enc; // Encrypted PKCS#8 key made with wc_EncryptPKCS8Key.
word32 pkcs8EncSz; // Size of pkcs8Enc.
const char* password; // Password to use for decryption.
int passwordSz; // Length of password (not including NULL terminator).
ret = wc_DecryptPKCS8Key(pkcs8Enc, pkcs8EncSz, password, passwordSz);
\endcode
\sa wc_GetPkcs8TraditionalOffset
\sa wc_CreatePKCS8Key
\sa wc_EncryptPKCS8Key
\sa wc_CreateEncryptedPKCS8Key
*/
WOLFSSL_API int wc_DecryptPKCS8Key(byte* input, word32 sz, const char* password,
int passwordSz);
/*!
\ingroup ASN
\brief This function takes a traditional, DER key, converts it to PKCS#8
format, and encrypts it. It uses wc_CreatePKCS8Key and wc_EncryptPKCS8Key
to do this.
\return The size of the encrypted key placed in out on success.
\return LENGTH_ONLY_E if out is NULL, with required output buffer size in
outSz.
\return Other negative values on failure.
\param key Buffer with traditional DER key.
\param keySz Size of key buffer.
\param out Buffer to place result in. If NULL, required out buffer size
returned in outSz.
\param outSz Size of out buffer.
\param password The password to use for the password-based encryption
algorithm.
\param passwordSz The length of the password (not including the NULL
terminator).
\param vPKCS The PKCS version to use. Can be 1 for PKCS12 or PKCS5.
\param pbeOid The OID of the PBE scheme to use (e.g. PBES2 or one of the
OIDs for PBES1 in RFC 2898 A.3).
\param encAlgId The encryption algorithm ID to use (e.g. AES256CBCb).
\param salt The salt buffer to use. If NULL, a random salt will be used.
\param saltSz The length of the salt buffer. Can be 0 if passing NULL for
salt.
\param itt The number of iterations to use for the KDF.
\param rng A pointer to an initialized WC_RNG object.
\param heap A pointer to the heap used for dynamic allocation. Can be NULL.
_Example_
\code
byte* key; // Traditional private key (DER formatted).
word32 keySz; // Size of key.
byte* pkcs8Enc; // Encrypted PKCS#8 key.
word32 pkcs8EncSz; // Size of pkcs8Enc.
const char* password; // Password to use for encryption.
int passwordSz; // Length of password (not including NULL terminator).
WC_RNG rng;
// The following produces an encrypted, PKCS#8 version of key in pkcs8Enc.
// The encryption uses password-based encryption scheme 2 (PBE2) from PKCS#5
// and the AES cipher in CBC mode with a 256-bit key. See RFC 8018 for more
// on PKCS#5.
ret = wc_CreateEncryptedPKCS8Key(key, keySz, pkcs8Enc, &pkcs8EncSz,
password, passwordSz, PKCS5, PBES2, AES256CBCb, NULL, 0,
WC_PKCS12_ITT_DEFAULT, &rng, NULL);
\endcode
\sa wc_GetPkcs8TraditionalOffset
\sa wc_CreatePKCS8Key
\sa wc_EncryptPKCS8Key
\sa wc_DecryptPKCS8Key
*/
WOLFSSL_API int wc_CreateEncryptedPKCS8Key(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);

View File

@ -39640,6 +39640,50 @@ static void test_wolfSSL_CRYPTO_memcmp(void)
| wolfCrypt ASN
*----------------------------------------------------------------------------*/
static void test_wc_CreateEncryptedPKCS8Key(void)
{
#if defined(HAVE_PKCS8) && !defined(NO_PWDBASED) && defined(WOLFSSL_AES_256) \
&& !defined(NO_AES_CBC) && !defined(NO_RSA) && !defined(NO_SHA)
WC_RNG rng;
byte* encKey = NULL;
word32 encKeySz = 0;
word32 decKeySz = 0;
const char password[] = "Lorem ipsum dolor sit amet";
word32 passwordSz = (word32)XSTRLEN(password);
word32 tradIdx = 0;
printf(testingFmt, "test_wc_CreateEncryptedPKCS8Key");
AssertIntEQ(wc_InitRng(&rng), 0);
/* Call with NULL for out buffer to get necessary length. */
AssertIntEQ(wc_CreateEncryptedPKCS8Key((byte*)server_key_der_2048,
sizeof_server_key_der_2048, NULL, &encKeySz, password, passwordSz,
PKCS5, PBES2, AES256CBCb, NULL, 0, WC_PKCS12_ITT_DEFAULT, &rng, NULL),
LENGTH_ONLY_E);
AssertNotNull(encKey = (byte*)XMALLOC(encKeySz, HEAP_HINT,
DYNAMIC_TYPE_TMP_BUFFER));
/* Call with the allocated out buffer. */
AssertIntGT(wc_CreateEncryptedPKCS8Key((byte*)server_key_der_2048,
sizeof_server_key_der_2048, encKey, &encKeySz, password, passwordSz,
PKCS5, PBES2, AES256CBCb, NULL, 0, WC_PKCS12_ITT_DEFAULT, &rng, NULL),
0);
/* Decrypt the encrypted PKCS8 key we just made. */
AssertIntGT((decKeySz = wc_DecryptPKCS8Key(encKey, encKeySz, password,
passwordSz)), 0);
/* encKey now holds the decrypted key (decrypted in place). */
AssertIntGT(wc_GetPkcs8TraditionalOffset(encKey, &tradIdx, decKeySz), 0);
/* Check that the decrypted key matches the key prior to encryption. */
AssertIntEQ(XMEMCMP(encKey + tradIdx, server_key_der_2048,
sizeof_server_key_der_2048), 0);
if (encKey != NULL)
XFREE(encKey, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
wc_FreeRng(&rng);
printf(resultFmt, passed);
#endif
}
static void test_wc_GetPkcs8TraditionalOffset(void)
{
#if !defined(NO_ASN) && !defined(NO_FILESYSTEM) && defined(HAVE_PKCS8)
@ -45238,6 +45282,7 @@ void ApiTest(void)
#endif
/* wolfCrypt ASN tests */
test_wc_CreateEncryptedPKCS8Key();
test_wc_GetPkcs8TraditionalOffset();
test_wc_SetSubjectRaw();
test_wc_GetSubjectRaw();

View File

@ -2852,10 +2852,6 @@ int ToTraditional(byte* input, word32 sz)
#if defined(HAVE_PKCS8) && !defined(NO_CERTS)
/* find beginning of traditional key inside PKCS#8 unencrypted buffer
* return traditional length on success, with inOutIdx at beginning of
* traditional
* return negative on failure/error */
int wc_GetPkcs8TraditionalOffset(byte* input, word32* inOutIdx, word32 sz)
{
int length;
@ -2869,33 +2865,6 @@ int wc_GetPkcs8TraditionalOffset(byte* input, word32* inOutIdx, word32 sz)
return length;
}
/* PKCS#8 from RFC 5208
* This function takes in a DER key and converts it to PKCS#8 format. Used
* in creating PKCS#12 shrouded key bags.
* Reverse of ToTraditional
*
* PrivateKeyInfo ::= SEQUENCE {
* version Version,
* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier,
* privateKey PrivateKey,
* attributes optional
* }
* Version ::= INTEGER
* PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier
* PrivateKey ::= OCTET STRING
*
* out buffer to place result in
* outSz size of out buffer
* key buffer with DER key
* keySz size of key buffer
* algoID algorithm ID i.e. RSAk
* curveOID ECC curve oid if used. Should be NULL for RSA keys.
* oidSz size of curve oid. Is set to 0 if curveOID is NULL.
*
* Returns the size of PKCS#8 placed into out. In error cases returns negative
* values.
*/
int wc_CreatePKCS8Key(byte* out, word32* outSz, byte* key, word32 keySz,
int algoID, const byte* curveOID, word32 oidSz)
{
@ -3506,234 +3475,16 @@ static int Pkcs8Pad(byte* buf, int sz, int blockSz)
#ifdef HAVE_PKCS8
/*
* Used when creating PKCS12 shrouded key bags
* vPKCS is the version of PKCS to use
* vAlgo is the algorithm version to use
*
* if salt is NULL a random number is generated
*
* returns the size of encrypted data on success
* Equivalent to calling TraditionalEnc with the same parameters but with
* encAlgId set to 0. This function must be kept alive because it's sometimes
* part of the API (WOLFSSL_ASN_API).
*/
int UnTraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz,
const char* password, int passwordSz, int vPKCS, int vAlgo,
byte* salt, word32 saltSz, int itt, WC_RNG* rng, void* heap)
{
int algoID = 0;
byte* tmp;
word32 tmpSz = 0;
word32 sz;
word32 seqSz;
word32 inOutIdx = 0;
word32 totalSz = 0;
int version, id;
int ret;
int blockSz = 0;
const byte* curveOID = NULL;
word32 oidSz = 0;
#ifdef WOLFSSL_SMALL_STACK
byte* saltTmp = NULL;
byte* cbcIv = NULL;
#else
byte saltTmp[MAX_IV_SIZE];
byte cbcIv[MAX_IV_SIZE];
#endif
WOLFSSL_ENTER("UnTraditionalEnc()");
if (saltSz > MAX_SALT_SIZE)
return ASN_PARSE_E;
inOutIdx += MAX_SEQ_SZ; /* leave room for size of finished shroud */
if (CheckAlgo(vPKCS, vAlgo, &id, &version, &blockSz) < 0) {
WOLFSSL_MSG("Bad/Unsupported algorithm ID");
return ASN_INPUT_E; /* Algo ID error */
}
if (out != NULL) {
if (*outSz < inOutIdx + MAX_ALGO_SZ + MAX_SALT_SIZE + MAX_SEQ_SZ + 1 +
MAX_LENGTH_SZ + MAX_SHORT_SZ + 1)
return BUFFER_E;
if (version == PKCS5v2) {
WOLFSSL_MSG("PKCS5v2 Not supported yet\n");
return ASN_VERSION_E;
}
if (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;
#endif
salt = saltTmp;
if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) {
WOLFSSL_MSG("Error generating random salt");
#ifdef WOLFSSL_SMALL_STACK
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ret;
}
}
/* leave room for a sequence (contains salt and iterations int) */
inOutIdx += MAX_SEQ_SZ; sz = 0;
inOutIdx += MAX_ALGO_SZ;
/* place salt in buffer */
out[inOutIdx++] = ASN_OCTET_STRING; sz++;
tmpSz = SetLength(saltSz, out + inOutIdx);
inOutIdx += tmpSz; sz += tmpSz;
XMEMCPY(out + inOutIdx, salt, saltSz);
inOutIdx += saltSz; sz += saltSz;
/* place iteration count in buffer */
ret = SetShortInt(out, &inOutIdx, itt, *outSz);
if (ret < 0) {
#ifdef WOLFSSL_SMALL_STACK
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ret;
}
sz += (word32)ret;
/* wind back index and set sequence then clean up buffer */
inOutIdx -= (sz + MAX_SEQ_SZ);
tmpSz = SetSequence(sz, out + inOutIdx);
XMEMMOVE(out + inOutIdx + tmpSz, out + inOutIdx + MAX_SEQ_SZ, sz);
totalSz += tmpSz + sz; sz += tmpSz;
/* add in algo ID */
inOutIdx -= MAX_ALGO_SZ;
tmpSz = SetAlgoID(id, out + inOutIdx, oidPBEType, sz);
XMEMMOVE(out + inOutIdx + tmpSz, out + inOutIdx + MAX_ALGO_SZ, sz);
totalSz += tmpSz; inOutIdx += tmpSz + sz;
/* octet string containing encrypted key */
out[inOutIdx++] = ASN_OCTET_STRING; totalSz++;
}
/* check key type and get OID if ECC */
if ((ret = wc_GetKeyOID(key, keySz, &curveOID, &oidSz, &algoID, heap))< 0) {
WOLFSSL_MSG("Error getting key OID");
#ifdef WOLFSSL_SMALL_STACK
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ret;
}
/* PKCS#8 wrapping around key */
if (wc_CreatePKCS8Key(NULL, &tmpSz, key, keySz, algoID, curveOID, oidSz)
!= LENGTH_ONLY_E) {
#ifdef WOLFSSL_SMALL_STACK
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return MEMORY_E;
}
/* check if should return max size */
if (out == NULL) {
/* account for salt size */
if (salt == NULL || saltSz == 0) {
tmpSz += MAX_SALT_SIZE;
}
else {
tmpSz += saltSz;
}
/* plus 3 for tags */
*outSz = tmpSz + MAX_ALGO_SZ + MAX_LENGTH_SZ +MAX_LENGTH_SZ + MAX_SEQ_SZ
+ MAX_LENGTH_SZ + MAX_SEQ_SZ + 3;
#ifdef WOLFSSL_SMALL_STACK
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return LENGTH_ONLY_E;
}
/* reserve buffer for crypto and make sure it supports full blocks */
tmp = (byte*)XMALLOC(tmpSz + (blockSz-1), heap, DYNAMIC_TYPE_TMP_BUFFER);
if (tmp == NULL) {
#ifdef WOLFSSL_SMALL_STACK
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return MEMORY_E;
}
if ((ret = wc_CreatePKCS8Key(tmp, &tmpSz, key, keySz, algoID, curveOID,
oidSz)) < 0) {
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
WOLFSSL_MSG("Error wrapping key with PKCS#8");
#ifdef WOLFSSL_SMALL_STACK
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ret;
}
tmpSz = ret;
/* adjust size to pad */
tmpSz = Pkcs8Pad(tmp, tmpSz, blockSz);
#ifdef WOLFSSL_SMALL_STACK
cbcIv = (byte*)XMALLOC(MAX_IV_SIZE, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (cbcIv == NULL) {
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
return MEMORY_E;
}
#endif
/* encrypt PKCS#8 wrapped key */
if ((ret = wc_CryptKey(password, passwordSz, salt, saltSz, itt, id,
tmp, tmpSz, version, cbcIv, 1, 0)) < 0) {
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
WOLFSSL_MSG("Error encrypting key");
#ifdef WOLFSSL_SMALL_STACK
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (cbcIv != NULL)
XFREE(cbcIv, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ret; /* encryption failure */
}
totalSz += tmpSz;
#ifdef WOLFSSL_SMALL_STACK
if (saltTmp != NULL)
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (cbcIv != NULL)
XFREE(cbcIv, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
if (*outSz < inOutIdx + tmpSz + MAX_LENGTH_SZ) {
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
return BUFFER_E;
}
/* set length of key and copy over encrypted key */
seqSz = SetLength(tmpSz, out + inOutIdx);
inOutIdx += seqSz; totalSz += seqSz;
XMEMCPY(out + inOutIdx, tmp, tmpSz);
XFREE(tmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
/* set total size at beginning */
sz = SetSequence(totalSz, out);
XMEMMOVE(out + sz, out + MAX_SEQ_SZ, totalSz);
(void)rng;
return totalSz + sz;
return TraditionalEnc(key, keySz, out, outSz, password, passwordSz,
vPKCS, vAlgo, 0, salt, saltSz, itt, rng, heap);
}
static int GetAlgoV2(int encAlgId, const byte** oid, int *len, int* id,
@ -3775,37 +3526,7 @@ static int GetAlgoV2(int encAlgId, const byte** oid, int *len, int* id,
return ret;
}
/* 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 wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32 outSz,
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)
@ -3815,6 +3536,7 @@ int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32 outSz,
#else
byte saltTmp[MAX_SALT_SIZE];
#endif
int genSalt = 0;
int ret = 0;
int version = 0;
int pbeId = 0;
@ -3837,27 +3559,20 @@ int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32 outSz,
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)
ret = MEMORY_E;
#endif
salt = saltTmp;
if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) {
WOLFSSL_MSG("Error generating random salt");
#ifdef WOLFSSL_SMALL_STACK
XFREE(saltTmp, heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
}
if (key == NULL || outSz == NULL || password == NULL) {
ret = BAD_FUNC_ARG;
}
if (ret == 0 && version == PKCS5v2)
if (ret == 0) {
ret = CheckAlgo(vPKCS, pbeOid, &pbeId, &version, &blockSz);
}
if (ret == 0 && (salt == NULL || saltSz == 0)) {
genSalt = 1;
saltSz = 8;
}
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 */
@ -3882,10 +3597,19 @@ int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32 outSz,
}
}
if (ret == 0) {
/* outer = SEQ [ pbe ] OCT encrypted_PKCS#8_key */
/* outerLen = length of PBE encoding + octet string data */
/* Plus 2 for tag and length for pbe */
outerLen = 2 + pbeLen;
outerLen += SetOctetString(keySz + padSz, out);
/* Octet string tag, length */
outerLen += 1 + SetLength(keySz + padSz, NULL);
/* Octet string bytes */
outerLen += keySz + padSz;
if (out == NULL) {
/* Sequence tag, length */
*outSz = 1 + SetLength(outerLen, NULL) + outerLen;
return LENGTH_ONLY_E;
}
SetOctetString(keySz + padSz, out);
idx += SetSequence(outerLen, out + idx);
@ -3896,8 +3620,26 @@ int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32 outSz,
XMEMSET(out + encIdx + keySz, padSz, padSz);
keySz += padSz;
}
if (genSalt == 1) {
#ifdef WOLFSSL_SMALL_STACK
saltTmp = (byte*)XMALLOC(saltSz, heap, DYNAMIC_TYPE_TMP_BUFFER);
if (saltTmp == NULL) {
ret = MEMORY_E;
}
else
#endif
{
salt = saltTmp;
if ((ret = wc_RNG_GenerateBlock(rng, saltTmp, saltSz)) != 0) {
WOLFSSL_MSG("Error generating random salt");
}
}
}
}
if (ret == 0) {
ret = wc_CryptKey(password, passwordSz, salt, saltSz, itt, pbeId,
out + encIdx, keySz, version, cbcIv, 1, 0);
out + encIdx, keySz, version, cbcIv, 1, 0);
}
if (ret == 0) {
if (version != PKCS5v2) {
@ -3924,7 +3666,7 @@ int wc_EncryptPKCS8Key(byte* key, word32 keySz, byte* out, word32 outSz,
idx += SetSequence(innerLen, out + idx);
idx += SetOctetString(saltSz, out + idx);
XMEMCPY(out + idx, salt, saltSz); idx += saltSz;
ret = SetShortInt(out, &idx, itt, outSz);
ret = SetShortInt(out, &idx, itt, *outSz);
if (ret > 0)
ret = 0;
}
@ -3958,14 +3700,6 @@ 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)
{
@ -3973,6 +3707,10 @@ int wc_DecryptPKCS8Key(byte* input, word32 sz, const char* password,
int length;
word32 inOutIdx = 0;
if (input == NULL || password == NULL) {
return BAD_FUNC_ARG;
}
if (GetSequence(input, &inOutIdx, &length, sz) < 0) {
ret = ASN_PARSE_E;
}
@ -4001,7 +3739,9 @@ int wc_DecryptPKCS8Key(byte* input, word32 sz, const char* password,
}
/* Takes an unencrypted, traditional DER-encoded key and converts it to a PKCS#8
* encrypted key. */
* encrypted key. If out is not NULL, it will hold the encrypted key. If it's
* NULL, LENGTH_ONLY_E will be returned and outSz will have the required out
* buffer size. */
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,
@ -4040,7 +3780,7 @@ int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz,
}
}
if (ret == 0) {
ret = wc_EncryptPKCS8Key(pkcs8Key, pkcs8KeySz, out, *outSz, password,
ret = wc_EncryptPKCS8Key(pkcs8Key, pkcs8KeySz, out, outSz, password,
passwordSz, vPKCS, vAlgo, encAlgId, salt, saltSz, itt, rng, heap);
}
@ -4054,6 +3794,17 @@ int TraditionalEnc(byte* key, word32 keySz, byte* out, word32* outSz,
return ret;
}
/* Same as TraditionalEnc, but in the public API. */
int wc_CreateEncryptedPKCS8Key(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)
{
return TraditionalEnc(key, keySz, out, outSz, password, passwordSz, vPKCS,
pbeOid, encAlgId, salt, saltSz, itt, rng, heap);
}
#endif /* HAVE_PKCS8 */
#if defined(HAVE_PKCS8) || defined(HAVE_PKCS12)

View File

@ -593,10 +593,13 @@ WOLFSSL_API int wc_GetCTC_HashOID(int type);
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*,
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);
WOLFSSL_API int wc_CreateEncryptedPKCS8Key(byte*, word32, byte*, word32*,
const char*, int, int, int, int, byte*, word32, int, WC_RNG*, void*);
#ifndef NO_ASN_TIME
/* Time */