Skip to content

Commit 9aec51d

Browse files
authored
Merge pull request #10334 from lealem47/acme
Add TLS-ALPN-01 challenge cert support (RFC 8737 acmeId extension)
2 parents 07ea486 + e729749 commit 9aec51d

9 files changed

Lines changed: 329 additions & 10 deletions

File tree

.wolfssl_known_macro_extras

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -833,7 +833,6 @@ WOLFSSL_NO_DH186
833833
WOLFSSL_NO_DTLS_SIZE_CHECK
834834
WOLFSSL_NO_ETM_ALERT
835835
WOLFSSL_NO_FENCE
836-
WOLFSSL_NO_INIT_CTX_KEY
837836
WOLFSSL_NO_ISSUERHASH_TDPEER
838837
WOLFSSL_NO_KCAPI_AES_CBC
839838
WOLFSSL_NO_KCAPI_HMAC_SHA1

configure.ac

Lines changed: 27 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1298,6 +1298,7 @@ then
12981298
AC_MSG_ERROR([--enable-all-osp is incompatible with --enable-linuxkm-defaults])
12991299
fi
13001300

1301+
test "$enable_tailscale" = "" && enable_tailscale=yes
13011302
test "$enable_wolfguard" = "" && enable_wolfguard=yes
13021303
test "$enable_webserver" = "" && enable_webserver=yes
13031304

@@ -1648,6 +1649,30 @@ then
16481649
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ECDSA_DETERMINISTIC_K_VARIANT"
16491650
fi
16501651

1652+
# Support for Tailscale port
1653+
AC_ARG_ENABLE([tailscale],
1654+
[AS_HELP_STRING([--enable-tailscale],[Enable Tailscale build dependencies (default: disabled)])],
1655+
[ ENABLED_TAILSCALE=$enableval ],
1656+
[ ENABLED_TAILSCALE=no ]
1657+
)
1658+
if test "$ENABLED_TAILSCALE" = "yes"
1659+
then
1660+
enable_wolfguard=yes
1661+
test "x$enable_sp" = "x" && enable_sp="yes,256"
1662+
enable_opensslall=yes
1663+
enable_alpn=yes
1664+
enable_sni=yes
1665+
enable_certgen=yes
1666+
enable_certreq=yes
1667+
enable_certext=yes
1668+
enable_sessioncerts=yes
1669+
enable_cert_setup_cb=yes
1670+
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_PUBLIC_MP"
1671+
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_NO_INIT_CTX_KEY"
1672+
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ALWAYS_KEEP_SNI"
1673+
AM_CFLAGS="$AM_CFLAGS -DWC_CTC_NAME_SIZE=128 -DWOLFSSL_ACME_OID"
1674+
fi
1675+
16511676
# wolfGuard
16521677
AC_ARG_ENABLE([wolfguard],
16531678
[AS_HELP_STRING([--enable-wolfguard],[Enable wolfGuard dependencies (default: disabled)])],
@@ -1661,9 +1686,9 @@ then
16611686
test "$enable_aesgcm" = "" && enable_aesgcm=yes
16621687
test "$enable_base64encode" = "" && enable_base64encode=yes
16631688
test "$enable_base16" = "" && enable_base16=yes
1689+
test "$enable_compkey" = "" && enable_compkey=yes
16641690
if test "$ENABLED_FIPS" = "no" || test "$HAVE_FIPS_VERSION" -ge 6
16651691
then
1666-
test "$enable_compkey" = "" && enable_compkey=yes
16671692
test "$enable_aesgcm_stream" = "" && enable_aesgcm_stream=yes
16681693
fi
16691694
fi
@@ -4912,8 +4937,7 @@ AC_ARG_ENABLE([compkey],
49124937
[ ENABLED_COMPKEY=no ]
49134938
)
49144939

4915-
if (test "$ENABLED_WPAS" = "yes" || test "$ENABLED_OPENSSLALL" = "yes") &&
4916-
(test "$HAVE_FIPS_VERSION" != "5")
4940+
if (test "$ENABLED_WPAS" = "yes" || test "$ENABLED_OPENSSLALL" = "yes")
49174941
then
49184942
ENABLED_COMPKEY=yes
49194943
fi
@@ -6810,11 +6834,6 @@ AS_CASE([$FIPS_VERSION],
68106834
(test "$FIPS_VERSION" != "v5-dev" || test "$enable_keygen" != "no")],
68116835
[ENABLED_KEYGEN="yes"; AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_KEY_GEN"])
68126836
6813-
AS_IF([test "$ENABLED_COMPKEY" = "yes" &&
6814-
! (test "$FIPS_VERSION" = "v5-dev" && test "$enable_compkey" = "yes")],
6815-
[AC_MSG_WARN([Forcing off compkey for FIPS ${FIPS_VERSION}.])
6816-
ENABLED_COMPKEY="no"])
6817-
68186837
AS_IF([test "$ENABLED_SHA224" != "yes" &&
68196838
(test "$FIPS_VERSION" != "v5-dev" || test "$enable_sha224" != "no")],
68206839
[ENABLED_SHA224="yes"; AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_SHA224"])

tests/api.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11626,6 +11626,90 @@ static int test_wolfSSL_mcast(void)
1162611626
| Wolfcrypt
1162711627
*----------------------------------------------------------------------------*/
1162811628

11629+
/*
11630+
* Testing wc_SetAcmeIdentifierExt() round-trip - the RFC 8737
11631+
* id-pe-acmeIdentifier (1.3.6.1.5.5.7.1.31) extension used by
11632+
* TLS-ALPN-01 ACME challenge certs.
11633+
*/
11634+
static int test_wc_SetAcmeIdentifierExt(void)
11635+
{
11636+
EXPECT_DECLS;
11637+
#if defined(WOLFSSL_ACME_OID) && defined(WOLFSSL_CERT_GEN) && \
11638+
defined(HAVE_ECC) && !defined(NO_SHA256) && !defined(NO_ASN_TIME) && \
11639+
!defined(WC_NO_RNG) && !defined(NO_RSA)
11640+
Cert cert;
11641+
DecodedCert decoded;
11642+
WC_RNG rng;
11643+
ecc_key key;
11644+
byte der[TWOK_BUF];
11645+
int derSz = 0;
11646+
int rngInited = 0, keyInited = 0;
11647+
const char* keyAuth = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ-PCt92wr-oA"
11648+
".kZdq0qaDcXNVxKBkP_uiKvw2Yg5sRJ8KBfQa9Ru13nE";
11649+
word32 keyAuthSz = (word32)XSTRLEN(keyAuth);
11650+
byte expected[WC_SHA256_DIGEST_SIZE];
11651+
11652+
XMEMSET(&cert, 0, sizeof(cert));
11653+
XMEMSET(&decoded, 0, sizeof(decoded));
11654+
XMEMSET(&rng, 0, sizeof(rng));
11655+
XMEMSET(&key, 0, sizeof(key));
11656+
XMEMSET(der, 0, sizeof(der));
11657+
11658+
/* Compute the expected digest */
11659+
ExpectIntEQ(wc_Sha256Hash((const byte*)keyAuth, keyAuthSz, expected), 0);
11660+
11661+
/* Input validation. */
11662+
ExpectIntEQ(wc_SetAcmeIdentifierExt(NULL, (const byte*)keyAuth, keyAuthSz),
11663+
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
11664+
ExpectIntEQ(wc_SetAcmeIdentifierExt(&cert, NULL, keyAuthSz),
11665+
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
11666+
ExpectIntEQ(wc_SetAcmeIdentifierExt(&cert, (const byte*)keyAuth, 0),
11667+
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
11668+
11669+
/* Build a P-256 keypair to sign the cert. */
11670+
ExpectIntEQ(wc_InitRng(&rng), 0);
11671+
rngInited = 1;
11672+
ExpectIntEQ(wc_ecc_init(&key), 0);
11673+
keyInited = 1;
11674+
ExpectIntEQ(wc_ecc_make_key_ex(&rng, KEY32, &key, ECC_SECP256R1), 0);
11675+
11676+
/* Build a minimal self-signed cert template carrying the extension. */
11677+
ExpectIntEQ(wc_InitCert(&cert), 0);
11678+
cert.sigType = CTC_SHA256wECDSA;
11679+
cert.daysValid = 1;
11680+
cert.isCA = 0;
11681+
XSTRNCPY(cert.subject.commonName, "acme-test.example", CTC_NAME_SIZE);
11682+
11683+
ExpectIntEQ(wc_SetAcmeIdentifierExt(&cert, (const byte*)keyAuth,
11684+
keyAuthSz), 0);
11685+
ExpectIntEQ(cert.acmeIdentifierSz, WC_SHA256_DIGEST_SIZE);
11686+
ExpectIntEQ(XMEMCMP(cert.acmeIdentifier, expected,
11687+
WC_SHA256_DIGEST_SIZE), 0);
11688+
11689+
/* MakeCert + SignCert. ECC_TYPE selects the ECDSA signing path. */
11690+
ExpectIntGT(derSz = wc_MakeCert_ex(&cert, der, sizeof(der),
11691+
ECC_TYPE, &key, &rng), 0);
11692+
ExpectIntGT(derSz = wc_SignCert_ex(cert.bodySz, cert.sigType,
11693+
der, sizeof(der),
11694+
ECC_TYPE, &key, &rng), 0);
11695+
11696+
/* Parse the cert back and verify the extension survives the
11697+
* round-trip via DecodeAcmeId. */
11698+
wc_InitDecodedCert(&decoded, der, derSz, NULL);
11699+
ExpectIntEQ(wc_ParseCert(&decoded, CERT_TYPE, NO_VERIFY, NULL), 0);
11700+
ExpectIntEQ(decoded.extAcmeIdentifierSet, 1);
11701+
ExpectIntEQ(decoded.extAcmeIdentifierCrit, 1);
11702+
ExpectIntEQ(decoded.acmeIdentifierSz, WC_SHA256_DIGEST_SIZE);
11703+
ExpectIntEQ(XMEMCMP(decoded.acmeIdentifier, expected,
11704+
WC_SHA256_DIGEST_SIZE), 0);
11705+
11706+
wc_FreeDecodedCert(&decoded);
11707+
if (keyInited) wc_ecc_free(&key);
11708+
if (rngInited) wc_FreeRng(&rng);
11709+
#endif
11710+
return EXPECT_RESULT();
11711+
} /* END test_wc_SetAcmeIdentifierExt */
11712+
1162911713
/*
1163011714
* Testing wc_SetKeyUsage()
1163111715
*/
@@ -37017,6 +37101,7 @@ TEST_CASE testCases[] = {
3701737101
#ifdef WOLFSSL_CERT_SIGN_CB
3701837102
TEST_DECL(test_wc_SignCert_cb),
3701937103
#endif
37104+
TEST_DECL(test_wc_SetAcmeIdentifierExt),
3702037105
TEST_DECL(test_wc_SetKeyUsage),
3702137106
TEST_DECL(test_wc_SetAuthKeyIdFromPublicKey_ex),
3702237107
TEST_DECL(test_wc_SetSubjectBuffer),

wolfcrypt/src/asn.c

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19014,6 +19014,39 @@ static int DecodeKeyUsageInternal(const byte* input, word32 sz,
1901419014
return DecodeKeyUsage(input, sz, &cert->extKeyUsage);
1901519015
}
1901619016

19017+
#ifdef WOLFSSL_ACME_OID
19018+
/* Decodes the RFC 8737 id-pe-acmeIdentifier (1.3.6.1.5.5.7.1.31)
19019+
* extension value into cert->acmeIdentifier.
19020+
*
19021+
* The extnValue is an OCTET STRING wrapping a SHA-256 digest of the
19022+
* ACME keyAuth, per RFC 8737 3. Length is verified against
19023+
* WC_SHA256_DIGEST_SIZE.
19024+
*
19025+
* @param [in] input ASN.1 DER-encoded extension value.
19026+
* @param [in] sz Length of input in bytes.
19027+
* @param [in, out] cert DecodedCert to populate (acmeIdentifier and
19028+
* acmeIdentifierSz fields).
19029+
*
19030+
* @return 0 on success.
19031+
* @return ASN_PARSE_E when the inner OCTET STRING is missing or not
19032+
* exactly WC_SHA256_DIGEST_SIZE bytes.
19033+
*/
19034+
static int DecodeAcmeId(const byte* input, word32 sz, DecodedCert* cert)
19035+
{
19036+
word32 hashIdx = 0;
19037+
int hashLen = 0;
19038+
19039+
if (GetOctetString(input, &hashIdx, &hashLen, sz) < 0)
19040+
return ASN_PARSE_E;
19041+
if (hashLen != WC_SHA256_DIGEST_SIZE)
19042+
return ASN_PARSE_E;
19043+
19044+
XMEMCPY(cert->acmeIdentifier, &input[hashIdx], WC_SHA256_DIGEST_SIZE);
19045+
cert->acmeIdentifierSz = WC_SHA256_DIGEST_SIZE;
19046+
return 0;
19047+
}
19048+
#endif /* WOLFSSL_ACME_OID */
19049+
1901719050
#ifdef WOLFSSL_ASN_TEMPLATE
1901819051
/* ASN.1 template for KeyPurposeId.
1901919052
* X.509: RFC 5280, 4.2.1.12 - Extended Key Usage.
@@ -20236,6 +20269,14 @@ int DecodeExtensionType(const byte* input, word32 length, word32 oid,
2023620269
return ASN_PARSE_E;
2023720270
break;
2023820271
#endif /* WOLFSSL_DUAL_ALG_CERTS */
20272+
#ifdef WOLFSSL_ACME_OID
20273+
case ACME_IDENTIFIER_OID:
20274+
VERIFY_AND_SET_OID(cert->extAcmeIdentifierSet);
20275+
cert->extAcmeIdentifierCrit = critical ? 1 : 0;
20276+
if (DecodeAcmeId(&input[idx], length, cert) < 0)
20277+
return ASN_PARSE_E;
20278+
break;
20279+
#endif
2023920280
default:
2024020281
if (isUnknownExt != NULL)
2024120282
*isUnknownExt = 1;
@@ -25250,6 +25291,9 @@ typedef struct DerCert {
2525025291
#endif
2525125292
byte certPolicies[MAX_CERTPOL_NB*MAX_CERTPOL_SZ]; /* Certificate Policies */
2525225293
byte crlInfo[CTC_MAX_CRLINFO_SZ]; /* CRL Distribution Points */
25294+
#ifdef WOLFSSL_ACME_OID
25295+
byte acmeId[MAX_ACMEID_SZ]; /* RFC 8737 id-pe-acmeIdentifier */
25296+
#endif
2525325297
#endif
2525425298
#ifdef WOLFSSL_CERT_REQ
2525525299
byte attrib[MAX_ATTRIB_SZ]; /* Cert req attributes encoded */
@@ -25280,6 +25324,9 @@ typedef struct DerCert {
2528025324
#endif
2528125325
int certPoliciesSz; /* encoded CertPolicies extension length*/
2528225326
int crlInfoSz; /* encoded CRL Dist Points length */
25327+
#ifdef WOLFSSL_ACME_OID
25328+
int acmeIdSz; /* encoded acmeIdentifier length */
25329+
#endif
2528325330
#endif
2528425331
#ifdef WOLFSSL_ALT_NAMES
2528525332
int altNamesSz; /* encoded AltNames extension length */
@@ -26510,6 +26557,12 @@ static const ASNItem static_certExtsASN[] = {
2651026557
/* CRLINFO_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
2651126558
/* CRLINFO_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
2651226559
/* CRLINFO_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 },
26560+
/* RFC 8737 id-pe-acmeIdentifier */
26561+
/* ACMEID_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
26562+
/* ACMEID_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
26563+
/* ACMEID_CRIT */ { 1, ASN_BOOLEAN, 0, 0, 0 },
26564+
/* ACMEID_STR */ { 1, ASN_OCTET_STRING, 0, 1, 0 },
26565+
/* ACMEID_HASH */ { 2, ASN_OCTET_STRING, 0, 0, 0 },
2651326566
#ifdef WOLFSSL_DUAL_ALG_CERTS
2651426567
/* SAPKI_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
2651526568
/* SAPKI_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
@@ -26568,6 +26621,11 @@ enum {
2656826621
CERTEXTSASN_IDX_CRLINFO_SEQ,
2656926622
CERTEXTSASN_IDX_CRLINFO_OID,
2657026623
CERTEXTSASN_IDX_CRLINFO_STR,
26624+
CERTEXTSASN_IDX_ACMEID_SEQ,
26625+
CERTEXTSASN_IDX_ACMEID_OID,
26626+
CERTEXTSASN_IDX_ACMEID_CRIT,
26627+
CERTEXTSASN_IDX_ACMEID_STR,
26628+
CERTEXTSASN_IDX_ACMEID_HASH,
2657126629
#ifdef WOLFSSL_DUAL_ALG_CERTS
2657226630
CERTEXTSASN_IDX_SAPKI_SEQ,
2657326631
CERTEXTSASN_IDX_SAPKI_OID,
@@ -26623,6 +26681,10 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
2662326681
static const byte nsCertOID[] = { 0x60, 0x86, 0x48, 0x01,
2662426682
0x86, 0xF8, 0x42, 0x01, 0x01 };
2662526683
static const byte crlInfoOID[] = { 0x55, 0x1D, 0x1F };
26684+
#ifdef WOLFSSL_ACME_OID
26685+
static const byte acmeIdOID[] = { 0x2B, 0x06, 0x01, 0x05, 0x05, 0x07,
26686+
0x01, 0x1F };
26687+
#endif
2662626688
#ifdef WOLFSSL_DUAL_ALG_CERTS
2662726689
static const byte sapkiOID[] = { 0x55, 0x1d, 0x48 };
2662826690
static const byte altSigAlgOID[] = { 0x55, 0x1d, 0x49 };
@@ -26878,6 +26940,24 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
2687826940
CERTEXTSASN_IDX_CRLINFO_STR);
2687926941
}
2688026942

26943+
#ifdef WOLFSSL_ACME_OID
26944+
/* id-pe-acmeIdentifier (TLS-ALPN-01 challenge cert).
26945+
* Always critical=TRUE. */
26946+
if (cert->acmeIdentifierSz == WC_SHA256_DIGEST_SIZE) {
26947+
SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ACMEID_OID],
26948+
acmeIdOID, sizeof(acmeIdOID));
26949+
SetASN_Boolean(&dataASN[CERTEXTSASN_IDX_ACMEID_CRIT], 1);
26950+
SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_ACMEID_HASH],
26951+
cert->acmeIdentifier, (word32)cert->acmeIdentifierSz);
26952+
}
26953+
else
26954+
#endif /* WOLFSSL_ACME_OID */
26955+
{
26956+
/* Don't write out the ACME identifier extension. */
26957+
SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_ACMEID_SEQ,
26958+
CERTEXTSASN_IDX_ACMEID_HASH);
26959+
}
26960+
2688126961
#ifdef WOLFSSL_DUAL_ALG_CERTS
2688226962
if (cert->sapkiDer != NULL) {
2688326963
/* Set subject alternative public key info OID, criticality and
@@ -29313,6 +29393,40 @@ int wc_SetExtKeyUsage(Cert *cert, const char *value)
2931329393
return ret;
2931429394
}
2931529395

29396+
#ifdef WOLFSSL_ACME_OID
29397+
/* Set the id-pe-acmeIdentifier extension value from the ACME
29398+
* keyAuth string. Computes SHA-256 over keyAuth and stores the digest
29399+
* as the extension value. RFC 8737 3 requires critical=TRUE; that's
29400+
* applied at encode time in EncodeExtensions.
29401+
*
29402+
* keyAuth is the raw bytes of the key authorization string per
29403+
* RFC 8555 8.1: token "." JWK_thumbprint.
29404+
*/
29405+
int wc_SetAcmeIdentifierExt(Cert *cert, const byte *keyAuth, word32 keyAuthSz)
29406+
{
29407+
int ret;
29408+
byte digest[WC_SHA256_DIGEST_SIZE];
29409+
wc_Sha256 sha;
29410+
29411+
if (cert == NULL || keyAuth == NULL || keyAuthSz == 0)
29412+
return BAD_FUNC_ARG;
29413+
29414+
ret = wc_InitSha256(&sha);
29415+
if (ret != 0)
29416+
return ret;
29417+
ret = wc_Sha256Update(&sha, keyAuth, keyAuthSz);
29418+
if (ret == 0)
29419+
ret = wc_Sha256Final(&sha, digest);
29420+
wc_Sha256Free(&sha);
29421+
if (ret != 0)
29422+
return ret;
29423+
29424+
XMEMCPY(cert->acmeIdentifier, digest, WC_SHA256_DIGEST_SIZE);
29425+
cert->acmeIdentifierSz = WC_SHA256_DIGEST_SIZE;
29426+
return 0;
29427+
}
29428+
#endif /* WOLFSSL_ACME_OID */
29429+
2931629430
#ifdef WOLFSSL_EKU_OID
2931729431
/*
2931829432
* cert structure to set EKU oid in

0 commit comments

Comments
 (0)