mirror of
https://github.com/wolfSSL/wolfssl.git
synced 2026-07-06 11:50:49 +02:00
9393d62591
Replace the liboqs-based pre-standardization SPHINCS+ implementation with the native FIPS 205 SLH-DSA implementation across the certificate / ASN.1 / X.509 layers, and add SLH-DSA-rooted test certificates plus TLS 1.3 .conf scenarios that exercise the new verification path. All liboqs SPHINCS+ code is removed. This enables SLH-DSA for certificate chain authentication: CA certificates signed with SLH-DSA, certificate signature verification against an SLH-DSA root. TLS 1.3 entity authentication via CertificateVerify with SLH-DSA will be added in a follow-up PR. Follows RFC 9909 (X.509 Algorithm Identifiers for SLH-DSA) and NIST FIPS 205. Supports both SHAKE and SHA-2 parameter families across all twelve standardized variants. DER codec: - New PrivateKeyDecode, PublicKeyDecode, KeyToDer, PrivateKeyToDer, PublicKeyToDer with RFC 9909 encoding (bare OCTET STRING containing 4*n raw bytes = SK.seed || SK.prf || PK.seed || PK.root, no nested wrapper). OID auto-detection across all twelve SHAKE / SHA-2 variants. - PublicKeyDecode raw-bytes fast path mirrors wc_Falcon_PublicKeyDecode and wc_Dilithium_PublicKeyDecode so callers (notably wolfssl_x509_make_der and ConfirmSignature, which pass the raw BIT STRING contents stashed by StoreKey) decode correctly. Honours the caller's *inOutIdx start offset. - Error paths in Private/PublicKeyDecode preserve params/flags/ inOutIdx and only ForceZero the buffer half each helper actually writes; skip the wipe entirely on BAD_LENGTH_E (no bytes touched). - ImportPublic uses |= on flags so a Private-then-Public import sequence retains FLAG_PRIVATE. OID dispatch: - 12 standardized NIST OIDs (6 SHAKE + 6 SHA-2) per RFC 9909. The pre-standardization OID-collision mechanism is removed since NIST OIDs do not collide. - wc_SlhDsaOidToParam / wc_SlhDsaOidToCertType return NOT_COMPILED_IN (rather than -1) for recognised SLH-DSA OIDs whose parameter set isn't built; wc_IsSlhDsaOid recognises both. The x509 dispatch surfaces this as a precise diagnostic instead of the generic "No public key found". - wc_GetKeyOID picks a placeholder parameter from whatever variant is compiled in and #errors at compile time if none is. - asn_orig.c EncodeCert / EncodeCertReq accept SHA-2 SLH-DSA keyTypes alongside SHAKE. Tests and fixtures: - Test cert chain in certs/slhdsa/: SLH-DSA-SHAKE-128s and SLH-DSA-SHA2-128s self-signed roots that sign reused ML-DSA-44 entity keys (server + client), plus the gen script (gen-slhdsa-mldsa-certs.sh, OpenSSL >= 3.5). - New TLS 1.3 .conf scenarios under tests/suites.c dispatch: test-tls13-slhdsa-shake.conf, test-tls13-slhdsa-sha2.conf, and a wrong-CA negative test test-tls13-slhdsa-fail.conf. - DER round-trip and on-disk decode tests; bench_slhdsa_*_key.der fixtures regenerated with wolfSSL's own encoder so the codec is pinned to RFC 9909. - New unit test test_wc_slhdsa_x509_i2d_roundtrip exercises the raw PublicKeyDecode entry point that wolfssl_x509_make_der relies on. - test_wc_slhdsa_check_key now tests both Public-then-Private and Private-then-Public import orderings. Build / ABI: - DYNAMIC_TYPE_SPHINCS = 98 kept as RESERVED with a tombstone comment for ABI stability; new code should use DYNAMIC_TYPE_SLHDSA (107). - All build system / IDE project files updated; SPHINCS+ sources, headers, and test data removed. - Dead bench_slhdsa_*_key arrays removed from gencertbuf.pl and certs_test.h; the .der files on disk drive the decode tests.
310 lines
10 KiB
C
310 lines
10 KiB
C
/*!
|
|
\ingroup ASN
|
|
\brief This function converts BER (Basic Encoding Rules) formatted data
|
|
to DER (Distinguished Encoding Rules) format. BER allows indefinite
|
|
length encoding while DER requires definite lengths. This function
|
|
calculates definite lengths for all indefinite length items.
|
|
|
|
\return 0 On success.
|
|
\return ASN_PARSE_E If the BER data is invalid.
|
|
\return BAD_FUNC_ARG If ber or derSz are NULL.
|
|
\return BUFFER_E If der is not NULL and derSz is too small.
|
|
|
|
\param ber pointer to the buffer containing BER formatted data
|
|
\param berSz size of the BER data in bytes
|
|
\param der pointer to buffer to store DER formatted data (can be NULL
|
|
to calculate required size)
|
|
\param derSz pointer to size of der buffer; updated with actual size
|
|
needed or used
|
|
|
|
\note This API is not public by default. Define WOLFSSL_PUBLIC_ASN to
|
|
expose APIs marked WOLFSSL_ASN_API.
|
|
|
|
_Example_
|
|
\code
|
|
byte ber[256] = { }; // BER encoded data
|
|
byte der[256];
|
|
word32 derSz = sizeof(der);
|
|
|
|
int ret = wc_BerToDer(ber, sizeof(ber), der, &derSz);
|
|
if (ret == 0) {
|
|
// der now contains DER formatted data of length derSz
|
|
}
|
|
\endcode
|
|
|
|
\sa wc_EncodeObjectId
|
|
*/
|
|
int wc_BerToDer(const byte* ber, word32 berSz, byte* der, word32* derSz);
|
|
|
|
/*!
|
|
\ingroup ASN
|
|
\brief This function frees a linked list of alternative names
|
|
(DNS_entry structures). It deallocates each node and its associated
|
|
name string, IP string, and RID string if present.
|
|
|
|
\return none No return value.
|
|
|
|
\param altNames pointer to the head of the alternative names linked list
|
|
\param heap pointer to heap hint for memory deallocation (can be NULL)
|
|
|
|
\note This API is not public by default. Define WOLFSSL_PUBLIC_ASN to
|
|
expose APIs marked WOLFSSL_ASN_API.
|
|
|
|
_Example_
|
|
\code
|
|
DNS_entry* altNames = NULL;
|
|
// populate altNames with certificate alternative names
|
|
|
|
FreeAltNames(altNames, NULL);
|
|
// altNames list is now freed
|
|
\endcode
|
|
|
|
\sa AltNameNew
|
|
*/
|
|
void FreeAltNames(DNS_entry* altNames, void* heap);
|
|
|
|
/*!
|
|
\ingroup ASN
|
|
\brief This function sets an extended callback for handling unknown
|
|
certificate extensions during certificate parsing. The callback
|
|
receives additional context information compared to the basic
|
|
callback.
|
|
|
|
\return 0 On success.
|
|
\return BAD_FUNC_ARG If cert is NULL.
|
|
|
|
\param cert pointer to the DecodedCert structure
|
|
\param cb callback function to handle unknown extensions
|
|
\param ctx context pointer passed to the callback
|
|
|
|
\note This API is not public by default. Define WOLFSSL_PUBLIC_ASN to
|
|
expose APIs marked WOLFSSL_ASN_API.
|
|
|
|
_Example_
|
|
\code
|
|
DecodedCert cert;
|
|
|
|
int UnknownExtCallback(const byte* oid, word32 oidSz, int crit,
|
|
const byte* der, word32 derSz, void* ctx) {
|
|
// handle unknown extension
|
|
return 0;
|
|
}
|
|
|
|
wc_InitDecodedCert(&cert, derCert, derCertSz, NULL);
|
|
wc_SetUnknownExtCallbackEx(&cert, UnknownExtCallback, myContext);
|
|
wc_ParseCert(&cert, CERT_TYPE, NO_VERIFY, NULL);
|
|
\endcode
|
|
|
|
\sa wc_SetUnknownExtCallback
|
|
\sa wc_InitDecodedCert
|
|
*/
|
|
int wc_SetUnknownExtCallbackEx(DecodedCert* cert,
|
|
wc_UnknownExtCallbackEx cb, void *ctx);
|
|
|
|
/*!
|
|
\ingroup ASN
|
|
\brief This function verifies the signature on a certificate using a
|
|
certificate manager. It checks that the certificate is properly
|
|
signed by a trusted CA.
|
|
|
|
\return 0 On successful signature verification.
|
|
\return ASN_SIG_CONFIRM_E If signature verification fails.
|
|
\return Other negative values on error.
|
|
|
|
\param cert pointer to the DER encoded certificate
|
|
\param certSz size of the certificate in bytes
|
|
\param heap pointer to heap hint for memory allocation (can be NULL)
|
|
\param cm pointer to certificate manager containing trusted CAs
|
|
|
|
_Example_
|
|
\code
|
|
byte cert[2048] = { }; // DER encoded certificate
|
|
word32 certSz = sizeof(cert);
|
|
WOLFSSL_CERT_MANAGER* cm;
|
|
|
|
cm = wolfSSL_CertManagerNew();
|
|
wolfSSL_CertManagerLoadCA(cm, "ca-cert.pem", NULL);
|
|
|
|
int ret = wc_CheckCertSignature(cert, certSz, NULL, cm);
|
|
if (ret == 0) {
|
|
// certificate signature is valid
|
|
}
|
|
wolfSSL_CertManagerFree(cm);
|
|
\endcode
|
|
|
|
\sa wolfSSL_CertManagerNew
|
|
\sa wolfSSL_CertManagerLoadCA
|
|
*/
|
|
int wc_CheckCertSignature(const byte* cert, word32 certSz, void* heap,
|
|
void* cm);
|
|
|
|
/*!
|
|
\ingroup ASN
|
|
\brief This function encodes an array of word16 values into an ASN.1
|
|
Object Identifier (OID) in DER format. OIDs are used to identify
|
|
algorithms, extensions, and other objects in certificates and
|
|
cryptographic protocols.
|
|
|
|
\return 0 On success.
|
|
\return BAD_FUNC_ARG If in, inSz, or outSz are invalid.
|
|
\return BUFFER_E If out is not NULL and outSz is too small.
|
|
|
|
\param in pointer to array of word16 values representing OID components
|
|
\param inSz number of components in the OID
|
|
\param out pointer to buffer to store encoded OID (can be NULL to
|
|
calculate size)
|
|
\param outSz pointer to size of out buffer; updated with actual size
|
|
|
|
_Example_
|
|
\code
|
|
word16 oid[] = {1, 2, 840, 113549, 1, 1, 11}; // sha256WithRSAEncryption
|
|
byte encoded[32];
|
|
word32 encodedSz = sizeof(encoded);
|
|
|
|
int ret = wc_EncodeObjectId(oid, sizeof(oid)/sizeof(word16),
|
|
encoded, &encodedSz);
|
|
if (ret == 0) {
|
|
// encoded contains DER encoded OID
|
|
}
|
|
\endcode
|
|
|
|
\sa wc_BerToDer
|
|
*/
|
|
int wc_EncodeObjectId(const word16* in, word32 inSz, byte* out,
|
|
word32* outSz);
|
|
|
|
/*!
|
|
\ingroup ASN
|
|
\brief This function sets the algorithm identifier in DER format. It
|
|
encodes the algorithm OID and optional parameters based on the
|
|
algorithm type and curve size.
|
|
|
|
\return Length of the encoded algorithm identifier on success.
|
|
\return Negative value on error.
|
|
|
|
\param algoOID algorithm object identifier constant
|
|
\param output pointer to buffer to store encoded algorithm ID
|
|
\param type type of encoding (oidSigType, oidHashType, etc.)
|
|
\param curveSz size of the curve for ECC algorithms (0 for non-ECC)
|
|
|
|
_Example_
|
|
\code
|
|
byte algId[32];
|
|
word32 len;
|
|
|
|
len = SetAlgoID(CTC_SHA256wRSA, algId, oidSigType, 0);
|
|
if (len > 0) {
|
|
// algId contains encoded algorithm identifier
|
|
}
|
|
\endcode
|
|
|
|
\sa wc_EncodeObjectId
|
|
*/
|
|
word32 SetAlgoID(int algoOID, byte* output, int type, int curveSz);
|
|
|
|
/*!
|
|
\ingroup ASN
|
|
\brief This function decodes a DER encoded Diffie-Hellman public key.
|
|
It extracts the public key value from the DER encoding and stores it
|
|
in the DhKey structure.
|
|
|
|
\return 0 On success.
|
|
\return BAD_FUNC_ARG If input, inOutIdx, key, or inSz are invalid.
|
|
\return ASN_PARSE_E If the DER encoding is invalid.
|
|
\return Other negative values on error.
|
|
|
|
\param input pointer to buffer containing DER encoded public key
|
|
\param inOutIdx pointer to index in buffer; updated to end of key
|
|
\param key pointer to DhKey structure to store decoded public key
|
|
\param inSz size of the input buffer
|
|
|
|
_Example_
|
|
\code
|
|
byte derKey[256] = { }; // DER encoded DH public key
|
|
word32 idx = 0;
|
|
DhKey key;
|
|
|
|
wc_InitDhKey(&key);
|
|
int ret = wc_DhPublicKeyDecode(derKey, &idx, &key, sizeof(derKey));
|
|
if (ret == 0) {
|
|
// key now contains the decoded public key
|
|
}
|
|
wc_FreeDhKey(&key);
|
|
\endcode
|
|
|
|
\sa wc_InitDhKey
|
|
\sa wc_DhKeyDecode
|
|
*/
|
|
int wc_DhPublicKeyDecode(const byte* input, word32* inOutIdx, DhKey* key,
|
|
word32 inSz);
|
|
|
|
/*!
|
|
\ingroup CertManager
|
|
\brief Sign a certificate or CSR using a callback function.
|
|
|
|
This function signs a certificate or Certificate Signing Request (CSR)
|
|
using a user-provided signing callback. This allows external signing
|
|
implementations (e.g., TPM, HSM) without requiring the crypto callback
|
|
infrastructure, making it suitable for FIPS-compliant applications.
|
|
|
|
The function performs the following:
|
|
1. Hashes the certificate/CSR body according to the signature algorithm
|
|
2. Encodes the hash (RSA) or prepares it for signing (ECC)
|
|
3. Calls the user-provided callback to perform the actual signing
|
|
4. Encodes the signature into the certificate/CSR DER structure
|
|
|
|
NOTE: Only RSA and ECC key types are supported. Ed25519, Ed448, and
|
|
post-quantum algorithms (Falcon, Dilithium, SLH-DSA) sign messages
|
|
directly rather than hashes, so they cannot use this callback-based API.
|
|
Use wc_SignCert_ex for those algorithms.
|
|
|
|
NOTE: This function does NOT support async crypto (WOLFSSL_ASYNC_CRYPT).
|
|
The internal context is local to this function and cannot persist across
|
|
async re-entry.
|
|
|
|
\param requestSz Size of the certificate body to sign (from Cert.bodySz).
|
|
\param sType Signature algorithm type (e.g., CTC_SHA256wRSA,
|
|
CTC_SHA256wECDSA).
|
|
\param buf Buffer containing the certificate/CSR DER data to sign.
|
|
\param buffSz Total size of the buffer (must be large enough for signature).
|
|
\param keyType Type of key used for signing. Only RSA_TYPE and ECC_TYPE
|
|
are supported.
|
|
\param signCb User-provided signing callback function.
|
|
\param signCtx Context pointer passed to the signing callback.
|
|
\param rng Random number generator (may be NULL if not needed).
|
|
|
|
\return Size of the signed certificate/CSR on success.
|
|
\return BAD_FUNC_ARG if signCb or buf is NULL, buffSz is 0, or keyType
|
|
is not RSA_TYPE or ECC_TYPE.
|
|
\return BUFFER_E if the buffer is too small for the signed certificate.
|
|
\return MEMORY_E if memory allocation fails.
|
|
\return Negative error code on other failures.
|
|
|
|
_Example_
|
|
\code
|
|
Cert cert;
|
|
byte derBuf[4096];
|
|
int derSz;
|
|
MySignCtx myCtx;
|
|
|
|
wc_InitCert(&cert);
|
|
|
|
derSz = wc_MakeCert(&cert, derBuf, sizeof(derBuf), NULL, NULL, &rng);
|
|
|
|
derSz = wc_SignCert_cb(cert.bodySz, cert.sigType, derBuf, sizeof(derBuf),
|
|
RSA_TYPE, mySignCallback, &myCtx, &rng);
|
|
if (derSz > 0) {
|
|
printf("Signed certificate is %d bytes\n", derSz);
|
|
}
|
|
\endcode
|
|
|
|
\sa wc_SignCertCb
|
|
\sa wc_SignCert
|
|
\sa wc_SignCert_ex
|
|
\sa wc_MakeCert
|
|
\sa wc_MakeCertReq
|
|
*/
|
|
int wc_SignCert_cb(int requestSz, int sType, byte* buf, word32 buffSz,
|
|
int keyType, wc_SignCertCb signCb, void* signCtx,
|
|
WC_RNG* rng);
|