diff --git a/doc/dox_comments/header_files/asn_public.h b/doc/dox_comments/header_files/asn_public.h index 30ea784b00..c3dfaa4911 100644 --- a/doc/dox_comments/header_files/asn_public.h +++ b/doc/dox_comments/header_files/asn_public.h @@ -1557,6 +1557,137 @@ int wc_EccPublicKeyToDer(ecc_key* key, byte* output, int wc_EccPublicKeyToDer_ex(ecc_key* key, byte* output, word32 inLen, int with_AlgCurve, int comp); + +/*! + \ingroup ASN + + \brief This function decodes a Curve25519 private key (only) from a DER + encoded buffer + + \return 0 Success + \return BAD_FUNC_ARG Returns if input, inOutIdx or key is null + \return ASN_PARSE_E Returns if there is an error parsing the DER encoded + data + \return ECC_BAD_ARG_E Returns if the key length is not CURVE25519_KEYSIZE or + the DER key contains other issues despite being properly formatted. + \return BUFFER_E Returns if the input buffer is too small to contain a + valid DER encoded key. + + \param input Pointer to buffer containing DER encoded private key + \param inOutIdx Index to start reading input buffer from. On output, + index is set to last position parsed of input buffer. + \param key Pointer to curve25519_key structure to store decoded key + \param inSz Size of input DER buffer +*/ +int wc_Curve25519PrivateKeyDecode(const byte* input, word32* inOutIdx, + curve25519_key* key, word32 inSz); + +/*! + \ingroup ASN + + \brief This function decodes a Curve25519 public key (only) from a DER + encoded buffer. + + \return 0 Success + \return BAD_FUNC_ARG Returns if input, inOutIdx or key is null + \return ASN_PARSE_E Returns if there is an error parsing the DER encoded + data + \return ECC_BAD_ARG_E Returns if the key length is not CURVE25519_KEYSIZE or + the DER key contains other issues despite being properly formatted. + \return BUFFER_E Returns if the input buffer is too small to contain a + valid DER encoded key. + + \param input Pointer to buffer containing DER encoded public key + \param inOutIdx Index to start reading input buffer from. On output, + index is set to last position parsed of input buffer. + \param key Pointer to curve25519_key structure to store decoded key + \param inSz Size of input DER buffer +*/ +int wc_Curve25519PublicKeyDecode(const byte* input, word32* inOutIdx, + curve25519_key* key, word32 inSz); + +/*! + \ingroup ASN + + \brief This function decodes a Curve25519 key from a DER encoded buffer. It + can decode either a private key, a public key, or both. + + \return 0 Success + \return BAD_FUNC_ARG Returns if input, inOutIdx or key is null + \return ASN_PARSE_E Returns if there is an error parsing the DER encoded + data + \return ECC_BAD_ARG_E Returns if the key length is not CURVE25519_KEYSIZE or + the DER key contains other issues despite being properly formatted. + \return BUFFER_E Returns if the input buffer is too small to contain a + valid DER encoded key. + + \param input Pointer to buffer containing DER encoded key + \param inOutIdx Index to start reading input buffer from. On output, + index is set to last position parsed of input buffer. + \param key Pointer to curve25519_key structure to store decoded key + \param inSz Size of input DER buffer +*/ +int wc_Curve25519KeyDecode(const byte* input, word32* inOutIdx, + curve25519_key* key, word32 inSz); + +/*! + \ingroup ASN + + \brief This function encodes a Curve25519 private key to DER format. If the + input key structure contains a public key, it will be ignored. + + \return >0 Success, length of DER encoding + \return BAD_FUNC_ARG Returns if key or output is null + \return MEMORY_E Returns if there is an allocation failure + \return BUFFER_E Returns if output buffer is too small + + \param key Pointer to curve25519_key structure containing private key to + encode + \param output Buffer to hold DER encoding + \param inLen Size of output buffer +*/ +int wc_Curve25519PrivateKeyToDer(curve25519_key* key, byte* output, + word32 inLen); + +/*! + \ingroup ASN + + \brief This function encodes a Curve25519 public key to DER format. If the + input key structure contains a private key, it will be ignored. + + \return >0 Success, length of DER encoding + \return BAD_FUNC_ARG Returns if key or output is null + \return MEMORY_E Returns if there is an allocation failure + \return BUFFER_E Returns if output buffer is too small + + \param key Pointer to curve25519_key structure containing public key to + encode + \param output Buffer to hold DER encoding + \param inLen Size of output buffer + \param withAlg Whether to include algorithm identifier +*/ +int wc_Curve25519PublicKeyToDer(curve25519_key* key, byte* output, word32 inLen, + int withAlg); + +/*! + \ingroup ASN + + \brief This function encodes a Curve25519 key to DER format. It can encode + either a private key, a public key, or both. + + \return >0 Success, length of DER encoding + \return BAD_FUNC_ARG Returns if key or output is null + \return MEMORY_E Returns if there is an allocation failure + \return BUFFER_E Returns if output buffer is too small + + \param key Pointer to curve25519_key structure containing key to encode + \param output Buffer to hold DER encoding + \param inLen Size of output buffer + \param withAlg Whether to include algorithm identifier +*/ +int wc_Curve25519KeyToDer(curve25519_key* key, byte* output, word32 inLen, + int withAlg); + /*! \ingroup ASN diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 0c520027bc..737f8bd41d 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 5b15367f8e..234cbf235a 100644 --- a/wolfcrypt/test/test.c +++ b/wolfcrypt/test/test.c @@ -35042,6 +35042,163 @@ 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; + } + + /* Test decode/encode of a key file containing both public and private + * fields */ + if (ret == 0) { + XMEMSET(&key, 0 , sizeof(key)); + + /* Decode public 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) { + /* Decode private key */ + idx = 0; + ret = wc_Curve25519KeyDecode(kCurve25519PrivDer, &idx, &key, + (word32)sizeof(kCurve25519PrivDer)); + if (ret < 0) { + ret = WC_TEST_RET_ENC_EC(ret); + } + } + /* Both public and private flags should be set */ + if ((ret == 0) && (!key.pubSet && !key.privSet)) { + ret = WC_TEST_RET_ENC_NC; + } + if (ret == 0) { + /* Export key to temporary DER */ + 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); + } + + /* Re-import temporary DER */ + if (ret == 0) { + idx = 0; + ret = wc_Curve25519KeyDecode(output, &idx, &key, sizeof(output)); + if (ret < 0) { + ret = WC_TEST_RET_ENC_EC(ret); + } + } + + /* Ensure public and private keys survived combined keypair + * export/import by re-exporting DER for private and public keys, + * individually, and re-checking output against known good vectors. + * This is slightly circuitous but does test the functionality + * without requiring the addition of new test keys */ + if (ret == 0) { + idx = 0; + ret = wc_Curve25519PrivateKeyDecode(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_Curve25519PrivateKeyToDer(&key, output, outputSz); + 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; + } + if (ret == 0) { + idx = 0; + ret = wc_Curve25519PublicKeyDecode(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_Curve25519PublicKeyToDer(&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 b8bbce40f4..1196c6a5f3 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 */