Fix parsing ECDSA keys
raw_key_to_ossl() assumes fixed ECDSA private key sizes (32 bytes for ECDSAP256SHA256, 48 bytes for ECDSAP384SHA384). Meanwhile, in rare cases, ECDSAP256SHA256 private keys are representable in 31 bytes or less (similarly for ECDSAP384SHA384) and that is how they are then stored in the "PrivateKey" field of the key file. Nevertheless, raw_key_to_ossl() always calls BN_bin2bn() with a fixed length argument, which in the cases mentioned above leads to erroneously interpreting uninitialized memory as a part of the private key. This results in the latter being malformed and broken signatures being generated. Address by using the key length provided by the caller rather than a fixed one. Apply the same change to public key parsing code for consistency, adding an INSIST() to prevent buffer overruns.
This commit is contained in:
committed by
Mark Andrews
parent
dbeea1afa0
commit
a482a6b204
@@ -66,24 +66,16 @@ raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key,
|
||||
OSSL_PARAM *params = NULL;
|
||||
EVP_PKEY_CTX *ctx = NULL;
|
||||
BIGNUM *priv = NULL;
|
||||
size_t len = 0;
|
||||
unsigned char buf[DNS_KEY_ECDSA384SIZE + 1];
|
||||
|
||||
if (key_alg == DST_ALG_ECDSA256) {
|
||||
groupname = "P-256";
|
||||
len = private ? DNS_KEY_ECDSA256SIZE / 2 : DNS_KEY_ECDSA256SIZE;
|
||||
} else if (key_alg == DST_ALG_ECDSA384) {
|
||||
groupname = "P-384";
|
||||
len = private ? DNS_KEY_ECDSA384SIZE / 2 : DNS_KEY_ECDSA384SIZE;
|
||||
} else {
|
||||
DST_RET(ISC_R_NOTIMPLEMENTED);
|
||||
}
|
||||
|
||||
ret = (private ? DST_R_INVALIDPRIVATEKEY : DST_R_INVALIDPUBLICKEY);
|
||||
if (*key_len < len) {
|
||||
DST_RET(ret);
|
||||
}
|
||||
|
||||
bld = OSSL_PARAM_BLD_new();
|
||||
if (bld == NULL) {
|
||||
DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_new",
|
||||
@@ -98,7 +90,7 @@ raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key,
|
||||
}
|
||||
|
||||
if (private) {
|
||||
priv = BN_bin2bn(key, len, NULL);
|
||||
priv = BN_bin2bn(key, *key_len, NULL);
|
||||
if (priv == NULL) {
|
||||
DST_RET(dst__openssl_toresult2("BN_bin2bn",
|
||||
DST_R_OPENSSLFAILURE));
|
||||
@@ -111,11 +103,12 @@ raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key,
|
||||
DST_R_OPENSSLFAILURE));
|
||||
}
|
||||
} else {
|
||||
INSIST(*key_len < sizeof(buf));
|
||||
buf[0] = POINT_CONVERSION_UNCOMPRESSED;
|
||||
memmove(buf + 1, key, len);
|
||||
memmove(buf + 1, key, *key_len);
|
||||
|
||||
status = OSSL_PARAM_BLD_push_octet_string(
|
||||
bld, OSSL_PKEY_PARAM_PUB_KEY, buf, 1 + len);
|
||||
bld, OSSL_PKEY_PARAM_PUB_KEY, buf, 1 + *key_len);
|
||||
if (status != 1) {
|
||||
DST_RET(dst__openssl_toresult2("OSSL_PARAM_BLD_push_"
|
||||
"octet_string",
|
||||
@@ -146,7 +139,6 @@ raw_key_to_ossl(unsigned int key_alg, int private, const unsigned char *key,
|
||||
DST_R_OPENSSLFAILURE));
|
||||
}
|
||||
|
||||
*key_len = len;
|
||||
ret = ISC_R_SUCCESS;
|
||||
|
||||
err:
|
||||
@@ -1184,8 +1176,6 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
|
||||
#if OPENSSL_VERSION_NUMBER < 0x30000000L
|
||||
EC_KEY *eckey = NULL;
|
||||
EC_KEY *pubeckey = NULL;
|
||||
#else
|
||||
size_t len;
|
||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
|
||||
const char *engine = NULL;
|
||||
const char *label = NULL;
|
||||
@@ -1257,14 +1247,9 @@ opensslecdsa_parse(dst_key_t *key, isc_lex_t *lexer, dst_key_t *pub) {
|
||||
key->keydata.pkey = NULL;
|
||||
}
|
||||
|
||||
if (key->key_alg == DST_ALG_ECDSA256) {
|
||||
len = DNS_KEY_ECDSA256SIZE / 2;
|
||||
} else {
|
||||
len = DNS_KEY_ECDSA384SIZE / 2;
|
||||
}
|
||||
|
||||
ret = raw_key_to_ossl(key->key_alg, 1,
|
||||
priv.elements[privkey_index].data, &len,
|
||||
priv.elements[privkey_index].data,
|
||||
&priv.elements[privkey_index].length,
|
||||
&key->keydata.pkey);
|
||||
#endif /* OPENSSL_VERSION_NUMBER < 0x30000000L */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user