Index: net/base/keygen_handler_unittest.cc |
diff --git a/net/base/keygen_handler_unittest.cc b/net/base/keygen_handler_unittest.cc |
index c1b5fe44e0064b97989fd441052af9a7cefd3d15..b045ababacd60548b4dc775d7cdc1fc7d7299c4e 100644 |
--- a/net/base/keygen_handler_unittest.cc |
+++ b/net/base/keygen_handler_unittest.cc |
@@ -4,6 +4,10 @@ |
#include "net/base/keygen_handler.h" |
+#include <openssl/bytestring.h> |
+#include <openssl/evp.h> |
+#include <stdint.h> |
+ |
#include <string> |
#include <utility> |
@@ -11,10 +15,12 @@ |
#include "base/bind.h" |
#include "base/location.h" |
#include "base/logging.h" |
+#include "base/strings/string_piece.h" |
#include "base/synchronization/waitable_event.h" |
#include "base/threading/thread_restrictions.h" |
#include "base/threading/worker_pool.h" |
#include "build/build_config.h" |
+#include "crypto/scoped_openssl_types.h" |
#include "testing/gtest/include/gtest/gtest.h" |
#if defined(USE_NSS_CERTS) |
@@ -48,14 +54,16 @@ class StubCryptoModuleDelegate : public crypto::NSSCryptoModuleDelegate { |
}; |
#endif |
+const char kChallenge[] = "some challenge"; |
+ |
class KeygenHandlerTest : public ::testing::Test { |
public: |
KeygenHandlerTest() {} |
~KeygenHandlerTest() override {} |
scoped_ptr<KeygenHandler> CreateKeygenHandler() { |
- scoped_ptr<KeygenHandler> handler(new KeygenHandler( |
- 768, "some challenge", GURL("http://www.example.com"))); |
+ scoped_ptr<KeygenHandler> handler( |
+ new KeygenHandler(768, kChallenge, GURL("http://www.example.com"))); |
#if defined(USE_NSS_CERTS) |
handler->set_crypto_module_delegate( |
scoped_ptr<crypto::NSSCryptoModuleDelegate>( |
@@ -71,40 +79,89 @@ class KeygenHandlerTest : public ::testing::Test { |
#endif |
}; |
+base::StringPiece StringPieceFromCBS(const CBS& cbs) { |
+ return base::StringPiece(reinterpret_cast<const char*>(CBS_data(&cbs)), |
+ CBS_len(&cbs)); |
+} |
+ |
// Assert that |result| is a valid output for KeygenHandler given challenge |
// string of |challenge|. |
void AssertValidSignedPublicKeyAndChallenge(const std::string& result, |
const std::string& challenge) { |
- ASSERT_GT(result.length(), 0U); |
- |
// Verify it's valid base64: |
std::string spkac; |
ASSERT_TRUE(base::Base64Decode(result, &spkac)); |
- // In lieu of actually parsing and validating the DER data, |
- // just check that it exists and has a reasonable length. |
- // (It's almost always 590 bytes, but the DER encoding of the random key |
- // and signature could sometimes be a few bytes different.) |
- ASSERT_GE(spkac.length(), 200U); |
- ASSERT_LE(spkac.length(), 300U); |
- |
- // NOTE: |
- // The value of |result| can be validated by prefixing 'SPKAC=' to it |
- // and piping it through |
- // openssl spkac -verify |
- // whose output should look like: |
- // Netscape SPKI: |
- // Public Key Algorithm: rsaEncryption |
- // RSA Public Key: (2048 bit) |
- // Modulus (2048 bit): |
- // 00:b6:cc:14:c9:43:b5:2d:51:65:7e:11:8b:80:9e: ..... |
- // Exponent: 65537 (0x10001) |
- // Challenge String: some challenge |
- // Signature Algorithm: md5WithRSAEncryption |
- // 92:f3:cc:ff:0b:d3:d0:4a:3a:4c:ba:ff:d6:38:7f:a5:4b:b5: ..... |
- // Signature OK |
+ |
+ // Parse the following structure: |
// |
- // The value of |spkac| can be ASN.1-parsed with: |
- // openssl asn1parse -inform DER |
+ // PublicKeyAndChallenge ::= SEQUENCE { |
+ // spki SubjectPublicKeyInfo, |
+ // challenge IA5STRING |
+ // } |
+ // SignedPublicKeyAndChallenge ::= SEQUENCE { |
+ // publicKeyAndChallenge PublicKeyAndChallenge, |
+ // signatureAlgorithm AlgorithmIdentifier, |
+ // signature BIT STRING |
+ // } |
+ |
+ CBS cbs; |
+ CBS_init(&cbs, reinterpret_cast<const uint8_t*>(spkac.data()), spkac.size()); |
+ |
+ // The input should consist of a SEQUENCE. |
+ CBS child; |
+ ASSERT_TRUE(CBS_get_asn1(&cbs, &child, CBS_ASN1_SEQUENCE)); |
+ ASSERT_EQ(0u, CBS_len(&cbs)); |
+ |
+ // Extract the raw PublicKeyAndChallenge. |
+ CBS public_key_and_challenge_raw; |
+ ASSERT_TRUE(CBS_get_asn1_element(&child, &public_key_and_challenge_raw, |
+ CBS_ASN1_SEQUENCE)); |
+ |
+ // Parse out the PublicKeyAndChallenge. |
+ CBS copy = public_key_and_challenge_raw; |
+ CBS public_key_and_challenge; |
+ ASSERT_TRUE( |
+ CBS_get_asn1(©, &public_key_and_challenge, CBS_ASN1_SEQUENCE)); |
+ ASSERT_EQ(0u, CBS_len(©)); |
+ crypto::ScopedEVP_PKEY key(EVP_parse_public_key(&public_key_and_challenge)); |
+ ASSERT_TRUE(key); |
+ CBS challenge_spkac; |
+ ASSERT_TRUE(CBS_get_asn1(&public_key_and_challenge, &challenge_spkac, |
+ CBS_ASN1_IA5STRING)); |
+ ASSERT_EQ(0u, CBS_len(&public_key_and_challenge)); |
+ |
+ // The challenge must match. |
+ ASSERT_EQ(challenge, StringPieceFromCBS(challenge_spkac)); |
+ |
+ // The next element must be the AlgorithmIdentifier for MD5 with RSA. |
+ static const uint8_t kMd5WithRsaEncryption[] = { |
+ 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, |
+ 0xf7, 0x0d, 0x01, 0x01, 0x04, 0x05, 0x00, |
+ }; |
+ CBS algorithm; |
+ ASSERT_TRUE(CBS_get_bytes(&child, &algorithm, sizeof(kMd5WithRsaEncryption))); |
+ ASSERT_EQ( |
+ base::StringPiece(reinterpret_cast<const char*>(kMd5WithRsaEncryption), |
+ sizeof(kMd5WithRsaEncryption)), |
+ StringPieceFromCBS(algorithm)); |
+ |
+ // Finally, parse the signature. |
+ CBS signature; |
+ ASSERT_TRUE(CBS_get_asn1(&child, &signature, CBS_ASN1_BITSTRING)); |
+ ASSERT_EQ(0u, CBS_len(&child)); |
+ uint8_t pad; |
+ ASSERT_TRUE(CBS_get_u8(&signature, &pad)); |
+ ASSERT_EQ(0u, pad); |
+ |
+ // Check the signature. |
+ crypto::ScopedEVP_MD_CTX ctx(EVP_MD_CTX_create()); |
+ ASSERT_TRUE( |
+ EVP_DigestVerifyInit(ctx.get(), nullptr, EVP_md5(), nullptr, key.get())); |
+ ASSERT_TRUE(EVP_DigestVerifyUpdate(ctx.get(), |
+ CBS_data(&public_key_and_challenge_raw), |
+ CBS_len(&public_key_and_challenge_raw))); |
+ ASSERT_TRUE(EVP_DigestVerifyFinal(ctx.get(), CBS_data(&signature), |
+ CBS_len(&signature))); |
} |
TEST_F(KeygenHandlerTest, SmokeTest) { |
@@ -112,7 +169,7 @@ TEST_F(KeygenHandlerTest, SmokeTest) { |
handler->set_stores_key(false); // Don't leave the key-pair behind |
std::string result = handler->GenKeyAndSignChallenge(); |
VLOG(1) << "KeygenHandler produced: " << result; |
- AssertValidSignedPublicKeyAndChallenge(result, "some challenge"); |
+ AssertValidSignedPublicKeyAndChallenge(result, kChallenge); |
} |
void ConcurrencyTestCallback(const std::string& challenge, |