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
144 changes: 144 additions & 0 deletions doc/dox_comments/header_files/asn_public.h
Original file line number Diff line number Diff line change
Expand Up @@ -1067,6 +1067,150 @@ int wc_SetSubjectBuffer(Cert* cert, const byte* der, int derSz);
*/
int wc_SetAltNamesBuffer(Cert* cert, const byte* der, int derSz);

/*!
\ingroup ASN

\brief This function allocates a single subject alternative name (SAN)
entry, copies the supplied name into it, sets its GeneralName type and
length, and appends it to the linked list pointed to by entries. The name
is duplicated internally, so the caller's buffer need not outlive the call.
The resulting list can be encoded with wc_FlattenAltNames and must be freed
with FreeAltNames.

\return 0 Returned on success.
\return MEMORY_E Returned if dynamic memory allocation fails.
\return BAD_FUNC_ARG Returned for an ASN_IP_TYPE entry whose length is not a
valid IPv4/IPv6 address, or an ASN_RID_TYPE entry with malformed contents.
\return BUFFER_E Returned if the string representation of an ASN_IP_TYPE or
ASN_RID_TYPE entry does not fit its internal buffer.
\return Other negative error codes may propagate from generating the string
form of ASN_IP_TYPE and ASN_RID_TYPE entries.

\param heap pointer to the heap hint used for allocations (may be NULL)
\param str pointer to the name bytes (e.g. a DNS string, or raw IP octets
for ASN_IP_TYPE)
\param strLen length of str in bytes
\param type GeneralName type (e.g. ASN_DNS_TYPE, ASN_IP_TYPE,
ASN_RFC822_TYPE, ASN_URI_TYPE)
\param entries in/out pointer to the head of the alt-name linked list; a new
entry is appended

_Example_
\code
DNS_entry* list = NULL;
if (wc_SetDNSEntry(NULL, "example.com", 11, ASN_DNS_TYPE, &list) != 0) {
// error adding alt name
}
// ... encode with wc_FlattenAltNames, then:
FreeAltNames(list, NULL);
\endcode

\note This helper (along with wc_FlattenAltNames and FreeAltNames) is
exported from the library only when WOLFSSL_PUBLIC_ASN, OPENSSL_EXTRA,
OPENSSL_EXTRA_X509_SMALL, or WOLFSSL_TEST_CERT is defined; its prototype
lives in wolfssl/wolfcrypt/asn.h (not asn_public.h) because it uses the
DNS_entry type.

\sa wc_FlattenAltNames
\sa FreeAltNames
*/
int wc_SetDNSEntry(void* heap, const char* str, int strLen, int type,
DNS_entry** entries);

/*!
\ingroup ASN

\brief This function encodes a linked list of subject alternative name
entries into the DER GeneralNames SEQUENCE used as the value of the
subjectAltName certificate extension. The output is suitable for assigning
to Cert.altNames (with the return value stored in Cert.altNamesSz) prior to
signing.

\return >0 the number of bytes written to output (the full SEQUENCE,
including its tag and length).
\return 0 Returned when names is NULL (nothing to encode).
\return BAD_FUNC_ARG Returned if output is NULL.
\return BUFFER_E Returned if output is too small to hold the encoding.

\param output buffer that receives the DER GeneralNames SEQUENCE; size it to
hold the full extension value (e.g. CTC_MAX_ALT_SIZE)
\param outputSz capacity of output in bytes
\param names head of the alt-name linked list to encode (e.g. built with
wc_SetDNSEntry, or taken from a parsed DecodedCert.altNames)

_Example_
\code
Cert cert;
DNS_entry* list = NULL;
// ... populate list with wc_SetDNSEntry ...
int n = wc_FlattenAltNames(cert.altNames, sizeof(cert.altNames), list);
if (n < 0) {
// error encoding alt names
}
cert.altNamesSz = n;
FreeAltNames(list, NULL);
\endcode

\note This helper (along with wc_SetDNSEntry and FreeAltNames) is exported
from the library only when WOLFSSL_PUBLIC_ASN, OPENSSL_EXTRA,
OPENSSL_EXTRA_X509_SMALL, or WOLFSSL_TEST_CERT is defined; its prototype
lives in wolfssl/wolfcrypt/asn.h (not asn_public.h) because it uses the
DNS_entry type.

\sa wc_SetDNSEntry
\sa wc_SetAltNamesFromList
\sa FreeAltNames
\sa wc_SetAltNamesBuffer
*/
int wc_FlattenAltNames(byte* output, word32 outputSz, const DNS_entry* names);

/*!
\ingroup ASN

\brief This function encodes a linked list of subject alternative name
entries directly into a Cert structure, ready for signing. It is a
convenience wrapper around wc_FlattenAltNames: the list is encoded into
cert->altNames and the encoded length is stored in cert->altNamesSz, so the
caller does not have to manage the buffer or size bookkeeping. The supplied
list is not consumed and must still be freed by the caller with
FreeAltNames.

\return 0 Returned on success.
\return BAD_FUNC_ARG Returned if cert is NULL.
\return BUFFER_E Returned if the encoded names do not fit in cert->altNames.

\param cert pointer to the Cert whose altNames/altNamesSz fields are set
\param names head of the alt-name linked list to encode (e.g. built with
wc_SetDNSEntry, or taken from a parsed DecodedCert.altNames); may be NULL,
in which case cert->altNamesSz is set to 0

_Example_
\code
Cert cert;
DNS_entry* list = NULL;
wc_InitCert(&cert);
// ... populate list with wc_SetDNSEntry ...
if (wc_SetAltNamesFromList(&cert, list) != 0) {
// error encoding alt names
}
FreeAltNames(list, NULL);
// ... wc_MakeCert / wc_SignCert ...
\endcode

\note This helper (along with wc_SetDNSEntry, wc_FlattenAltNames, and
FreeAltNames) is exported from the library only when WOLFSSL_PUBLIC_ASN,
OPENSSL_EXTRA, OPENSSL_EXTRA_X509_SMALL, or WOLFSSL_TEST_CERT is defined;
its prototype lives in wolfssl/wolfcrypt/asn.h (not asn_public.h) because it
uses the DNS_entry type. Its DER-input sibling wc_SetAltNamesBuffer is
always exported.

\sa wc_SetDNSEntry
\sa wc_FlattenAltNames
\sa wc_SetAltNamesBuffer
\sa FreeAltNames
*/
int wc_SetAltNamesFromList(Cert* cert, const DNS_entry* names);

/*!
\ingroup ASN

Expand Down
39 changes: 39 additions & 0 deletions wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -14520,6 +14520,16 @@ static int SetDNSEntry(void* heap, const char* str, int strLen,

return ret;
}

/* Public wrapper for SetDNSEntry(): allocate an alt-name entry that copies the
* given name, set its type/length, and append it to the linked list. The list
* is freed with FreeAltNames() and can be flattened with
* wc_FlattenAltNames(). */
int wc_SetDNSEntry(void* heap, const char* str, int strLen, int type,
DNS_entry** entries)
{
return SetDNSEntry(heap, str, strLen, type, entries);
}
#endif

/* Set the details of a subject name component into a certificate.
Expand Down Expand Up @@ -26960,6 +26970,15 @@ int FlattenAltNames(byte* output, word32 outputSz, const DNS_entry* names)
return (int)idx;
}

/* Public wrapper for FlattenAltNames(): encode a linked list of alt-name
* entries into the DER GeneralNames SEQUENCE used as the subjectAltName
* extension value. Returns the encoded length, 0 for a NULL list, or a
* negative error code. */
int wc_FlattenAltNames(byte* output, word32 outputSz, const DNS_entry* names)
{
return FlattenAltNames(output, outputSz, names);
}

#endif /* WOLFSSL_ALT_NAMES */
#endif /* WOLFSSL_CERT_GEN */

Expand Down Expand Up @@ -31507,6 +31526,26 @@ int wc_SetAltNamesBuffer(Cert* cert, const byte* der, int derSz)
return(ret);
}

/* Set cert alt names from a linked list of alt-name entries (e.g. built with
* wc_SetDNSEntry()). Encodes the list into cert->altNames and stores the
* length in cert->altNamesSz. Returns 0 on success or a negative error code. */
int wc_SetAltNamesFromList(Cert* cert, const DNS_entry* names)
{
int ret;

if (cert == NULL) {
return BAD_FUNC_ARG;
}

ret = FlattenAltNames(cert->altNames, sizeof(cert->altNames), names);
if (ret < 0) {
return ret;
}

cert->altNamesSz = ret;
return 0;
}

/* Set cert dates from DER buffer */
WOLFSSL_ABI
int wc_SetDatesBuffer(Cert* cert, const byte* der, int derSz)
Expand Down
128 changes: 128 additions & 0 deletions wolfcrypt/test/test.c
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t certext_test(void);
defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN)
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t decodedCertCache_test(void);
#endif
#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES) && \
defined(WOLFSSL_ASN_TEMPLATE) && \
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN))
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t flattenAltNames_test(void);
#endif
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t memory_test(void);
#if defined(WOLFSSL_PUBLIC_MP) && \
((defined(WOLFSSL_SP_MATH_ALL) && !defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
Expand Down Expand Up @@ -3089,6 +3095,16 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\
TEST_PASS("DECODED CERT CACHE test passed!\n");
#endif

#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES) && \
defined(WOLFSSL_ASN_TEMPLATE) && \
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN))
if ( (ret = flattenAltNames_test()) != 0)
TEST_FAIL("FLATTEN ALT NAMES test failed!\n", ret);
else
TEST_PASS("FLATTEN ALT NAMES test passed!\n");
#endif

#ifdef HAVE_CURVE25519
if ( (ret = curve25519_test()) != 0)
TEST_FAIL("CURVE25519 test failed!\n", ret);
Expand Down Expand Up @@ -26485,6 +26501,118 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t decodedCertCache_test(void)
#endif /* defined(WOLFSSL_CERT_GEN_CACHE) && defined(WOLFSSL_TEST_CERT) &&
defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN) */

#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES) && \
defined(WOLFSSL_ASN_TEMPLATE) && \
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN))
/* Exercise the public wc_SetDNSEntry() + wc_FlattenAltNames() pair: build an
* alt-name list and encode it into a GeneralNames SEQUENCE. The order entries
* land in depends on build config (OPENSSL_EXTRA appends, otherwise prepends),
* so presence checks are order-independent. Also exercise the
* wc_SetAltNamesFromList() convenience that encodes straight into a Cert. */
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t flattenAltNames_test(void)
{
wc_test_ret_t ret = 0;
DNS_entry* list = NULL;
Cert* cert = NULL;
byte out[256];
int len;
/* dNSName "example.com" -> [2] IMPLICIT IA5String */
static const byte dnsTlv[] = {
0x82, 0x0B, 'e','x','a','m','p','l','e','.','c','o','m'
};
/* iPAddress 10.0.0.7 -> [7] IMPLICIT OCTET STRING */
static const byte ipTlv[] = { 0x87, 0x04, 0x0A, 0x00, 0x00, 0x07 };
static const byte ip[] = { 0x0A, 0x00, 0x00, 0x07 };
const int innerSz = (int)sizeof(dnsTlv) + (int)sizeof(ipTlv); /* 19 */
const int expSz = 2 + innerSz; /* 0x30,len + body */
int i, foundDns = 0, foundIp = 0;

WOLFSSL_ENTER("flattenAltNames_test");

/* A NULL list encodes to nothing. */
len = wc_FlattenAltNames(out, sizeof(out), NULL);
if (len != 0)
ret = WC_TEST_RET_ENC_EC(len);

if (ret == 0) {
ret = wc_SetDNSEntry(HEAP_HINT, "example.com", 11, ASN_DNS_TYPE, &list);
if (ret != 0)
ret = WC_TEST_RET_ENC_EC(ret);
}
if (ret == 0) {
ret = wc_SetDNSEntry(HEAP_HINT, (const char*)ip, (int)sizeof(ip),
ASN_IP_TYPE, &list);
if (ret != 0)
ret = WC_TEST_RET_ENC_EC(ret);
}
if (ret == 0) {
len = wc_FlattenAltNames(out, sizeof(out), list);
if (len != expSz)
ret = WC_TEST_RET_ENC_EC(len);
}
if (ret == 0 && (out[0] != ASN_SEQUENCE + ASN_CONSTRUCTED ||
out[1] != (byte)innerSz))
ret = WC_TEST_RET_ENC_NC;
/* Both GeneralName TLVs must be present, regardless of order. */
for (i = 0; ret == 0 && i + (int)sizeof(dnsTlv) <= len; i++) {
if (XMEMCMP(out + i, dnsTlv, sizeof(dnsTlv)) == 0)
foundDns = 1;
}
for (i = 0; ret == 0 && i + (int)sizeof(ipTlv) <= len; i++) {
if (XMEMCMP(out + i, ipTlv, sizeof(ipTlv)) == 0)
foundIp = 1;
}
if (ret == 0 && (!foundDns || !foundIp))
ret = WC_TEST_RET_ENC_NC;
/* NULL output is rejected. */
if (ret == 0) {
len = wc_FlattenAltNames(NULL, sizeof(out), list);
if (len != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
ret = WC_TEST_RET_ENC_EC(len);
}
/* Output one byte too small is rejected with BUFFER_E. */
if (ret == 0) {
len = wc_FlattenAltNames(out, (word32)expSz - 1, list);
if (len != WC_NO_ERR_TRACE(BUFFER_E))
ret = WC_TEST_RET_ENC_EC(len);
}

/* wc_SetAltNamesFromList() encodes the same list straight into a Cert and
* records the length; the result must match the standalone encoding. */
if (ret == 0) {
cert = (Cert*)XMALLOC(sizeof(Cert), HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
if (cert == NULL)
ret = WC_TEST_RET_ENC_EC(MEMORY_E);
}
if (ret == 0) {
ret = wc_InitCert_ex(cert, HEAP_HINT, devId);
if (ret != 0)
ret = WC_TEST_RET_ENC_EC(ret);
}
if (ret == 0) {
ret = wc_SetAltNamesFromList(cert, list);
if (ret != 0)
ret = WC_TEST_RET_ENC_EC(ret);
}
if (ret == 0 && (cert->altNamesSz != expSz ||
XMEMCMP(cert->altNames, out, (size_t)expSz) != 0))
ret = WC_TEST_RET_ENC_NC;
/* NULL cert is rejected. */
if (ret == 0) {
int r = wc_SetAltNamesFromList(NULL, list);
if (r != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
ret = WC_TEST_RET_ENC_EC(r);
}

XFREE(cert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
FreeAltNames(list, HEAP_HINT);
return ret;
}
#endif /* WOLFSSL_CERT_GEN && WOLFSSL_ALT_NAMES && WOLFSSL_ASN_TEMPLATE &&
* (WOLFSSL_TEST_CERT || OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL ||
* WOLFSSL_PUBLIC_ASN) */

#define RSA_TEST_BYTES (RSA_MAX_SIZE / 8)

#if !defined(NO_ASN) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \
Expand Down
6 changes: 6 additions & 0 deletions wolfcrypt/test/test.h
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,12 @@ extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t certext_test(void);
defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN)
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t decodedCertCache_test(void);
#endif
#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES) && \
defined(WOLFSSL_ASN_TEMPLATE) && \
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN))
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t flattenAltNames_test(void);
#endif
extern WOLFSSL_TEST_SUBROUTINE wc_test_ret_t memory_test(void);
#if defined(WOLFSSL_PUBLIC_MP) && \
((defined(WOLFSSL_SP_MATH_ALL) && !defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
Expand Down
10 changes: 10 additions & 0 deletions wolfssl/wolfcrypt/asn.h
Original file line number Diff line number Diff line change
Expand Up @@ -2390,6 +2390,11 @@ WOLFSSL_LOCAL int StreamOctetString(const byte* inBuf, word32 inBufSz,
WOLFSSL_ASN_API void FreeAltNames(DNS_entry* altNames, void* heap);
WOLFSSL_ASN_API DNS_entry* AltNameNew(void* heap);
WOLFSSL_ASN_API DNS_entry* AltNameDup(DNS_entry* from, void* heap);
#if defined(WOLFSSL_ASN_TEMPLATE) && \
(defined(WOLFSSL_CERT_GEN) || !defined(NO_CERTS))
WOLFSSL_ASN_API int wc_SetDNSEntry(void* heap, const char* str, int strLen,
int type, DNS_entry** entries);
#endif
#ifndef IGNORE_NAME_CONSTRAINTS
WOLFSSL_ASN_API void FreeNameSubtrees(Base_entry* names, void* heap);
#endif /* IGNORE_NAME_CONSTRAINTS */
Expand Down Expand Up @@ -2677,6 +2682,11 @@ WOLFSSL_API int wc_DhPublicKeyDecode(const byte* input, word32* inOutIdx,
#endif
WOLFSSL_LOCAL int FlattenAltNames(byte* output, word32 outputSz,
const DNS_entry* names);
#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES)
WOLFSSL_ASN_API int wc_FlattenAltNames(byte* output, word32 outputSz,
const DNS_entry* names);
WOLFSSL_ASN_API int wc_SetAltNamesFromList(Cert* cert, const DNS_entry* names);
#endif

WOLFSSL_LOCAL int wc_EncodeName(EncodedName* name, const char* nameStr,
char nameType, byte type);
Expand Down
Loading