Fix and test EVP_PKEY_CTX copying.
The RSA-PSS salt length was not being copied, and copying an Ed25519 EVP_MD_CTX did not work. This is rather pointless (an EVP_PKEY_CTX is just a bundle of parameters), and it's unlikely anyone ever will use this. But since OpenSSL's EVP_PKEY signing API reuses EVP_MD_CTX and EVP_MD_CTX_copy_ex is plausible in that scenario, we're stuck making EVP_MD_CTX_copy_ex reachable for EVP_PKEY too. That then implies EVP_PKEY_dup should exist, and if it exists we should be testing it. Change-Id: I189435d0c716a83f58e1d8ac4abc2c409ecfea64 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/35626 Commit-Queue: David Benjamin <davidben@google.com> Commit-Queue: Adam Langley <agl@google.com> Reviewed-by: Adam Langley <agl@google.com>
This commit is contained in:
parent
d1a6d23686
commit
2e26348e25
@ -297,16 +297,19 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (md_op_init) {
|
if (md_op_init) {
|
||||||
bssl::ScopedEVP_MD_CTX ctx;
|
bssl::ScopedEVP_MD_CTX ctx, copy;
|
||||||
EVP_PKEY_CTX *pctx;
|
EVP_PKEY_CTX *pctx;
|
||||||
if (!md_op_init(ctx.get(), &pctx, digest, nullptr, key) ||
|
if (!md_op_init(ctx.get(), &pctx, digest, nullptr, key) ||
|
||||||
!SetupContext(t, pctx)) {
|
!SetupContext(t, pctx) ||
|
||||||
|
!EVP_MD_CTX_copy_ex(copy.get(), ctx.get())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (is_verify) {
|
if (is_verify) {
|
||||||
return !!EVP_DigestVerify(ctx.get(), output.data(), output.size(),
|
return EVP_DigestVerify(ctx.get(), output.data(), output.size(),
|
||||||
input.data(), input.size());
|
input.data(), input.size()) &&
|
||||||
|
EVP_DigestVerify(copy.get(), output.data(), output.size(),
|
||||||
|
input.data(), input.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t len;
|
size_t len;
|
||||||
@ -321,6 +324,21 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
|
|||||||
}
|
}
|
||||||
actual.resize(len);
|
actual.resize(len);
|
||||||
EXPECT_EQ(Bytes(output), Bytes(actual));
|
EXPECT_EQ(Bytes(output), Bytes(actual));
|
||||||
|
|
||||||
|
// Repeat the test with |copy|, to check |EVP_MD_CTX_copy_ex| duplicated
|
||||||
|
// everything.
|
||||||
|
if (!EVP_DigestSign(copy.get(), nullptr, &len, input.data(),
|
||||||
|
input.size())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
actual.resize(len);
|
||||||
|
if (!EVP_DigestSign(copy.get(), actual.data(), &len, input.data(),
|
||||||
|
input.size()) ||
|
||||||
|
!t->GetBytes(&output, "Output")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
actual.resize(len);
|
||||||
|
EXPECT_EQ(Bytes(output), Bytes(actual));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -333,72 +351,78 @@ static bool TestEVP(FileTest *t, KeyMap *key_map) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bssl::UniquePtr<EVP_PKEY_CTX> copy(EVP_PKEY_CTX_dup(ctx.get()));
|
||||||
|
if (!copy) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (is_verify) {
|
if (is_verify) {
|
||||||
return !!EVP_PKEY_verify(ctx.get(), output.data(), output.size(),
|
return EVP_PKEY_verify(ctx.get(), output.data(), output.size(),
|
||||||
input.data(), input.size());
|
input.data(), input.size()) &&
|
||||||
|
EVP_PKEY_verify(copy.get(), output.data(), output.size(),
|
||||||
|
input.data(), input.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t len;
|
for (EVP_PKEY_CTX *pctx : {ctx.get(), copy.get()}) {
|
||||||
if (!key_op(ctx.get(), nullptr, &len, input.data(), input.size())) {
|
size_t len;
|
||||||
return false;
|
if (!key_op(pctx, nullptr, &len, input.data(), input.size())) {
|
||||||
}
|
return false;
|
||||||
actual.resize(len);
|
}
|
||||||
if (!key_op(ctx.get(), actual.data(), &len, input.data(), input.size())) {
|
actual.resize(len);
|
||||||
return false;
|
if (!key_op(pctx, actual.data(), &len, input.data(), input.size())) {
|
||||||
}
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// Encryption is non-deterministic, so we check by decrypting.
|
if (t->HasAttribute("CheckDecrypt")) {
|
||||||
if (t->HasAttribute("CheckDecrypt")) {
|
// Encryption is non-deterministic, so we check by decrypting.
|
||||||
size_t plaintext_len;
|
size_t plaintext_len;
|
||||||
ctx.reset(EVP_PKEY_CTX_new(key, nullptr));
|
bssl::UniquePtr<EVP_PKEY_CTX> decrypt_ctx(EVP_PKEY_CTX_new(key, nullptr));
|
||||||
if (!ctx ||
|
if (!decrypt_ctx ||
|
||||||
!EVP_PKEY_decrypt_init(ctx.get()) ||
|
!EVP_PKEY_decrypt_init(decrypt_ctx.get()) ||
|
||||||
(digest != nullptr &&
|
(digest != nullptr &&
|
||||||
!EVP_PKEY_CTX_set_signature_md(ctx.get(), digest)) ||
|
!EVP_PKEY_CTX_set_signature_md(decrypt_ctx.get(), digest)) ||
|
||||||
!SetupContext(t, ctx.get()) ||
|
!SetupContext(t, decrypt_ctx.get()) ||
|
||||||
!EVP_PKEY_decrypt(ctx.get(), nullptr, &plaintext_len, actual.data(),
|
!EVP_PKEY_decrypt(decrypt_ctx.get(), nullptr, &plaintext_len,
|
||||||
actual.size())) {
|
actual.data(), actual.size())) {
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
output.resize(plaintext_len);
|
||||||
|
if (!EVP_PKEY_decrypt(decrypt_ctx.get(), output.data(), &plaintext_len,
|
||||||
|
actual.data(), actual.size())) {
|
||||||
|
ADD_FAILURE() << "Could not decrypt result.";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
output.resize(plaintext_len);
|
||||||
|
EXPECT_EQ(Bytes(input), Bytes(output)) << "Decrypted result mismatch.";
|
||||||
|
} else if (t->HasAttribute("CheckVerify")) {
|
||||||
|
// Some signature schemes are non-deterministic, so we check by verifying.
|
||||||
|
bssl::UniquePtr<EVP_PKEY_CTX> verify_ctx(EVP_PKEY_CTX_new(key, nullptr));
|
||||||
|
if (!verify_ctx ||
|
||||||
|
!EVP_PKEY_verify_init(verify_ctx.get()) ||
|
||||||
|
(digest != nullptr &&
|
||||||
|
!EVP_PKEY_CTX_set_signature_md(verify_ctx.get(), digest)) ||
|
||||||
|
!SetupContext(t, verify_ctx.get())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (t->HasAttribute("VerifyPSSSaltLength")) {
|
||||||
|
if (!EVP_PKEY_CTX_set_rsa_pss_saltlen(
|
||||||
|
verify_ctx.get(),
|
||||||
|
atoi(t->GetAttributeOrDie("VerifyPSSSaltLength").c_str()))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EXPECT_TRUE(EVP_PKEY_verify(verify_ctx.get(), actual.data(),
|
||||||
|
actual.size(), input.data(), input.size()))
|
||||||
|
<< "Could not verify result.";
|
||||||
|
} else {
|
||||||
|
// By default, check by comparing the result against Output.
|
||||||
|
if (!t->GetBytes(&output, "Output")) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
actual.resize(len);
|
||||||
|
EXPECT_EQ(Bytes(output), Bytes(actual));
|
||||||
}
|
}
|
||||||
output.resize(plaintext_len);
|
|
||||||
if (!EVP_PKEY_decrypt(ctx.get(), output.data(), &plaintext_len,
|
|
||||||
actual.data(), actual.size())) {
|
|
||||||
ADD_FAILURE() << "Could not decrypt result.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
output.resize(plaintext_len);
|
|
||||||
EXPECT_EQ(Bytes(input), Bytes(output)) << "Decrypted result mismatch.";
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Some signature schemes are non-deterministic, so we check by verifying.
|
|
||||||
if (t->HasAttribute("CheckVerify")) {
|
|
||||||
ctx.reset(EVP_PKEY_CTX_new(key, nullptr));
|
|
||||||
if (!ctx ||
|
|
||||||
!EVP_PKEY_verify_init(ctx.get()) ||
|
|
||||||
(digest != nullptr &&
|
|
||||||
!EVP_PKEY_CTX_set_signature_md(ctx.get(), digest)) ||
|
|
||||||
!SetupContext(t, ctx.get())) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (t->HasAttribute("VerifyPSSSaltLength") &&
|
|
||||||
!EVP_PKEY_CTX_set_rsa_pss_saltlen(
|
|
||||||
ctx.get(),
|
|
||||||
atoi(t->GetAttributeOrDie("VerifyPSSSaltLength").c_str()))) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
EXPECT_TRUE(EVP_PKEY_verify(ctx.get(), actual.data(), actual.size(),
|
|
||||||
input.data(), input.size()))
|
|
||||||
<< "Could not verify result.";
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// By default, check by comparing the result against Output.
|
|
||||||
if (!t->GetBytes(&output, "Output")) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
actual.resize(len);
|
|
||||||
EXPECT_EQ(Bytes(output), Bytes(actual));
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,6 +274,17 @@ Digest = SHA256
|
|||||||
Input = "0123456789ABCDEF0123456789ABCDEF"
|
Input = "0123456789ABCDEF0123456789ABCDEF"
|
||||||
CheckVerify
|
CheckVerify
|
||||||
|
|
||||||
|
# Check a salt length with a non-standard digest length, to verify things are
|
||||||
|
# not just working due to defaults. (The current default is a maximum salt
|
||||||
|
# length, but the ecosystem has converged on matching the digest length, so we
|
||||||
|
# may change this in the future.)
|
||||||
|
Sign = RSA-2048
|
||||||
|
RSAPadding = PSS
|
||||||
|
PSSSaltLength = 42
|
||||||
|
Digest = SHA256
|
||||||
|
Input = "0123456789ABCDEF0123456789ABCDEF"
|
||||||
|
CheckVerify
|
||||||
|
|
||||||
# Auto-detected salt length
|
# Auto-detected salt length
|
||||||
Verify = RSA-2048-SPKI
|
Verify = RSA-2048-SPKI
|
||||||
RSAPadding = PSS
|
RSAPadding = PSS
|
||||||
|
@ -132,6 +132,7 @@ static int pkey_rsa_copy(EVP_PKEY_CTX *dst, EVP_PKEY_CTX *src) {
|
|||||||
dctx->pad_mode = sctx->pad_mode;
|
dctx->pad_mode = sctx->pad_mode;
|
||||||
dctx->md = sctx->md;
|
dctx->md = sctx->md;
|
||||||
dctx->mgf1md = sctx->mgf1md;
|
dctx->mgf1md = sctx->mgf1md;
|
||||||
|
dctx->saltlen = sctx->saltlen;
|
||||||
if (sctx->oaep_label) {
|
if (sctx->oaep_label) {
|
||||||
OPENSSL_free(dctx->oaep_label);
|
OPENSSL_free(dctx->oaep_label);
|
||||||
dctx->oaep_label = BUF_memdup(sctx->oaep_label, sctx->oaep_labellen);
|
dctx->oaep_label = BUF_memdup(sctx->oaep_label, sctx->oaep_labellen);
|
||||||
|
@ -116,7 +116,9 @@ void EVP_MD_CTX_free(EVP_MD_CTX *ctx) {
|
|||||||
void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx) { EVP_MD_CTX_free(ctx); }
|
void EVP_MD_CTX_destroy(EVP_MD_CTX *ctx) { EVP_MD_CTX_free(ctx); }
|
||||||
|
|
||||||
int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) {
|
int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) {
|
||||||
if (in == NULL || in->digest == NULL) {
|
// |in->digest| may be NULL if this is a signing |EVP_MD_CTX| for, e.g.,
|
||||||
|
// Ed25519 which does not hash with |EVP_MD_CTX|.
|
||||||
|
if (in == NULL || (in->pctx == NULL && in->digest == NULL)) {
|
||||||
OPENSSL_PUT_ERROR(DIGEST, DIGEST_R_INPUT_NOT_INITIALIZED);
|
OPENSSL_PUT_ERROR(DIGEST, DIGEST_R_INPUT_NOT_INITIALIZED);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -131,29 +133,34 @@ int EVP_MD_CTX_copy_ex(EVP_MD_CTX *out, const EVP_MD_CTX *in) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t *tmp_buf;
|
uint8_t *tmp_buf = NULL;
|
||||||
if (out->digest != in->digest) {
|
if (in->digest != NULL) {
|
||||||
assert(in->digest->ctx_size != 0);
|
if (out->digest != in->digest) {
|
||||||
tmp_buf = OPENSSL_malloc(in->digest->ctx_size);
|
assert(in->digest->ctx_size != 0);
|
||||||
if (tmp_buf == NULL) {
|
tmp_buf = OPENSSL_malloc(in->digest->ctx_size);
|
||||||
if (pctx) {
|
if (tmp_buf == NULL) {
|
||||||
in->pctx_ops->free(pctx);
|
if (pctx) {
|
||||||
|
in->pctx_ops->free(pctx);
|
||||||
|
}
|
||||||
|
OPENSSL_PUT_ERROR(DIGEST, ERR_R_MALLOC_FAILURE);
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
OPENSSL_PUT_ERROR(DIGEST, ERR_R_MALLOC_FAILURE);
|
} else {
|
||||||
return 0;
|
// |md_data| will be the correct size in this case. It's removed from
|
||||||
|
// |out| so that |EVP_MD_CTX_cleanup| doesn't free it, and then it's
|
||||||
|
// reused.
|
||||||
|
tmp_buf = out->md_data;
|
||||||
|
out->md_data = NULL;
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// |md_data| will be the correct size in this case. It's removed from |out|
|
|
||||||
// so that |EVP_MD_CTX_cleanup| doesn't free it, and then it's reused.
|
|
||||||
tmp_buf = out->md_data;
|
|
||||||
out->md_data = NULL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
EVP_MD_CTX_cleanup(out);
|
EVP_MD_CTX_cleanup(out);
|
||||||
|
|
||||||
out->digest = in->digest;
|
out->digest = in->digest;
|
||||||
out->md_data = tmp_buf;
|
out->md_data = tmp_buf;
|
||||||
OPENSSL_memcpy(out->md_data, in->md_data, in->digest->ctx_size);
|
if (in->digest != NULL) {
|
||||||
|
OPENSSL_memcpy(out->md_data, in->md_data, in->digest->ctx_size);
|
||||||
|
}
|
||||||
out->pctx = pctx;
|
out->pctx = pctx;
|
||||||
out->pctx_ops = in->pctx_ops;
|
out->pctx_ops = in->pctx_ops;
|
||||||
assert(out->pctx == NULL || out->pctx_ops != NULL);
|
assert(out->pctx == NULL || out->pctx_ops != NULL);
|
||||||
|
Loading…
Reference in New Issue
Block a user