From 1f260ccb0a6b9101455b8196c9a498f115b32e03 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Mon, 27 Apr 2026 17:15:06 -0600 Subject: [PATCH 1/7] Add TLS-ALPN-01 challenge cert support (RFC 8737 acmeId extension) --- tests/api.c | 85 ++++++++++++++++++++++++++ wolfcrypt/src/asn.c | 108 +++++++++++++++++++++++++++++++++ wolfssl/wolfcrypt/asn.h | 8 ++- wolfssl/wolfcrypt/asn_public.h | 27 +++++++++ wolfssl/wolfcrypt/oid_sum.h | 12 +++- 5 files changed, 237 insertions(+), 3 deletions(-) diff --git a/tests/api.c b/tests/api.c index 05a7688d7f..840562d32b 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..6ecf6a05d5 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; @@ -26510,6 +26551,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 +26615,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 +26675,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 +26934,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 +29387,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/wolfssl/wolfcrypt/asn.h b/wolfssl/wolfcrypt/asn.h index b3028c9c99..d4ede74abd 100644 --- a/wolfssl/wolfcrypt/asn.h +++ b/wolfssl/wolfcrypt/asn.h @@ -2107,7 +2107,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..c32bac563a 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..5e83664f7d 100644 --- a/wolfssl/wolfcrypt/oid_sum.h +++ b/wolfssl/wolfcrypt/oid_sum.h @@ -431,7 +431,11 @@ enum Extensions_Sum { /* 0x55,0x1d,0x49 */ ALT_SIG_ALG_OID = 187, /* 2.5.29.73 */ /* 0x55,0x1d,0x4a */ - ALT_SIG_VAL_OID = 188 /* 2.5.29.74 */ + ALT_SIG_VAL_OID = 188, /* 2.5.29.74 */ +#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 #else /* 0x55,0x1d,0x13 */ BASIC_CA_OID = 0x7fec1daa, /* 2.5.29.19 */ @@ -488,7 +492,11 @@ enum Extensions_Sum { /* 0x55,0x1d,0x49 */ ALT_SIG_ALG_OID = 0x7fb61daa, /* 2.5.29.73 */ /* 0x55,0x1d,0x4a */ - ALT_SIG_VAL_OID = 0x7fb51daa /* 2.5.29.74 */ + ALT_SIG_VAL_OID = 0x7fb51daa, /* 2.5.29.74 */ +#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 #endif }; From 4791d8c26d49419dd9c330415d2c3e7f4a586736 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Tue, 28 Apr 2026 07:05:26 -0600 Subject: [PATCH 2/7] Add --enable-tailscale to autotools --- configure.ac | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/configure.ac b/configure.ac index 21ba1304a7..186c23c457 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,31 @@ 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_compkey=yes + 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)])], From c4400a15fb76bb5d56dc94161326d2aadffb143e Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Tue, 28 Apr 2026 08:10:10 -0600 Subject: [PATCH 3/7] Address copilot feedback --- configure.ac | 2 +- wolfssl/wolfcrypt/oid_sum.h | 20 ++++++++++---------- wolfssl/wolfcrypt/settings.h | 5 +++++ 3 files changed, 16 insertions(+), 11 deletions(-) diff --git a/configure.ac b/configure.ac index 186c23c457..1972989f5b 100644 --- a/configure.ac +++ b/configure.ac @@ -1653,7 +1653,7 @@ fi AC_ARG_ENABLE([tailscale], [AS_HELP_STRING([--enable-tailscale],[Enable Tailscale build dependencies (default: disabled)])], [ ENABLED_TAILSCALE=$enableval ], - [ ENABLED_TAILSCALE=no ] + [ ENABLED_TAILSCALE=${enable_tailscale:-no} ] ) if test "$ENABLED_TAILSCALE" = "yes" then diff --git a/wolfssl/wolfcrypt/oid_sum.h b/wolfssl/wolfcrypt/oid_sum.h index 5e83664f7d..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 */ @@ -431,11 +435,7 @@ enum Extensions_Sum { /* 0x55,0x1d,0x49 */ ALT_SIG_ALG_OID = 187, /* 2.5.29.73 */ /* 0x55,0x1d,0x4a */ - ALT_SIG_VAL_OID = 188, /* 2.5.29.74 */ -#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 + ALT_SIG_VAL_OID = 188 /* 2.5.29.74 */ #else /* 0x55,0x1d,0x13 */ BASIC_CA_OID = 0x7fec1daa, /* 2.5.29.19 */ @@ -473,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 */ @@ -492,11 +496,7 @@ enum Extensions_Sum { /* 0x55,0x1d,0x49 */ ALT_SIG_ALG_OID = 0x7fb61daa, /* 2.5.29.73 */ /* 0x55,0x1d,0x4a */ - ALT_SIG_VAL_OID = 0x7fb51daa, /* 2.5.29.74 */ -#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 + ALT_SIG_VAL_OID = 0x7fb51daa /* 2.5.29.74 */ #endif }; 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) From 82b15efebc7cba91ebee32ab514dfe210a8d6103 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Tue, 28 Apr 2026 11:51:40 -0600 Subject: [PATCH 4/7] Add acmeIdentifier to asn=original --- wolfcrypt/src/asn.c | 6 +++++ wolfcrypt/src/asn_orig.c | 52 ++++++++++++++++++++++++++++++++++++++++ wolfssl/wolfcrypt/asn.h | 4 ++++ 3 files changed, 62 insertions(+) diff --git a/wolfcrypt/src/asn.c b/wolfcrypt/src/asn.c index 6ecf6a05d5..a83d651022 100644 --- a/wolfcrypt/src/asn.c +++ b/wolfcrypt/src/asn.c @@ -25291,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 */ @@ -25321,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 */ 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 d4ede74abd..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 */ From 2a0a5cc6108767ba78fcba33ed0472d277345971 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Tue, 28 Apr 2026 13:25:14 -0600 Subject: [PATCH 5/7] Multi-test fixes --- .wolfssl_known_macro_extras | 1 - tests/api.c | 2 +- wolfssl/wolfcrypt/asn_public.h | 2 +- 3 files changed, 2 insertions(+), 3 deletions(-) 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/tests/api.c b/tests/api.c index 840562d32b..2dba31d135 100644 --- a/tests/api.c +++ b/tests/api.c @@ -11627,7 +11627,7 @@ static int test_wolfSSL_mcast(void) *----------------------------------------------------------------------------*/ /* - * Testing wc_SetAcmeIdentifierExt() round-trip — the RFC 8737 + * 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. */ diff --git a/wolfssl/wolfcrypt/asn_public.h b/wolfssl/wolfcrypt/asn_public.h index c32bac563a..15359ae835 100644 --- a/wolfssl/wolfcrypt/asn_public.h +++ b/wolfssl/wolfcrypt/asn_public.h @@ -635,7 +635,7 @@ WOLFSSL_API int wc_SetExtKeyUsage(Cert *cert, const char *value); * certificates. * * keyAuth is the ACME key authorization string (token "." JWK_thumbprint - * per RFC 8555 8.1), as raw bytes — keyAuthSz is its byte length. + * 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. From 7f7d28372b1653db75211cb6da4e46b45d390043 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Wed, 29 Apr 2026 09:53:03 -0600 Subject: [PATCH 6/7] Configure.ac cleanup for demented AI logic --- configure.ac | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 1972989f5b..186c23c457 100644 --- a/configure.ac +++ b/configure.ac @@ -1653,7 +1653,7 @@ fi AC_ARG_ENABLE([tailscale], [AS_HELP_STRING([--enable-tailscale],[Enable Tailscale build dependencies (default: disabled)])], [ ENABLED_TAILSCALE=$enableval ], - [ ENABLED_TAILSCALE=${enable_tailscale:-no} ] + [ ENABLED_TAILSCALE=no ] ) if test "$ENABLED_TAILSCALE" = "yes" then From e7297493d4a0dcc2aeffc63c9e04640d5c54fa27 Mon Sep 17 00:00:00 2001 From: Lealem Amedie Date: Wed, 29 Apr 2026 11:09:33 -0600 Subject: [PATCH 7/7] --enable-compkey allowed with v5 --- configure.ac | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/configure.ac b/configure.ac index 186c23c457..d324fb30d3 100644 --- a/configure.ac +++ b/configure.ac @@ -1659,7 +1659,6 @@ if test "$ENABLED_TAILSCALE" = "yes" then enable_wolfguard=yes test "x$enable_sp" = "x" && enable_sp="yes,256" - enable_compkey=yes enable_opensslall=yes enable_alpn=yes enable_sni=yes @@ -1687,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 @@ -4938,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 @@ -6836,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"])