/* * Copyright 2014-2023 The GmSSL Project. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the License); you may * not use this file except in compliance with the License. * * http://www.apache.org/licenses/LICENSE-2.0 */ // https://www.obj-sys.com/asn1tutorial/node128.html #include #include #include #include #include #include #include #include #include #include #include static const char *asn1_tag_index[] = { "[0]", "[1]", "[2]", "[3]", "[4]", "[5]", "[6]", "[7]", "[8]", "[9]", "[10]", "[11]", "[12]", "[13]", "[14]", "[15]", "[16]", "[17]", "[18]", "[19]", "[20]", "[21]", "[22]", "[23]", "[24]", "[25]", "[26]", "[27]", "[28]", "[29]", "[30]", "[31]", }; const char *asn1_tag_name(int tag) { if (tag < 0 || tag > 0xff) { error_print(); return NULL; } switch (tag & 0xc0) { case ASN1_TAG_CONTENT_SPECIFIC: return asn1_tag_index[tag & 0xe0]; case ASN1_TAG_APPLICATION: return "Application"; case ASN1_TAG_PRIVATE: return "Private"; } switch (tag) { case ASN1_TAG_BOOLEAN: return "BOOLEAN"; case ASN1_TAG_INTEGER: return "INTEGER"; case ASN1_TAG_BIT_STRING: return "BIT STRING"; case ASN1_TAG_OCTET_STRING: return "OCTET STRING"; case ASN1_TAG_NULL: return "NULL"; case ASN1_TAG_OBJECT_IDENTIFIER: return "OBJECT IDENTIFIER"; case ASN1_TAG_ObjectDescriptor: return "ObjectDescriptor"; case ASN1_TAG_EXTERNAL: return "EXTERNAL"; case ASN1_TAG_REAL: return "REAL"; case ASN1_TAG_ENUMERATED: return "ENUMERATED"; case ASN1_TAG_EMBEDDED: return "EMBEDDED"; case ASN1_TAG_UTF8String: return "UTF8String"; case ASN1_TAG_RELATIVE_OID: return "RELATIVE_OID"; case ASN1_TAG_NumericString: return "NumericString"; case ASN1_TAG_PrintableString: return "PrintableString"; case ASN1_TAG_TeletexString: return "TeletexString"; case ASN1_TAG_VideotexString: return "VideotexString"; case ASN1_TAG_IA5String: return "IA5String"; case ASN1_TAG_UTCTime: return "UTCTime"; case ASN1_TAG_GeneralizedTime: return "GeneralizedTime"; case ASN1_TAG_GraphicString: return "GraphicString"; case ASN1_TAG_VisibleString: return "VisibleString"; case ASN1_TAG_GeneralString: return "GeneralString"; case ASN1_TAG_UniversalString: return "UniversalString"; case ASN1_TAG_CHARACTER_STRING: return "CHARACTER STRING"; case ASN1_TAG_BMPString: return "BMPString"; case ASN1_TAG_SEQUENCE: return "SEQUENCE"; case ASN1_TAG_SET: return "SET"; case ASN1_TAG_EXPLICIT: return "EXPLICIT"; } error_print(); return NULL; } // not in-use int asn1_tag_is_cstring(int tag) { switch (tag) { case ASN1_TAG_UTF8String: case ASN1_TAG_NumericString: case ASN1_TAG_PrintableString: case ASN1_TAG_TeletexString: case ASN1_TAG_IA5String: case ASN1_TAG_GeneralString: return 1; } return 0; } // not in-use int asn1_tag_to_der(int tag, uint8_t **out, size_t *outlen) { if (!outlen) { error_print(); return -1; } if (out && *out) { *(*out)++ = (uint8_t)tag; } (*outlen)++; return 1; } // not in-use int asn1_tag_from_der(int *tag, const uint8_t **in, size_t *inlen) { if (!tag || !in || !(*in) || !inlen) { error_print(); return -1; } if (*inlen == 0) { return 0; } *tag = *(*in)++; (*inlen)--; return 1; } int asn1_tag_from_der_readonly(int *tag, const uint8_t **in, size_t *inlen) { if (!tag || !in || !(*in) || !inlen) { error_print(); return -1; } if (*inlen == 0) { return 0; } *tag = **in; return 1; } int asn1_length_to_der(size_t len, uint8_t **out, size_t *outlen) { if (len > INT_MAX) { error_print(); return -1; } if (!outlen) { error_print(); return -1; } if (len < 128) { if (out && *out) { *(*out)++ = (uint8_t)len; } (*outlen)++; } else { uint8_t buf[4]; int nbytes; if (len < 256) nbytes = 1; else if (len < 65536) nbytes = 2; else if (len < (1 << 24)) nbytes = 3; else nbytes = 4; PUTU32(buf, (uint32_t)len); if (out && *out) { *(*out)++ = 0x80 + nbytes; memcpy(*out, buf + 4 - nbytes, nbytes); (*out) += nbytes; } (*outlen) += 1 + nbytes; } return 1; } int asn1_length_from_der(size_t *len, const uint8_t **in, size_t *inlen) { if (!len || !in || !(*in) || !inlen) { error_print(); return -1; } if (*inlen == 0) { error_print(); return -1; } if (**in < 128) { *len = *(*in)++; (*inlen)--; } else { uint8_t buf[4] = {0}; int nbytes = *(*in)++ & 0x7f; (*inlen)--; if (nbytes < 1 || nbytes > 4) { error_print(); return -1; } if (*inlen < nbytes) { error_print(); return -1; } memcpy(buf + 4 - nbytes, *in, nbytes); *len = (size_t)GETU32(buf); *in += nbytes; *inlen -= nbytes; } // check if the left input is enough for reading (d,dlen) if (*inlen < *len) { error_print(); return -2; // 特殊错误值用于 test_asn1_length() 的测试 // TODO: 修改 asn1test.c 的测试向量 } return 1; } // asn1_data_to_der do not check the validity of data int asn1_data_to_der(const uint8_t *data, size_t datalen, uint8_t **out, size_t *outlen) { if (!outlen) { error_print(); return -1; } if (datalen == 0) { return 0; } if (out && *out) { if (!data) { error_print(); return -1; } memcpy(*out, data, datalen); *out += datalen; } *outlen += datalen; return 1; } // not in-use int asn1_data_from_der(const uint8_t **data, size_t datalen, const uint8_t **in, size_t *inlen) { if (!data || !datalen || !in || !(*in) || !inlen) { error_print(); return -1; } if (*inlen < datalen) { error_print(); return -1; } *data = *in; *in += datalen; *inlen -= datalen; return 1; } int asn1_header_to_der(int tag, size_t dlen, uint8_t **out, size_t *outlen) { if (!outlen) { error_print(); return -1; } if (out && *out) { *(*out)++ = (uint8_t)tag; } (*outlen)++; (void)asn1_length_to_der(dlen, out, outlen); return 1; } int asn1_type_to_der(int tag, const uint8_t *d, size_t dlen, uint8_t **out, size_t *outlen) { if (!outlen) { error_print(); return -1; } if (!d) { if (dlen) { error_print(); return -1; } return 0; } // tag if (out && *out) { *(*out)++ = (uint8_t)tag; } (*outlen)++; // length (void)asn1_length_to_der(dlen, out, outlen); // data if (out && *out) { memcpy(*out, d, dlen); *out += dlen; } *outlen += dlen; return 1; } int asn1_type_from_der(int tag, const uint8_t **d, size_t *dlen, const uint8_t **in, size_t *inlen) { if (!d || !dlen || !in || !(*in) || !inlen) { error_print(); return -1; } // tag if (*inlen == 0 || **in != tag) { *d = NULL; *dlen = 0; return 0; } (*in)++; (*inlen)--; // length if (asn1_length_from_der(dlen, in, inlen) != 1) { error_print(); return -1; } // data *d = *in; *in += *dlen; *inlen -= *dlen; return 1; } int asn1_nonempty_type_to_der(int tag, const uint8_t *d, size_t dlen, uint8_t **out, size_t *outlen) { int ret; if (d && dlen == 0) { error_print(); return -1; } if ((ret = asn1_type_to_der(tag, d, dlen, out, outlen)) != 1) { if (ret) error_print(); return ret; } return 1; } int asn1_nonempty_type_from_der(int tag, const uint8_t **d, size_t *dlen, const uint8_t **in, size_t *inlen) { int ret; if ((ret = asn1_type_from_der(tag, d, dlen, in, inlen)) != 1) { if (ret) error_print(); return ret; } if (*dlen == 0) { error_print(); return -1; } return 1; } int asn1_any_type_from_der(int *tag, const uint8_t **d, size_t *dlen, const uint8_t **in, size_t *inlen) { if (!tag || !d || !dlen || !in || !(*in) || !inlen) { error_print(); return -1; } if (*inlen == 0) { *tag = - 1; *d = NULL; *dlen = 0; return 0; } *tag = *(*in)++; (*inlen)--; if (asn1_length_from_der(dlen, in, inlen) != 1) { error_print(); return -1; } *d = *in; *in += *dlen; *inlen -= *dlen; return 1; } // we need to check this is an asn.1 type int asn1_any_to_der(const uint8_t *a, size_t alen, uint8_t **out, size_t *outlen) { if (!outlen) { error_print(); return -1; } if (!a) { if (a) { error_print(); return -1; } return 0; } if (out && *out) { memcpy(*out, a, alen); *out += alen; } *outlen += alen; return 1; } int asn1_any_from_der(const uint8_t **a, size_t *alen, const uint8_t **in, size_t *inlen) { int ret; int tag; const uint8_t *d; size_t dlen; if (!a || !alen || !in || !(*in) || !inlen) { error_print(); return -1; } *a = *in; *alen = *inlen; if ((ret = asn1_any_type_from_der(&tag, &d, &dlen, in, inlen)) != 1) { if (ret) error_print(); return ret; } *alen -= *inlen; return 1; } const char *asn1_boolean_name(int val) { switch (val) { case 1: return "true"; case 0: return "false"; } return NULL; } int asn1_boolean_from_name(int *val, const char *name) { if (strcmp(name, "true") == 0) { *val = 1; return 1; } else if (strcmp(name, "false") == 0) { *val = 0; return 1; } *val = -1; return -1; } int asn1_boolean_to_der_ex(int tag, int val, uint8_t **out, size_t *outlen) { if (!outlen) { error_print(); return -1; } if (val < 0) { return 0; } if (out && *out) { *(*out)++ = tag; *(*out)++ = 0x01; *(*out)++ = val ? 0xff : 0x00; } (*outlen) += 3; return 1; } int asn1_boolean_from_der_ex(int tag, int *val, const uint8_t **in, size_t *inlen) { if (!val || !in || !(*in) || !inlen) { error_print(); return -1; } if (*inlen == 0 || (*in)[0] != tag) { *val = -1; return 0; } if (*inlen < 3) { error_print(); return -1; } if ((*in)[1] != 0x01) { error_print(); return -1; } if ((*in)[2] != ASN1_TRUE && (*in)[2] != ASN1_FALSE) { error_print(); return -1; } *val = ((*in)[2] == ASN1_TRUE) ? 1 : 0; *in += 3; *inlen -= 3; return 1; } int asn1_integer_to_der_ex(int tag, const uint8_t *a, size_t alen, uint8_t **out, size_t *outlen) { if (!outlen) { error_print(); return -1; } if (!a) { return 0; } if (alen <= 0 || alen > INT_MAX) { error_print(); return -1; } if (out && *out) *(*out)++ = tag; (*outlen)++; while (*a == 0 && alen > 1) { a++; alen--; } if (a[0] & 0x80) { asn1_length_to_der(alen + 1, out, outlen); if (out && *out) { *(*out)++ = 0x00; memcpy(*out, a, alen); (*out) += alen; } (*outlen) += 1 + alen; } else { asn1_length_to_der(alen, out ,outlen); if (out && *out) { memcpy(*out, a, alen); (*out) += alen; } (*outlen) += alen; } return 1; } int asn1_integer_from_der_ex(int tag, const uint8_t **a, size_t *alen, const uint8_t **in, size_t *inlen) { size_t len; if (!a || !alen || !in || !(*in) || !inlen) { error_print(); return -1; } // tag if (*inlen == 0 || **in != tag) { *a = NULL; *alen = 0; return 0; } (*in)++; (*inlen)--; // length (not zero) if (asn1_length_from_der(&len, in, inlen) != 1) { error_print(); return -1; } if (len == 0) { error_print(); return -1; } // check if ASN1_INTEGER is negative if (**in & 0x80) { error_print(); return -1; } // remove leading zero if (**in == 0 && len > 1) { (*in)++; (*inlen)--; len--; // the following bit should be one if (((**in) & 0x80) == 0) { error_print(); return -1; } } // no leading zeros if (**in == 0 && len > 1) { error_print(); return -1; } // return integer bytes *a = *in; *alen = len; *in += len; *inlen -= len; return 1; } int asn1_int_to_der_ex(int tag, int a, uint8_t **out, size_t *outlen) { uint8_t buf[4] = {0}; size_t len = 0; if (a == -1) { return 0; } while (a > 0) { buf[3 - len] = a & 0xff; a >>= 8; len++; } if (!len) { len = 1; } if (asn1_integer_to_der_ex(tag, buf + 4 - len, len, out, outlen) != 1) { error_print(); return -1; } return 1; } int asn1_int_from_der_ex(int tag, int *a, const uint8_t **in, size_t *inlen) { int ret; const uint8_t *p; size_t len; size_t i; if (!a || !in || !(*in) || !inlen) { error_print(); return -1; } if ((ret = asn1_integer_from_der_ex(tag, &p, &len, in, inlen)) != 1) { if (ret < 0) error_print(); else *a = -1; return ret; } if (len > sizeof(*a)) { error_print(); return -1; } *a = 0; for (i = 0; i < len; i++) { *a = ((*a) << 8) | p[i]; } if (*a < 0) { error_print(); return -1; } return 1; } int asn1_bit_string_to_der_ex(int tag, const uint8_t *bits, size_t nbits, uint8_t **out, size_t *outlen) { size_t nbytes = (nbits + 7) / 8; size_t unused_nbits = nbytes * 8 - nbits; if (!outlen) { error_print(); return -1; } if (!bits) { if (nbits) { error_print(); return -1; } return 0; } // tag if (out && *out) { *(*out)++ = tag; } (*outlen)++; // length (void)asn1_length_to_der(nbytes + 1, out, outlen); // unused num of bits if (out && *out) { *(*out)++ = (uint8_t)unused_nbits; } (*outlen)++; // bits if (out && *out) { memcpy(*out, bits, nbytes); *out += nbytes; } *outlen += nbytes; return 1; } int asn1_bit_string_from_der_ex(int tag, const uint8_t **bits, size_t *nbits, const uint8_t **in, size_t *inlen) { size_t len; int unused_bits; if (!bits || !nbits || !in || !(*in) || !inlen) { error_print(); return -1; } // tag if (*inlen == 0 || **in != tag) { *bits = NULL; *nbits = 0; return 0; } (*in)++; (*inlen)--; // length (min == 2) if (asn1_length_from_der(&len, in, inlen) != 1) { error_print(); return -1; } if (len < 2) { error_print(); return -1; } // unused_bits counter unused_bits = **in; if (unused_bits > 7) { error_print(); return -1; } (*in)++; (*inlen)--; len--; // return bits *bits = *in; *nbits = (len << 3) - unused_bits; *in += len; *inlen -= len; return 1; } int asn1_bit_octets_to_der_ex(int tag, const uint8_t *octs, size_t nocts, uint8_t **out, size_t *outlen) { int ret; if ((ret = asn1_bit_string_to_der_ex(tag, octs, nocts << 3, out, outlen)) != 1) { if (ret) error_print(); return ret; } return 1; } int asn1_bit_octets_from_der_ex(int tag, const uint8_t **octs, size_t *nocts, const uint8_t **in, size_t *inlen) { int ret; const uint8_t *bits; size_t nbits; if (!octs || !nocts) { error_print(); return -1; } if ((ret = asn1_bit_string_from_der_ex(tag, &bits, &nbits, in, inlen)) != 1) { if (ret < 0) error_print(); else { *octs = NULL; *nocts = 0; } return ret; } if (nbits % 8) { error_print(); return -1; } *octs = bits; *nocts = nbits >> 3; return 1; } int asn1_bits_to_der_ex(int tag, int bits, uint8_t **out, size_t *outlen) { size_t nbits = 0; uint8_t mask = 0x80; uint8_t buf[4] = {0}; int i = 0; if (bits < 0) { return 0; } while (bits > 0) { if (bits & 1) buf[i] |= mask; mask >>= 1; bits >>= 1; nbits++; if (nbits % 8 == 0) { i++; mask = 0x80; } } if (!nbits) { nbits = 1; } if (asn1_bit_string_to_der_ex(tag, buf, nbits, out, outlen) != 1) { error_print(); return -1; } return 1; } int asn1_bits_from_der_ex(int tag, int *bits, const uint8_t **in, size_t *inlen) { int ret; const uint8_t *p; uint8_t c; size_t nbits; size_t i; if (!bits) { error_print(); return -1; } if ((ret = asn1_bit_string_from_der_ex(tag, &p, &nbits, in, inlen)) != 1) { if (ret < 0) error_print(); else *bits = -1; return ret; } if (nbits > 31) { error_print(); return -1; } *bits = 0; for (i = 0; i < nbits; i++) { if (i % 8 == 0) { c = *p++; } *bits |= ((c & 0x80) >> 7) << i; c <<= 1; } return 1; } int asn1_bits_print(FILE *fp, int fmt, int ind, const char *label, const char **names, size_t names_cnt, int bits) { size_t i; format_print(fp, fmt, ind, "%s: ", label); for (i = 0; i < names_cnt; i++) { if (bits & 0x01) fprintf(fp, "%s%s", names[i], bits >> 1 ? "," : ""); bits >>= 1; } fprintf(fp, "\n"); if (bits) { error_print(); return -1; } return 1; } const char *asn1_null_name(void) { return "null"; } int asn1_null_to_der(uint8_t **out, size_t *outlen) { if (!outlen) { error_print(); return -1; } if (out && *out) { *(*out)++ = ASN1_TAG_NULL; *(*out)++ = 0x00; } *outlen += 2; return 1; } int asn1_null_from_der(const uint8_t **in, size_t *inlen) { if (!in || !(*in) || !inlen) { error_print(); return -1; } // tag if (*inlen == 0 || **in != ASN1_TAG_NULL) { return 0; } (*in)++; (*inlen)--; // value if (*inlen < 1) { error_print(); return -1; } if (**in != 0x00) { error_print(); return -1; } (*in)++; (*inlen)--; return 1; } static void asn1_oid_node_to_base128(uint32_t a, uint8_t **out, size_t *outlen) { uint8_t buf[5]; int n = 0; buf[n++] = a & 0x7f; a >>= 7; while (a) { buf[n++] = 0x80 | (a & 0x7f); a >>= 7; } while (n--) { if (out && *out) { *(*out)++ = buf[n]; } (*outlen)++; } } static int asn1_oid_node_from_base128(uint32_t *a, const uint8_t **in, size_t *inlen) { uint8_t buf[5]; int n = 0; int i; for (;;) { if ((*inlen)-- < 1 || n >= 5) { error_print(); return -1; } buf[n] = *(*in)++; if ((buf[n++] & 0x80) == 0) { break; } } // 32 - 7*4 = 4, so the first byte should be like 1000bbbb if (n == 5 && (buf[0] & 0x70)) { error_print(); return -1; } *a = 0; for (i = 0; i < n; i++) { *a = ((*a) << 7) | (buf[i] & 0x7f); } return 1; } int asn1_object_identifier_to_octets(const uint32_t *nodes, size_t nodes_cnt, uint8_t *out, size_t *outlen) { if (!nodes || !outlen) { error_print(); return -1; } if (nodes_cnt < ASN1_OID_MIN_NODES || nodes_cnt > ASN1_OID_MAX_NODES) { error_print(); return -1; } if (out) { *out++ = (uint8_t)(nodes[0] * 40 + nodes[1]); } (*outlen) = 1; nodes += 2; nodes_cnt -= 2; while (nodes_cnt--) { asn1_oid_node_to_base128(*nodes++, &out, outlen); } return 1; } int asn1_object_identifier_from_octets(uint32_t *nodes, size_t *nodes_cnt, const uint8_t *in, size_t inlen) { if (!nodes_cnt || !in || !inlen) { error_print(); return -1; } if (nodes) { *nodes++ = (*in) / 40; *nodes++ = (*in) % 40; } in++; inlen--; *nodes_cnt = 2; while (inlen) { uint32_t val; if (*nodes_cnt > ASN1_OID_MAX_NODES) { error_print(); return -1; } if (asn1_oid_node_from_base128(&val, &in, &inlen) < 0) { error_print(); return -1; } if (nodes) { *nodes++ = val; } (*nodes_cnt)++; } return 1; } int asn1_object_identifier_to_der_ex(int tag, const uint32_t *nodes, size_t nodes_cnt, uint8_t **out, size_t *outlen) { uint8_t octets[ASN1_OID_MAX_OCTETS]; size_t octetslen = 0; if (!outlen) { error_print(); return -1; } if (!nodes) { if (nodes_cnt) { error_print(); return -1; } return 0; } if (asn1_object_identifier_to_octets(nodes, nodes_cnt, octets, &octetslen) != 1) { error_print(); return -1; } if (out && *out) { *(*out)++ = tag; } (*outlen)++; (void)asn1_length_to_der(octetslen, out, outlen); if (out && *out) { memcpy(*out, octets, octetslen); *out += octetslen; } *outlen += octetslen; return 1; } int asn1_object_identifier_from_der_ex(int tag, uint32_t *nodes, size_t *nodes_cnt, const uint8_t **in, size_t *inlen) { size_t len; // unlike _from_octets(), _from_der() require output buffer if (!nodes || !nodes_cnt || !in || !(*in) || !inlen) { error_print(); return -1; } // tag if (*inlen == 0 || **in != tag) { *nodes_cnt = 0; return 0; } (*in)++; (*inlen)--; // length (not zero) if (asn1_length_from_der(&len, in, inlen) != 1) { error_print(); return -1; } if (len < ASN1_OID_MIN_OCTETS) { error_print(); return -1; } // parse OID if (asn1_object_identifier_from_octets(nodes, nodes_cnt, *in, len) != 1) { error_print(); return -1; } *in += len; *inlen -= len; return 1; } int asn1_object_identifier_equ(const uint32_t *a, size_t a_cnt, const uint32_t *b, size_t b_cnt) { if (!a || a_cnt < ASN1_OID_MIN_NODES || a_cnt > ASN1_OID_MAX_NODES || !b || b_cnt < ASN1_OID_MIN_NODES || b_cnt > ASN1_OID_MAX_NODES) { error_print(); return 0; // _equ() should return 1 or 0 } if (a_cnt != b_cnt || memcmp(a, b, b_cnt * sizeof(uint32_t))) { return 0; } return 1; } int asn1_object_identifier_print(FILE *fp, int format, int indent, const char *label, const char *name, const uint32_t *nodes, size_t nodes_cnt) { size_t i; format_print(fp, format, indent, "%s: %s", label, name ? name : "(unknown)"); if (nodes) { fprintf(fp, " ("); for (i = 0; i < nodes_cnt - 1; i++) { fprintf(fp, "%d.", (int)nodes[i]); } fprintf(fp, "%d)", nodes[i]); } fprintf(fp, "\n"); return 1; } const ASN1_OID_INFO *asn1_oid_info_from_name(const ASN1_OID_INFO *infos, size_t infos_cnt, const char *name) { size_t i; if (!infos || !infos_cnt || !name) { error_print(); return NULL; } for (i = 0; i < infos_cnt; i++) { if (strcmp(infos[i].name, name) == 0) { return &infos[i]; } } return NULL; } const ASN1_OID_INFO *asn1_oid_info_from_oid(const ASN1_OID_INFO *infos, size_t infos_cnt, int oid) { size_t i; if (!infos || !infos_cnt || oid < 0) { error_print(); return NULL; } for (i = 0; i < infos_cnt; i++) { if (infos[i].oid == oid) { return &infos[i]; } } return NULL; } int asn1_oid_info_from_der_ex(const ASN1_OID_INFO **info, uint32_t *nodes, size_t *nodes_cnt, const ASN1_OID_INFO *infos, size_t infos_cnt, const uint8_t **in, size_t *inlen) { int ret; size_t i; if (!info) { error_print(); return -1; } if ((ret = asn1_object_identifier_from_der(nodes, nodes_cnt, in, inlen)) != 1) { if (ret < 0) error_print(); else *info = NULL; return ret; } for (i = 0; i < infos_cnt; i++) { if (*nodes_cnt == infos[i].nodes_cnt && memcmp(nodes, infos[i].nodes, (*nodes_cnt) * sizeof(int)) == 0) { *info = &infos[i]; return 1; } } // OID with correct encoding but in the (infos, infos_cnt) list *info = NULL; return 1; } int asn1_oid_info_from_der(const ASN1_OID_INFO **info, const ASN1_OID_INFO *infos, size_t count, const uint8_t **in, size_t *inlen) { int ret; uint32_t nodes[ASN1_OID_MAX_NODES]; size_t nodes_cnt; if ((ret = asn1_oid_info_from_der_ex(info, nodes, &nodes_cnt, infos, count, in, inlen)) != 1) { if (ret < 0) error_print(); return ret; } if (*info == NULL) { asn1_object_identifier_print(stderr, 0, 0, "Unknown OID", NULL, nodes, nodes_cnt); error_print(); return -1; } return 1; } /* utf-8 character encoding 1-byte: 0xxxxxxx 2-byte: 110xxxxx 10xxxxxx 3-byte: 1110xxxx 10xxxxxx 10xxxxxx 4-byte: 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ static int asn1_utf8char_from_bytes(uint32_t *c, const uint8_t **pin, size_t *pinlen) { uint32_t utf8char; const uint8_t *in = *pin; size_t inlen = *pinlen; uint32_t utf8char_len, i; if (!inlen) { return 0; } if ((in[0] & 0x80) == 0x00) { utf8char_len = 1; } else if ((in[0] & 0xe0) == 0xc0) { utf8char_len = 2; } else if ((in[0] & 0xf0) == 0xe0) { utf8char_len = 3; } else if ((in[0] & 0xf8) == 0xf0) { utf8char_len = 4; } else { //error_print(); // disable error_print for _is_ compare return -1; } if (inlen < utf8char_len) { //error_print(); // disable error_print for _is_ compare return -1; } utf8char = in[0]; for (i = 1; i < utf8char_len; i++) { if ((in[i] & 0x60) != 0x80) { //error_print(); // disable error_print for _is_ compare return -1; } utf8char = (utf8char << 8) | in[i]; } *c = utf8char; (*pin) += utf8char_len; (*pinlen) -= utf8char_len; return 1; } int asn1_string_is_utf8_string(const char *a, size_t alen) { uint32_t utf8char; if (!a || !alen) { return 0; } while (alen) { if (asn1_utf8char_from_bytes(&utf8char, (const uint8_t **)&a, &alen) != 1) { return 0; } } return 1; } int asn1_utf8_string_to_der_ex(int tag, const char *d, size_t dlen, uint8_t **out, size_t *outlen) { int ret; if (asn1_string_is_utf8_string(d, dlen) != 1) { error_print(); return -1; } if ((ret = asn1_type_to_der(tag, (const uint8_t *)d, dlen, out, outlen)) != 1) { if (ret) error_print(); return ret; } return 1; } int asn1_utf8_string_from_der_ex(int tag, const char **a, size_t *alen, const uint8_t **in, size_t *inlen) { int ret; if ((ret = asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen)) != 1) { if (ret < 0) error_print(); return ret; } if (*a == NULL || *alen == 0) { error_print(); return -1; } if (asn1_string_is_utf8_string(*a, *alen) != 1) { error_print(); return -1; } return 1; } static int asn1_char_is_printable(int a) { if (('0' <= a && a <= '9') || ('a' <= a && a <= 'z') || ('A' <= a && a <= 'Z')) { return 1; } switch (a) { case ' ': case '\'': case '(': case ')': case '+': case ',': case '-': case '.': case '/': case ':': case '=': case '?': return 1; } return 0; } int asn1_string_is_printable_string(const char *a, size_t alen) { size_t i; for (i = 0; i < alen; i++) { if (asn1_char_is_printable(a[i]) != 1) { return 0; } } return 1; } int asn1_printable_string_case_ignore_match(const char *a, size_t alen, const char *b, size_t blen) { // remove leading and suffix space chars while (alen && *a == ' ') { a++; alen--; } while (alen && a[alen - 1] == ' ') { alen--; } // remove leading and suffix space chars while (blen && *b == ' ') { b++; blen--; } while (blen && b[blen - 1] == ' ') { blen--; } if (alen != blen) { return 0; } // case insensitive compare while (alen--) { if (toupper(*a) != toupper(*b)) { return 0; } } return 1; } int asn1_printable_string_to_der_ex(int tag, const char *d, size_t dlen, uint8_t **out, size_t *outlen) { int ret; if (asn1_string_is_printable_string(d, dlen) != 1) { error_print(); return -1; } if ((ret = asn1_type_to_der(tag, (const uint8_t *)d, dlen, out, outlen)) != 1) { if (ret) error_print(); return ret; } return 1; } int asn1_printable_string_from_der_ex(int tag, const char **a, size_t *alen, const uint8_t **in, size_t *inlen) { int ret; if ((ret = asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen)) != 1) { if (ret < 0) error_print(); return ret; } if (*a == NULL || *alen == 0) { error_print(); return -1; } if (asn1_string_is_printable_string(*a, *alen) != 1) { error_print(); return -1; } return 1; } int asn1_string_is_ia5_string(const char *a, size_t alen) { size_t i; for (i = 0; i < alen; i++) { if (!isascii(a[i])) { return 0; } } return 1; } int asn1_ia5_string_to_der_ex(int tag, const char *d, size_t dlen, uint8_t **out, size_t *outlen) { int ret; if (asn1_string_is_ia5_string(d, dlen) != 1) { error_print(); return -1; } if ((ret = asn1_type_to_der(tag, (const uint8_t *)d, dlen, out, outlen)) != 1) { if (ret) error_print(); return ret; } return 1; } int asn1_ia5_string_from_der_ex(int tag, const char **a, size_t *alen, const uint8_t **in, size_t *inlen) { int ret; if ((ret = asn1_type_from_der(tag, (const uint8_t **)a, alen, in, inlen)) != 1) { if (ret < 0) error_print(); return ret; } if (*a == NULL || *alen == 0) { error_print(); return -1; } if (asn1_string_is_ia5_string(*a, *alen) != 1) { error_print(); return -1; } return 1; } int asn1_string_print(FILE *fp, int fmt, int ind, const char *label, int tag, const uint8_t *d, size_t dlen) { format_print(fp, fmt, ind, "%s: ", label); while (dlen--) { fprintf(fp, "%c", *d++); } fprintf(fp, "\n"); return 1; } static int is_leap_year(int year) { return ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)) ? 1 : 0; } #define val(c) ((c)-'0') int asn1_time_from_str(int utc_time, time_t *timestamp, const char *str) { int time_str_len[2] = { 15, 13 }; int days_per_year[2] = { 365, 366 }; int days_per_month[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; int year, month, day, hour, minute, second; const char *p = str; int i; utc_time &= 1; for (i = 0; i < time_str_len[utc_time] - 1; i++) { if (!('0' <= str[i] && str[i] <= '9')) { error_print(); return -1; } } if (str[i] != 'Z') { error_print(); return -1; } if (utc_time) { year = val(p[0]) * 10 + val(p[1]); if (year <= 50) { year += 2000; } else { year += 1900; } p += 2; } else { year = val(p[0]) * 1000 + val(str[1]) * 100 + val(str[2]) * 10 + val(str[3]); p += 4; } if (is_leap_year(year)) { days_per_month[2] = 29; } month = val(p[0]) * 10 + val(p[1]); p += 2; day = val(p[0]) * 10 + val(p[1]); p += 2; hour = val(p[0]) * 10 + val(p[1]); p += 2; minute = val(p[0]) * 10 + val(p[1]); p += 2; second = val(p[0]) * 10 + val(p[1]); p += 2; if (year < 1970 || month < 1 || month > 12 || day < 1 || day > days_per_month[month] || hour < 0 || hour > 23 || minute < 0 || minute > 59 || second < 0 || second > 59) { error_print(); return -1; } day--; while (year-- > 1970) { day += days_per_year[is_leap_year(year)]; } while (month-- > 1) { day += days_per_month[month]; } *timestamp = (time_t)day * 86400 + hour * 3600 + minute * 60 + second; return 1; } int asn1_time_to_str(int utc_time, time_t timestamp, char *str) { int days_per_month[] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; int days_per_year[2] = { 365, 366 }; int max_year[2] = { 9999, 2050 }; int year, month, second, hour, minute; time_t day; char *p = str; utc_time &= 1; day = timestamp / 86400; second = timestamp % 86400; // In UTCTime, year in [1951, 2050], YY <= 50, year = 20YY; YY > 50, year = 19YY // For Validity, year SHOULD <= 2049 (NOT 2050) for (year = 1970; year <= max_year[utc_time]; year++) { if (day < days_per_year[is_leap_year(year)]) { break; } day -= days_per_year[is_leap_year(year)]; } if (year > max_year[utc_time]) { error_print(); return -1; } day++; if (is_leap_year(year)) { days_per_month[2] = 29; } for (month = 1; month <= 12; month++) { if (day <= days_per_month[month]) { break; } day -= days_per_month[month]; } hour = second / 3600; second %= 3600; minute = second / 60; second %= 60; if (utc_time) { memset(p, '0', 12); } else { memset(p, '0', 14); p[0] += (year / 100) / 10; p[1] += (year / 100) % 10; p += 2; } year %= 100; p[0] += year / 10; p[1] += year % 10; p[2] += month / 10; p[3] += month % 10; p[4] += (int)day / 10; p[5] += day % 10; p[6] += hour / 10; p[7] += hour % 10; p[8] += minute / 10; p[9] += minute % 10; p[10] += second / 10; p[11] += second % 10; p[12] = 'Z'; return 1; } int asn1_utc_time_to_der_ex(int tag, time_t a, uint8_t **out, size_t *outlen) { char buf[ASN1_UTC_TIME_STRLEN + 1] = {0}; int utc_time = 1; if (!outlen) { error_print(); return -1; } if (a == -1) { return 0; } if (asn1_time_to_str(utc_time, a, buf) != 1) { error_print(); return -1; } if (out && *out) { *(*out)++ = tag; } (*outlen)++; asn1_length_to_der(ASN1_UTC_TIME_STRLEN, out, outlen); if (out && *out) { memcpy(*out, buf, ASN1_UTC_TIME_STRLEN); (*out) += ASN1_UTC_TIME_STRLEN; } *outlen += ASN1_UTC_TIME_STRLEN; return 1; } int asn1_utc_time_from_der_ex(int tag, time_t *t, const uint8_t **in, size_t *inlen) { size_t len; if (!t || !in || !(*in) || !inlen) { error_print(); return -1; } // tag if (*inlen == 0 || **in != tag) { *t = -1; return 0; } (*in)++; (*inlen)--; // length if (asn1_length_from_der(&len, in, inlen) != 1) { error_print(); return -1; } if (len == sizeof("YYMMDDHHMMSSZ")-1) { char buf[sizeof("YYMMDDHHMMSSZ")-1]; memcpy(buf, *in, len); if (asn1_time_from_str(1, t, buf) != 1) { error_print(); return -1; } } else if (len == sizeof("YYMMDDHHMMSS+HHMM")-1) { char buf[sizeof("YYMMDDHHMMSS+HHMM")-1]; memcpy(buf, *in, len); // this format is not supported yet error_print(); return -1; } else { error_print(); return -1; } *in += len; *inlen -= len; return 1; } int asn1_generalized_time_to_der_ex(int tag, time_t a, uint8_t **out, size_t *outlen) { char buf[ASN1_GENERALIZED_TIME_STRLEN + 1] = {0}; int utc_time = 0; if (!outlen) { error_print(); return -1; } if (a == -1) { return 0; } if (asn1_time_to_str(utc_time, a, buf) != 1) { error_print(); return -1; } if (out && *out) *(*out)++ = tag; (*outlen)++; asn1_length_to_der(ASN1_GENERALIZED_TIME_STRLEN, out, outlen); if (out && *out) { memcpy(*out, buf, ASN1_GENERALIZED_TIME_STRLEN); (*out) += ASN1_GENERALIZED_TIME_STRLEN; } *outlen += ASN1_GENERALIZED_TIME_STRLEN; return 1; } int asn1_generalized_time_from_der_ex(int tag, time_t *t, const uint8_t **in, size_t *inlen) { size_t len; if (!t || !in || !(*in) || !inlen) { error_print(); return -1; } // tag if (*inlen == 0 || **in != tag) { *t = -1; return 0; } (*in)++; (*inlen)--; // length if (asn1_length_from_der(&len, in, inlen) != 1) { error_print(); return -1; } if (len == sizeof("YYYYMMDDHHMMSSZ")-1) { char buf[sizeof("YYYYMMDDHHMMSSZ")-1]; memcpy(buf, *in, len); if (asn1_time_from_str(0, t, buf) != 1) { error_print(); return -1; } } else if (len == sizeof("YYYYMMDDHHMMSS+HHMM")-1) { char buf[sizeof("YYYYMMDDHHMMSS+HHMM")-1]; memcpy(buf, *in, len); error_print(); return -1; } else { error_print(); return -1; } *in += len; *inlen -= len; return 1; } int asn1_sequence_of_int_to_der(const int *nums, size_t nums_cnt, uint8_t **out, size_t *outlen) { size_t len = 0; size_t i; if (!nums || !nums_cnt || !outlen) { error_print(); return -1; } for (i = 0; i < nums_cnt; i++) { if (asn1_int_to_der(nums[i], NULL, &len) != 1) { error_print(); return -1; } } if (asn1_sequence_header_to_der(len, out, outlen) != 1) { error_print(); return -1; } for (i = 0; i < nums_cnt; i++) { if (asn1_int_to_der(nums[i], out, outlen) != 1) { error_print(); return -1; } } return 1; } int asn1_sequence_of_int_from_der(int *nums, size_t *nums_cnt, size_t max_nums, const uint8_t **in, size_t *inlen) { int ret; const uint8_t *d; size_t dlen; if (!nums || !nums_cnt || !max_nums) { error_print(); return -1; } *nums_cnt = 0; if ((ret = asn1_sequence_from_der(&d, &dlen, in, inlen)) != 1) { if (ret < 0) error_print(); return ret; } while (dlen) { int num; if (*nums_cnt > max_nums) { error_print(); return -1; } if (asn1_int_from_der(&num, &d, &dlen) != 1) { error_print(); return -1; } *nums++ = num; (*nums_cnt)++; } return 1; } int asn1_sequence_of_int_print(FILE *fp, int fmt, int ind, const char *label, const uint8_t *d, size_t dlen) { int val; format_print(fp, fmt, ind, "%s: ", label); while (dlen) { if (asn1_int_from_der(&val, &d, &dlen) != 1) { error_print(); return -1; } fprintf(fp, "%d%s", val, dlen ? "," : ""); } fprintf(fp, "\n"); return 1; } int asn1_types_get_count(const uint8_t *d, size_t dlen, int tag, size_t *cnt) { int item_tag; const uint8_t *item_d; size_t item_dlen; if (!d || !cnt) { error_print(); return -1; } *cnt = 0; while (dlen) { if (asn1_any_type_from_der(&item_tag, &item_d, &item_dlen, &d, &dlen) != 1) { error_print(); return -1; } if (item_tag != tag) { error_print(); return -1; } (*cnt)++; } return 1; } int asn1_types_get_item_by_index(const uint8_t *d, size_t dlen, int tag, int index, const uint8_t **item_d, size_t *item_dlen) { int a_tag; const uint8_t *a_d; size_t a_dlen; int i = 0; if (!d || !item_d || !item_dlen) { error_print(); return -1; } while (dlen) { if (asn1_any_type_from_der(&a_tag, &a_d, &a_dlen, &d, &dlen) != 1) { error_print(); return -1; } if (a_tag != tag) { error_print(); return -1; } if (i++ == index) { *item_d = d; *item_dlen = dlen; return 1; // do not check the following } } error_print(); return -1; } int asn1_check(int expr) { if (expr) return 1; error_print(); return -1; } int asn1_length_is_zero(size_t len) { if (len) { error_print(); return -1; } return 1; } int asn1_length_le(size_t len1, size_t len2) { if (len1 > len2) { error_print(); format_print(stderr, 0, 0, "%s: %zu <= %zu failed\n", __FUNCTION__, len1, len2); return -1; } return 1; }