Commit d5587416 authored by davidben's avatar davidben Committed by Commit bot

Get net_unittests working on Windows BoringSSL port.

BUG=338884

Review URL: https://codereview.chromium.org/687833002

Cr-Commit-Position: refs/heads/master@{#302161}
parent 044d85df
......@@ -249,6 +249,11 @@ component("net") {
"cert/cert_verify_proc_nss.h",
]
}
if (is_win) {
sources -= [
"cert/cert/sha256_legacy_support_nss_win.cc",
]
}
} else {
sources -= [
"base/crypto_module_openssl.cc",
......@@ -284,6 +289,7 @@ component("net") {
}
if (is_win) {
sources -= [
"cert/cert/sha256_legacy_support_openssl_win.cc",
"ssl/openssl_platform_key_win.cc",
]
}
......
// 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 <cert.h>
#include <keyhi.h>
#include <secoid.h>
#include "base/logging.h"
#include "crypto/scoped_nss_types.h"
#include "net/cert/sha256_legacy_support_win.h"
namespace net {
namespace sha256_interception {
BOOL CryptVerifyCertificateSignatureExHook(
CryptVerifyCertificateSignatureExFunc original_func,
HCRYPTPROV_LEGACY provider,
DWORD encoding_type,
DWORD subject_type,
void* subject_data,
DWORD issuer_type,
void* issuer_data,
DWORD flags,
void* extra) {
CHECK(original_func);
// Only intercept if the arguments are supported.
if (provider != NULL || (encoding_type != X509_ASN_ENCODING) ||
!IsSupportedSubjectType(subject_type) || subject_data == NULL ||
!IsSupportedIssuerType(issuer_type) || issuer_data == NULL) {
return original_func(provider, encoding_type, subject_type, subject_data,
issuer_type, issuer_data, flags, extra);
}
base::StringPiece subject_signature =
GetSubjectSignature(subject_type, subject_data);
bool should_intercept = false;
crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
CERTSignedData signed_data;
memset(&signed_data, 0, sizeof(signed_data));
// Attempt to decode the subject using the generic "Signed Data" template,
// which all of the supported subject types match. If the signature
// algorithm is RSA with one of the SHA-2 algorithms supported by NSS
// (excluding SHA-224, which is pointless), then defer to the NSS
// implementation. Otherwise, fall back and let the OS handle it (e.g.
// in case there are any algorithm policies in effect).
if (!subject_signature.empty()) {
SECItem subject_sig_item;
subject_sig_item.data = const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(subject_signature.data()));
subject_sig_item.len = subject_signature.size();
SECStatus rv = SEC_QuickDERDecodeItem(
arena.get(), &signed_data, SEC_ASN1_GET(CERT_SignedDataTemplate),
&subject_sig_item);
if (rv == SECSuccess) {
SECOidTag signature_alg =
SECOID_GetAlgorithmTag(&signed_data.signatureAlgorithm);
if (signature_alg == SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION ||
signature_alg == SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION ||
signature_alg == SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION) {
should_intercept = true;
}
}
}
if (!should_intercept) {
return original_func(provider, encoding_type, subject_type, subject_data,
issuer_type, issuer_data, flags, extra);
}
// Rather than attempting to synthesize a CERTSubjectPublicKeyInfo by hand,
// just force the OS to do an ASN.1 encoding and then decode it back into
// NSS. This is silly for performance, but safest for consistency.
PCERT_PUBLIC_KEY_INFO issuer_public_key =
GetIssuerPublicKey(issuer_type, issuer_data);
if (!issuer_public_key) {
SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
return FALSE;
}
unsigned char* issuer_spki_data = NULL;
DWORD issuer_spki_len = 0;
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
issuer_public_key, CRYPT_ENCODE_ALLOC_FLAG, NULL,
&issuer_spki_data, &issuer_spki_len)) {
return FALSE;
}
SECItem nss_issuer_spki;
nss_issuer_spki.data = issuer_spki_data;
nss_issuer_spki.len = issuer_spki_len;
CERTSubjectPublicKeyInfo* spki =
SECKEY_DecodeDERSubjectPublicKeyInfo(&nss_issuer_spki);
::LocalFree(issuer_spki_data);
if (!spki) {
SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
return FALSE;
}
// Attempt to actually verify the signed data. If it fails, synthesize the
// failure as a generic "bad signature" and let CryptoAPI handle the rest.
SECStatus rv = CERT_VerifySignedDataWithPublicKeyInfo(
&signed_data, spki, NULL);
SECKEY_DestroySubjectPublicKeyInfo(spki);
if (rv != SECSuccess) {
SetLastError(static_cast<DWORD>(NTE_BAD_SIGNATURE));
return FALSE;
}
return TRUE;
}
} // namespace sha256_interception
} // namespace net
\ No newline at end of file
// 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 <openssl/asn1.h>
#include <openssl/bytestring.h>
#include <openssl/evp.h>
#include <openssl/obj.h>
#include <openssl/x509.h>
#include "base/logging.h"
#include "crypto/scoped_openssl_types.h"
#include "net/cert/sha256_legacy_support_win.h"
namespace net {
namespace sha256_interception {
namespace {
typedef crypto::ScopedOpenSSL<X509_ALGOR, X509_ALGOR_free>::Type
ScopedX509_ALGOR;
// Parses |subject_signature| and writes the components into |*out_tbs_data|,
// |*out_algor|, and |*out_signature|. The BIT STRING in the signature must be
// a multiple of 8 bits. |*out_signature| will have the padding byte removed.
// It returns true on success and false on failure.
bool ParseSubjectSignature(const base::StringPiece& subject_signature,
CBS* out_tbs_data,
ScopedX509_ALGOR* out_algor,
CBS* out_signature) {
CBS cbs, sequence, tbs_data, algorithm, signature;
CBS_init(&cbs, reinterpret_cast<const uint8_t*>(subject_signature.data()),
subject_signature.size());
if (!CBS_get_asn1(&cbs, &sequence, CBS_ASN1_SEQUENCE) ||
CBS_len(&cbs) != 0 ||
!CBS_get_any_asn1_element(&sequence, &tbs_data, nullptr, nullptr) ||
!CBS_get_asn1_element(&sequence, &algorithm,
CBS_ASN1_SEQUENCE) ||
!CBS_get_asn1(&sequence, &signature, CBS_ASN1_BITSTRING) ||
CBS_len(&sequence) != 0) {
return false;
}
// Decode the algorithm.
const uint8_t* ptr = CBS_data(&algorithm);
ScopedX509_ALGOR algor(d2i_X509_ALGOR(NULL, &ptr, CBS_len(&algorithm)));
if (!algor || ptr != CBS_data(&algorithm) + CBS_len(&algorithm))
return false;
// An ASN.1 BIT STRING is encoded with a leading byte denoting the number of
// padding bits. All supported signature algorithms output octets, so the
// leading byte must be zero.
uint8_t padding;
if (!CBS_get_u8(&signature, &padding) || padding != 0)
return false;
*out_tbs_data = tbs_data;
*out_algor = algor.Pass();
*out_signature = signature;
return true;
}
} // namespace
BOOL CryptVerifyCertificateSignatureExHook(
CryptVerifyCertificateSignatureExFunc original_func,
HCRYPTPROV_LEGACY provider,
DWORD encoding_type,
DWORD subject_type,
void* subject_data,
DWORD issuer_type,
void* issuer_data,
DWORD flags,
void* extra) {
CHECK(original_func);
// Only intercept if the arguments are supported.
if (provider != NULL || (encoding_type != X509_ASN_ENCODING) ||
!IsSupportedSubjectType(subject_type) || subject_data == NULL ||
!IsSupportedIssuerType(issuer_type) || issuer_data == NULL) {
return original_func(provider, encoding_type, subject_type, subject_data,
issuer_type, issuer_data, flags, extra);
}
base::StringPiece subject_signature =
GetSubjectSignature(subject_type, subject_data);
bool should_intercept = false;
// Parse out the data, AlgorithmIdentifier, and signature.
CBS tbs_data, signature;
ScopedX509_ALGOR algor;
if (ParseSubjectSignature(subject_signature, &tbs_data, &algor,
&signature)) {
// If the signature algorithm is RSA with one of the SHA-2 algorithms
// supported by BoringSSL (excluding SHA-224, which is pointless), then
// defer to the BoringSSL implementation. Otherwise, fall back and let the
// OS handle it (e.g. in case there are any algorithm policies in effect).
int nid = OBJ_obj2nid(algor->algorithm);
if (nid == NID_sha256WithRSAEncryption ||
nid == NID_sha384WithRSAEncryption ||
nid == NID_sha512WithRSAEncryption) {
should_intercept = true;
}
}
if (!should_intercept) {
return original_func(provider, encoding_type, subject_type, subject_data,
issuer_type, issuer_data, flags, extra);
}
// Rather than attempting to synthesize an EVP_PKEY by hand, just force the
// OS to do an ASN.1 encoding and then decode it back into BoringSSL. This
// is silly for performance, but safest for consistency.
PCERT_PUBLIC_KEY_INFO issuer_public_key =
GetIssuerPublicKey(issuer_type, issuer_data);
if (!issuer_public_key) {
SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
return FALSE;
}
uint8_t* issuer_spki_data = NULL;
DWORD issuer_spki_len = 0;
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
issuer_public_key, CRYPT_ENCODE_ALLOC_FLAG, NULL,
&issuer_spki_data, &issuer_spki_len)) {
return FALSE;
}
const uint8_t* ptr = issuer_spki_data;
crypto::ScopedEVP_PKEY pubkey(d2i_PUBKEY(NULL, &ptr, issuer_spki_len));
if (!pubkey.get() || ptr != issuer_spki_data + issuer_spki_len) {
::LocalFree(issuer_spki_data);
SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
return FALSE;
}
::LocalFree(issuer_spki_data);
crypto::ScopedEVP_MD_CTX md_ctx(EVP_MD_CTX_create());
if (!EVP_DigestVerifyInitFromAlgorithm(md_ctx.get(), algor.get(),
pubkey.get()) ||
!EVP_DigestVerifyUpdate(md_ctx.get(), CBS_data(&tbs_data),
CBS_len(&tbs_data)) ||
!EVP_DigestVerifyFinal(md_ctx.get(), CBS_data(&signature),
CBS_len(&signature))) {
SetLastError(static_cast<DWORD>(NTE_BAD_SIGNATURE));
return FALSE;
}
return TRUE;
}
} // namespace sha256_interception
} // namespace net
\ No newline at end of file
......@@ -4,22 +4,10 @@
#include "net/cert/sha256_legacy_support_win.h"
#include <cert.h>
#include <keyhi.h>
#include <secoid.h>
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "base/strings/string_piece.h"
#include "base/win/windows_version.h"
#include "crypto/scoped_nss_types.h"
namespace net {
namespace sha256_interception {
namespace {
bool IsSupportedSubjectType(DWORD subject_type) {
switch (subject_type) {
case CRYPT_VERIFY_CERT_SIGN_SUBJECT_BLOB:
......@@ -86,107 +74,6 @@ PCERT_PUBLIC_KEY_INFO GetIssuerPublicKey(DWORD issuer_type,
return NULL;
}
} // namespace
BOOL CryptVerifyCertificateSignatureExHook(
CryptVerifyCertificateSignatureExFunc original_func,
HCRYPTPROV_LEGACY provider,
DWORD encoding_type,
DWORD subject_type,
void* subject_data,
DWORD issuer_type,
void* issuer_data,
DWORD flags,
void* extra) {
CHECK(original_func);
// Only intercept if the arguments are supported.
if (provider != NULL || (encoding_type != X509_ASN_ENCODING) ||
!IsSupportedSubjectType(subject_type) || subject_data == NULL ||
!IsSupportedIssuerType(issuer_type) || issuer_data == NULL) {
return original_func(provider, encoding_type, subject_type, subject_data,
issuer_type, issuer_data, flags, extra);
}
base::StringPiece subject_signature =
GetSubjectSignature(subject_type, subject_data);
bool should_intercept = false;
crypto::ScopedPLArenaPool arena(PORT_NewArena(DER_DEFAULT_CHUNKSIZE));
CERTSignedData signed_data;
memset(&signed_data, 0, sizeof(signed_data));
// Attempt to decode the subject using the generic "Signed Data" template,
// which all of the supported subject types match. If the signature
// algorithm is RSA with one of the SHA-2 algorithms supported by NSS
// (excluding SHA-224, which is pointless), then defer to the NSS
// implementation. Otherwise, fall back and let the OS handle it (e.g.
// in case there are any algorithm policies in effect).
if (!subject_signature.empty()) {
SECItem subject_sig_item;
subject_sig_item.data = const_cast<unsigned char*>(
reinterpret_cast<const unsigned char*>(subject_signature.data()));
subject_sig_item.len = subject_signature.size();
SECStatus rv = SEC_QuickDERDecodeItem(
arena.get(), &signed_data, SEC_ASN1_GET(CERT_SignedDataTemplate),
&subject_sig_item);
if (rv == SECSuccess) {
SECOidTag signature_alg =
SECOID_GetAlgorithmTag(&signed_data.signatureAlgorithm);
if (signature_alg == SEC_OID_PKCS1_SHA256_WITH_RSA_ENCRYPTION ||
signature_alg == SEC_OID_PKCS1_SHA384_WITH_RSA_ENCRYPTION ||
signature_alg == SEC_OID_PKCS1_SHA512_WITH_RSA_ENCRYPTION) {
should_intercept = true;
}
}
}
if (!should_intercept) {
return original_func(provider, encoding_type, subject_type, subject_data,
issuer_type, issuer_data, flags, extra);
}
// Rather than attempting to synthesize a CERTSubjectPublicKeyInfo by hand,
// just force the OS to do an ASN.1 encoding and then decode it back into
// NSS. This is silly for performance, but safest for consistency.
PCERT_PUBLIC_KEY_INFO issuer_public_key =
GetIssuerPublicKey(issuer_type, issuer_data);
if (!issuer_public_key) {
SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
return FALSE;
}
unsigned char* issuer_spki_data = NULL;
DWORD issuer_spki_len = 0;
if (!CryptEncodeObjectEx(X509_ASN_ENCODING, X509_PUBLIC_KEY_INFO,
issuer_public_key, CRYPT_ENCODE_ALLOC_FLAG, NULL,
&issuer_spki_data, &issuer_spki_len)) {
return FALSE;
}
SECItem nss_issuer_spki;
nss_issuer_spki.data = issuer_spki_data;
nss_issuer_spki.len = issuer_spki_len;
CERTSubjectPublicKeyInfo* spki =
SECKEY_DecodeDERSubjectPublicKeyInfo(&nss_issuer_spki);
::LocalFree(issuer_spki_data);
if (!spki) {
SetLastError(static_cast<DWORD>(NTE_BAD_ALGID));
return FALSE;
}
// Attempt to actually verify the signed data. If it fails, synthesize the
// failure as a generic "bad signature" and let CryptoAPI handle the rest.
SECStatus rv = CERT_VerifySignedDataWithPublicKeyInfo(
&signed_data, spki, NULL);
SECKEY_DestroySubjectPublicKeyInfo(spki);
if (rv != SECSuccess) {
SetLastError(static_cast<DWORD>(NTE_BAD_SIGNATURE));
return FALSE;
}
return TRUE;
}
} // namespace sha256_interception
} // namespace net
\ No newline at end of file
......@@ -7,6 +7,7 @@
#include <windows.h>
#include "base/strings/string_piece.h"
#include "crypto/wincrypt_shim.h"
#include "net/base/net_export.h"
......@@ -41,6 +42,21 @@ NET_EXPORT BOOL CryptVerifyCertificateSignatureExHook(
DWORD flags,
void* extra);
// Returns true if |subject_type| a supported subject type for interception.
bool IsSupportedSubjectType(DWORD subject_type);
// Returns true if |issuer_type| is a supported issuer type for interception.
bool IsSupportedIssuerType(DWORD issuer_type);
// Returns the encoded form of |subject_data| or an empty StringPiece if not
// supported.
base::StringPiece GetSubjectSignature(DWORD subject_type,
void* subject_data);
// Returns the public key of |issuer_data| or NULL if not supported.
PCERT_PUBLIC_KEY_INFO GetIssuerPublicKey(DWORD issuer_type,
void* issuer_data);
} // namespace sha256_interception
} // namespace net
......
......@@ -4,8 +4,6 @@
#include "net/cert/x509_certificate.h"
#include <blapi.h> // Implement CalculateChainFingerprint() with NSS.
#include "base/logging.h"
#include "base/memory/scoped_ptr.h"
#include "base/pickle.h"
......@@ -17,6 +15,13 @@
#include "crypto/sha2.h"
#include "net/base/net_errors.h"
// Implement CalculateChainFingerprint() with our native crypto library.
#if defined(USE_OPENSSL)
#include <openssl/sha.h>
#else
#include <blapi.h>
#endif
#pragma comment(lib, "crypt32.lib")
using base::Time;
......@@ -334,15 +339,22 @@ SHA256HashValue X509Certificate::CalculateFingerprint256(OSCertHandle cert) {
return sha256;
}
// TODO(wtc): This function is implemented with NSS low-level hash
// functions to ensure it is fast. Reimplement this function with
// CryptoAPI. May need to cache the HCRYPTPROV to reduce the overhead.
// static
SHA1HashValue X509Certificate::CalculateCAFingerprint(
const OSCertHandles& intermediates) {
SHA1HashValue sha1;
memset(sha1.data, 0, sizeof(sha1.data));
#if defined(USE_OPENSSL)
SHA_CTX ctx;
if (!SHA1_Init(&ctx))
return sha1;
for (size_t i = 0; i < intermediates.size(); ++i) {
PCCERT_CONTEXT ca_cert = intermediates[i];
if (!SHA1_Update(&ctx, ca_cert->pbCertEncoded, ca_cert->cbCertEncoded))
return sha1;
}
SHA1_Final(sha1.data, &ctx);
#else // !USE_OPENSSL
SHA1Context* sha1_ctx = SHA1_NewContext();
if (!sha1_ctx)
return sha1;
......@@ -354,6 +366,7 @@ SHA1HashValue X509Certificate::CalculateCAFingerprint(
unsigned int result_len;
SHA1_End(sha1_ctx, sha1.data, &result_len, SHA1_LENGTH);
SHA1_DestroyContext(sha1_ctx, PR_TRUE);
#endif // USE_OPENSSL
return sha1;
}
......
......@@ -220,6 +220,7 @@
'cert/nss_cert_database_chromeos.h',
'cert/nss_profile_filter_chromeos.cc',
'cert/nss_profile_filter_chromeos.h',
'cert/sha256_legacy_support_nss_win.cc',
'cert/scoped_nss_types.h',
'cert/test_root_certs_nss.cc',
'cert/x509_certificate_nss.cc',
......@@ -258,6 +259,7 @@
'cert/ct_log_verifier_openssl.cc',
'cert/ct_objects_extractor_openssl.cc',
'cert/jwk_serializer_openssl.cc',
'cert/sha256_legacy_support_openssl_win.cc',
'cert/x509_util_openssl.cc',
'cert/x509_util_openssl.h',
'crypto/scoped_openssl_types.h',
......
......@@ -77,6 +77,8 @@
'cert/ct_verify_result.h',
'cert/pem_tokenizer.cc',
'cert/pem_tokenizer.h',
'cert/sha256_legacy_support_nss_win.cc',
'cert/sha256_legacy_support_openssl_win.cc',
'cert/sha256_legacy_support_win.cc',
'cert/sha256_legacy_support_win.h',
'cert/signed_certificate_timestamp.cc',
......
......@@ -250,7 +250,7 @@ class SSLSessionCacheOpenSSLImpl {
void* session_is_good =
SSL_SESSION_get_ex_data(session, GetSSLSessionExIndex());
return session_is_good;
return session_is_good != NULL;
}
void MarkSSLSessionAsGood(SSL* ssl) {
......
......@@ -20,12 +20,12 @@
// |s| is the target SSL connection handle.
// |session| is non-0 to ask for the creation of a new session. If 0,
// this will set an empty session with no ID instead.
extern "C" int ssl_get_new_session(SSL* s, int session);
extern "C" OPENSSL_EXPORT int ssl_get_new_session(SSL* s, int session);
// This is an internal OpenSSL function which is used internally to add
// a new session to the cache. It is normally triggered by a succesful
// connection. However, this unit test does not use the network at all.
extern "C" void ssl_update_cache(SSL* s, int mode);
extern "C" OPENSSL_EXPORT void ssl_update_cache(SSL* s, int mode);
namespace net {
......
......@@ -43,8 +43,8 @@ class NET_EXPORT OpenSSLClientKeyStore {
// Returns false if an error occured.
// This function does not take ownership of the private_key, but may
// increment its internal reference count.
NET_EXPORT bool RecordClientCertPrivateKey(const X509Certificate* cert,
EVP_PKEY* private_key);
bool RecordClientCertPrivateKey(const X509Certificate* cert,
EVP_PKEY* private_key);
// Given a certificate's |public_key|, return the corresponding private
// key that has been recorded previously by RecordClientCertPrivateKey().
......
......@@ -52,7 +52,7 @@ int main(int argc, char** argv) {
NetTestSuite test_suite(argc, argv);
ClientSocketPoolBaseHelper::set_connect_backup_jobs_enabled(false);
#if defined(OS_WIN)
#if defined(OS_WIN) && !defined(USE_OPENSSL)
// We want to be sure to init NSPR on the main thread.
crypto::EnsureNSPRInit();
#endif
......
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