diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 3114ef915..bf2f00744 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -1596,6 +1596,121 @@ int wc_GetPkcs8TraditionalOffset(byte* input, word32* inOutIdx, word32 sz) } +/* 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) +{ + word32 keyIdx = 0; + word32 tmpSz = 0; + word32 sz; + + + /* If out is NULL then return the max size needed + * + 2 for ASN_OBJECT_ID and ASN_OCTET_STRING tags */ + if (out == NULL && outSz != NULL) { + *outSz = keySz + MAX_SEQ_SZ + MAX_VERSION_SZ + MAX_ALGO_SZ + + MAX_LENGTH_SZ + MAX_LENGTH_SZ + 2; + + if (curveOID != NULL) + *outSz += oidSz + MAX_LENGTH_SZ + 1; + + WOLFSSL_MSG("Checking size of PKCS8"); + + return LENGTH_ONLY_E; + } + + WOLFSSL_ENTER("wc_CreatePKCS8Key()"); + + if (key == NULL || out == NULL || outSz == NULL) { + return BAD_FUNC_ARG; + } + + /* check the buffer has enough room for largest possible size */ + if (curveOID != NULL) { + if (*outSz < (keySz + MAX_SEQ_SZ + MAX_VERSION_SZ + MAX_ALGO_SZ + + MAX_LENGTH_SZ + MAX_LENGTH_SZ + 3 + oidSz + MAX_LENGTH_SZ)) + return BUFFER_E; + } + else { + oidSz = 0; /* with no curveOID oid size must be 0 */ + if (*outSz < (keySz + MAX_SEQ_SZ + MAX_VERSION_SZ + MAX_ALGO_SZ + + MAX_LENGTH_SZ + MAX_LENGTH_SZ + 2)) + return BUFFER_E; + } + + /* PrivateKeyInfo ::= SEQUENCE */ + keyIdx += MAX_SEQ_SZ; /* save room for sequence */ + + /* version Version + * no header information just INTEGER */ + sz = SetMyVersion(PKCS8v0, out + keyIdx, 0); + tmpSz += sz; keyIdx += sz; + + /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier */ + sz = 0; /* set sz to 0 and get privateKey oid buffer size needed */ + if (curveOID != NULL && oidSz > 0) { + byte buf[MAX_LENGTH_SZ]; + sz = SetLength(oidSz, buf); + sz += 1; /* plus one for ASN object id */ + } + sz = SetAlgoID(algoID, out + keyIdx, oidKeyType, oidSz + sz); + tmpSz += sz; keyIdx += sz; + + /* privateKey PrivateKey * + * pkcs8 ecc uses slightly different format. Places curve oid in + * buffer */ + if (curveOID != NULL && oidSz > 0) { + out[keyIdx++] = ASN_OBJECT_ID; tmpSz++; + sz = SetLength(oidSz, out + keyIdx); + keyIdx += sz; tmpSz += sz; + XMEMCPY(out + keyIdx, curveOID, oidSz); + keyIdx += oidSz; tmpSz += oidSz; + } + + out[keyIdx] = ASN_OCTET_STRING; + keyIdx++; tmpSz++; + + sz = SetLength(keySz, out + keyIdx); + keyIdx += sz; tmpSz += sz; + XMEMCPY(out + keyIdx, key, keySz); + tmpSz += keySz; + + /* attributes optional + * No attributes currently added */ + + /* rewind and add sequence */ + sz = SetSequence(tmpSz, out); + XMEMMOVE(out + sz, out + MAX_SEQ_SZ, tmpSz); + + return tmpSz + sz; +} + + /* check that the private key is a pair for the public key in certificate * return 1 (true) on match * return 0 or negative value on failure/error @@ -1934,6 +2049,75 @@ static int DecryptKey(const char* password, int passwordSz, byte* salt, } +int wc_GetKeyOID(byte* key, word32 keySz, const byte** curveOID, word32* oidSz, + int* algoID, void* heap) +{ + word32 tmpIdx = 0; + + #ifdef HAVE_ECC + ecc_key ecc; + #endif + #ifndef NO_RSA + RsaKey rsa; + #endif + + if (algoID == NULL) { + return BAD_FUNC_ARG; + } + *algoID = 0; + + #ifndef NO_RSA + wc_InitRsaKey(&rsa, heap); + if (wc_RsaPrivateKeyDecode(key, &tmpIdx, &rsa, keySz) == 0) { + *algoID = RSAk; + } + else { + WOLFSSL_MSG("Not RSA DER key"); + } + wc_FreeRsaKey(&rsa); + #endif /* NO_RSA */ + #ifdef HAVE_ECC + if (*algoID != RSAk) { + tmpIdx = 0; + wc_ecc_init_ex(&ecc, heap, INVALID_DEVID); + if (wc_EccPrivateKeyDecode(key, &tmpIdx, &ecc, keySz) == 0) { + *algoID = ECDSAk; + + /* sanity check on arguments */ + if (curveOID == NULL || oidSz == NULL) { + WOLFSSL_MSG("Error getting ECC curve OID"); + wc_ecc_free(&ecc); + return BAD_FUNC_ARG; + } + + /* now find oid */ + if (wc_ecc_get_oid(ecc.dp->oidSum, curveOID, oidSz) < 0) { + WOLFSSL_MSG("Error getting ECC curve OID"); + wc_ecc_free(&ecc); + return BAD_FUNC_ARG; + } + } + else { + WOLFSSL_MSG("Not ECC DER key either"); + } + wc_ecc_free(&ecc); + } + #endif /* HAVE_ECC */ + + /* if flag is not set then is neither RSA or ECC key that could be + * found */ + if (*algoID == 0) { + WOLFSSL_MSG("Bad key DER or compile options"); + return BAD_FUNC_ARG; + } + + (void)curveOID; + (void)oidSz; + + return 1; +} + + /* Remove Encrypted PKCS8 header, move beginning of traditional to beginning of input */ int ToTraditionalEnc(byte* input, word32 sz,const char* password,int passwordSz) diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index f1419a1d2..b7377b9fd 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -134,6 +134,7 @@ enum Misc_ASN { MAX_KEY_SIZE = 64, /* MAX PKCS Key length */ PKCS5 = 5, /* PKCS oid tag */ PKCS5v2 = 6, /* PKCS #5 v2.0 */ + PKCS8v0 = 0, /* default PKCS#8 version */ PKCS12 = 12, /* PKCS #12 */ MAX_UNICODE_SZ = 256, ASN_BOOL_SIZE = 2, /* including type */ @@ -684,6 +685,8 @@ WOLFSSL_LOCAL int ToTraditionalInline(const byte* input, word32* inOutIdx, word32 length); WOLFSSL_LOCAL int ToTraditionalEnc(byte* buffer, word32 length,const char*,int); WOLFSSL_LOCAL int DecryptContent(byte* input, word32 sz,const char* psw,int pswSz); +WOLFSSL_LOCAL int wc_GetKeyOID(byte* key, word32 keySz, const byte** curveOID, + word32* oidSz, int* algoID, void* heap); typedef struct tm wolfssl_tm; #if defined(WOLFSSL_MYSQL_COMPATIBLE) || defined(WOLFSSL_NGINX) diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 78c48c684..83eb3e78d 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -270,6 +270,8 @@ 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); /* Time */ /* Returns seconds (Epoch/UTC)