in Sources/CCryptoBoringSSL/crypto/asn1/a_mbstr.c [88:284]
int ASN1_mbstring_ncopy(ASN1_STRING **out, const unsigned char *in, int len,
int inform, unsigned long mask,
long minsize, long maxsize)
{
int str_type;
char free_out;
ASN1_STRING *dest;
size_t nchar = 0;
char strbuf[32];
if (len == -1)
len = strlen((const char *)in);
if (!mask)
mask = DIRSTRING_TYPE;
int (*decode_func)(CBS *, uint32_t*);
int error;
switch (inform) {
case MBSTRING_BMP:
decode_func = cbs_get_ucs2_be;
error = ASN1_R_INVALID_BMPSTRING;
break;
case MBSTRING_UNIV:
decode_func = cbs_get_utf32_be;
error = ASN1_R_INVALID_UNIVERSALSTRING;
break;
case MBSTRING_UTF8:
decode_func = cbs_get_utf8;
error = ASN1_R_INVALID_UTF8STRING;
break;
case MBSTRING_ASC:
decode_func = cbs_get_latin1;
error = ERR_R_INTERNAL_ERROR; // Latin-1 inputs are never invalid.
break;
default:
OPENSSL_PUT_ERROR(ASN1, ASN1_R_UNKNOWN_FORMAT);
return -1;
}
/* Check |minsize| and |maxsize| and work out the minimal type, if any. */
CBS cbs;
CBS_init(&cbs, in, len);
size_t utf8_len = 0;
while (CBS_len(&cbs) != 0) {
uint32_t c;
if (!decode_func(&cbs, &c)) {
OPENSSL_PUT_ERROR(ASN1, error);
return -1;
}
if (nchar == 0 &&
(inform == MBSTRING_BMP || inform == MBSTRING_UNIV) &&
c == 0xfeff) {
/* Reject byte-order mark. We could drop it but that would mean
* adding ambiguity around whether a BOM was included or not when
* matching strings.
*
* For a little-endian UCS-2 string, the BOM will appear as 0xfffe
* and will be rejected as noncharacter, below. */
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_CHARACTERS);
return -1;
}
/* Update which output formats are still possible. */
if ((mask & B_ASN1_PRINTABLESTRING) && !asn1_is_printable(c)) {
mask &= ~B_ASN1_PRINTABLESTRING;
}
if ((mask & B_ASN1_IA5STRING) && (c > 127)) {
mask &= ~B_ASN1_IA5STRING;
}
if ((mask & B_ASN1_T61STRING) && (c > 0xff)) {
mask &= ~B_ASN1_T61STRING;
}
if ((mask & B_ASN1_BMPSTRING) && (c > 0xffff)) {
mask &= ~B_ASN1_BMPSTRING;
}
if (!mask) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_CHARACTERS);
return -1;
}
nchar++;
utf8_len += cbb_get_utf8_len(c);
}
if (minsize > 0 && nchar < (size_t)minsize) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_STRING_TOO_SHORT);
BIO_snprintf(strbuf, sizeof strbuf, "%ld", minsize);
ERR_add_error_data(2, "minsize=", strbuf);
return -1;
}
if (maxsize > 0 && nchar > (size_t)maxsize) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_STRING_TOO_LONG);
BIO_snprintf(strbuf, sizeof strbuf, "%ld", maxsize);
ERR_add_error_data(2, "maxsize=", strbuf);
return -1;
}
/* Now work out output format and string type */
int (*encode_func)(CBB *, uint32_t) = cbb_add_latin1;
size_t size_estimate = nchar;
int outform = MBSTRING_ASC;
if (mask & B_ASN1_PRINTABLESTRING) {
str_type = V_ASN1_PRINTABLESTRING;
} else if (mask & B_ASN1_IA5STRING) {
str_type = V_ASN1_IA5STRING;
} else if (mask & B_ASN1_T61STRING) {
str_type = V_ASN1_T61STRING;
} else if (mask & B_ASN1_BMPSTRING) {
str_type = V_ASN1_BMPSTRING;
outform = MBSTRING_BMP;
encode_func = cbb_add_ucs2_be;
size_estimate = 2 * nchar;
} else if (mask & B_ASN1_UNIVERSALSTRING) {
str_type = V_ASN1_UNIVERSALSTRING;
encode_func = cbb_add_utf32_be;
size_estimate = 4 * nchar;
outform = MBSTRING_UNIV;
} else if (mask & B_ASN1_UTF8STRING) {
str_type = V_ASN1_UTF8STRING;
outform = MBSTRING_UTF8;
encode_func = cbb_add_utf8;
size_estimate = utf8_len;
} else {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_ILLEGAL_CHARACTERS);
return -1;
}
if (!out)
return str_type;
if (*out) {
free_out = 0;
dest = *out;
if (dest->data) {
dest->length = 0;
OPENSSL_free(dest->data);
dest->data = NULL;
}
dest->type = str_type;
} else {
free_out = 1;
dest = ASN1_STRING_type_new(str_type);
if (!dest) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
return -1;
}
*out = dest;
}
/* If both the same type just copy across */
if (inform == outform) {
if (!ASN1_STRING_set(dest, in, len)) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
return -1;
}
return str_type;
}
CBB cbb;
if (!CBB_init(&cbb, size_estimate + 1)) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_MALLOC_FAILURE);
goto err;
}
CBS_init(&cbs, in, len);
while (CBS_len(&cbs) != 0) {
uint32_t c;
if (!decode_func(&cbs, &c) ||
!encode_func(&cbb, c)) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_INTERNAL_ERROR);
goto err;
}
}
uint8_t *data = NULL;
size_t data_len;
if (/* OpenSSL historically NUL-terminated this value with a single byte,
* even for |MBSTRING_BMP| and |MBSTRING_UNIV|. */
!CBB_add_u8(&cbb, 0) ||
!CBB_finish(&cbb, &data, &data_len) ||
data_len < 1 ||
data_len > INT_MAX) {
OPENSSL_PUT_ERROR(ASN1, ERR_R_INTERNAL_ERROR);
OPENSSL_free(data);
goto err;
}
dest->length = (int)(data_len - 1);
dest->data = data;
return str_type;
err:
if (free_out)
ASN1_STRING_free(dest);
CBB_cleanup(&cbb);
return -1;
}