Commit b98e1776 authored by Maksim Ivanov's avatar Maksim Ivanov Committed by Commit Bot

Add RSA-PSS support into certificateProvider

Extend the chrome.certificateProvider API to support the RSA-PSS
signature algorithms.

This makes it possible to use this API with TLS 1.3 - for example, for
using a smart card middleware extension for authenticating on a website
via a smart card (once the extension gets migrated to support this new
algorithm).

Fixed: 792204
Change-Id: I863b7fac59344f69788820a579f3ef8cdfeb9cd2
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2545925
Auto-Submit: Maksim Ivanov <emaxx@chromium.org>
Reviewed-by: default avatarKaran Bhatia <karandeepb@chromium.org>
Reviewed-by: default avatarDavid Benjamin <davidben@chromium.org>
Reviewed-by: default avatarFabian Sommer <fabiansommer@chromium.org>
Commit-Queue: Karan Bhatia <karandeepb@chromium.org>
Commit-Queue: Maksim Ivanov <emaxx@chromium.org>
Cr-Commit-Position: refs/heads/master@{#828892}
parent c3ede407
...@@ -127,6 +127,15 @@ std::unique_ptr<extensions::Event> BuildOnSignatureRequestedEvent( ...@@ -127,6 +127,15 @@ std::unique_ptr<extensions::Event> BuildOnSignatureRequestedEvent(
case SSL_SIGN_RSA_PKCS1_SHA512: case SSL_SIGN_RSA_PKCS1_SHA512:
request.algorithm = api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_SHA512; request.algorithm = api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_SHA512;
break; break;
case SSL_SIGN_RSA_PSS_RSAE_SHA256:
request.algorithm = api_cp::ALGORITHM_RSASSA_PSS_SHA256;
break;
case SSL_SIGN_RSA_PSS_RSAE_SHA384:
request.algorithm = api_cp::ALGORITHM_RSASSA_PSS_SHA384;
break;
case SSL_SIGN_RSA_PSS_RSAE_SHA512:
request.algorithm = api_cp::ALGORITHM_RSASSA_PSS_SHA512;
break;
default: default:
LOG(ERROR) << "Unknown signature algorithm"; LOG(ERROR) << "Unknown signature algorithm";
return nullptr; return nullptr;
......
...@@ -252,6 +252,15 @@ bool ParseClientCertificateInfo( ...@@ -252,6 +252,15 @@ bool ParseClientCertificateInfo(
case api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_SHA512: case api_cp::ALGORITHM_RSASSA_PKCS1_V1_5_SHA512:
out_info->supported_algorithms.push_back(SSL_SIGN_RSA_PKCS1_SHA512); out_info->supported_algorithms.push_back(SSL_SIGN_RSA_PKCS1_SHA512);
break; break;
case api_cp::ALGORITHM_RSASSA_PSS_SHA256:
out_info->supported_algorithms.push_back(SSL_SIGN_RSA_PSS_RSAE_SHA256);
break;
case api_cp::ALGORITHM_RSASSA_PSS_SHA384:
out_info->supported_algorithms.push_back(SSL_SIGN_RSA_PSS_RSAE_SHA384);
break;
case api_cp::ALGORITHM_RSASSA_PSS_SHA512:
out_info->supported_algorithms.push_back(SSL_SIGN_RSA_PSS_RSAE_SHA512);
break;
case api_cp::ALGORITHM_NONE: case api_cp::ALGORITHM_NONE:
NOTREACHED(); NOTREACHED();
return false; return false;
......
...@@ -98,17 +98,23 @@ bool RsaSignRawData(uint16_t openssl_signature_algorithm, ...@@ -98,17 +98,23 @@ bool RsaSignRawData(uint16_t openssl_signature_algorithm,
const EVP_MD* const digest_algorithm = const EVP_MD* const digest_algorithm =
SSL_get_signature_algorithm_digest(openssl_signature_algorithm); SSL_get_signature_algorithm_digest(openssl_signature_algorithm);
bssl::ScopedEVP_MD_CTX ctx; bssl::ScopedEVP_MD_CTX ctx;
if (!EVP_DigestSignInit(ctx.get(), /*EVP_PKEY_CTX** pctx=*/nullptr, EVP_PKEY_CTX* pkey_ctx = nullptr;
digest_algorithm, if (!EVP_DigestSignInit(ctx.get(), &pkey_ctx, digest_algorithm,
/*ENGINE* e=*/nullptr, key->key())) { /*ENGINE* e=*/nullptr, key->key()))
return false; return false;
if (SSL_is_signature_algorithm_rsa_pss(openssl_signature_algorithm)) {
// For RSA-PSS, configure the special padding and set the salt length to be
// equal to the hash size.
if (!EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PSS_PADDING) ||
!EVP_PKEY_CTX_set_rsa_pss_saltlen(pkey_ctx, /*salt_len=*/-1)) {
return false;
}
} }
size_t sig_len = 0; size_t sig_len = 0;
// Determine the signature length for the buffer. // Determine the signature length for the buffer.
if (!EVP_DigestSign(ctx.get(), /*out_sig=*/nullptr, &sig_len, input.data(), if (!EVP_DigestSign(ctx.get(), /*out_sig=*/nullptr, &sig_len, input.data(),
input.size())) { input.size()))
return false; return false;
}
signature->resize(sig_len); signature->resize(sig_len);
return EVP_DigestSign(ctx.get(), signature->data(), &sig_len, input.data(), return EVP_DigestSign(ctx.get(), signature->data(), &sig_len, input.data(),
input.size()) != 0; input.size()) != 0;
...@@ -118,6 +124,8 @@ bool RsaSignPrehashed(uint16_t openssl_signature_algorithm, ...@@ -118,6 +124,8 @@ bool RsaSignPrehashed(uint16_t openssl_signature_algorithm,
const std::vector<uint8_t>& digest, const std::vector<uint8_t>& digest,
crypto::RSAPrivateKey* key, crypto::RSAPrivateKey* key,
std::vector<uint8_t>* signature) { std::vector<uint8_t>* signature) {
// RSA-PSS is not supported for prehashed data.
EXPECT_FALSE(SSL_is_signature_algorithm_rsa_pss(openssl_signature_algorithm));
RSA* rsa_key = EVP_PKEY_get0_RSA(key->key()); RSA* rsa_key = EVP_PKEY_get0_RSA(key->key());
if (!rsa_key) if (!rsa_key)
return false; return false;
...@@ -337,8 +345,8 @@ class CertificateProviderApiMockedExtensionTest ...@@ -337,8 +345,8 @@ class CertificateProviderApiMockedExtensionTest
// Tests the api by navigating to a webpage that requests to perform a // Tests the api by navigating to a webpage that requests to perform a
// signature operation with the available certificate. // signature operation with the available certificate.
// This signs the request using the algorithm specified by // This signs the request using the algorithm specified by
// `openssl_signature_algorithm`, hashes it if `is_raw_data` is true, and // `openssl_signature_algorithm`, with additionally hashing it if
// replies to the extension's page. // `is_raw_data` is true, and replies to the page.
void TestNavigationToCertificateRequestingWebPage( void TestNavigationToCertificateRequestingWebPage(
const std::string& expected_request_signature_algorithm, const std::string& expected_request_signature_algorithm,
uint16_t openssl_signature_algorithm, uint16_t openssl_signature_algorithm,
...@@ -674,7 +682,7 @@ IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, ...@@ -674,7 +682,7 @@ IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
} }
// Tests the RSA MD5/SHA-1 signature algorithm. Note that TLS 1.1 is used in // Tests the RSA MD5/SHA-1 signature algorithm. Note that TLS 1.1 is used in
// order to employ this algorithm. // order to make this algorithm employed.
IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, RsaMd5Sha1) { IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, RsaMd5Sha1) {
ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_1)); ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_1));
ExecuteJavascript("supportedAlgorithms = ['RSASSA_PKCS1_v1_5_MD5_SHA1'];"); ExecuteJavascript("supportedAlgorithms = ['RSASSA_PKCS1_v1_5_MD5_SHA1'];");
...@@ -686,7 +694,7 @@ IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, RsaMd5Sha1) { ...@@ -686,7 +694,7 @@ IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, RsaMd5Sha1) {
} }
// Tests the RSA MD5/SHA-1 signature algorithm using the legacy version of the // Tests the RSA MD5/SHA-1 signature algorithm using the legacy version of the
// API. Note that TLS 1.1 is used in order to employ this algorithm. // API. Note that TLS 1.1 is used in order to make this algorithm employed.
IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
LegacyRsaMd5Sha1) { LegacyRsaMd5Sha1) {
ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_1)); ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_1));
...@@ -793,10 +801,7 @@ IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, ...@@ -793,10 +801,7 @@ IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
} }
// Tests that the RSA SHA-512 signature algorithm is still used when there are // Tests that the RSA SHA-512 signature algorithm is still used when there are
// other, less strong, algorithms specified after it. Note: The test is written // other, less strong, algorithms specified after it.
// in a way that it doesn't depend on whether the algorithm order matters or not
// (currently, the API implementation respects it, but this might change in the
// future).
IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
RsaSha512AndOthers) { RsaSha512AndOthers) {
ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_2)); ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_2));
...@@ -826,6 +831,42 @@ IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest, ...@@ -826,6 +831,42 @@ IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
/*is_raw_data=*/true); /*is_raw_data=*/true);
} }
// Tests the RSA-PSS SHA-256 signature algorithm.
IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
RsaPssSha256) {
ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_3));
ExecuteJavascript("supportedAlgorithms = ['RSASSA_PSS_SHA256'];");
ExecuteJavascript("registerForSignatureRequests();");
ExecuteJavascriptAndWaitForCallback("setCertificates();");
TestNavigationToCertificateRequestingWebPage("RSASSA_PSS_SHA256",
SSL_SIGN_RSA_PSS_RSAE_SHA256,
/*is_raw_data=*/true);
}
// Tests the RSA-PSS SHA-384 signature algorithm.
IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
RsaPssSha384) {
ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_3));
ExecuteJavascript("supportedAlgorithms = ['RSASSA_PSS_SHA384'];");
ExecuteJavascript("registerForSignatureRequests();");
ExecuteJavascriptAndWaitForCallback("setCertificates();");
TestNavigationToCertificateRequestingWebPage("RSASSA_PSS_SHA384",
SSL_SIGN_RSA_PSS_RSAE_SHA384,
/*is_raw_data=*/true);
}
// Tests the RSA-PSS SHA-512 signature algorithm.
IN_PROC_BROWSER_TEST_F(CertificateProviderApiMockedExtensionTest,
RsaPssSha512) {
ASSERT_TRUE(StartHttpsServer(net::SSL_PROTOCOL_VERSION_TLS1_3));
ExecuteJavascript("supportedAlgorithms = ['RSASSA_PSS_SHA512'];");
ExecuteJavascript("registerForSignatureRequests();");
ExecuteJavascriptAndWaitForCallback("setCertificates();");
TestNavigationToCertificateRequestingWebPage("RSASSA_PSS_SHA512",
SSL_SIGN_RSA_PSS_RSAE_SHA512,
/*is_raw_data=*/true);
}
// Test that the certificateProvider events are delivered correctly in the // Test that the certificateProvider events are delivered correctly in the
// scenario when the event listener is in a lazy background page that gets idle. // scenario when the event listener is in a lazy background page that gets idle.
IN_PROC_BROWSER_TEST_F(CertificateProviderApiTest, LazyBackgroundPage) { IN_PROC_BROWSER_TEST_F(CertificateProviderApiTest, LazyBackgroundPage) {
......
...@@ -19,7 +19,16 @@ namespace certificateProvider { ...@@ -19,7 +19,16 @@ namespace certificateProvider {
// RSASSA PKCS#1 v1.5 signature algorithm with the SHA-384 hashing function. // RSASSA PKCS#1 v1.5 signature algorithm with the SHA-384 hashing function.
RSASSA_PKCS1_v1_5_SHA384, RSASSA_PKCS1_v1_5_SHA384,
// RSASSA PKCS#1 v1.5 signature algorithm with the SHA-512 hashing function. // RSASSA PKCS#1 v1.5 signature algorithm with the SHA-512 hashing function.
RSASSA_PKCS1_v1_5_SHA512 RSASSA_PKCS1_v1_5_SHA512,
// RSASSA PSS signature algorithm with the SHA-256 hashing function, MGF1
// mask generation function and the salt of the same size as the hash.
RSASSA_PSS_SHA256,
// RSASSA PSS signature algorithm with the SHA-384 hashing function, MGF1
// mask generation function and the salt of the same size as the hash.
RSASSA_PSS_SHA384,
// RSASSA PSS signature algorithm with the SHA-512 hashing function, MGF1
// mask generation function and the salt of the same size as the hash.
RSASSA_PSS_SHA512
}; };
// Types of errors that the extension can report. // Types of errors that the extension can report.
......
...@@ -134,7 +134,7 @@ function registerForSignatureRequests() { ...@@ -134,7 +134,7 @@ function registerForSignatureRequests() {
request) { request) {
assertTrue(Number.isInteger(request.signRequestId)); assertTrue(Number.isInteger(request.signRequestId));
assertEq(l1LeafCert.buffer, request.certificate); assertEq(l1LeafCert.buffer, request.certificate);
// The sign request must refer to an algorithm that was declared to be // The sign request must refer to the algorithm that was declared to be
// supported. // supported.
assertTrue(supportedAlgorithms.includes(request.algorithm)); assertTrue(supportedAlgorithms.includes(request.algorithm));
signatureCallback = (signature) => { signatureCallback = (signature) => {
...@@ -151,7 +151,8 @@ function registerForLegacySignatureRequests() { ...@@ -151,7 +151,8 @@ function registerForLegacySignatureRequests() {
chrome.certificateProvider.onSignDigestRequested.addListener(function( chrome.certificateProvider.onSignDigestRequested.addListener(function(
request, callback) { request, callback) {
assertEq(l1LeafCert.buffer, request.certificate); assertEq(l1LeafCert.buffer, request.certificate);
// The sign request must refer to a hash that was declared to be supported. // The sign request must refer to the hash that was declared to be
// supported.
assertTrue(supportedLegacyHashes.includes(request.hash)); assertTrue(supportedLegacyHashes.includes(request.hash));
signatureCallback = callback; signatureCallback = callback;
signatureRequestAlgorithm = request.hash; signatureRequestAlgorithm = request.hash;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment