diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 0c520027b..737f8bd41 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -35661,6 +35661,55 @@ int wc_Curve25519PublicKeyDecode(const byte* input, word32* inOutIdx, } return ret; } + +/* Decode Curve25519 key from DER format - can handle private only, + * public only, or private+public key pairs. + * return 0 on success, negative on error */ +int wc_Curve25519KeyDecode(const byte* input, word32* inOutIdx, + curve25519_key* key, word32 inSz) +{ + int ret; + byte privKey[CURVE25519_KEYSIZE]; + byte pubKey[CURVE25519_KEYSIZE]; + word32 privKeyLen = CURVE25519_KEYSIZE; + word32 pubKeyLen = CURVE25519_KEYSIZE; + + /* sanity check */ + if (input == NULL || inOutIdx == NULL || key == NULL || inSz == 0) { + return BAD_FUNC_ARG; + } + + /* Try to decode as private key first (may include public) */ + ret = DecodeAsymKey(input, inOutIdx, inSz, privKey, &privKeyLen, + pubKey, &pubKeyLen, X25519k); + + if (ret == 0) { + /* Successfully decoded private key */ + if (pubKeyLen > 0) { + /* Have both private and public */ + ret = wc_curve25519_import_private_raw(privKey, privKeyLen, + pubKey, pubKeyLen, key); + } + else { + /* Private only */ + ret = wc_curve25519_import_private(privKey, privKeyLen, key); + } + } + else { + /* Try decoding as public key */ + *inOutIdx = 0; /* Reset index */ + pubKeyLen = CURVE25519_KEYSIZE; + ret = DecodeAsymKeyPublic(input, inOutIdx, inSz, + pubKey, &pubKeyLen, X25519k); + if (ret == 0) { + /* Successfully decoded public key */ + ret = wc_curve25519_import_public(pubKey, pubKeyLen, key); + } + } + + return ret; +} + #endif /* HAVE_CURVE25519 && HAVE_ED25519_KEY_IMPORT */ @@ -35868,6 +35917,63 @@ int wc_Curve25519PublicKeyToDer(curve25519_key* key, byte* output, word32 inLen, } return ret; } + +/* Export Curve25519 key to DER format - handles private only, public only, + * or private+public key pairs based on what's set in the key structure. + * Returns length written on success, negative on error */ +int wc_Curve25519KeyToDer(curve25519_key* key, byte* output, word32 inLen, int withAlg) +{ + int ret; + byte privKey[CURVE25519_KEYSIZE]; + byte pubKey[CURVE25519_KEYSIZE]; + word32 privKeyLen = CURVE25519_KEYSIZE; + word32 pubKeyLen = CURVE25519_KEYSIZE; + + if (key == NULL) { + return BAD_FUNC_ARG; + } + + /* Check what we have in the key structure */ + if (key->privSet) { + /* Export private key to buffer */ + ret = wc_curve25519_export_private_raw(key, privKey, &privKeyLen); + if (ret != 0) { + return ret; + } + + if (key->pubSet) { + /* Export public key if available */ + ret = wc_curve25519_export_public(key, pubKey, &pubKeyLen); + if (ret != 0) { + return ret; + } + /* Export both private and public */ + ret = SetAsymKeyDer(privKey, privKeyLen, + pubKey, pubKeyLen, + output, inLen, X25519k); + } + else { + /* Export private only */ + ret = SetAsymKeyDer(privKey, privKeyLen, + NULL, 0, + output, inLen, X25519k); + } + } + else if (key->pubSet) { + /* Export public key only */ + ret = wc_curve25519_export_public(key, pubKey, &pubKeyLen); + if (ret == 0) { + ret = SetAsymKeyDerPublic(pubKey, pubKeyLen, + output, inLen, X25519k, withAlg); + } + } + else { + /* Neither public nor private key is set */ + ret = BAD_FUNC_ARG; + } + + return ret; +} #endif /* HAVE_CURVE25519 && HAVE_CURVE25519_KEY_EXPORT */ #if defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT) diff --git a/wolfcrypt/test/test.c b/wolfcrypt/test/test.c index 5b15367f8..aa1b4be5a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -35042,6 +35042,62 @@ static wc_test_ret_t curve255519_der_test(void) ret = WC_TEST_RET_ENC_NC; } + + /* Test decode/encode of Curve25519 private key (only) using generic API */ + if (ret == 0) { + /* clear key, since generic API will try to decode all fields */ + XMEMSET(&key, 0, sizeof(key)); + + idx = 0; + ret = wc_Curve25519KeyDecode(kCurve25519PrivDer, &idx, &key, + (word32)sizeof(kCurve25519PrivDer)); + if (ret < 0) { + ret = WC_TEST_RET_ENC_EC(ret); + } + } + if (ret == 0) { + outputSz = (word32)sizeof(output); + ret = wc_Curve25519KeyToDer(&key, output, outputSz, 1); + if (ret >= 0) { + outputSz = (word32)ret; + ret = 0; + } + else { + ret = WC_TEST_RET_ENC_EC(ret); + } + } + if (ret == 0 && (outputSz != (word32)sizeof(kCurve25519PrivDer) || + XMEMCMP(output, kCurve25519PrivDer, outputSz) != 0)) { + ret = WC_TEST_RET_ENC_NC; + } + + /* Test decode/encode of Curve25519 public key (only) using generic API */ + if (ret == 0) { + /* clear key, since generic API will try to decode all fields */ + XMEMSET(&key, 0, sizeof(key)); + idx = 0; + ret = wc_Curve25519KeyDecode(kCurve25519PubDer, &idx, &key, + (word32)sizeof(kCurve25519PubDer)); + if (ret < 0) { + ret = WC_TEST_RET_ENC_EC(ret); + } + } + if (ret == 0) { + outputSz = (word32)sizeof(output); + ret = wc_Curve25519KeyToDer(&key, output, outputSz, 1); + if (ret >= 0) { + outputSz = (word32)ret; + ret = 0; + } + else { + ret = WC_TEST_RET_ENC_EC(ret); + } + } + if (ret == 0 && (outputSz != (word32)sizeof(kCurve25519PubDer) || + XMEMCMP(output, kCurve25519PubDer, outputSz) != 0)) { + ret = WC_TEST_RET_ENC_NC; + } + wc_curve25519_free(&key); return ret; diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index b8bbce40f..1196c6a5f 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -841,12 +841,16 @@ WOLFSSL_API int wc_Curve25519PrivateKeyDecode( const byte* input, word32* inOutIdx, curve25519_key* key, word32 inSz); WOLFSSL_API int wc_Curve25519PublicKeyDecode( const byte* input, word32* inOutIdx, curve25519_key* key, word32 inSz); +WOLFSSL_API int wc_Curve25519KeyDecode(const byte *input, word32 *inOutIdx, + curve25519_key *key, word32 inSz); #endif #ifdef HAVE_CURVE25519_KEY_EXPORT WOLFSSL_API int wc_Curve25519PrivateKeyToDer( curve25519_key* key, byte* output, word32 inLen); WOLFSSL_API int wc_Curve25519PublicKeyToDer( curve25519_key* key, byte* output, word32 inLen, int withAlg); +WOLFSSL_API int wc_Curve25519KeyToDer(curve25519_key* key, byte* output, + word32 inLen, int withAlg); #endif #endif /* HAVE_CURVE25519 */