Skip to content
Merged
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
149 changes: 149 additions & 0 deletions tests/api/test_asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@

#include <tests/api/test_asn.h>

#include <wolfssl/wolfcrypt/asn.h>

#if defined(WC_ENABLE_ASYM_KEY_EXPORT) && defined(HAVE_ED25519)
static int test_SetAsymKeyDer_once(byte* privKey, word32 privKeySz, byte* pubKey,
word32 pubKeySz, byte* trueDer, word32 trueDerSz)
Expand Down Expand Up @@ -638,3 +640,150 @@ int test_wc_IndexSequenceOf(void)

return EXPECT_RESULT();
}

int test_wolfssl_local_MatchBaseName(void)
{
EXPECT_DECLS;

#if !defined(NO_CERTS) && !defined(NO_ASN) && !defined(IGNORE_NAME_CONSTRAINTS)
/*
* Tests for DNS type (ASN_DNS_TYPE = 0x02)
*/

/* Positive tests - should match */
/* Exact match */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"domain.com", 10, "domain.com", 10), 1);
/* Case insensitive match */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"DOMAIN.COM", 10, "domain.com", 10), 1);
/* Subdomain match (RFC 5280: adding labels to the left) */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"sub.domain.com", 14, "domain.com", 10), 1);
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"a.b.domain.com", 14, "domain.com", 10), 1);
/* Leading dot constraint with subdomain (not RFC 5280 compliant for DNS,
* but kept for backwards compatibility) */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"sub.domain.com", 14, ".domain.com", 11), 1);
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"a.b.domain.com", 14, ".domain.com", 11), 1);

/* Negative tests - should NOT match */
/* Bug #3: fakedomain.com should NOT match domain.com (no dot boundary) */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"fakedomain.com", 14, "domain.com", 10), 0);
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"notdomain.com", 13, "domain.com", 10), 0);
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"xexample.com", 12, "example.com", 11), 0);
/* Bug #3: fakedomain.com should NOT match .domain.com */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"fakedomain.com", 14, ".domain.com", 11), 0);
/* domain.com should NOT match .domain.com (leading dot requires subdomain) */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"domain.com", 10, ".domain.com", 11), 0);
/* Different domain */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"other.com", 9, "domain.com", 10), 0);
/* Name starting with dot */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
".domain.com", 11, "domain.com", 10), 0);

/*
* Tests for email type (ASN_RFC822_TYPE = 0x01)
*/

/* Positive tests - should match */
/* Exact email match */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 15, "[email protected]", 15), 1);
/* Email with domain constraint (leading dot) - subdomain present */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 19, ".domain.com", 11), 1);
/* Email with domain constraint (no leading dot) - exact domain */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 15, "domain.com", 10), 1);

/* Negative tests - should NOT match */
/* [email protected] should NOT match .domain.com (subdomain required) */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 15, ".domain.com", 11), 0);
/* [email protected] should NOT match domain.com (exact domain only) */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 19, "domain.com", 10), 0);
/* @ at start is invalid */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"@domain.com", 11, ".domain.com", 11), 0);
/* @ at end is invalid */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"user@", 5, ".domain.com", 11), 0);
/* double @ is invalid */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"user@@domain.com", 16, ".domain.com", 11), 0);
/* multiple @ is invalid */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"user@[email protected]", 21, ".domain.com", 11), 0);
/* No @ in email name */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"userdomain.com", 14, ".domain.com", 11), 0);
/* Email domain doesn't match constraint */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 14, ".domain.com", 11), 0);
/* Email suffix without dot boundary (fakedomain) */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 19, ".domain.com", 11), 0);
/* Base constraint with invalid @ position */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 15, "@domain.com", 11), 0);
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_RFC822_TYPE,
"[email protected]", 15, "user@", 5), 0);

/*
* Tests for directory type (ASN_DIR_TYPE = 0x04)
*/

/* Positive tests - should match */
/* Exact match */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DIR_TYPE,
"CN=test", 7, "CN=test", 7), 1);
/* Prefix match (name longer than base) */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DIR_TYPE,
"CN=test,O=org", 13, "CN=test", 7), 1);

/* Negative tests - should NOT match */
/* Different content */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DIR_TYPE,
"CN=other", 8, "CN=test", 7), 0);
/* Case sensitive for directory */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DIR_TYPE,
"CN=TEST", 7, "CN=test", 7), 0);

/*
* Edge cases and error handling
*/

/* NULL pointers */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
NULL, 10, "domain.com", 10), 0);
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"domain.com", 10, NULL, 10), 0);
/* Empty/zero size */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"", 0, "domain.com", 10), 0);
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"domain.com", 10, "", 0), 0);
/* Invalid type */
ExpectIntEQ(wolfssl_local_MatchBaseName(0xFF,
"domain.com", 10, "domain.com", 10), 0);
/* Name starting with dot */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
".", 1, ".", 1), 0);
/* Name shorter than base */
ExpectIntEQ(wolfssl_local_MatchBaseName(ASN_DNS_TYPE,
"a.com", 5, "domain.com", 10), 0);

#endif /* !NO_CERTS && !NO_ASN && !IGNORE_NAME_CONSTRAINTS */

return EXPECT_RESULT();
}
4 changes: 3 additions & 1 deletion tests/api/test_asn.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,12 @@
int test_SetAsymKeyDer(void);
int test_GetSetShortInt(void);
int test_wc_IndexSequenceOf(void);
int test_wolfssl_local_MatchBaseName(void);

#define TEST_ASN_DECLS \
TEST_DECL_GROUP("asn", test_SetAsymKeyDer), \
TEST_DECL_GROUP("asn", test_GetSetShortInt), \
TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf)
TEST_DECL_GROUP("asn", test_wc_IndexSequenceOf), \
TEST_DECL_GROUP("asn", test_wolfssl_local_MatchBaseName)

#endif /* WOLFCRYPT_TEST_ASN_H */
68 changes: 46 additions & 22 deletions wolfcrypt/src/asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -19315,8 +19315,8 @@ int ConfirmSignature(SignatureCtx* sigCtx,

#ifndef IGNORE_NAME_CONSTRAINTS

static int MatchBaseName(int type, const char* name, int nameSz,
const char* base, int baseSz)
int wolfssl_local_MatchBaseName(int type, const char* name, int nameSz,
const char* base, int baseSz)
{
if (base == NULL || baseSz <= 0 || name == NULL || nameSz <= 0 ||
name[0] == '.' || nameSz < baseSz ||
Expand All @@ -19333,6 +19333,8 @@ static int MatchBaseName(int type, const char* name, int nameSz,
if (type == ASN_RFC822_TYPE) {
const char* p = NULL;
int count = 0;
int baseIsEmail = 0;
int atPos = -1;

if (base[0] != '.') {
p = base;
Expand All @@ -19344,39 +19346,61 @@ static int MatchBaseName(int type, const char* name, int nameSz,
p++;
}

/* No '@' in base, reset p to NULL */
if (count >= baseSz)
p = NULL;
if (count < baseSz) {
/* '@' found in base - validate it's not at start/end and only one */
if (count == 0 || count == baseSz - 1)
return 0; /* '@' at start or end of base is invalid */
baseIsEmail = 1;
}
}

if (p == NULL) {
/* Base isn't an email address, it is a domain name,
* wind the name forward one character past its '@'. */
p = name;
count = 0;
while (*p != '@' && count < baseSz) {
count++;
p++;
/* verify that name is a valid email address, store @ position */
p = name;
count = 0;
while (count < nameSz) {
if (*p == '@') {
if (atPos >= 0)
return 0; /* Multiple '@' in name is invalid */
atPos = count;
}
count++;
p++;
}

if (count < baseSz && *p == '@') {
name = p + 1;
nameSz -= count + 1;
}
/* Validate '@' exists and is not at start or end */
if (atPos < 0 || atPos == 0 || atPos == nameSz - 1)
return 0;

if (!baseIsEmail) {
/* Base isn't an email address but a domain or host.
* wind the name forward one character past its '@'. */
name = name + atPos + 1;
nameSz -= atPos + 1;
}
}

/* RFC 5280 section 4.2.1.10
* "...Any DNS name that can be constructed by simply adding zero or more
* labels to the left-hand side of the name satisfies the name constraint."
* i.e www.host.example.com works for host.example.com name constraint and
* host1.example.com does not. */
* host1.example.com does not.
*
* Note: For DNS type, RFC 5280 does not allow leading dot in constraint.
* However, we accept it here for backwards compatibility. */
if (type == ASN_DNS_TYPE || (type == ASN_RFC822_TYPE && base[0] == '.')) {
int szAdjust = nameSz - baseSz;
/* Check dot boundary: if there's a prefix and base doesn't start with
* '.', the character before the matched suffix must be '.'.
* When base starts with '.', the dot is included in the comparison. */
if (szAdjust > 0 && base[0] != '.' && name[szAdjust - 1] != '.')
return 0;
name += szAdjust;
nameSz -= szAdjust;
}

if (nameSz != baseSz)
return 0;

while (nameSz > 0) {
if (XTOLOWER((unsigned char)*name) !=
XTOLOWER((unsigned char)*base))
Expand Down Expand Up @@ -19408,8 +19432,8 @@ static int PermittedListOk(DNS_entry* name, Base_entry* dnsList, byte nameType)
if (current->type == nameType) {
need = 1; /* restriction on permitted names is set for this type */
if (name->len >= current->nameSz &&
MatchBaseName(nameType, name->name, name->len,
current->name, current->nameSz)) {
wolfssl_local_MatchBaseName(nameType, name->name, name->len,
current->name, current->nameSz)) {
match = 1; /* found the current name in the permitted list*/
break;
}
Expand Down Expand Up @@ -19439,8 +19463,8 @@ static int IsInExcludedList(DNS_entry* name, Base_entry* dnsList, byte nameType)
while (current != NULL) {
if (current->type == nameType) {
if (name->len >= current->nameSz &&
MatchBaseName(nameType, name->name, name->len,
current->name, current->nameSz)) {
wolfssl_local_MatchBaseName(nameType, name->name, name->len,
current->name, current->nameSz)) {
ret = 1;
break;
}
Expand Down
6 changes: 6 additions & 0 deletions wolfssl/wolfcrypt/asn.h
Original file line number Diff line number Diff line change
Expand Up @@ -2789,6 +2789,12 @@ WOLFSSL_LOCAL int VerifyX509Acert(const byte* cert, word32 certSz,
#endif /* WOLFSSL_ACERT */


#ifndef IGNORE_NAME_CONSTRAINTS
WOLFSSL_TEST_VIS int wolfssl_local_MatchBaseName(int type, const char* name,
int nameSz, const char* base,
int baseSz);
#endif

#if ((defined(HAVE_ED25519) && defined(HAVE_ED25519_KEY_IMPORT)) \
|| (defined(HAVE_CURVE25519) && defined(HAVE_CURVE25519_KEY_IMPORT)) \
|| (defined(HAVE_ED448) && defined(HAVE_ED448_KEY_IMPORT)) \
Expand Down