@@ -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