Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions src/internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -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) from an
* envelope-target cert and a CSR. A CA target names itself; an RA names its
* issuer, which assumes that is the CA issuing the requested cert. */
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,
Expand Down
96 changes: 78 additions & 18 deletions src/scep/scep_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,32 @@ static int enc_printable(const char* s, byte* out, size_t cap)
return enc_printable_n((const byte*)s, strlen(s), out, cap);
}

/* Wrap `vl` bytes in a SEQUENCE. Returns total bytes written, or -1 if `cap`
* is too small. */
static int enc_seq(const byte* v, size_t vl, byte* out, size_t cap)
{
if (cap < 1)
return -1;

out[0] = 0x30;
int ll = der_put_len(out + 1, cap - 1, vl);
if (ll < 0 || 1 + (size_t)ll + vl > cap)
return -1;

memcpy(out + 1 + ll, v, vl);
return (int)(1 + (size_t)ll + vl);
}

/* Size of the TLV that enc_seq writes for `vl` content bytes, or 0 when the
* length cannot be encoded. */
static size_t enc_seq_len(size_t vl)
{
byte tmp[8];
int ll = der_put_len(tmp, sizeof(tmp), vl);

return (ll < 0) ? 0 : (size_t)(1 + (size_t)ll + vl);
}

static int enc_octet(const byte* v, size_t vl, byte* out, size_t cap)
{
if (cap < 1)
Expand Down Expand Up @@ -795,23 +821,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)
Comment thread
yosuke-wolfssl marked this conversation as resolved.
{
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) {
Expand All @@ -829,15 +855,37 @@ 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. */

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This derives the issuing CA from the envelope-target certificate alone. Right for the usual NDES shape, but wrong whenever the RA's own certificate came from somewhere other than the enrolling CA - an RA credential issued by a policy CA while enrolment is served by a separate issuing CA, or an RA cert still carrying a pre-rollover issuer DN. Both call sites (scep_client.c:1229 and :1749) already hold ca_bundle, so preferring the bundle entry whose subject matches the derived issuer, and falling back to this heuristic when nothing matches, would close that cheaply. Otherwise I would state the assumption in the internal.h contract: the RA certificate must be issued by the CA that will issue the requested cert.

Two small things on the same condition. ic.extBasicConstSet && is redundant - wolfSSL assigns isCA in exactly one place (DecodeBasicCaConstraintInternal), and only after VERIFY_AND_SET_OID has already set extBasicConstSet, so isCA is never 1 without it. And make_signed_cert(is_ca = 0) emits no basicConstraints at all, while a real RA certificate carries one - worth shaping the fixture like what the split-RA path will actually meet.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Both small things taken. extBasicConstSet is gone — VERIFY_AND_SET_OID sets it immediately before the only assignment to isCA. The RA fixture now carries CA:FALSE, and a fourth target with no basicConstraints covers the one shape where the new rule differs from the old code.

On the RA whose issuer is not the enrolling CA: real, but I would rather state the assumption than half-close it. The bundle lookup as described does not change the emitted bytes — a matching entry's subject is issuerRaw, and with no match we fall back to the same heuristic. The rule that would address your two cases is "take the CA-flagged bundle entry, ignore the RA's issuer", which needs ca_bundle threaded in and still goes ambiguous when the bundle holds a root and an intermediate. Closing it properly means the caller naming the enrolling CA, i.e. a public API change.

So internal.h now says an RA target names its issuer, "which assumes that is the CA issuing the requested cert", and I am filing a follow-up covering this together with the CLI enveloping to the first GetCACert certificate and wolfcert_scep_verify_rep_signer's missing chain walk — three halves of one gap, better designed together.

if (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;
/* Give each Name its own SEQUENCE, so the result decodes as
* IssuerAndSubject ::= SEQUENCE { issuer Name, subject Name }. */
size_t issuer_tlv = enc_seq_len((size_t)issuer_name_len);
size_t subject_tlv = enc_seq_len((size_t)sc.subjectRawLen);
size_t inner = issuer_tlv + subject_tlv;
size_t cap = inner + 8;

if (issuer_tlv == 0 || subject_tlv == 0) {
wc_FreeDecodedCert(&ic);
wc_FreeDecodedCert(&sc);
return WOLFCERT_ERR_MEMORY;
}

uint8_t* buf = (uint8_t*)WOLFCERT_XMALLOC(cap, heap);
if (buf == NULL) {
wc_FreeDecodedCert(&ic);
Expand All @@ -855,10 +903,22 @@ 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, sc.subjectRaw, (size_t)sc.subjectRawLen);
off += (size_t)sc.subjectRawLen;
int n = enc_seq(issuer_name, (size_t)issuer_name_len,
buf + off, cap - off);
if (n > 0) {
off += (size_t)n;
n = enc_seq(sc.subjectRaw, (size_t)sc.subjectRawLen,
buf + off, cap - off);
}

if (n < 0) {
WOLFCERT_XFREE(buf, heap);
wc_FreeDecodedCert(&ic);
wc_FreeDecodedCert(&sc);
return WOLFCERT_ERR_MEMORY;
}

off += (size_t)n;

wc_FreeDecodedCert(&ic);
wc_FreeDecodedCert(&sc);
Expand Down
Loading
Loading