/*- * Copyright (c) 2003, 2004 Lev Walkin . All rights reserved. * Redistribution and modifications are permitted subject to BSD license. */ #include #include #include #include #include /* for CHAR_BIT */ #include /* * OBJECT IDENTIFIER basic type description. */ static const ber_tlv_tag_t asn_DEF_OBJECT_IDENTIFIER_tags[] = { (ASN_TAG_CLASS_UNIVERSAL | (6 << 2)) }; asn_TYPE_operation_t asn_OP_OBJECT_IDENTIFIER = { ASN__PRIMITIVE_TYPE_free, OBJECT_IDENTIFIER_print, OCTET_STRING_compare, /* Implemented in terms of a string comparison */ ber_decode_primitive, der_encode_primitive, OBJECT_IDENTIFIER_decode_xer, OBJECT_IDENTIFIER_encode_xer, #ifdef ASN_DISABLE_OER_SUPPORT 0, 0, #else OBJECT_IDENTIFIER_decode_oer, OBJECT_IDENTIFIER_encode_oer, #endif /* ASN_DISABLE_OER_SUPPORT */ #ifdef ASN_DISABLE_PER_SUPPORT 0, 0, #else OCTET_STRING_decode_uper, OCTET_STRING_encode_uper, #endif /* ASN_DISABLE_PER_SUPPORT */ OBJECT_IDENTIFIER_random_fill, 0 /* Use generic outmost tag fetcher */ }; asn_TYPE_descriptor_t asn_DEF_OBJECT_IDENTIFIER = { "OBJECT IDENTIFIER", "OBJECT_IDENTIFIER", &asn_OP_OBJECT_IDENTIFIER, asn_DEF_OBJECT_IDENTIFIER_tags, sizeof(asn_DEF_OBJECT_IDENTIFIER_tags) / sizeof(asn_DEF_OBJECT_IDENTIFIER_tags[0]), asn_DEF_OBJECT_IDENTIFIER_tags, /* Same as above */ sizeof(asn_DEF_OBJECT_IDENTIFIER_tags) / sizeof(asn_DEF_OBJECT_IDENTIFIER_tags[0]), { 0, 0, OBJECT_IDENTIFIER_constraint }, 0, 0, /* No members */ 0 /* No specifics */ }; int OBJECT_IDENTIFIER_constraint(const asn_TYPE_descriptor_t *td, const void *sptr, asn_app_constraint_failed_f *ctfailcb, void *app_key) { const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr; if(st && st->buf) { if(st->size < 1) { ASN__CTFAIL(app_key, td, sptr, "%s: at least one numerical value " "expected (%s:%d)", td->name, __FILE__, __LINE__); return -1; } } else { ASN__CTFAIL(app_key, td, sptr, "%s: value not given (%s:%d)", td->name, __FILE__, __LINE__); return -1; } return 0; } static ssize_t OBJECT_IDENTIFIER_get_first_arcs(const uint8_t *arcbuf, size_t arcbuf_len, asn_oid_arc_t *arc0, asn_oid_arc_t *arc1) { asn_oid_arc_t value; ssize_t rd = OBJECT_IDENTIFIER_get_single_arc(arcbuf, arcbuf_len, &value); if(rd <= 0) return rd; if(value >= 80) { *arc0 = 2; *arc1 = value - 80; } else if(value >= 40) { *arc0 = 1; *arc1 = value - 40; } else { *arc0 = 0; *arc1 = value; } return rd; } ssize_t OBJECT_IDENTIFIER_get_single_arc(const uint8_t *arcbuf, size_t arcbuf_len, asn_oid_arc_t *ret_value) { const uint8_t *b = arcbuf; const uint8_t *arcend = arcbuf + arcbuf_len; /* End of arc */ if(arcbuf == arcend) { return 0; } else { asn_oid_arc_t accum; /* Gather all bits into the accumulator */ for(accum = 0; b < arcend; b++) { accum = (accum << 7) | (*b & ~0x80); if((*b & 0x80) == 0) { if(accum <= ASN_OID_ARC_MAX) { *ret_value = accum; return 1 + (b - arcbuf); } else { errno = ERANGE; /* Overflow */ return -1; } } } errno = EINVAL; return -1; } } static ssize_t OBJECT_IDENTIFIER__dump_body(const OBJECT_IDENTIFIER_t *st, asn_app_consume_bytes_f *cb, void *app_key) { char scratch[32]; asn_oid_arc_t arc0, arc1; size_t produced = 0; size_t off = 0; ssize_t rd; int ret; rd = OBJECT_IDENTIFIER_get_first_arcs(st->buf, st->size, &arc0, &arc1); if(rd <= 0) { return -1; } ret = snprintf(scratch, sizeof(scratch), "%"PRIu32".%"PRIu32, arc0, arc1); if(ret >= (ssize_t)sizeof(scratch)) { return -1; } produced += ret; if(cb(scratch, ret, app_key) < 0) return -1; for(off = rd; ; ) { asn_oid_arc_t arc; rd = OBJECT_IDENTIFIER_get_single_arc(st->buf + off, st->size - off, &arc); if(rd < 0) { return -1; } else if(rd == 0) { /* No more arcs. */ break; } else { off += rd; assert(off <= st->size); ret = snprintf(scratch, sizeof(scratch), ".%" PRIu32, arc); if(ret >= (ssize_t)sizeof(scratch)) { return -1; } produced += ret; if(cb(scratch, ret, app_key) < 0) return -1; } } if(off != st->size) { ASN_DEBUG("Could not scan to the end of Object Identifier"); return -1; } return produced; } static enum xer_pbd_rval OBJECT_IDENTIFIER__xer_body_decode(const asn_TYPE_descriptor_t *td, void *sptr, const void *chunk_buf, size_t chunk_size) { OBJECT_IDENTIFIER_t *st = (OBJECT_IDENTIFIER_t *)sptr; const char *chunk_end = (const char *)chunk_buf + chunk_size; const char *endptr; asn_oid_arc_t s_arcs[10]; asn_oid_arc_t *arcs = s_arcs; ssize_t num_arcs; ssize_t ret; (void)td; num_arcs = OBJECT_IDENTIFIER_parse_arcs( (const char *)chunk_buf, chunk_size, arcs, sizeof(s_arcs) / sizeof(s_arcs[0]), &endptr); if(num_arcs < 0) { /* Expecting more than zero arcs */ return XPBD_BROKEN_ENCODING; } else if(num_arcs == 0) { return XPBD_NOT_BODY_IGNORE; } assert(endptr == chunk_end); if((size_t)num_arcs > sizeof(s_arcs)/sizeof(s_arcs[0])) { arcs = (asn_oid_arc_t *)MALLOC(num_arcs * sizeof(asn_oid_arc_t)); if(!arcs) return XPBD_SYSTEM_FAILURE; ret = OBJECT_IDENTIFIER_parse_arcs((const char *)chunk_buf, chunk_size, arcs, num_arcs, &endptr); if(ret != num_arcs) return XPBD_SYSTEM_FAILURE; /* assert?.. */ } /* * Convert arcs into BER representation. */ ret = OBJECT_IDENTIFIER_set_arcs(st, arcs, num_arcs); if(arcs != s_arcs) FREEMEM(arcs); return ret ? XPBD_SYSTEM_FAILURE : XPBD_BODY_CONSUMED; } asn_dec_rval_t OBJECT_IDENTIFIER_decode_xer(const asn_codec_ctx_t *opt_codec_ctx, const asn_TYPE_descriptor_t *td, void **sptr, const char *opt_mname, const void *buf_ptr, size_t size) { return xer_decode_primitive(opt_codec_ctx, td, sptr, sizeof(OBJECT_IDENTIFIER_t), opt_mname, buf_ptr, size, OBJECT_IDENTIFIER__xer_body_decode); } asn_enc_rval_t OBJECT_IDENTIFIER_encode_xer(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, enum xer_encoder_flags_e flags, asn_app_consume_bytes_f *cb, void *app_key) { const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr; asn_enc_rval_t er; (void)ilevel; (void)flags; if(!st || !st->buf) { ASN__ENCODE_FAILED; } er.encoded = OBJECT_IDENTIFIER__dump_body(st, cb, app_key); if(er.encoded < 0) ASN__ENCODE_FAILED; ASN__ENCODED_OK(er); } int OBJECT_IDENTIFIER_print(const asn_TYPE_descriptor_t *td, const void *sptr, int ilevel, asn_app_consume_bytes_f *cb, void *app_key) { const OBJECT_IDENTIFIER_t *st = (const OBJECT_IDENTIFIER_t *)sptr; (void)td; /* Unused argument */ (void)ilevel; /* Unused argument */ if(!st || !st->buf) return (cb("", 8, app_key) < 0) ? -1 : 0; /* Dump preamble */ if(cb("{ ", 2, app_key) < 0) return -1; if(OBJECT_IDENTIFIER__dump_body(st, cb, app_key) < 0) { return -1; } return (cb(" }", 2, app_key) < 0) ? -1 : 0; } ssize_t OBJECT_IDENTIFIER_get_arcs(const OBJECT_IDENTIFIER_t *st, asn_oid_arc_t *arcs, size_t arc_slots) { asn_oid_arc_t arc0, arc1; size_t num_arcs = 0; size_t off; ssize_t rd; if(!st || !st->buf) { errno = EINVAL; return -1; } rd = OBJECT_IDENTIFIER_get_first_arcs(st->buf, st->size, &arc0, &arc1); if(rd <= 0) { return -1; } num_arcs = 2; switch(arc_slots) { default: case 2: arcs[1] = arc1; /* Fall through */ case 1: arcs[0] = arc0; /* Fall through */ case 0: break; } for(off = rd; ; ) { asn_oid_arc_t arc; rd = OBJECT_IDENTIFIER_get_single_arc(st->buf + off, st->size - off, &arc); if(rd < 0) { return -1; } else if(rd == 0) { /* No more arcs. */ break; } else { off += rd; if(num_arcs < arc_slots) { arcs[num_arcs] = arc; } num_arcs++; } } if(off != st->size) { return -1; } return num_arcs; } /* * Save the single value as an object identifier arc. */ ssize_t OBJECT_IDENTIFIER_set_single_arc(uint8_t *arcbuf, size_t arcbuf_len, asn_oid_arc_t value) { /* * The following conditions must hold: * assert(arcbuf); */ uint8_t scratch[((sizeof(value) * CHAR_BIT + 6) / 7)]; uint8_t *scratch_end = &scratch[sizeof(scratch)-1]; uint8_t *b; size_t result_len; uint8_t mask; for(b = scratch_end, mask = 0; ; mask = 0x80, b--) { *b = mask | (value & 0x7f); value >>= 7; if(!value) { break; } } result_len = (scratch_end - b) + 1; if(result_len > arcbuf_len) { return -1; } memcpy(arcbuf, b, result_len); return result_len; } int OBJECT_IDENTIFIER_set_arcs(OBJECT_IDENTIFIER_t *st, const asn_oid_arc_t *arcs, size_t arc_slots) { uint8_t *buf; uint8_t *bp; ssize_t wrote; asn_oid_arc_t arc0; asn_oid_arc_t arc1; size_t size; size_t i; if(!st || !arcs || arc_slots < 2) { errno = EINVAL; return -1; } arc0 = arcs[0]; arc1 = arcs[1]; if(arc0 <= 1) { if(arc1 >= 40) { /* 8.19.4: At most 39 subsequent values (including 0) */ errno = ERANGE; return -1; } } else if(arc0 == 2) { if(arc1 > ASN_OID_ARC_MAX - 80) { errno = ERANGE; return -1; } } else if(arc0 > 2) { /* 8.19.4: Only three values are allocated from the root node */ errno = ERANGE; return -1; } /* * After above tests it is known that the value of arc0 is completely * trustworthy (0..2). However, the arc1's value is still meaningless. */ /* * Roughly estimate the maximum size necessary to encode these arcs. * This estimation implicitly takes in account the following facts, * that cancel each other: * * the first two arcs are encoded in a single value. * * the first value may require more space (+1 byte) * * the value of the first arc which is in range (0..2) */ size = ((sizeof(asn_oid_arc_t) * CHAR_BIT + 6) / 7) * arc_slots; bp = buf = (uint8_t *)MALLOC(size + 1); if(!buf) { /* ENOMEM */ return -1; } wrote = OBJECT_IDENTIFIER_set_single_arc(bp, size, arc0 * 40 + arc1); if(wrote <= 0) { FREEMEM(buf); return -1; } assert((size_t)wrote <= size); bp += wrote; size -= wrote; for(i = 2; i < arc_slots; i++) { wrote = OBJECT_IDENTIFIER_set_single_arc(bp, size, arcs[i]); if(wrote <= 0) { FREEMEM(buf); return -1; } assert((size_t)wrote <= size); bp += wrote; size -= wrote; } /* * Replace buffer. */ st->size = bp - buf; bp = st->buf; st->buf = buf; st->buf[st->size] = '\0'; if(bp) FREEMEM(bp); return 0; } ssize_t OBJECT_IDENTIFIER_parse_arcs(const char *oid_text, ssize_t oid_txt_length, asn_oid_arc_t *arcs, size_t arcs_count, const char **opt_oid_text_end) { size_t num_arcs = 0; const char *oid_end; enum { ST_LEADSPACE, ST_TAILSPACE, ST_AFTERVALUE, /* Next character ought to be '.' or a space */ ST_WAITDIGITS /* Next character is expected to be a digit */ } state = ST_LEADSPACE; if(!oid_text || oid_txt_length < -1 || (arcs_count && !arcs)) { if(opt_oid_text_end) *opt_oid_text_end = oid_text; errno = EINVAL; return -1; } if(oid_txt_length == -1) oid_txt_length = strlen(oid_text); #define _OID_CAPTURE_ARC(oid_text, oid_end) \ do { \ const char *endp = oid_end; \ unsigned long value; \ switch(asn_strtoul_lim(oid_text, &endp, &value)) { \ case ASN_STRTOX_EXTRA_DATA: \ case ASN_STRTOX_OK: \ if(value <= ASN_OID_ARC_MAX) { \ if(num_arcs < arcs_count) arcs[num_arcs] = value; \ num_arcs++; \ oid_text = endp - 1; \ break; \ } \ /* Fall through */ \ case ASN_STRTOX_ERROR_RANGE: \ if(opt_oid_text_end) *opt_oid_text_end = oid_text; \ errno = ERANGE; \ return -1; \ case ASN_STRTOX_ERROR_INVAL: \ case ASN_STRTOX_EXPECT_MORE: \ if(opt_oid_text_end) *opt_oid_text_end = oid_text; \ errno = EINVAL; \ return -1; \ } \ } while(0) for(oid_end = oid_text + oid_txt_length; oid_text broken OID */ return -1; case ST_LEADSPACE: case ST_WAITDIGITS: _OID_CAPTURE_ARC(oid_text, oid_end); state = ST_AFTERVALUE; continue; } break; default: /* Unexpected symbols */ state = ST_WAITDIGITS; break; } /* switch() */ break; } /* for() */ if(opt_oid_text_end) *opt_oid_text_end = oid_text; /* Finalize last arc */ switch(state) { case ST_LEADSPACE: return 0; /* No OID found in input data */ case ST_WAITDIGITS: errno = EINVAL; /* Broken OID */ return -1; case ST_AFTERVALUE: case ST_TAILSPACE: return num_arcs; } errno = EINVAL; /* Broken OID */ return -1; } /* * Generate values from the list of interesting values, or just a random * value up to the upper limit. */ static asn_oid_arc_t OBJECT_IDENTIFIER__biased_random_arc(asn_oid_arc_t upper_bound) { const asn_oid_arc_t values[] = {0, 1, 127, 128, 129, 254, 255, 256}; size_t idx; switch(asn_random_between(0, 2)) { case 0: idx = asn_random_between(0, sizeof(values) / sizeof(values[0]) - 1); if(values[idx] < upper_bound) { return values[idx]; } /* Fall through */ case 1: return asn_random_between(0, upper_bound); case 2: default: return upper_bound; } } asn_random_fill_result_t OBJECT_IDENTIFIER_random_fill(const asn_TYPE_descriptor_t *td, void **sptr, const asn_encoding_constraints_t *constraints, size_t max_length) { asn_random_fill_result_t result_ok = {ARFILL_OK, 1}; asn_random_fill_result_t result_failed = {ARFILL_FAILED, 0}; asn_random_fill_result_t result_skipped = {ARFILL_SKIPPED, 0}; OBJECT_IDENTIFIER_t *st; asn_oid_arc_t arcs[5]; size_t arcs_len = asn_random_between(2, 5); size_t i; (void)constraints; if(max_length < arcs_len) return result_skipped; if(*sptr) { st = *sptr; } else { st = CALLOC(1, sizeof(*st)); } arcs[0] = asn_random_between(0, 2); arcs[1] = OBJECT_IDENTIFIER__biased_random_arc( arcs[0] <= 1 ? 39 : (ASN_OID_ARC_MAX - 80)); for(i = 2; i < arcs_len; i++) { arcs[i] = OBJECT_IDENTIFIER__biased_random_arc(ASN_OID_ARC_MAX); } if(OBJECT_IDENTIFIER_set_arcs(st, arcs, arcs_len)) { if(st != *sptr) { ASN_STRUCT_FREE(*td, st); } return result_failed; } *sptr = st; result_ok.length = st->size; return result_ok; }