diff --git a/.wolfssl_known_macro_extras b/.wolfssl_known_macro_extras index c676c15524..3bb23b805f 100644 --- a/.wolfssl_known_macro_extras +++ b/.wolfssl_known_macro_extras @@ -833,7 +833,6 @@ WOLFSSL_NO_DH186 WOLFSSL_NO_DTLS_SIZE_CHECK WOLFSSL_NO_ETM_ALERT WOLFSSL_NO_FENCE -WOLFSSL_NO_INIT_CTX_KEY WOLFSSL_NO_ISSUERHASH_TDPEER WOLFSSL_NO_KCAPI_AES_CBC WOLFSSL_NO_KCAPI_HMAC_SHA1 diff --git a/configure.ac b/configure.ac index 21ba1304a7..d324fb30d3 100644 --- a/configure.ac +++ b/configure.ac @@ -1298,6 +1298,7 @@ then AC_MSG_ERROR([--enable-all-osp is incompatible with --enable-linuxkm-defaults]) fi + test "$enable_tailscale" = "" && enable_tailscale=yes test "$enable_wolfguard" = "" && enable_wolfguard=yes test "$enable_webserver" = "" && enable_webserver=yes @@ -1648,6 +1649,30 @@ then AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT" fi +# Support for Tailscale port +AC_ARG_ENABLE([tailscale], + [AS_HELP_STRING([--enable-tailscale],[Enable Tailscale build dependencies (default: disabled)])], + [ ENABLED_TAILSCALE=$enableval ], + [ ENABLED_TAILSCALE=no ] + ) +if test "$ENABLED_TAILSCALE" = "yes" +then + enable_wolfguard=yes + test "x$enable_sp" = "x" && enable_sp="yes,256" + enable_opensslall=yes + enable_alpn=yes + enable_sni=yes + enable_certgen=yes + enable_certreq=yes + enable_certext=yes + enable_sessioncerts=yes + enable_cert_setup_cb=yes + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_PUBLIC_MP" + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_INIT_CTX_KEY" + AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ALWAYS_KEEP_SNI" + AM_CFLAGS="$AM_CFLAGS -DWC_CTC_NAME_SIZE=128 -DWOLFSSL_ACME_OID" +fi + # wolfGuard AC_ARG_ENABLE([wolfguard], [AS_HELP_STRING([--enable-wolfguard],[Enable wolfGuard dependencies (default: disabled)])], @@ -1661,9 +1686,9 @@ then test "$enable_aesgcm" = "" && enable_aesgcm=yes test "$enable_base64encode" = "" && enable_base64encode=yes test "$enable_base16" = "" && enable_base16=yes + test "$enable_compkey" = "" && enable_compkey=yes if test "$ENABLED_FIPS" = "no" || test "$HAVE_FIPS_VERSION" -ge 6 then - test "$enable_compkey" = "" && enable_compkey=yes test "$enable_aesgcm_stream" = "" && enable_aesgcm_stream=yes fi fi @@ -4912,8 +4937,7 @@ AC_ARG_ENABLE([compkey], [ ENABLED_COMPKEY=no ] ) -if (test "$ENABLED_WPAS" = "yes" || test "$ENABLED_OPENSSLALL" = "yes") && - (test "$HAVE_FIPS_VERSION" != "5") +if (test "$ENABLED_WPAS" = "yes" || test "$ENABLED_OPENSSLALL" = "yes") then ENABLED_COMPKEY=yes fi @@ -6810,11 +6834,6 @@ AS_CASE([$FIPS_VERSION], (test "$FIPS_VERSION" != "v5-dev" || test "$enable_keygen" != "no")], [ENABLED_KEYGEN="yes"; AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_KEY_GEN"]) - AS_IF([test "$ENABLED_COMPKEY" = "yes" && - ! (test "$FIPS_VERSION" = "v5-dev" && test "$enable_compkey" = "yes")], - [AC_MSG_WARN([Forcing off compkey for FIPS ${FIPS_VERSION}.]) - ENABLED_COMPKEY="no"]) - AS_IF([test "$ENABLED_SHA224" != "yes" && (test "$FIPS_VERSION" != "v5-dev" || test "$enable_sha224" != "no")], [ENABLED_SHA224="yes"; AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SHA224"]) diff --git a/tests/api.c b/tests/api.c index 05a7688d7f..2dba31d135 100644 --- a/tests/api.c +++ b/tests/api.c @@ -11626,6 +11626,90 @@ static int test_wolfSSL_mcast(void) | Wolfcrypt *----------------------------------------------------------------------------*/ +/* + * Testing wc_SetAcmeIdentifierExt() round-trip - the RFC 8737 + * id-pe-acmeIdentifier (1.3.6.1.5.5.7.1.31) extension used by + * TLS-ALPN-01 ACME challenge certs. + */ +static int test_wc_SetAcmeIdentifierExt(void) +{ + EXPECT_DECLS; +#if defined(WOLFSSL_ACME_OID) && defined(WOLFSSL_CERT_GEN) && \ + defined(HAVE_ECC) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \ + !defined(WC_NO_RNG) && !defined(NO_RSA) + Cert cert; + DecodedCert decoded; + WC_RNG rng; + ecc_key key; + byte der[TWOK_BUF]; + int derSz = 0; + int rngInited = 0, keyInited = 0; + const char* keyAuth = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA" + ".kZdq0qaDcXNVxKBkP_uiKvw2Yg5sRJ8KBfQa9Ru13nE"; + word32 keyAuthSz = (word32)XSTRLEN(keyAuth); + byte expected[WC_SHA256_DIGEST_SIZE]; + + XMEMSET(&cert, 0, sizeof(cert)); + XMEMSET(&decoded, 0, sizeof(decoded)); + XMEMSET(&rng, 0, sizeof(rng)); + XMEMSET(&key, 0, sizeof(key)); + XMEMSET(der, 0, sizeof(der)); + + /* Compute the expected digest */ + ExpectIntEQ(wc_Sha256Hash((const byte*)keyAuth, keyAuthSz, expected), 0); + + /* Input validation. */ + ExpectIntEQ(wc_SetAcmeIdentifierExt(NULL, (const byte*)keyAuth, keyAuthSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_SetAcmeIdentifierExt(&cert, NULL, keyAuthSz), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + ExpectIntEQ(wc_SetAcmeIdentifierExt(&cert, (const byte*)keyAuth, 0), + WC_NO_ERR_TRACE(BAD_FUNC_ARG)); + + /* Build a P-256 keypair to sign the cert. */ + ExpectIntEQ(wc_InitRng(&rng), 0); + rngInited = 1; + ExpectIntEQ(wc_ecc_init(&key), 0); + keyInited = 1; + ExpectIntEQ(wc_ecc_make_key_ex(&rng, KEY32, &key, ECC_SECP256R1), 0); + + /* Build a minimal self-signed cert template carrying the extension. */ + ExpectIntEQ(wc_InitCert(&cert), 0); + cert.sigType = CTC_SHA256wECDSA; + cert.daysValid = 1; + cert.isCA = 0; + XSTRNCPY(cert.subject.commonName, "acme-test.example", CTC_NAME_SIZE); + + ExpectIntEQ(wc_SetAcmeIdentifierExt(&cert, (const byte*)keyAuth, + keyAuthSz), 0); + ExpectIntEQ(cert.acmeIdentifierSz, WC_SHA256_DIGEST_SIZE); + ExpectIntEQ(XMEMCMP(cert.acmeIdentifier, expected, + WC_SHA256_DIGEST_SIZE), 0); + + /* MakeCert + SignCert. ECC_TYPE selects the ECDSA signing path. */ + ExpectIntGT(derSz = wc_MakeCert_ex(&cert, der, sizeof(der), + ECC_TYPE, &key, &rng), 0); + ExpectIntGT(derSz = wc_SignCert_ex(cert.bodySz, cert.sigType, + der, sizeof(der), + ECC_TYPE, &key, &rng), 0); + + /* Parse the cert back and verify the extension survives the + * round-trip via DecodeAcmeId. */ + wc_InitDecodedCert(&decoded, der, derSz, NULL); + ExpectIntEQ(wc_ParseCert(&decoded, CERT_TYPE, NO_VERIFY, NULL), 0); + ExpectIntEQ(decoded.extAcmeIdentifierSet, 1); + ExpectIntEQ(decoded.extAcmeIdentifierCrit, 1); + ExpectIntEQ(decoded.acmeIdentifierSz, WC_SHA256_DIGEST_SIZE); + ExpectIntEQ(XMEMCMP(decoded.acmeIdentifier, expected, + WC_SHA256_DIGEST_SIZE), 0); + + wc_FreeDecodedCert(&decoded); + if (keyInited) wc_ecc_free(&key); + if (rngInited) wc_FreeRng(&rng); +#endif + return EXPECT_RESULT(); +} /* END test_wc_SetAcmeIdentifierExt */ + /* * Testing wc_SetKeyUsage() */ @@ -37017,6 +37101,7 @@ TEST_CASE testCases[] = { #ifdef WOLFSSL_CERT_SIGN_CB TEST_DECL(test_wc_SignCert_cb), #endif + TEST_DECL(test_wc_SetAcmeIdentifierExt), TEST_DECL(test_wc_SetKeyUsage), TEST_DECL(test_wc_SetAuthKeyIdFromPublicKey_ex), TEST_DECL(test_wc_SetSubjectBuffer), diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 9a3be56616..a83d651022 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -19014,6 +19014,39 @@ static int DecodeKeyUsageInternal(const byte* input, word32 sz, return DecodeKeyUsage(input, sz, &cert->extKeyUsage); } +#ifdef WOLFSSL_ACME_OID +/* Decodes the RFC 8737 id-pe-acmeIdentifier (1.3.6.1.5.5.7.1.31) + * extension value into cert->acmeIdentifier. + * + * The extnValue is an OCTET STRING wrapping a SHA-256 digest of the + * ACME keyAuth, per RFC 8737 3. Length is verified against + * WC_SHA256_DIGEST_SIZE. + * + * @param [in] input ASN.1 DER-encoded extension value. + * @param [in] sz Length of input in bytes. + * @param [in, out] cert DecodedCert to populate (acmeIdentifier and + * acmeIdentifierSz fields). + * + * @return 0 on success. + * @return ASN_PARSE_E when the inner OCTET STRING is missing or not + * exactly WC_SHA256_DIGEST_SIZE bytes. + */ +static int DecodeAcmeId(const byte* input, word32 sz, DecodedCert* cert) +{ + word32 hashIdx = 0; + int hashLen = 0; + + if (GetOctetString(input, &hashIdx, &hashLen, sz) < 0) + return ASN_PARSE_E; + if (hashLen != WC_SHA256_DIGEST_SIZE) + return ASN_PARSE_E; + + XMEMCPY(cert->acmeIdentifier, &input[hashIdx], WC_SHA256_DIGEST_SIZE); + cert->acmeIdentifierSz = WC_SHA256_DIGEST_SIZE; + return 0; +} +#endif /* WOLFSSL_ACME_OID */ + #ifdef WOLFSSL_ASN_TEMPLATE /* ASN.1 template for KeyPurposeId. * X.509: RFC 5280, 4.2.1.12 - Extended Key Usage. @@ -20236,6 +20269,14 @@ int DecodeExtensionType(const byte* input, word32 length, word32 oid, return ASN_PARSE_E; break; #endif /* WOLFSSL_DUAL_ALG_CERTS */ + #ifdef WOLFSSL_ACME_OID + case ACME_IDENTIFIER_OID: + VERIFY_AND_SET_OID(cert->extAcmeIdentifierSet); + cert->extAcmeIdentifierCrit = critical ? 1 : 0; + if (DecodeAcmeId(&input[idx], length, cert) < 0) + return ASN_PARSE_E; + break; + #endif default: if (isUnknownExt != NULL) *isUnknownExt = 1; @@ -25250,6 +25291,9 @@ typedef struct DerCert { #endif byte certPolicies[MAX_CERTPOL_NB*MAX_CERTPOL_SZ]; /* Certificate Policies */ byte crlInfo[CTC_MAX_CRLINFO_SZ]; /* CRL Distribution Points */ +#ifdef WOLFSSL_ACME_OID + byte acmeId[MAX_ACMEID_SZ]; /* RFC 8737 id-pe-acmeIdentifier */ +#endif #endif #ifdef WOLFSSL_CERT_REQ byte attrib[MAX_ATTRIB_SZ]; /* Cert req attributes encoded */ @@ -25280,6 +25324,9 @@ typedef struct DerCert { #endif int certPoliciesSz; /* encoded CertPolicies extension length*/ int crlInfoSz; /* encoded CRL Dist Points length */ +#ifdef WOLFSSL_ACME_OID + int acmeIdSz; /* encoded acmeIdentifier length */ +#endif #endif #ifdef WOLFSSL_ALT_NAMES int altNamesSz; /* encoded AltNames extension length */ @@ -26510,6 +26557,12 @@ static const ASNItem static_certExtsASN[] = { /* CRLINFO_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, /* CRLINFO_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 }, /* CRLINFO_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 }, + /* RFC 8737 id-pe-acmeIdentifier */ +/* ACMEID_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, +/* ACMEID_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 }, +/* ACMEID_CRIT */ { 1, ASN_BOOLEAN, 0, 0, 0 }, +/* ACMEID_STR */ { 1, ASN_OCTET_STRING, 0, 1, 0 }, +/* ACMEID_HASH */ { 2, ASN_OCTET_STRING, 0, 0, 0 }, #ifdef WOLFSSL_DUAL_ALG_CERTS /* SAPKI_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 }, /* SAPKI_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 }, @@ -26568,6 +26621,11 @@ enum { CERTEXTSASN_IDX_CRLINFO_SEQ, CERTEXTSASN_IDX_CRLINFO_OID, CERTEXTSASN_IDX_CRLINFO_STR, + CERTEXTSASN_IDX_ACMEID_SEQ, + CERTEXTSASN_IDX_ACMEID_OID, + CERTEXTSASN_IDX_ACMEID_CRIT, + CERTEXTSASN_IDX_ACMEID_STR, + CERTEXTSASN_IDX_ACMEID_HASH, #ifdef WOLFSSL_DUAL_ALG_CERTS CERTEXTSASN_IDX_SAPKI_SEQ, CERTEXTSASN_IDX_SAPKI_OID, @@ -26623,6 +26681,10 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, static const byte nsCertOID[] = { 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x42, 0x01, 0x01 }; static const byte crlInfoOID[] = { 0x55, 0x1D, 0x1F }; +#ifdef WOLFSSL_ACME_OID + static const byte acmeIdOID[] = { 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x1F }; +#endif #ifdef WOLFSSL_DUAL_ALG_CERTS static const byte sapkiOID[] = { 0x55, 0x1d, 0x48 }; static const byte altSigAlgOID[] = { 0x55, 0x1d, 0x49 }; @@ -26878,6 +26940,24 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz, CERTEXTSASN_IDX_CRLINFO_STR); } + #ifdef WOLFSSL_ACME_OID + /* id-pe-acmeIdentifier (TLS-ALPN-01 challenge cert). + * Always critical=TRUE. */ + if (cert->acmeIdentifierSz == WC_SHA256_DIGEST_SIZE) { + SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ACMEID_OID], + acmeIdOID, sizeof(acmeIdOID)); + SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_ACMEID_CRIT], 1); + SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ACMEID_HASH], + cert->acmeIdentifier, (word32)cert->acmeIdentifierSz); + } + else + #endif /* WOLFSSL_ACME_OID */ + { + /* Don't write out the ACME identifier extension. */ + SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_ACMEID_SEQ, + CERTEXTSASN_IDX_ACMEID_HASH); + } + #ifdef WOLFSSL_DUAL_ALG_CERTS if (cert->sapkiDer != NULL) { /* Set subject alternative public key info OID, criticality and @@ -29313,6 +29393,40 @@ int wc_SetExtKeyUsage(Cert *cert, const char *value) return ret; } +#ifdef WOLFSSL_ACME_OID +/* Set the id-pe-acmeIdentifier extension value from the ACME + * keyAuth string. Computes SHA-256 over keyAuth and stores the digest + * as the extension value. RFC 8737 3 requires critical=TRUE; that's + * applied at encode time in EncodeExtensions. + * + * keyAuth is the raw bytes of the key authorization string per + * RFC 8555 8.1: token "." JWK_thumbprint. + */ +int wc_SetAcmeIdentifierExt(Cert *cert, const byte *keyAuth, word32 keyAuthSz) +{ + int ret; + byte digest[WC_SHA256_DIGEST_SIZE]; + wc_Sha256 sha; + + if (cert == NULL || keyAuth == NULL || keyAuthSz == 0) + return BAD_FUNC_ARG; + + ret = wc_InitSha256(&sha); + if (ret != 0) + return ret; + ret = wc_Sha256Update(&sha, keyAuth, keyAuthSz); + if (ret == 0) + ret = wc_Sha256Final(&sha, digest); + wc_Sha256Free(&sha); + if (ret != 0) + return ret; + + XMEMCPY(cert->acmeIdentifier, digest, WC_SHA256_DIGEST_SIZE); + cert->acmeIdentifierSz = WC_SHA256_DIGEST_SIZE; + return 0; +} +#endif /* WOLFSSL_ACME_OID */ + #ifdef WOLFSSL_EKU_OID /* * cert structure to set EKU oid in diff --git a/wolfcrypt/src/asn_orig.c b/wolfcrypt/src/asn_orig.c index d6568aa5d1..b39417c60e 100644 --- a/wolfcrypt/src/asn_orig.c +++ b/wolfcrypt/src/asn_orig.c @@ -5295,6 +5295,31 @@ static int SetAKID(byte* output, word32 outSz, byte *input, word32 length, return (int)idx + enc_valSz; } +#ifdef WOLFSSL_ACME_OID +/* encode RFC 8737 id-pe-acmeIdentifier extension, return total bytes written + * RFC8737 : critical */ +static int SetAcmeIdentifier(byte* output, word32 outSz, const byte* digest, + word32 digestSz) +{ + byte inner[1 + MAX_LENGTH_SZ + WC_SHA256_DIGEST_SIZE]; + word32 innerSz; + const byte acmeId_oid[] = { 0x06, 0x08, 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07, + 0x01, 0x1F, 0x01, 0x01, 0xFF, 0x04 }; + + if (output == NULL || digest == NULL) + return BAD_FUNC_ARG; + if (digestSz != WC_SHA256_DIGEST_SIZE) + return BAD_FUNC_ARG; + + innerSz = SetOctetString(digestSz, inner); + XMEMCPY(inner + innerSz, digest, digestSz); + innerSz += digestSz; + + return SetOidValue(output, outSz, acmeId_oid, sizeof(acmeId_oid), + inner, innerSz); +} +#endif /* WOLFSSL_ACME_OID */ + /* encode Key Usage, return total bytes written * RFC5280 : critical */ static int SetKeyUsage(byte* output, word32 outSz, word16 input) @@ -6340,6 +6365,22 @@ static int EncodeCert(Cert* cert, DerCert* der, RsaKey* rsaKey, ecc_key* eccKey, der->certPoliciesSz = 0; #endif /* WOLFSSL_CERT_EXT */ +#ifdef WOLFSSL_ACME_OID + /* RFC 8737 id-pe-acmeIdentifier (TLS-ALPN-01 challenge cert). + * Always critical=TRUE. */ + if (cert->acmeIdentifierSz == WC_SHA256_DIGEST_SIZE) { + der->acmeIdSz = SetAcmeIdentifier(der->acmeId, sizeof(der->acmeId), + cert->acmeIdentifier, + (word32)cert->acmeIdentifierSz); + if (der->acmeIdSz <= 0) + return EXTENSIONS_E; + + der->extensionsSz += der->acmeIdSz; + } + else + der->acmeIdSz = 0; +#endif + /* put extensions */ if (der->extensionsSz > 0) { @@ -6436,6 +6477,17 @@ static int EncodeCert(Cert* cert, DerCert* der, RsaKey* rsaKey, ecc_key* eccKey, return EXTENSIONS_E; } #endif /* WOLFSSL_CERT_EXT */ + +#ifdef WOLFSSL_ACME_OID + /* put ACME Identifier */ + if (der->acmeIdSz) { + ret = SetExtensions(der->extensions, sizeof(der->extensions), + &der->extensionsSz, + der->acmeId, der->acmeIdSz); + if (ret <= 0) + return EXTENSIONS_E; + } +#endif } der->total = der->versionSz + der->serialSz + der->sigAlgoSz + diff --git a/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index b3028c9c99..abdfa953f9 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -1310,6 +1310,10 @@ enum Misc_ASN { #endif MAX_CERTPOL_NB = CTC_MAX_CERTPOL_NB,/* Max number of Cert Policy */ MAX_CERTPOL_SZ = CTC_MAX_CERTPOL_SZ, +#endif +#ifdef WOLFSSL_ACME_OID + MAX_ACMEID_SZ = 19 + WC_SHA256_DIGEST_SIZE, /* Max encoded + acmeIdentifier size */ #endif OCSP_NONCE_EXT_SZ = 35, /* OCSP Nonce Extension size */ MAX_OCSP_EXT_SZ = 58, /* Max OCSP Extension length */ @@ -2107,7 +2111,13 @@ struct DecodedCert { WC_BITFIELD extAltSigAlgCrit:1; WC_BITFIELD extAltSigValCrit:1; #endif /* WOLFSSL_DUAL_ALG_CERTS */ - +#ifdef WOLFSSL_ACME_OID + /* id-pe-acmeIdentifier (TLS-ALPN-01 challenge cert) */ + byte acmeIdentifier[WC_SHA256_DIGEST_SIZE]; + int acmeIdentifierSz; + WC_BITFIELD extAcmeIdentifierSet:1; + WC_BITFIELD extAcmeIdentifierCrit:1; +#endif /* WOLFSSL_ACME_OID */ WOLFSSL_AIA_ENTRY extAuthInfoList[WOLFSSL_MAX_AIA_ENTRIES]; WC_BITFIELD extAuthInfoListSz:7; WC_BITFIELD extAuthInfoListOverflow:1; diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index 3d4a78b598..15359ae835 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -36,6 +36,9 @@ This library defines the interface APIs for X509 certificates. #include #endif #include +#ifdef WOLFSSL_ACME_OID + #include +#endif #ifdef __cplusplus extern "C" { @@ -469,6 +472,10 @@ typedef struct Cert { word16 certPoliciesNb; /* Number of Cert Policy */ byte crlInfo[CTC_MAX_CRLINFO_SZ]; /* CRL Distribution points */ int crlInfoSz; +#ifdef WOLFSSL_ACME_OID + byte acmeIdentifier[WC_SHA256_DIGEST_SIZE]; /* SHA256 of ACME keyAuth */ + int acmeIdentifierSz; +#endif #endif #if defined(WOLFSSL_CERT_EXT) || defined(OPENSSL_EXTRA) || \ defined(WOLFSSL_CERT_REQ) @@ -622,6 +629,26 @@ WOLFSSL_API int wc_SetKeyUsage(Cert *cert, const char *value); */ WOLFSSL_API int wc_SetExtKeyUsage(Cert *cert, const char *value); +#ifdef WOLFSSL_ACME_OID +/* Set the RFC 8737 id-pe-acmeIdentifier (1.3.6.1.5.5.7.1.31) extension on + * the certificate. Used to construct TLS-ALPN-01 ACME challenge response + * certificates. + * + * keyAuth is the ACME key authorization string (token "." JWK_thumbprint + * per RFC 8555 8.1), as raw bytes - keyAuthSz is its byte length. + * wc_SetAcmeIdentifierExt computes SHA-256 over keyAuth internally and + * stores the digest as the extension value, emitted critical=TRUE per + * RFC 8737 3. + * + * Returns 0 on success. + * Returns BAD_FUNC_ARG when cert is NULL, keyAuth is NULL, or + * keyAuthSz is 0. + * May return a SHA-256 error code if hashing fails. */ +WOLFSSL_API int wc_SetAcmeIdentifierExt(Cert *cert, + const byte *keyAuth, + word32 keyAuthSz); +#endif /* WOLFSSL_ACME_OID */ + #ifdef WOLFSSL_EKU_OID /* Set ExtendedKeyUsage with unique OID diff --git a/wolfssl/wolfcrypt/oid_sum.h b/wolfssl/wolfcrypt/oid_sum.h index e1fbee5f44..a85b6fb7c5 100644 --- a/wolfssl/wolfcrypt/oid_sum.h +++ b/wolfssl/wolfcrypt/oid_sum.h @@ -412,6 +412,10 @@ enum Extensions_Sum { ISSUE_ALT_NAMES_OID = 132, /* 2.5.29.18 */ /* 0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x18 */ TLS_FEATURE_OID = 92, /* 1.3.6.1.5.5.7.1.24 */ +#ifdef WOLFSSL_ACME_OID + /* 0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x1f */ + ACME_IDENTIFIER_OID = 99, /* 1.3.6.1.5.5.7.1.31 */ +#endif /* 0x2b,0x06,0x01,0x05,0x05,0x07,0x08,0x07 */ DNS_SRV_OID = 82, /* 1.3.6.1.5.5.7.8.7 */ /* 0x60,0x86,0x48,0x01,0x86,0xf8,0x42,0x01,0x01 */ @@ -469,6 +473,10 @@ enum Extensions_Sum { ISSUE_ALT_NAMES_OID = 0x7fed1daa, /* 2.5.29.18 */ /* 0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x18 */ TLS_FEATURE_OID = 0x1d00012e, /* 1.3.6.1.5.5.7.1.24 */ +#ifdef WOLFSSL_ACME_OID + /* 0x2b,0x06,0x01,0x05,0x05,0x07,0x01,0x1f */ + ACME_IDENTIFIER_OID = 0x1a00012e, /* 1.3.6.1.5.5.7.1.31 */ +#endif /* 0x2b,0x06,0x01,0x05,0x05,0x07,0x08,0x07 */ DNS_SRV_OID = 0x0209012e, /* 1.3.6.1.5.5.7.8.7 */ /* 0x60,0x86,0x48,0x01,0x86,0xf8,0x42,0x01,0x01 */ diff --git a/wolfssl/wolfcrypt/settings.h b/wolfssl/wolfcrypt/settings.h index 1dfc05a909..952e4645f9 100644 --- a/wolfssl/wolfcrypt/settings.h +++ b/wolfssl/wolfcrypt/settings.h @@ -558,6 +558,11 @@ #define HAVE_OID_DECODING #endif /* WOLFSSL_DUAL_ALG_CERTS */ +/* RFC 8737 id-pe-acmeIdentifier (TLS-ALPN-01) requires SHA-256. */ +#if defined(WOLFSSL_ACME_OID) && defined(NO_SHA256) + #undef WOLFSSL_ACME_OID +#endif + #if defined(_WIN32) && !defined(_M_X64) && \ defined(HAVE_AESGCM) && defined(WOLFSSL_AESNI)