int CBS_asn1_ber_to_der(CBS *in, uint8_t **out, size_t *out_len) { CBB cbb; /* First, do a quick walk to find any indefinite-length elements. Most of the * time we hope that there aren't any and thus we can quickly return. */ char conversion_needed; if (!cbs_find_ber(in, &conversion_needed, 0)) { return 0; } if (!conversion_needed) { *out = NULL; *out_len = 0; return 1; } if (!CBB_init(&cbb, CBS_len(in)) || !cbs_convert_ber(in, &cbb, 0, 0, 0) || !CBB_finish(&cbb, out, out_len)) { CBB_cleanup(&cbb); return 0; } return 1; }
/* cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found| * depending on whether an indefinite length element was found. The value of * |in| is not changed. It returns one on success (i.e. |*ber_found| was set) * and zero on error. */ static int cbs_find_ber(CBS *orig_in, char *ber_found, unsigned depth) { CBS in; if (depth > kMaxDepth) { return 0; } CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in)); *ber_found = 0; while (CBS_len(&in) > 0) { CBS contents; unsigned tag; size_t header_len; if (!CBS_get_any_ber_asn1_element(&in, &contents, &tag, &header_len)) { return 0; } if (CBS_len(&contents) == header_len && header_len > 0 && CBS_data(&contents)[header_len-1] == 0x80) { *ber_found = 1; return 1; } if (tag & CBS_ASN1_CONSTRUCTED) { if (!CBS_skip(&contents, header_len) || !cbs_find_ber(&contents, ber_found, depth + 1)) { return 0; } } } return 1; }
/* cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found| * depending on whether an indefinite length element or constructed string was * found. The value of |orig_in| is not changed. It returns one on success (i.e. * |*ber_found| was set) and zero on error. */ static int cbs_find_ber(const CBS *orig_in, char *ber_found, unsigned depth) { CBS in; if (depth > kMaxDepth) { return 0; } CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in)); *ber_found = 0; while (CBS_len(&in) > 0) { CBS contents; unsigned tag; size_t header_len; if (!CBS_get_any_ber_asn1_element(&in, &contents, &tag, &header_len)) { return 0; } if (CBS_len(&contents) == header_len && header_len > 0 && CBS_data(&contents)[header_len-1] == 0x80) { /* Found an indefinite-length element. */ *ber_found = 1; return 1; } if (tag & CBS_ASN1_CONSTRUCTED) { if (is_string_type(tag)) { /* Constructed strings are only legal in BER and require conversion. */ *ber_found = 1; return 1; } if (!CBS_skip(&contents, header_len) || !cbs_find_ber(&contents, ber_found, depth + 1)) { return 0; } } } return 1; }