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
20 changes: 14 additions & 6 deletions doc/dox_comments/header_files/asn.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,16 @@ void FreeAltNames(DNS_entry* altNames, void* heap);
\note This API is not public by default. Define WOLFSSL_PUBLIC_ASN to
expose APIs marked WOLFSSL_ASN_API.

\note The oid argument passed to the callback is an array of word32
elements, one per OID arc, so that arcs > 65535 are represented without
truncation. Callbacks must declare their oid parameter as const word32*.
The same applies to wc_SetUnknownExtCallback.

_Example_
\code
DecodedCert cert;

int UnknownExtCallback(const byte* oid, word32 oidSz, int crit,
int UnknownExtCallback(const word32* oid, word32 oidSz, int crit,
const byte* der, word32 derSz, void* ctx) {
// handle unknown extension
return 0;
Expand Down Expand Up @@ -140,28 +145,31 @@ int wc_CheckCertSignature(const byte* cert, word32 certSz, void* heap,

/*!
\ingroup ASN
\brief This function encodes an array of word16 values into an ASN.1
\brief This function encodes an array of word32 values into an ASN.1
Object Identifier (OID) in DER format. OIDs are used to identify
algorithms, extensions, and other objects in certificates and
cryptographic protocols.

Each OID arc is a word32, so OIDs containing arcs with values > 65535 are
represented without truncation.

\return 0 On success.
\return BAD_FUNC_ARG If in, inSz, or outSz are invalid.
\return BUFFER_E If out is not NULL and outSz is too small.

\param in pointer to array of word16 values representing OID components
\param in pointer to array of word32 values representing OID components
\param inSz number of components in the OID
\param out pointer to buffer to store encoded OID (can be NULL to
calculate size)
\param outSz pointer to size of out buffer; updated with actual size

_Example_
\code
word16 oid[] = {1, 2, 840, 113549, 1, 1, 11}; // sha256WithRSAEncryption
word32 oid[] = {1, 2, 840, 113549, 1, 1, 11}; // sha256WithRSAEncryption
byte encoded[32];
word32 encodedSz = sizeof(encoded);

int ret = wc_EncodeObjectId(oid, sizeof(oid)/sizeof(word16),
int ret = wc_EncodeObjectId(oid, sizeof(oid)/sizeof(*oid),
encoded, &encodedSz);
if (ret == 0) {
// encoded contains DER encoded OID
Expand All @@ -170,7 +178,7 @@ int wc_CheckCertSignature(const byte* cert, word32 certSz, void* heap,

\sa wc_BerToDer
*/
int wc_EncodeObjectId(const word16* in, word32 inSz, byte* out,
int wc_EncodeObjectId(const word32* in, word32 inSz, byte* out,
word32* outSz);

/*!
Expand Down
4 changes: 2 additions & 2 deletions doc/dox_comments/header_files/asn_public.h
Original file line number Diff line number Diff line change
Expand Up @@ -3953,7 +3953,7 @@ int wc_SetCustomExtension(Cert *cert, int critical, const char *oid,
\code
int ret = 0;
// Unknown extension callback prototype
int myUnknownExtCallback(const word16* oid, word32 oidSz, int crit,
int myUnknownExtCallback(const word32* oid, word32 oidSz, int crit,
const unsigned char* der, word32 derSz);

// Register it
Expand All @@ -3967,7 +3967,7 @@ int wc_SetCustomExtension(Cert *cert, int critical, const char *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,
int myCustomExtCallback(const word32* oid, word32 oidSz, int crit,
const unsigned char* der, word32 derSz) {

// Logic to parse extension goes here.
Expand Down
280 changes: 266 additions & 14 deletions tests/api/test_asn.c
Original file line number Diff line number Diff line change
Expand Up @@ -1624,36 +1624,49 @@ int test_SerialNumber0_RootCA(void)
return EXPECT_RESULT();
}

int test_wc_DecodeObjectId(void)

int test_wc_DecodeObjectId_FIPS16(void)
{
EXPECT_DECLS;

#if !defined(NO_ASN) && \
(defined(HAVE_OID_DECODING) || defined(WOLFSSL_ASN_PRINT))
{
/* OID 1.2.840.113549.1.1.11 (sha256WithRSAEncryption)
* DER encoding: 2a 86 48 86 f7 0d 01 01 0b
* First byte 0x2a = 42 => arc0 = 42/40 = 1, arc1 = 42%40 = 2
* Remaining arcs: 840, 113549, 1, 1, 11
word32 i;
static const word16 oid_dot_2[] = {
2, 100, 4, 6
};

static const byte oid_start_with_2[] = {
0x81, 0x34, 0x04, 0x06
};


/* OID 1.3.132.0.6 (secp112r1)
* DER encoding: 2b 81 04 00 06
* First byte 0x2b = 43 => arc0 = 43/40 = 1, arc1 = 43%40 = 3
* Remaining arcs: 132 0 6
*/
static const byte oid_sha256rsa[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
0x2B, 0x81, 0x04, 0x00, 0x06
};

static const word16 oid_dot_form[] = {
1U, 3U, 132U, 0U, 6U
};

word16 out[MAX_OID_SZ];
word32 outSz;

word32 trueOutSz = sizeof(oid_dot_form) / sizeof(*oid_dot_form);
/* Test 1: Normal decode */
outSz = MAX_OID_SZ;
ExpectIntEQ(DecodeObjectId(oid_sha256rsa, sizeof(oid_sha256rsa),
out, &outSz), 0);
ExpectIntEQ((int)outSz, 7);
ExpectIntEQ(out[0], 1);
ExpectIntEQ(out[1], 2);
ExpectIntEQ(out[2], 840);
ExpectIntEQ(out[3], (word16)113549); /* truncated to word16 */
ExpectIntEQ(out[4], 1);
ExpectIntEQ(out[5], 1);
ExpectIntEQ(out[6], 11);
ExpectIntEQ((int)outSz, trueOutSz);
for (i = 0; i < ((outSz <= trueOutSz) ? outSz : trueOutSz); i++) {
ExpectIntEQ(out[i], oid_dot_form[i]);
}

/* Test 2: NULL args */
outSz = MAX_OID_SZ;
Expand Down Expand Up @@ -1692,12 +1705,251 @@ int test_wc_DecodeObjectId(void)
ExpectIntEQ(DecodeObjectId(oid_sha256rsa, sizeof(oid_sha256rsa),
out, &outSz),
WC_NO_ERR_TRACE(BUFFER_E));

/* Test 7: first Arc is 2 */
{
word32 trueOutSz2 = sizeof(oid_dot_2) / sizeof(*oid_dot_2);
outSz = MAX_OID_SZ;
ExpectIntEQ(DecodeObjectId(oid_start_with_2,
sizeof(oid_start_with_2),
out, &outSz), 0);
ExpectIntEQ((int)outSz, trueOutSz2);
for (i = 0; i < ((outSz <= trueOutSz2) ?
outSz : trueOutSz2); i++) {
ExpectIntEQ(out[i], oid_dot_2[i]);
}
}

/* Test 8: an OID with an arc that exceeds word16. Tests that wrong
* but unchangeable behavior is working correctly,
*
* word16 version is used in FIPS build
*/
{
static const byte oid_large_arc[] = {
0x2a, 0x86, 0x48, 0x82, 0xf7, 0x0d, 0x01, 0x01, 0x0b
};
static const word16 oid_dot_large_arc[] = {
1U, 2U, 840U, (word16)113549U, 1U, 1U, 11U
};
word32 trueOutSz3 = sizeof(oid_dot_large_arc) / sizeof(word16);

outSz = MAX_OID_SZ;
ExpectIntEQ(DecodeObjectId(oid_large_arc, sizeof(oid_large_arc),
out, &outSz), 0);
ExpectIntEQ((int)outSz, (int)trueOutSz3);
for (i = 0; i < ((outSz <= trueOutSz3) ? outSz : trueOutSz3); i++) {
ExpectIntEQ(out[i], oid_dot_large_arc[i]);
}
#undef LARGE_ARC_EXPECTED
}
}
#endif /* !NO_ASN && (HAVE_OID_DECODING || WOLFSSL_ASN_PRINT) */

return EXPECT_RESULT();
}

/* Test for word16 FIPS version of function */
int test_wc_DecodeObjectId_ex(void)
{
EXPECT_DECLS;

#if !defined(NO_ASN) && \
(defined(HAVE_OID_DECODING) || defined(WOLFSSL_ASN_PRINT))
{
word32 i;
static const word32 oid_dot_2[] = {
2, 100, 4, 6
};

static const byte oid_start_with_2[] = {
0x81, 0x34, 0x04, 0x06
};


/* OID 1.3.132.0.6 (secp112r1)
* DER encoding: 2b 81 04 00 06
* First byte 0x2b = 43 => arc0 = 43/40 = 1, arc1 = 43%40 = 3
* Remaining arcs: 132 0 6
*/
static const byte oid_sha256rsa[] = {
0x2B, 0x81, 0x04, 0x00, 0x06
};

static const word32 oid_dot_form[] = {
1U, 3U, 132U, 0U, 6U
};

word32 out[MAX_OID_SZ];
word32 outSz;

word32 trueOutSz = sizeof(oid_dot_form) / sizeof(word32);
/* Test 1: Normal decode */
outSz = MAX_OID_SZ;
ExpectIntEQ(DecodeObjectId_ex(oid_sha256rsa, sizeof(oid_sha256rsa),
out, &outSz), 0);
ExpectIntEQ((int)outSz, trueOutSz);
for (i = 0; i < ((outSz <= trueOutSz) ? outSz : trueOutSz); i++) {
ExpectIntEQ(out[i], oid_dot_form[i]);
}

/* Test 2: NULL args */
outSz = MAX_OID_SZ;
ExpectIntEQ(DecodeObjectId_ex(NULL, sizeof(oid_sha256rsa), out, &outSz),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(DecodeObjectId_ex(oid_sha256rsa, sizeof(oid_sha256rsa),
out, NULL),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));

/* Test 3 (Bug 1): outSz=1 must return BUFFER_E, not OOB write.
* The first OID byte decodes into two arcs, so outSz must be >= 2. */
outSz = 1;
ExpectIntEQ(DecodeObjectId_ex(oid_sha256rsa, sizeof(oid_sha256rsa),
out, &outSz),
WC_NO_ERR_TRACE(BUFFER_E));

/* Test 4: outSz=0 must also return BUFFER_E */
outSz = 0;
ExpectIntEQ(DecodeObjectId_ex(oid_sha256rsa, sizeof(oid_sha256rsa),
out, &outSz),
WC_NO_ERR_TRACE(BUFFER_E));

/* Test 5: outSz=2 is enough for a single-byte OID (two arcs) */
{
static const byte oid_one_byte[] = { 0x2a }; /* 1.2 */
outSz = 2;
ExpectIntEQ(DecodeObjectId_ex(oid_one_byte, sizeof(oid_one_byte),
out, &outSz), 0);
ExpectIntEQ((int)outSz, 2);
ExpectIntEQ(out[0], 1);
ExpectIntEQ(out[1], 2);
}

/* Test 6: Buffer too small for later arcs */
outSz = 3; /* only room for 3 arcs, but OID has 7 */
ExpectIntEQ(DecodeObjectId_ex(oid_sha256rsa, sizeof(oid_sha256rsa),
out, &outSz),
WC_NO_ERR_TRACE(BUFFER_E));

/* Test 7: first Arc is 2 */
{
word32 trueOutSz2 = sizeof(oid_dot_2) / sizeof(word32);
outSz = MAX_OID_SZ;
ExpectIntEQ(DecodeObjectId_ex(oid_start_with_2,
sizeof(oid_start_with_2),
out, &outSz), 0);
ExpectIntEQ((int)outSz, trueOutSz2);
for (i = 0; i < ((outSz <= trueOutSz2) ?
outSz : trueOutSz2); i++) {
ExpectIntEQ(out[i], oid_dot_2[i]);
}
}

/* Test 8: an OID with an arc that exceeds word16.
*/
{
static const byte oid_large_arc[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
};
static const word32 oid_dot_large_arc[] = {
1U, 2U, 840U, 113549U, 1U, 1U, 11U
};
word32 trueOutSz3 = sizeof(oid_dot_large_arc) / sizeof(word32);

outSz = MAX_OID_SZ;
ExpectIntEQ(DecodeObjectId_ex(oid_large_arc, sizeof(oid_large_arc),
out, &outSz), 0);
ExpectIntEQ((int)outSz, (int)trueOutSz3);
for (i = 0; i < ((outSz <= trueOutSz3) ? outSz : trueOutSz3); i++) {
ExpectIntEQ(out[i], oid_dot_large_arc[i]);
}
#undef LARGE_ARC_EXPECTED
}
}
#endif /* !NO_ASN && (HAVE_OID_DECODING || WOLFSSL_ASN_PRINT) */

return EXPECT_RESULT();
}

int test_wc_EncodeObjectId(void)
{
EXPECT_DECLS;
#if defined(HAVE_OID_ENCODING) && !defined(NO_ASN)
{
/* 1.3.132.0.6 (secp112r1) -- every arc fits in word16, so this
* encodes identically in both build configs. */
static const word32 oid_small[] = { 1U, 3U, 132U, 0U, 6U };
static const byte oid_small_der[] = {
0x2b, 0x81, 0x04, 0x00, 0x06
};
const word32 oid_small_cnt = sizeof(oid_small) / sizeof(word32);
byte out[MAX_OID_SZ];
word32 outSz;
word32 i;

/* Test 1: length-only query (out == NULL) */
outSz = 0;
ExpectIntEQ(wc_EncodeObjectId(oid_small, oid_small_cnt, NULL, &outSz),
0);
ExpectIntEQ((int)outSz, (int)sizeof(oid_small_der));

/* Test 2: normal encode matches expected DER */
outSz = sizeof(out);
ExpectIntEQ(wc_EncodeObjectId(oid_small, oid_small_cnt, out, &outSz), 0);
ExpectIntEQ((int)outSz, (int)sizeof(oid_small_der));
for (i = 0; i < outSz && i < sizeof(oid_small_der); i++) {
ExpectIntEQ(out[i], oid_small_der[i]);
}

/* Test 3: NULL args */
outSz = sizeof(out);
ExpectIntEQ(wc_EncodeObjectId(NULL, oid_small_cnt, out, &outSz),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));
ExpectIntEQ(wc_EncodeObjectId(oid_small, oid_small_cnt, out, NULL),
WC_NO_ERR_TRACE(BAD_FUNC_ARG));

/* Test 4: output buffer too small */
outSz = 1;
ExpectIntEQ(wc_EncodeObjectId(oid_small, oid_small_cnt, out, &outSz),
WC_NO_ERR_TRACE(BUFFER_E));

/* Test 5 ): an arc > word16 can only be represented in the
*/
{
static const word32 oid_large[] = {
1U, 2U, 840U, 113549U, 1U, 1U, 11U
};
static const byte oid_large_der[] = {
0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x0b
};
const word32 oid_large_cnt = sizeof(oid_large) / sizeof(word32);

outSz = sizeof(out);
ExpectIntEQ(wc_EncodeObjectId(oid_large, oid_large_cnt, out, &outSz),
0);
ExpectIntEQ((int)outSz, (int)sizeof(oid_large_der));
for (i = 0; i < outSz && i < sizeof(oid_large_der); i++) {
ExpectIntEQ(out[i], oid_large_der[i]);
}

#if defined(HAVE_OID_DECODING) || defined(WOLFSSL_ASN_PRINT)
{
word32 dec[MAX_OID_SZ];
word32 decSz = MAX_OID_SZ;
ExpectIntEQ(DecodeObjectId_ex(out, outSz, dec, &decSz), 0);
ExpectIntEQ((int)decSz, (int)oid_large_cnt);
for (i = 0; i < decSz && i < oid_large_cnt; i++) {
ExpectIntEQ(dec[i], oid_large[i]);
}
}
#endif /* HAVE_OID_DECODING || WOLFSSL_ASN_PRINT */
}
}
#endif /* HAVE_OID_ENCODING && !NO_ASN */

return EXPECT_RESULT();
}

#if defined(HAVE_PKCS8) && !defined(NO_ASN) && \
(defined(WOLFSSL_TEST_CERT) || defined(OPENSSL_EXTRA) || \
defined(OPENSSL_EXTRA_X509_SMALL) || defined(WOLFSSL_PUBLIC_ASN)) && \
Expand Down
Loading
Loading