ASN.1 print: implementation to parse and print added

New API to parse and print DER/BER data from a buffer.
Add an example to parse DER, Base64 and PEM files and print out ASN.1 items.
pull/6352/head
Sean Parkinson 2023-04-28 12:10:05 +10:00
parent f8559b745e
commit 9cdee20a7d
11 changed files with 1679 additions and 83 deletions

1
.gitignore vendored
View File

@ -73,6 +73,7 @@ examples/sctp/sctp-server
examples/sctp/sctp-server-dtls
examples/sctp/sctp-client
examples/sctp/sctp-client-dtls
examples/asn1/asn1
server_ready
snifftest
output

View File

@ -4016,6 +4016,26 @@ else
ENABLED_BIGNUM="yes"
fi
case $host_os in
*linux* | *darwin* | *freebsd*)
DEF_ASN_PRINT="yes"
;;
*)
DEF_ASN_PRINT="no"
;;
esac
AC_ARG_ENABLE([asn-print],
[AS_HELP_STRING([--enable-asn-print],[Enable ASN Print API (default: enabled)])],
[ ENABLED_ASN_PRINT=$enableval ],
[ ENABLED_ASN_PRINT=$DEF_ASN_PRINT ]
)
if test "$ENABLED_ASN_PRINT" = "yes"
then
AM_CFLAGS="$AM_CFLAGS -DWOLFSSL_ASN_PRINT"
fi
# AES
AC_ARG_ENABLE([aes],
@ -8496,6 +8516,7 @@ AM_CONDITIONAL([BUILD_FASTMATH],[test "x$ENABLED_FASTMATH" = "xyes" || test "x$E
AM_CONDITIONAL([BUILD_HEAPMATH],[test "x$ENABLED_HEAPMATH" = "xyes" || test "x$ENABLED_USERSETTINGS" = "xyes"])
AM_CONDITIONAL([BUILD_EXAMPLE_SERVERS],[test "x$ENABLED_EXAMPLES" = "xyes" && test "x$ENABLED_LEANTLS" = "xno"])
AM_CONDITIONAL([BUILD_EXAMPLE_CLIENTS],[test "x$ENABLED_EXAMPLES" = "xyes"])
AM_CONDITIONAL([BUILD_EXAMPLE_ASN1],[test "x$ENABLED_EXAMPLES" = "xyes"] && [test "x$ENABLED_ASN_PRINT" = "xyes"] && [test "x$ENABLED_ASN" = "xyes"])
AM_CONDITIONAL([BUILD_TESTS],[test "x$ENABLED_EXAMPLES" = "xyes"])
AM_CONDITIONAL([BUILD_THREADED_EXAMPLES],[test "x$ENABLED_SINGLETHREADED" = "xno" && test "x$ENABLED_EXAMPLES" = "xyes" && test "x$ENABLED_LEANTLS" = "xno"])
AM_CONDITIONAL([BUILD_WOLFCRYPT_TESTS],[test "x$ENABLED_CRYPT_TESTS" = "xyes"])

View File

@ -2167,3 +2167,148 @@ int wc_SetUnknownExtCallback(DecodedCert* cert,
int wc_CheckCertSigPubKey(const byte* cert, word32 certSz,
void* heap, const byte* pubKey,
word32 pubKeySz, int pubKeyOID);
/*!
\ingroup ASN
\brief This function initializes the ASN.1 print options.
\return 0 on success.
\return BAD_FUNC_ARG when asn1 is NULL.
\param opts The ASN.1 options for printing.
_Example_
\code
Asn1PrintOptions opt;
// Initialize ASN.1 print options before use.
wc_Asn1PrintOptions_Init(&opt);
\endcode
\sa wc_Asn1PrintOptions_Set
\sa wc_Asn1_PrintAll
*/
int wc_Asn1PrintOptions_Init(Asn1PrintOptions* opts);
/*!
\ingroup ASN
\brief This function sets a print option into an ASN.1 print options object.
\return 0 on success.
\return BAD_FUNC_ARG when asn1 is NULL.
\return BAD_FUNC_ARG when val is out of range for option.
\param opts The ASN.1 options for printing.
\param opt An option to set value for.
\param val The value to set.
_Example_
\code
Asn1PrintOptions opt;
// Initialize ASN.1 print options before use.
wc_Asn1PrintOptions_Init(&opt);
// Set the number of indents when printing tag name to be 1.
wc_Asn1PrintOptions_Set(&opt, ASN1_PRINT_OPT_INDENT, 1);
\endcode
\sa wc_Asn1PrintOptions_Init
\sa wc_Asn1_PrintAll
*/
int wc_Asn1PrintOptions_Set(Asn1PrintOptions* opts, enum Asn1PrintOpt opt,
word32 val);
/*!
\ingroup ASN
\brief This function initializes an ASN.1 parsing object.
\return 0 on success.
\return BAD_FUNC_ARG when asn1 is NULL.
\param asn1 ASN.1 parse object.
_Example_
\code
Asn1 asn1;
// Initialize ASN.1 parse object before use.
wc_Asn1_Init(&asn1);
\endcode
\sa wc_Asn1_SetFile
\sa wc_Asn1_PrintAll
*/
int wc_Asn1_Init(Asn1* asn1);
/*!
\ingroup ASN
\brief This function sets the file to use when printing into an ASN.1
parsing object.
\return 0 on success.
\return BAD_FUNC_ARG when asn1 is NULL.
\return BAD_FUNC_ARG when file is XBADFILE.
\param asn1 The ASN.1 parse object.
\param file File to print to.
_Example_
\code
Asn1 asn1;
// Initialize ASN.1 parse object before use.
wc_Asn1_Init(&asn1);
// Set standard out to be the file descriptor to write to.
wc_Asn1_SetFile(&asn1, stdout);
\endcode
\sa wc_Asn1_Init
\sa wc_Asn1_PrintAll
*/
int wc_Asn1_SetFile(Asn1* asn1, XFILE file);
/*!
\ingroup ASN
\brief Print all ASN.1 items.
\return 0 on success.
\return BAD_FUNC_ARG when asn1 or opts is NULL.
\return ASN_LEN_E when ASN.1 item's length too long.
\return ASN_DEPTH_E when end offset invalid.
\return ASN_PARSE_E when not all of an ASN.1 item parsed.
\param asn1 The ASN.1 parse object.
\param opts The ASN.1 print options.
\param data Buffer containing BER/DER data to print.
\param len Length of data to print in bytes.
\code
Asn1PrintOptions opts;
Asn1 asn1;
unsigned char data[] = { Initialize with DER/BER data };
word32 len = sizeof(data);
// Initialize ASN.1 print options before use.
wc_Asn1PrintOptions_Init(&opt);
// Set the number of indents when printing tag name to be 1.
wc_Asn1PrintOptions_Set(&opt, ASN1_PRINT_OPT_INDENT, 1);
// Initialize ASN.1 parse object before use.
wc_Asn1_Init(&asn1);
// Set standard out to be the file descriptor to write to.
wc_Asn1_SetFile(&asn1, stdout);
// Print all ASN.1 items in buffer with the specified print options.
wc_Asn1_PrintAll(&asn1, &opts, data, len);
\endcode
\sa wc_Asn1_Init
\sa wc_Asn1_SetFile
*/
int wc_Asn1_PrintAll(Asn1* asn1, Asn1PrintOptions* opts, unsigned char* data,
word32 len);

View File

@ -0,0 +1,494 @@
/* asn1.c
*
* Copyright (C) 2006-2023 wolfSSL Inc.
*
* This file is part of wolfSSL.
*
* wolfSSL is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* wolfSSL is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifndef WOLFSSL_USER_SETTINGS
#include <wolfssl/options.h>
#endif
#include <wolfssl/wolfcrypt/settings.h>
#include <wolfssl/wolfcrypt/asn_public.h>
#include <wolfssl/wolfcrypt/coding.h>
#include <wolfssl/wolfcrypt/error-crypt.h>
#ifdef WOLFSSL_ASN_PRINT
/* Increment allocated data by this much. */
#define DATA_INC_LEN 256
/* File format is DER/BER. */
#define FORMAT_DER 0
/* File format is BASE64. */
#define FORMAT_BASE64 1
/* File format is PEM. */
#define FORMAT_PEM 2
/* ASN.1 print options. */
static Asn1PrintOptions opts;
/* ASN.1 parsing state. */
static Asn1 asn1;
/* Read the contents of a file into a dynamically allocated buffer.
*
* Uses realloc as input may be stdin.
*
* @param [in] fp File pointer to read from.
* @param [out] pdata Pointer to data.
* @param [out] plen Pointer to length.
* @return 0 on success.
* @return 1 on failure.
*/
static int ReadFile(FILE* fp, unsigned char** pdata, word32* plen)
{
int ret = 0;
word32 len = 0;
size_t read_len;
/* Allocate a minimum amount. */
unsigned char* data = (unsigned char*)malloc(DATA_INC_LEN);
if (data != NULL) {
/* Read more data. */
while ((read_len = fread(data + len, 1, DATA_INC_LEN, fp)) != 0) {
unsigned char* p;
/* Add read data amount to length. */
len += (word32)read_len;
/* Stop if we are at end-of-file. */
if (feof(fp)) {
break;
}
/* Make space for more data to be added to buffer. */
p = (unsigned char*)realloc(data, len + DATA_INC_LEN);
if (p == NULL) {
/* Reallocation failed - free current buffer. */
free(data);
data = NULL;
break;
}
/* Set data to new pointer. */
data = p;
}
/* Done with file. */
fclose(fp);
}
if (data != NULL) {
/* Return data and length. */
*pdata = data;
*plen = len;
}
else {
/* Failed to allocate data. */
ret = MEMORY_E;
}
return ret;
}
/* Print ASN.1 of a file containing BER/DER data.
*
* @param [in] fp File pointer to read from.
* @return 0 on success.
* @return 1 on failure.
*/
static int PrintDer(FILE* fp)
{
int ret = 0;
word32 len = 0;
unsigned char* data = NULL;
/* Load DER/BER file. */
if (ReadFile(fp, &data, &len) != 0) {
ret = 1;
}
if ((ret == 0) && (data != NULL)) {
/* Print DER/BER. */
ret = wc_Asn1_PrintAll(&asn1, &opts, data, len);
/* Dispose of buffer. */
free(data);
}
return ret;
}
/* Print ASN.1 of a file containing Base64 encoding of BER/DER data.
*
* @param [in] fp File pointer to read from.
* @return 0 on success.
* @return 1 on failure.
*/
static int PrintBase64(FILE* fp)
{
int ret = 0;
word32 len = 0;
unsigned char* data = NULL;
/* Load Base64 encoded file. */
if (ReadFile(fp, &data, &len) != 0) {
ret = 1;
}
if ((ret == 0) && (data != NULL)) {
/* Decode Base64. */
if (Base64_Decode(data, len, data, &len) != 0) {
fprintf(stderr, "Invalid Base64 encoding\n");
ret = 1;
}
if (ret == 0) {
/* Print DER/BER. */
ret = wc_Asn1_PrintAll(&asn1, &opts, data, len);
}
/* Dispose of buffer. */
free(data);
}
return ret;
}
/* Find the next PEM block.
*
* @param [in] data PEM data.
* @param [in] offset Offset into data to start looking.
* @param [in] len Length of PEM data.
* @param [out] start Start of Base64 encoding.
* @param [out] end End of Base64 encoding.
*/
static int FindPem(unsigned char* data, word32 offset, word32 len,
word32* start, word32* end)
{
int ret = 0;
word32 i;
word32 j;
/* Find header. */
for (i = offset; i < len; i++) {
if ((data[i] == '-') &&
(strncmp((char*)data + i, "-----BEGIN", 10) == 0)) {
break;
}
}
if (i == len) {
/* Got to end without finding PEM header. */
fprintf(stderr, "No PEM header found\n");
ret = 1;
}
if (ret == 0) {
/* Confirm header. */
for (i += 10; i < len; i++) {
if ((data[i] == '-') &&
(strncmp((char*)data + i, "-----", 5) == 0)) {
break;
}
}
if (i == len) {
/* Got to end without finding rest of PEM header. */
fprintf(stderr, "Invalid PEM header\n");
ret = 1;
}
}
if (ret == 0) {
/* Find footer. */
i += 6;
for (j = i + 1; j < len; j++) {
if ((data[j] == '-') &&
(strncmp((char*)data + j, "-----END", 8) == 0)) {
break;
}
}
if (j == len) {
/* Got to end without finding PEM footer. */
fprintf(stderr, "No PEM footer found\n");
ret = 1;
}
}
if (ret == 0) {
/* Return start and end indeces. */
*start = i;
*end = j;
}
return ret;
}
/* Print ASN.1 of file containing PEM.
*
* Only one block is printed.
*
* @param [in] fp File pointer to read from.
* @param [in] pem_skip Number of PEM blocks to skip.
* @return 0 on success.
* @return 1 on failure.
*/
static int PrintPem(FILE* fp, int pem_skip)
{
int ret = 0;
unsigned char* data = NULL;
word32 len = 0;
/* Load PEM file. */
if (ReadFile(fp, &data, &len) != 0) {
ret = 1;
}
if ((ret == 0) && (data != NULL)) {
word32 i = 0;
word32 j = 0;
/* Find PEM blocks and skip number requested. */
do {
/* Find start and end of PEM Base64 data. */
ret = FindPem(data, j, len, &i, &j);
} while ((ret == 0) && ((pem_skip--) != 0));
/* Decode data between header and footer. */
if ((ret == 0) && (Base64_Decode(data + i, j - i, data, &len) != 0)) {
fprintf(stderr, "Invalid Base64 encoding\n");
ret = 1;
}
if (ret == 0) {
/* Print DER/BER. */
ret = wc_Asn1_PrintAll(&asn1, &opts, data, len);
}
/* Dispose of buffer. */
free(data);
}
return ret;
}
/* Usage lines to show. */
const char* usage[] = {
"asn1 [OPTOIN]... [FILE]",
"Display a human-readable version of a DER/BER encoding.",
"",
"Options:",
" -?, --help display this help and exit",
" -b, --branch draw branches before tag name",
" -B, --base64 file contents are Base64 encoded",
" -d, --dump show all ASN.1 item data as a hex dump",
" -h, --headers show all ASN.1 item headers as a hex dump",
" -i, --indent indent tag name with depth",
" -l, --length LEN display length bytes of data",
" -n, --no-text do not show data as text",
" -N, --no-dump-text do not show data as a hex dump text",
" -o, --offset OFFSET start decoding from offset",
" -O, --oid show wolfSSL OID value in text",
" -p, --pem file contents are PEM",
" -s, --skip-pem NUM number of PEM blocks to skip",
};
/* Number of usage lines. */
#define USAGE_SZ ((int)(sizeof(usage) / sizeof(*usage)))
/* Print out usage lines.
*/
static void Usage(void)
{
int i;
for (i = 0; i < USAGE_SZ; i++) {
printf("%s\n", usage[i]);
}
}
/* Main entry of ASN.1 printing program.
*
* @param [in] argc Count of command line argements.
* @param [in] argv Command line argements.
* @return 0 on success.
* @return 1 on failure.
*/
int main(int argc, char* argv[])
{
int ret = 0;
/* Default to reading STDIN. */
FILE* fp = stdin;
int file_format = FORMAT_DER;
int indent = 0;
int pem_skip = 0;
/* Reset options. */
(void)wc_Asn1PrintOptions_Init(&opts);
/* Skip over program name. */
argc--;
argv++;
while (argc > 0) {
/* Show branches instead of indenting. */
if ((strcmp(argv[0], "-b") == 0) ||
(strcmp(argv[0], "--branch") == 0)) {
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_DRAW_BRANCH, 1);
}
/* File is Base64 encoded data. */
else if ((strcmp(argv[0], "-b64") == 0) ||
(strcmp(argv[0], "--base64") == 0)) {
file_format = FORMAT_BASE64;
}
/* Dump all ASN.1 item data. */
else if ((strcmp(argv[0], "-d") == 0) ||
(strcmp(argv[0], "--dump") == 0)) {
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_SHOW_DATA, 1);
}
/* Dump ASN.1 item headers. */
else if ((strcmp(argv[0], "-h") == 0) ||
(strcmp(argv[0], "--headers") == 0)) {
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_SHOW_HEADER_DATA, 1);
}
/* Indent to text to indicate depth. */
else if ((strcmp(argv[0], "-i") == 0) ||
(strcmp(argv[0], "--indent") == 0)) {
indent++;
if (indent > 15) {
}
}
/* Only parse the specified length of DER/BER data. */
else if ((strcmp(argv[0], "-l") == 0) ||
(strcmp(argv[0], "--length") == 0)) {
if (argc == 1) {
printf("Missing length value\n");
return 1;
}
argc--;
argv++;
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_LENGTH,
atoi(argv[0]));
}
/* Do not show text representations of ASN.1 item data. */
else if ((strcmp(argv[0], "-n") == 0) ||
(strcmp(argv[0], "--no-text") == 0)) {
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_SHOW_NO_TEXT, 1);
}
/* Do not show hex dump text representations of ASN.1 item data. */
else if ((strcmp(argv[0], "-N") == 0) ||
(strcmp(argv[0], "--no-dump-text") == 0)) {
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_SHOW_NO_DUMP_TEXT, 1);
}
/* Offset into DER/BER to start decoding from. */
else if ((strcmp(argv[0], "-o") == 0) ||
(strcmp(argv[0], "--offset") == 0)) {
if (argc == 1) {
fprintf(stderr, "Missing offset value\n");
return 1;
}
argc--;
argv++;
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_OFFSET,
atoi(argv[0]));
}
/* Show wolfSSL OID value for all OBJECT_IDs. */
else if ((strcmp(argv[0], "-O") == 0) ||
(strcmp(argv[0], "--oid") == 0)) {
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_SHOW_OID, 1);
}
/* File contains PEM blocks. */
else if ((strcmp(argv[0], "-p") == 0) ||
(strcmp(argv[0], "--pem") == 0)) {
file_format = FORMAT_PEM;
}
/* Skip a number of PEM blocks. */
else if ((strcmp(argv[0], "-s") == 0) ||
(strcmp(argv[0], "--skip-pem") == 0)) {
if (argc == 1) {
fprintf(stderr, "Missing number of PEM blocks to skip\n");
return 1;
}
argc--;
argv++;
pem_skip = atoi(argv[0]);
if ((pem_skip < 0) || (pem_skip > 15)) {
fprintf(stderr, "Skip value out of range: %d\n", pem_skip);
return 1;
}
}
/* Display help/usage. */
else if ((strcmp(argv[0], "-?") == 0) ||
(strcmp(argv[0], "--help") == 0)) {
Usage();
return 0;
}
/* Unknown option dectection. */
else if (argv[0][0] == '-') {
fprintf(stderr, "Bad option: %s\n", argv[0]);
Usage();
return 1;
}
else {
/* Name of file to read. */
fp = fopen(argv[0], "r");
if (fp == NULL) {
fprintf(stderr, "File not able to be read: %s\n", argv[0]);
return 1;
}
}
/* Move on to next command line argument. */
argc--;
argv++;
}
wc_Asn1PrintOptions_Set(&opts, ASN1_PRINT_OPT_INDENT, indent);
(void)wc_Asn1_Init(&asn1);
(void)wc_Asn1_SetFile(&asn1, stdout);
/* Process file based on type. */
if (file_format == FORMAT_DER) {
ret = PrintDer(fp);
}
else if (file_format == FORMAT_BASE64) {
ret = PrintBase64(fp);
}
else if (file_format == FORMAT_PEM) {
ret = PrintPem(fp, pem_skip);
}
if (ret != 0) {
fprintf(stderr, "%s\n", wc_GetErrorString(ret));
}
return (ret == 0) ? 0 : 1;
}
#else
/* Main entry of ASN.1 printing program.
*
* @param [in] argc Count of command line argements.
* @param [in] argv Command line argements.
* @return 0 on success.
* @return 1 on failure.
*/
int main(int argc, char* argv[])
{
(void)argc;
(void)argv;
fprintf(stderr, "ASN.1 Parsing and Printing not compiled in.\n");
return 0;
}
#endif

View File

@ -0,0 +1,12 @@
# vim:ft=automake
# included from Top Level Makefile.am
# All paths should be given relative to the root
if BUILD_EXAMPLE_ASN1
noinst_PROGRAMS += examples/asn1/asn1
examples_asn1_asn1_SOURCES = examples/asn1/asn1.c
examples_asn1_asn1_LDADD = src/libwolfssl@LIBSUFFIX@.la $(LIB_STATIC_ADD)
examples_asn1_asn1_DEPENDENCIES = src/libwolfssl@LIBSUFFIX@.la
endif

View File

@ -8,4 +8,5 @@ include examples/echoserver/include.am
include examples/server/include.am
include examples/sctp/include.am
include examples/configs/include.am
include examples/asn1/include.am
EXTRA_DIST += examples/README.md

View File

@ -218,6 +218,88 @@ extern int wc_InitRsaHw(RsaKey* key);
#endif /* HAVE_SELFTEST */
#endif
#if defined(WOLFSSL_ASN_PRINT) || defined(WOLFSSL_DEBUG_ASN_TEMPLATE)
/* String representations of tags. */
static const char* tagString[4][32] = {
/* Universal */
{
"EOC",
"BOOLEAN",
"INTEGER",
"BIT STRING",
"OCTET STRING",
"NULL",
"OBJECT ID",
"ObjectDescriptor",
"INSTANCE OF",
"REAL",
"ENUMERATED",
"EMBEDDED PDV",
"UT8String",
"RELATIVE-OID",
"(0x0e) 14",
"(0x0f) 15",
"SEQUENCE",
"SET",
"NumericString",
"PrintableString",
"T61String",
"VideotexString",
"IA5String",
"UTCTime",
"GeneralizedTime",
"GraphicString",
"ISO646String",
"GeneralString",
"UniversalString",
"CHARACTER STRING",
"BMPString",
"(0x1f) 31",
},
/* Application */
{
"[A 0]", "[A 1]", "[A 2]", "[A 3]",
"[A 4]", "[A 5]", "[A 6]", "[A 7]",
"[A 8]", "[A 9]", "[A 10]", "[A 11]",
"[A 12]", "[A 13]", "[A 14]", "[A 15]",
"[A 16]", "[A 17]", "[A 18]", "[A 19]",
"[A 20]", "[A 21]", "[A 22]", "[A 23]",
"[A 24]", "[A 25]", "[A 26]", "[A 27]",
"[A 28]", "[A 20]", "[A 30]", "[A 31]"
},
/* Context-Specific */
{
"[0]", "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]",
"[8]", "[9]", "[10]", "[11]", "[12]", "[13]", "[14]", "[15]",
"[16]", "[17]", "[18]", "[19]", "[20]", "[21]", "[22]", "[23]",
"[24]", "[25]", "[26]", "[27]", "[28]", "[20]", "[30]", "[31]"
},
/* Private */
{
"[P 0]", "[P 1]", "[P 2]", "[P 3]",
"[P 4]", "[P 5]", "[P 6]", "[P 7]",
"[P 8]", "[P 9]", "[P 10]", "[P 11]",
"[P 12]", "[P 13]", "[P 14]", "[P 15]",
"[P 16]", "[P 17]", "[P 18]", "[P 19]",
"[P 20]", "[P 21]", "[P 22]", "[P 23]",
"[P 24]", "[P 25]", "[P 26]", "[P 27]",
"[P 28]", "[P 20]", "[P 30]", "[P 31]"
}
};
/* Converts a tag byte to string.
*
* @param [in] tag BER tag value to interpret.
* @return String corresponding to tag.
*/
static const char* TagString(byte tag)
{
return tagString[tag >> 6][tag & ASN_TYPE_MASK];
}
#endif
/* Calculates the minimum number of bytes required to encode the value.
*
@ -482,83 +564,6 @@ static word32 SizeASNLength(word32 length)
#endif
#ifdef WOLFSSL_DEBUG_ASN_TEMPLATE
/* String representations of tags. */
static const char* tagString[4][32] = {
/* Universal */
{
"EOC",
"BOOLEAN",
"INTEGER",
"BIT STRING",
"OCTET STRING",
"NULL",
"OBJECT ID",
"ObjectDescriptor",
"INSTANCE OF",
"REAL",
"ENUMERATED",
"EMBEDDED PDV",
"UT8String",
"RELATIVE-OID",
"(0x0e) 14",
"(0x0f) 15",
"SEQUENCE",
"SET",
"NumericString",
"PrintableString",
"T61String",
"VideotexString",
"IA5String",
"UTCTime",
"GeneralizedTime",
"GraphicString",
"ISO646String",
"GeneralString",
"UniversalString",
"CHARACTER STRING",
"BMPString",
"(0x1f) 31",
},
/* Application */
{
"[A 0]", "[A 1]", "[A 2]", "[A 3]",
"[A 4]", "[A 5]", "[A 6]", "[A 7]",
"[A 8]", "[A 9]", "[A 10]", "[A 11]",
"[A 12]", "[A 13]", "[A 14]", "[A 15]",
"[A 16]", "[A 17]", "[A 18]", "[A 19]",
"[A 20]", "[A 21]", "[A 22]", "[A 23]",
"[A 24]", "[A 25]", "[A 26]", "[A 27]",
"[A 28]", "[A 20]", "[A 30]", "[A 31]"
},
/* Context-Specific */
{
"[0]", "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]",
"[8]", "[9]", "[10]", "[11]", "[12]", "[13]", "[14]", "[15]",
"[16]", "[17]", "[18]", "[19]", "[20]", "[21]", "[22]", "[23]",
"[24]", "[25]", "[26]", "[27]", "[28]", "[20]", "[30]", "[31]"
},
/* Private */
{
"[P 0]", "[P 1]", "[P 2]", "[P 3]",
"[P 4]", "[P 5]", "[P 6]", "[P 7]",
"[P 8]", "[P 9]", "[P 10]", "[P 11]",
"[P 12]", "[P 13]", "[P 14]", "[P 15]",
"[P 16]", "[P 17]", "[P 18]", "[P 19]",
"[P 20]", "[P 21]", "[P 22]", "[P 23]",
"[P 24]", "[P 25]", "[P 26]", "[P 27]",
"[P 28]", "[P 20]", "[P 30]", "[P 31]"
}
};
/* Converts a tag byte to string.
*
* @param [in] tag BER tag value to interpret.
* @return String corresponding to tag.
*/
static const char* TagString(byte tag)
{
return tagString[tag >> 6][tag & ASN_TYPE_MASK];
}
#include <stdarg.h>
@ -5507,7 +5512,7 @@ int EncodeObjectId(const word16* in, word32 inSz, byte* out, word32* outSz)
}
#endif /* HAVE_OID_ENCODING */
#ifdef HAVE_OID_DECODING
#if defined(HAVE_OID_DECODING) || defined(WOLFSSL_ASN_PRINT)
/* Encode dotted form of OID into byte array version.
*
* @param [in] in Byte array containing OID.
@ -5537,12 +5542,12 @@ int DecodeObjectId(const byte* in, word32 inSz, word16* out, word32* outSz)
return BUFFER_E;
}
if (y == 0) {
out[0] = (t / 40);
out[1] = (t % 40);
out[0] = (word16)(t / 40);
out[1] = (word16)(t % 40);
y = 2;
}
else {
out[y++] = t;
out[y++] = (word16)t;
}
t = 0; /* reset tmp */
}
@ -37020,6 +37025,803 @@ int wc_MIME_free_hdrs(MimeHdr* head)
#undef ERROR_OUT
#ifdef WOLFSSL_ASN_PRINT
/*******************************************************************************
* ASN.1 Parsing and Printing Implemenation
******************************************************************************/
/* Initialize ASN.1 print options.
*
* @param [in, out] opts ASN.1 options for printing.
* @return 0 on success.
* @return BAD_FUNC_ARG when asn1 is NULL.
*/
int wc_Asn1PrintOptions_Init(Asn1PrintOptions* opts)
{
int ret = 0;
if (opts == NULL) {
ret = BAD_FUNC_ARG;
}
else {
XMEMSET(opts, 0, sizeof(*opts));
}
return ret;
}
/* Set a print option into Asn1PrintOptions object.
*
* @param [in, out] opts ASN.1 options for printing.
* @param [in] opt Option to set value of.
* @param [in] val Value to set for option.
* @return 0 on success.
* @return BAD_FUNC_ARG when asn1 is NULL.
* @return BAD_FUNC_ARG when val is out of range for option.
*/
int wc_Asn1PrintOptions_Set(Asn1PrintOptions* opts, enum Asn1PrintOpt opt,
word32 val)
{
int ret = 0;
/* Validate parameters. */
if (opts == NULL) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
switch (opt) {
/* Offset into DER/BER data to start decoding from. */
case ASN1_PRINT_OPT_OFFSET:
opts->offset = val;
break;
/* Length of DER/BER encoding to parse. */
case ASN1_PRINT_OPT_LENGTH:
opts->length = val;
break;
/* Number of spaces to indent for each change in depth. */
case ASN1_PRINT_OPT_INDENT:
/* Only 4 bits available for value. */
if (val >= (1 << 4)) {
ret = BAD_FUNC_ARG;
}
else {
opts->indent = val;
}
break;
/* Draw branches instead of indenting. */
case ASN1_PRINT_OPT_DRAW_BRANCH:
/* Boolean value. */
if (val > 1) {
ret = BAD_FUNC_ARG;
}
else {
opts->draw_branch = val;
}
break;
/* Show raw data of primitive types as octets. */
case ASN1_PRINT_OPT_SHOW_DATA:
/* Boolean value. */
if (val > 1) {
ret = BAD_FUNC_ARG;
}
else {
opts->show_data = val;
}
break;
/* Show header data as octets. */
case ASN1_PRINT_OPT_SHOW_HEADER_DATA:
/* Boolean value. */
if (val > 1) {
ret = BAD_FUNC_ARG;
}
else {
opts->show_header_data = val;
}
break;
/* Show the wolfSSL OID value for OBJECT_ID. */
case ASN1_PRINT_OPT_SHOW_OID:
/* Boolean value. */
if (val > 1) {
ret = BAD_FUNC_ARG;
}
else {
opts->show_oid = val;
}
break;
/* Don't show text representations of primitive types. */
case ASN1_PRINT_OPT_SHOW_NO_TEXT:
/* Boolean value. */
if (val > 1) {
ret = BAD_FUNC_ARG;
}
else {
opts->show_no_text = val;
}
break;
/* Don't show dump text representations of primitive types. */
case ASN1_PRINT_OPT_SHOW_NO_DUMP_TEXT:
/* Boolean value. */
if (val > 1) {
ret = BAD_FUNC_ARG;
}
else {
opts->show_no_dump_text = val;
}
break;
}
}
return ret;
}
/* Initialize an ASN.1 parse object.
*
* @param [in, out] asn1 ASN.1 parse object.
* @return 0 on success.
* @return BAD_FUNC_ARG when asn1 is NULL.
*/
int wc_Asn1_Init(Asn1* asn1)
{
int ret = 0;
if (asn1 == NULL) {
ret = BAD_FUNC_ARG;
}
else {
XMEMSET(asn1, 0, sizeof(*asn1));
asn1->file = XBADFILE;
}
return ret;
}
/* Set the file to use when printing.
*
* @param [in, out] asn1 ASN.1 parse object.
* @param [in] file File to print to.
* @return 0 on success.
* @return BAD_FUNC_ARG when asn1 is NULL.
* @return BAD_FUNC_ARG when file is XBADFILE.
*/
int wc_Asn1_SetFile(Asn1* asn1, XFILE file)
{
int ret = 0;
if ((asn1 == NULL) || (file == XBADFILE)) {
ret = BAD_FUNC_ARG;
}
else {
asn1->file = file;
}
return ret;
}
/* Maximum OID dotted form size. */
#define ASN1_OID_DOTTED_MAX_SZ 16
/* Print OID in dotted form or as hex bytes.
*
* @param [in] file File pointer to write to.
* @param [in] oid OBJECT_ID data.
* @param [in] oid_len Length of OBJECT_ID data.
*/
static void PrintObjectIdNum(XFILE file, unsigned char* oid, word32 len)
{
word16 dotted_nums[ASN1_OID_DOTTED_MAX_SZ];
word32 num = ASN1_OID_DOTTED_MAX_SZ;
word32 i;
/* Decode OBJECT_ID into dotted form array. */
if (DecodeObjectId(oid, len, dotted_nums, &num) == 0) {
/* Print out each number of dotted form. */
for (i = 0; i < num; i++) {
XFPRINTF(file, "%d", dotted_nums[i]);
/* Add separetor. */
if (i < num - 1) {
XFPRINTF(file, ".");
}
}
}
else {
/* Print out bytes as we couldn't decode. */
for (i = 0; i < len; i++) {
XFPRINTF(file, "%02x", oid[i]);
/* Add separetor. */
if (i < len - 1) {
XFPRINTF(file, ":");
}
}
}
}
/* OID value to name mapping. */
typedef struct OidName {
/* wolfSSL OID value. */
word32 oid;
/* Long name to print when OID seen. */
const char* name;
} OidName;
/* Extra OID to name mappings. */
static const OidName extraOids[] = {
{ 0x005c, "commonName" },
{ 0x005d, "surname" },
{ 0x005e, "serialNumber" },
{ 0x005f, "countryName" },
{ 0x0060, "localityName" },
{ 0x0061, "stateOrProvinceName" },
{ 0x0062, "streetAddress" },
{ 0x0063, "organizationName" },
{ 0x0064, "organizationUnitName" },
{ 0x0065, "title" },
{ 0x0086, "certificateExtension" },
{ 0x028d, "emailAddress" },
{ 0x0293, "challengePassword" },
{ 0x029a, "extensionReq" },
};
/* Length of table of extra OID to name mappings. */
#define EXTRA_OIDS_LEN ((int)(sizeof(extraOids) / sizeof(*extraOids)))
/* Convert OID value to long name.
*
* @param [in] oid OID value.
* @param [out] name Long name for OID when known.
* @return 1 when OID known.
* @return 0 when OID not known.
*/
static int Oid2LongName(word32 oid, const char** name)
{
int ret = 0;
int i;
/* Step through each entry in table. */
for (i = 0; i < EXTRA_OIDS_LEN; i++) {
if (extraOids[i].oid == oid) {
/* Return the name associated with the OID value. */
*name = extraOids[i].name;
ret = 1;
break;
}
}
return ret;
}
/* Print the text version of the OBJECT_ID.
*
* @param [in] asn1 ASN.1 parse object.
* @param [in] opts ASN.1 options for printing.
*/
static void PrintObjectIdText(Asn1* asn1, Asn1PrintOptions* opts)
{
word32 oid = (word32)-1;
#if !defined(WOLFCRYPT_ONLY) && defined(OPENSSL_EXTRA)
word32 nid;
#endif
const char* ln = NULL;
word32 i = 0;
int known = 1;
/* Get the OID value for the OBJECT_ID. */
GetObjectId(asn1->data + asn1->offset, &i, &oid, oidIgnoreType,
asn1->item.len + 2);
#if !defined(WOLFCRYPT_ONLY) && defined(OPENSSL_EXTRA)
/* Lookup NID for OID value. */
if ((nid = oid2nid(oid, oidIgnoreType)) != (word32)-1) {
/* Lookup long name for NID. */
ln = wolfSSL_OBJ_nid2ln(nid);
}
else
#endif
/* Lookup long name for extra known OID values. */
if (!Oid2LongName(oid, &ln)) {
/* Unknown OID value. */
ln = NULL;
known = 0;
}
XFPRINTF(asn1->file, ":");
/* Show OID value if not known or asked to. */
if ((!known) || opts->show_oid) {
XFPRINTF(asn1->file, "(0x%x) ", oid);
}
if (ln != NULL) {
/* Print long name. */
XFPRINTF(asn1->file, "%s", ln);
}
else {
/* Print out as numbers - either dotted or hex values. */
PrintObjectIdNum(asn1->file, asn1->data + asn1->item.data_idx,
asn1->item.len);
}
}
/* Print ASN.1 data as a character string.
*
* @param [in] asn1 ASN.1 parse object.
*/
static void PrintText(Asn1* asn1)
{
word32 i;
XFPRINTF(asn1->file, ":");
/* Print all data bytes as characters. */
for (i = 0; i < asn1->item.len; i++) {
XFPRINTF(asn1->file, "%c", asn1->data[asn1->item.data_idx + i]);
}
}
/* Print data as a hex bytes.
*
* @param [in] file File pointer to write to.
* @param [in] data Data to print.
* @param [in] len Number of bytes to print.
*/
static void PrintHex(XFILE file, unsigned char* data, word32 len)
{
word32 i;
/* Print data bytes as hex numbers. */
for (i = 0; i < len; i++) {
XFPRINTF(file, "%02x", data[i]);
}
}
/* Print ASN.1 data as a hex bytes.
*
* @param [in] asn1 ASN.1 parse object.
*/
static void PrintHexText(Asn1* asn1)
{
XFPRINTF(asn1->file, ":");
PrintHex(asn1->file, asn1->data + asn1->item.data_idx, asn1->item.len);
}
/* Print ASN.1 BIT_STRING data as hex bytes noting special first byte.
*
* @param [in] asn1 ASN.1 parse object.
*/
static void PrintBitStringText(Asn1* asn1)
{
if (asn1->item.len > 0) {
XFPRINTF(asn1->file, ":[%02x]", asn1->data[asn1->item.data_idx]);
PrintHex(asn1->file, asn1->data + asn1->item.data_idx + 1,
asn1->item.len - 1);
}
}
/* Print ASN.1 BOOLEAN data as text with value.
*
* @param [in] asn1 ASN.1 parse object.
*/
static void PrintBooleanText(Asn1* asn1)
{
/* Booleans should be 1 byte of data. */
if (asn1->item.len == 1) {
XFPRINTF(asn1->file, ":%s (%d)",
(asn1->data[asn1->item.data_idx] == 0) ? "FALSE" : "TRUE",
asn1->data[asn1->item.data_idx]);
}
}
/* Print ASN.1 data as single byte +/- number.
*
* @param [in] asn1 ASN.1 parse object.
*/
static void PrintNumberText(Asn1* asn1)
{
/* Only supporting 1 byte of data for now. */
if (asn1->item.len == 1) {
int num = asn1->data[asn1->item.data_idx];
XFPRINTF(asn1->file, ":%d", num >= 0x80 ? num - 0x100 : num);
}
}
/* Print ASN.1 data as a text based on the tag.
*
* TODO: handle more tags.
*
* @param [in] asn1 ASN.1 parse object.
* @param [in] opts ASN.1 options for printing.
*/
static void PrintAsn1Text(Asn1* asn1, Asn1PrintOptions* opts)
{
/* Get the long name for OBJECT_ID where possible. */
if (asn1->item.tag == ASN_OBJECT_ID) {
PrintObjectIdText(asn1, opts);
}
/* Data is an array of printable characters. */
else if ((asn1->item.tag == ASN_UTF8STRING) ||
(asn1->item.tag == ASN_IA5_STRING) ||
(asn1->item.tag == ASN_PRINTABLE_STRING) ||
(asn1->item.tag == ASN_T61STRING) ||
(asn1->item.tag == ASN_BMPSTRING) ||
(asn1->item.tag == ASN_UTC_TIME) ||
(asn1->item.tag == ASN_GENERALIZED_TIME) ||
(asn1->item.tag == ASN_UNIVERSALSTRING) ||
(asn1->item.tag == ASN_OBJECT_DESC) ||
(asn1->item.tag == ASN_CHARACTER_STRING)) {
PrintText(asn1);
}
/* Show TRUE and FALSE with number. */
else if (asn1->item.tag == ASN_BOOLEAN) {
PrintBooleanText(asn1);
}
/* Show number. */
else if (asn1->item.tag == ASN_ENUMERATED) {
PrintNumberText(asn1);
}
/* Dumping potentially long string of hex digites. */
else if (!opts->show_no_dump_text) {
/* Dump all bytes. */
if ((asn1->item.tag == ASN_INTEGER) ||
(asn1->item.tag == ASN_OCTET_STRING) ||
((asn1->item.tag > ASN_APPLICATION) && (asn1->item.cons))) {
PrintHexText(asn1);
}
/* First byte is number of unused bits in last byte.
* Print first specially and dump rest of the bytes. */
else if (asn1->item.tag == ASN_BIT_STRING) {
PrintBitStringText(asn1);
}
}
}
#define HexToChar(n) ((((n) >= 32) && ((n) < 127)) ? (n) : '.')
/* Dump data as hex bytes.
*
* @param [in] file File pointer to write to.
* @param [in] data Data to print.
* @param [in] len Number of bytes to print.
*/
static void DumpData(XFILE file, unsigned char* data, word32 len)
{
word32 i;
word32 j;
for (i = 0; i < len; i += j) {
/* Print offset. */
XFPRINTF(file, " %04x:", i);
for (j = 0; (j < 16) && (i + j < len); j++) {
/* Print byte as hex number. */
XFPRINTF(file, "%s%02x", (j == 8) ? " " : " ", data[i + j]);
}
/* Print spaces between hex and characters. */
XFPRINTF(file, " %*s", (16 - j) * 3 + ((j < 8) ? 1 : 0), "");
for (j = 0; (j < 16) && (i + j < len); j++) {
/* Print byte as hex number. */
XFPRINTF(file, "%c", HexToChar(data[i + j]));
}
XFPRINTF(file, "\n");
}
}
/* Update current depth based on the current position.
*
* @param [in, out] asn1 ASN.1 parse object.
*/
static void UpdateDepth(Asn1* asn1)
{
/* If current index is greater than or equal end index then it is done. */
while ((asn1->depth > 0) &&
(asn1->end_idx[asn1->depth-1] <= asn1->curr)) {
/* Move up a depth. */
asn1->depth--;
}
}
/* Check validity of end index of constructed ASN.1 items.
*
* @param [in, out] asn1 ASN.1 parse object.
* @return 0 on success.
* @return ASN_DEPTH_E when end offset invalid.
*/
static int CheckDepth(Asn1* asn1)
{
int ret = 0;
int i;
word32 curr_end = asn1->curr + asn1->item.len;
for (i = 0; (ret == 0) && (i < asn1->depth); i++) {
/* Each end index must be at least as large as the current one. */
if (asn1->end_idx[i] < asn1->end_idx[asn1->depth]) {
ret = ASN_DEPTH_E;
}
/* Each end index must be at least as large as current index. */
if (asn1->end_idx[i] < curr_end) {
ret = ASN_DEPTH_E;
}
}
return ret;
}
/* Draw branching based on depth for an ASN.1 item.
*
* @param [in] asn1 ASN.1 parse object.
*/
static void DrawBranch(Asn1* asn1)
{
int i;
word32 end = asn1->curr + asn1->item.len;
/* Write out the character for all depths but current. */
for (i = 0; i < asn1->depth; i++) {
if (asn1->item.cons || (end < asn1->end_idx[i])) {
if (i < asn1->depth - 1) {
/* Constructed or not end index and not current depth: | */
XFPRINTF(asn1->file, "\xe2\x94\x82");
}
else {
/* Constructed or not end index and current depth: |- */
XFPRINTF(asn1->file, "\xe2\x94\x9c");
}
}
else if ((i > 1) && (end >= asn1->end_idx[i-1])) {
/* End index for previous: _|_ (in top half) */
XFPRINTF(asn1->file, "\xe2\x94\xb4");
}
else {
/* End index but not for previous: L (in top half) */
XFPRINTF(asn1->file, "\xe2\x94\x94");
}
}
/* Prefix to tag name. */
if (asn1->item.cons) {
if (asn1->depth > 0) {
/* Have other line to connect to: T (in bottom half) */
XFPRINTF(asn1->file, "\xe2\x94\xac");
}
else {
/* Have no other line to connect to: r */
XFPRINTF(asn1->file, "\xe2\x94\x8c");
}
}
else {
/* In a sequence: - */
XFPRINTF(asn1->file, "\xe2\x94\x80");
}
}
/* Print data as hex bytes separated by space.
*
* @param [in] file File pointer to write to.
* @param [in] data Data to print.
* @param [in] len Number of bytes to print.
*/
static void PrintHexBytes(XFILE file, unsigned char* data, int len)
{
int i;
for (i = 0; i < len; i++) {
XFPRINTF(file, " %02x", data[i]);
}
}
/* Dump header data.
*
* @param [in] asn1 ASN.1 parse object.
* @param [in] opts ASN.1 options for printing.
*/
static void DumpHeader(Asn1* asn1, Asn1PrintOptions* opts)
{
/* Put on same line when not showing data too and not showing text data. */
if ((!opts->show_data) && opts->show_no_text) {
XFPRINTF(asn1->file, "%10s %02x", "", asn1->item.tag);
}
else {
/* Align with start of data. */
XFPRINTF(asn1->file, "\n%12s %02x", "", asn1->item.tag);
}
/* Print the header bytes as hex bytes separated by a space. */
PrintHexBytes(asn1->file, asn1->data + asn1->offset + 1,
asn1->curr - (asn1->offset + 1));
}
/* Print ASN.1 item info based on header and indeces.
*
* @param [in] asn1 ASN.1 parse object.
* @param [in] opts ASN.1 options for printing.
*/
static void PrintInfo(Asn1* asn1, Asn1PrintOptions* opts)
{
/* Print offset of this ASN.1 item. */
XFPRINTF(asn1->file, "%4d: ", asn1->offset);
/* Print length of header. */
XFPRINTF(asn1->file, "%1d ", asn1->curr - asn1->offset);
/* Print data length. */
XFPRINTF(asn1->file, "%c%4d%c", asn1->item.cons ? '[' : '+', asn1->item.len,
asn1->item.cons ? ']' : ' ');
/* Print depth. */
XFPRINTF(asn1->file, " %s(%d)", (asn1->depth < 10) ? " " : "", asn1->depth);
if (!opts->draw_branch) {
/* Indent to depth as required. */
XFPRINTF(asn1->file, "%*s ", asn1->depth * opts->indent, "");
if (!opts->indent) {
/* Indicate constructed if no indent. */
XFPRINTF(asn1->file, "%c", asn1->item.cons ? '+' : ' ');
}
}
else {
/* Draw branch structure for ASN.1 item. */
XFPRINTF(asn1->file, " ");
DrawBranch(asn1);
}
/* Print tag name. */
XFPRINTF(asn1->file, "%-16s", TagString(asn1->item.tag));
}
/* Expecting tag part of ASN.1 item. */
#define ASN_PART_TAG 0
/* Expecting length part of ASN.1 item. */
#define ASN_PART_LENGTH 1
/* Expecting data part of ASN.1 item. */
#define ASN_PART_DATA 2
/* Print next ASN.1 item.
*
* @param [in, out] asn1 ASN.1 parse object.
* @param [in] opts ASN.1 print options.
* @return 0 on success.
* @return BAD_FUNC_ARG when asn1 or opts is NULL.
* @return ASN_LEN_E when ASN.1 item's length too long.
* @return ASN_DEPTH_E when end offset invalid.
*/
static int wc_Asn1_Print(Asn1* asn1, Asn1PrintOptions* opts)
{
int ret = 0;
if ((asn1 == NULL) || (opts == NULL)) {
ret = BAD_FUNC_ARG;
}
/* Process tag. */
if (asn1->part == ASN_PART_TAG) {
/* Recalculate which depth we are at. */
UpdateDepth(asn1);
/* Get tag. */
asn1->item.tag = asn1->data[asn1->curr] & ~ASN_CONSTRUCTED;
/* Store whether tag indicates constructed. */
asn1->item.cons = (asn1->data[asn1->curr] & ASN_CONSTRUCTED) ==
ASN_CONSTRUCTED;
/* Start of ASN.1 item is current index. */
asn1->offset = asn1->curr;
/* Step over tag. */
asn1->curr++;
/* Next part is length. */
asn1->part = ASN_PART_LENGTH;
}
/* Process length. */
if (asn1->part == ASN_PART_LENGTH) {
int len;
/* Decode length and step over it. */
if (GetLength(asn1->data, &asn1->curr, &len, asn1->max) < 0) {
ret = ASN_LEN_E;
}
else {
/* Store ASN.1 item data offset. */
asn1->item.data_idx = asn1->curr;
/* Store ASN.1 item data length. */
asn1->item.len = len;
/* Print info about ASN.1 item. */
PrintInfo(asn1, opts);
if (!asn1->item.cons) {
/* Move on to print data. */
asn1->part = ASN_PART_DATA;
}
else {
/* Print header now if not printing data. */
if (opts->show_header_data) {
DumpHeader(asn1, opts);
}
XFPRINTF(asn1->file, "\n");
/* Record end offset for this depth. */
asn1->end_idx[asn1->depth++] = asn1->curr + asn1->item.len;
/* Done with this ASN.1 item. */
asn1->part = ASN_PART_TAG;
}
/* Check end indeces are valid. */
ret = CheckDepth(asn1);
}
}
/* Process data. */
if ((ret == 0) && (asn1->part == ASN_PART_DATA)) {
if (!opts->show_no_text) {
/* Print text representation of data. */
PrintAsn1Text(asn1, opts);
}
if (opts->show_header_data) {
/* Dump header bytes. */
DumpHeader(asn1, opts);
}
XFPRINTF(asn1->file, "\n");
if (opts->show_data) {
/* Dump data bytes. */
DumpData(asn1->file, asn1->data + asn1->item.data_idx,
asn1->item.len);
}
/* Step past data to next ASN.1 item. */
asn1->curr += asn1->item.len;
/* Update the depth based on end indeces. */
UpdateDepth(asn1);
/* Done with this ASN.1 item. */
asn1->part = ASN_PART_TAG;
}
/* Make ASN.1 item printing go out. */
fflush(asn1->file);
return ret;
}
/* Print all ASN.1 items.
*
* @param [in, out] asn1 ASN.1 parse object.
* @param [in] opts ASN.1 print options.
* @param [in] data BER/DER data to print.
* @param [in] len Length of data to print in bytes.
* @return 0 on success.
* @return BAD_FUNC_ARG when asn1 or opts is NULL.
* @return ASN_LEN_E when ASN.1 item's length too long.
* @return ASN_DEPTH_E when end offset invalid.
* @return ASN_PARSE_E when not all of an ASN.1 item parsed.
*/
int wc_Asn1_PrintAll(Asn1* asn1, Asn1PrintOptions* opts, unsigned char* data,
word32 len)
{
int ret = 0;
if (asn1 == NULL) {
ret = BAD_FUNC_ARG;
}
if (ret == 0) {
/* Initialize start position. */
asn1->curr = 0;
/* Start parsing at tag. */
asn1->part = ASN_PART_TAG;
/* Start depth at 0. */
asn1->depth = 0;
/* Store the starting point of the data to parse. */
asn1->data = data + opts->offset;
if (opts->length > 0) {
/* Use user specified maximum length. */
asn1->max = opts->length;
}
else {
/* Maximum length is up to end from offset. */
asn1->max = len - opts->offset;
}
/* Keep going while no error and have data to parse. */
while ((ret == 0) && (asn1->curr < asn1->max)) {
/* Print an ASN.1 item. */
ret = wc_Asn1_Print(asn1, opts);
}
}
if ((ret == 0) && (asn1->part != ASN_PART_TAG)) {
/* Stopped before finishing ASN.1 item. */
ret = ASN_PARSE_E;
}
if ((ret == 0) && (asn1->depth != 0)) {
/* Stopped without seeing all items in a constructed item. */
ret = ASN_DEPTH_E;
}
return ret;
}
#endif /* WOLFSSL_ASN_PRINT */
#endif /* !NO_ASN */
#ifdef WOLFSSL_SEP

View File

@ -583,6 +583,12 @@ const char* wc_GetErrorString(int error)
case ENTROPY_APT_E:
return "Entropy Adaptive Proportion Test failed";
case ASN_DEPTH_E:
return "Invalid ASN.1 - depth check";
case ASN_LEN_E:
return "ASN.1 length invalid";
default:
return "unknown error number";

View File

@ -95,16 +95,27 @@ enum ASN_Tags {
ASN_OCTET_STRING = 0x04,
ASN_TAG_NULL = 0x05,
ASN_OBJECT_ID = 0x06,
ASN_OBJECT_DESC = 0x07,
ASN_INSTANCE_OF = 0x08,
ASN_REAL = 0x09,
ASN_ENUMERATED = 0x0a,
ASN_EMBEDDED_PDV = 0x0b,
ASN_UTF8STRING = 0x0c,
ASN_RELATIVE_OID = 0x0d,
ASN_SEQUENCE = 0x10,
ASN_SET = 0x11,
ASN_NUMERICSTRING = 0x12,
ASN_PRINTABLE_STRING = 0x13,
ASN_T61STRING = 0x14,
ASN_VIDEOTEXSTRING = 0x15,
ASN_IA5_STRING = 0x16,
ASN_UTC_TIME = 0x17,
ASN_GENERALIZED_TIME = 0x18,
ASN_GRAPHICSTRING = 0x19,
ASN_ISO646STRING = 0x1a,
ASN_GENERALSTRING = 0x1b,
ASN_UNIVERSALSTRING = 0x1c,
ASN_CHARACTER_STRING = 0x1d,
ASN_BMPSTRING = 0x1e,
ASN_TYPE_MASK = 0x1f,
@ -2153,7 +2164,7 @@ WOLFSSL_LOCAL int GetInt(mp_int* mpi, const byte* input, word32* inOutIdx,
WOLFSSL_LOCAL int EncodeObjectId(const word16* in, word32 inSz,
byte* out, word32* outSz);
#endif
#ifdef HAVE_OID_DECODING
#if defined(HAVE_OID_DECODING) || defined(WOLFSSL_ASN_PRINT)
WOLFSSL_LOCAL int DecodeObjectId(const byte* in, word32 inSz,
word16* out, word32* outSz);
#endif

View File

@ -914,4 +914,104 @@ WOLFSSL_API int wc_GetFASCNFromCert(struct DecodedCert* cert,
} /* extern "C" */
#endif
#if !defined(XFPRINTF) || defined(NO_FILESYSTEM) || \
defined(NO_STDIO_FILESYSTEM) && defined(WOLFSSL_ASN_PRINT)
#undef WOLFSSL_ASN_PRINT
#endif
#ifdef WOLFSSL_ASN_PRINT
enum Asn1PrintOpt {
/* Offset into DER/BER data to start decoding from. */
ASN1_PRINT_OPT_OFFSET,
/* Length of DER/BER encoding to parse. */
ASN1_PRINT_OPT_LENGTH,
/* Number of spaces to indent for each change in depth. */
ASN1_PRINT_OPT_INDENT,
/* Draw branches instead of indenting. */
ASN1_PRINT_OPT_DRAW_BRANCH,
/* Show raw data of primitive types as octets. */
ASN1_PRINT_OPT_SHOW_DATA,
/* Show header data as octets. */
ASN1_PRINT_OPT_SHOW_HEADER_DATA,
/* Show the wolfSSL OID value for OBJECT_ID. */
ASN1_PRINT_OPT_SHOW_OID,
/* Don't show text representations of primitive types. */
ASN1_PRINT_OPT_SHOW_NO_TEXT,
/* Don't show dump text representations of primitive types. */
ASN1_PRINT_OPT_SHOW_NO_DUMP_TEXT,
};
/* ASN.1 print options. */
typedef struct Asn1PrintOptions {
/* Offset into DER/BER encoding to start parsing from. */
word32 offset;
/* Length of DER/BER encoding to parse. */
word32 length;
/* Number of spaces to indent for each change in depth. */
int indent:4;
/* Draw branches instead of indenting. */
int draw_branch:1;
/* Show raw data of primitive types as octets. */
int show_data:1;
/* Show header data as octets. */
int show_header_data:1;
/* Show the wolfSSL OID value for OBJECT_ID. */
int show_oid:1;
/* Don't show text representations of primitive types. */
int show_no_text:1;
/* Don't show dump text representations of primitive types. */
int show_no_dump_text:1;
} Asn1PrintOptions;
/* ASN.1 item data. */
typedef struct Asn1Item {
/* Tag of current item. */
unsigned char tag;
/* Whether current item is constructed. */
unsigned char cons;
/* Length of data in current ASN.1 item. */
word32 len;
/* Index into data of ASN.1 item data. */
word32 data_idx;
} Asn1Item;
/* Maximum supported depth of ASN.1 items. */
#define ASN_MAX_DEPTH 16
/* ASN.1 parsing state. */
typedef struct Asn1 {
/* ASN.1 item data. */
Asn1Item item;
/* Current depth of ASN.1 item. */
unsigned char depth;
/* End indeces of ASN.1 items at different depths. */
word32 end_idx[ASN_MAX_DEPTH];
/* Buffer to print. */
unsigned char* data;
/* Maximum number of bytes to process. */
word32 max;
/* Starting offset of current ASN.1 item. */
word32 offset;
/* Current offset into ASN.1 data. */
word32 curr;
/* Next part of ASN.1 item expected. */
unsigned char part;
/* File pointer to print to. */
XFILE file;
} Asn1;
WOLFSSL_API int wc_Asn1PrintOptions_Init(Asn1PrintOptions* opts);
WOLFSSL_API int wc_Asn1PrintOptions_Set(Asn1PrintOptions* opts,
enum Asn1PrintOpt opt, word32 val);
WOLFSSL_API int wc_Asn1_Init(Asn1* asn1);
WOLFSSL_API int wc_Asn1_SetFile(Asn1* asn1, XFILE file);
WOLFSSL_API int wc_Asn1_PrintAll(Asn1* asn1, Asn1PrintOptions* opts,
unsigned char* data, word32 len);
#endif /* WOLFSSL_ASN_PRINT */
#endif /* WOLF_CRYPT_ASN_PUBLIC_H */

View File

@ -257,7 +257,10 @@ enum {
ENTROPY_RT_E = -294, /* Entropy Repetition Test failed */
ENTROPY_APT_E = -295, /* Entropy Adaptive Proportion Test failed */
WC_LAST_E = -295, /* Update this to indicate last error */
ASN_DEPTH_E = -296, /* Invalid ASN.1 - depth check */
ASN_LEN_E = -297, /* ASN.1 length invalid */
WC_LAST_E = -297, /* Update this to indicate last error */
MIN_CODE_E = -300 /* errors -101 - -299 */
/* add new companion error id strings for any new error codes