|
|
|
/* Written by Dr Stephen N Henson (steve@openssl.org) for the OpenSSL project
|
|
|
|
* 2006.
|
|
|
|
*/
|
|
|
|
/* ====================================================================
|
|
|
|
* Copyright (c) 2006 The OpenSSL Project. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
*
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in
|
|
|
|
* the documentation and/or other materials provided with the
|
|
|
|
* distribution.
|
|
|
|
*
|
|
|
|
* 3. All advertising materials mentioning features or use of this
|
|
|
|
* software must display the following acknowledgment:
|
|
|
|
* "This product includes software developed by the OpenSSL Project
|
|
|
|
* for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
|
|
|
|
*
|
|
|
|
* 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
|
|
|
|
* endorse or promote products derived from this software without
|
|
|
|
* prior written permission. For written permission, please contact
|
|
|
|
* licensing@OpenSSL.org.
|
|
|
|
*
|
|
|
|
* 5. Products derived from this software may not be called "OpenSSL"
|
|
|
|
* nor may "OpenSSL" appear in their names without prior written
|
|
|
|
* permission of the OpenSSL Project.
|
|
|
|
*
|
|
|
|
* 6. Redistributions of any form whatsoever must retain the following
|
|
|
|
* acknowledgment:
|
|
|
|
* "This product includes software developed by the OpenSSL Project
|
|
|
|
* for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
|
|
|
|
* EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR
|
|
|
|
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
|
|
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
|
|
|
|
* OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
* ====================================================================
|
|
|
|
*
|
|
|
|
* This product includes cryptographic software written by Eric Young
|
|
|
|
* (eay@cryptsoft.com). This product includes software written by Tim
|
|
|
|
* Hudson (tjh@cryptsoft.com). */
|
|
|
|
|
|
|
|
#include <openssl/evp.h>
|
|
|
|
|
|
|
|
#include <openssl/digest.h>
|
|
|
|
#include <openssl/bn.h>
|
|
|
|
#include <openssl/bytestring.h>
|
|
|
|
#include <openssl/dsa.h>
|
|
|
|
#include <openssl/err.h>
|
|
|
|
|
|
|
|
#include "internal.h"
|
|
|
|
|
|
|
|
|
|
|
|
static int dsa_pub_decode(EVP_PKEY *out, CBS *params, CBS *key) {
|
|
|
|
// See RFC 3279, section 2.3.2.
|
|
|
|
|
|
|
|
// Parameters may or may not be present.
|
|
|
|
DSA *dsa;
|
|
|
|
if (CBS_len(params) == 0) {
|
|
|
|
dsa = DSA_new();
|
|
|
|
if (dsa == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dsa = DSA_parse_parameters(params);
|
|
|
|
if (dsa == NULL || CBS_len(params) != 0) {
|
|
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dsa->pub_key = BN_new();
|
|
|
|
if (dsa->pub_key == NULL) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!BN_parse_asn1_unsigned(key, dsa->pub_key) ||
|
|
|
|
CBS_len(key) != 0) {
|
|
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
EVP_PKEY_assign_DSA(out, dsa);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
err:
|
|
|
|
DSA_free(dsa);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_pub_encode(CBB *out, const EVP_PKEY *key) {
|
|
|
|
const DSA *dsa = key->pkey.dsa;
|
|
|
|
const int has_params = dsa->p != NULL && dsa->q != NULL && dsa->g != NULL;
|
|
|
|
|
|
|
|
// See RFC 5480, section 2.
|
|
|
|
CBB spki, algorithm, oid, key_bitstring;
|
|
|
|
if (!CBB_add_asn1(out, &spki, CBS_ASN1_SEQUENCE) ||
|
|
|
|
!CBB_add_asn1(&spki, &algorithm, CBS_ASN1_SEQUENCE) ||
|
|
|
|
!CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
|
|
|
|
!CBB_add_bytes(&oid, dsa_asn1_meth.oid, dsa_asn1_meth.oid_len) ||
|
|
|
|
(has_params &&
|
|
|
|
!DSA_marshal_parameters(&algorithm, dsa)) ||
|
|
|
|
!CBB_add_asn1(&spki, &key_bitstring, CBS_ASN1_BITSTRING) ||
|
|
|
|
!CBB_add_u8(&key_bitstring, 0 /* padding */) ||
|
|
|
|
!BN_marshal_asn1(&key_bitstring, dsa->pub_key) ||
|
|
|
|
!CBB_flush(out)) {
|
|
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_priv_decode(EVP_PKEY *out, CBS *params, CBS *key) {
|
|
|
|
// See PKCS#11, v2.40, section 2.5.
|
|
|
|
|
|
|
|
// Decode parameters.
|
|
|
|
BN_CTX *ctx = NULL;
|
|
|
|
DSA *dsa = DSA_parse_parameters(params);
|
|
|
|
if (dsa == NULL || CBS_len(params) != 0) {
|
|
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
dsa->priv_key = BN_new();
|
|
|
|
dsa->pub_key = BN_new();
|
|
|
|
if (dsa->priv_key == NULL || dsa->pub_key == NULL) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
Bound RSA and DSA key sizes better.
Most asymmetric operations scale superlinearly, which makes them
potential DoS vectors. This (and other problems) are mitigated with
fixed sizes, like RSA-2048, P-256, or curve25519.
In older algorithms like RSA and DSA, these sizes are conventions rather
than well-defined algorithms. "Everyone" uses RSA-2048, but code which
imports an RSA key may see an arbitrary key size, possibly from an
untrusted source. This is commonly a public key, so we bound RSA key
sizes in check_modulus_and_exponent_sizes.
However, some applications import external private keys, and may need
tighter bounds. These typically parse the key then check the result.
However, parsing itself can perform superlinear work (RSA_check_key or
recovering the DSA public key).
This CL does the following:
- Rename check_modulus_and_exponent_sizes to rsa_check_public_key and
additionally call it from RSA_check_key.
- Fix a bug where RSA_check_key, on CRT-less keys, did not bound d, and
bound p and q before multiplying (quadratic).
- Our DSA verifier had stricter checks on q (160-, 224-, and 256-bit
only) than our DSA signer (multiple of 8 bits). Aligner the signer to
the verifier's checks.
- Validate DSA group sizes on parse, as well as priv_key < q, to bound
the running time.
Ideally these invariants would be checked exactly once at construction,
but our RSA and DSA implementations suffer from some OpenSSL's API
mistakes (https://crbug.com/boringssl/316), which means it is hard to
consistently enforce invariants. This CL focuses on the parser, but
later I'd like to better rationalize the freeze_private_key logic.
Performance of parsing RSA and DSA keys, gathered on my laptop.
Did 15130 RSA-2048 parse operations in 5022458us (3012.5 ops/sec)
Did 4888 RSA-4096 parse operations in 5060606us (965.9 ops/sec)
Did 354 RSA-16384 parse operations in 5043565us (70.2 ops/sec)
Did 88 RSA-32768 parse operations in 5038293us (17.5 ops/sec) [rejected by this CL]
Did 35000 DSA-1024/256 parse operations in 5030447us (6957.6 ops/sec)
Did 11316 DSA-2048/256 parse operations in 5094664us (2221.1 ops/sec)
Did 5488 DSA-3072/256 parse operations in 5096032us (1076.9 ops/sec)
Did 3172 DSA-4096/256 parse operations in 5041220us (629.2 ops/sec)
Did 840 DSA-8192/256 parse operations in 5070616us (165.7 ops/sec)
Did 285 DSA-10000/256 parse operations in 5004033us (57.0 ops/sec)
Did 74 DSA-20000/256 parse operations in 5066299us (14.6 ops/sec) [rejected by this CL]
Update-Note: Some invalid or overly large RSA and DSA keys may
previously have been accepted that are now rejected at parse time. For
public keys, this only moves the error from verification to parsing. In
some private key cases, we would previously allow signing with those
keys, but the resulting signatures would not be accepted by BoringSSL
anyway. This CL makes us behave more consistently.
Bug: oss-fuzz:24730
Change-Id: I4ad2003ee61138b693e65d3da4c6aa00bc165251
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/42504
Reviewed-by: Adam Langley <agl@google.com>
5 years ago
|
|
|
// Decode the key. To avoid DoS attacks when importing private keys, we bound
|
|
|
|
// |dsa->priv_key| against |dsa->q|, which itself bound by
|
|
|
|
// |DSA_parse_parameters|. (We cannot call |BN_num_bits| on |dsa->priv_key|.
|
|
|
|
// That would leak a secret bit width.)
|
|
|
|
if (!BN_parse_asn1_unsigned(key, dsa->priv_key) ||
|
Bound RSA and DSA key sizes better.
Most asymmetric operations scale superlinearly, which makes them
potential DoS vectors. This (and other problems) are mitigated with
fixed sizes, like RSA-2048, P-256, or curve25519.
In older algorithms like RSA and DSA, these sizes are conventions rather
than well-defined algorithms. "Everyone" uses RSA-2048, but code which
imports an RSA key may see an arbitrary key size, possibly from an
untrusted source. This is commonly a public key, so we bound RSA key
sizes in check_modulus_and_exponent_sizes.
However, some applications import external private keys, and may need
tighter bounds. These typically parse the key then check the result.
However, parsing itself can perform superlinear work (RSA_check_key or
recovering the DSA public key).
This CL does the following:
- Rename check_modulus_and_exponent_sizes to rsa_check_public_key and
additionally call it from RSA_check_key.
- Fix a bug where RSA_check_key, on CRT-less keys, did not bound d, and
bound p and q before multiplying (quadratic).
- Our DSA verifier had stricter checks on q (160-, 224-, and 256-bit
only) than our DSA signer (multiple of 8 bits). Aligner the signer to
the verifier's checks.
- Validate DSA group sizes on parse, as well as priv_key < q, to bound
the running time.
Ideally these invariants would be checked exactly once at construction,
but our RSA and DSA implementations suffer from some OpenSSL's API
mistakes (https://crbug.com/boringssl/316), which means it is hard to
consistently enforce invariants. This CL focuses on the parser, but
later I'd like to better rationalize the freeze_private_key logic.
Performance of parsing RSA and DSA keys, gathered on my laptop.
Did 15130 RSA-2048 parse operations in 5022458us (3012.5 ops/sec)
Did 4888 RSA-4096 parse operations in 5060606us (965.9 ops/sec)
Did 354 RSA-16384 parse operations in 5043565us (70.2 ops/sec)
Did 88 RSA-32768 parse operations in 5038293us (17.5 ops/sec) [rejected by this CL]
Did 35000 DSA-1024/256 parse operations in 5030447us (6957.6 ops/sec)
Did 11316 DSA-2048/256 parse operations in 5094664us (2221.1 ops/sec)
Did 5488 DSA-3072/256 parse operations in 5096032us (1076.9 ops/sec)
Did 3172 DSA-4096/256 parse operations in 5041220us (629.2 ops/sec)
Did 840 DSA-8192/256 parse operations in 5070616us (165.7 ops/sec)
Did 285 DSA-10000/256 parse operations in 5004033us (57.0 ops/sec)
Did 74 DSA-20000/256 parse operations in 5066299us (14.6 ops/sec) [rejected by this CL]
Update-Note: Some invalid or overly large RSA and DSA keys may
previously have been accepted that are now rejected at parse time. For
public keys, this only moves the error from verification to parsing. In
some private key cases, we would previously allow signing with those
keys, but the resulting signatures would not be accepted by BoringSSL
anyway. This CL makes us behave more consistently.
Bug: oss-fuzz:24730
Change-Id: I4ad2003ee61138b693e65d3da4c6aa00bc165251
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/42504
Reviewed-by: Adam Langley <agl@google.com>
5 years ago
|
|
|
CBS_len(key) != 0 ||
|
|
|
|
BN_cmp(dsa->priv_key, dsa->q) >= 0) {
|
|
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_DECODE_ERROR);
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Calculate the public key.
|
|
|
|
ctx = BN_CTX_new();
|
|
|
|
if (ctx == NULL ||
|
|
|
|
!BN_mod_exp_mont_consttime(dsa->pub_key, dsa->g, dsa->priv_key, dsa->p,
|
|
|
|
ctx, NULL)) {
|
|
|
|
goto err;
|
|
|
|
}
|
|
|
|
|
|
|
|
BN_CTX_free(ctx);
|
|
|
|
EVP_PKEY_assign_DSA(out, dsa);
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
err:
|
|
|
|
BN_CTX_free(ctx);
|
|
|
|
DSA_free(dsa);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_priv_encode(CBB *out, const EVP_PKEY *key) {
|
|
|
|
const DSA *dsa = key->pkey.dsa;
|
|
|
|
if (dsa == NULL || dsa->priv_key == NULL) {
|
|
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_MISSING_PARAMETERS);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// See PKCS#11, v2.40, section 2.5.
|
|
|
|
CBB pkcs8, algorithm, oid, private_key;
|
|
|
|
if (!CBB_add_asn1(out, &pkcs8, CBS_ASN1_SEQUENCE) ||
|
|
|
|
!CBB_add_asn1_uint64(&pkcs8, 0 /* version */) ||
|
|
|
|
!CBB_add_asn1(&pkcs8, &algorithm, CBS_ASN1_SEQUENCE) ||
|
|
|
|
!CBB_add_asn1(&algorithm, &oid, CBS_ASN1_OBJECT) ||
|
|
|
|
!CBB_add_bytes(&oid, dsa_asn1_meth.oid, dsa_asn1_meth.oid_len) ||
|
|
|
|
!DSA_marshal_parameters(&algorithm, dsa) ||
|
|
|
|
!CBB_add_asn1(&pkcs8, &private_key, CBS_ASN1_OCTETSTRING) ||
|
|
|
|
!BN_marshal_asn1(&private_key, dsa->priv_key) ||
|
|
|
|
!CBB_flush(out)) {
|
|
|
|
OPENSSL_PUT_ERROR(EVP, EVP_R_ENCODE_ERROR);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int int_dsa_size(const EVP_PKEY *pkey) {
|
|
|
|
return DSA_size(pkey->pkey.dsa);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_bits(const EVP_PKEY *pkey) {
|
|
|
|
return BN_num_bits(pkey->pkey.dsa->p);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_missing_parameters(const EVP_PKEY *pkey) {
|
|
|
|
DSA *dsa;
|
|
|
|
dsa = pkey->pkey.dsa;
|
|
|
|
if (dsa->p == NULL || dsa->q == NULL || dsa->g == NULL) {
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dup_bn_into(BIGNUM **out, BIGNUM *src) {
|
|
|
|
BIGNUM *a;
|
|
|
|
|
|
|
|
a = BN_dup(src);
|
|
|
|
if (a == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
BN_free(*out);
|
|
|
|
*out = a;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) {
|
|
|
|
if (!dup_bn_into(&to->pkey.dsa->p, from->pkey.dsa->p) ||
|
|
|
|
!dup_bn_into(&to->pkey.dsa->q, from->pkey.dsa->q) ||
|
|
|
|
!dup_bn_into(&to->pkey.dsa->g, from->pkey.dsa->g)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) {
|
|
|
|
return BN_cmp(a->pkey.dsa->p, b->pkey.dsa->p) == 0 &&
|
|
|
|
BN_cmp(a->pkey.dsa->q, b->pkey.dsa->q) == 0 &&
|
|
|
|
BN_cmp(a->pkey.dsa->g, b->pkey.dsa->g) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dsa_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) {
|
|
|
|
return BN_cmp(b->pkey.dsa->pub_key, a->pkey.dsa->pub_key) == 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void int_dsa_free(EVP_PKEY *pkey) { DSA_free(pkey->pkey.dsa); }
|
|
|
|
|
|
|
|
const EVP_PKEY_ASN1_METHOD dsa_asn1_meth = {
|
|
|
|
EVP_PKEY_DSA,
|
|
|
|
// 1.2.840.10040.4.1
|
|
|
|
{0x2a, 0x86, 0x48, 0xce, 0x38, 0x04, 0x01}, 7,
|
|
|
|
|
|
|
|
dsa_pub_decode,
|
|
|
|
dsa_pub_encode,
|
|
|
|
dsa_pub_cmp,
|
|
|
|
|
|
|
|
dsa_priv_decode,
|
|
|
|
dsa_priv_encode,
|
|
|
|
|
|
|
|
NULL /* set_priv_raw */,
|
|
|
|
NULL /* set_pub_raw */,
|
|
|
|
NULL /* get_priv_raw */,
|
|
|
|
NULL /* get_pub_raw */,
|
|
|
|
|
|
|
|
NULL /* pkey_opaque */,
|
|
|
|
|
|
|
|
int_dsa_size,
|
|
|
|
dsa_bits,
|
|
|
|
|
|
|
|
dsa_missing_parameters,
|
|
|
|
dsa_copy_parameters,
|
|
|
|
dsa_cmp_parameters,
|
|
|
|
|
|
|
|
int_dsa_free,
|
|
|
|
};
|