Просмотр исходного кода

Reimplement OBJ_txt2obj and add a lower-level function.

OBJ_txt2obj is currently implemented using BIGNUMs which is absurd. It
also depends on the giant OID table, which is undesirable. Write a new
one and expose the low-level function so Chromium can use it without the
OID table.

Bug: chromium:706445
Change-Id: I61ff750a914194f8776cb8d81ba5d3eb5eaa3c3d
Reviewed-on: https://boringssl-review.googlesource.com/23364
Commit-Queue: David Benjamin <davidben@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
Reviewed-by: Steven Valdez <svaldez@google.com>
kris/onging/CECPQ3_patch15
David Benjamin 7 лет назад
committed by CQ bot account: commit-bot@chromium.org
Родитель
Сommit
47b8f00fdc
8 измененных файлов: 191 добавлений и 216 удалений
  1. +0
    -128
      crypto/asn1/a_object.c
  2. +54
    -0
      crypto/bytestring/bytestring_test.cc
  3. +95
    -18
      crypto/bytestring/cbb.c
  4. +1
    -0
      crypto/err/obj.errordata
  5. +29
    -69
      crypto/obj/obj.c
  6. +0
    -1
      include/openssl/asn1.h
  7. +11
    -0
      include/openssl/bytestring.h
  8. +1
    -0
      include/openssl/obj.h

+ 0
- 128
crypto/asn1/a_object.c Просмотреть файл

@@ -87,134 +87,6 @@ int i2d_ASN1_OBJECT(ASN1_OBJECT *a, unsigned char **pp)
return (objsize);
}

int a2d_ASN1_OBJECT(unsigned char *out, int olen, const char *buf, int num)
{
int i, first, len = 0, c, use_bn;
char ftmp[24], *tmp = ftmp;
int tmpsize = sizeof ftmp;
const char *p;
unsigned long l;
BIGNUM *bl = NULL;

if (num == 0)
return (0);
else if (num == -1)
num = strlen(buf);

p = buf;
c = *(p++);
num--;
if ((c >= '0') && (c <= '2')) {
first = c - '0';
} else {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_FIRST_NUM_TOO_LARGE);
goto err;
}

if (num <= 0) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_MISSING_SECOND_NUMBER);
goto err;
}
c = *(p++);
num--;
for (;;) {
if (num <= 0)
break;
if ((c != '.') && (c != ' ')) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_SEPARATOR);
goto err;
}
l = 0;
use_bn = 0;
for (;;) {
if (num <= 0)
break;
num--;
c = *(p++);
if ((c == ' ') || (c == '.'))
break;
if ((c < '0') || (c > '9')) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_INVALID_DIGIT);
goto err;
}
if (!use_bn && l >= ((ULONG_MAX - 80) / 10L)) {
use_bn = 1;
if (!bl)
bl = BN_new();
if (!bl || !BN_set_word(bl, l))
goto err;
}
if (use_bn) {
if (!BN_mul_word(bl, 10L)
|| !BN_add_word(bl, c - '0'))
goto err;
} else
l = l * 10L + (long)(c - '0');
}
if (len == 0) {
if ((first < 2) && (l >= 40)) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_SECOND_NUMBER_TOO_LARGE);
goto err;
}
if (use_bn) {
if (!BN_add_word(bl, first * 40))
goto err;
} else
l += (long)first *40;
}
i = 0;
if (use_bn) {
int blsize;
blsize = BN_num_bits(bl);
blsize = (blsize + 6) / 7;
if (blsize > tmpsize) {
if (tmp != ftmp)
OPENSSL_free(tmp);
tmpsize = blsize + 32;
tmp = OPENSSL_malloc(tmpsize);
if (!tmp)
goto err;
}
while (blsize--) {
BN_ULONG t = BN_div_word(bl, 0x80L);
if (t == (BN_ULONG)-1)
goto err;
tmp[i++] = (unsigned char)t;
}
} else {

for (;;) {
tmp[i++] = (unsigned char)l & 0x7f;
l >>= 7L;
if (l == 0L)
break;
}

}
if (out != NULL) {
if (len + i > olen) {
OPENSSL_PUT_ERROR(ASN1, ASN1_R_BUFFER_TOO_SMALL);
goto err;
}
while (--i > 0)
out[len++] = tmp[i] | 0x80;
out[len++] = tmp[0];
} else
len += i;
}
if (tmp != ftmp)
OPENSSL_free(tmp);
if (bl)
BN_free(bl);
return (len);
err:
if (tmp != ftmp)
OPENSSL_free(tmp);
if (bl)
BN_free(bl);
return (0);
}

int i2t_ASN1_OBJECT(char *buf, int buf_len, ASN1_OBJECT *a)
{
return OBJ_obj2txt(buf, buf_len, a, 0);


+ 54
- 0
crypto/bytestring/bytestring_test.cc Просмотреть файл

@@ -886,3 +886,57 @@ TEST(CBSTest, BitString) {
CBS_asn1_bitstring_has_bit(&cbs, test.bit));
}
}

TEST(CBBTest, AddOIDFromText) {
const struct {
const char *in;
bool ok;
std::vector<uint8_t> out;
} kTests[] = {
// Some valid values.
{"1.2.3.4", true, {0x2a, 0x3, 0x4}},
{"1.2.840.113554.4.1.72585",
true,
{0x2a, 0x86, 0x48, 0x86, 0xf7, 0x12, 0x04, 0x01, 0x84, 0xb7, 0x09}},
// Test edge cases around the first component.
{"0.39", true, {0x27}},
{"0.40", false, {}},
{"1.0", true, {0x28}},
{"1.39", true, {0x4f}},
{"1.40", false, {}},
{"2.0", true, {0x50}},
{"2.1", true, {0x51}},
{"2.40", true, {0x78}},
// The empty string is not an OID.
{"", false, {}},
// No empty components.
{".1.2.3.4.5", false, {}},
{"1..2.3.4.5", false, {}},
{"1.2.3.4.5.", false, {}},
// There must be at least two components.
{"1", false, {}},
// No extra leading zeros.
{"00.1.2.3.4", false, {}},
{"01.1.2.3.4", false, {}},
// Check for overflow.
{"1.2.4294967295", true, {0x2a, 0x8f, 0xff, 0xff, 0xff, 0x7f}},
{"1.2.4294967296", false, {}},
// 40*A + B overflows.
{"2.4294967215", true, {0x8f, 0xff, 0xff, 0xff, 0x7f}},
{"2.4294967216", false, {}},
};
for (const auto &t : kTests) {
SCOPED_TRACE(t.in);
bssl::ScopedCBB cbb;
ASSERT_TRUE(CBB_init(cbb.get(), 0));
int ok = CBB_add_asn1_oid_from_text(cbb.get(), t.in, strlen(t.in));
EXPECT_EQ(t.ok, static_cast<bool>(ok));
if (ok) {
uint8_t *out;
size_t len;
ASSERT_TRUE(CBB_finish(cbb.get(), &out, &len));
bssl::UniquePtr<uint8_t> free_out(out);
EXPECT_EQ(Bytes(t.out), Bytes(out, len));
}
}
}

+ 95
- 18
crypto/bytestring/cbb.c Просмотреть файл

@@ -15,6 +15,7 @@
#include <openssl/bytestring.h>

#include <assert.h>
#include <limits.h>
#include <string.h>

#include <openssl/mem.h>
@@ -328,6 +329,32 @@ int CBB_add_u24_length_prefixed(CBB *cbb, CBB *out_contents) {
return cbb_add_length_prefixed(cbb, out_contents, 3);
}

// add_base128_integer encodes |v| as a big-endian base-128 integer where the
// high bit of each byte indicates where there is more data. This is the
// encoding used in DER for both high tag number form and OID components.
static int add_base128_integer(CBB *cbb, uint32_t v) {
unsigned len_len = 0;
unsigned copy = v;
while (copy > 0) {
len_len++;
copy >>= 7;
}
if (len_len == 0) {
len_len = 1; // Zero is encoded with one byte.
}
for (unsigned i = len_len - 1; i < len_len; i--) {
uint8_t byte = (v >> (7 * i)) & 0x7f;
if (i != 0) {
// The high bit denotes whether there is more data.
byte |= 0x80;
}
if (!CBB_add_u8(cbb, byte)) {
return 0;
}
}
return 1;
}

int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
if (!CBB_flush(cbb)) {
return 0;
@@ -338,26 +365,10 @@ int CBB_add_asn1(CBB *cbb, CBB *out_contents, unsigned tag) {
unsigned tag_number = tag & CBS_ASN1_TAG_NUMBER_MASK;
if (tag_number >= 0x1f) {
// Set all the bits in the tag number to signal high tag number form.
if (!CBB_add_u8(cbb, tag_bits | 0x1f)) {
if (!CBB_add_u8(cbb, tag_bits | 0x1f) ||
!add_base128_integer(cbb, tag_number)) {
return 0;
}

unsigned len_len = 0;
unsigned copy = tag_number;
while (copy > 0) {
len_len++;
copy >>= 7;
}
for (unsigned i = len_len - 1; i < len_len; i--) {
uint8_t byte = (tag_number >> (7 * i)) & 0x7f;
if (i != 0) {
// The high bit denotes whether there is more data.
byte |= 0x80;
}
if (!CBB_add_u8(cbb, byte)) {
return 0;
}
}
} else if (!CBB_add_u8(cbb, tag_bits | tag_number)) {
return 0;
}
@@ -492,3 +503,69 @@ int CBB_add_asn1_uint64(CBB *cbb, uint64_t value) {

return CBB_flush(cbb);
}

// parse_dotted_decimal parses one decimal component from |cbs|, where |cbs| is
// an OID literal, e.g., "1.2.840.113554.4.1.72585". It consumes both the
// component and the dot, so |cbs| may be passed into the function again for the
// next value.
static int parse_dotted_decimal(CBS *cbs, uint32_t *out) {
*out = 0;
int seen_digit = 0;
for (;;) {
// Valid terminators for a component are the end of the string or a
// non-terminal dot. If the string ends with a dot, this is not a valid OID
// string.
uint8_t u;
if (!CBS_get_u8(cbs, &u) ||
(u == '.' && CBS_len(cbs) > 0)) {
break;
}
if (u < '0' || u > '9' ||
// Forbid stray leading zeros.
(seen_digit && *out == 0) ||
// Check for overflow.
*out > UINT32_MAX / 10 ||
*out * 10 > UINT32_MAX - (u - '0')) {
return 0;
}
*out = *out * 10 + (u - '0');
seen_digit = 1;
}
// The empty string is not a legal OID component.
return seen_digit;
}

int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text, size_t len) {
if (!CBB_flush(cbb)) {
return 0;
}

CBS cbs;
CBS_init(&cbs, (const uint8_t *)text, len);

// OIDs must have at least two components.
uint32_t a, b;
if (!parse_dotted_decimal(&cbs, &a) ||
!parse_dotted_decimal(&cbs, &b)) {
return 0;
}

// The first component is encoded as 40 * |a| + |b|. This assumes that |a| is
// 0, 1, or 2 and that, when it is 0 or 1, |b| is at most 39.
if (a > 2 ||
(a < 2 && b > 39) ||
b > UINT32_MAX - 80 ||
!add_base128_integer(cbb, 40 * a + b)) {
return 0;
}

// The remaining components are encoded unmodified.
while (CBS_len(&cbs) > 0) {
if (!parse_dotted_decimal(&cbs, &a) ||
!add_base128_integer(cbb, a)) {
return 0;
}
}

return 1;
}

+ 1
- 0
crypto/err/obj.errordata Просмотреть файл

@@ -1 +1,2 @@
OBJ,101,INVALID_OID_STRING
OBJ,100,UNKNOWN_NID

+ 29
- 69
crypto/obj/obj.c Просмотреть файл

@@ -389,16 +389,30 @@ const char *OBJ_nid2ln(int nid) {
return obj->ln;
}

ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
int nid = NID_undef;
ASN1_OBJECT *op = NULL;
unsigned char *buf;
unsigned char *p;
const unsigned char *bufp;
int contents_len, total_len;
static ASN1_OBJECT *create_object_with_text_oid(int (*get_nid)(void),
const char *oid,
const char *short_name,
const char *long_name) {
uint8_t *buf;
size_t len;
CBB cbb;
if (!CBB_init(&cbb, 32) ||
!CBB_add_asn1_oid_from_text(&cbb, oid, strlen(oid)) ||
!CBB_finish(&cbb, &buf, &len)) {
OPENSSL_PUT_ERROR(OBJ, OBJ_R_INVALID_OID_STRING);
CBB_cleanup(&cbb);
return NULL;
}

ASN1_OBJECT *ret = ASN1_OBJECT_create(get_nid ? get_nid() : NID_undef, buf,
len, short_name, long_name);
OPENSSL_free(buf);
return ret;
}

ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
if (!dont_search_names) {
nid = OBJ_sn2nid(s);
int nid = OBJ_sn2nid(s);
if (nid == NID_undef) {
nid = OBJ_ln2nid(s);
}
@@ -408,31 +422,7 @@ ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
}
}

// Work out size of content octets
contents_len = a2d_ASN1_OBJECT(NULL, 0, s, -1);
if (contents_len <= 0) {
return NULL;
}
// Work out total size
total_len = ASN1_object_size(0, contents_len, V_ASN1_OBJECT);

buf = OPENSSL_malloc(total_len);
if (buf == NULL) {
OPENSSL_PUT_ERROR(OBJ, ERR_R_MALLOC_FAILURE);
return NULL;
}

p = buf;
// Write out tag+length
ASN1_put_object(&p, 0, contents_len, V_ASN1_OBJECT, V_ASN1_UNIVERSAL);
// Write out contents
a2d_ASN1_OBJECT(p, contents_len, s, -1);

bufp = buf;
op = d2i_ASN1_OBJECT(NULL, &bufp, total_len);
OPENSSL_free(buf);

return op;
return create_object_with_text_oid(NULL, s, NULL, NULL);
}

static int strlcpy_int(char *dst, const char *src, int dst_size) {
@@ -621,41 +611,11 @@ static int obj_add_object(ASN1_OBJECT *obj) {
}

int OBJ_create(const char *oid, const char *short_name, const char *long_name) {
int ret = NID_undef;
ASN1_OBJECT *op = NULL;
unsigned char *buf = NULL;
int len;

len = a2d_ASN1_OBJECT(NULL, 0, oid, -1);
if (len <= 0) {
goto err;
}

buf = OPENSSL_malloc(len);
if (buf == NULL) {
OPENSSL_PUT_ERROR(OBJ, ERR_R_MALLOC_FAILURE);
goto err;
}

len = a2d_ASN1_OBJECT(buf, len, oid, -1);
if (len == 0) {
goto err;
}

op = (ASN1_OBJECT *)ASN1_OBJECT_create(obj_next_nid(), buf, len, short_name,
long_name);
if (op == NULL) {
goto err;
}

if (obj_add_object(op)) {
ret = op->nid;
ASN1_OBJECT *op =
create_object_with_text_oid(obj_next_nid, oid, short_name, long_name);
if (op == NULL ||
!obj_add_object(op)) {
return NID_undef;
}
op = NULL;

err:
ASN1_OBJECT_free(op);
OPENSSL_free(buf);

return ret;
return op->nid;
}

+ 0
- 1
include/openssl/asn1.h Просмотреть файл

@@ -737,7 +737,6 @@ OPENSSL_EXPORT int i2a_ASN1_OBJECT(BIO *bp,ASN1_OBJECT *a);
OPENSSL_EXPORT int i2a_ASN1_STRING(BIO *bp, ASN1_STRING *a, int type);
OPENSSL_EXPORT int i2t_ASN1_OBJECT(char *buf,int buf_len,ASN1_OBJECT *a);

OPENSSL_EXPORT int a2d_ASN1_OBJECT(unsigned char *out,int olen, const char *buf, int num);
OPENSSL_EXPORT ASN1_OBJECT *ASN1_OBJECT_create(int nid, unsigned char *data,int len, const char *sn, const char *ln);

OPENSSL_EXPORT int ASN1_INTEGER_set(ASN1_INTEGER *a, long v);


+ 11
- 0
include/openssl/bytestring.h Просмотреть файл

@@ -443,6 +443,17 @@ OPENSSL_EXPORT void CBB_discard_child(CBB *cbb);
// error.
OPENSSL_EXPORT int CBB_add_asn1_uint64(CBB *cbb, uint64_t value);

// CBB_add_asn1_oid_from_text decodes |len| bytes from |text| as an ASCII OID
// representation, e.g. "1.2.840.113554.4.1.72585", and writes the DER-encoded
// contents to |cbb|. It returns one on success and zero on malloc failure or if
// |text| was invalid. It does not include the OBJECT IDENTIFER framing, only
// the element's contents.
//
// This function considers OID strings with components which do not fit in a
// |uint32_t| to be invalid.
OPENSSL_EXPORT int CBB_add_asn1_oid_from_text(CBB *cbb, const char *text,
size_t len);


#if defined(__cplusplus)
} // extern C


+ 1
- 0
include/openssl/obj.h Просмотреть файл

@@ -228,5 +228,6 @@ OPENSSL_EXPORT void OBJ_NAME_do_all(int type, void (*callback)(const OBJ_NAME *,
#endif

#define OBJ_R_UNKNOWN_NID 100
#define OBJ_R_INVALID_OID_STRING 101

#endif // OPENSSL_HEADER_OBJ_H

Загрузка…
Отмена
Сохранить