// Copyright 2014 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include "base/numerics/safe_math.h" #include "components/webcrypto/algorithms/rsa_sign.h" #include "components/webcrypto/algorithms/util.h" #include "components/webcrypto/blink_key_handle.h" #include "components/webcrypto/crypto_data.h" #include "components/webcrypto/status.h" #include "crypto/openssl_util.h" #include "crypto/scoped_openssl_types.h" #include "third_party/WebKit/public/platform/WebCryptoKeyAlgorithm.h" namespace webcrypto { namespace { // Extracts the OpenSSL key and digest from a WebCrypto key. The returned // pointers will remain valid as long as |key| is alive. Status GetPKeyAndDigest(const blink::WebCryptoKey& key, EVP_PKEY** pkey, const EVP_MD** digest) { *pkey = GetEVP_PKEY(key); *digest = GetDigest(key.algorithm().rsaHashedParams()->hash()); if (!*digest) return Status::ErrorUnsupported(); return Status::Success(); } // Sets the PSS parameters on |pctx| if the key is for RSA-PSS. // // Otherwise returns Success without doing anything. Status ApplyRsaPssOptions(const blink::WebCryptoKey& key, const EVP_MD* const mgf_digest, unsigned int salt_length_bytes, EVP_PKEY_CTX* pctx) { // Only apply RSA-PSS options if the key is for RSA-PSS. if (key.algorithm().id() != blink::WebCryptoAlgorithmIdRsaPss) { DCHECK_EQ(0u, salt_length_bytes); DCHECK_EQ(blink::WebCryptoAlgorithmIdRsaSsaPkcs1v1_5, key.algorithm().id()); return Status::Success(); } // BoringSSL takes a signed int for the salt length, and interprets // negative values in a special manner. Make sure not to silently underflow. base::CheckedNumeric salt_length_bytes_int(salt_length_bytes); if (!salt_length_bytes_int.IsValid()) { // TODO(eroman): Give a better error message. return Status::OperationError(); } if (1 != EVP_PKEY_CTX_set_rsa_padding(pctx, RSA_PKCS1_PSS_PADDING) || 1 != EVP_PKEY_CTX_set_rsa_mgf1_md(pctx, mgf_digest) || 1 != EVP_PKEY_CTX_set_rsa_pss_saltlen( pctx, salt_length_bytes_int.ValueOrDie())) { return Status::OperationError(); } return Status::Success(); } } // namespace Status RsaSign(const blink::WebCryptoKey& key, unsigned int pss_salt_length_bytes, const CryptoData& data, std::vector* buffer) { if (key.type() != blink::WebCryptoKeyTypePrivate) return Status::ErrorUnexpectedKeyType(); crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); EVP_PKEY_CTX* pctx = NULL; // Owned by |ctx|. EVP_PKEY* private_key = NULL; const EVP_MD* digest = NULL; Status status = GetPKeyAndDigest(key, &private_key, &digest); if (status.IsError()) return status; // NOTE: A call to EVP_DigestSignFinal() with a NULL second parameter // returns a maximum allocation size, while the call without a NULL returns // the real one, which may be smaller. size_t sig_len = 0; if (!ctx.get() || !EVP_DigestSignInit(ctx.get(), &pctx, digest, NULL, private_key)) { return Status::OperationError(); } // Set PSS-specific options (if applicable). status = ApplyRsaPssOptions(key, digest, pss_salt_length_bytes, pctx); if (status.IsError()) return status; if (!EVP_DigestSignUpdate(ctx.get(), data.bytes(), data.byte_length()) || !EVP_DigestSignFinal(ctx.get(), NULL, &sig_len)) { return Status::OperationError(); } buffer->resize(sig_len); if (!EVP_DigestSignFinal(ctx.get(), buffer->data(), &sig_len)) return Status::OperationError(); buffer->resize(sig_len); return Status::Success(); } Status RsaVerify(const blink::WebCryptoKey& key, unsigned int pss_salt_length_bytes, const CryptoData& signature, const CryptoData& data, bool* signature_match) { if (key.type() != blink::WebCryptoKeyTypePublic) return Status::ErrorUnexpectedKeyType(); crypto::OpenSSLErrStackTracer err_tracer(FROM_HERE); crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); EVP_PKEY_CTX* pctx = NULL; // Owned by |ctx|. EVP_PKEY* public_key = NULL; const EVP_MD* digest = NULL; Status status = GetPKeyAndDigest(key, &public_key, &digest); if (status.IsError()) return status; if (!EVP_DigestVerifyInit(ctx.get(), &pctx, digest, NULL, public_key)) return Status::OperationError(); // Set PSS-specific options (if applicable). status = ApplyRsaPssOptions(key, digest, pss_salt_length_bytes, pctx); if (status.IsError()) return status; if (!EVP_DigestVerifyUpdate(ctx.get(), data.bytes(), data.byte_length())) return Status::OperationError(); *signature_match = 1 == EVP_DigestVerifyFinal(ctx.get(), signature.bytes(), signature.byte_length()); return Status::Success(); } } // namespace webcrypto