Add crypto/bytestring-based BIGNUM DER functions.
RSA and ECDSA will both require being able to convert ASN.1 INTEGERs to
and from DER. Don't bother handling negative BIGNUMs for now. It doesn't
seem necessary and saves bothering with two's-complement vs
sign-and-magnitude.
BUG=499653
Change-Id: I1e80052067ed528809493af73b04f82539d564ff
Reviewed-on: https://boringssl-review.googlesource.com/5268
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/crypto/bn/CMakeLists.txt b/crypto/bn/CMakeLists.txt
index 2e0cb45..fa6d207 100644
--- a/crypto/bn/CMakeLists.txt
+++ b/crypto/bn/CMakeLists.txt
@@ -38,6 +38,7 @@
add.c
asm/x86_64-gcc.c
+ asn1.c
bn.c
cmp.c
convert.c
diff --git a/crypto/bn/asn1.c b/crypto/bn/asn1.c
new file mode 100644
index 0000000..81e7b89
--- /dev/null
+++ b/crypto/bn/asn1.c
@@ -0,0 +1,74 @@
+/* Copyright (c) 2015, 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/bn.h>
+
+#include <openssl/bytestring.h>
+#include <openssl/err.h>
+
+
+int BN_cbs2unsigned(CBS *cbs, BIGNUM *ret) {
+ CBS child;
+ if (!CBS_get_asn1(cbs, &child, CBS_ASN1_INTEGER) ||
+ CBS_len(&child) == 0) {
+ OPENSSL_PUT_ERROR(BN, BN_cbs2unsigned, BN_R_BAD_ENCODING);
+ return 0;
+ }
+ if (CBS_data(&child)[0] & 0x80) {
+ OPENSSL_PUT_ERROR(BN, BN_cbs2unsigned, BN_R_NEGATIVE_NUMBER);
+ return 0;
+ }
+ /* INTEGERs must be minimal. */
+ if (CBS_data(&child)[0] == 0x00 &&
+ CBS_len(&child) > 1 &&
+ !(CBS_data(&child)[1] & 0x80)) {
+ OPENSSL_PUT_ERROR(BN, BN_cbs2unsigned, BN_R_BAD_ENCODING);
+ return 0;
+ }
+ return BN_bin2bn(CBS_data(&child), CBS_len(&child), ret) != NULL;
+}
+
+int BN_bn2cbb(CBB *cbb, const BIGNUM *bn) {
+ /* Negative numbers are unsupported. */
+ if (BN_is_negative(bn)) {
+ OPENSSL_PUT_ERROR(BN, BN_bn2cbb, BN_R_NEGATIVE_NUMBER);
+ return 0;
+ }
+
+ CBB child;
+ if (!CBB_add_asn1(cbb, &child, CBS_ASN1_INTEGER)) {
+ OPENSSL_PUT_ERROR(BN, BN_bn2cbb, BN_R_ENCODE_ERROR);
+ return 0;
+ }
+
+ /* The number must be padded with a leading zero if the high bit would
+ * otherwise be set (or |bn| is zero). */
+ if (BN_num_bits(bn) % 8 == 0 &&
+ !CBB_add_u8(&child, 0x00)) {
+ OPENSSL_PUT_ERROR(BN, BN_bn2cbb, BN_R_ENCODE_ERROR);
+ return 0;
+ }
+
+ uint8_t *out;
+ if (!CBB_add_space(&child, &out, BN_num_bytes(bn))) {
+ OPENSSL_PUT_ERROR(BN, BN_bn2cbb, BN_R_ENCODE_ERROR);
+ return 0;
+ }
+ BN_bn2bin(bn, out);
+ if (!CBB_flush(cbb)) {
+ OPENSSL_PUT_ERROR(BN, BN_bn2cbb, BN_R_ENCODE_ERROR);
+ return 0;
+ }
+ return 1;
+}
diff --git a/crypto/bn/bn_test.cc b/crypto/bn/bn_test.cc
index 6a7d48c..eaceb27 100644
--- a/crypto/bn/bn_test.cc
+++ b/crypto/bn/bn_test.cc
@@ -117,11 +117,12 @@
static bool test_small_prime(FILE *fp, BN_CTX *ctx);
static bool test_mod_exp_mont5(FILE *fp, BN_CTX *ctx);
static bool test_sqrt(FILE *fp, BN_CTX *ctx);
-static bool test_bn2bin_padded(FILE *fp, BN_CTX *ctx);
-static bool test_dec2bn(FILE *fp, BN_CTX *ctx);
-static bool test_hex2bn(FILE *fp, BN_CTX *ctx);
-static bool test_asc2bn(FILE *fp, BN_CTX *ctx);
+static bool test_bn2bin_padded(BN_CTX *ctx);
+static bool test_dec2bn(BN_CTX *ctx);
+static bool test_hex2bn(BN_CTX *ctx);
+static bool test_asc2bn(BN_CTX *ctx);
static bool test_rand();
+static bool test_asn1();
static const uint8_t kSample[] =
"\xC6\x4F\x43\x04\x2A\xEA\xCA\x6E\x58\x36\x80\x5B\xE8\xC9"
@@ -311,35 +312,14 @@
}
flush_fp(bc_file.get());
- message(bc_file.get(), "BN_bn2bin_padded");
- if (!test_bn2bin_padded(bc_file.get(), ctx.get())) {
+ if (!test_bn2bin_padded(ctx.get()) ||
+ !test_dec2bn(ctx.get()) ||
+ !test_hex2bn(ctx.get()) ||
+ !test_asc2bn(ctx.get()) ||
+ !test_rand() ||
+ !test_asn1()) {
return 1;
}
- flush_fp(bc_file.get());
-
- message(bc_file.get(), "BN_dec2bn");
- if (!test_dec2bn(bc_file.get(), ctx.get())) {
- return 1;
- }
- flush_fp(bc_file.get());
-
- message(bc_file.get(), "BN_hex2bn");
- if (!test_hex2bn(bc_file.get(), ctx.get())) {
- return 1;
- }
- flush_fp(bc_file.get());
-
- message(bc_file.get(), "BN_asc2bn");
- if (!test_asc2bn(bc_file.get(), ctx.get())) {
- return 1;
- }
- flush_fp(bc_file.get());
-
- message(bc_file.get(), "BN_rand");
- if (!test_rand()) {
- return 1;
- }
- flush_fp(bc_file.get());
printf("PASS\n");
return 0;
@@ -1371,7 +1351,7 @@
return true;
}
-static bool test_bn2bin_padded(FILE *fp, BN_CTX *ctx) {
+static bool test_bn2bin_padded(BN_CTX *ctx) {
uint8_t zeros[256], out[256], reference[128];
memset(zeros, 0, sizeof(zeros));
@@ -1448,7 +1428,7 @@
return ret;
}
-static bool test_dec2bn(FILE *fp, BN_CTX *ctx) {
+static bool test_dec2bn(BN_CTX *ctx) {
ScopedBIGNUM bn;
int ret = DecimalToBIGNUM(&bn, "0");
if (ret != 1 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
@@ -1490,7 +1470,7 @@
return ret;
}
-static bool test_hex2bn(FILE *fp, BN_CTX *ctx) {
+static bool test_hex2bn(BN_CTX *ctx) {
ScopedBIGNUM bn;
int ret = HexToBIGNUM(&bn, "0");
if (ret != 1 || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
@@ -1533,7 +1513,7 @@
return ScopedBIGNUM(raw);
}
-static bool test_asc2bn(FILE *fp, BN_CTX *ctx) {
+static bool test_asc2bn(BN_CTX *ctx) {
ScopedBIGNUM bn = ASCIIToBIGNUM("0");
if (!bn || !BN_is_zero(bn.get()) || BN_is_negative(bn.get())) {
fprintf(stderr, "BN_asc2bn gave a bad result.\n");
@@ -1628,3 +1608,111 @@
return true;
}
+
+struct ASN1Test {
+ const char *value_ascii;
+ const char *der;
+ size_t der_len;
+};
+
+static const ASN1Test kASN1Tests[] = {
+ {"0", "\x02\x01\x00", 3},
+ {"1", "\x02\x01\x01", 3},
+ {"127", "\x02\x01\x7f", 3},
+ {"128", "\x02\x02\x00\x80", 4},
+ {"0xdeadbeef", "\x02\x05\x00\xde\xad\xbe\xef", 7},
+ {"0x0102030405060708",
+ "\x02\x08\x01\x02\x03\x04\x05\x06\x07\x08", 10},
+ {"0xffffffffffffffff",
+ "\x02\x09\x00\xff\xff\xff\xff\xff\xff\xff\xff", 11},
+};
+
+struct ASN1InvalidTest {
+ const char *der;
+ size_t der_len;
+};
+
+static const ASN1InvalidTest kASN1InvalidTests[] = {
+ // Bad tag.
+ {"\x03\x01\x00", 3},
+ // Empty contents.
+ {"\x02\x00", 2},
+ // Negative number.
+ {"\x02\x01\x80", 3},
+ // Leading zeros.
+ {"\x02\x02\x00\x01", 4},
+};
+
+static bool test_asn1() {
+ for (const ASN1Test &test : kASN1Tests) {
+ ScopedBIGNUM bn = ASCIIToBIGNUM(test.value_ascii);
+ if (!bn) {
+ return false;
+ }
+
+ // Test that the input is correctly parsed.
+ ScopedBIGNUM bn2(BN_new());
+ if (!bn2) {
+ return false;
+ }
+ CBS cbs;
+ CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
+ if (!BN_cbs2unsigned(&cbs, bn2.get()) || CBS_len(&cbs) != 0) {
+ fprintf(stderr, "Parsing ASN.1 INTEGER failed.\n");
+ return false;
+ }
+ if (BN_cmp(bn.get(), bn2.get()) != 0) {
+ fprintf(stderr, "Bad parse.\n");
+ return false;
+ }
+
+ // Test the value serializes correctly.
+ CBB cbb;
+ uint8_t *der;
+ size_t der_len;
+ CBB_zero(&cbb);
+ if (!CBB_init(&cbb, 0) ||
+ !BN_bn2cbb(&cbb, bn.get()) ||
+ !CBB_finish(&cbb, &der, &der_len)) {
+ CBB_cleanup(&cbb);
+ return false;
+ }
+ ScopedOpenSSLBytes delete_der(der);
+ if (der_len != test.der_len ||
+ memcmp(der, reinterpret_cast<const uint8_t*>(test.der), der_len) != 0) {
+ fprintf(stderr, "Bad serialization.\n");
+ return false;
+ }
+ }
+
+ for (const ASN1InvalidTest &test : kASN1InvalidTests) {
+ ScopedBIGNUM bn(BN_new());
+ if (!bn) {
+ return false;
+ }
+ CBS cbs;
+ CBS_init(&cbs, reinterpret_cast<const uint8_t*>(test.der), test.der_len);
+ if (BN_cbs2unsigned(&cbs, bn.get())) {
+ fprintf(stderr, "Parsed invalid input.\n");
+ return false;
+ }
+ ERR_clear_error();
+ }
+
+ // Serializing negative numbers is not supported.
+ ScopedBIGNUM bn = ASCIIToBIGNUM("-1");
+ if (!bn) {
+ return false;
+ }
+ CBB cbb;
+ CBB_zero(&cbb);
+ if (!CBB_init(&cbb, 0) ||
+ BN_bn2cbb(&cbb, bn.get())) {
+ fprintf(stderr, "Serialized negative number.\n");
+ CBB_cleanup(&cbb);
+ return false;
+ }
+ CBB_cleanup(&cbb);
+
+ return true;
+}
diff --git a/crypto/err/bn.errordata b/crypto/err/bn.errordata
index 6fd4968..a0e699a 100644
--- a/crypto/err/bn.errordata
+++ b/crypto/err/bn.errordata
@@ -1,8 +1,10 @@
BN,function,100,BN_CTX_get
BN,function,101,BN_CTX_new
BN,function,102,BN_CTX_start
+BN,function,127,BN_bn2cbb
BN,function,103,BN_bn2dec
BN,function,104,BN_bn2hex
+BN,function,128,BN_cbs2unsigned
BN,function,105,BN_div
BN,function,106,BN_div_recp
BN,function,107,BN_exp
@@ -26,11 +28,13 @@
BN,function,123,bn_wexpand
BN,function,124,mod_exp_recp
BN,reason,100,ARG2_LT_ARG3
+BN,reason,117,BAD_ENCODING
BN,reason,101,BAD_RECIPROCAL
BN,reason,102,BIGNUM_TOO_LONG
BN,reason,103,BITS_TOO_SMALL
BN,reason,104,CALLED_WITH_EVEN_MODULUS
BN,reason,105,DIV_BY_ZERO
+BN,reason,118,ENCODE_ERROR
BN,reason,106,EXPAND_ON_STATIC_BIGNUM_DATA
BN,reason,107,INPUT_NOT_REDUCED
BN,reason,108,INVALID_RANGE
diff --git a/include/openssl/bn.h b/include/openssl/bn.h
index a61e5cf..a2f6e40 100644
--- a/include/openssl/bn.h
+++ b/include/openssl/bn.h
@@ -298,6 +298,17 @@
OPENSSL_EXPORT BN_ULONG BN_get_word(const BIGNUM *bn);
+/* ASN.1 functions. */
+
+/* BN_cbs2unsigned parses a non-negative DER INTEGER from |cbs| writes the
+ * result to |ret|. It returns one on success and zero on failure. */
+OPENSSL_EXPORT int BN_cbs2unsigned(CBS *cbs, BIGNUM *ret);
+
+/* BN_bn2cbb marshals |bn| as a non-negative DER INTEGER and appends the result
+ * to |cbb|. It returns one on success and zero on failure. */
+OPENSSL_EXPORT int BN_bn2cbb(CBB *cbb, const BIGNUM *bn);
+
+
/* Internal functions.
*
* These functions are useful for code that is doing low-level manipulations of
@@ -855,6 +866,8 @@
#define BN_F_mod_exp_recp 124
#define BN_F_BN_lshift 125
#define BN_F_BN_rshift 126
+#define BN_F_BN_bn2cbb 127
+#define BN_F_BN_cbs2unsigned 128
#define BN_R_ARG2_LT_ARG3 100
#define BN_R_BAD_RECIPROCAL 101
#define BN_R_BIGNUM_TOO_LONG 102
@@ -872,5 +885,7 @@
#define BN_R_P_IS_NOT_PRIME 114
#define BN_R_TOO_MANY_ITERATIONS 115
#define BN_R_TOO_MANY_TEMPORARY_VARIABLES 116
+#define BN_R_BAD_ENCODING 117
+#define BN_R_ENCODE_ERROR 118
#endif /* OPENSSL_HEADER_BN_H */