in crypto/asn1/tasn_enc.c [555:697]
static int asn1_ex_i2c(ASN1_VALUE **pval, unsigned char *cout, int *out_omit,
int *putype, const ASN1_ITEM *it) {
ASN1_BOOLEAN *tbool = NULL;
ASN1_STRING *strtmp;
ASN1_OBJECT *otmp;
int utype;
const unsigned char *cont;
unsigned char c;
int len;
// Historically, |it->funcs| for primitive types contained an
// |ASN1_PRIMITIVE_FUNCS| table of callbacks.
assert(it->funcs == NULL);
*out_omit = 0;
// Should type be omitted?
if ((it->itype != ASN1_ITYPE_PRIMITIVE) || (it->utype != V_ASN1_BOOLEAN)) {
if (!*pval) {
*out_omit = 1;
return 0;
}
}
if (it->itype == ASN1_ITYPE_MSTRING) {
// If MSTRING type set the underlying type
strtmp = (ASN1_STRING *)*pval;
utype = strtmp->type;
if (utype < 0 && utype != V_ASN1_OTHER) {
// MSTRINGs can have type -1 when default-constructed.
OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
return -1;
}
// Negative INTEGER and ENUMERATED values use |ASN1_STRING| type values
// that do not match their corresponding utype values. INTEGERs cannot
// participate in MSTRING types, but ENUMERATEDs can.
//
// TODO(davidben): Is this a bug? Although arguably one of the MSTRING
// types should contain more values, rather than less. See
// https://crbug.com/boringssl/412. But it is not possible to fit all
// possible ANY values into an |ASN1_STRING|, so matching the spec here
// is somewhat hopeless.
if (utype == V_ASN1_NEG_INTEGER) {
utype = V_ASN1_INTEGER;
} else if (utype == V_ASN1_NEG_ENUMERATED) {
utype = V_ASN1_ENUMERATED;
}
*putype = utype;
} else if (it->utype == V_ASN1_ANY) {
// If ANY set type and pointer to value
ASN1_TYPE *typ;
typ = (ASN1_TYPE *)*pval;
utype = typ->type;
if (utype < 0 && utype != V_ASN1_OTHER) {
// |ASN1_TYPE|s can have type -1 when default-constructed.
OPENSSL_PUT_ERROR(ASN1, ASN1_R_WRONG_TYPE);
return -1;
}
*putype = utype;
pval = &typ->value.asn1_value;
} else {
utype = *putype;
}
switch (utype) {
case V_ASN1_OBJECT:
otmp = (ASN1_OBJECT *)*pval;
cont = otmp->data;
len = otmp->length;
if (len == 0) {
// Some |ASN1_OBJECT|s do not have OIDs and cannot be serialized.
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_OBJECT);
return -1;
}
break;
case V_ASN1_NULL:
cont = NULL;
len = 0;
break;
case V_ASN1_BOOLEAN:
tbool = (ASN1_BOOLEAN *)pval;
if (*tbool == ASN1_BOOLEAN_NONE) {
*out_omit = 1;
return 0;
}
if (it->utype != V_ASN1_ANY) {
// Default handling if value == size field then omit
if ((*tbool && (it->size > 0)) || (!*tbool && !it->size)) {
*out_omit = 1;
return 0;
}
}
c = *tbool ? 0xff : 0x00;
cont = &c;
len = 1;
break;
case V_ASN1_BIT_STRING: {
int ret =
i2c_ASN1_BIT_STRING((ASN1_BIT_STRING *)*pval, cout ? &cout : NULL);
// |i2c_ASN1_BIT_STRING| returns zero on error instead of -1.
return ret <= 0 ? -1 : ret;
}
case V_ASN1_INTEGER:
case V_ASN1_ENUMERATED: {
// |i2c_ASN1_INTEGER| also handles ENUMERATED.
int ret = i2c_ASN1_INTEGER((ASN1_INTEGER *)*pval, cout ? &cout : NULL);
// |i2c_ASN1_INTEGER| returns zero on error instead of -1.
return ret <= 0 ? -1 : ret;
}
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_SEQUENCE:
case V_ASN1_SET:
default:
// All based on ASN1_STRING and handled the same
strtmp = (ASN1_STRING *)*pval;
cont = strtmp->data;
len = strtmp->length;
break;
}
if (cout && len) {
OPENSSL_memcpy(cout, cont, len);
}
return len;
}