Make CBS/CBB-versions of crypto/asn1 types
I had hoped to do this incrementally, but X509_ALGOR contains an
ASN1_TYPE, so all types get imported at once. For now, these functions
are just wired up to tasn_dec.cc, but subsequent changes will read them
one-by-one.
For the parse convention, I opted for a caller-supplied output variable,
even though we've generally tried to avoid object reuse in the
caller-exposed d2i functions. At the level of core ASN.1 types, the
object reuse is hopefully manageable (and in practice I expect they'll
always be in the empty state). The motivation here is to let us
internally embed types into the X509, etc., structs and avoid the layers
of tiny allocations.
This does mean that the dispatch code inside ASN1_TYPE and the dispatch
code in tasn_dec.cc is no longer shared, but that sharing wasn't valid
by strict aliasing anyway. (Nothing in tasn_dec.cc is valid by strict
aliasing.)
Bug: 42290417
Change-Id: Idb6910e23f4fa4d01ee39ebcbf2ae09df14e87e3
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/81772
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: David Benjamin <davidben@google.com>
diff --git a/crypto/asn1/a_gentm.cc b/crypto/asn1/a_gentm.cc
index f3b2fae..7596a1f 100644
--- a/crypto/asn1/a_gentm.cc
+++ b/crypto/asn1/a_gentm.cc
@@ -36,6 +36,23 @@
return 1;
}
+int asn1_parse_generalized_time(CBS *cbs, ASN1_GENERALIZEDTIME *out,
+ CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_GENERALIZEDTIME : tag;
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, tag) ||
+ !CBS_parse_generalized_time(&child, nullptr,
+ /*allow_timezone_offset=*/0)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ if (!ASN1_STRING_set(out, CBS_data(&child), CBS_len(&child))) {
+ return 0;
+ }
+ out->type = V_ASN1_GENERALIZEDTIME;
+ return 1;
+}
+
int ASN1_GENERALIZEDTIME_check(const ASN1_GENERALIZEDTIME *d) {
return asn1_generalizedtime_to_tm(NULL, d);
}
diff --git a/crypto/asn1/a_int.cc b/crypto/asn1/a_int.cc
index e641705..3f2bc24 100644
--- a/crypto/asn1/a_int.cc
+++ b/crypto/asn1/a_int.cc
@@ -21,6 +21,7 @@
#include <openssl/bytestring.h>
#include <openssl/err.h>
#include <openssl/mem.h>
+#include <openssl/span.h>
#include "../internal.h"
#include "internal.h"
@@ -146,32 +147,13 @@
return len;
}
-ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **out, const unsigned char **inp,
- long len) {
- // This function can handle lengths up to INT_MAX - 1, but the rest of the
- // legacy ASN.1 code mixes integer types, so avoid exposing it to
- // ASN1_INTEGERS with larger lengths.
- if (len < 0 || len > INT_MAX / 2) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_TOO_LONG);
- return NULL;
- }
-
- CBS cbs;
- CBS_init(&cbs, *inp, (size_t)len);
+static int asn1_parse_integer_contents(bssl::Span<const uint8_t> in,
+ ASN1_INTEGER *out) {
+ CBS cbs = in;
int is_negative;
if (!CBS_is_valid_asn1_integer(&cbs, &is_negative)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_INTEGER);
- return NULL;
- }
-
- ASN1_INTEGER *ret = NULL;
- if (out == NULL || *out == NULL) {
- ret = ASN1_INTEGER_new();
- if (ret == NULL) {
- return NULL;
- }
- } else {
- ret = *out;
+ return 0;
}
// Convert to |ASN1_INTEGER|'s sign-and-magnitude representation. First,
@@ -192,33 +174,75 @@
}
}
- if (!ASN1_STRING_set(ret, CBS_data(&cbs), CBS_len(&cbs))) {
- goto err;
+ if (!ASN1_STRING_set(out, CBS_data(&cbs), CBS_len(&cbs))) {
+ return 0;
}
if (is_negative) {
- ret->type = V_ASN1_NEG_INTEGER;
- negate_twos_complement(ret->data, ret->length);
+ out->type = V_ASN1_NEG_INTEGER;
+ negate_twos_complement(out->data, out->length);
} else {
- ret->type = V_ASN1_INTEGER;
+ out->type = V_ASN1_INTEGER;
}
// The value should be minimally-encoded.
- assert(ret->length == 0 || ret->data[0] != 0);
+ assert(out->length == 0 || out->data[0] != 0);
// Zero is not negative.
- assert(!is_negative || ret->length > 0);
+ assert(!is_negative || out->length > 0);
+ return 1;
+}
+
+int asn1_parse_integer(CBS *cbs, ASN1_INTEGER *out, CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_INTEGER : tag;
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, tag)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ return asn1_parse_integer_contents(child, out);
+}
+
+int asn1_parse_enumerated(CBS *cbs, ASN1_ENUMERATED *out, CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_ENUMERATED : tag;
+ if (!asn1_parse_integer(cbs, out, tag)) {
+ return 0;
+ }
+ // Fix the type value.
+ out->type =
+ (out->type & V_ASN1_NEG) ? V_ASN1_NEG_ENUMERATED : V_ASN1_ENUMERATED;
+ return 1;
+}
+
+ASN1_INTEGER *c2i_ASN1_INTEGER(ASN1_INTEGER **out, const unsigned char **inp,
+ long len) {
+ if (len < 0) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_STRING_TOO_SHORT);
+ return nullptr;
+ }
+
+ ASN1_INTEGER *ret = nullptr;
+ if (out == nullptr || *out == nullptr) {
+ ret = ASN1_INTEGER_new();
+ if (ret == nullptr) {
+ return nullptr;
+ }
+ } else {
+ ret = *out;
+ }
+
+ if (!asn1_parse_integer_contents(bssl::Span(*inp, len), ret)) {
+ if (ret != nullptr && (out == nullptr || *out != ret)) {
+ ASN1_INTEGER_free(ret);
+ }
+ return nullptr;
+ }
*inp += len;
- if (out != NULL) {
+ if (out != nullptr) {
*out = ret;
}
return ret;
-err:
- if (ret != NULL && (out == NULL || *out != ret)) {
- ASN1_INTEGER_free(ret);
- }
- return NULL;
}
int ASN1_INTEGER_set_int64(ASN1_INTEGER *a, int64_t v) {
diff --git a/crypto/asn1/a_object.cc b/crypto/asn1/a_object.cc
index a4ede8a..bb4bc4a 100644
--- a/crypto/asn1/a_object.cc
+++ b/crypto/asn1/a_object.cc
@@ -27,26 +27,29 @@
#include "internal.h"
-int i2d_ASN1_OBJECT(const ASN1_OBJECT *in, unsigned char **outp) {
+int asn1_marshal_object(CBB *out, const ASN1_OBJECT *in, CBS_ASN1_TAG tag) {
if (in == NULL) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_PASSED_NULL_PARAMETER);
- return -1;
+ return 0;
}
if (in->length <= 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_OBJECT);
+ return 0;
+ }
+
+ tag = tag == 0 ? CBS_ASN1_OBJECT : tag;
+ return CBB_add_asn1_element(out, tag, in->data, in->length);
+}
+
+int i2d_ASN1_OBJECT(const ASN1_OBJECT *in, unsigned char **outp) {
+ bssl::ScopedCBB cbb;
+ if (!CBB_init(cbb.get(), static_cast<size_t>(in->length) + 2) ||
+ !asn1_marshal_object(cbb.get(), in, /*tag=*/0)) {
return -1;
}
- CBB cbb, child;
- if (!CBB_init(&cbb, (size_t)in->length + 2) ||
- !CBB_add_asn1(&cbb, &child, CBS_ASN1_OBJECT) ||
- !CBB_add_bytes(&child, in->data, in->length)) {
- CBB_cleanup(&cbb);
- return -1;
- }
-
- return CBB_finish_i2d(&cbb, outp);
+ return CBB_finish_i2d(cbb.get(), outp);
}
int i2t_ASN1_OBJECT(char *buf, int buf_len, const ASN1_OBJECT *a) {
@@ -139,6 +142,21 @@
return ret;
}
+ASN1_OBJECT *asn1_parse_object(CBS *cbs, CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_OBJECT : tag;
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, tag)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return nullptr;
+ }
+ if (!CBS_is_valid_asn1_oid(&child)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_OBJECT_ENCODING);
+ return nullptr;
+ }
+ return ASN1_OBJECT_create(NID_undef, CBS_data(&child), CBS_len(&child),
+ /*sn=*/nullptr, /*ln=*/nullptr);
+}
+
ASN1_OBJECT *ASN1_OBJECT_new(void) {
ASN1_OBJECT *ret;
diff --git a/crypto/asn1/a_type.cc b/crypto/asn1/a_type.cc
index 92e9ede..23629e0 100644
--- a/crypto/asn1/a_type.cc
+++ b/crypto/asn1/a_type.cc
@@ -170,3 +170,220 @@
return result;
}
+
+int asn1_parse_any(CBS *cbs, ASN1_TYPE *out) {
+ CBS_ASN1_TAG tag;
+ CBS elem;
+ size_t header_len;
+ if (!CBS_get_any_asn1_element(cbs, &elem, &tag, &header_len)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+
+ // Handle the non-string types.
+ if (tag == CBS_ASN1_OBJECT) {
+ bssl::UniquePtr<ASN1_OBJECT> obj(asn1_parse_object(&elem, /*tag=*/0));
+ if (obj == nullptr) {
+ return 0;
+ }
+ ASN1_TYPE_set(out, V_ASN1_OBJECT, obj.release());
+ return 1;
+ }
+ if (tag == CBS_ASN1_NULL) {
+ if (CBS_len(&elem) != header_len) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ ASN1_TYPE_set(out, V_ASN1_NULL, nullptr);
+ return 1;
+ }
+ if (tag == CBS_ASN1_BOOLEAN) {
+ int b;
+ if (!CBS_get_asn1_bool(&elem, &b)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ // V_ASN1_BOOLEAN will interpret the pointer as null for false and any
+ // arbitrary non-null pointer for true.
+ ASN1_TYPE_set(out, V_ASN1_BOOLEAN, b ? out : nullptr);
+ return 1;
+ }
+
+ // All other cases are handled identically to the string-based ANY parser.
+ bssl::UniquePtr<ASN1_STRING> str(ASN1_STRING_new());
+ if (str == nullptr || !asn1_parse_any_as_string(&elem, str.get())) {
+ return 0;
+ }
+ asn1_type_set0_string(out, str.release());
+ return 1;
+}
+
+int asn1_parse_any_as_string(CBS *cbs, ASN1_STRING *out) {
+ CBS_ASN1_TAG tag;
+ CBS elem;
+ size_t header_len;
+ if (!CBS_get_any_asn1_element(cbs, &elem, &tag, &header_len)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+
+ // Reject unexpectedly constructed or primitive universal types, rather than
+ // encoding them as an opaque |V_ASN1_OTHER|. As of X.680 (02/2021), tag
+ // numbers 0-36 have been allocated, except 15. Of these, only 8 (EXTERNAL),
+ // 11 (EMBEDDED PDV), 16 (SEQUENCE), 17 (SET), and 29 (CHARACTER STRING) are
+ // constructed.
+ const CBS_ASN1_TAG tag_class = (tag & CBS_ASN1_CLASS_MASK);
+ const CBS_ASN1_TAG number = tag & CBS_ASN1_TAG_NUMBER_MASK;
+ if (tag_class == CBS_ASN1_UNIVERSAL && number <= 36 && number != 15) {
+ const bool is_constructed = (tag & CBS_ASN1_CONSTRUCTED) != 0;
+ if (number == V_ASN1_EXTERNAL || number == 11 /* EMBEDDED PDV */ ||
+ number == V_ASN1_SEQUENCE || number == V_ASN1_SET ||
+ number == 29 /* CHARACTER STRING*/) {
+ if (!is_constructed) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_TYPE_NOT_CONSTRUCTED);
+ return 0;
+ }
+ } else {
+ if (is_constructed) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_TYPE_NOT_PRIMITIVE);
+ return 0;
+ }
+ }
+ }
+
+ // Historically, parsing high universal tag numbers made OpenSSL's
+ // |ASN1_STRING| representation ambiguous. We've since fixed this with
+ // |V_ASN1_OTHER| but, for now, continue to enforce the limit.
+ if (tag_class == CBS_ASN1_UNIVERSAL && number > V_ASN1_MAX_UNIVERSAL) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+
+ // These types are just parsed as |V_ASN1_OTHER| here. Check the contents
+ // before the generic |V_ASN1_OTHER| path.
+ CBS body = elem;
+ BSSL_CHECK(CBS_skip(&body, header_len));
+ switch (tag) {
+ case CBS_ASN1_OBJECT:
+ if (!CBS_is_valid_asn1_oid(&body)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_OBJECT_ENCODING);
+ return 0;
+ }
+ break;
+ case CBS_ASN1_NULL:
+ if (CBS_len(&body) != 0) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_NULL_IS_WRONG_LENGTH);
+ return 0;
+ }
+ break;
+ case CBS_ASN1_BOOLEAN: {
+ uint8_t v;
+ if (!CBS_get_u8(&body, &v) || CBS_len(&body) != 0) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_BOOLEAN_IS_WRONG_LENGTH);
+ return 0;
+ }
+ if (v != 0 && v != 0xff) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ break;
+ }
+ }
+
+ switch (tag) {
+ case CBS_ASN1_INTEGER:
+ return asn1_parse_integer(&elem, out, tag);
+ case CBS_ASN1_ENUMERATED:
+ return asn1_parse_enumerated(&elem, out, tag);
+ case CBS_ASN1_BITSTRING:
+ return asn1_parse_bit_string(&elem, out, tag);
+ case CBS_ASN1_UNIVERSALSTRING:
+ return asn1_parse_universal_string(&elem, out, tag);
+ case CBS_ASN1_BMPSTRING:
+ return asn1_parse_bmp_string(&elem, out, tag);
+ case CBS_ASN1_UTF8STRING:
+ return asn1_parse_utf8_string(&elem, out, tag);
+ case CBS_ASN1_UTCTIME:
+ // TODO(crbug.com/42290221): Reject timezone offsets here. We have no
+ // known cases where UTCTime inside ANY needs accept invalid timezones.
+ return asn1_parse_utc_time(&elem, out, tag, /*allow_timezone_offset=*/1);
+ case CBS_ASN1_GENERALIZEDTIME:
+ return asn1_parse_generalized_time(&elem, out, tag);
+ case CBS_ASN1_OCTETSTRING:
+ case CBS_ASN1_T61STRING:
+ case CBS_ASN1_IA5STRING:
+ case CBS_ASN1_NUMERICSTRING:
+ case CBS_ASN1_PRINTABLESTRING:
+ case CBS_ASN1_VIDEOTEXSTRING:
+ case CBS_ASN1_GRAPHICSTRING:
+ case CBS_ASN1_VISIBLESTRING:
+ case CBS_ASN1_GENERALSTRING:
+ // T61String is parsed as Latin-1, so all byte strings are valid. The
+ // others we currently do not enforce.
+ //
+ // TODO(crbug.com/42290290): Enforce the encoding of the other string
+ // types.
+ if (!asn1_parse_octet_string(&elem, out, tag)) {
+ return 0;
+ }
+ out->type = static_cast<int>(tag);
+ return 1;
+ default:
+ // All unrecognized types, or types that cannot be represented as
+ // |ASN1_STRING|, are represented as the whole element.
+ if (!ASN1_STRING_set(out, CBS_data(&elem), CBS_len(&elem))) {
+ return 0;
+ }
+ if (tag == CBS_ASN1_SEQUENCE) {
+ out->type = V_ASN1_SEQUENCE;
+ } else if (tag == CBS_ASN1_SET) {
+ out->type = V_ASN1_SET;
+ } else {
+ out->type = V_ASN1_OTHER;
+ }
+ return 1;
+ }
+}
+
+int asn1_marshal_any(CBB *out, const ASN1_TYPE *in) {
+ switch (in->type) {
+ case V_ASN1_OBJECT:
+ return asn1_marshal_object(out, in->value.object, /*tag=*/0);
+ case V_ASN1_NULL:
+ return CBB_add_asn1_element(out, CBS_ASN1_NULL, nullptr, 0);
+ case V_ASN1_BOOLEAN:
+ return CBB_add_asn1_bool(out, in->value.boolean != ASN1_BOOLEAN_FALSE);
+ case V_ASN1_INTEGER:
+ case V_ASN1_ENUMERATED:
+ return asn1_marshal_integer(out, in->value.integer,
+ static_cast<CBS_ASN1_TAG>(in->type));
+ case V_ASN1_BIT_STRING:
+ return asn1_marshal_bit_string(out, in->value.bit_string, /*tag=*/0);
+ case V_ASN1_OCTET_STRING:
+ case V_ASN1_NUMERICSTRING:
+ case V_ASN1_PRINTABLESTRING:
+ case V_ASN1_T61STRING:
+ case V_ASN1_VIDEOTEXSTRING:
+ case V_ASN1_IA5STRING:
+ case V_ASN1_UTCTIME:
+ case V_ASN1_GENERALIZEDTIME:
+ case V_ASN1_GRAPHICSTRING:
+ case V_ASN1_VISIBLESTRING:
+ case V_ASN1_GENERALSTRING:
+ case V_ASN1_UNIVERSALSTRING:
+ case V_ASN1_BMPSTRING:
+ case V_ASN1_UTF8STRING:
+ return asn1_marshal_octet_string(out, in->value.asn1_string,
+ static_cast<CBS_ASN1_TAG>(in->type));
+ case V_ASN1_SEQUENCE:
+ case V_ASN1_SET:
+ case V_ASN1_OTHER:
+ // These three types store the whole TLV as contents.
+ return CBB_add_bytes(out, ASN1_STRING_get0_data(in->value.asn1_string),
+ ASN1_STRING_length(in->value.asn1_string));
+ default:
+ // |ASN1_TYPE|s can have type -1 when default-constructed.
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
+ return 0;
+ }
+}
diff --git a/crypto/asn1/a_utctm.cc b/crypto/asn1/a_utctm.cc
index 5c9e359..a0298a2 100644
--- a/crypto/asn1/a_utctm.cc
+++ b/crypto/asn1/a_utctm.cc
@@ -37,6 +37,22 @@
return 1;
}
+int asn1_parse_utc_time(CBS *cbs, ASN1_UTCTIME *out, CBS_ASN1_TAG tag,
+ int allow_timezone_offset) {
+ tag = tag == 0 ? CBS_ASN1_UTCTIME : tag;
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, tag) ||
+ !CBS_parse_utc_time(&child, nullptr, allow_timezone_offset)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ if (!ASN1_STRING_set(out, CBS_data(&child), CBS_len(&child))) {
+ return 0;
+ }
+ out->type = V_ASN1_UTCTIME;
+ return 1;
+}
+
int ASN1_UTCTIME_check(const ASN1_UTCTIME *d) {
return asn1_utctime_to_tm(NULL, d, /*allow_timezone_offset=*/1);
}
diff --git a/crypto/asn1/asn1_lib.cc b/crypto/asn1/asn1_lib.cc
index ad8c822..ececcdc 100644
--- a/crypto/asn1/asn1_lib.cc
+++ b/crypto/asn1/asn1_lib.cc
@@ -363,3 +363,70 @@
const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *str) {
return str->data;
}
+
+int asn1_parse_octet_string(CBS *cbs, ASN1_STRING *out, CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_OCTETSTRING : tag;
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, tag)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ if (!ASN1_STRING_set(out, CBS_data(&child), CBS_len(&child))) {
+ return 0;
+ }
+ out->type = V_ASN1_OCTET_STRING;
+ return 1;
+}
+
+int asn1_marshal_octet_string(CBB *out, const ASN1_STRING *in,
+ CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_OCTETSTRING : tag;
+ return CBB_add_asn1_element(out, tag, ASN1_STRING_get0_data(in),
+ ASN1_STRING_length(in));
+}
+
+static int asn1_parse_character_string(CBS *cbs, ASN1_STRING *out,
+ CBS_ASN1_TAG tag, int str_type,
+ int (*get_char)(CBS *cbs, uint32_t *),
+ int bad_char_err) {
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, tag)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ CBS copy = child;
+ while (CBS_len(©) != 0) {
+ uint32_t c;
+ if (!get_char(©, &c)) {
+ OPENSSL_PUT_ERROR(ASN1, bad_char_err);
+ return 0;
+ }
+ }
+ if (!ASN1_STRING_set(out, CBS_data(&child), CBS_len(&child))) {
+ return 0;
+ }
+ out->type = str_type;
+ return 1;
+}
+
+int asn1_parse_bmp_string(CBS *cbs, ASN1_BMPSTRING *out, CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_BMPSTRING : tag;
+ return asn1_parse_character_string(cbs, out, tag, V_ASN1_BMPSTRING,
+ &CBS_get_ucs2_be,
+ ASN1_R_INVALID_BMPSTRING);
+}
+
+int asn1_parse_universal_string(CBS *cbs, ASN1_UNIVERSALSTRING *out,
+ CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_UNIVERSALSTRING : tag;
+ return asn1_parse_character_string(cbs, out, tag, V_ASN1_UNIVERSALSTRING,
+ &CBS_get_utf32_be,
+ ASN1_R_INVALID_UNIVERSALSTRING);
+}
+
+int asn1_parse_utf8_string(CBS *cbs, ASN1_UNIVERSALSTRING *out,
+ CBS_ASN1_TAG tag) {
+ tag = tag == 0 ? CBS_ASN1_UTF8STRING : tag;
+ return asn1_parse_character_string(cbs, out, tag, V_ASN1_UTF8STRING,
+ &CBS_get_utf8, ASN1_R_INVALID_UTF8STRING);
+}
diff --git a/crypto/asn1/asn1_test.cc b/crypto/asn1/asn1_test.cc
index 54d2cb4..be865ce 100644
--- a/crypto/asn1/asn1_test.cc
+++ b/crypto/asn1/asn1_test.cc
@@ -693,6 +693,7 @@
{0x05, 0x01, 0xff}, // NULL { `ff` }
{0x01, 0x00}, // BOOLEAN {}
{0x01, 0x02, 0x00, 0x00}, // BOOLEAN { `0000` }
+ {0x01, 0x01, 0x42}, // BOOLEAN { `42` }
};
for (const auto &t : kInvalidTests) {
SCOPED_TRACE(Bytes(t));
diff --git a/crypto/asn1/internal.h b/crypto/asn1/internal.h
index b64e55e..74657e0 100644
--- a/crypto/asn1/internal.h
+++ b/crypto/asn1/internal.h
@@ -48,7 +48,8 @@
const struct tm *from,
const struct tm *to);
-// Internal ASN1 structures and functions: not for application use
+
+// Object identifiers.
// These are used internally in the ASN1_OBJECT to keep track of
// whether the names and data need to be free()ed
@@ -72,6 +73,127 @@
ASN1_OBJECT *ASN1_OBJECT_new(void);
+// asn1_parse_object parses a DER-encoded ASN.1 OBJECT IDENTIFIER from |cbs| and
+// write the result to |out|. If |tag| is non-zero, the value is implicitly
+// tagged with |tag|. On success, it returns a newly-allocated |ASN1_OBJECT|
+// with the result and advances |cbs| past the parsed element.
+//
+// TODO(crbug.com/boringssl/414361735): This should return a bssl::UniquePtr,
+// but cannot until it is made C++ linkage.
+ASN1_OBJECT *asn1_parse_object(CBS *cbs, CBS_ASN1_TAG tag);
+
+// asn1_marshal_object marshals |in| as a DER-encoded, ASN.1 OBJECT IDENTIFIER
+// and writes the result to |out|. It returns one on success and zero on error.
+// If |tag| is non-zero, the tag is replaced with |tag|.
+int asn1_marshal_object(CBB *out, const ASN1_OBJECT *in, CBS_ASN1_TAG tag);
+
+
+// Strings.
+
+// asn1_is_printable returns one if |value| is a valid Unicode codepoint for an
+// ASN.1 PrintableString, and zero otherwise.
+int asn1_is_printable(uint32_t value);
+
+// asn1_string_init initializes |str|, which may be uninitialized, with type
+// |type|.
+void asn1_string_init(ASN1_STRING *str, int type);
+
+// asn1_string_cleanup releases memory associated with |str|'s value, without
+// freeing |str| itself.
+void asn1_string_cleanup(ASN1_STRING *str);
+
+// asn1_bit_string_length returns the number of bytes in |str| and sets
+// |*out_padding_bits| to the number of padding bits.
+//
+// This function should be used instead of |ASN1_STRING_length| to correctly
+// handle the non-|ASN1_STRING_FLAG_BITS_LEFT| case.
+int asn1_bit_string_length(const ASN1_BIT_STRING *str,
+ uint8_t *out_padding_bits);
+
+// The following functions parse a DER-encoded ASN.1 value of the specified
+// type from |cbs| and write the result to |*out|. If |tag| is non-zero, the
+// value is implicitly tagged with |tag|. On success, they return one and
+// advance |cbs| past the parsed element. On entry, |*out| must contain an
+// |ASN1_STRING| in some valid state.
+int asn1_parse_bit_string(CBS *cbs, ASN1_BIT_STRING *out, CBS_ASN1_TAG tag);
+int asn1_parse_integer(CBS *cbs, ASN1_INTEGER *out, CBS_ASN1_TAG tag);
+int asn1_parse_enumerated(CBS *cbs, ASN1_ENUMERATED *out, CBS_ASN1_TAG tag);
+int asn1_parse_octet_string(CBS *cbs, ASN1_STRING *out, CBS_ASN1_TAG tag);
+int asn1_parse_bmp_string(CBS *cbs, ASN1_BMPSTRING *out, CBS_ASN1_TAG tag);
+int asn1_parse_universal_string(CBS *cbs, ASN1_UNIVERSALSTRING *out,
+ CBS_ASN1_TAG tag);
+int asn1_parse_utf8_string(CBS *cbs, ASN1_UNIVERSALSTRING *out,
+ CBS_ASN1_TAG tag);
+int asn1_parse_generalized_time(CBS *cbs, ASN1_GENERALIZEDTIME *out,
+ CBS_ASN1_TAG tag);
+int asn1_parse_utc_time(CBS *cbs, ASN1_UTCTIME *out, CBS_ASN1_TAG tag,
+ int allow_timezone_offset);
+
+// asn1_parse_bit_string_with_bad_length behaves like |asn1_parse_bit_string|
+// but tolerates BER non-minimal, definite lengths.
+int asn1_parse_bit_string_with_bad_length(CBS *cbs, ASN1_BIT_STRING *out);
+
+// asn1_marshal_bit_string marshals |in| as a DER-encoded, ASN.1 BIT STRING and
+// writes the result to |out|. It returns one on success and zero on error. If
+// |tag| is non-zero, the tag is replaced with |tag|.
+int asn1_marshal_bit_string(CBB *out, const ASN1_BIT_STRING *in,
+ CBS_ASN1_TAG tag);
+
+// asn1_marshal_integer marshals |in| as a DER-encoded, ASN.1 INTEGER and writes
+// the result to |out|. It returns one on success and zero on error. If |tag| is
+// non-zero, the tag is replaced with |tag|. This can also be used to marshal an
+// ASN.1 ENUMERATED value by overriding the tag.
+int asn1_marshal_integer(CBB *out, const ASN1_INTEGER *in, CBS_ASN1_TAG tag);
+
+// asn1_marshal_octet_string marshals |in| as a DER-encoded, ASN.1 OCTET STRING
+// and writes the result to |out|. It returns one on success and zero on error.
+// If |tag| is non-zero, the tag is replaced with |tag|.
+//
+// This function may be used to marshal other string-based universal types whose
+// encoding is that of an implicitly-tagged OCTET STRING, e.g. UTF8String.
+int asn1_marshal_octet_string(CBB *out, const ASN1_STRING *in,
+ CBS_ASN1_TAG tag);
+
+OPENSSL_EXPORT int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d,
+ int allow_timezone_offset);
+OPENSSL_EXPORT int asn1_generalizedtime_to_tm(struct tm *tm,
+ const ASN1_GENERALIZEDTIME *d);
+
+
+// The ASN.1 ANY type.
+
+// asn1_type_value_as_pointer returns |a|'s value in pointer form. This is
+// usually the value object but, for BOOLEAN values, is 0 or 0xff cast to
+// a pointer.
+const void *asn1_type_value_as_pointer(const ASN1_TYPE *a);
+
+// asn1_type_set0_string sets |a|'s value to the object represented by |str| and
+// takes ownership of |str|.
+void asn1_type_set0_string(ASN1_TYPE *a, ASN1_STRING *str);
+
+// asn1_type_cleanup releases memory associated with |a|'s value, without
+// freeing |a| itself.
+void asn1_type_cleanup(ASN1_TYPE *a);
+
+// asn1_parse_any parses a DER-encoded ASN.1 value of any type from |cbs| and
+// writes the result to |*out|. On success, it advances |cbs| past the parsed
+// element and returns one. On entry, |*out| must contain an |ASN1_TYPE| in some
+// valid state.
+int asn1_parse_any(CBS *cbs, ASN1_TYPE *out);
+
+// asn1_parse_any_as_string behaves like |asn1_parse_any| but represents the
+// value as an |ASN1_STRING|. Types which are not represented with
+// |ASN1_STRING|, such as |ASN1_OBJECT|, are represented with type
+// |V_ASN1_OTHER|.
+int asn1_parse_any_as_string(CBS *cbs, ASN1_STRING *out);
+
+// asn1_marshal_any marshals |in| as a DER-encoded ASN.1 value and writes the
+// result to |out|. It returns one on success and zeron on error.
+int asn1_marshal_any(CBB *out, const ASN1_TYPE *in);
+
+
+// Support structures for the template-based encoder.
+
// ASN1_ENCODING is used to save the received encoding of an ASN.1 type. This
// avoids problems with invalid encodings that break signatures.
typedef struct ASN1_ENCODING_st {
@@ -84,11 +206,6 @@
CRYPTO_BUFFER *buf;
} ASN1_ENCODING;
-OPENSSL_EXPORT int asn1_utctime_to_tm(struct tm *tm, const ASN1_UTCTIME *d,
- int allow_timezone_offset);
-OPENSSL_EXPORT int asn1_generalizedtime_to_tm(struct tm *tm,
- const ASN1_GENERALIZEDTIME *d);
-
int ASN1_item_ex_new(ASN1_VALUE **pval, const ASN1_ITEM *it);
void ASN1_item_ex_free(ASN1_VALUE **pval, const ASN1_ITEM *it);
@@ -155,59 +272,6 @@
// asn1_encoding_clear clears the cached encoding in |enc|.
void asn1_encoding_clear(ASN1_ENCODING *enc);
-// asn1_type_value_as_pointer returns |a|'s value in pointer form. This is
-// usually the value object but, for BOOLEAN values, is 0 or 0xff cast to
-// a pointer.
-const void *asn1_type_value_as_pointer(const ASN1_TYPE *a);
-
-// asn1_type_set0_string sets |a|'s value to the object represented by |str| and
-// takes ownership of |str|.
-void asn1_type_set0_string(ASN1_TYPE *a, ASN1_STRING *str);
-
-// asn1_type_cleanup releases memory associated with |a|'s value, without
-// freeing |a| itself.
-void asn1_type_cleanup(ASN1_TYPE *a);
-
-// asn1_is_printable returns one if |value| is a valid Unicode codepoint for an
-// ASN.1 PrintableString, and zero otherwise.
-int asn1_is_printable(uint32_t value);
-
-// asn1_string_init initializes |str|, which may be uninitialized, with type
-// |type|.
-void asn1_string_init(ASN1_STRING *str, int type);
-
-// asn1_string_cleanup releases memory associated with |str|'s value, without
-// freeing |str| itself.
-void asn1_string_cleanup(ASN1_STRING *str);
-
-// asn1_bit_string_length returns the number of bytes in |str| and sets
-// |*out_padding_bits| to the number of padding bits.
-//
-// This function should be used instead of |ASN1_STRING_length| to correctly
-// handle the non-|ASN1_STRING_FLAG_BITS_LEFT| case.
-int asn1_bit_string_length(const ASN1_BIT_STRING *str,
- uint8_t *out_padding_bits);
-
-// asn1_parse_bit_string parses a DER-encoded ASN.1 BIT STRING and writes the
-// result to |out|. If |tag| is non-zero, the BIT STRING is implicitly tagged
-// with |tag|.
-int asn1_parse_bit_string(CBS *cbs, ASN1_BIT_STRING *out, CBS_ASN1_TAG tag);
-
-// asn1_parse_bit_string_with_bad_length behaves like |asn1_parse_bit_string|
-// but tolerates BER non-minimal, definite lengths.
-int asn1_parse_bit_string_with_bad_length(CBS *cbs, ASN1_BIT_STRING *out);
-
-// asn1_marshal_bit_string marshals |in| as a DER-encoded, ASN.1 BIT STRING and
-// writes the result to |out|. It returns one on success and zero on error. If
-// |tag| is non-zero, the tag is replaced with |tag|.
-int asn1_marshal_bit_string(CBB *out, const ASN1_BIT_STRING *in,
- CBS_ASN1_TAG tag);
-
-// asn1_marshal_integer marshals |in| as a DER-encoded, ASN.1 INTEGER and writes
-// the result to |out|. It returns one on success and zero on error. If |tag| is
-// non-zero, the tag is replaced with |tag|.
-int asn1_marshal_integer(CBB *out, const ASN1_INTEGER *in, CBS_ASN1_TAG tag);
-
typedef struct {
int nid;
long minsize;
diff --git a/crypto/asn1/tasn_dec.cc b/crypto/asn1/tasn_dec.cc
index 627c135..1d2b34b 100644
--- a/crypto/asn1/tasn_dec.cc
+++ b/crypto/asn1/tasn_dec.cc
@@ -43,8 +43,6 @@
static int asn1_template_noexp_d2i(ASN1_VALUE **val, const unsigned char **in,
long len, const ASN1_TEMPLATE *tt, char opt,
CRYPTO_BUFFER *buf, int depth);
-static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len,
- int utype, const ASN1_ITEM *it);
static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in,
long len, const ASN1_ITEM *it, int tag,
int aclass, char opt);
@@ -91,25 +89,6 @@
}
}
-static int is_supported_universal_type(int tag, int aclass) {
- if (aclass != V_ASN1_UNIVERSAL) {
- return 0;
- }
- return tag == V_ASN1_OBJECT || tag == V_ASN1_NULL || tag == V_ASN1_BOOLEAN ||
- tag == V_ASN1_BIT_STRING || tag == V_ASN1_INTEGER ||
- tag == V_ASN1_ENUMERATED || tag == V_ASN1_OCTET_STRING ||
- tag == V_ASN1_NUMERICSTRING || tag == V_ASN1_PRINTABLESTRING ||
- tag == V_ASN1_T61STRING || tag == V_ASN1_VIDEOTEXSTRING ||
- tag == V_ASN1_IA5STRING || tag == V_ASN1_UTCTIME ||
- tag == V_ASN1_GENERALIZEDTIME || tag == V_ASN1_GRAPHICSTRING ||
- tag == V_ASN1_VISIBLESTRING || tag == V_ASN1_GENERALSTRING ||
- tag == V_ASN1_UNIVERSALSTRING || tag == V_ASN1_BMPSTRING ||
- tag == V_ASN1_UTF8STRING || tag == V_ASN1_SET ||
- tag == V_ASN1_SEQUENCE;
-}
-
-// Macro to initialize and invalidate the cache
-
// Decode an ASN1 item, this currently behaves just like a standard 'd2i'
// function. 'in' points to a buffer to read the data from, in future we
// will have more advanced versions that can input data a piece at a time and
@@ -193,7 +172,6 @@
depth);
}
return asn1_d2i_ex_primitive(pval, in, len, it, tag, aclass, opt);
- break;
case ASN1_ITYPE_MSTRING:
// It never makes sense for multi-strings to have implicit tagging, so
@@ -573,6 +551,7 @@
skfield = NULL;
if (!asn1_item_ex_d2i(&skfield, &p, len, ASN1_ITEM_ptr(tt->item),
/*tag=*/-1, /*aclass=*/0, /*opt=*/0, buf, depth)) {
+ ASN1_item_ex_free(&skfield, ASN1_ITEM_ptr(tt->item));
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
goto err;
}
@@ -612,306 +591,211 @@
return 0;
}
+// TODO(crbug.com/42290418): Switch the whole file to use a CBS-based calling
+// convention.
+static int asn1_d2i_ex_primitive_cbs(ASN1_VALUE **pval, CBS *cbs,
+ const ASN1_ITEM *it, int tag, int aclass,
+ char opt);
+
+// asn1_d2i_ex_primitive returns one on success, zero on error, and -1 if an
+// optional value was skipped.
static int asn1_d2i_ex_primitive(ASN1_VALUE **pval, const unsigned char **in,
long inlen, const ASN1_ITEM *it, int tag,
int aclass, char opt) {
- int ret = 0, utype;
- long plen;
- char cst;
- const unsigned char *p;
- const unsigned char *cont = NULL;
- long len;
- if (!pval) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_NULL);
- return 0; // Should never happen
+ CBS cbs;
+ CBS_init(&cbs, *in, inlen);
+ int ret = asn1_d2i_ex_primitive_cbs(pval, &cbs, it, tag, aclass, opt);
+ if (ret <= 0) {
+ return ret;
}
+ *in = CBS_data(&cbs);
+ return 1;
+}
+static ASN1_STRING *ensure_string(ASN1_VALUE **pval) {
+ if (*pval) {
+ return (ASN1_STRING *)*pval;
+ }
+ ASN1_STRING *str = ASN1_STRING_new();
+ if (str == nullptr) {
+ return nullptr;
+ }
+ *pval = (ASN1_VALUE *)str;
+ return str;
+}
+
+static int asn1_d2i_ex_primitive_cbs(ASN1_VALUE **pval, CBS *cbs,
+ const ASN1_ITEM *it, int tag, int aclass,
+ char opt) {
+ // Historically, |it->funcs| for primitive types contained an
+ // |ASN1_PRIMITIVE_FUNCS| table of callbacks.
+ assert(it->funcs == NULL);
+
+ int utype;
assert(it->itype == ASN1_ITYPE_PRIMITIVE || it->itype == ASN1_ITYPE_MSTRING);
if (it->itype == ASN1_ITYPE_MSTRING) {
+ // MSTRING passes utype in |tag|, normally used for implicit tagging.
utype = tag;
tag = -1;
} else {
utype = it->utype;
}
+ // Handle ANY types.
if (utype == V_ASN1_ANY || utype == V_ASN1_ANY_AS_STRING) {
- // If type is ANY need to figure out type from tag
- unsigned char oclass;
if (tag >= 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_TAGGED_ANY);
return 0;
}
- if (opt) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_OPTIONAL_ANY);
- return 0;
- }
- const int is_string = utype == V_ASN1_ANY_AS_STRING;
- p = *in;
- ret = asn1_check_tlen(&plen, &utype, &oclass, &cst, &p, inlen, -1, 0, 0);
- if (!ret) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
- return 0;
- }
- if (!is_supported_universal_type(utype, oclass)) {
- utype = V_ASN1_OTHER;
- }
- // These three types are not represented as |ASN1_STRING|, so they must be
- // parsed separately and then treated as an opaque |V_ASN1_OTHER|.
- if (is_string && (utype == V_ASN1_OBJECT || utype == V_ASN1_NULL ||
- utype == V_ASN1_BOOLEAN)) {
- if (cst) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_TYPE_NOT_PRIMITIVE);
- return 0;
- }
- CBS cbs;
- CBS_init(&cbs, p, plen);
- if (utype == V_ASN1_OBJECT && !CBS_is_valid_asn1_oid(&cbs)) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_OBJECT_ENCODING);
- return 0;
- }
- if (utype == V_ASN1_NULL && CBS_len(&cbs) != 0) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_NULL_IS_WRONG_LENGTH);
- return 0;
- }
- if (utype == V_ASN1_BOOLEAN) {
- if (CBS_len(&cbs) != 1) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_BOOLEAN_IS_WRONG_LENGTH);
- return 0;
- }
- uint8_t v = CBS_data(&cbs)[0];
- if (v != 0 && v != 0xff) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
- return 0;
- }
- }
- utype = V_ASN1_OTHER;
+ if (opt && CBS_len(cbs) == 0) {
+ return -1; // Omitted OPTIONAL value.
}
}
- if (tag == -1) {
- tag = utype;
- aclass = V_ASN1_UNIVERSAL;
- }
- p = *in;
- // Check header
- ret = asn1_check_tlen(&plen, NULL, NULL, &cst, &p, inlen, tag, aclass, opt);
- if (!ret) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_NESTED_ASN1_ERROR);
- return 0;
- } else if (ret == -1) {
- return -1;
- }
- ret = 0;
- // SEQUENCE, SET and "OTHER" are left in encoded form
- if ((utype == V_ASN1_SEQUENCE) || (utype == V_ASN1_SET) ||
- (utype == V_ASN1_OTHER)) {
- // SEQUENCE and SET must be constructed
- if (utype != V_ASN1_OTHER && !cst) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_TYPE_NOT_CONSTRUCTED);
- return 0;
- }
-
- cont = *in;
- len = p - cont + plen;
- p += plen;
- } else if (cst) {
- // This parser historically supported BER constructed strings. We no
- // longer do and will gradually tighten this parser into a DER
- // parser. BER types should use |CBS_asn1_ber_to_der|.
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_TYPE_NOT_PRIMITIVE);
- return 0;
- } else {
- cont = p;
- len = plen;
- p += plen;
- }
-
- // We now have content length and type: translate into a structure
- if (!asn1_ex_c2i(pval, cont, len, utype, it)) {
- goto err;
- }
-
- *in = p;
- ret = 1;
-err:
- return ret;
-}
-
-// Translate ASN1 content octets into a structure
-
-static int asn1_ex_c2i(ASN1_VALUE **pval, const unsigned char *cont, long len,
- int utype, const ASN1_ITEM *it) {
- ASN1_VALUE **opval = NULL;
- ASN1_STRING *stmp;
- ASN1_TYPE *typ = NULL;
- int ret = 0;
- ASN1_INTEGER **tint;
-
- // Historically, |it->funcs| for primitive types contained an
- // |ASN1_PRIMITIVE_FUNCS| table of callbacks.
- assert(it->funcs == NULL);
-
- // If ANY type clear type and set pointer to internal value
- if (it->utype == V_ASN1_ANY) {
+ if (utype == V_ASN1_ANY) {
+ ASN1_TYPE *typ;
if (!*pval) {
typ = ASN1_TYPE_new();
if (typ == NULL) {
- goto err;
+ return 0;
}
*pval = (ASN1_VALUE *)typ;
} else {
typ = (ASN1_TYPE *)*pval;
}
-
- if (utype != typ->type) {
- ASN1_TYPE_set(typ, utype, NULL);
+ return asn1_parse_any(cbs, typ);
+ }
+ if (utype == V_ASN1_ANY_AS_STRING) {
+ ASN1_STRING *str = ensure_string(pval);
+ if (str == nullptr) {
+ return 0;
}
- opval = pval;
- pval = &typ->value.asn1_value;
+ return asn1_parse_any_as_string(cbs, str);
}
- // If implementing a type that is not represented in |ASN1_STRING|, the
- // |V_ASN1_ANY_AS_STRING| logic must be modified to redirect it to
- // |V_ASN1_OTHER|.
- switch (utype) {
- case V_ASN1_OBJECT:
- if (!c2i_ASN1_OBJECT((ASN1_OBJECT **)pval, &cont, len)) {
- goto err;
- }
- break;
+ // Convert the crypto/asn1 tag into a CBS one.
+ if (tag == -1) {
+ tag = utype;
+ aclass = V_ASN1_UNIVERSAL;
+ }
- case V_ASN1_NULL:
- if (len) {
+ // All edge cases of |utype| should have been handled already. |utype| is now
+ // either a primitive |ASN1_ITEM|, handled by |DECLARE_ASN1_ITEM|, or a
+ // multistring option with a corresponding |B_ASN1_*| constant.
+ assert(utype >= 0 && utype <= V_ASN1_MAX_UNIVERSAL);
+ CBS_ASN1_TAG cbs_tag =
+ (static_cast<CBS_ASN1_TAG>(aclass) << CBS_ASN1_TAG_SHIFT) |
+ static_cast<CBS_ASN1_TAG>(tag);
+ if (utype == V_ASN1_SEQUENCE || utype == V_ASN1_SET) {
+ cbs_tag |= CBS_ASN1_CONSTRUCTED;
+ }
+
+ if (opt && !CBS_peek_asn1_tag(cbs, cbs_tag)) {
+ return -1; // Omitted OPTIONAL value.
+ }
+
+ // Handle non-|ASN1_STRING| types.
+ switch (utype) {
+ case V_ASN1_OBJECT: {
+ bssl::UniquePtr<ASN1_OBJECT> obj(asn1_parse_object(cbs, cbs_tag));
+ if (obj == nullptr) {
+ return 0;
+ }
+ ASN1_OBJECT_free((ASN1_OBJECT *)*pval);
+ *pval = (ASN1_VALUE *)obj.release();
+ return 1;
+ }
+ case V_ASN1_NULL: {
+ CBS null;
+ if (!CBS_get_asn1(cbs, &null, cbs_tag)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ if (CBS_len(&null) != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NULL_IS_WRONG_LENGTH);
- goto err;
+ return 0;
}
*pval = (ASN1_VALUE *)1;
- break;
-
- case V_ASN1_BOOLEAN:
- if (len != 1) {
+ return 1;
+ }
+ case V_ASN1_BOOLEAN: {
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, cbs_tag)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
+ }
+ // TODO(crbug.com/42290221): Reject invalid BOOLEAN encodings and just
+ // call |CBS_get_asn1_bool|.
+ if (CBS_len(&child) != 1) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BOOLEAN_IS_WRONG_LENGTH);
- goto err;
- } else {
- ASN1_BOOLEAN *tbool;
- tbool = (ASN1_BOOLEAN *)pval;
- *tbool = *cont;
+ return 0;
}
- break;
+ ASN1_BOOLEAN *tbool;
+ tbool = (ASN1_BOOLEAN *)pval;
+ *tbool = CBS_data(&child)[0];
+ return 1;
+ }
+ }
+ // All other types as an |ASN1_STRING| representation.
+ ASN1_STRING *str = ensure_string(pval);
+ if (str == nullptr) {
+ return 0;
+ }
+
+ switch (utype) {
case V_ASN1_BIT_STRING:
- if (!c2i_ASN1_BIT_STRING((ASN1_BIT_STRING **)pval, &cont, len)) {
- goto err;
- }
- break;
-
+ return asn1_parse_bit_string(cbs, str, cbs_tag);
case V_ASN1_INTEGER:
+ return asn1_parse_integer(cbs, str, cbs_tag);
case V_ASN1_ENUMERATED:
- tint = (ASN1_INTEGER **)pval;
- if (!c2i_ASN1_INTEGER(tint, &cont, len)) {
- goto err;
- }
- // Fixup type to match the expected form
- (*tint)->type = utype | ((*tint)->type & V_ASN1_NEG);
- break;
-
+ return asn1_parse_enumerated(cbs, str, cbs_tag);
+ case V_ASN1_UNIVERSALSTRING:
+ return asn1_parse_universal_string(cbs, str, cbs_tag);
+ case V_ASN1_BMPSTRING:
+ return asn1_parse_bmp_string(cbs, str, cbs_tag);
+ case V_ASN1_UTF8STRING:
+ return asn1_parse_utf8_string(cbs, str, cbs_tag);
+ case V_ASN1_UTCTIME:
+ // TODO(crbug.com/42290417): Once |X509|'s parser is written by hand, we
+ // won't have any known compatibility constraints forcing an invalid
+ // parser here. At that point, we can make the general case strict.
+ return asn1_parse_utc_time(cbs, str, cbs_tag,
+ /*allow_timezone_offset=*/1);
+ case V_ASN1_GENERALIZEDTIME:
+ return asn1_parse_generalized_time(cbs, str, cbs_tag);
case V_ASN1_OCTET_STRING:
case V_ASN1_NUMERICSTRING:
case V_ASN1_PRINTABLESTRING:
case V_ASN1_T61STRING:
case V_ASN1_VIDEOTEXSTRING:
case V_ASN1_IA5STRING:
- case V_ASN1_UTCTIME:
- case V_ASN1_GENERALIZEDTIME:
case V_ASN1_GRAPHICSTRING:
case V_ASN1_VISIBLESTRING:
case V_ASN1_GENERALSTRING:
- case V_ASN1_UNIVERSALSTRING:
- case V_ASN1_BMPSTRING:
- case V_ASN1_UTF8STRING:
- case V_ASN1_OTHER:
- case V_ASN1_SET:
+ // T61String is parsed as Latin-1, so all byte strings are valid. The
+ // others we currently do not enforce.
+ //
+ // TODO(crbug.com/42290290): Enforce the encoding of the other string
+ // types.
+ if (!asn1_parse_octet_string(cbs, str, cbs_tag)) {
+ return 0;
+ }
+ str->type = utype;
+ return 1;
case V_ASN1_SEQUENCE: {
- CBS cbs;
- CBS_init(&cbs, cont, (size_t)len);
- if (utype == V_ASN1_BMPSTRING) {
- while (CBS_len(&cbs) != 0) {
- uint32_t c;
- if (!CBS_get_ucs2_be(&cbs, &c)) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_BMPSTRING);
- goto err;
- }
- }
+ // Save the entire element in the string.
+ CBS elem;
+ if (!CBS_get_asn1_element(cbs, &elem, cbs_tag)) {
+ OPENSSL_PUT_ERROR(ASN1, ASN1_R_DECODE_ERROR);
+ return 0;
}
- if (utype == V_ASN1_UNIVERSALSTRING) {
- while (CBS_len(&cbs) != 0) {
- uint32_t c;
- if (!CBS_get_utf32_be(&cbs, &c)) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_UNIVERSALSTRING);
- goto err;
- }
- }
- }
- if (utype == V_ASN1_UTF8STRING) {
- while (CBS_len(&cbs) != 0) {
- uint32_t c;
- if (!CBS_get_utf8(&cbs, &c)) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_UTF8STRING);
- goto err;
- }
- }
- }
- if (utype == V_ASN1_UTCTIME) {
- if (!CBS_parse_utc_time(&cbs, NULL, /*allow_timezone_offset=*/1)) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_TIME_FORMAT);
- goto err;
- }
- }
- if (utype == V_ASN1_GENERALIZEDTIME) {
- if (!CBS_parse_generalized_time(&cbs, NULL,
- /*allow_timezone_offset=*/0)) {
- OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_TIME_FORMAT);
- goto err;
- }
- }
- // TODO(https://crbug.com/boringssl/427): Check other string types.
-
- // All based on ASN1_STRING and handled the same
- if (!*pval) {
- stmp = ASN1_STRING_type_new(utype);
- if (!stmp) {
- goto err;
- }
- *pval = (ASN1_VALUE *)stmp;
- } else {
- stmp = (ASN1_STRING *)*pval;
- stmp->type = utype;
- }
- if (!ASN1_STRING_set(stmp, cont, len)) {
- ASN1_STRING_free(stmp);
- *pval = NULL;
- goto err;
- }
- break;
+ str->type = V_ASN1_SEQUENCE;
+ return ASN1_STRING_set(str, CBS_data(&elem), CBS_len(&elem));
}
-
default:
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
- goto err;
- }
- // If ASN1_ANY and NULL type fix up value
- if (typ && (utype == V_ASN1_NULL)) {
- typ->value.ptr = NULL;
- }
-
- ret = 1;
-err:
- if (!ret) {
- ASN1_TYPE_free(typ);
- if (opval) {
- *opval = NULL;
+ return 0;
}
- }
- return ret;
}
// Check an ASN1 tag and length: a bit like ASN1_get_object but it
diff --git a/crypto/asn1/tasn_typ.cc b/crypto/asn1/tasn_typ.cc
index 1d11ae8..3c1e13f 100644
--- a/crypto/asn1/tasn_typ.cc
+++ b/crypto/asn1/tasn_typ.cc
@@ -19,6 +19,9 @@
#include "internal.h"
+// TODO(crbug.com/42290417): While we need |ASN1_ITEM|s, the exposed new, free,
+// i2d, and d2i functions should call the underlying implementations directly.
+
#define IMPLEMENT_ASN1_STRING_FUNCTIONS(sname) \
IMPLEMENT_ASN1_TYPE(sname) \
IMPLEMENT_ASN1_ENCODE_FUNCTIONS_const_fname(sname, sname, sname) \