From 5cb64770bf5288077a34776cd6bf7a532b10fc66 Mon Sep 17 00:00:00 2001 From: Yosuke Shimizu Date: Fri, 21 Aug 2026 13:03:17 +0900 Subject: [PATCH] scep: name the issuing CA in the GetCertInitial IssuerAndSubject - wolfcert_scep_issuer_and_subject selects the issuer Name from the envelope-target certificate: its own subject when it sets basic constraints CA, otherwise the name of its issuer. The SEQUENCE length, allocation and copy all use the selected Name, and the parameter is renamed ra_cert_der/ra_cert_len. - The function and its internal.h declaration are WOLFCERT_TEST_VIS, and both doc comments state the selection rule. - tests: make_signed_cert issues a certificate under the make_ca CA with its own subject and an optional CA flag; test_issuer_and_subject_issuer_name checks the built structure for an RA certificate, the CA itself and a sub-CA, comparing both Names byte for byte. Issue: F-8022 --- src/internal.h | 11 +- src/scep/scep_msg.c | 43 +++++--- tests/unit/test_scep_msg.c | 206 +++++++++++++++++++++++++++++++++++++ 3 files changed, 239 insertions(+), 21 deletions(-) diff --git a/src/internal.h b/src/internal.h index c6c636e..49306e7 100644 --- a/src/internal.h +++ b/src/internal.h @@ -352,11 +352,12 @@ typedef struct { const char* fail_info; } WolfCertScepAttrs; -/* Build a DER-encoded IssuerAndSubject SEQUENCE (RFC 8894 section 3.3.2) from - * the raw Name bytes of the RA/CA cert (-> issuer) and a CSR (-> subject). - * Used as the enveloped content of a GetCertInitial pkiMessage. */ -int wolfcert_scep_issuer_and_subject(const uint8_t* issuer_cert_der, size_t issuer_cert_len, - const uint8_t* csr_der, size_t csr_len, +/* Build the GetCertInitial IssuerAndSubject (RFC 8894 section 3.3.2). The + * issuer Name is the envelope-target cert's own subject when it is a CA, + * otherwise its issuer; the subject Name comes from the CSR. */ +WOLFCERT_TEST_VIS int wolfcert_scep_issuer_and_subject( + const uint8_t* ra_cert_der, size_t ra_cert_len, + const uint8_t* csr_der, size_t csr_len, WolfCertBuffer* out_der, void* heap); WOLFCERT_TEST_VIS int wolfcert_scep_envelop(const uint8_t* ra_cert_der, diff --git a/src/scep/scep_msg.c b/src/scep/scep_msg.c index 7116734..8047f9e 100644 --- a/src/scep/scep_msg.c +++ b/src/scep/scep_msg.c @@ -787,23 +787,23 @@ WOLFCERT_TEST_VIS int wolfcert_scep_parse_pki_message(const uint8_t* pki_der, return WOLFCERT_OK; } -/* Build RFC 8894 section 3.3.2 IssuerAndSubject: - * IssuerAndSubject ::= SEQUENCE { issuer Name, subject Name } - * where the issuer Name is copied from the RA/CA cert and the subject - * Name is copied from the CSR. This is the enveloped content of a - * GetCertInitial (messageType 20) pkiMessage - it lets the server - * locate the pending request by DN when transactionID matching is - * ambiguous. */ -int wolfcert_scep_issuer_and_subject(const uint8_t* issuer_cert_der, size_t issuer_cert_len, - const uint8_t* csr_der, size_t csr_len, +/* Build the GetCertInitial (messageType 20) enveloped content, RFC 8894 + * section 3.3.2 IssuerAndSubject ::= SEQUENCE { issuer Name, subject Name }: + * the Name of the issuing CA, then the subject Name from the CSR. */ +WOLFCERT_TEST_VIS int wolfcert_scep_issuer_and_subject( + const uint8_t* ra_cert_der, size_t ra_cert_len, + const uint8_t* csr_der, size_t csr_len, WolfCertBuffer* out_der, void* heap) { - if (issuer_cert_der == NULL || csr_der == NULL || out_der == NULL) + const uint8_t* issuer_name; + int issuer_name_len; + + if (ra_cert_der == NULL || csr_der == NULL || out_der == NULL) return WOLFCERT_ERR_BAD_ARG; DecodedCert ic; - wc_InitDecodedCert(&ic, (byte*)issuer_cert_der, - (word32)issuer_cert_len, heap); + wc_InitDecodedCert(&ic, (byte*)ra_cert_der, + (word32)ra_cert_len, heap); int rc = wc_ParseCert(&ic, CERT_TYPE, NO_VERIFY, NULL); if (rc != 0) { @@ -821,14 +821,25 @@ int wolfcert_scep_issuer_and_subject(const uint8_t* issuer_cert_der, size_t issu return WOLFCERT_ERR_PARSE; } - if (ic.subjectRaw == NULL || ic.subjectRawLen <= 0 || + /* A CA certificate issues under its own name. An RA certificate is an + * end entity, so the CA that will issue is the one that issued it. */ + if (ic.extBasicConstSet && ic.isCA) { + issuer_name = ic.subjectRaw; + issuer_name_len = ic.subjectRawLen; + } + else { + issuer_name = ic.issuerRaw; + issuer_name_len = ic.issuerRawLen; + } + + if (issuer_name == NULL || issuer_name_len <= 0 || sc.subjectRaw == NULL || sc.subjectRawLen <= 0) { wc_FreeDecodedCert(&ic); wc_FreeDecodedCert(&sc); return WOLFCERT_ERR_PARSE; } - size_t inner = (size_t)ic.subjectRawLen + (size_t)sc.subjectRawLen; + size_t inner = (size_t)issuer_name_len + (size_t)sc.subjectRawLen; size_t cap = inner + 8; uint8_t* buf = (uint8_t*)WOLFCERT_XMALLOC(cap, heap); if (buf == NULL) { @@ -847,8 +858,8 @@ int wolfcert_scep_issuer_and_subject(const uint8_t* issuer_cert_der, size_t issu } size_t off = 1 + (size_t)ll; - memcpy(buf + off, ic.subjectRaw, (size_t)ic.subjectRawLen); - off += (size_t)ic.subjectRawLen; + memcpy(buf + off, issuer_name, (size_t)issuer_name_len); + off += (size_t)issuer_name_len; memcpy(buf + off, sc.subjectRaw, (size_t)sc.subjectRawLen); off += (size_t)sc.subjectRawLen; diff --git a/tests/unit/test_scep_msg.c b/tests/unit/test_scep_msg.c index 5508346..5f75971 100644 --- a/tests/unit/test_scep_msg.c +++ b/tests/unit/test_scep_msg.c @@ -141,6 +141,96 @@ static int make_ca(uint8_t** cert_out, size_t* cert_out_len, return ret; } +/* Issue a certificate signed by the make_ca CA cert/key, so its issuer and + * subject names differ. `is_ca` sets the basic constraints CA flag. Ownership + * of the returned buffer passes to the caller (free with free()). */ +static int make_signed_cert(const uint8_t* ca_der, size_t ca_der_len, + const uint8_t* ca_key_der, size_t ca_key_len, + const char* cn, int is_ca, + uint8_t** cert_out, size_t* cert_out_len) +{ + RsaKey ca_key; + RsaKey sub_key; + WC_RNG rng; + Cert* cert = NULL; + uint8_t* der = NULL; + word32 idx = 0; + int ret = 0; + int body_n = 0; + int sign_n = 0; + + if (wc_InitRng(&rng) != 0) + return -1; + if (wc_InitRsaKey(&ca_key, NULL) != 0) { + wc_FreeRng(&rng); + return -1; + } + if (wc_InitRsaKey(&sub_key, NULL) != 0) { + wc_FreeRsaKey(&ca_key); + wc_FreeRng(&rng); + return -1; + } + + if (wc_RsaPrivateKeyDecode(ca_key_der, &idx, &ca_key, + (word32)ca_key_len) != 0) + ret = -1; + + if (ret == 0 && wc_MakeRsaKey(&sub_key, 2048, WC_RSA_EXPONENT, &rng) != 0) + ret = -1; + + if (ret == 0) { + der = (uint8_t*)malloc(4096); + if (der == NULL) + ret = -1; + } + + if (ret == 0) { + cert = wc_CertNew(NULL); + if (cert == NULL) + ret = -1; + } + + if (ret == 0) { + wc_InitCert_ex(cert, NULL, INVALID_DEVID); + strncpy(cert->subject.commonName, cn, CTC_NAME_SIZE - 1); + cert->subject.commonName[CTC_NAME_SIZE - 1] = '\0'; + cert->isCA = is_ca; + cert->selfSigned = 0; + cert->sigType = CTC_SHA256wRSA; + cert->daysValid = 2; + + if (wc_SetIssuerBuffer(cert, ca_der, (int)ca_der_len) != 0) + ret = -1; + } + + if (ret == 0) { + body_n = wc_MakeCert(cert, der, 4096, &sub_key, NULL, &rng); + if (body_n <= 0) + ret = -1; + } + + if (ret == 0) { + sign_n = wc_SignCert(cert->bodySz, cert->sigType, der, 4096, &ca_key, + NULL, &rng); + if (sign_n <= 0) + ret = -1; + } + + if (ret == 0) { + *cert_out = der; + *cert_out_len = (size_t)sign_n; + der = NULL; /* ownership transferred */ + } + + if (cert != NULL) + wc_CertFree(cert); + free(der); + wc_FreeRsaKey(&sub_key); + wc_FreeRsaKey(&ca_key); + wc_FreeRng(&rng); + return ret; +} + #ifdef HAVE_ECC /* Generate a throwaway self-signed ECC (P-256) CA cert, DER. Ownership of the * returned buffer passes to the caller (free with free()). */ @@ -541,6 +631,120 @@ static int test_signer_subject_matches_csr(void) return 0; } +/* Build the IssuerAndSubject for `ra_der` and require its issuer Name to be + * the subject DN of `name_der`, followed by the CSR subject DN. */ +static int check_issuer_and_subject(const uint8_t* ra_der, size_t ra_len, + const uint8_t* name_der, size_t name_len, + const uint8_t* csr_der, size_t csr_len) +{ + WolfCertBuffer ias = { 0 }; + DecodedCert nc; + DecodedCert sc; + size_t hdr = 0; + int rc = 0; + + if (wolfcert_scep_issuer_and_subject(ra_der, ra_len, csr_der, csr_len, + &ias, NULL) != WOLFCERT_OK) + return 1; + + wc_InitDecodedCert(&nc, name_der, (word32)name_len, NULL); + wc_InitDecodedCert(&sc, csr_der, (word32)csr_len, NULL); + + if (wc_ParseCert(&nc, CERT_TYPE, NO_VERIFY, NULL) != 0 || + wc_ParseCert(&sc, CERTREQ_TYPE, NO_VERIFY, NULL) != 0) + rc = 1; + + if (rc == 0 && (nc.subjectRaw == NULL || nc.subjectRawLen <= 0 || + sc.subjectRaw == NULL || sc.subjectRawLen <= 0)) + rc = 1; + + /* Outer SEQUENCE: tag octet plus short- or long-form length octets. */ + if (rc == 0) { + if (ias.len < 2 || ias.data[0] != 0x30) + rc = 1; + else if ((ias.data[1] & 0x80) == 0) + hdr = 2; + else + hdr = 2 + (size_t)(ias.data[1] & 0x7F); + } + + if (rc == 0 && ias.len != hdr + (size_t)nc.subjectRawLen + + (size_t)sc.subjectRawLen) + rc = 1; + + if (rc == 0 && memcmp(ias.data + hdr, nc.subjectRaw, + (size_t)nc.subjectRawLen) != 0) + rc = 1; + + if (rc == 0 && memcmp(ias.data + hdr + (size_t)nc.subjectRawLen, + sc.subjectRaw, (size_t)sc.subjectRawLen) != 0) + rc = 1; + + wc_FreeDecodedCert(&nc); + wc_FreeDecodedCert(&sc); + wolfcert_buffer_free(&ias); + return rc; +} + +/* RFC 8894 section 3.3.2: the IssuerAndSubject issuer Name identifies the CA + * that issues the requested cert - an RA contributes its issuer's name, a CA + * (including a sub-CA under an offline root) its own subject. */ +static int test_issuer_and_subject_issuer_name(void) +{ + RsaKey key; + WC_RNG rng; + uint8_t* ca_der = NULL; + size_t ca_len = 0; + uint8_t* ca_key_der = NULL; + size_t ca_key_len = 0; + uint8_t* ra_der = NULL; + size_t ra_len = 0; + uint8_t* sub_der = NULL; + size_t sub_len = 0; + uint8_t* csr_der = NULL; + size_t csr_len = 0; + int rc = 0; + + REQUIRE(wc_InitRng(&rng) == 0); + REQUIRE(wc_InitRsaKey(&key, NULL) == 0); + REQUIRE(wc_MakeRsaKey(&key, 2048, WC_RSA_EXPONENT, &rng) == 0); + + REQUIRE(make_ca(&ca_der, &ca_len, &ca_key_der, &ca_key_len) == 0); + REQUIRE(make_signed_cert(ca_der, ca_len, ca_key_der, ca_key_len, + "wolfCert Test RA Encryption", 0, + &ra_der, &ra_len) == 0); + REQUIRE(make_signed_cert(ca_der, ca_len, ca_key_der, ca_key_len, + "wolfCert Test Sub CA", 1, + &sub_der, &sub_len) == 0); + REQUIRE(make_csr(&key, &rng, "device-4711.example.org", "Widgets Inc", + &csr_der, &csr_len) == 0); + + /* Split RA/CA: the RA is an end entity, so its issuer names the CA. */ + rc = check_issuer_and_subject(ra_der, ra_len, ca_der, ca_len, + csr_der, csr_len); + + /* Single self-signed CA as the envelope target. */ + if (rc == 0) + rc = check_issuer_and_subject(ca_der, ca_len, ca_der, ca_len, + csr_der, csr_len); + + /* Sub-CA with no RA: it issues, so it names itself. */ + if (rc == 0) + rc = check_issuer_and_subject(sub_der, sub_len, sub_der, sub_len, + csr_der, csr_len); + + free(csr_der); + free(sub_der); + free(ra_der); + free(ca_key_der); + free(ca_der); + wc_FreeRsaKey(&key); + wc_FreeRng(&rng); + + REQUIRE(rc == 0); + return 0; +} + /* RFC 8894: a CertRep must be signed by the CA/RA certificate the client * fetched via GetCACert. A response signed by any other certificate, as a * MITM or rogue server would forge, must be rejected before the client @@ -1276,6 +1480,8 @@ int main(void) return 1; if (test_signer_subject_matches_csr()) return 1; + if (test_issuer_and_subject_issuer_name()) + return 1; if (test_cert_rep_signer_trust()) return 1; if (test_cert_rep_txid_and_type())