Skip to content

Commit 3b00cb4

Browse files
Decode encrypted PKCS#8 (EncryptedPrivateKeyInfo) for all key types
1 parent 3b15acb commit 3b00cb4

14 files changed

Lines changed: 383 additions & 84 deletions

File tree

include/wolfprovider/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,8 @@ int wp_encrypt_key_pkcs8(WOLFPROV_CTX* provCtx, int cipher,
239239
const unsigned char* plain, word32 plainLen,
240240
unsigned char* out, size_t* outLen,
241241
OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg);
242+
int wp_decrypt_key_pkcs8(unsigned char* data, word32* len,
243+
OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg);
242244

243245
int wp_read_der_bio(WOLFPROV_CTX* provCtx, OSSL_CORE_BIO *coreBio, unsigned char** data, word32* len);
244246
int wp_read_pem_bio(WOLFPROV_CTX *provctx, OSSL_CORE_BIO *coreBio,

src/wp_dh_kmgmt.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2284,6 +2284,43 @@ static int wp_dh_dec_send_params(wp_Dh* dh, OSSL_CALLBACK *dataCb,
22842284
return ok;
22852285
}
22862286

2287+
#ifdef WOLFSSL_ENCRYPTED_KEYS
2288+
/**
2289+
* Decode an encrypted PKCS#8 DER DH private key into the DH key object.
2290+
*
2291+
* @param [in, out] dh DH key object.
2292+
* @param [in] data DER encoding (decrypted in place).
2293+
* @param [in] len Length, in bytes, of DER encoding.
2294+
* @param [in] pwCb Password callback.
2295+
* @param [in] pwCbArg Argument to pass to password callback.
2296+
* @return 1 on success.
2297+
* @return 0 on failure.
2298+
*/
2299+
static int wp_dh_decode_enc_pki(wp_Dh* dh, unsigned char* data, word32 len,
2300+
OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg)
2301+
{
2302+
int ok = 1;
2303+
2304+
WOLFPROV_ENTER_SILENT(WP_LOG_COMP_DH, WOLFPROV_FUNC_NAME);
2305+
2306+
if (!wolfssl_prov_is_running()) {
2307+
ok = 0;
2308+
}
2309+
/* Decrypt the PBES2 EncryptedPrivateKeyInfo in place. */
2310+
if (ok && (!wp_decrypt_key_pkcs8(data, &len, pwCb, pwCbArg))) {
2311+
ok = 0;
2312+
}
2313+
if (ok) {
2314+
/* Decode the recovered plaintext private key. */
2315+
ok = wp_dh_decode_pki(dh, data, len);
2316+
}
2317+
2318+
WOLFPROV_LEAVE_SILENT(WP_LOG_COMP_DH, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__),
2319+
ok);
2320+
return ok;
2321+
}
2322+
#endif
2323+
22872324
/**
22882325
* Decode the data in the core BIO.
22892326
*
@@ -2364,8 +2401,13 @@ static int wp_dh_decode(wp_DhEncDecCtx* ctx, OSSL_CORE_BIO *cBio,
23642401
}
23652402
else if (ok && (ctx->format == WP_ENC_FORMAT_PKI)) {
23662403
if (!wp_dh_decode_pki(dh, data, len)) {
2367-
ok = 0;
2368-
decoded = 0;
2404+
#ifdef WOLFSSL_ENCRYPTED_KEYS
2405+
if (!wp_dh_decode_enc_pki(dh, data, len, pwCb, pwCbArg))
2406+
#endif
2407+
{
2408+
ok = 0;
2409+
decoded = 0;
2410+
}
23692411
}
23702412
}
23712413

src/wp_ecc_kmgmt.c

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2285,6 +2285,43 @@ static int wp_ecc_dec_send_params(wp_Ecc* ecc, OSSL_CALLBACK *dataCb,
22852285
return ok;
22862286
}
22872287

2288+
#ifdef WOLFSSL_ENCRYPTED_KEYS
2289+
/**
2290+
* Decode an encrypted PKCS#8 DER ECC private key into the ECC key object.
2291+
*
2292+
* @param [in, out] ecc ECC key object.
2293+
* @param [in] data DER encoding (decrypted in place).
2294+
* @param [in] len Length, in bytes, of DER encoding.
2295+
* @param [in] pwCb Password callback.
2296+
* @param [in] pwCbArg Argument to pass to password callback.
2297+
* @return 1 on success.
2298+
* @return 0 on failure.
2299+
*/
2300+
static int wp_ecc_decode_enc_pki(wp_Ecc* ecc, unsigned char* data, word32 len,
2301+
OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg)
2302+
{
2303+
int ok = 1;
2304+
2305+
WOLFPROV_ENTER_SILENT(WP_LOG_COMP_ECC, WOLFPROV_FUNC_NAME);
2306+
2307+
if (!wolfssl_prov_is_running()) {
2308+
ok = 0;
2309+
}
2310+
/* Decrypt the PBES2 EncryptedPrivateKeyInfo in place. */
2311+
if (ok && (!wp_decrypt_key_pkcs8(data, &len, pwCb, pwCbArg))) {
2312+
ok = 0;
2313+
}
2314+
if (ok) {
2315+
/* Decode the recovered plaintext private key. */
2316+
ok = wp_ecc_decode_pki(ecc, data, len);
2317+
}
2318+
2319+
WOLFPROV_LEAVE_SILENT(WP_LOG_COMP_ECC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__),
2320+
ok);
2321+
return ok;
2322+
}
2323+
#endif
2324+
22882325
/**
22892326
* Decode the data in the core BIO.
22902327
*
@@ -2362,8 +2399,13 @@ static int wp_ecc_decode(wp_EccEncDecCtx* ctx, OSSL_CORE_BIO *cBio,
23622399
}
23632400
else if (ok && (ctx->format == WP_ENC_FORMAT_PKI)) {
23642401
if (!wp_ecc_decode_pki(ecc, data, len)) {
2365-
ok = 0;
2366-
decoded = 0;
2402+
#ifdef WOLFSSL_ENCRYPTED_KEYS
2403+
if (!wp_ecc_decode_enc_pki(ecc, data, len, pwCb, pwCbArg))
2404+
#endif
2405+
{
2406+
ok = 0;
2407+
decoded = 0;
2408+
}
23672409
}
23682410
}
23692411

src/wp_ecx_kmgmt.c

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2021,6 +2021,46 @@ static int wp_ecx_dec_send_params(wp_Ecx* ecx, const char* dataType,
20212021
return ok;
20222022
}
20232023

2024+
#ifdef WOLFSSL_ENCRYPTED_KEYS
2025+
/**
2026+
* Decode an encrypted PKCS#8 DER ECX private key into the ECX key object.
2027+
*
2028+
* @param [in] ctx ECX encoder/decoder context object.
2029+
* @param [in, out] ecx ECX key object.
2030+
* @param [in] data DER encoding (decrypted in place).
2031+
* @param [in] len Length, in bytes, of DER encoding.
2032+
* @param [in] pwCb Password callback.
2033+
* @param [in] pwCbArg Argument to pass to password callback.
2034+
* @return 1 on success.
2035+
* @return 0 on failure.
2036+
*/
2037+
static int wp_ecx_decode_enc_pki(wp_EcxEncDecCtx* ctx, wp_Ecx* ecx,
2038+
unsigned char* data, word32 len, OSSL_PASSPHRASE_CALLBACK* pwCb,
2039+
void* pwCbArg)
2040+
{
2041+
int ok = 1;
2042+
word32 idx = 0;
2043+
2044+
WOLFPROV_ENTER_SILENT(WP_LOG_COMP_KE, WOLFPROV_FUNC_NAME);
2045+
2046+
if (!wolfssl_prov_is_running()) {
2047+
ok = 0;
2048+
}
2049+
/* Decrypt the PBES2 EncryptedPrivateKeyInfo in place. */
2050+
if (ok && (!wp_decrypt_key_pkcs8(data, &len, pwCb, pwCbArg))) {
2051+
ok = 0;
2052+
}
2053+
/* Decode the recovered plaintext private key. */
2054+
if (ok && (ctx->decode(data, &idx, (void*)&ecx->key, len) != 0)) {
2055+
ok = 0;
2056+
}
2057+
2058+
WOLFPROV_LEAVE_SILENT(WP_LOG_COMP_KE, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__),
2059+
ok);
2060+
return ok;
2061+
}
2062+
#endif
2063+
20242064
/**
20252065
* Decode the data in the core BIO.
20262066
*
@@ -2102,9 +2142,16 @@ static int wp_ecx_decode(wp_EcxEncDecCtx* ctx, OSSL_CORE_BIO* cBio,
21022142
if (ok) {
21032143
rc = ctx->decode(data, &idx, (void*)&ecx->key, len);
21042144
if (rc != 0) {
2105-
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "decode", rc);
2106-
ok = 0;
2107-
decoded = 0;
2145+
#ifdef WOLFSSL_ENCRYPTED_KEYS
2146+
/* May be an encrypted PKCS#8 key - decrypt and retry. */
2147+
if ((ctx->format != WP_ENC_FORMAT_PKI) ||
2148+
(!wp_ecx_decode_enc_pki(ctx, ecx, data, len, pwCb, pwCbArg)))
2149+
#endif
2150+
{
2151+
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "decode", rc);
2152+
ok = 0;
2153+
decoded = 0;
2154+
}
21082155
}
21092156
}
21102157
if (ok && (ctx->format == WP_ENC_FORMAT_SPKI)) {

src/wp_internal.c

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1073,6 +1073,112 @@ int wp_encrypt_key_pkcs8(WOLFPROV_CTX* provCtx, int cipher,
10731073
#endif
10741074
}
10751075

1076+
#if defined(HAVE_PKCS8) && !defined(NO_PWDBASED)
1077+
/* DER encoding of the PBKDF2 OID (1.2.840.113549.1.5.12). */
1078+
static const unsigned char wp_pbkdf2_oid[] = {
1079+
42, 134, 72, 134, 247, 13, 1, 5, 12
1080+
};
1081+
1082+
/*
1083+
* Detect whether a DER blob is a PBES2-encrypted PKCS#8 key by looking for the
1084+
* PBKDF2 OID near the start of the encryptionAlgorithm field. Avoids prompting
1085+
* for a passphrase on data that is not an encrypted key.
1086+
*/
1087+
static int wp_is_pbkdf2_encrypted(const unsigned char* data, word32 len)
1088+
{
1089+
int found = 0;
1090+
word32 i;
1091+
1092+
for (i = 0; (i < 40) && (i + sizeof(wp_pbkdf2_oid) < len); i++) {
1093+
if (XMEMCMP(data + i, wp_pbkdf2_oid, sizeof(wp_pbkdf2_oid)) == 0) {
1094+
found = 1;
1095+
break;
1096+
}
1097+
}
1098+
1099+
return found;
1100+
}
1101+
#endif
1102+
1103+
/**
1104+
* Decrypt a PBES2 EncryptedPrivateKeyInfo to plaintext PKCS#8 in place, getting
1105+
* the passphrase from the callback. Returns 0 without prompting when the data
1106+
* is not a PBES2-encrypted key.
1107+
*
1108+
* @param [in, out] data On in, encrypted key; on out, plaintext PKCS#8.
1109+
* @param [in, out] len On in, encrypted length; on out, plaintext length.
1110+
* @param [in] pwCb Password callback.
1111+
* @param [in] pwCbArg Argument to pass to the password callback.
1112+
* @return 1 on success.
1113+
* @return 0 on failure or when the data is not an encrypted PKCS#8 key.
1114+
*/
1115+
int wp_decrypt_key_pkcs8(unsigned char* data, word32* len,
1116+
OSSL_PASSPHRASE_CALLBACK* pwCb, void* pwCbArg)
1117+
{
1118+
#if defined(HAVE_PKCS8) && !defined(NO_PWDBASED)
1119+
int ok = 1;
1120+
int rc;
1121+
#ifdef WOLFSSL_SMALL_STACK
1122+
char* password = NULL;
1123+
#else
1124+
char password[WP_EPKI_PASSWORD_MAX];
1125+
#endif
1126+
size_t passwordSz = WP_EPKI_PASSWORD_MAX;
1127+
1128+
WOLFPROV_ENTER(WP_LOG_COMP_PROVIDER, "wp_decrypt_key_pkcs8");
1129+
1130+
/* Only handle data that looks like a PBES2-encrypted PKCS#8 key. */
1131+
if ((data == NULL) || (!wp_is_pbkdf2_encrypted(data, *len))) {
1132+
ok = 0;
1133+
}
1134+
#ifdef WOLFSSL_SMALL_STACK
1135+
if (ok) {
1136+
password = (char*)XMALLOC(WP_EPKI_PASSWORD_MAX, NULL,
1137+
DYNAMIC_TYPE_TMP_BUFFER);
1138+
if (password == NULL) {
1139+
ok = 0;
1140+
}
1141+
}
1142+
#endif
1143+
/* Get the password from the callback. */
1144+
if (ok && (!pwCb(password, passwordSz, &passwordSz, NULL, pwCbArg))) {
1145+
ok = 0;
1146+
}
1147+
if (ok) {
1148+
/* Decrypt the key in place. */
1149+
rc = wc_DecryptPKCS8Key(data, *len, password, (int)passwordSz);
1150+
if (rc <= 0) {
1151+
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "wc_DecryptPKCS8Key",
1152+
rc);
1153+
ok = 0;
1154+
}
1155+
else {
1156+
*len = (word32)rc;
1157+
}
1158+
}
1159+
1160+
/* Password is sensitive - force zeroization. */
1161+
#ifdef WOLFSSL_SMALL_STACK
1162+
if (password != NULL) {
1163+
wc_ForceZero(password, WP_EPKI_PASSWORD_MAX);
1164+
XFREE(password, NULL, DYNAMIC_TYPE_TMP_BUFFER);
1165+
}
1166+
#else
1167+
wc_ForceZero(password, sizeof(password));
1168+
#endif
1169+
1170+
WOLFPROV_LEAVE(WP_LOG_COMP_PROVIDER, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__),
1171+
ok);
1172+
return ok;
1173+
#else
1174+
(void)data;
1175+
(void)len;
1176+
(void)pwCb;
1177+
(void)pwCbArg;
1178+
return 0;
1179+
#endif
1180+
}
1181+
10761182
/**
10771183
* Read data out of the core BIO.
10781184
*

src/wp_mldsa_kmgmt.c

Lines changed: 51 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1332,6 +1332,46 @@ static int wp_mldsa_dec_send_params(wp_MlDsa* mldsa, const char* dataType,
13321332
return ok;
13331333
}
13341334

1335+
#ifdef WOLFSSL_ENCRYPTED_KEYS
1336+
/**
1337+
* Decode an encrypted PKCS#8 DER ML-DSA private key into the ML-DSA key object.
1338+
*
1339+
* @param [in] ctx ML-DSA encoder/decoder context object.
1340+
* @param [in, out] mldsa ML-DSA key object.
1341+
* @param [in] data DER encoding (decrypted in place).
1342+
* @param [in] len Length, in bytes, of DER encoding.
1343+
* @param [in] pwCb Password callback.
1344+
* @param [in] pwCbArg Argument to pass to password callback.
1345+
* @return 1 on success.
1346+
* @return 0 on failure.
1347+
*/
1348+
static int wp_mldsa_decode_enc_pki(wp_MlDsaEncDecCtx* ctx, wp_MlDsa* mldsa,
1349+
unsigned char* data, word32 len, OSSL_PASSPHRASE_CALLBACK* pwCb,
1350+
void* pwCbArg)
1351+
{
1352+
int ok = 1;
1353+
word32 idx = 0;
1354+
1355+
WOLFPROV_ENTER_SILENT(WP_LOG_COMP_PQC, WOLFPROV_FUNC_NAME);
1356+
1357+
if (!wolfssl_prov_is_running()) {
1358+
ok = 0;
1359+
}
1360+
/* Decrypt the PBES2 EncryptedPrivateKeyInfo in place. */
1361+
if (ok && (!wp_decrypt_key_pkcs8(data, &len, pwCb, pwCbArg))) {
1362+
ok = 0;
1363+
}
1364+
/* Decode the recovered plaintext private key. */
1365+
if (ok && (ctx->decode(data, &idx, (void*)&mldsa->key, len) != 0)) {
1366+
ok = 0;
1367+
}
1368+
1369+
WOLFPROV_LEAVE_SILENT(WP_LOG_COMP_PQC, __FILE__ ":" WOLFPROV_STRINGIZE(__LINE__),
1370+
ok);
1371+
return ok;
1372+
}
1373+
#endif
1374+
13351375
/**
13361376
* Decode the data in the core BIO.
13371377
*
@@ -1383,9 +1423,17 @@ static int wp_mldsa_decode(wp_MlDsaEncDecCtx* ctx, OSSL_CORE_BIO* cBio,
13831423
if (ok) {
13841424
rc = ctx->decode(data, &idx, (void*)&mldsa->key, len);
13851425
if (rc != 0) {
1386-
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "decode", rc);
1387-
ok = 0;
1388-
decoded = 0;
1426+
#ifdef WOLFSSL_ENCRYPTED_KEYS
1427+
/* May be an encrypted PKCS#8 key - decrypt and retry. */
1428+
if ((ctx->format != WP_ENC_FORMAT_PKI) ||
1429+
(!wp_mldsa_decode_enc_pki(ctx, mldsa, data, len, pwCb,
1430+
pwCbArg)))
1431+
#endif
1432+
{
1433+
WOLFPROV_MSG_DEBUG_RETCODE(WP_LOG_LEVEL_DEBUG, "decode", rc);
1434+
ok = 0;
1435+
decoded = 0;
1436+
}
13891437
}
13901438
}
13911439
if (ok && (ctx->format == WP_ENC_FORMAT_SPKI)) {

0 commit comments

Comments
 (0)