blob: f23bd110f3b5a025a294df4c0d982a726e1bfbc7 [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "crypto/sign.h"
#include "base/check.h"
#include "base/check_op.h"
#include "crypto/openssl_util.h"
#include "third_party/boringssl/src/include/openssl/evp.h"
#include "third_party/boringssl/src/include/openssl/rsa.h"
namespace crypto::sign {
namespace {
enum SignatureMode {
kOneShot,
kStreaming,
};
bool CanUseKeyForSignatureKind(SignatureKind kind,
const EVP_PKEY* key,
SignatureMode mode) {
// There is a separate EVP_PKEY_RSA_PSS value that this can return for
// PSS-specific keys, but it's not used by PublicKey or PrivateKey.
const int id = EVP_PKEY_id(key);
switch (kind) {
case RSA_PKCS1_SHA1:
case RSA_PKCS1_SHA256:
case RSA_PSS_SHA256:
// There exists an EVP_PKEY_RSA_PSS key type for RSA-PSS-specific keys,
// but BoringSSL doesn't implement it and Chromium doesn't use it.
return id == EVP_PKEY_RSA;
case ECDSA_SHA256:
return id == EVP_PKEY_EC;
case ED25519:
return id == EVP_PKEY_ED25519 && mode == kOneShot;
}
return false;
}
const EVP_MD* DigestForSignatureKind(SignatureKind kind) {
switch (kind) {
case RSA_PKCS1_SHA1:
return EVP_sha1();
case RSA_PKCS1_SHA256:
return EVP_sha256();
case RSA_PSS_SHA256:
return EVP_sha256();
case ECDSA_SHA256:
return EVP_sha256();
case ED25519:
return nullptr;
}
}
void ConfigurePkeyCtx(EVP_PKEY_CTX* pkctx, SignatureKind kind) {
if (kind == RSA_PSS_SHA256) {
CHECK(EVP_PKEY_CTX_set_rsa_padding(pkctx, RSA_PKCS1_PSS_PADDING));
CHECK(EVP_PKEY_CTX_set_rsa_mgf1_md(pkctx, DigestForSignatureKind(kind)));
CHECK(EVP_PKEY_CTX_set_rsa_pss_saltlen(pkctx, RSA_PSS_SALTLEN_DIGEST));
}
}
} // namespace
std::vector<uint8_t> Sign(SignatureKind kind,
const crypto::keypair::PrivateKey& key,
base::span<const uint8_t> data) {
CHECK(CanUseKeyForSignatureKind(kind, key.key(), kOneShot));
const EVP_MD* const md = DigestForSignatureKind(kind);
EVP_PKEY_CTX* pkctx;
bssl::UniquePtr<EVP_MD_CTX> context(EVP_MD_CTX_new());
CHECK(EVP_DigestSignInit(context.get(), &pkctx, md, nullptr,
const_cast<EVP_PKEY*>(key.key())));
ConfigurePkeyCtx(pkctx, kind);
size_t len = 0;
CHECK(EVP_DigestSign(context.get(), nullptr, &len, data.data(), data.size()));
std::vector<uint8_t> result(len);
CHECK(EVP_DigestSign(context.get(), result.data(), &len, data.data(),
data.size()));
result.resize(len);
return result;
}
bool Verify(SignatureKind kind,
const crypto::keypair::PublicKey& key,
base::span<const uint8_t> data,
base::span<const uint8_t> signature) {
CHECK(CanUseKeyForSignatureKind(kind, key.key(), kOneShot));
const EVP_MD* const md = DigestForSignatureKind(kind);
EVP_PKEY_CTX* pkctx;
bssl::UniquePtr<EVP_MD_CTX> context(EVP_MD_CTX_new());
CHECK(EVP_DigestVerifyInit(context.get(), &pkctx, md, nullptr,
const_cast<EVP_PKEY*>(key.key())));
ConfigurePkeyCtx(pkctx, kind);
return EVP_DigestVerify(context.get(), signature.data(), signature.size(),
data.data(), data.size()) == 1;
}
Signer::Signer(SignatureKind kind, crypto::keypair::PrivateKey key)
: key_(key), sign_context_(EVP_MD_CTX_new()) {
CHECK(CanUseKeyForSignatureKind(kind, key.key(), kStreaming));
OpenSSLErrStackTracer err_tracer(FROM_HERE);
const EVP_MD* const md = DigestForSignatureKind(kind);
EVP_PKEY_CTX* pkctx;
CHECK(
EVP_DigestSignInit(sign_context_.get(), &pkctx, md, nullptr, key.key()));
ConfigurePkeyCtx(pkctx, kind);
}
Signer::~Signer() = default;
void Signer::Update(base::span<const uint8_t> data) {
CHECK(EVP_DigestSignUpdate(sign_context_.get(), data.data(), data.size()));
}
std::vector<uint8_t> Signer::Finish() {
OpenSSLErrStackTracer err_tracer(FROM_HERE);
size_t len = 0;
// Determine the maximum length of the signature.
CHECK(EVP_DigestSignFinal(sign_context_.get(), nullptr, &len));
std::vector<uint8_t> signature(len);
CHECK(EVP_DigestSignFinal(sign_context_.get(), signature.data(), &len));
signature.resize(len);
return signature;
}
Verifier::Verifier(SignatureKind kind,
crypto::keypair::PublicKey key,
base::span<const uint8_t> signature)
: key_(key), verify_context_(EVP_MD_CTX_new()) {
OpenSSLErrStackTracer err_tracer(FROM_HERE);
CHECK(CanUseKeyForSignatureKind(kind, key.key(), kStreaming));
signature_.resize(signature.size());
base::span(signature_).copy_from(signature);
EVP_PKEY_CTX* pkctx;
const EVP_MD* const md = DigestForSignatureKind(kind);
CHECK(EVP_DigestVerifyInit(verify_context_.get(), &pkctx, md, nullptr,
key.key()));
ConfigurePkeyCtx(pkctx, kind);
}
Verifier::~Verifier() = default;
void Verifier::Update(base::span<const uint8_t> data) {
OpenSSLErrStackTracer err_tracer(FROM_HERE);
CHECK_EQ(
EVP_DigestVerifyUpdate(verify_context_.get(), data.data(), data.size()),
1);
}
bool Verifier::Finish() {
OpenSSLErrStackTracer err_tracer(FROM_HERE);
int rv = EVP_DigestVerifyFinal(verify_context_.get(), signature_.data(),
signature_.size());
return rv == 1;
}
} // namespace crypto::sign