Skip to content

Commit c69c2c5

Browse files
Merge pull request #263 from hanno-arm/asn1_traversal_api
Introduce ASN.1 SEQUENCE traversal API
2 parents c0611a5 + 34aada2 commit c69c2c5

File tree

4 files changed

+379
-52
lines changed

4 files changed

+379
-52
lines changed

include/mbedtls/asn1.h

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,18 @@
9090
#define MBEDTLS_ASN1_CONSTRUCTED 0x20
9191
#define MBEDTLS_ASN1_CONTEXT_SPECIFIC 0x80
9292

93+
/* Slightly smaller way to check if tag is a string tag
94+
* compared to canonical implementation. */
95+
#define MBEDTLS_ASN1_IS_STRING_TAG( tag ) \
96+
( ( tag ) < 32u && ( \
97+
( ( 1u << ( tag ) ) & ( ( 1u << MBEDTLS_ASN1_BMP_STRING ) | \
98+
( 1u << MBEDTLS_ASN1_UTF8_STRING ) | \
99+
( 1u << MBEDTLS_ASN1_T61_STRING ) | \
100+
( 1u << MBEDTLS_ASN1_IA5_STRING ) | \
101+
( 1u << MBEDTLS_ASN1_UNIVERSAL_STRING ) | \
102+
( 1u << MBEDTLS_ASN1_PRINTABLE_STRING ) | \
103+
( 1u << MBEDTLS_ASN1_BIT_STRING ) ) ) != 0 ) )
104+
93105
/*
94106
* Bit masks for each of the components of an ASN.1 tag as specified in
95107
* ITU X.690 (08/2015), section 8.1 "General rules for encoding",
@@ -120,6 +132,10 @@
120132
( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf)->len ) || \
121133
memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) != 0 )
122134

135+
#define MBEDTLS_OID_CMP_RAW(oid_str, oid_buf, oid_buf_len) \
136+
( ( MBEDTLS_OID_SIZE(oid_str) != (oid_buf_len) ) || \
137+
memcmp( (oid_str), (oid_buf), (oid_buf_len) ) != 0 )
138+
123139
#ifdef __cplusplus
124140
extern "C" {
125141
#endif
@@ -327,6 +343,9 @@ int mbedtls_asn1_get_bitstring_null( unsigned char **p,
327343
* \brief Parses and splits an ASN.1 "SEQUENCE OF <tag>".
328344
* Updates the pointer to immediately behind the full sequence tag.
329345
*
346+
* This function allocates memory for the sequence elements. You can free
347+
* the allocated memory with mbedtls_asn1_sequence_free().
348+
*
330349
* \note On error, this function may return a partial list in \p cur.
331350
* You must set `cur->next = NULL` before calling this function!
332351
* Otherwise it is impossible to distinguish a previously non-null
@@ -360,14 +379,133 @@ int mbedtls_asn1_get_bitstring_null( unsigned char **p,
360379
* \return 0 if successful.
361380
* \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input contains
362381
* extra data after a valid SEQUENCE OF \p tag.
382+
* \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the input starts with
383+
* an ASN.1 SEQUENCE in which an element has a tag that
384+
* is different from \p tag.
363385
* \return #MBEDTLS_ERR_ASN1_ALLOC_FAILED if a memory allocation failed.
364386
* \return An ASN.1 error code if the input does not start with
365-
* a valid ASN.1 BIT STRING.
387+
* a valid ASN.1 SEQUENCE.
366388
*/
367389
int mbedtls_asn1_get_sequence_of( unsigned char **p,
368390
const unsigned char *end,
369391
mbedtls_asn1_sequence *cur,
370392
int tag );
393+
/**
394+
* \brief Free a heap-allocated linked list presentation of
395+
* an ASN.1 sequence, including the first element.
396+
*
397+
* There are two common ways to manage the memory used for the representation
398+
* of a parsed ASN.1 sequence:
399+
* - Allocate a head node `mbedtls_asn1_sequence *head` with mbedtls_calloc().
400+
* Pass this node as the `cur` argument to mbedtls_asn1_get_sequence_of().
401+
* When you have finished processing the sequence,
402+
* call mbedtls_asn1_sequence_free() on `head`.
403+
* - Allocate a head node `mbedtls_asn1_sequence *head` in any manner,
404+
* for example on the stack. Make sure that `head->next == NULL`.
405+
* Pass `head` as the `cur` argument to mbedtls_asn1_get_sequence_of().
406+
* When you have finished processing the sequence,
407+
* call mbedtls_asn1_sequence_free() on `head->cur`,
408+
* then free `head` itself in the appropriate manner.
409+
*
410+
* \param seq The address of the first sequence component. This may
411+
* be \c NULL, in which case this functions returns
412+
* immediately.
413+
*/
414+
void mbedtls_asn1_sequence_free( mbedtls_asn1_sequence *seq );
415+
416+
/**
417+
* \brief Traverse an ASN.1 SEQUENCE container and
418+
* call a callback for each entry.
419+
*
420+
* This function checks that the input is a SEQUENCE of elements that
421+
* each have a "must" tag, and calls a callback function on the elements
422+
* that have a "may" tag.
423+
*
424+
* For example, to validate that the input is a SEQUENCE of `tag1` and call
425+
* `cb` on each element, use
426+
* ```
427+
* mbedtls_asn1_traverse_sequence_of(&p, end, 0xff, tag1, 0, 0, cb, ctx);
428+
* ```
429+
*
430+
* To validate that the input is a SEQUENCE of ANY and call `cb` on
431+
* each element, use
432+
* ```
433+
* mbedtls_asn1_traverse_sequence_of(&p, end, 0, 0, 0, 0, cb, ctx);
434+
* ```
435+
*
436+
* To validate that the input is a SEQUENCE of CHOICE {NULL, OCTET STRING}
437+
* and call `cb` on each element that is an OCTET STRING, use
438+
* ```
439+
* mbedtls_asn1_traverse_sequence_of(&p, end, 0xfe, 0x04, 0xff, 0x04, cb, ctx);
440+
* ```
441+
*
442+
* The callback is called on the elements with a "may" tag from left to
443+
* right. If the input is not a valid SEQUENCE of elements with a "must" tag,
444+
* the callback is called on the elements up to the leftmost point where
445+
* the input is invalid.
446+
*
447+
* \warning This function is still experimental and may change
448+
* at any time.
449+
*
450+
* \param p The address of the pointer to the beginning of
451+
* the ASN.1 SEQUENCE header. This is updated to
452+
* point to the end of the ASN.1 SEQUENCE container
453+
* on a successful invocation.
454+
* \param end The end of the ASN.1 SEQUENCE container.
455+
* \param tag_must_mask A mask to be applied to the ASN.1 tags found within
456+
* the SEQUENCE before comparing to \p tag_must_value.
457+
* \param tag_must_val The required value of each ASN.1 tag found in the
458+
* SEQUENCE, after masking with \p tag_must_mask.
459+
* Mismatching tags lead to an error.
460+
* For example, a value of \c 0 for both \p tag_must_mask
461+
* and \p tag_must_val means that every tag is allowed,
462+
* while a value of \c 0xFF for \p tag_must_mask means
463+
* that \p tag_must_val is the only allowed tag.
464+
* \param tag_may_mask A mask to be applied to the ASN.1 tags found within
465+
* the SEQUENCE before comparing to \p tag_may_value.
466+
* \param tag_may_val The desired value of each ASN.1 tag found in the
467+
* SEQUENCE, after masking with \p tag_may_mask.
468+
* Mismatching tags will be silently ignored.
469+
* For example, a value of \c 0 for \p tag_may_mask and
470+
* \p tag_may_val means that any tag will be considered,
471+
* while a value of \c 0xFF for \p tag_may_mask means
472+
* that all tags with value different from \p tag_may_val
473+
* will be ignored.
474+
* \param cb The callback to trigger for each component
475+
* in the ASN.1 SEQUENCE that matches \p tag_may_val.
476+
* The callback function is called with the following
477+
* parameters:
478+
* - \p ctx.
479+
* - The tag of the current element.
480+
* - A pointer to the start of the current element's
481+
* content inside the input.
482+
* - The length of the content of the current element.
483+
* If the callback returns a non-zero value,
484+
* the function stops immediately,
485+
* forwarding the callback's return value.
486+
* \param ctx The context to be passed to the callback \p cb.
487+
*
488+
* \return \c 0 if successful the entire ASN.1 SEQUENCE
489+
* was traversed without parsing or callback errors.
490+
* \return #MBEDTLS_ERR_ASN1_LENGTH_MISMATCH if the input
491+
* contains extra data after a valid SEQUENCE
492+
* of elements with an accepted tag.
493+
* \return #MBEDTLS_ERR_ASN1_UNEXPECTED_TAG if the input starts
494+
* with an ASN.1 SEQUENCE in which an element has a tag
495+
* that is not accepted.
496+
* \return An ASN.1 error code if the input does not start with
497+
* a valid ASN.1 SEQUENCE.
498+
* \return A non-zero error code forwarded from the callback
499+
* \p cb in case the latter returns a non-zero value.
500+
*/
501+
int mbedtls_asn1_traverse_sequence_of(
502+
unsigned char **p,
503+
const unsigned char *end,
504+
unsigned char tag_must_mask, unsigned char tag_must_val,
505+
unsigned char tag_may_mask, unsigned char tag_may_val,
506+
int (*cb)( void *ctx, int tag,
507+
unsigned char* start, size_t len ),
508+
void *ctx );
371509

372510
#if defined(MBEDTLS_BIGNUM_C)
373511
/**

library/asn1parse.c

Lines changed: 101 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -247,6 +247,58 @@ int mbedtls_asn1_get_bitstring( unsigned char **p, const unsigned char *end,
247247
return( 0 );
248248
}
249249

250+
/*
251+
* Traverse an ASN.1 "SEQUENCE OF <tag>"
252+
* and call a callback for each entry found.
253+
*/
254+
int mbedtls_asn1_traverse_sequence_of(
255+
unsigned char **p,
256+
const unsigned char *end,
257+
unsigned char tag_must_mask, unsigned char tag_must_val,
258+
unsigned char tag_may_mask, unsigned char tag_may_val,
259+
int (*cb)( void *ctx, int tag,
260+
unsigned char *start, size_t len ),
261+
void *ctx )
262+
{
263+
int ret;
264+
size_t len;
265+
266+
/* Get main sequence tag */
267+
if( ( ret = mbedtls_asn1_get_tag( p, end, &len,
268+
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
269+
{
270+
return( ret );
271+
}
272+
273+
if( *p + len != end )
274+
return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
275+
276+
while( *p < end )
277+
{
278+
unsigned char const tag = *(*p)++;
279+
280+
if( ( tag & tag_must_mask ) != tag_must_val )
281+
return( MBEDTLS_ERR_ASN1_UNEXPECTED_TAG );
282+
283+
if( ( ret = mbedtls_asn1_get_len( p, end, &len ) ) != 0 )
284+
return( ret );
285+
286+
if( ( tag & tag_may_mask ) == tag_may_val )
287+
{
288+
if( cb != NULL )
289+
{
290+
ret = cb( ctx, tag, *p, len );
291+
if( ret != 0 )
292+
return( ret );
293+
}
294+
}
295+
296+
*p += len;
297+
}
298+
299+
return( 0 );
300+
}
301+
250302
/*
251303
* Get a bit string without unused bits
252304
*/
@@ -269,61 +321,67 @@ int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end
269321
return( 0 );
270322
}
271323

272-
273-
274-
/*
275-
* Parses and splits an ASN.1 "SEQUENCE OF <tag>"
276-
*/
277-
int mbedtls_asn1_get_sequence_of( unsigned char **p,
278-
const unsigned char *end,
279-
mbedtls_asn1_sequence *cur,
280-
int tag)
324+
void mbedtls_asn1_sequence_free( mbedtls_asn1_sequence *seq )
281325
{
282-
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
283-
size_t len;
284-
mbedtls_asn1_buf *buf;
285-
286-
/* Get main sequence tag */
287-
if( ( ret = mbedtls_asn1_get_tag( p, end, &len,
288-
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
289-
return( ret );
290-
291-
if( *p + len != end )
292-
return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
293-
294-
while( *p < end )
326+
while( seq != NULL )
295327
{
296-
buf = &(cur->buf);
297-
buf->tag = **p;
298-
299-
if( ( ret = mbedtls_asn1_get_tag( p, end, &buf->len, tag ) ) != 0 )
300-
return( ret );
328+
mbedtls_asn1_sequence *next = seq->next;
329+
mbedtls_platform_zeroize( seq, sizeof( *seq ) );
330+
mbedtls_free( seq );
331+
seq = next;
332+
}
333+
}
301334

302-
buf->p = *p;
303-
*p += buf->len;
335+
typedef struct
336+
{
337+
int tag;
338+
mbedtls_asn1_sequence *cur;
339+
} asn1_get_sequence_of_cb_ctx_t;
340+
341+
static int asn1_get_sequence_of_cb( void *ctx,
342+
int tag,
343+
unsigned char *start,
344+
size_t len )
345+
{
346+
asn1_get_sequence_of_cb_ctx_t *cb_ctx =
347+
(asn1_get_sequence_of_cb_ctx_t *) ctx;
348+
mbedtls_asn1_sequence *cur =
349+
cb_ctx->cur;
304350

305-
/* Allocate and assign next pointer */
306-
if( *p < end )
307-
{
308-
cur->next = (mbedtls_asn1_sequence*)mbedtls_calloc( 1,
309-
sizeof( mbedtls_asn1_sequence ) );
351+
if( cur->buf.p != NULL )
352+
{
353+
cur->next =
354+
mbedtls_calloc( 1, sizeof( mbedtls_asn1_sequence ) );
310355

311-
if( cur->next == NULL )
312-
return( MBEDTLS_ERR_ASN1_ALLOC_FAILED );
356+
if( cur->next == NULL )
357+
return( MBEDTLS_ERR_ASN1_ALLOC_FAILED );
313358

314-
cur = cur->next;
315-
}
359+
cur = cur->next;
316360
}
317361

318-
/* Set final sequence entry's next pointer to NULL */
319-
cur->next = NULL;
320-
321-
if( *p != end )
322-
return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
362+
cur->buf.p = start;
363+
cur->buf.len = len;
364+
cur->buf.tag = tag;
323365

366+
cb_ctx->cur = cur;
324367
return( 0 );
325368
}
326369

370+
/*
371+
* Parses and splits an ASN.1 "SEQUENCE OF <tag>"
372+
*/
373+
int mbedtls_asn1_get_sequence_of( unsigned char **p,
374+
const unsigned char *end,
375+
mbedtls_asn1_sequence *cur,
376+
int tag)
377+
{
378+
asn1_get_sequence_of_cb_ctx_t cb_ctx = { tag, cur };
379+
memset( cur, 0, sizeof( mbedtls_asn1_sequence ) );
380+
return( mbedtls_asn1_traverse_sequence_of(
381+
p, end, 0xFF, tag, 0, 0,
382+
asn1_get_sequence_of_cb, &cb_ctx ) );
383+
}
384+
327385
int mbedtls_asn1_get_alg( unsigned char **p,
328386
const unsigned char *end,
329387
mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params )

0 commit comments

Comments
 (0)