/* * Copyright (c) 2017 Lev Walkin . * All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ #ifndef ASN_DISABLE_OER_SUPPORT #include #include #include #include /* * This macro "eats" the part of the buffer which is definitely "consumed", * i.e. was correctly converted into local representation or rightfully skipped. */ #undef ADVANCE #define ADVANCE(num_bytes) \ do { \ size_t num = num_bytes; \ ptr = ((const char *)ptr) + num; \ size -= num; \ consumed_myself += num; \ } while(0) /* * Switch to the next phase of parsing. */ #undef NEXT_PHASE #define NEXT_PHASE(ctx) \ do { \ ctx->phase++; \ ctx->step = 0; \ } while(0) #undef SET_PHASE #define SET_PHASE(ctx, value) \ do { \ ctx->phase = value; \ ctx->step = 0; \ } while(0) /* * Return a standardized complex structure. */ #undef RETURN #define RETURN(_code) \ do { \ asn_dec_rval_t rval; \ rval.code = _code; \ rval.consumed = consumed_myself; \ return rval; \ } while(0) /* * The SEQUENCE OF and SET OF values utilize a "quantity field". * It is is a pointless combination of #8.6 (length determinant, capable * of encoding tiny and huge numbers in the shortest possible number of octets) * and the variable sized integer. What could have been encoded by #8.6 alone * is required to be encoded by #8.6 followed by that number of unsigned octets. * This doesn't make too much sense. It seems that the original version of OER * standard have been using the unconstrained unsigned integer as a quantity * field, and this legacy have gone through ISO/ITU-T standardization process. */ static ssize_t oer_fetch_quantity(const void *ptr, size_t size, size_t *qty_r) { const uint8_t *b; const uint8_t *bend; size_t len = 0; size_t qty; ssize_t len_len = oer_fetch_length(ptr, size, &len); if(len_len <= 0) { *qty_r = 0; return len_len; } if((len_len + len) > size) { *qty_r = 0; return 0; } b = (const uint8_t *)ptr + len_len; bend = b + len; /* Skip the leading 0-bytes */ for(; b < bend && *b == 0; b++) { } if((bend - b) > (ssize_t)sizeof(size_t)) { /* Length is not representable by the native size_t type */ *qty_r = 0; return -1; } for(qty = 0; b < bend; b++) { qty = (qty << 8) + *b; } if(qty > RSIZE_MAX) { /* A bit of C11 validation */ *qty_r = 0; return -1; } *qty_r = qty; assert((size_t)len_len + len == (size_t)(bend - (const uint8_t *)ptr)); return len_len + len; } asn_dec_rval_t SET_OF_decode_oer(const asn_codec_ctx_t *opt_codec_ctx, const asn_TYPE_descriptor_t *td, const asn_oer_constraints_t *constraints, void **struct_ptr, const void *ptr, size_t size) { const asn_SET_OF_specifics_t *specs = (const asn_SET_OF_specifics_t *)td->specifics; asn_dec_rval_t rval = {RC_OK, 0}; void *st = *struct_ptr; /* Target structure */ asn_struct_ctx_t *ctx; /* Decoder context */ size_t consumed_myself = 0; /* Consumed bytes from ptr. */ (void)constraints; if(ASN__STACK_OVERFLOW_CHECK(opt_codec_ctx)) ASN__DECODE_FAILED; /* * Create the target structure if it is not present already. */ if(st == 0) { st = *struct_ptr = CALLOC(1, specs->struct_size); if(st == 0) { RETURN(RC_FAIL); } } /* * Restore parsing context. */ ctx = (asn_struct_ctx_t *)((char *)st + specs->ctx_offset); /* * Start to parse where left previously. */ switch(ctx->phase) { case 0: { /* * Fetch number of elements to decode. */ size_t length = 0; size_t len_size = oer_fetch_quantity(ptr, size, &length); switch(len_size) { case 0: RETURN(RC_WMORE); case -1: RETURN(RC_FAIL); default: ADVANCE(len_size); ctx->left = length; } } NEXT_PHASE(ctx); /* FALL THROUGH */ case 1: { /* Decode components of the extension root */ asn_TYPE_member_t *elm = td->elements; asn_anonymous_set_ *list = _A_SET_FROM_VOID(st); const void *base_ptr = ptr; ber_tlv_len_t base_ctx_left = ctx->left; assert(td->elements_count == 1); ASN_DEBUG("OER SET OF %s Decoding PHASE 1", td->name); for(; ctx->left > 0; ctx->left--) { asn_dec_rval_t rv = elm->type->op->oer_decoder( opt_codec_ctx, elm->type, elm->encoding_constraints.oer_constraints, &ctx->ptr, ptr, size); ADVANCE(rv.consumed); switch(rv.code) { case RC_OK: if(ASN_SET_ADD(list, ctx->ptr) != 0) { RETURN(RC_FAIL); } else { ctx->ptr = 0; /* * This check is to avoid compression bomb with * specs like SEQUENCE/SET OF NULL which don't * consume data at all. */ if(rv.consumed == 0 && base_ptr == ptr && (base_ctx_left - ctx->left) > 200) { ASN__DECODE_FAILED; } break; } case RC_WMORE: RETURN(RC_WMORE); case RC_FAIL: ASN_STRUCT_FREE(*elm->type, ctx->ptr); ctx->ptr = 0; SET_PHASE(ctx, 3); RETURN(RC_FAIL); } } /* Decoded decently. */ NEXT_PHASE(ctx); } /* Fall through */ case 2: /* Ignore fully decoded */ assert(ctx->left == 0); RETURN(RC_OK); case 3: /* Failed to decode. */ RETURN(RC_FAIL); } return rval; } static ssize_t oer_put_quantity(size_t qty, asn_app_consume_bytes_f *cb, void *app_key) { uint8_t buf[1 + sizeof(size_t)]; uint8_t *b = &buf[sizeof(size_t)]; /* Last addressable */ size_t encoded; do { *b-- = qty; qty >>= 8; } while(qty); *b = sizeof(buf) - (b-buf) - 1; encoded = sizeof(buf) - (b-buf); if(cb(b, encoded, app_key) < 0) return -1; return encoded; } /* * Encode as Canonical OER. */ asn_enc_rval_t SET_OF_encode_oer(const asn_TYPE_descriptor_t *td, const asn_oer_constraints_t *constraints, const void *sptr, asn_app_consume_bytes_f *cb, void *app_key) { const asn_TYPE_member_t *elm; const asn_anonymous_set_ *list; size_t computed_size = 0; ssize_t qty_len; int n; (void)constraints; if(!sptr) ASN__ENCODE_FAILED; elm = td->elements; list = _A_CSET_FROM_VOID(sptr); qty_len = oer_put_quantity(list->count, cb, app_key); if(qty_len < 0) { ASN__ENCODE_FAILED; } computed_size += qty_len; for(n = 0; n < list->count; n++) { void *memb_ptr = list->array[n]; asn_enc_rval_t er = {0,0,0}; er = elm->type->op->oer_encoder( elm->type, elm->encoding_constraints.oer_constraints, memb_ptr, cb, app_key); if(er.encoded < 0) { return er; } else { computed_size += er.encoded; } } { asn_enc_rval_t erval = {0,0,0}; erval.encoded = computed_size; ASN__ENCODED_OK(erval); } } #endif /* ASN_DISABLE_OER_SUPPORT */