Skip to content

Commit 70a2aff

Browse files
committed
asn: add public alt-name list APIs
Expose the internal SAN-list helpers as public wc_ APIs so callers can build a DNS_entry list, encode it into a DER GeneralNames SEQUENCE, and populate a Cert directly: - wc_SetDNSEntry() - append a typed alt-name entry to a list - wc_FlattenAltNames() - encode a list into a buffer (thin wrapper) - wc_SetAltNamesFromList() - encode a list straight into cert->altNames/Sz Declarations live in asn.h (they use the DNS_entry type) and are gated by the existing WOLFSSL_ASN_API export macro; doxygen notes the WOLFSSL_PUBLIC_ASN/ OPENSSL_EXTRA export requirement. Adds a wolfCrypt test covering the success, NULL-list, NULL-output, BUFFER_E, and Cert paths.
1 parent ac01707 commit 70a2aff

4 files changed

Lines changed: 321 additions & 0 deletions

File tree

doc/dox_comments/header_files/asn_public.h

Lines changed: 144 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1067,6 +1067,150 @@ int wc_SetSubjectBuffer(Cert* cert, const byte* der, int derSz);
10671067
*/
10681068
int wc_SetAltNamesBuffer(Cert* cert, const byte* der, int derSz);
10691069

1070+
/*!
1071+
\ingroup ASN
1072+
1073+
\brief This function allocates a single subject alternative name (SAN)
1074+
entry, copies the supplied name into it, sets its GeneralName type and
1075+
length, and appends it to the linked list pointed to by entries. The name
1076+
is duplicated internally, so the caller's buffer need not outlive the call.
1077+
The resulting list can be encoded with wc_FlattenAltNames and must be freed
1078+
with FreeAltNames.
1079+
1080+
\return 0 Returned on success.
1081+
\return MEMORY_E Returned if dynamic memory allocation fails.
1082+
\return BAD_FUNC_ARG Returned for an ASN_IP_TYPE entry whose length is not a
1083+
valid IPv4/IPv6 address, or an ASN_RID_TYPE entry with malformed contents.
1084+
\return BUFFER_E Returned if the string representation of an ASN_IP_TYPE or
1085+
ASN_RID_TYPE entry does not fit its internal buffer.
1086+
\return Other negative error codes may propagate from generating the string
1087+
form of ASN_IP_TYPE and ASN_RID_TYPE entries.
1088+
1089+
\param heap pointer to the heap hint used for allocations (may be NULL)
1090+
\param str pointer to the name bytes (e.g. a DNS string, or raw IP octets
1091+
for ASN_IP_TYPE)
1092+
\param strLen length of str in bytes
1093+
\param type GeneralName type (e.g. ASN_DNS_TYPE, ASN_IP_TYPE,
1094+
ASN_RFC822_TYPE, ASN_URI_TYPE)
1095+
\param entries in/out pointer to the head of the alt-name linked list; a new
1096+
entry is appended
1097+
1098+
_Example_
1099+
\code
1100+
DNS_entry* list = NULL;
1101+
if (wc_SetDNSEntry(NULL, "example.com", 11, ASN_DNS_TYPE, &list) != 0) {
1102+
// error adding alt name
1103+
}
1104+
// ... encode with wc_FlattenAltNames, then:
1105+
FreeAltNames(list, NULL);
1106+
\endcode
1107+
1108+
\note This helper (along with wc_FlattenAltNames and FreeAltNames) is
1109+
exported from the library only when WOLFSSL_PUBLIC_ASN, OPENSSL_EXTRA,
1110+
OPENSSL_EXTRA_X509_SMALL, or WOLFSSL_TEST_CERT is defined; its prototype
1111+
lives in wolfssl/wolfcrypt/asn.h (not asn_public.h) because it uses the
1112+
DNS_entry type.
1113+
1114+
\sa wc_FlattenAltNames
1115+
\sa FreeAltNames
1116+
*/
1117+
int wc_SetDNSEntry(void* heap, const char* str, int strLen, int type,
1118+
DNS_entry** entries);
1119+
1120+
/*!
1121+
\ingroup ASN
1122+
1123+
\brief This function encodes a linked list of subject alternative name
1124+
entries into the DER GeneralNames SEQUENCE used as the value of the
1125+
subjectAltName certificate extension. The output is suitable for assigning
1126+
to Cert.altNames (with the return value stored in Cert.altNamesSz) prior to
1127+
signing.
1128+
1129+
\return >0 the number of bytes written to output (the full SEQUENCE,
1130+
including its tag and length).
1131+
\return 0 Returned when names is NULL (nothing to encode).
1132+
\return BAD_FUNC_ARG Returned if output is NULL.
1133+
\return BUFFER_E Returned if output is too small to hold the encoding.
1134+
1135+
\param output buffer that receives the DER GeneralNames SEQUENCE; size it to
1136+
hold the full extension value (e.g. CTC_MAX_ALT_SIZE)
1137+
\param outputSz capacity of output in bytes
1138+
\param names head of the alt-name linked list to encode (e.g. built with
1139+
wc_SetDNSEntry, or taken from a parsed DecodedCert.altNames)
1140+
1141+
_Example_
1142+
\code
1143+
Cert cert;
1144+
DNS_entry* list = NULL;
1145+
// ... populate list with wc_SetDNSEntry ...
1146+
int n = wc_FlattenAltNames(cert.altNames, sizeof(cert.altNames), list);
1147+
if (n < 0) {
1148+
// error encoding alt names
1149+
}
1150+
cert.altNamesSz = n;
1151+
FreeAltNames(list, NULL);
1152+
\endcode
1153+
1154+
\note This helper (along with wc_SetDNSEntry and FreeAltNames) is exported
1155+
from the library only when WOLFSSL_PUBLIC_ASN, OPENSSL_EXTRA,
1156+
OPENSSL_EXTRA_X509_SMALL, or WOLFSSL_TEST_CERT is defined; its prototype
1157+
lives in wolfssl/wolfcrypt/asn.h (not asn_public.h) because it uses the
1158+
DNS_entry type.
1159+
1160+
\sa wc_SetDNSEntry
1161+
\sa wc_SetAltNamesFromList
1162+
\sa FreeAltNames
1163+
\sa wc_SetAltNamesBuffer
1164+
*/
1165+
int wc_FlattenAltNames(byte* output, word32 outputSz, const DNS_entry* names);
1166+
1167+
/*!
1168+
\ingroup ASN
1169+
1170+
\brief This function encodes a linked list of subject alternative name
1171+
entries directly into a Cert structure, ready for signing. It is a
1172+
convenience wrapper around wc_FlattenAltNames: the list is encoded into
1173+
cert->altNames and the encoded length is stored in cert->altNamesSz, so the
1174+
caller does not have to manage the buffer or size bookkeeping. The supplied
1175+
list is not consumed and must still be freed by the caller with
1176+
FreeAltNames.
1177+
1178+
\return 0 Returned on success.
1179+
\return BAD_FUNC_ARG Returned if cert is NULL.
1180+
\return BUFFER_E Returned if the encoded names do not fit in cert->altNames.
1181+
1182+
\param cert pointer to the Cert whose altNames/altNamesSz fields are set
1183+
\param names head of the alt-name linked list to encode (e.g. built with
1184+
wc_SetDNSEntry, or taken from a parsed DecodedCert.altNames); may be NULL,
1185+
in which case cert->altNamesSz is set to 0
1186+
1187+
_Example_
1188+
\code
1189+
Cert cert;
1190+
DNS_entry* list = NULL;
1191+
wc_InitCert(&cert);
1192+
// ... populate list with wc_SetDNSEntry ...
1193+
if (wc_SetAltNamesFromList(&cert, list) != 0) {
1194+
// error encoding alt names
1195+
}
1196+
FreeAltNames(list, NULL);
1197+
// ... wc_MakeCert / wc_SignCert ...
1198+
\endcode
1199+
1200+
\note This helper (along with wc_SetDNSEntry, wc_FlattenAltNames, and
1201+
FreeAltNames) is exported from the library only when WOLFSSL_PUBLIC_ASN,
1202+
OPENSSL_EXTRA, OPENSSL_EXTRA_X509_SMALL, or WOLFSSL_TEST_CERT is defined;
1203+
its prototype lives in wolfssl/wolfcrypt/asn.h (not asn_public.h) because it
1204+
uses the DNS_entry type. Its DER-input sibling wc_SetAltNamesBuffer is
1205+
always exported.
1206+
1207+
\sa wc_SetDNSEntry
1208+
\sa wc_FlattenAltNames
1209+
\sa wc_SetAltNamesBuffer
1210+
\sa FreeAltNames
1211+
*/
1212+
int wc_SetAltNamesFromList(Cert* cert, const DNS_entry* names);
1213+
10701214
/*!
10711215
\ingroup ASN
10721216

wolfcrypt/src/asn.c

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14520,6 +14520,16 @@ static int SetDNSEntry(void* heap, const char* str, int strLen,
1452014520

1452114521
return ret;
1452214522
}
14523+
14524+
/* Public wrapper for SetDNSEntry(): allocate an alt-name entry that copies the
14525+
* given name, set its type/length, and append it to the linked list. The list
14526+
* is freed with FreeAltNames() and can be flattened with
14527+
* wc_FlattenAltNames(). */
14528+
int wc_SetDNSEntry(void* heap, const char* str, int strLen, int type,
14529+
DNS_entry** entries)
14530+
{
14531+
return SetDNSEntry(heap, str, strLen, type, entries);
14532+
}
1452314533
#endif
1452414534

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

26973+
/* Public wrapper for FlattenAltNames(): encode a linked list of alt-name
26974+
* entries into the DER GeneralNames SEQUENCE used as the subjectAltName
26975+
* extension value. Returns the encoded length, 0 for a NULL list, or a
26976+
* negative error code. */
26977+
int wc_FlattenAltNames(byte* output, word32 outputSz, const DNS_entry* names)
26978+
{
26979+
return FlattenAltNames(output, outputSz, names);
26980+
}
26981+
2696326982
#endif /* WOLFSSL_ALT_NAMES */
2696426983
#endif /* WOLFSSL_CERT_GEN */
2696526984

@@ -31507,6 +31526,26 @@ int wc_SetAltNamesBuffer(Cert* cert, const byte* der, int derSz)
3150731526
return(ret);
3150831527
}
3150931528

31529+
/* Set cert alt names from a linked list of alt-name entries (e.g. built with
31530+
* wc_SetDNSEntry()). Encodes the list into cert->altNames and stores the
31531+
* length in cert->altNamesSz. Returns 0 on success or a negative error code. */
31532+
int wc_SetAltNamesFromList(Cert* cert, const DNS_entry* names)
31533+
{
31534+
int ret;
31535+
31536+
if (cert == NULL) {
31537+
return BAD_FUNC_ARG;
31538+
}
31539+
31540+
ret = FlattenAltNames(cert->altNames, sizeof(cert->altNames), names);
31541+
if (ret < 0) {
31542+
return ret;
31543+
}
31544+
31545+
cert->altNamesSz = ret;
31546+
return 0;
31547+
}
31548+
3151031549
/* Set cert dates from DER buffer */
3151131550
WOLFSSL_ABI
3151231551
int wc_SetDatesBuffer(Cert* cert, const byte* der, int derSz)

wolfcrypt/test/test.c

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1035,6 +1035,12 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t certext_test(void);
10351035
defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN)
10361036
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t decodedCertCache_test(void);
10371037
#endif
1038+
#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES) && \
1039+
defined(WOLFSSL_ASN_TEMPLATE) && \
1040+
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
1041+
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN))
1042+
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t flattenAltNames_test(void);
1043+
#endif
10381044
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t memory_test(void);
10391045
#if defined(WOLFSSL_PUBLIC_MP) && \
10401046
((defined(WOLFSSL_SP_MATH_ALL) && !defined(WOLFSSL_RSA_VERIFY_ONLY)) || \
@@ -3089,6 +3095,16 @@ options: [-s max_relative_stack_bytes] [-m max_relative_heap_memory_bytes]\n\
30893095
TEST_PASS("DECODED CERT CACHE test passed!\n");
30903096
#endif
30913097

3098+
#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES) && \
3099+
defined(WOLFSSL_ASN_TEMPLATE) && \
3100+
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
3101+
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN))
3102+
if ( (ret = flattenAltNames_test()) != 0)
3103+
TEST_FAIL("FLATTEN ALT NAMES test failed!\n", ret);
3104+
else
3105+
TEST_PASS("FLATTEN ALT NAMES test passed!\n");
3106+
#endif
3107+
30923108
#ifdef HAVE_CURVE25519
30933109
if ( (ret = curve25519_test()) != 0)
30943110
TEST_FAIL("CURVE25519 test failed!\n", ret);
@@ -26485,6 +26501,118 @@ WOLFSSL_TEST_SUBROUTINE wc_test_ret_t decodedCertCache_test(void)
2648526501
#endif /* defined(WOLFSSL_CERT_GEN_CACHE) && defined(WOLFSSL_TEST_CERT) &&
2648626502
defined(WOLFSSL_CERT_EXT) && defined(WOLFSSL_CERT_GEN) */
2648726503

26504+
#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES) && \
26505+
defined(WOLFSSL_ASN_TEMPLATE) && \
26506+
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
26507+
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN))
26508+
/* Exercise the public wc_SetDNSEntry() + wc_FlattenAltNames() pair: build an
26509+
* alt-name list and encode it into a GeneralNames SEQUENCE. The order entries
26510+
* land in depends on build config (OPENSSL_EXTRA appends, otherwise prepends),
26511+
* so presence checks are order-independent. Also exercise the
26512+
* wc_SetAltNamesFromList() convenience that encodes straight into a Cert. */
26513+
WOLFSSL_TEST_SUBROUTINE wc_test_ret_t flattenAltNames_test(void)
26514+
{
26515+
wc_test_ret_t ret = 0;
26516+
DNS_entry* list = NULL;
26517+
Cert* cert = NULL;
26518+
byte out[256];
26519+
int len;
26520+
/* dNSName "example.com" -> [2] IMPLICIT IA5String */
26521+
static const byte dnsTlv[] = {
26522+
0x82, 0x0B, 'e','x','a','m','p','l','e','.','c','o','m'
26523+
};
26524+
/* iPAddress 10.0.0.7 -> [7] IMPLICIT OCTET STRING */
26525+
static const byte ipTlv[] = { 0x87, 0x04, 0x0A, 0x00, 0x00, 0x07 };
26526+
static const byte ip[] = { 0x0A, 0x00, 0x00, 0x07 };
26527+
const int innerSz = (int)sizeof(dnsTlv) + (int)sizeof(ipTlv); /* 19 */
26528+
const int expSz = 2 + innerSz; /* 0x30,len + body */
26529+
int i, foundDns = 0, foundIp = 0;
26530+
26531+
WOLFSSL_ENTER("flattenAltNames_test");
26532+
26533+
/* A NULL list encodes to nothing. */
26534+
len = wc_FlattenAltNames(out, sizeof(out), NULL);
26535+
if (len != 0)
26536+
ret = WC_TEST_RET_ENC_EC(len);
26537+
26538+
if (ret == 0) {
26539+
ret = wc_SetDNSEntry(HEAP_HINT, "example.com", 11, ASN_DNS_TYPE, &list);
26540+
if (ret != 0)
26541+
ret = WC_TEST_RET_ENC_EC(ret);
26542+
}
26543+
if (ret == 0) {
26544+
ret = wc_SetDNSEntry(HEAP_HINT, (const char*)ip, (int)sizeof(ip),
26545+
ASN_IP_TYPE, &list);
26546+
if (ret != 0)
26547+
ret = WC_TEST_RET_ENC_EC(ret);
26548+
}
26549+
if (ret == 0) {
26550+
len = wc_FlattenAltNames(out, sizeof(out), list);
26551+
if (len != expSz)
26552+
ret = WC_TEST_RET_ENC_EC(len);
26553+
}
26554+
if (ret == 0 && (out[0] != ASN_SEQUENCE + ASN_CONSTRUCTED ||
26555+
out[1] != (byte)innerSz))
26556+
ret = WC_TEST_RET_ENC_NC;
26557+
/* Both GeneralName TLVs must be present, regardless of order. */
26558+
for (i = 0; ret == 0 && i + (int)sizeof(dnsTlv) <= len; i++) {
26559+
if (XMEMCMP(out + i, dnsTlv, sizeof(dnsTlv)) == 0)
26560+
foundDns = 1;
26561+
}
26562+
for (i = 0; ret == 0 && i + (int)sizeof(ipTlv) <= len; i++) {
26563+
if (XMEMCMP(out + i, ipTlv, sizeof(ipTlv)) == 0)
26564+
foundIp = 1;
26565+
}
26566+
if (ret == 0 && (!foundDns || !foundIp))
26567+
ret = WC_TEST_RET_ENC_NC;
26568+
/* NULL output is rejected. */
26569+
if (ret == 0) {
26570+
len = wc_FlattenAltNames(NULL, sizeof(out), list);
26571+
if (len != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
26572+
ret = WC_TEST_RET_ENC_EC(len);
26573+
}
26574+
/* Output one byte too small is rejected with BUFFER_E. */
26575+
if (ret == 0) {
26576+
len = wc_FlattenAltNames(out, (word32)expSz - 1, list);
26577+
if (len != WC_NO_ERR_TRACE(BUFFER_E))
26578+
ret = WC_TEST_RET_ENC_EC(len);
26579+
}
26580+
26581+
/* wc_SetAltNamesFromList() encodes the same list straight into a Cert and
26582+
* records the length; the result must match the standalone encoding. */
26583+
if (ret == 0) {
26584+
cert = (Cert*)XMALLOC(sizeof(Cert), HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
26585+
if (cert == NULL)
26586+
ret = WC_TEST_RET_ENC_EC(MEMORY_E);
26587+
}
26588+
if (ret == 0) {
26589+
ret = wc_InitCert_ex(cert, HEAP_HINT, devId);
26590+
if (ret != 0)
26591+
ret = WC_TEST_RET_ENC_EC(ret);
26592+
}
26593+
if (ret == 0) {
26594+
ret = wc_SetAltNamesFromList(cert, list);
26595+
if (ret != 0)
26596+
ret = WC_TEST_RET_ENC_EC(ret);
26597+
}
26598+
if (ret == 0 && (cert->altNamesSz != expSz ||
26599+
XMEMCMP(cert->altNames, out, (size_t)expSz) != 0))
26600+
ret = WC_TEST_RET_ENC_NC;
26601+
/* NULL cert is rejected. */
26602+
if (ret == 0) {
26603+
int r = wc_SetAltNamesFromList(NULL, list);
26604+
if (r != WC_NO_ERR_TRACE(BAD_FUNC_ARG))
26605+
ret = WC_TEST_RET_ENC_EC(r);
26606+
}
26607+
26608+
XFREE(cert, HEAP_HINT, DYNAMIC_TYPE_TMP_BUFFER);
26609+
FreeAltNames(list, HEAP_HINT);
26610+
return ret;
26611+
}
26612+
#endif /* WOLFSSL_CERT_GEN && WOLFSSL_ALT_NAMES && WOLFSSL_ASN_TEMPLATE &&
26613+
* (WOLFSSL_TEST_CERT || OPENSSL_EXTRA || OPENSSL_EXTRA_X509_SMALL ||
26614+
* WOLFSSL_PUBLIC_ASN) */
26615+
2648826616
#define RSA_TEST_BYTES (RSA_MAX_SIZE / 8)
2648926617

2649026618
#if !defined(NO_ASN) && !defined(WOLFSSL_RSA_PUBLIC_ONLY) && \

wolfssl/wolfcrypt/asn.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2390,6 +2390,11 @@ WOLFSSL_LOCAL int StreamOctetString(const byte* inBuf, word32 inBufSz,
23902390
WOLFSSL_ASN_API void FreeAltNames(DNS_entry* altNames, void* heap);
23912391
WOLFSSL_ASN_API DNS_entry* AltNameNew(void* heap);
23922392
WOLFSSL_ASN_API DNS_entry* AltNameDup(DNS_entry* from, void* heap);
2393+
#if defined(WOLFSSL_ASN_TEMPLATE) && \
2394+
(defined(WOLFSSL_CERT_GEN) || !defined(NO_CERTS))
2395+
WOLFSSL_ASN_API int wc_SetDNSEntry(void* heap, const char* str, int strLen,
2396+
int type, DNS_entry** entries);
2397+
#endif
23932398
#ifndef IGNORE_NAME_CONSTRAINTS
23942399
WOLFSSL_ASN_API void FreeNameSubtrees(Base_entry* names, void* heap);
23952400
#endif /* IGNORE_NAME_CONSTRAINTS */
@@ -2677,6 +2682,11 @@ WOLFSSL_API int wc_DhPublicKeyDecode(const byte* input, word32* inOutIdx,
26772682
#endif
26782683
WOLFSSL_LOCAL int FlattenAltNames(byte* output, word32 outputSz,
26792684
const DNS_entry* names);
2685+
#if defined(WOLFSSL_CERT_GEN) && defined(WOLFSSL_ALT_NAMES)
2686+
WOLFSSL_ASN_API int wc_FlattenAltNames(byte* output, word32 outputSz,
2687+
const DNS_entry* names);
2688+
WOLFSSL_ASN_API int wc_SetAltNamesFromList(Cert* cert, const DNS_entry* names);
2689+
#endif
26802690

26812691
WOLFSSL_LOCAL int wc_EncodeName(EncodedName* name, const char* nameStr,
26822692
char nameType, byte type);

0 commit comments

Comments
 (0)