mirror of https://github.com/wolfSSL/wolfssh.git
certman: require keyCertSign on self-issued peer intermediates
- CertManIntermediateIsCA() demotes a CA intermediate whose KeyUsage extension lacks keyCertSign whether or not it is self-issued. - certmanForgeCert() takes the subject name from issuerCert when cn is NULL, and with WOLFSSL_CERT_EXT sets the subject and authority key ids. - certmanIsSelfIssued() reports whether a cert's issuer and subject name hashes match; tests/unit.c includes asn.h for it. - certmanCheckIntermediate() gains selfIssued, which gives the intermediate the root's subject name and fails with -935 when the forged cert is not self-issued, and appendRoot, which also sends the trusted root at the end of the chain. A promoted case passes only on WS_SUCCESS or WS_CERT_PROFILE_E. - test_CertMan_PromoteValidCaIntermediate() covers a self-issued intermediate with and without keyCertSign, and a chain that ends in the trusted root.pull/1318/head
parent
7320687a1e
commit
63c8aef551
|
|
@ -313,8 +313,9 @@ enum {
|
|||
#define MAX_CHAIN_DEPTH 9
|
||||
#endif
|
||||
|
||||
/* Returns 1 if der is a CA: isCA set and, unless self-signed, keyCertSign
|
||||
* set. Already signature-verified by the caller, so parse NO_VERIFY. */
|
||||
/* Returns 1 if der is a CA: isCA set and, when a KeyUsage extension is
|
||||
* present, keyCertSign set. Already signature-verified by the caller, so
|
||||
* parse NO_VERIFY. */
|
||||
static int CertManIntermediateIsCA(WOLFSSH_CERTMAN* cm,
|
||||
const unsigned char* der, word32 derSz)
|
||||
{
|
||||
|
|
@ -336,10 +337,11 @@ static int CertManIntermediateIsCA(WOLFSSH_CERTMAN* cm,
|
|||
if (wc_ParseCert(decoded, WOLFSSL_FILETYPE_ASN1, NO_VERIFY, NULL) == 0) {
|
||||
isCA = decoded->isCA;
|
||||
#ifndef ALLOW_INVALID_CERTSIGN
|
||||
if (isCA && !decoded->selfSigned && decoded->extKeyUsageSet &&
|
||||
if (isCA && decoded->extKeyUsageSet &&
|
||||
(decoded->extKeyUsage & KEYUSE_KEY_CERT_SIGN) == 0) {
|
||||
/* If a KeyUsage extension is present, an intermediate CA must
|
||||
* assert the keyCertSign bit. */
|
||||
/* A KeyUsage extension must assert keyCertSign. This also
|
||||
* rejects a trusted root the peer appends without the bit.
|
||||
* TODO: rework after wolfSSL selfSigned handling is improved. */
|
||||
isCA = 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
|||
102
tests/unit.c
102
tests/unit.c
|
|
@ -65,6 +65,7 @@
|
|||
!defined(NO_FILESYSTEM)
|
||||
#define WOLFSSH_TEST_CERTMAN_PROMOTE
|
||||
#include <wolfssl/wolfcrypt/ecc.h>
|
||||
#include <wolfssl/wolfcrypt/asn.h>
|
||||
#endif
|
||||
|
||||
/* Cert manager tests that read a test cert off disk. A superset of the
|
||||
|
|
@ -18286,7 +18287,8 @@ static int certmanForgeChild(const byte* issuerCert, word32 issuerCertSz,
|
|||
return ret;
|
||||
}
|
||||
|
||||
/* Forge a cert with the given subject CN. When isCA is set the cert asserts
|
||||
/* Forge a cert with the given subject CN, or with issuerCert's subject name
|
||||
* when cn is NULL (a self-issued cert). When isCA is set the cert asserts
|
||||
* basicConstraints CA=TRUE. When keyUsage is non-NULL the cert carries a
|
||||
* KeyUsage extension with the named usage(s) (e.g. "keyCertSign" or
|
||||
* "digitalSignature"); NULL omits the extension entirely. extKeyUsage does
|
||||
|
|
@ -18315,12 +18317,17 @@ static int certmanForgeCert(const char* cn, int isCA, const char* keyUsage,
|
|||
/* wc_InitCert zeroed the struct, so leaving the final byte untouched
|
||||
* keeps the name NUL-terminated and avoids a strncpy truncation
|
||||
* warning on the runtime cn pointer. */
|
||||
WSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1);
|
||||
WSTRNCPY(cert.subject.commonName, cn, CTC_NAME_SIZE - 1);
|
||||
if (cn != NULL) {
|
||||
WSTRNCPY(cert.subject.country, "US", CTC_NAME_SIZE - 1);
|
||||
WSTRNCPY(cert.subject.commonName, cn, CTC_NAME_SIZE - 1);
|
||||
}
|
||||
else {
|
||||
ret = wc_SetSubjectBuffer(&cert, issuerCert, (int)issuerCertSz);
|
||||
}
|
||||
cert.sigType = CTC_SHA256wECDSA;
|
||||
cert.daysValid = 365;
|
||||
cert.isCA = isCA;
|
||||
if (keyUsage != NULL) {
|
||||
if (ret == 0 && keyUsage != NULL) {
|
||||
#ifdef WOLFSSL_CERT_EXT
|
||||
ret = wc_SetKeyUsage(&cert, keyUsage);
|
||||
#else
|
||||
|
|
@ -18347,6 +18354,14 @@ static int certmanForgeCert(const char* cn, int isCA, const char* keyUsage,
|
|||
}
|
||||
if (ret == 0)
|
||||
ret = wc_SetIssuerBuffer(&cert, issuerCert, (int)issuerCertSz);
|
||||
#ifdef WOLFSSL_CERT_EXT
|
||||
/* Key ids make the signer lookup go by key, not by the issuer name,
|
||||
* which a self-issued intermediate shares with the root. */
|
||||
if (ret == 0)
|
||||
ret = wc_SetSubjectKeyIdFromPublicKey_ex(&cert, ECC_TYPE, subjectKey);
|
||||
if (ret == 0)
|
||||
ret = wc_SetAuthKeyIdFromCert(&cert, issuerCert, (int)issuerCertSz);
|
||||
#endif
|
||||
if (ret == 0) {
|
||||
sz = wc_MakeCert(&cert, der, *derSz, NULL, subjectKey, &rng);
|
||||
if (sz < 0)
|
||||
|
|
@ -18505,6 +18520,25 @@ done:
|
|||
return result;
|
||||
}
|
||||
|
||||
/* Returns 1 when der's issuer name equals its subject name. Compares the name
|
||||
* hashes, since DecodedCert.selfSigned also requires a self-signature in
|
||||
* some wolfSSL versions. */
|
||||
static int certmanIsSelfIssued(const byte* der, word32 derSz)
|
||||
{
|
||||
DecodedCert decoded;
|
||||
int ret = 0;
|
||||
|
||||
wc_InitDecodedCert(&decoded, der, derSz, NULL);
|
||||
if (wc_ParseCert(&decoded, CERT_TYPE, NO_VERIFY, NULL) == 0 &&
|
||||
WMEMCMP(decoded.issuerHash, decoded.subjectHash,
|
||||
sizeof(decoded.subjectHash)) == 0) {
|
||||
ret = 1;
|
||||
}
|
||||
wc_FreeDecodedCert(&decoded);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Drives CertManIntermediateIsCA through a forged [leaf <- intermediate] chain
|
||||
* with only the root trusted, so the intermediate CA must be promoted for the
|
||||
* leaf to find a signer.
|
||||
|
|
@ -18517,15 +18551,19 @@ done:
|
|||
* "digitalSignature" has KeyUsage but lacks keyCertSign: intermediate
|
||||
* CA must be demoted (keyCertSign-rejection branch).
|
||||
*
|
||||
* expectPromote==1 asserts the intermediate was promoted (verify returns
|
||||
* anything but WS_CERT_NO_SIGNER_E); ==0 asserts it was not (verify returns
|
||||
* WS_CERT_NO_SIGNER_E). Promotion is the unit under test, not full chain
|
||||
* success: with FPKI profile enforcement (--enable-all) a promoted chain's
|
||||
* synthetic leaf is rejected later with WS_CERT_PROFILE_E, which is
|
||||
* orthogonal -- hence the "anything but WS_CERT_NO_SIGNER_E" success criterion
|
||||
* mirroring the negative test's sanity check. */
|
||||
static int certmanCheckIntermediate(const char* interKeyUsage,
|
||||
int expectPromote)
|
||||
* selfIssued gives the intermediate the root's subject name while it keeps its
|
||||
* own key and the root's signature, the shape of a CA key rollover cert.
|
||||
*
|
||||
* appendRoot also sends the trusted root at the end of the chain, so it goes
|
||||
* through CertManIntermediateIsCA too and must be promoted as well.
|
||||
*
|
||||
* expectPromote==1 asserts the intermediate was promoted and the leaf
|
||||
* verified through it: verify returns WS_SUCCESS, or WS_CERT_PROFILE_E when
|
||||
* FPKI profile enforcement (--enable-all) rejects the synthetic leaf after the
|
||||
* chain verified. ==0 asserts it was not promoted (verify returns
|
||||
* WS_CERT_NO_SIGNER_E). */
|
||||
static int certmanCheckIntermediate(const char* interKeyUsage, int selfIssued,
|
||||
int appendRoot, int expectPromote)
|
||||
{
|
||||
int result = 0;
|
||||
int ret;
|
||||
|
|
@ -18586,12 +18624,17 @@ static int certmanCheckIntermediate(const char* interKeyUsage,
|
|||
}
|
||||
|
||||
/* Intermediate CA signed by the root. */
|
||||
ret = certmanForgeCert("IntermediateCA", 1, interKeyUsage, NULL,
|
||||
root, rootSz, &rootKey, &interKey, inter, &interSz);
|
||||
ret = certmanForgeCert(selfIssued ? NULL : "IntermediateCA", 1,
|
||||
interKeyUsage, NULL, root, rootSz, &rootKey, &interKey, inter,
|
||||
&interSz);
|
||||
if (ret != 0) {
|
||||
printf("CertMan: forge intermediate failed, ret=%d\n", ret);
|
||||
result = -929; goto done;
|
||||
}
|
||||
if (selfIssued && !certmanIsSelfIssued(inter, interSz)) {
|
||||
printf("CertMan: forged intermediate is not self-issued\n");
|
||||
result = -935; goto done;
|
||||
}
|
||||
|
||||
/* Leaf signed by the intermediate. */
|
||||
ret = certmanForgeCert("ValidLeaf", 0, NULL, NULL, inter, interSz,
|
||||
|
|
@ -18615,9 +18658,15 @@ static int certmanCheckIntermediate(const char* interKeyUsage,
|
|||
leaf, leafSz);
|
||||
chainSz = certmanAppendCert(chain, (word32)sizeof(chain), chainSz,
|
||||
inter, interSz);
|
||||
ret = wolfSSH_CERTMAN_VerifyCerts_buffer(cm, chain, chainSz, 2);
|
||||
if (expectPromote && ret == WS_CERT_NO_SIGNER_E) {
|
||||
printf("CertMan: valid intermediate CA not promoted, ret=%d\n", ret);
|
||||
if (appendRoot) {
|
||||
chainSz = certmanAppendCert(chain, (word32)sizeof(chain), chainSz,
|
||||
root, rootSz);
|
||||
}
|
||||
ret = wolfSSH_CERTMAN_VerifyCerts_buffer(cm, chain, chainSz,
|
||||
appendRoot ? 3 : 2);
|
||||
if (expectPromote && ret != WS_SUCCESS && ret != WS_CERT_PROFILE_E) {
|
||||
printf("CertMan: chain did not verify through the intermediate CA, "
|
||||
"ret=%d\n", ret);
|
||||
result = -933; goto done;
|
||||
}
|
||||
if (!expectPromote && ret != WS_CERT_NO_SIGNER_E) {
|
||||
|
|
@ -18647,16 +18696,25 @@ static int test_CertMan_PromoteValidCaIntermediate(void)
|
|||
int result;
|
||||
|
||||
/* A CA intermediate with no KeyUsage extension must still be promoted. */
|
||||
result = certmanCheckIntermediate(NULL, 1);
|
||||
result = certmanCheckIntermediate(NULL, 0, 0, 1);
|
||||
/* A peer may send the trusted root too; it asserts keyCertSign, so it is
|
||||
* promoted like any other CA. */
|
||||
if (result == 0)
|
||||
result = certmanCheckIntermediate(NULL, 0, 1, 1);
|
||||
#ifdef WOLFSSL_CERT_EXT
|
||||
/* As must a CA intermediate that explicitly asserts keyCertSign. */
|
||||
if (result == 0)
|
||||
result = certmanCheckIntermediate("keyCertSign", 1);
|
||||
result = certmanCheckIntermediate("keyCertSign", 0, 0, 1);
|
||||
if (result == 0)
|
||||
result = certmanCheckIntermediate("keyCertSign", 1, 0, 1);
|
||||
#ifndef ALLOW_INVALID_CERTSIGN
|
||||
/* But a CA intermediate carrying a KeyUsage extension that omits
|
||||
* keyCertSign must be rejected (the keyCertSign-rejection branch). */
|
||||
* keyCertSign must be rejected (the keyCertSign-rejection branch),
|
||||
* self-issued or not. */
|
||||
if (result == 0)
|
||||
result = certmanCheckIntermediate("digitalSignature", 0);
|
||||
result = certmanCheckIntermediate("digitalSignature", 0, 0, 0);
|
||||
if (result == 0)
|
||||
result = certmanCheckIntermediate("digitalSignature", 1, 0, 0);
|
||||
#endif
|
||||
#endif
|
||||
return result;
|
||||
|
|
|
|||
Loading…
Reference in New Issue