Rework support for ASN.1 BER.

Previously, the ASN.1 functions in bytestring were capable of processing
indefinite length elements when the _ber functions were used. That works
well enough for PKCS#3, but NSS goes a bit crazy with BER encoding and
PKCS#12. Rather than complicate the core bytestring functions further,
the BER support is removed from them and moved to a separate function
that converts from BER to DER (if needed).

Change-Id: I2212b28e99bab9fab8c61f80d2012d3e5a3cc2f0
Reviewed-on: https://boringssl-review.googlesource.com/1591
Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
Adam Langley
2014-08-25 21:38:08 +00:00
committed by Adam Langley
parent cc8fcf45bb
commit 9c01e00c2e
7 changed files with 465 additions and 160 deletions
+1
View File
@@ -5,6 +5,7 @@ add_library(
OBJECT
ber.c
cbs.c
cbb.c
)
+226
View File
@@ -0,0 +1,226 @@
/* Copyright (c) 2014, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#include <openssl/bytestring.h>
#include "internal.h"
/* kMaxDepth is a just a sanity limit. The code should be such that the length
* of the input being processes always decreases. None the less, a very large
* input could otherwise cause the stack to overflow. */
static const unsigned kMaxDepth = 2048;
/* cbs_find_ber walks an ASN.1 structure in |orig_in| and sets |*ber_found|
* depending on whether an indefinite length element was found. The value of
* |in| is not changed. It returns one on success (i.e. |*ber_found| was set)
* and zero on error. */
static int cbs_find_ber(CBS *orig_in, char *ber_found, unsigned depth) {
CBS in;
if (depth > kMaxDepth) {
return 0;
}
CBS_init(&in, CBS_data(orig_in), CBS_len(orig_in));
*ber_found = 0;
while (CBS_len(&in) > 0) {
CBS contents;
unsigned tag;
size_t header_len;
if (!CBS_get_any_asn1_element(&in, &contents, &tag, &header_len)) {
return 0;
}
if (CBS_len(&contents) == header_len &&
header_len > 0 &&
CBS_data(&contents)[header_len-1] == 0x80) {
*ber_found = 1;
return 1;
}
if (tag & CBS_ASN1_CONSTRUCTED) {
if (!CBS_skip(&contents, header_len) ||
!cbs_find_ber(&contents, ber_found, depth + 1)) {
return 0;
}
}
}
return 1;
}
/* is_primitive_type returns true if |tag| likely a primitive type. Normally
* one can just test the "constructed" bit in the tag but, in BER, even
* primitive tags can have the constructed bit if they have indefinite
* length. */
static char is_primitive_type(unsigned tag) {
return (tag & 0xc0) == 0 &&
(tag & 0x1f) != (CBS_ASN1_SEQUENCE & 0x1f) &&
(tag & 0x1f) != (CBS_ASN1_SET & 0x1f);
}
/* is_eoc returns true if |header_len| and |contents|, as returned by
* |CBS_get_any_asn1_element|, indicate an "end of contents" (EOC) value. */
static char is_eoc(size_t header_len, CBS *contents) {
return header_len == 2 && CBS_len(contents) == 2 &&
memcmp(CBS_data(contents), "\x00\x00", 2) == 0;
}
/* cbs_convert_ber reads BER data from |in| and writes DER data to |out|. If
* |squash_header| is set then the top-level of elements from |in| will not
* have their headers written. This is used when concatenating the fragments of
* an indefinite length, primitive value. If |looking_for_eoc| is set then any
* EOC elements found will cause the function to return after consuming it.
* It returns one on success and zero on error. */
static int cbs_convert_ber(CBS *in, CBB *out, char squash_header,
char looking_for_eoc, unsigned depth) {
if (depth > kMaxDepth) {
return 0;
}
while (CBS_len(in) > 0) {
CBS contents;
unsigned tag;
size_t header_len;
CBB *out_contents, out_contents_storage;
if (!CBS_get_any_asn1_element(in, &contents, &tag, &header_len)) {
return 0;
}
out_contents = out;
if (CBS_len(&contents) == header_len) {
if (is_eoc(header_len, &contents)) {
return looking_for_eoc;
}
if (header_len > 0 && CBS_data(&contents)[header_len - 1] == 0x80) {
CBB out_context_specific;
/* This is an indefinite length element. If it's a SEQUENCE or SET then
* we just need to write the out the contents as normal, but with a
* concrete length prefix.
*
* If it's a something else then the contents will be a series of BER
* elements of the same type which need to be concatenated. */
const char context_specific = (tag & 0xc0) == 0x80;
const char simple_type = is_primitive_type(tag);
if (!squash_header) {
unsigned out_tag = tag;
if (simple_type) {
out_tag &= ~CBS_ASN1_CONSTRUCTED;
}
if (!CBB_add_asn1(out, &out_contents_storage, out_tag)) {
return 0;
}
out_contents = &out_contents_storage;
}
/* If context specific then we peek at the inner tag and replicate it.
* This is because NSS produces odd-seeming BER structures where an
* indefinite length explicit tag doesn't have the expected actual tag
* inside it. */
if (context_specific) {
CBS in_copy, contents;
unsigned tag;
size_t header_len;
CBS_init(&in_copy, CBS_data(in), CBS_len(in));
if (!CBS_get_any_asn1_element(&in_copy, &contents, &tag, &header_len)) {
return 0;
}
if (is_eoc(header_len, &contents)) {
/* The indefinite-length value is empty. Unread the EOC and
* continue. */
CBS_init(in, CBS_data(&in_copy), CBS_len(&in_copy));
continue;
}
if (is_primitive_type(tag)) {
tag &= 0x1f;
}
if (!CBB_add_asn1(out_contents, &out_context_specific, tag)) {
return 0;
}
out_contents = &out_context_specific;
}
if (!cbs_convert_ber(in, out_contents,
context_specific || simple_type,
1 /* looking for eoc */, depth + 1)) {
return 0;
}
if (out_contents != out && !CBB_flush(out)) {
return 0;
}
continue;
}
}
if (!squash_header) {
if (!CBB_add_asn1(out, &out_contents_storage, tag)) {
return 0;
}
out_contents = &out_contents_storage;
}
if (!CBS_skip(&contents, header_len)) {
return 0;
}
if (tag & CBS_ASN1_CONSTRUCTED) {
if (!cbs_convert_ber(&contents, out_contents, 0 /* don't squash header */,
0 /* not looking for eoc */, depth + 1)) {
return 0;
}
} else {
if (!CBB_add_bytes(out_contents, CBS_data(&contents),
CBS_len(&contents))) {
return 0;
}
}
if (out_contents != out && !CBB_flush(out)) {
return 0;
}
}
return looking_for_eoc == 0;
}
int CBS_asn1_ber_to_der(CBS *in, uint8_t **out, size_t *out_len) {
CBB cbb;
/* First, do a quick walk to find any indefinite-length elements. Most of the
* time we hope that there aren't any and thus we can quickly return. */
char conversion_needed;
if (!cbs_find_ber(in, &conversion_needed, 0)) {
return 0;
}
if (!conversion_needed) {
*out = NULL;
*out_len = 0;
return 1;
}
CBB_init(&cbb, CBS_len(in));
if (!cbs_convert_ber(in, &cbb, 0, 0, 0)) {
CBB_cleanup(&cbb);
return 0;
}
return CBB_finish(&cbb, out, out_len);
}
+85 -57
View File
@@ -17,6 +17,8 @@
#include <openssl/bytestring.h>
#include "internal.h"
static int test_skip(void) {
static const uint8_t kData[] = {1, 2, 3};
@@ -145,61 +147,6 @@ static int test_get_asn1(void) {
return 1;
}
static int test_get_indef(void) {
static const uint8_t kData1[] = {0x30, 0x80, 0x00, 0x00};
static const uint8_t kDataWithoutEOC[] = {0x30, 0x80, 0x01, 0x00};
static const uint8_t kDataWithBadInternalLength[] = {0x30, 0x80, 0x01, 0x01};
static const uint8_t kDataNested[] = {0x30, 0x80, 0x30, 0x80, 0x30, 0x80,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
static const uint8_t kDataPrimitive[] = {0x02, 0x80, 0x00, 0x00};
CBS data, contents;
CBS_init(&data, kData1, sizeof(kData1));
if (CBS_get_asn1(&data, &contents, 0x30)) {
/* Indefinite lengths should not be supported in DER mode. */
fprintf(stderr, "Indefinite length parsed by CBS_get_asn1.\n");
return 0;
}
if (!CBS_get_asn1_ber(&data, &contents, 0x30) ||
CBS_len(&contents) != 0 ||
CBS_len(&data) != 0) {
fprintf(stderr, "Simple indefinite length failed.\n");
return 0;
}
CBS_init(&data, kDataWithoutEOC, sizeof(kDataWithoutEOC));
if (CBS_get_asn1_ber(&data, &contents, 0x30)) {
fprintf(stderr, "Parsed without EOC.\n");
return 0;
}
CBS_init(&data, kDataWithBadInternalLength,
sizeof(kDataWithBadInternalLength));
if (CBS_get_asn1_ber(&data, &contents, 0x30)) {
fprintf(stderr, "Parsed with internal length.\n");
return 0;
}
CBS_init(&data, kDataNested, sizeof(kDataNested));
if (!CBS_get_asn1_ber(&data, &contents, 0x30) ||
CBS_len(&contents) != 8 ||
CBS_len(&data) != 0) {
fprintf(stderr, "Nested indefinite lengths failed.\n");
return 0;
}
CBS_init(&data, kDataPrimitive, sizeof(kDataPrimitive));
if (CBS_get_asn1_ber(&data, &contents, 0x02)) {
/* Indefinite lengths should not be supported for non-constructed
* elements. */
fprintf(stderr, "Parsed non-constructed element with indefinite length\n");
return 0;
}
return 1;
}
static int test_cbb_basic(void) {
static const uint8_t kExpected[] = {1, 2, 3, 4, 5, 6, 7, 8};
uint8_t *buf;
@@ -405,19 +352,100 @@ static int test_cbb_asn1(void) {
return 1;
}
static int do_ber_convert(const char *name,
const uint8_t *der_expected, size_t der_len,
const uint8_t *ber, size_t ber_len) {
CBS in;
uint8_t *out;
size_t out_len;
CBS_init(&in, ber, ber_len);
if (!CBS_asn1_ber_to_der(&in, &out, &out_len)) {
fprintf(stderr, "%s: CBS_asn1_ber_to_der failed.\n", name);
return 0;
}
if (out == NULL) {
if (ber_len != der_len ||
memcmp(der_expected, ber, ber_len) != 0) {
fprintf(stderr, "%s: incorrect unconverted result.\n", name);
return 0;
}
return 1;
}
if (out_len != der_len ||
memcmp(out, der_expected, der_len) != 0) {
fprintf(stderr, "%s: incorrect converted result.\n", name);
return 0;
}
free(out);
return 1;
}
static int test_ber_convert(void) {
static const uint8_t kSimpleBER[] = {0x01, 0x01, 0x00};
/* kIndefBER contains a SEQUENCE with an indefinite length. */
static const uint8_t kIndefBER[] = {0x30, 0x80, 0x01, 0x01, 0x02, 0x00, 0x00};
static const uint8_t kIndefDER[] = {0x30, 0x03, 0x01, 0x01, 0x02};
/* kOctetStringBER contains an indefinite length OCTETSTRING with two parts.
* These parts need to be concatenated in DER form. */
static const uint8_t kOctetStringBER[] = {0x24, 0x80, 0x04, 0x02, 0, 1,
0x04, 0x02, 2, 3, 0x00, 0x00};
static const uint8_t kOctetStringDER[] = {0x04, 0x04, 0, 1, 2, 3};
/* kNSSBER is part of a PKCS#12 message generated by NSS that uses indefinite
* length elements extensively. */
static const uint8_t kNSSBER[] = {
0x30, 0x80, 0x02, 0x01, 0x03, 0x30, 0x80, 0x06, 0x09, 0x2a, 0x86, 0x48,
0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x80, 0x24, 0x80, 0x04, 0x04,
0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0x39,
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05,
0x00, 0x04, 0x14, 0x84, 0x98, 0xfc, 0x66, 0x33, 0xee, 0xba, 0xe7, 0x90,
0xc1, 0xb6, 0xe8, 0x8f, 0xfe, 0x1d, 0xc5, 0xa5, 0x97, 0x93, 0x3e, 0x04,
0x10, 0x38, 0x62, 0xc6, 0x44, 0x12, 0xd5, 0x30, 0x00, 0xf8, 0xf2, 0x1b,
0xf0, 0x6e, 0x10, 0x9b, 0xb8, 0x02, 0x02, 0x07, 0xd0, 0x00, 0x00,
};
static const uint8_t kNSSDER[] = {
0x30, 0x53, 0x02, 0x01, 0x03, 0x30, 0x13, 0x06, 0x09, 0x2a, 0x86,
0x48, 0x86, 0xf7, 0x0d, 0x01, 0x07, 0x01, 0xa0, 0x06, 0x04, 0x04,
0x01, 0x02, 0x03, 0x04, 0x30, 0x39, 0x30, 0x21, 0x30, 0x09, 0x06,
0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x84,
0x98, 0xfc, 0x66, 0x33, 0xee, 0xba, 0xe7, 0x90, 0xc1, 0xb6, 0xe8,
0x8f, 0xfe, 0x1d, 0xc5, 0xa5, 0x97, 0x93, 0x3e, 0x04, 0x10, 0x38,
0x62, 0xc6, 0x44, 0x12, 0xd5, 0x30, 0x00, 0xf8, 0xf2, 0x1b, 0xf0,
0x6e, 0x10, 0x9b, 0xb8, 0x02, 0x02, 0x07, 0xd0,
};
return do_ber_convert("kSimpleBER", kSimpleBER, sizeof(kSimpleBER),
kSimpleBER, sizeof(kSimpleBER)) &&
do_ber_convert("kIndefBER", kIndefDER, sizeof(kIndefDER), kIndefBER,
sizeof(kIndefBER)) &&
do_ber_convert("kOctetStringBER", kOctetStringDER,
sizeof(kOctetStringDER), kOctetStringBER,
sizeof(kOctetStringBER)) &&
do_ber_convert("kNSSBER", kNSSDER, sizeof(kNSSDER), kNSSBER,
sizeof(kNSSBER));
}
int main(void) {
if (!test_skip() ||
!test_get_u() ||
!test_get_prefixed() ||
!test_get_prefixed_bad() ||
!test_get_asn1() ||
!test_get_indef() ||
!test_cbb_basic() ||
!test_cbb_fixed() ||
!test_cbb_finish_child() ||
!test_cbb_misuse() ||
!test_cbb_prefixed() ||
!test_cbb_asn1()) {
!test_cbb_asn1() ||
!test_ber_convert()) {
return 1;
}
+46 -66
View File
@@ -19,6 +19,8 @@
#include <assert.h>
#include <string.h>
#include "internal.h"
void CBS_init(CBS *cbs, const uint8_t *data, size_t len) {
cbs->data = data;
@@ -156,50 +158,8 @@ int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out) {
return cbs_get_length_prefixed(cbs, out, 3);
}
static int cbs_get_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
size_t *out_header_len, unsigned depth,
int *was_indefinite_len);
/* cbs_get_asn1_indefinite_len sets |*out| to be a CBS that covers an
* indefinite length element in |cbs| and advances |*in|. On entry, |cbs| will
* not have had the tag and length byte removed. On exit, |*out| does not cover
* the EOC element, but |*in| is skipped over it.
*
* The |depth| argument counts the number of times the code has recursed trying
* to find an indefinite length. */
static int cbs_get_asn1_indefinite_len(CBS *in, CBS *out, unsigned depth) {
static const size_t kEOCLength = 2;
size_t header_len;
unsigned tag;
int was_indefinite_len;
CBS orig = *in, child;
if (!CBS_skip(in, 2 /* tag plus 0x80 byte for indefinite len */)) {
return 0;
}
for (;;) {
if (!cbs_get_asn1_element(in, &child, &tag, &header_len, depth + 1,
&was_indefinite_len)) {
return 0;
}
if (!was_indefinite_len && CBS_len(&child) == kEOCLength &&
header_len == kEOCLength && tag == 0) {
break;
}
}
return CBS_get_bytes(&orig, out, CBS_len(&orig) - CBS_len(in) - kEOCLength);
}
/* MAX_DEPTH the maximum number of levels of indefinite lengths that we'll
* support. */
#define MAX_DEPTH 64
static int cbs_get_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
size_t *out_header_len, unsigned depth,
int *was_indefinite_len) {
int CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
size_t *out_header_len) {
uint8_t tag, length_byte;
CBS header = *cbs;
if (!CBS_get_u8(&header, &tag) ||
@@ -213,9 +173,6 @@ static int cbs_get_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
}
*out_tag = tag;
if (was_indefinite_len) {
*was_indefinite_len = 0;
}
size_t len;
if ((length_byte & 0x80) == 0) {
@@ -227,14 +184,10 @@ static int cbs_get_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
const size_t num_bytes = length_byte & 0x7f;
uint32_t len32;
if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && depth < MAX_DEPTH &&
num_bytes == 0) {
if ((tag & CBS_ASN1_CONSTRUCTED) != 0 && num_bytes == 0) {
/* indefinite length */
*out_header_len = 2;
if (was_indefinite_len) {
*was_indefinite_len = 1;
}
return cbs_get_asn1_indefinite_len(cbs, out, depth);
return CBS_get_bytes(cbs, out, 2);
}
if (num_bytes == 0 || num_bytes > 4) {
@@ -263,7 +216,7 @@ static int cbs_get_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
return CBS_get_bytes(cbs, out, len);
}
static int cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value, int ber,
static int cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value,
int skip_header) {
size_t header_len;
unsigned tag;
@@ -273,9 +226,13 @@ static int cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value, int ber,
out = &throwaway;
}
if (!cbs_get_asn1_element(cbs, out, &tag, &header_len, ber ? 0 : MAX_DEPTH,
NULL) ||
tag != tag_value) {
if (!CBS_get_any_asn1_element(cbs, out, &tag, &header_len) ||
tag != tag_value ||
(header_len > 0 &&
/* This ensures that the tag is either zero length or
* indefinite-length. */
CBS_len(out) == header_len &&
CBS_data(out)[header_len - 1] == 0x80)) {
return 0;
}
@@ -288,16 +245,39 @@ static int cbs_get_asn1(CBS *cbs, CBS *out, unsigned tag_value, int ber,
}
int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value) {
return cbs_get_asn1(cbs, out, tag_value, 0 /* DER */,
1 /* skip header */);
}
int CBS_get_asn1_ber(CBS *cbs, CBS *out, unsigned tag_value) {
return cbs_get_asn1(cbs, out, tag_value, 1 /* BER */,
1 /* skip header */);
return cbs_get_asn1(cbs, out, tag_value, 1 /* skip header */);
}
int CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned tag_value) {
return cbs_get_asn1(cbs, out, tag_value, 0 /* DER */,
0 /* include header */);
return cbs_get_asn1(cbs, out, tag_value, 0 /* include header */);
}
int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out) {
CBS bytes;
const uint8_t *data;
size_t i, len;
if (!CBS_get_asn1(cbs, &bytes, CBS_ASN1_INTEGER)) {
return 0;
}
*out = 0;
data = CBS_data(&bytes);
len = CBS_len(&bytes);
if (len > 0 && (data[0] & 0x80) != 0) {
/* negative number */
return 0;
}
for (i = 0; i < len; i++) {
if ((*out >> 56) != 0) {
/* Too large to represent as a uint64_t. */
return 0;
}
*out <<= 8;
*out |= data[i];
}
return 1;
}
+55
View File
@@ -0,0 +1,55 @@
/* Copyright (c) 2014, Google Inc.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
#ifndef OPENSSL_HEADER_BYTESTRING_INTERNAL_H
#define OPENSSL_HEADER_BYTESTRING_INTERNAL_H
#include <openssl/base.h>
#if defined(__cplusplus)
extern "C" {
#endif
/* CBS_get_any_asn1_element sets |*out| to contain the next ASN.1 element from
* |*cbs| (including header bytes) and advances |*cbs|. It sets |*out_tag| to
* the tag number and |*out_header_len| to the length of the ASN.1 header. If
* the element has indefinite length then |*out| will only contain the header.
*
* Tag numbers greater than 31 are not supported. */
int CBS_get_any_asn1_element(CBS *cbs, CBS *out, unsigned *out_tag,
size_t *out_header_len);
/* CBS_asn1_ber_to_der reads an ASN.1 structure from |in|. If it finds
* indefinite-length elements then it attempts to convert the BER data to DER
* and sets |*out| and |*out_length| to describe a malloced buffer containing
* the DER data. Additionally, |*in| will be advanced over the ASN.1 data.
*
* If it doesn't find any indefinite-length elements then it sets |*out| to
* NULL and |*in| is unmodified.
*
* A sufficiently complex ASN.1 structure will break this function because it's
* not possible to generically convert BER to DER without knowledge of the
* structure itself. However, this sufficies to handle the PKCS#7 and #12 output
* from NSS.
*
* It returns one on success and zero otherwise. */
int CBS_asn1_ber_to_der(CBS *in, uint8_t **out, size_t *out_len);
#if defined(__cplusplus)
} /* extern C */
#endif
#endif /* OPENSSL_HEADER_BYTESTRING_INTERNAL_H */
+46 -27
View File
@@ -19,48 +19,61 @@
#include <openssl/obj.h>
#include <openssl/stack.h>
#include "../bytestring/internal.h"
int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) {
CBS content_info, content_type, wrapped_signed_data, signed_data,
version_bytes, certificates;
int nid;
uint8_t *der_bytes = NULL;
size_t der_len;
CBS in, content_info, content_type, wrapped_signed_data, signed_data,
certificates;
const size_t initial_certs_len = sk_X509_num(out_certs);
uint64_t version;
int ret = 0;
/* See https://tools.ietf.org/html/rfc2315#section-7 */
if (!CBS_get_asn1_ber(cbs, &content_info, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT)) {
/* The input may be in BER format. */
if (!CBS_asn1_ber_to_der(cbs, &der_bytes, &der_len)) {
return 0;
}
if (der_bytes != NULL) {
CBS_init(&in, der_bytes, der_len);
} else {
CBS_init(&in, CBS_data(cbs), CBS_len(cbs));
}
nid = OBJ_cbs2nid(&content_type);
if (nid != NID_pkcs7_signed) {
/* See https://tools.ietf.org/html/rfc2315#section-7 */
if (!CBS_get_asn1(&in, &content_info, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&content_info, &content_type, CBS_ASN1_OBJECT)) {
goto err;
}
if (OBJ_cbs2nid(&content_type) != NID_pkcs7_signed) {
OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
X509_R_NOT_PKCS7_SIGNED_DATA);
return 0;
goto err;
}
/* See https://tools.ietf.org/html/rfc2315#section-9.1 */
if (!CBS_get_asn1_ber(&content_info, &wrapped_signed_data,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
!CBS_get_asn1_ber(&wrapped_signed_data, &signed_data,
CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_ber(&signed_data, &version_bytes, CBS_ASN1_INTEGER) ||
!CBS_get_asn1_ber(&signed_data, NULL /* digests */, CBS_ASN1_SET) ||
!CBS_get_asn1_ber(&signed_data, NULL /* content */, CBS_ASN1_SEQUENCE)) {
return 0;
if (!CBS_get_asn1(&content_info, &wrapped_signed_data,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
!CBS_get_asn1(&wrapped_signed_data, &signed_data, CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1_uint64(&signed_data, &version) ||
!CBS_get_asn1(&signed_data, NULL /* digests */, CBS_ASN1_SET) ||
!CBS_get_asn1(&signed_data, NULL /* content */, CBS_ASN1_SEQUENCE)) {
goto err;
}
if (CBS_len(&version_bytes) < 1 || CBS_data(&version_bytes)[0] == 0) {
if (version < 1) {
OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
X509_R_BAD_PKCS7_VERSION);
return 0;
goto err;
}
if (!CBS_get_asn1_ber(&signed_data, &certificates,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
if (!CBS_get_asn1(&signed_data, &certificates,
CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0)) {
OPENSSL_PUT_ERROR(X509, PKCS7_get_certificates,
X509_R_NO_CERTIFICATES_INCLUDED);
return 0;
goto err;
}
while (CBS_len(&certificates) > 0) {
@@ -86,15 +99,21 @@ int PKCS7_get_certificates(STACK_OF(X509) *out_certs, CBS *cbs) {
sk_X509_push(out_certs, x509);
}
return 1;
ret = 1;
err:
while (sk_X509_num(out_certs) != initial_certs_len) {
X509 *x509 = sk_X509_pop(out_certs);
X509_free(x509);
if (der_bytes) {
OPENSSL_free(der_bytes);
}
return 0;
if (!ret) {
while (sk_X509_num(out_certs) != initial_certs_len) {
X509 *x509 = sk_X509_pop(out_certs);
X509_free(x509);
}
}
return ret;
}
int PKCS7_bundle_certificates(CBB *out, const STACK_OF(X509) *certs) {
+6 -10
View File
@@ -138,20 +138,16 @@ OPENSSL_EXPORT int CBS_get_u24_length_prefixed(CBS *cbs, CBS *out);
* Tag numbers greater than 31 are not supported. */
OPENSSL_EXPORT int CBS_get_asn1(CBS *cbs, CBS *out, unsigned tag_value);
/* CBS_get_asn1_ber sets |*out| to the contents of BER-encoded, ASN.1 element
* (not including tag and length bytes) and advances |cbs| over it. The ASN.1
* element must match |tag_value|. It returns one on success and zero on error.
*
* The major difference between this function and |CBS_get_asn1| is that
* indefinite-length elements may be processed by this function.
*
* Tag numbers greater than 31 are not supported. */
OPENSSL_EXPORT int CBS_get_asn1_ber(CBS *cbs, CBS *out, unsigned tag_value);
/* CBS_get_asn1_element acts like |CBS_get_asn1| but |out| will include the
* ASN.1 header bytes too. */
OPENSSL_EXPORT int CBS_get_asn1_element(CBS *cbs, CBS *out, unsigned tag_value);
/* CBS_get_asn1_uint64 gets an ASN.1 INTEGER from |cbs| using |CBS_get_asn1|
* and sets |*out| to its value. It returns one on success and zero on error,
* where error includes the integer being negative, or too large to represent
* in 64 bits. */
OPENSSL_EXPORT int CBS_get_asn1_uint64(CBS *cbs, uint64_t *out);
/* CRYPTO ByteBuilder.
*