Merge pull request #4905 from anhu/custom_ext_parse

Injection and parsing of custom extensions in X.509 certificates.
This commit is contained in:
Sean Parkinson
2022-03-10 10:39:05 +10:00
committed by GitHub
5 changed files with 339 additions and 20 deletions

View File

@ -1937,3 +1937,103 @@ WOLFSSL_API int wc_SetTimeCb(wc_time_cb f);
\sa wc_SetTimeCb
*/
WOLFSSL_API time_t wc_Time(time_t* t);
/*!
\ingroup ASN
\brief This function injects a custom extension in to an X.509 certificate.
\return 0 Returned on success.
\return Other negative values on failure.
\param cert Pointer to an initialized DecodedCert object.
\param critical If 0, the extension will not be marked critical, otherwise
it will be marked critical.
\param oid Dot separted oid as a string. For example "1.2.840.10045.3.1.7"
\param der The der encoding of the content of the extension.
\param derSz The size in bytes of the der encoding.
_Example_
\code
int ret = 0;
Cert newCert;
wc_InitCert(&newCert);
// Code to setup subject, public key, issuer, and other things goes here.
ret = wc_SetCustomExtension(&newCert, 1, "1.2.3.4.5",
(const byte *)"This is a critical extension", 28);
if (ret < 0) {
// Failed to set the extension.
}
ret = wc_SetCustomExtension(&newCert, 0, "1.2.3.4.6",
(const byte *)"This is NOT a critical extension", 32)
if (ret < 0) {
// Failed to set the extension.
}
// Code to sign the certificate and then write it out goes here.
\endcode
\sa wc_InitCert
\sa wc_SetUnknownExtCallback
*/
WOLFSSL_API int wc_SetCustomExtension(Cert *cert, int critical, const char *oid,
const byte *der, word32 derSz);
/*!
\ingroup ASN
\brief This function registers a callback that will be used anytime
wolfSSL encounters an unknown X.509 extension in a certificate while parsing
a certificate. The prototype of the callback should be:
\return 0 Returned on success.
\return Other negative values on failure.
\param cert the DecodedCert struct that is to be associated with this
callback.
\param cb function to register as the time callback.
_Example_
\code
int ret = 0;
// Unkown extension callback prototype
int myUnknownExtCallback(const word16* oid, word32 oidSz, int crit,
const unsigned char* der, word32 derSz);
// Register it
ret = wc_SetUnknownExtCallback(cert, myUnknownExtCallback);
if (ret != 0) {
// failed to set the callback
}
// oid: Array of integers that are the dot separated values in an oid.
// oidSz: Number of values in oid.
// crit: Whether the extension was mark critical.
// der: The der encoding of the content of the extension.
// derSz: The size in bytes of the der encoding.
int myCustomExtCallback(const word16* oid, word32 oidSz, int crit,
const unsigned char* der, word32 derSz) {
// Logic to parse extension goes here.
// NOTE: by returning zero, we are accepting this extension and
// informing wolfSSL that it is acceptable. If you find an extension
// that you do not find acceptable, you should return an error. The
// standard behavior upon encountering an unknown extension with the
// critical flag set is to return ASN_CRIT_EXT_E. For the sake of
// brevity, this example is always accepting every extension; you
// should use different logic.
return 0;
}
\endcode
\sa ParseCert
\sa wc_SetCustomExtension
*/
WOLFSSL_ASN_API int wc_SetUnknownExtCallback(DecodedCert* cert,
wc_UnknownExtCallback cb);

View File

@ -5119,7 +5119,7 @@ static int DumpOID(const byte* oidData, word32 oidSz, word32 oid,
#ifdef HAVE_OID_DECODING
{
word16 decOid[16];
word16 decOid[MAX_OID_SZ];
word32 decOidSz = sizeof(decOid);
/* Decode the OID into dotted form. */
ret = DecodeObjectId(oidData, oidSz, decOid, &decOidSz);
@ -16569,11 +16569,15 @@ exit:
* @return Other -ve value on error.
*/
static int DecodeExtensionType(const byte* input, int length, word32 oid,
byte critical, DecodedCert* cert)
byte critical, DecodedCert* cert,
int *isUnknownExt)
{
int ret = 0;
word32 idx = 0;
if (isUnknownExt != NULL)
*isUnknownExt = 0;
switch (oid) {
/* Basic Constraints. */
case BASIC_CA_OID:
@ -16757,6 +16761,8 @@ static int DecodeExtensionType(const byte* input, int length, word32 oid,
return ASN_PARSE_E;
break;
default:
if (isUnknownExt != NULL)
*isUnknownExt = 1;
#ifndef WOLFSSL_NO_ASN_STRICT
/* While it is a failure to not support critical extensions,
* still parse the certificate ignoring the unsupported
@ -16810,6 +16816,19 @@ enum {
#define certExtASN_Length (sizeof(certExtASN) / sizeof(ASNItem))
#endif
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_ASN_TEMPLATE) \
&& defined(HAVE_OID_DECODING)
int wc_SetUnknownExtCallback(DecodedCert* cert,
wc_UnknownExtCallback cb) {
if (cert == NULL) {
return BAD_FUNC_ARG;
}
cert->unknownExtCallback = cb;
return 0;
}
#endif
/*
* Processing the Certificate Extensions. This does not modify the current
* index. It is works starting with the recorded extensions pointer.
@ -16897,7 +16916,8 @@ static int DecodeCertExtensions(DecodedCert* cert)
return ret;
}
ret = DecodeExtensionType(input + idx, length, oid, critical, cert);
ret = DecodeExtensionType(input + idx, length, oid, critical, cert,
NULL);
if (ret == ASN_CRIT_EXT_E) {
ret = 0;
criticalFail = 1;
@ -16942,6 +16962,7 @@ end:
/* Parse each extension. */
while ((ret == 0) && (idx < (word32)sz)) {
byte critical = 0;
int isUnknownExt = 0;
/* Clear dynamic data. */
XMEMSET(dataASN, 0, sizeof(*dataASN) * certExtASN_Length);
@ -16957,7 +16978,30 @@ end:
int length = dataASN[CERTEXTASN_IDX_VAL].length;
/* Decode the extension by type. */
ret = DecodeExtensionType(input + idx, length, oid, critical, cert);
ret = DecodeExtensionType(input + idx, length, oid, critical, cert,
&isUnknownExt);
#if defined(WOLFSSL_CUSTOM_OID) && defined(HAVE_OID_DECODING)
if (isUnknownExt && (cert->unknownExtCallback != NULL)) {
word16 decOid[MAX_OID_SZ];
word32 decOidSz = sizeof(decOid);
ret = DecodeObjectId(
dataASN[CERTEXTASN_IDX_OID].data.oid.data,
dataASN[CERTEXTASN_IDX_OID].data.oid.length,
decOid, &decOidSz);
if (ret != 0) {
/* Should never get here as the extension was successfully
* decoded earlier. Something might be corrupted. */
WOLFSSL_MSG("DecodeObjectId() failed. Corruption?");
WOLFSSL_ERROR(ret);
}
ret = cert->unknownExtCallback(decOid, decOidSz, critical,
dataASN[CERTEXTASN_IDX_VAL].data.buffer.data,
dataASN[CERTEXTASN_IDX_VAL].length);
}
#endif
(void)isUnknownExt;
/* Move index on to next extension. */
idx += length;
}
@ -21227,7 +21271,7 @@ static int SetEccPublicKey(byte* output, ecc_key* key, int outLen,
oidKeyType);
/* Set the curve OID. */
SetASN_Buffer(&dataASN[ECCPUBLICKEYASN_IDX_ALGOID_CURVEID],
key->dp->oid, key->dp->oidSz);
(const byte *)key->dp->oid, key->dp->oidSz);
/* Don't try to write out explicit parameters. */
dataASN[ECCPUBLICKEYASN_IDX_ALGOID_PARAMS].noOut = 1;
/* Set size of public point to ensure space is made for it. */
@ -23217,7 +23261,7 @@ static int EncodePublicKey(int keyType, byte* output, int outLen,
* X.509: RFC 5280, 4.1 - Basic Certificate Fields.
* All extensions supported for encoding are described.
*/
static const ASNItem certExtsASN[] = {
static const ASNItem static_certExtsASN[] = {
/* Basic Constraints Extension - 4.2.1.9 */
/* BC_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* BC_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
@ -23231,7 +23275,6 @@ static const ASNItem certExtsASN[] = {
/* SAN_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* SAN_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* SAN_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 },
#ifdef WOLFSSL_CERT_EXT
/* Subject Key Identifier - 4.2.1.2 */
/* SKID_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* SKID_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
@ -23257,7 +23300,7 @@ static const ASNItem certExtsASN[] = {
/* POLICIES_SEQ, */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* POLICIES_OID, */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* POLICIES_STR, */ { 1, ASN_OCTET_STRING, 0, 1, 0 },
/* POLICIES_INFO */ { 2, ASN_SEQUENCE, 0, 0, 0 },
/* POLICIES_INFO */ { 2, ASN_SEQUENCE, 1, 0, 0 },
/* Netscape Certificate Type */
/* NSTYPE_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* NSTYPE_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
@ -23266,12 +23309,9 @@ static const ASNItem certExtsASN[] = {
/* CRLINFO_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* CRLINFO_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* CRLINFO_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 },
#endif /* WOLFSSL_CERT_EXT */
#ifdef WOLFSSL_CUSTOM_OID
/* CUSTOM_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* CUSTOM_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* CUSTOM_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 },
#endif
};
enum {
CERTEXTSASN_IDX_BC_SEQ = 0,
@ -23314,10 +23354,21 @@ enum {
CERTEXTSASN_IDX_CUSTOM_SEQ,
CERTEXTSASN_IDX_CUSTOM_OID,
CERTEXTSASN_IDX_CUSTOM_STR,
CERTEXTSASN_IDX_START_CUSTOM,
};
/* Number of items in ASN.1 template for certificate extensions. */
#define certExtsASN_Length (sizeof(certExtsASN) / sizeof(ASNItem))
/* Number of items in ASN.1 template for certificate extensions. We multiply
* by 4 because there are 4 things (seq, OID, crit flag, octet string). */
#define certExtsASN_Length ((sizeof(static_certExtsASN) / sizeof(ASNItem)) \
+ (NUM_CUSTOM_EXT * 4))
static const ASNItem customExtASN[] = {
/* CUSTOM_SEQ */ { 0, ASN_SEQUENCE, 1, 1, 0 },
/* CUSTOM_OID */ { 1, ASN_OBJECT_ID, 0, 0, 0 },
/* CUSTOM_CRIT */ { 1, ASN_BOOLEAN, 0, 0, 0 },
/* CUSTOM_STR */ { 1, ASN_OCTET_STRING, 0, 0, 0 },
};
static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
int forRequest)
@ -23325,6 +23376,7 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
DECL_ASNSETDATA(dataASN, certExtsASN_Length);
int sz;
int ret = 0;
int i = 0;
static const byte bcOID[] = { 0x55, 0x1d, 0x13 };
#ifdef WOLFSSL_ALT_NAMES
static const byte sanOID[] = { 0x55, 0x1d, 0x11 };
@ -23340,6 +23392,41 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
static const byte crlInfoOID[] = { 0x55, 0x1D, 0x1F };
#endif
#ifdef WOLFSSL_SMALL_STACK
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
byte *encodedOids;
#endif
ASNItem *certExtsASN = (ASNItem *)XMALLOC(certExtsASN_Length *
sizeof(ASNItem), cert->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (certExtsASN == NULL) {
return MEMORY_E;
}
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
encodedOids = (byte *)XMALLOC(NUM_CUSTOM_EXT * MAX_OID_SZ, cert->heap,
DYNAMIC_TYPE_TMP_BUFFER);
if (encodedOids == NULL) {
XFREE(certExtsASN, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
return MEMORY_E;
}
#endif
#else
ASNItem certExtsASN[certExtsASN_Length];
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
byte encodedOids[NUM_CUSTOM_EXT * MAX_OID_SZ];
#endif
#endif
/* Clone static_certExtsASN into a certExtsASN and then fill the rest of it
* with (NUM_CUSTOM_EXT*4) more ASNItems specifying extensions. See comment
* above definition of certExtsASN_Length. */
XMEMCPY(certExtsASN, static_certExtsASN, sizeof(static_certExtsASN));
for (i = sizeof(static_certExtsASN) / sizeof(ASNItem);
i < (int)(sizeof(certExtsASN) / sizeof(ASNItem)); i += 4) {
XMEMCPY(&certExtsASN[i], customExtASN, sizeof(customExtASN));
}
(void)forRequest;
CALLOC_ASNSETDATA(dataASN, certExtsASN_Length, ret, cert->heap);
@ -23489,7 +23576,6 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_CRLINFO_SEQ,
CERTEXTSASN_IDX_CRLINFO_STR);
}
#endif /* WOLFSSL_CERT_EXT */
#ifdef WOLFSSL_CUSTOM_OID
/* encode a custom oid and value */
@ -23500,12 +23586,44 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
SetASN_Buffer(&dataASN[CERTEXTSASN_IDX_CUSTOM_STR],
cert->extCustom.val, cert->extCustom.valSz);
}
else {
else
#endif
{
/* Don't write out custom OID. */
SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_CUSTOM_SEQ,
CERTEXTSASN_IDX_CUSTOM_STR);
}
i = 0;
#ifdef WOLFSSL_CUSTOM_OID
for (; i < cert->customCertExtCount; i++) {
int idx = CERTEXTSASN_IDX_START_CUSTOM + (i * 4);
word32 encodedOidSz = MAX_OID_SZ;
idx++; /* Skip one for for SEQ. */
/* EncodePolicyOID() will never return error since we parsed this
* OID when it was set. */
EncodePolicyOID(&encodedOids[i * MAX_OID_SZ], &encodedOidSz,
cert->customCertExt[i].oid, NULL);
SetASN_Buffer(&dataASN[idx], &encodedOids[i * MAX_OID_SZ],
encodedOidSz);
idx++;
if (cert->customCertExt[i].crit) {
SetASN_Boolean(&dataASN[idx], 1);
} else {
dataASN[idx].noOut = 1;
}
idx++;
SetASN_Buffer(&dataASN[idx], cert->customCertExt[i].val,
cert->customCertExt[i].valSz);
}
#endif
while (i < NUM_CUSTOM_EXT) {
SetASNItem_NoOut(dataASN, CERTEXTSASN_IDX_START_CUSTOM + (i * 4),
CERTEXTSASN_IDX_START_CUSTOM + (i * 4) + 3);
i++;
}
#endif /* WOLFSSL_CERT_EXT */
}
if (ret == 0) {
@ -23554,6 +23672,13 @@ static int EncodeExtensions(Cert* cert, byte* output, word32 maxSz,
}
FREE_ASNSETDATA(dataASN, cert->heap);
#ifdef WOLFSSL_SMALL_STACK
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_CERT_EXT)
XFREE(encodedOids, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
XFREE(certExtsASN, cert->heap, DYNAMIC_TYPE_TMP_BUFFER);
#endif
return ret;
}
#endif /* WOLFSSL_ASN_TEMPLATE */
@ -26157,6 +26282,43 @@ int wc_SetExtKeyUsageOID(Cert *cert, const char *in, word32 sz, byte idx,
return 0;
}
#endif /* WOLFSSL_EKU_OID */
#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_CERT_GEN) && \
defined(WOLFSSL_CUSTOM_OID) && defined(HAVE_OID_ENCODING) && \
defined(WOLFSSL_CERT_EXT)
int wc_SetCustomExtension(Cert *cert, int critical, const char *oid,
const byte *der, word32 derSz) {
CertExtension *ext;
byte encodedOid[MAX_OID_SZ];
word32 encodedOidSz = MAX_OID_SZ;
int ret;
if (cert == NULL || oid == NULL || der == NULL || derSz == 0) {
return BAD_FUNC_ARG;
}
if (cert->customCertExtCount >= NUM_CUSTOM_EXT) {
return MEMORY_E;
}
/* Make sure we can properly parse the OID. */
ret = EncodePolicyOID(encodedOid, &encodedOidSz, oid, NULL);
if (ret != 0) {
return ret;
}
ext = &cert->customCertExt[cert->customCertExtCount];
ext->oid = oid;
ext->crit = (critical == 0) ? 0 : 1;
ext->val = der;
ext->valSz = derSz;
cert->customCertExtCount++;
return 0;
}
#endif
#endif /* WOLFSSL_CERT_EXT */
@ -28226,8 +28388,8 @@ static int wc_BuildEccKeyDer(ecc_key* key, byte* output, word32 *inLen,
SetASN_Buffer(&dataASN[ECCKEYASN_IDX_PKEY], NULL, privSz);
if (curveIn) {
/* Curve OID */
SetASN_Buffer(&dataASN[ECCKEYASN_IDX_CURVEID], key->dp->oid,
key->dp->oidSz);
SetASN_Buffer(&dataASN[ECCKEYASN_IDX_CURVEID],
(const byte *)key->dp->oid, key->dp->oidSz);
/* TODO: add support for SpecifiedECDomain curve. */
dataASN[ECCKEYASN_IDX_CURVEPARAMS].noOut = 1;
}

View File

@ -4072,17 +4072,37 @@ int wc_ecc_get_curve_id_from_dp_params(const ecc_set_type* dp)
int wc_ecc_get_curve_id_from_oid(const byte* oid, word32 len)
{
int curve_idx;
#ifdef HAVE_OID_DECODING
int ret;
word16 decOid[MAX_OID_SZ];
word32 decOidSz = sizeof(decOid);
#endif
if (oid == NULL)
return BAD_FUNC_ARG;
#ifdef HAVE_OID_DECODING
ret = DecodeObjectId(oid, len, decOid, &decOidSz);
if (ret != 0) {
return ret;
}
#endif
for (curve_idx = 0; ecc_sets[curve_idx].size != 0; curve_idx++) {
if (
#ifndef WOLFSSL_ECC_CURVE_STATIC
ecc_sets[curve_idx].oid &&
#endif
#ifdef HAVE_OID_DECODING
/* We double because decOidSz is a count of word16 elements. */
ecc_sets[curve_idx].oidSz == decOidSz &&
XMEMCMP(ecc_sets[curve_idx].oid, decOid,
decOidSz * 2) == 0
#else
ecc_sets[curve_idx].oidSz == len &&
XMEMCMP(ecc_sets[curve_idx].oid, oid, len) == 0) {
XMEMCMP(ecc_sets[curve_idx].oid, oid, len) == 0
#endif
) {
break;
}
}

View File

@ -1459,6 +1459,11 @@ typedef struct TrustedPeerCert TrustedPeerCert;
typedef struct SignatureCtx SignatureCtx;
typedef struct CertSignCtx CertSignCtx;
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_ASN_TEMPLATE) \
&& defined(HAVE_OID_DECODING)
typedef int (*wc_UnknownExtCallback)(const word16* oid, word32 oidSz, int crit,
const unsigned char* der, word32 derSz);
#endif
struct DecodedCert {
const byte* publicKey;
@ -1689,6 +1694,10 @@ struct DecodedCert {
#ifdef WOLFSSL_CERT_REQ
byte isCSR : 1; /* Do we intend on parsing a CSR? */
#endif
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_ASN_TEMPLATE) \
&& defined(HAVE_OID_DECODING)
wc_UnknownExtCallback unknownExtCallback;
#endif
};
#ifdef NO_SHA
@ -1816,6 +1825,12 @@ WOLFSSL_ASN_API void FreeDecodedCert(DecodedCert* cert);
WOLFSSL_ASN_API int ParseCert(DecodedCert* cert, int type, int verify,
void* cm);
#if defined(WOLFSSL_CUSTOM_OID) && defined(WOLFSSL_ASN_TEMPLATE) \
&& defined(HAVE_OID_DECODING)
WOLFSSL_ASN_API int wc_SetUnknownExtCallback(DecodedCert* cert,
wc_UnknownExtCallback cb);
#endif
WOLFSSL_LOCAL int DecodePolicyOID(char *out, word32 outSz, const byte *in,
word32 inSz);
WOLFSSL_LOCAL int EncodePolicyOID(byte *out, word32 *outSz,

View File

@ -322,6 +322,13 @@ typedef struct CertOidField {
int valSz;
char enc;
} CertOidField;
typedef struct CertExtension {
const char* oid;
byte crit;
const byte* val;
int valSz;
} CertExtension;
#endif
#endif /* WOLFSSL_CERT_GEN */
@ -369,6 +376,10 @@ typedef struct CertName {
#ifdef WOLFSSL_CERT_GEN
#ifndef NUM_CUSTOM_EXT
#define NUM_CUSTOM_EXT 16
#endif
/* for user to fill for certificate generation */
typedef struct Cert {
int version; /* x509 version */
@ -432,9 +443,13 @@ typedef struct Cert {
int challengePwPrintableString; /* encode as PrintableString */
#endif
#ifdef WOLFSSL_CUSTOM_OID
CertOidField extCustom; /* user oid and value to go in req extensions */
#endif
/* user oid and value to go in req extensions */
CertOidField extCustom;
/* Extensions to go into X.509 certificates */
CertExtension customCertExt[NUM_CUSTOM_EXT];
int customCertExtCount;
#endif
void* decodedCert; /* internal DecodedCert allocated from heap */
byte* der; /* Pointer to buffer of current DecodedCert cache */
void* heap; /* heap hint */
@ -530,6 +545,13 @@ WOLFSSL_API int wc_SetExtKeyUsage(Cert *cert, const char *value);
WOLFSSL_API int wc_SetExtKeyUsageOID(Cert *cert, const char *oid, word32 sz,
byte idx, void* heap);
#endif /* WOLFSSL_EKU_OID */
#if defined(WOLFSSL_ASN_TEMPLATE) && defined(WOLFSSL_CUSTOM_OID) && \
defined(HAVE_OID_ENCODING)
WOLFSSL_API int wc_SetCustomExtension(Cert *cert, int critical, const char *oid,
const byte *der, word32 derSz);
#endif
#endif /* WOLFSSL_CERT_EXT */
#endif /* WOLFSSL_CERT_GEN */