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). 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,
Expand Down
43 changes: 27 additions & 16 deletions src/scep/scep_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
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 @@ -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) {
Expand All @@ -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;

Expand Down
206 changes: 206 additions & 0 deletions tests/unit/test_scep_msg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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()). */
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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())
Expand Down
Loading