/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;tab-width:4;coding:utf-8 -*-│ │vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi│ ╞══════════════════════════════════════════════════════════════════════════════╡ │ Copyright The Mbed TLS Contributors │ │ │ │ Licensed under the Apache License, Version 2.0 (the "License"); │ │ you may not use this file except in compliance with the License. │ │ You may obtain a copy of the License at │ │ │ │ http://www.apache.org/licenses/LICENSE-2.0 │ │ │ │ Unless required by applicable law or agreed to in writing, software │ │ distributed under the License is distributed on an "AS IS" BASIS, │ │ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. │ │ See the License for the specific language governing permissions and │ │ limitations under the License. │ ╚─────────────────────────────────────────────────────────────────────────────*/ #include "third_party/mbedtls/asn1.h" #include "third_party/mbedtls/bignum.h" #include "third_party/mbedtls/common.h" #include "third_party/mbedtls/error.h" #include "third_party/mbedtls/platform.h" asm(".ident\t\"\\n\\n\ Mbed TLS (Apache 2.0)\\n\ Copyright ARM Limited\\n\ Copyright Mbed TLS Contributors\""); asm(".include \"libc/disclaimer.inc\""); /* clang-format off */ #if defined(MBEDTLS_ASN1_PARSE_C) /* * ASN.1 DER decoding routines */ int mbedtls_asn1_get_len_impl( unsigned char **p, const unsigned char *end, size_t *len ) { if( ( end - *p ) < 1 ) return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); if( ( **p & 0x80 ) == 0 ) *len = *(*p)++; else { switch( **p & 0x7F ) { case 1: if( ( end - *p ) < 2 ) return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); *len = (*p)[1]; (*p) += 2; break; case 2: if( ( end - *p ) < 3 ) return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); *len = ( (size_t)(*p)[1] << 8 ) | (*p)[2]; (*p) += 3; break; case 3: if( ( end - *p ) < 4 ) return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); *len = ( (size_t)(*p)[1] << 16 ) | ( (size_t)(*p)[2] << 8 ) | (*p)[3]; (*p) += 4; break; case 4: if( ( end - *p ) < 5 ) return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); *len = ( (size_t)(*p)[1] << 24 ) | ( (size_t)(*p)[2] << 16 ) | ( (size_t)(*p)[3] << 8 ) | (*p)[4]; (*p) += 5; break; default: return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); } } if( *len > (size_t) ( end - *p ) ) return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); return( 0 ); } int mbedtls_asn1_get_bool( unsigned char **p, const unsigned char *end, int *val ) { int ret = MBEDTLS_ERR_THIS_CORRUPTION; size_t len; if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_BOOLEAN ) ) != 0 ) return( ret ); if( len != 1 ) return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); *val = ( **p != 0 ) ? 1 : 0; (*p)++; return( 0 ); } static int asn1_get_tagged_int( unsigned char **p, const unsigned char *end, int tag, int *val ) { int ret = MBEDTLS_ERR_THIS_CORRUPTION; size_t len; if( ( ret = mbedtls_asn1_get_tag( p, end, &len, tag ) ) != 0 ) return( ret ); /* * len==0 is malformed (0 must be represented as 020100 for INTEGER, * or 0A0100 for ENUMERATED tags */ if( len == 0 ) return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); /* This is a cryptography library. Reject negative integers. */ if( ( **p & 0x80 ) != 0 ) return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); /* Skip leading zeros. */ while( len > 0 && **p == 0 ) { ++( *p ); --len; } /* Reject integers that don't fit in an int. This code assumes that * the int type has no padding bit. */ if( len > sizeof( int ) ) return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); if( len == sizeof( int ) && ( **p & 0x80 ) != 0 ) return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); *val = 0; while( len-- > 0 ) { *val = ( *val << 8 ) | **p; (*p)++; } return( 0 ); } int mbedtls_asn1_get_int( unsigned char **p, const unsigned char *end, int *val ) { return( asn1_get_tagged_int( p, end, MBEDTLS_ASN1_INTEGER, val) ); } int mbedtls_asn1_get_enum( unsigned char **p, const unsigned char *end, int *val ) { return( asn1_get_tagged_int( p, end, MBEDTLS_ASN1_ENUMERATED, val) ); } int mbedtls_asn1_get_mpi( unsigned char **p, const unsigned char *end, mbedtls_mpi *X ) { int ret = MBEDTLS_ERR_THIS_CORRUPTION; size_t len; if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_INTEGER ) ) != 0 ) return( ret ); ret = mbedtls_mpi_read_binary( X, *p, len ); *p += len; return( ret ); } int mbedtls_asn1_get_bitstring( unsigned char **p, const unsigned char *end, mbedtls_asn1_bitstring *bs) { int ret = MBEDTLS_ERR_THIS_CORRUPTION; /* Certificate type is a single byte bitstring */ if( ( ret = mbedtls_asn1_get_tag( p, end, &bs->len, MBEDTLS_ASN1_BIT_STRING ) ) != 0 ) return( ret ); /* Check length, subtract one for actual bit string length */ if( bs->len < 1 ) return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); bs->len -= 1; /* Get number of unused bits, ensure unused bits <= 7 */ bs->unused_bits = **p; if( bs->unused_bits > 7 ) return( MBEDTLS_ERR_ASN1_INVALID_LENGTH ); (*p)++; /* Get actual bitstring */ bs->p = *p; *p += bs->len; if( *p != end ) return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); return( 0 ); } /* * Traverse an ASN.1 "SEQUENCE OF " * and call a callback for each entry found. */ int mbedtls_asn1_traverse_sequence_of( unsigned char **p, const unsigned char *end, unsigned char tag_must_mask, unsigned char tag_must_val, unsigned char tag_may_mask, unsigned char tag_may_val, int (*cb)( void *ctx, int tag, unsigned char *start, size_t len ), void *ctx ) { int ret; size_t len; /* Get main sequence tag */ if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) { return( ret ); } if( *p + len != end ) return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); while( *p < end ) { unsigned char const tag = *(*p)++; if( ( tag & tag_must_mask ) != tag_must_val ) return( MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); if( ( ret = mbedtls_asn1_get_len( p, end, &len ) ) != 0 ) return( ret ); if( ( tag & tag_may_mask ) == tag_may_val ) { if( cb ) { ret = cb( ctx, tag, *p, len ); if( ret != 0 ) return( ret ); } } *p += len; } return( 0 ); } /* * Get a bit string without unused bits */ int mbedtls_asn1_get_bitstring_null( unsigned char **p, const unsigned char *end, size_t *len ) { int ret = MBEDTLS_ERR_THIS_CORRUPTION; if( ( ret = mbedtls_asn1_get_tag( p, end, len, MBEDTLS_ASN1_BIT_STRING ) ) != 0 ) return( ret ); if( !*len ) return( MBEDTLS_ERR_ASN1_INVALID_DATA ); --( *len ); if( **p != 0 ) return( MBEDTLS_ERR_ASN1_INVALID_DATA ); ++( *p ); return( 0 ); } void mbedtls_asn1_sequence_free( mbedtls_asn1_sequence *seq ) { while( seq ) { mbedtls_asn1_sequence *next = seq->next; mbedtls_platform_zeroize( seq, sizeof( *seq ) ); mbedtls_free( seq ); seq = next; } } typedef struct { int tag; mbedtls_asn1_sequence *cur; } asn1_get_sequence_of_cb_ctx_t; static int asn1_get_sequence_of_cb( void *ctx, int tag, unsigned char *start, size_t len ) { asn1_get_sequence_of_cb_ctx_t *cb_ctx = (asn1_get_sequence_of_cb_ctx_t *) ctx; mbedtls_asn1_sequence *cur = cb_ctx->cur; if( cur->buf.p ) { cur->next = mbedtls_calloc( 1, sizeof( mbedtls_asn1_sequence ) ); if( !cur->next ) return( MBEDTLS_ERR_ASN1_ALLOC_FAILED ); cur = cur->next; } cur->buf.p = start; cur->buf.len = len; cur->buf.tag = tag; cb_ctx->cur = cur; return( 0 ); } /* * Parses and splits an ASN.1 "SEQUENCE OF " */ int mbedtls_asn1_get_sequence_of( unsigned char **p, const unsigned char *end, mbedtls_asn1_sequence *cur, int tag) { asn1_get_sequence_of_cb_ctx_t cb_ctx = { tag, cur }; mbedtls_platform_zeroize( cur, sizeof( mbedtls_asn1_sequence ) ); return( mbedtls_asn1_traverse_sequence_of( p, end, 0xFF, tag, 0, 0, asn1_get_sequence_of_cb, &cb_ctx ) ); } int mbedtls_asn1_get_alg( unsigned char **p, const unsigned char *end, mbedtls_asn1_buf *alg, mbedtls_asn1_buf *params ) { int ret = MBEDTLS_ERR_THIS_CORRUPTION; size_t len; if( ( ret = mbedtls_asn1_get_tag( p, end, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) return( ret ); if( ( end - *p ) < 1 ) return( MBEDTLS_ERR_ASN1_OUT_OF_DATA ); alg->tag = **p; end = *p + len; if( ( ret = mbedtls_asn1_get_tag( p, end, &alg->len, MBEDTLS_ASN1_OID ) ) != 0 ) return( ret ); alg->p = *p; *p += alg->len; if( *p == end ) { mbedtls_platform_zeroize( params, sizeof(mbedtls_asn1_buf) ); return( 0 ); } params->tag = **p; (*p)++; if( ( ret = mbedtls_asn1_get_len( p, end, ¶ms->len ) ) != 0 ) return( ret ); params->p = *p; *p += params->len; if( *p != end ) return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); return( 0 ); } int mbedtls_asn1_get_alg_null( unsigned char **p, const unsigned char *end, mbedtls_asn1_buf *alg ) { int ret = MBEDTLS_ERR_THIS_CORRUPTION; mbedtls_asn1_buf params; mbedtls_platform_zeroize( ¶ms, sizeof(mbedtls_asn1_buf) ); if( ( ret = mbedtls_asn1_get_alg( p, end, alg, ¶ms ) ) != 0 ) return( ret ); if( ( params.tag != MBEDTLS_ASN1_NULL && params.tag != 0 ) || params.len != 0 ) return( MBEDTLS_ERR_ASN1_INVALID_DATA ); return( 0 ); } void mbedtls_asn1_free_named_data( mbedtls_asn1_named_data *cur ) { if( !cur ) return; mbedtls_free( cur->oid.p ); mbedtls_free( cur->val.p ); mbedtls_platform_zeroize( cur, sizeof( mbedtls_asn1_named_data ) ); } void mbedtls_asn1_free_named_data_list( mbedtls_asn1_named_data **head ) { mbedtls_asn1_named_data *cur; while( ( cur = *head ) ) { *head = cur->next; mbedtls_asn1_free_named_data( cur ); mbedtls_free( cur ); } } mbedtls_asn1_named_data * mbedtls_asn1_find_named_data(mbedtls_asn1_named_data *list, const char *oid, size_t len ) { while( list ) { if( list->oid.len == len && timingsafe_bcmp( list->oid.p, oid, len ) == 0 ) { break; } list = list->next; } return( list ); } #endif /* MBEDTLS_ASN1_PARSE_C */