Переглянути джерело

Add |BIO_read_asn1| to read a single ASN.1 object.

Android needs to be able to read a PKCS#7 blob from a Java
InputStream. This change adds |BIO_read_asn1| which reads a single
ASN.1 object from the start of a BIO without overreading.

Change-Id: I74776e686529c8e58af1c26a4909f9bd4e87b707
kris/onging/CECPQ3_patch15
Adam Langley 9 роки тому
джерело
коміт
71106adf89
3 змінених файлів з 238 додано та 1 видалено
  1. +140
    -0
      crypto/bio/bio.c
  2. +83
    -1
      crypto/bio/bio_test.cc
  3. +15
    -0
      include/openssl/bio.h

+ 140
- 0
crypto/bio/bio.c Переглянути файл

@@ -56,6 +56,7 @@

#include <openssl/bio.h>

#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <string.h>
@@ -460,3 +461,142 @@ static int print_bio(const char *str, size_t len, void *bio) {
void BIO_print_errors(BIO *bio) {
ERR_print_errors_cb(print_bio, bio);
}

/* bio_read_all reads everything from |bio| and prepends |prefix| to it. On
* success, |*out| is set to an allocated buffer (which should be freed with
* |OPENSSL_free|), |*out_len| is set to its length and one is returned. The
* buffer will contain |prefix| followed by the contents of |bio|. On failure,
* zero is returned.
*
* The function will fail if the size of the output would equal or exceed
* |max_len|. */
static int bio_read_all(BIO *bio, uint8_t **out, size_t *out_len,
const uint8_t *prefix, size_t prefix_len,
size_t max_len) {
static const size_t kChunkSize = 4096;

size_t len = prefix_len + kChunkSize;
if (len > max_len) {
len = max_len;
}
if (len < prefix_len) {
return 0;
}
*out = OPENSSL_malloc(len);
if (*out == NULL) {
return 0;
}
memcpy(*out, prefix, prefix_len);
size_t done = prefix_len;

for (;;) {
if (done == len) {
OPENSSL_free(*out);
return 0;
}
const size_t todo = len - done;
assert(todo < INT_MAX);
const int n = BIO_read(bio, *out + done, todo);
if (n == 0) {
*out_len = done;
return 1;
} else if (n == -1) {
OPENSSL_free(*out);
return 0;
}

done += n;
if (len < max_len && len - done < kChunkSize / 2) {
len += kChunkSize;
if (len < kChunkSize || len > max_len) {
len = max_len;
}
uint8_t *new_buf = OPENSSL_realloc(*out, len);
if (new_buf == NULL) {
OPENSSL_free(*out);
return 0;
}
*out = new_buf;
}
}
}

int BIO_read_asn1(BIO *bio, uint8_t **out, size_t *out_len, size_t max_len) {
uint8_t header[6];

static const size_t kInitialHeaderLen = 2;
if (BIO_read(bio, header, kInitialHeaderLen) != kInitialHeaderLen) {
return 0;
}

const uint8_t tag = header[0];
const uint8_t length_byte = header[1];

if ((tag & 0x1f) == 0x1f) {
/* Long form tags are not supported. */
return 0;
}

size_t len, header_len;
if ((length_byte & 0x80) == 0) {
/* Short form length. */
len = length_byte;
header_len = kInitialHeaderLen;
} else {
const size_t num_bytes = length_byte & 0x7f;

if ((tag & 0x20 /* constructed */) != 0 && num_bytes == 0) {
/* indefinite length. */
return bio_read_all(bio, out, out_len, header, kInitialHeaderLen,
max_len);
}

if (num_bytes == 0 || num_bytes > 4) {
return 0;
}

if (BIO_read(bio, header + kInitialHeaderLen, num_bytes) != num_bytes) {
return 0;
}
header_len = kInitialHeaderLen + num_bytes;

uint32_t len32 = 0;
unsigned i;
for (i = 0; i < num_bytes; i++) {
len32 <<= 8;
len32 |= header[kInitialHeaderLen + i];
}

if (len32 < 128) {
/* Length should have used short-form encoding. */
return 0;
}

if ((len32 >> ((num_bytes-1)*8)) == 0) {
/* Length should have been at least one byte shorter. */
return 0;
}

len = len32;
}

if (len + header_len < len ||
len + header_len > max_len) {
return 0;
}
len += header_len;
*out_len = len;

*out = OPENSSL_malloc(len);
if (*out == NULL) {
return 0;
}
memcpy(*out, header, header_len);
if (BIO_read(bio, (*out) + header_len, len - header_len) !=
len - header_len) {
OPENSSL_free(*out);
return 0;
}

return 1;
}

+ 83
- 1
crypto/bio/bio_test.cc Переглянути файл

@@ -329,6 +329,87 @@ static bool TestPrintf() {
return true;
}

static bool ReadASN1(bool should_succeed, const uint8_t *data, size_t data_len,
size_t expected_len, size_t max_len) {
ScopedBIO bio(BIO_new_mem_buf(const_cast<uint8_t*>(data), data_len));

uint8_t *out;
size_t out_len;
int ok = BIO_read_asn1(bio.get(), &out, &out_len, max_len);
if (!ok) {
out = nullptr;
}
ScopedOpenSSLBytes out_storage(out);

if (should_succeed != (ok == 1)) {
return false;
}

if (should_succeed &&
(out_len != expected_len || memcmp(data, out, expected_len) != 0)) {
return false;
}

return true;
}

static bool TestASN1() {
static const uint8_t kData1[] = {0x30, 2, 1, 2, 0, 0};
static const uint8_t kData2[] = {0x30, 3, 1, 2}; /* truncated */
static const uint8_t kData3[] = {0x30, 0x81, 1, 1}; /* should be short len */
static const uint8_t kData4[] = {0x30, 0x82, 0, 1, 1}; /* zero padded. */

if (!ReadASN1(true, kData1, sizeof(kData1), 4, 100) ||
!ReadASN1(false, kData2, sizeof(kData2), 0, 100) ||
!ReadASN1(false, kData3, sizeof(kData3), 0, 100) ||
!ReadASN1(false, kData4, sizeof(kData4), 0, 100)) {
return false;
}

static const size_t kLargePayloadLen = 8000;
static const uint8_t kLargePrefix[] = {0x30, 0x82, kLargePayloadLen >> 8,
kLargePayloadLen & 0xff};
ScopedOpenSSLBytes large(reinterpret_cast<uint8_t *>(
OPENSSL_malloc(sizeof(kLargePrefix) + kLargePayloadLen)));
if (!large) {
return false;
}
memset(large.get() + sizeof(kLargePrefix), 0, kLargePayloadLen);
memcpy(large.get(), kLargePrefix, sizeof(kLargePrefix));

if (!ReadASN1(true, large.get(), sizeof(kLargePrefix) + kLargePayloadLen,
sizeof(kLargePrefix) + kLargePayloadLen,
kLargePayloadLen * 2)) {
fprintf(stderr, "Large payload test failed.\n");
return false;
}

if (!ReadASN1(false, large.get(), sizeof(kLargePrefix) + kLargePayloadLen,
sizeof(kLargePrefix) + kLargePayloadLen,
kLargePayloadLen - 1)) {
fprintf(stderr, "max_len test failed.\n");
return false;
}

static const uint8_t kIndefPrefix[] = {0x30, 0x80};
memcpy(large.get(), kIndefPrefix, sizeof(kIndefPrefix));
if (!ReadASN1(true, large.get(), sizeof(kLargePrefix) + kLargePayloadLen,
sizeof(kLargePrefix) + kLargePayloadLen,
kLargePayloadLen*2)) {
fprintf(stderr, "indefinite length test failed.\n");
return false;
}

if (!ReadASN1(false, large.get(), sizeof(kLargePrefix) + kLargePayloadLen,
sizeof(kLargePrefix) + kLargePayloadLen,
kLargePayloadLen-1)) {
fprintf(stderr, "indefinite length, max_len test failed.\n");
return false;
}

return true;
}

int main(void) {
CRYPTO_library_init();
ERR_load_crypto_strings();
@@ -350,7 +431,8 @@ int main(void) {

if (!TestSocketConnect() ||
!TestPrintf() ||
!TestZeroCopyBioPairs()) {
!TestZeroCopyBioPairs() ||
!TestASN1()) {
return 1;
}



+ 15
- 0
include/openssl/bio.h Переглянути файл

@@ -339,6 +339,21 @@ OPENSSL_EXPORT int BIO_hexdump(BIO *bio, const uint8_t *data, size_t len,
* using human readable strings where possible. */
OPENSSL_EXPORT void BIO_print_errors(BIO *bio);

/* BIO_read_asn1 reads a single ASN.1 object from |bio|. If successful it sets
* |*out| to be an allocated buffer (that should be freed with |OPENSSL_free|),
* |*out_size| to the length, in bytes, of that buffer and returns one.
* Otherwise it returns zero.
*
* If the length of the object is greater than |max_len| or 2^32 then the
* function will fail. Long-form tags are not supported. If the length of the
* object is indefinite the full contents of |bio| are read, unless it would be
* greater than |max_len|, in which case the function fails.
*
* If the function fails then some unknown amount of data may have been read
* from |bio|. */
OPENSSL_EXPORT int BIO_read_asn1(BIO *bio, uint8_t **out, size_t *out_len,
size_t max_len);


/* Memory BIOs.
*


Завантаження…
Відмінити
Зберегти