blob: ae83044aa6266481c99ec5481a5d74f30b4b17eb [file]
// Copyright 2000-2016 The OpenSSL Project Authors. 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.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <openssl/asn1.h>
#include <assert.h>
#include <limits.h>
#include <string.h>
#include <openssl/asn1t.h>
#include <openssl/mem.h>
#include "../bytestring/internal.h"
#include "../internal.h"
#include "internal.h"
using namespace bssl;
static int asn1_marshal_item(CBB *cbb, ASN1_VALUE **pval, const ASN1_ITEM *it,
CBS_ASN1_TAG tag);
static int asn1_marshal_template(CBB *cbb, ASN1_VALUE **pval,
const ASN1_TEMPLATE *tt, CBS_ASN1_TAG tag);
static int asn1_marshal_template_no_explicit(CBB *cbb, ASN1_VALUE **pval,
const ASN1_TEMPLATE *tt,
CBS_ASN1_TAG tag);
static int asn1_marshal_primitive(CBB *cbb, ASN1_VALUE **pval,
const ASN1_ITEM *it, CBS_ASN1_TAG tag);
static bool asn1_item_has_value(ASN1_VALUE **pval, const ASN1_ITEM *it);
static bool asn1_template_has_value(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt);
int ASN1_item_i2d(ASN1_VALUE *val, uint8_t **out, const ASN1_ITEM *it) {
return I2DFromCBB(/*initial_capacity=*/64, out, [&](CBB *cbb) -> bool {
return asn1_marshal_item(cbb, &val, it, /*tag=*/0);
});
}
// asn1_item_has_value returns whether `pval`, which must have type `it`,
// contains a value.
bool asn1_item_has_value(ASN1_VALUE **pval, const ASN1_ITEM *it) {
// Almost every `ASN1_VALUE` is a pointer, except for types represented as
// `ASN1_BOOLEAN`.
if (it->itype != ASN1_ITYPE_PRIMITIVE) {
// All non-primitive types are pointers.
return asn1_load_ptr(pval) != nullptr;
}
if (it->templates != nullptr) {
// This is an `ASN1_ITEM_TEMPLATE`. Recurse into the template.
return asn1_template_has_value(pval, it->templates);
}
if (it->utype != V_ASN1_BOOLEAN) {
return asn1_load_ptr(pval) != nullptr;
}
// `ASN1_BOOLEAN` may be omitted in two ways. -1 indicates an omitted
// OPTIONAL BOOLEAN. Additionally, if `it` is `ASN1_FBOOLEAN` or
// `ASN1_TBOOLEAN`, this is DEFAULT FALSE and DEFAULT TRUE, respectively. This
// is stored in `it->size`.
ASN1_BOOLEAN b = *reinterpret_cast<const ASN1_BOOLEAN *>(pval);
if (b == ASN1_BOOLEAN_NONE) {
return false;
}
if (it->size > 0) {
// This is DEFAULT TRUE, so only FALSE is present.
return b == ASN1_BOOLEAN_FALSE;
}
if (it->size == 0) {
// This is DEFAULT FALSE, so only TRUE is present.
return b != ASN1_BOOLEAN_FALSE;
}
return true;
}
// asn1_template_has_value returns whether `pval`, which must have type `tt`,
// contains a value.
bool asn1_template_has_value(ASN1_VALUE **pval, const ASN1_TEMPLATE *tt) {
if (tt->flags & ASN1_TFLG_SK_MASK) {
// If `tt` is a SEQUENCE OF or SET OF type, the C type is `STACK_OF(T)` and
// is a pointer.
return asn1_load_ptr(pval) != nullptr;
}
// Otherwise, `tt`'s representation is the underlying item.
return asn1_item_has_value(pval, ASN1_ITEM_ptr(tt->item));
}
// asn1_marshal_item marshals `pval`, of type `it`, and writes the result to
// `cbb`. It returns one on success and zero on error. If `tag` is non-zero, the
// value is implicitly tagged with `tag`.
static int asn1_marshal_item(CBB *cbb, ASN1_VALUE **pval, const ASN1_ITEM *it,
CBS_ASN1_TAG tag) {
// All fields are pointers, except for boolean `ASN1_ITYPE_PRIMITIVE`s. Those
// cases will be checked later.
if ((it->itype != ASN1_ITYPE_PRIMITIVE) && !asn1_load_ptr(pval)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
return 0;
}
switch (it->itype) {
case ASN1_ITYPE_PRIMITIVE:
if (it->templates) {
// This is an `ASN1_ITEM_TEMPLATE`, so the underlying template cannot be
// optional. Optionality should be expressed at the layer above.
if (it->templates->flags & ASN1_TFLG_OPTIONAL) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
return 0;
}
return asn1_marshal_template(cbb, pval, it->templates, tag);
}
return asn1_marshal_primitive(cbb, pval, it, tag);
case ASN1_ITYPE_MSTRING:
// CHOICE types cannot be implicitly tagged.
if (tag != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
return 0;
}
return asn1_marshal_any_string(cbb, asn1_load_ptr_as<ASN1_STRING>(pval));
case ASN1_ITYPE_CHOICE: {
// CHOICE types cannot be implicitly tagged.
if (tag != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
return 0;
}
int i = asn1_get_choice_selector(pval, it);
if (i < 0 || i >= it->tcount) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_NO_MATCHING_CHOICE_TYPE);
return 0;
}
const ASN1_TEMPLATE *chtt = &it->templates[i];
if (chtt->flags & ASN1_TFLG_OPTIONAL) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
return 0;
}
ASN1_VALUE **pchval = asn1_get_field_ptr(pval, chtt);
return asn1_marshal_template(cbb, pchval, chtt, /*tag=*/0);
}
case ASN1_ITYPE_EXTERN: {
// We don't support implicit tagging with external types.
if (tag != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
return 0;
}
auto *ef = static_cast<const ASN1_EXTERN_FUNCS *>(it->funcs);
return ef->asn1_ex_marshal(cbb, pval, it);
}
case ASN1_ITYPE_SEQUENCE: {
Span<const uint8_t> enc;
if (asn1_enc_restore(&enc, pval, it)) {
return CBB_add_bytes(cbb, enc.data(), enc.size());
}
tag = tag == 0 ? CBS_ASN1_SEQUENCE : (tag | CBS_ASN1_CONSTRUCTED);
CBB seq;
if (!CBB_add_asn1(cbb, &seq, tag)) {
return 0;
}
for (int i = 0; i < it->tcount; i++) {
const ASN1_TEMPLATE *tt = &it->templates[i];
const ASN1_TEMPLATE *seqtt = asn1_do_adb(pval, tt, /*nullerr=*/1);
if (!seqtt) {
return 0;
}
ASN1_VALUE **pseqval = asn1_get_field_ptr(pval, seqtt);
if (!asn1_template_has_value(pseqval, seqtt)) {
if ((seqtt->flags & ASN1_TFLG_OPTIONAL) != 0) {
continue;
}
OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
return 0;
}
if (!asn1_marshal_template(&seq, pseqval, seqtt, /*tag=*/0)) {
return 0;
}
}
return CBB_flush(cbb);
}
default:
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
return 0;
}
}
// asn1_marshal_template marshals `pval`, of type `tt`, and writes the result to
// `cbb`. It returns one on success and zero on error. If `tag` is non-zero, the
// value is implicitly tagged with `tag`.
//
// This function does not look at `ASN1_TFLG_OPTIONAL`. The caller is expected
// to have evaluated it already and, if applicable, skip this function.
static int asn1_marshal_template(CBB *cbb, ASN1_VALUE **pval,
const ASN1_TEMPLATE *tt, CBS_ASN1_TAG tag) {
uint32_t flags = tt->flags;
if (!(flags & ASN1_TFLG_EXPTAG)) {
return asn1_marshal_template_no_explicit(cbb, pval, tt, tag);
}
// Determine the explicit tag to use. If the caller supplied an implicit tag,
// it overrides this one. Otherwise, use the tag in the template.
if (tag == 0) {
tag = asn1_tag_to_cbs(flags & ASN1_TFLG_TAG_CLASS, tt->tag);
if (tag == 0) {
return 0;
}
}
CBB child;
return CBB_add_asn1(cbb, &child, tag | CBS_ASN1_CONSTRUCTED) &&
asn1_marshal_template_no_explicit(&child, pval, tt, /*tag=*/0) &&
CBB_flush(cbb);
}
// asn1_marshal_template_no_explicit behaves like `asn1_marshal_template` except
// it ignores any explicit tagging specified in `tt`.
static int asn1_marshal_template_no_explicit(CBB *cbb, ASN1_VALUE **pval,
const ASN1_TEMPLATE *tt,
CBS_ASN1_TAG tag) {
uint32_t flags = tt->flags;
// If `tt` provides implicit tagging and the caller did not already override
// the tag, pick up the tag from the template.
if ((flags & ASN1_TFLG_IMPTAG) && tag == 0) {
tag = asn1_tag_to_cbs(flags & ASN1_TFLG_TAG_CLASS, tt->tag);
if (tag == 0) {
return 0;
}
}
if (flags & ASN1_TFLG_SK_MASK) {
// This is a SET OF or SEQUENCE OF type.
STACK_OF(ASN1_VALUE) *sk = asn1_load_ptr_as<STACK_OF(ASN1_VALUE)>(pval);
if (sk == nullptr) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
return 0;
}
bool is_set = (flags & ASN1_TFLG_SET_OF) != 0;
if (is_set) {
// Historically, types with both bits set were mutated when serialized to
// apply the sort. We no longer support this.
assert((flags & ASN1_TFLG_SEQUENCE_OF) == 0);
}
if (tag == 0) {
tag = is_set ? CBS_ASN1_SET : CBS_ASN1_SEQUENCE;
}
CBB child;
if (!CBB_add_asn1(cbb, &child, tag | CBS_ASN1_CONSTRUCTED)) {
return 0;
}
for (size_t i = 0; i < sk_ASN1_VALUE_num(sk); i++) {
ASN1_VALUE *elem = sk_ASN1_VALUE_value(sk, i);
if (!asn1_marshal_item(&child, &elem, ASN1_ITEM_ptr(tt->item),
/*tag=*/0)) {
return 0;
}
}
// If this is a SET OF type, sort the encodings when flushing.
if (is_set) {
return CBB_flush_asn1_set_of(&child) && CBB_flush(cbb);
}
return CBB_flush(cbb);
}
return asn1_marshal_item(cbb, pval, ASN1_ITEM_ptr(tt->item), tag);
}
static int asn1_marshal_primitive(CBB *cbb, ASN1_VALUE **pval,
const ASN1_ITEM *it, CBS_ASN1_TAG tag) {
assert(it->itype == ASN1_ITYPE_PRIMITIVE);
// Historically, `it->funcs` for primitive types contained an
// `ASN1_PRIMITIVE_FUNCS` table of callbacks.
assert(it->funcs == nullptr);
if (!asn1_item_has_value(pval, it)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_VALUE);
return 0;
}
// Handle non-string types.
switch (it->utype) {
case V_ASN1_ANY:
// ANY types cannot be implicitly tagged.
if (tag != 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
return 0;
}
return asn1_marshal_any(cbb, asn1_load_ptr_as<ASN1_TYPE>(pval));
case V_ASN1_OBJECT:
return asn1_marshal_object(cbb, asn1_load_ptr_as<ASN1_OBJECT>(pval), tag);
case V_ASN1_NULL:
tag = tag == 0 ? CBS_ASN1_NULL : tag;
return CBB_add_asn1_element(cbb, tag, nullptr, 0);
case V_ASN1_BOOLEAN: {
// `ASN1_BOOLEAN` is not a pointer type.
ASN1_BOOLEAN b = *reinterpret_cast<const ASN1_BOOLEAN *>(pval);
tag = tag == 0 ? CBS_ASN1_BOOLEAN : tag;
CBB child;
return CBB_add_asn1(cbb, &child, tag) &&
CBB_add_u8(&child, b ? 0xff : 0x00) && //
CBB_flush(cbb);
}
}
// All other supported types are represented as strings.
const ASN1_STRING *str = asn1_load_ptr_as<ASN1_STRING>(pval);
switch (it->utype) {
case V_ASN1_BIT_STRING:
return asn1_marshal_bit_string(cbb, str, tag);
case V_ASN1_INTEGER:
return asn1_marshal_integer(cbb, str, tag);
case V_ASN1_ENUMERATED:
tag = tag == 0 ? CBS_ASN1_ENUMERATED : tag;
return asn1_marshal_integer(cbb, str, 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:
tag = tag == 0 ? static_cast<CBS_ASN1_TAG>(it->utype) : tag;
return asn1_marshal_octet_string(cbb, str, tag);
case V_ASN1_SEQUENCE:
// The `ASN1_SEQUENCE` item stores the entire element.
return CBB_add_bytes(cbb, ASN1_STRING_get0_data(str),
ASN1_STRING_length(str));
default:
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BAD_TEMPLATE);
return 0;
}
}