diff --git a/tests/api/api.h b/tests/api/api.h index d618de0cad..f7c2be5792 100644 --- a/tests/api/api.h +++ b/tests/api/api.h @@ -252,5 +252,77 @@ typedef struct testVector { extern int testDevId; +/* Skip past outer SEQ header of a PKCS#8 / RFC 5958 OneAsymmetricKey DER blob, + * return version byte (0=v1, 1=v2) or -1 on malformed input. */ +#ifdef WC_ENABLE_ASYM_KEY_EXPORT +static WC_INLINE int test_pkcs8_get_version_byte(const byte* der, word32 derSz) +{ + word32 idx = 0; + word32 nBytes = 0; + + /* SEQ tag + short len + INT tag + INT len + version = 5 */ + if (der == NULL || derSz < 5) { + return -1; + } + /* SEQUENCE */ + if (der[idx++] != 0x30) { + return -1; + } + if (idx >= derSz) { + return -1; + } + if ((der[idx] & 0x80) == 0) { + /* short-form length */ + idx += 1; + } + else { + /* long-form length */ + nBytes = (word32)(der[idx] & 0x7F); + if (nBytes == 0 || nBytes > 4) { + return -1; + } + idx += (1 + nBytes); + } + if (idx + 3 > derSz) { + return -1; + } + /* INTEGER, len 1 */ + if (der[idx] != 0x02 || der[idx + 1] != 0x01) { + return -1; + } + + return (int)der[idx + 2]; +} + +/* Overwrite OneAsymmetricKey version byte. Return the patched offset or + * -1 on malformed input. */ +static WC_INLINE int test_pkcs8_patch_version_byte(byte* der, word32 derSz, + byte newVer) +{ + word32 idx = 0; + word32 nBytes = 0; + + if (der == NULL || derSz < 5 || der[idx++] != 0x30) { + return -1; + } + if ((der[idx] & 0x80) == 0) { + idx += 1; + } + else { + nBytes = (word32)(der[idx] & 0x7F); + if (nBytes == 0 || nBytes > 4) { + return -1; + } + idx += (1 + nBytes); + } + if (idx + 3 > derSz || der[idx] != 0x02 || der[idx + 1] != 0x01) { + return -1; + } + der[idx + 2] = newVer; + + return (int)(idx + 2); +} +#endif /* WC_ENABLE_ASYM_KEY_EXPORT */ + #endif /* WOLFCRYPT_TEST_API_H */ diff --git a/tests/api/test_asn.c b/tests/api/test_asn.c index 36e4ec72c1..0f7a637c2c 100644 --- a/tests/api/test_asn.c +++ b/tests/api/test_asn.c @@ -21,10 +21,16 @@ #include +#include #include #include +#include +#include #include +#ifdef HAVE_ED25519 + #include +#endif #if defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(HAVE_ED25519) static int test_SetAsymKeyDer_once(byte* privKey, word32 privKeySz, byte* pubKey, @@ -55,6 +61,9 @@ int test_SetAsymKeyDer(void) #if defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(HAVE_ED25519) /* We can't access the keyEd25519Oid variable, so declare it instead */ byte algId[] = {43, 101, 112}; + /* RFC 5958: version is v1 (0) for private only, v2 (1) when public key + * bundled. Conditions 1-5 are private only, 6-8 include pub key and + * mutate version[0] = 0x1 before building trueDer. */ byte version[] = {0x0}; byte keyPat = 0xcc; @@ -286,6 +295,7 @@ int test_SetAsymKeyDer(void) privKeySz = 32; pubKeySz = 32; trueDerSz = 82; + version[0] = 0x1; /* publicKey present (v2) */ /* SEQ */ trueDer[0] = ASN_SEQUENCE | ASN_CONSTRUCTED; @@ -328,6 +338,7 @@ int test_SetAsymKeyDer(void) privKeySz = 32; pubKeySz = 128; trueDerSz = 180; + version[0] = 0x1; /* publicKey present (v2) */ /* SEQ */ trueDer[0] = ASN_SEQUENCE | ASN_CONSTRUCTED; @@ -372,6 +383,7 @@ int test_SetAsymKeyDer(void) privKeySz = 32; pubKeySz = 256; trueDerSz = 310; + version[0] = 0x1; /* publicKey present (v2) */ /* SEQ */ trueDer[0] = ASN_SEQUENCE | ASN_CONSTRUCTED; @@ -413,6 +425,155 @@ int test_SetAsymKeyDer(void) } +/* RFC 5958 leniency: parser must accept all four variants: + * {v=0,v=1} x {publicKey absent, present}. */ +int test_DecodeAsymKey_lenient_versions(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \ + defined(HAVE_ED25519_KEY_IMPORT) && defined(WOLFSSL_KEY_GEN) + ed25519_key key; + ed25519_key parsed; + WC_RNG rng; + byte bundled[256]; /* v=1 + publicKey */ + byte privOnly[256]; /* v=0, no publicKey */ + byte tmp[256]; + int bundledSz = 0; + int privOnlySz = 0; + word32 idx; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&parsed, 0, sizeof(parsed)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ed25519_init(&key), 0); + ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &key), 0); + + ExpectIntGT(bundledSz = wc_Ed25519KeyToDer(&key, bundled, + (word32)sizeof(bundled)), 0); + ExpectIntGT(privOnlySz = wc_Ed25519PrivateKeyToDer(&key, privOnly, + (word32)sizeof(privOnly)), 0); + + if (EXPECT_SUCCESS() && + ((bundledSz > 0) && ((size_t)bundledSz <= sizeof(bundled)) && + (privOnlySz > 0) && ((size_t)privOnlySz <= sizeof(privOnly)))) { + + /* v=1 + publicKey */ + XMEMCPY(tmp, bundled, (size_t)bundledSz); + XMEMSET(&parsed, 0, sizeof(parsed)); + ExpectIntEQ(wc_ed25519_init(&parsed), 0); + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed, + (word32)bundledSz), 0); + wc_ed25519_free(&parsed); + + /* v=0 + publicKey: patch version byte, [1] publicKey field present. */ + XMEMCPY(tmp, bundled, (size_t)bundledSz); + ExpectIntGT(test_pkcs8_patch_version_byte(tmp, (word32)bundledSz, 0), + 0); + XMEMSET(&parsed, 0, sizeof(parsed)); + ExpectIntEQ(wc_ed25519_init(&parsed), 0); + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed, + (word32)bundledSz), 0); + wc_ed25519_free(&parsed); + + /* v=0, no publicKey */ + XMEMCPY(tmp, privOnly, (size_t)privOnlySz); + XMEMSET(&parsed, 0, sizeof(parsed)); + ExpectIntEQ(wc_ed25519_init(&parsed), 0); + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed, + (word32)privOnlySz), 0); + wc_ed25519_free(&parsed); + + /* v=1, no publicKey */ + XMEMCPY(tmp, privOnly, (size_t)privOnlySz); + ExpectIntGT(test_pkcs8_patch_version_byte(tmp, (word32)privOnlySz, 1), + 0); + XMEMSET(&parsed, 0, sizeof(parsed)); + ExpectIntEQ(wc_ed25519_init(&parsed), 0); + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed, + (word32)privOnlySz), 0); + wc_ed25519_free(&parsed); + } + + wc_ed25519_free(&key); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + +int test_DecodeAsymKey_negative(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \ + defined(HAVE_ED25519_KEY_IMPORT) && defined(WOLFSSL_KEY_GEN) + ed25519_key key; + ed25519_key parsed; + WC_RNG rng; + byte good[256]; + byte tmp[256]; + int goodSz = 0; + word32 idx; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&parsed, 0, sizeof(parsed)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ed25519_init(&key), 0); + ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &key), 0); + ExpectIntGT(goodSz = wc_Ed25519KeyToDer(&key, good, + (word32)sizeof(good)), 0); + + if (EXPECT_SUCCESS() && + (goodSz > 0 && (size_t)goodSz <= sizeof(good))) { + + /* Truncated buffer */ + XMEMCPY(tmp, good, (size_t)goodSz); + ExpectIntEQ(wc_ed25519_init(&parsed), 0); + idx = 0; + ExpectIntLT(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed, + (word32)(goodSz - 1)), 0); + wc_ed25519_free(&parsed); + + /* Outer length too big. Patch low-order length byte (long form: bump + * the last byte of the multi-byte length encoding). */ + XMEMCPY(tmp, good, (size_t)goodSz); + if ((good[1] & 0x80) == 0) { + tmp[1] = (byte)(good[1] + 1); + } + else { + word32 nBytes = (word32)(good[1] & 0x7F); + tmp[1 + nBytes] = (byte)(good[1 + nBytes] + 1); + } + XMEMSET(&parsed, 0, sizeof(parsed)); + ExpectIntEQ(wc_ed25519_init(&parsed), 0); + idx = 0; + ExpectIntLT(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed, + (word32)goodSz), 0); + wc_ed25519_free(&parsed); + + /* Outer tag not SEQUENCE */ + XMEMCPY(tmp, good, (size_t)goodSz); + tmp[0] = 0x02; + XMEMSET(&parsed, 0, sizeof(parsed)); + ExpectIntEQ(wc_ed25519_init(&parsed), 0); + idx = 0; + ExpectIntLT(wc_Ed25519PrivateKeyDecode(tmp, &idx, &parsed, + (word32)goodSz), 0); + wc_ed25519_free(&parsed); + } + + wc_ed25519_free(&key); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + #ifndef NO_ASN static int test_GetSetShortInt_once(word32 val, byte* valDer, word32 valDerSz) { diff --git a/tests/api/test_asn.h b/tests/api/test_asn.h index 97a2ee2b7f..f989da1770 100644 --- a/tests/api/test_asn.h +++ b/tests/api/test_asn.h @@ -25,6 +25,8 @@ #include int test_SetAsymKeyDer(void); +int test_DecodeAsymKey_lenient_versions(void); +int test_DecodeAsymKey_negative(void); int test_GetSetShortInt(void); int test_wc_IndexSequenceOf(void); int test_wolfssl_local_MatchBaseName(void); @@ -34,6 +36,8 @@ int test_wc_DecodeObjectId(void); #define TEST_ASN_DECLS \ TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \ + TEST_DECL_GROUP("asn", test_DecodeAsymKey_lenient_versions), \ + TEST_DECL_GROUP("asn", test_DecodeAsymKey_negative), \ TEST_DECL_GROUP("asn", test_GetSetShortInt), \ TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf), \ TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName), \ diff --git a/tests/api/test_curve25519.c b/tests/api/test_curve25519.c index 5eeb5c15b4..e6521c89db 100644 --- a/tests/api/test_curve25519.c +++ b/tests/api/test_curve25519.c @@ -608,3 +608,61 @@ int test_wc_curve25519_priv_clamp_check(void) return EXPECT_RESULT(); } /* END test_wc_curve25519_priv_clamp_check */ +/* + * RFC 5958 OneAsymmetricKey: version=v2 (1) when publicKey is bundled, + * version=v1 (0) for private only. + */ +int test_wc_Curve25519KeyToDer_oneasymkey_version(void) +{ + EXPECT_DECLS; +#if defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_EXPORT) && \ + defined(HAVE_CURVE25519_KEY_IMPORT) + curve25519_key key; + curve25519_key key2; + WC_RNG rng; + byte ref[256]; /* reference DER (bundled, then private only) */ + byte rt[256]; /* re-export target for memcmp */ + int refSz = 0; + int rtSz = 0; + word32 idx; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&key2, 0, sizeof(key2)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_curve25519_init(&key), 0); + ExpectIntEQ(wc_curve25519_init(&key2), 0); + ExpectIntEQ(wc_curve25519_make_key(&rng, CURVE25519_KEYSIZE, &key), 0); + + /* make_key sets both priv and pub: KeyToDer bundles both (v=1). + * Use wc_Curve25519KeyDecode so the publicKey field is preserved in key2 */ + ExpectIntGT(refSz = wc_Curve25519KeyToDer(&key, ref, + (word32)sizeof(ref), 1), 0); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 1); + idx = 0; + ExpectIntEQ(wc_Curve25519KeyDecode(ref, &idx, &key2, (word32)refSz), 0); + ExpectIntEQ(rtSz = wc_Curve25519KeyToDer(&key2, rt, (word32)sizeof(rt), 1), + refSz); + ExpectIntEQ(XMEMCMP(ref, rt, (size_t)refSz), 0); + + /* Private only creates v=0. Reuse ref/rt. */ + XMEMSET(&key2, 0, sizeof(key2)); + ExpectIntEQ(wc_curve25519_init(&key2), 0); + ExpectIntGT(refSz = wc_Curve25519PrivateKeyToDer(&key, ref, + (word32)sizeof(ref)), 0); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 0); + idx = 0; + ExpectIntEQ(wc_Curve25519PrivateKeyDecode(ref, &idx, &key2, + (word32)refSz), 0); + ExpectIntEQ(rtSz = wc_Curve25519PrivateKeyToDer(&key2, rt, + (word32)sizeof(rt)), refSz); + ExpectIntEQ(XMEMCMP(ref, rt, (size_t)refSz), 0); + + wc_curve25519_free(&key); + wc_curve25519_free(&key2); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + diff --git a/tests/api/test_curve25519.h b/tests/api/test_curve25519.h index ce4b1dd4bc..3d14bb53e3 100644 --- a/tests/api/test_curve25519.h +++ b/tests/api/test_curve25519.h @@ -36,6 +36,7 @@ int test_wc_curve25519_export_private_raw_ex(void); int test_wc_curve25519_import_private_raw_ex(void); int test_wc_curve25519_import_private(void); int test_wc_curve25519_priv_clamp_check(void); +int test_wc_Curve25519KeyToDer_oneasymkey_version(void); #define TEST_CURVE25519_DECLS \ TEST_DECL_GROUP("curve25519", test_wc_curve25519_init), \ @@ -49,6 +50,7 @@ int test_wc_curve25519_priv_clamp_check(void); TEST_DECL_GROUP("curve25519", test_wc_curve25519_export_private_raw_ex), \ TEST_DECL_GROUP("curve25519", test_wc_curve25519_import_private_raw_ex), \ TEST_DECL_GROUP("curve25519", test_wc_curve25519_import_private), \ - TEST_DECL_GROUP("curve25519", test_wc_curve25519_priv_clamp_check) + TEST_DECL_GROUP("curve25519", test_wc_curve25519_priv_clamp_check), \ + TEST_DECL_GROUP("curve25519", test_wc_Curve25519KeyToDer_oneasymkey_version) #endif /* WOLFCRYPT_TEST_CURVE25519_H */ diff --git a/tests/api/test_curve448.c b/tests/api/test_curve448.c index 384ede374f..bfc025d29b 100644 --- a/tests/api/test_curve448.c +++ b/tests/api/test_curve448.c @@ -403,3 +403,48 @@ int test_wc_Curve448PrivateKeyToDer(void) return EXPECT_RESULT(); } /* End wc_Curve448PrivateKeyToDer*/ +/* + * RFC 5958: private only path must create version=v1 (0). Curve448 has no + * public API to create bundled key. Only test private key path. */ +int test_wc_Curve448PrivateKeyToDer_oneasymkey_version(void) +{ + EXPECT_DECLS; +#if defined(HAVE_CURVE448) && defined(HAVE_CURVE448_KEY_EXPORT) && \ + defined(HAVE_CURVE448_KEY_IMPORT) + curve448_key key; + curve448_key key2; + WC_RNG rng; + byte ref[256]; /* reference DER (private only) */ + byte rt[256]; /* re-export target for memcmp */ + int refSz = 0; + int rtSz = 0; + word32 idx = 0; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&key2, 0, sizeof(key2)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_curve448_init(&key), 0); + ExpectIntEQ(wc_curve448_init(&key2), 0); + ExpectIntEQ(wc_curve448_make_key(&rng, CURVE448_KEY_SIZE, &key), 0); + + ExpectIntGT(refSz = wc_Curve448PrivateKeyToDer(&key, ref, + (word32)sizeof(ref)), 0); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 0); + + idx = 0; + ExpectIntEQ(wc_Curve448PrivateKeyDecode(ref, &idx, &key2, + (word32)refSz), 0); + ExpectIntGT(rtSz = wc_Curve448PrivateKeyToDer(&key2, rt, + (word32)sizeof(rt)), 0); + ExpectIntEQ(rtSz, refSz); + ExpectIntEQ(XMEMCMP(ref, rt, (size_t)refSz), 0); + + wc_curve448_free(&key); + wc_curve448_free(&key2); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + diff --git a/tests/api/test_curve448.h b/tests/api/test_curve448.h index 011e098963..93af7809fb 100644 --- a/tests/api/test_curve448.h +++ b/tests/api/test_curve448.h @@ -34,6 +34,7 @@ int test_wc_curve448_import_private(void); int test_wc_curve448_init(void); int test_wc_curve448_size(void); int test_wc_Curve448PrivateKeyToDer(void); +int test_wc_Curve448PrivateKeyToDer_oneasymkey_version(void); #define TEST_CURVE448_DECLS \ TEST_DECL_GROUP("curve448", test_wc_curve448_make_key), \ @@ -45,6 +46,7 @@ int test_wc_Curve448PrivateKeyToDer(void); TEST_DECL_GROUP("curve448", test_wc_curve448_import_private), \ TEST_DECL_GROUP("curve448", test_wc_curve448_init), \ TEST_DECL_GROUP("curve448", test_wc_curve448_size), \ - TEST_DECL_GROUP("curve448", test_wc_Curve448PrivateKeyToDer) + TEST_DECL_GROUP("curve448", test_wc_Curve448PrivateKeyToDer), \ + TEST_DECL_GROUP("curve448", test_wc_Curve448PrivateKeyToDer_oneasymkey_version) #endif /* WOLFCRYPT_TEST_CURVE448_H */ diff --git a/tests/api/test_ed25519.c b/tests/api/test_ed25519.c index 1ac474e4fb..2df8d41270 100644 --- a/tests/api/test_ed25519.c +++ b/tests/api/test_ed25519.c @@ -678,3 +678,50 @@ int test_wc_Ed25519PrivateKeyToDer(void) return EXPECT_RESULT(); } /* End test_wc_Ed25519PrivateKeyToDer*/ +/* + * RFC 5958: version=v2 (1) when pub key is bundled, v1 (0) for private only. */ +int test_wc_Ed25519KeyToDer_oneasymkey_version(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_EXPORT) && \ + defined(HAVE_ED25519_KEY_IMPORT) && defined(WOLFSSL_KEY_GEN) + ed25519_key key; + ed25519_key key2; + WC_RNG rng; + byte ref[256]; /* reference DER (bundled, then private only) */ + byte rt[256]; /* re-export target for memcmp */ + int refSz = 0; + int rtSz = 0; + word32 idx; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&key2, 0, sizeof(key2)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ed25519_init(&key), 0); + ExpectIntEQ(wc_ed25519_init(&key2), 0); + ExpectIntEQ(wc_ed25519_make_key(&rng, ED25519_KEY_SIZE, &key), 0); + + /* Bundled (v=1) */ + ExpectIntGT(refSz = wc_Ed25519KeyToDer(&key, ref, (word32)sizeof(ref)), 0); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 1); + idx = 0; + ExpectIntEQ(wc_Ed25519PrivateKeyDecode(ref, &idx, &key2, (word32)refSz), + 0); + ExpectIntEQ(rtSz = wc_Ed25519KeyToDer(&key2, rt, (word32)sizeof(rt)), + refSz); + ExpectIntEQ(XMEMCMP(ref, rt, (size_t)refSz), 0); + + /* Priv-only (v=0) */ + ExpectIntGT(refSz = wc_Ed25519PrivateKeyToDer(&key, ref, + (word32)sizeof(ref)), 0); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 0); + + wc_ed25519_free(&key); + wc_ed25519_free(&key2); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + diff --git a/tests/api/test_ed25519.h b/tests/api/test_ed25519.h index 42b23ddda0..4ac3693f66 100644 --- a/tests/api/test_ed25519.h +++ b/tests/api/test_ed25519.h @@ -36,6 +36,7 @@ int test_wc_ed25519_exportKey(void); int test_wc_Ed25519PublicKeyToDer(void); int test_wc_Ed25519KeyToDer(void); int test_wc_Ed25519PrivateKeyToDer(void); +int test_wc_Ed25519KeyToDer_oneasymkey_version(void); #define TEST_ED25519_DECLS \ TEST_DECL_GROUP("ed25519", test_wc_ed25519_make_key), \ @@ -49,6 +50,7 @@ int test_wc_Ed25519PrivateKeyToDer(void); TEST_DECL_GROUP("ed25519", test_wc_ed25519_exportKey), \ TEST_DECL_GROUP("ed25519", test_wc_Ed25519PublicKeyToDer), \ TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer), \ - TEST_DECL_GROUP("ed25519", test_wc_Ed25519PrivateKeyToDer) + TEST_DECL_GROUP("ed25519", test_wc_Ed25519PrivateKeyToDer), \ + TEST_DECL_GROUP("ed25519", test_wc_Ed25519KeyToDer_oneasymkey_version) #endif /* WOLFCRYPT_TEST_ED25519_H */ diff --git a/tests/api/test_ed448.c b/tests/api/test_ed448.c index 007e3b97b8..f6b7552302 100644 --- a/tests/api/test_ed448.c +++ b/tests/api/test_ed448.c @@ -604,3 +604,48 @@ int test_wc_Ed448PrivateKeyToDer(void) return EXPECT_RESULT(); } /* End test_wc_Ed448PrivateKeyToDer */ +/* + * RFC 5958: version=v2 (1) when pub key is bundled, v1 (0) for private only. */ +int test_wc_Ed448KeyToDer_oneasymkey_version(void) +{ + EXPECT_DECLS; +#if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_EXPORT) && \ + defined(HAVE_ED448_KEY_IMPORT) && defined(WOLFSSL_KEY_GEN) + ed448_key key; + ed448_key key2; + WC_RNG rng; + byte ref[512]; /* reference DER (bundled, then private only) */ + byte rt[512]; /* re-export target for memcmp */ + int refSz = 0; + int rtSz = 0; + word32 idx; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&key2, 0, sizeof(key2)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_ed448_init(&key), 0); + ExpectIntEQ(wc_ed448_init(&key2), 0); + ExpectIntEQ(wc_ed448_make_key(&rng, ED448_KEY_SIZE, &key), 0); + + /* Bundled (v=1) */ + ExpectIntGT(refSz = wc_Ed448KeyToDer(&key, ref, (word32)sizeof(ref)), 0); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 1); + idx = 0; + ExpectIntEQ(wc_Ed448PrivateKeyDecode(ref, &idx, &key2, (word32)refSz), 0); + ExpectIntEQ(rtSz = wc_Ed448KeyToDer(&key2, rt, (word32)sizeof(rt)), refSz); + ExpectIntEQ(XMEMCMP(ref, rt, (size_t)refSz), 0); + + /* Private only (v=0) */ + ExpectIntGT(refSz = wc_Ed448PrivateKeyToDer(&key, ref, + (word32)sizeof(ref)), 0); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 0); + + wc_ed448_free(&key); + wc_ed448_free(&key2); + wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} + diff --git a/tests/api/test_ed448.h b/tests/api/test_ed448.h index f0dc261172..68d85a6338 100644 --- a/tests/api/test_ed448.h +++ b/tests/api/test_ed448.h @@ -36,6 +36,7 @@ int test_wc_ed448_exportKey(void); int test_wc_Ed448PublicKeyToDer(void); int test_wc_Ed448KeyToDer(void); int test_wc_Ed448PrivateKeyToDer(void); +int test_wc_Ed448KeyToDer_oneasymkey_version(void); #define TEST_ED448_DECLS \ TEST_DECL_GROUP("ed448", test_wc_ed448_make_key), \ @@ -49,6 +50,7 @@ int test_wc_Ed448PrivateKeyToDer(void); TEST_DECL_GROUP("ed448", test_wc_ed448_exportKey), \ TEST_DECL_GROUP("ed448", test_wc_Ed448PublicKeyToDer), \ TEST_DECL_GROUP("ed448", test_wc_Ed448KeyToDer), \ - TEST_DECL_GROUP("ed448", test_wc_Ed448PrivateKeyToDer) + TEST_DECL_GROUP("ed448", test_wc_Ed448PrivateKeyToDer), \ + TEST_DECL_GROUP("ed448", test_wc_Ed448KeyToDer_oneasymkey_version) #endif /* WOLFCRYPT_TEST_ED448_H */ diff --git a/tests/api/test_mldsa.c b/tests/api/test_mldsa.c index 6d1d518ff4..6eb44b24f4 100644 --- a/tests/api/test_mldsa.c +++ b/tests/api/test_mldsa.c @@ -2982,13 +2982,18 @@ int test_wc_dilithium_public_der_decode(void) return EXPECT_RESULT(); } +#if defined(HAVE_DILITHIUM) && \ + !defined(WOLFSSL_DILITHIUM_NO_ASN1) && \ + !defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) +#define DILITHIUM_MAX_DER_SIZE 8192 +#endif + int test_wc_dilithium_der(void) { EXPECT_DECLS; #if defined(HAVE_DILITHIUM) && \ !defined(WOLFSSL_DILITHIUM_NO_ASN1) && \ !defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) -#define DILITHIUM_MAX_DER_SIZE 8192 dilithium_key* key; WC_RNG rng; byte* der = NULL; @@ -3195,6 +3200,93 @@ int test_wc_dilithium_der(void) return EXPECT_RESULT(); } +#if defined(HAVE_DILITHIUM) && !defined(WOLFSSL_DILITHIUM_NO_ASN1) && \ + !defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) + +/* Decode, re-export, byte-compare. Asserts version=1 on the bundled form and + * version=0 on the private only form. */ +static int dilithium_oneasymkey_version_check(int level) +{ + EXPECT_DECLS; + dilithium_key key; + dilithium_key key2; + WC_RNG rng; + byte* ref = NULL; + byte* rt = NULL; + int refSz = 0; + int rtSz = 0; + word32 idx; + + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(&key2, 0, sizeof(key2)); + XMEMSET(&rng, 0, sizeof(rng)); + + ExpectNotNull(ref = (byte*)XMALLOC(DILITHIUM_MAX_DER_SIZE, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + ExpectNotNull(rt = (byte*)XMALLOC(DILITHIUM_MAX_DER_SIZE, NULL, + DYNAMIC_TYPE_TMP_BUFFER)); + + ExpectIntEQ(wc_InitRng(&rng), 0); + ExpectIntEQ(wc_dilithium_init(&key), 0); + ExpectIntEQ(wc_dilithium_init(&key2), 0); + ExpectIntEQ(wc_dilithium_set_level(&key, level), 0); + ExpectIntEQ(wc_dilithium_set_level(&key2, level), 0); + ExpectIntEQ(wc_dilithium_make_key(&key, &rng), 0); + + /* Bundled (v=1) */ + PRIVATE_KEY_UNLOCK(); + ExpectIntGT(refSz = wc_Dilithium_KeyToDer(&key, ref, + DILITHIUM_MAX_DER_SIZE), 0); + PRIVATE_KEY_LOCK(); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 1); + + idx = 0; + ExpectIntEQ(wc_Dilithium_PrivateKeyDecode(ref, &idx, &key2, + (word32)refSz), 0); + PRIVATE_KEY_UNLOCK(); + ExpectIntEQ(rtSz = wc_Dilithium_KeyToDer(&key2, rt, + DILITHIUM_MAX_DER_SIZE), refSz); + PRIVATE_KEY_LOCK(); + ExpectIntEQ(XMEMCMP(ref, rt, (size_t)refSz), 0); + + /* Private only (v=0). Reuse ref. */ + PRIVATE_KEY_UNLOCK(); + ExpectIntGT(refSz = wc_Dilithium_PrivateKeyToDer(&key, ref, + DILITHIUM_MAX_DER_SIZE), 0); + PRIVATE_KEY_LOCK(); + ExpectIntEQ(test_pkcs8_get_version_byte(ref, (word32)refSz), 0); + + wc_dilithium_free(&key); + wc_dilithium_free(&key2); + wc_FreeRng(&rng); + XFREE(rt, NULL, DYNAMIC_TYPE_TMP_BUFFER); + XFREE(ref, NULL, DYNAMIC_TYPE_TMP_BUFFER); + + return EXPECT_RESULT(); +} +#endif + +int test_wc_dilithium_oneasymkey_version(void) +{ + EXPECT_DECLS; +#if defined(HAVE_DILITHIUM) && !defined(WOLFSSL_DILITHIUM_NO_ASN1) && \ + !defined(WOLFSSL_DILITHIUM_NO_MAKE_KEY) + #ifndef WOLFSSL_NO_ML_DSA_44 + ExpectIntEQ(dilithium_oneasymkey_version_check(WC_ML_DSA_44), + TEST_SUCCESS); + #endif + #ifndef WOLFSSL_NO_ML_DSA_65 + ExpectIntEQ(dilithium_oneasymkey_version_check(WC_ML_DSA_65), + TEST_SUCCESS); + #endif + #ifndef WOLFSSL_NO_ML_DSA_87 + ExpectIntEQ(dilithium_oneasymkey_version_check(WC_ML_DSA_87), + TEST_SUCCESS); + #endif +#endif + return EXPECT_RESULT(); +} + int test_wc_dilithium_make_key_from_seed(void) { EXPECT_DECLS; diff --git a/tests/api/test_mldsa.h b/tests/api/test_mldsa.h index ed98e30fa9..76c1f534b5 100644 --- a/tests/api/test_mldsa.h +++ b/tests/api/test_mldsa.h @@ -33,6 +33,7 @@ int test_wc_dilithium_sign_vfy(void); int test_wc_dilithium_check_key(void); int test_wc_dilithium_public_der_decode(void); int test_wc_dilithium_der(void); +int test_wc_dilithium_oneasymkey_version(void); int test_wc_dilithium_make_key_from_seed(void); int test_wc_dilithium_sig_kats(void); int test_wc_dilithium_sign_ctx_kats(void); @@ -57,6 +58,7 @@ int test_mldsa_x509_pubkey_sigtype(void); TEST_DECL_GROUP("mldsa", test_wc_dilithium_check_key), \ TEST_DECL_GROUP("mldsa", test_wc_dilithium_public_der_decode), \ TEST_DECL_GROUP("mldsa", test_wc_dilithium_der), \ + TEST_DECL_GROUP("mldsa", test_wc_dilithium_oneasymkey_version), \ TEST_DECL_GROUP("mldsa", test_wc_dilithium_make_key_from_seed), \ TEST_DECL_GROUP("mldsa", test_wc_dilithium_sig_kats), \ TEST_DECL_GROUP("mldsa", test_wc_dilithium_sign_ctx_kats), \ diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index d935483538..a5f562218f 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -31485,7 +31485,9 @@ int DecodeAsymKey_Assign(const byte* input, word32* inOutIdx, word32 inSz, if (GetMyVersion(input, inOutIdx, &version, inSz) < 0) return ASN_PARSE_E; - if (version != 0) { + + /* RFC 5958: v1 (0) for privateKey only, v2 (1) when publicKey added. */ + if (version != 0 && version != 1) { WOLFSSL_MSG("Unrecognized version of private key"); return ASN_PARSE_E; } @@ -32008,8 +32010,8 @@ int SetAsymKeyDer(const byte* privKey, word32 privKeyLen, /* seq */ seqSz = SetSequence(verSz + algoSz + privSz + pubSz, output); idx = seqSz; - /* ver */ - SetMyVersion(0, output + idx, FALSE); + /* ver: RFC 5958 requires v2 (1) iff publicKey present, else v1 (0). */ + SetMyVersion((word32)(pubKey ? PKCS8v1 : PKCS8v0), output + idx, FALSE); idx += verSz; /* algo */ algoSz = SetAlgoID(keyType, output + idx, oidKeyType, 0); @@ -32037,8 +32039,9 @@ int SetAsymKeyDer(const byte* privKey, word32 privKeyLen, CALLOC_ASNSETDATA(dataASN, privateKeyASN_Length, ret, NULL); if (ret == 0) { - /* Set version = 0 */ - SetASN_Int8Bit(&dataASN[PRIVKEYASN_IDX_VER], 0); + /* RFC 5958: v2 (1) iff publicKey present, else v1 (0). */ + SetASN_Int8Bit(&dataASN[PRIVKEYASN_IDX_VER], + (byte)(pubKey ? PKCS8v1 : PKCS8v0)); /* Set OID. */ SetASN_OID(&dataASN[PRIVKEYASN_IDX_PKEYALGO_OID], (word32)keyType, oidKeyType);