1// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "net/base/keygen_handler.h" 6 7#include <string> 8 9#include "build/build_config.h" 10#include "base/base64.h" 11#include "base/logging.h" 12#include "base/task.h" 13#include "base/threading/worker_pool.h" 14#include "base/threading/thread_restrictions.h" 15#include "base/synchronization/waitable_event.h" 16#include "crypto/nss_util.h" 17#include "testing/gtest/include/gtest/gtest.h" 18 19#if defined(USE_NSS) 20#include <private/pprthred.h> // PR_DetachThread 21#endif 22 23namespace net { 24 25namespace { 26 27class KeygenHandlerTest : public ::testing::Test { 28 public: 29 KeygenHandlerTest() {} 30 virtual ~KeygenHandlerTest() {} 31 32 virtual void SetUp() { 33#if defined(OS_CHROMEOS) 34 crypto::OpenPersistentNSSDB(); 35#endif 36 } 37}; 38 39// Assert that |result| is a valid output for KeygenHandler given challenge 40// string of |challenge|. 41void AssertValidSignedPublicKeyAndChallenge(const std::string& result, 42 const std::string& challenge) { 43 ASSERT_GT(result.length(), 0U); 44 45 // Verify it's valid base64: 46 std::string spkac; 47 ASSERT_TRUE(base::Base64Decode(result, &spkac)); 48 // In lieu of actually parsing and validating the DER data, 49 // just check that it exists and has a reasonable length. 50 // (It's almost always 590 bytes, but the DER encoding of the random key 51 // and signature could sometimes be a few bytes different.) 52 ASSERT_GE(spkac.length(), 200U); 53 ASSERT_LE(spkac.length(), 300U); 54 55 // NOTE: 56 // The value of |result| can be validated by prefixing 'SPKAC=' to it 57 // and piping it through 58 // openssl spkac -verify 59 // whose output should look like: 60 // Netscape SPKI: 61 // Public Key Algorithm: rsaEncryption 62 // RSA Public Key: (2048 bit) 63 // Modulus (2048 bit): 64 // 00:b6:cc:14:c9:43:b5:2d:51:65:7e:11:8b:80:9e: ..... 65 // Exponent: 65537 (0x10001) 66 // Challenge String: some challenge 67 // Signature Algorithm: md5WithRSAEncryption 68 // 92:f3:cc:ff:0b:d3:d0:4a:3a:4c:ba:ff:d6:38:7f:a5:4b:b5: ..... 69 // Signature OK 70 // 71 // The value of |spkac| can be ASN.1-parsed with: 72 // openssl asn1parse -inform DER 73} 74 75TEST_F(KeygenHandlerTest, SmokeTest) { 76 KeygenHandler handler(768, "some challenge", GURL("http://www.example.com")); 77 handler.set_stores_key(false); // Don't leave the key-pair behind 78 std::string result = handler.GenKeyAndSignChallenge(); 79 VLOG(1) << "KeygenHandler produced: " << result; 80 AssertValidSignedPublicKeyAndChallenge(result, "some challenge"); 81} 82 83class ConcurrencyTestTask : public Task { 84 public: 85 ConcurrencyTestTask(base::WaitableEvent* event, 86 const std::string& challenge, std::string* result) 87 : event_(event), 88 challenge_(challenge), 89 result_(result) { 90 } 91 92 virtual void Run() { 93 // We allow Singleton use on the worker thread here since we use a 94 // WaitableEvent to synchronize, so it's safe. 95 base::ThreadRestrictions::ScopedAllowSingleton scoped_allow_singleton; 96 KeygenHandler handler(768, "some challenge", 97 GURL("http://www.example.com")); 98 handler.set_stores_key(false); // Don't leave the key-pair behind. 99 *result_ = handler.GenKeyAndSignChallenge(); 100 event_->Signal(); 101#if defined(USE_NSS) 102 // Detach the thread from NSPR. 103 // Calling NSS functions attaches the thread to NSPR, which stores 104 // the NSPR thread ID in thread-specific data. 105 // The threads in our thread pool terminate after we have called 106 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets 107 // segfaults on shutdown when the threads' thread-specific data 108 // destructors run. 109 PR_DetachThread(); 110#endif 111 } 112 113 private: 114 base::WaitableEvent* event_; 115 std::string challenge_; 116 std::string* result_; 117}; 118 119// We asynchronously generate the keys so as not to hang up the IO thread. This 120// test tries to catch concurrency problems in the keygen implementation. 121TEST_F(KeygenHandlerTest, ConcurrencyTest) { 122 const int NUM_HANDLERS = 5; 123 base::WaitableEvent* events[NUM_HANDLERS] = { NULL }; 124 std::string results[NUM_HANDLERS]; 125 for (int i = 0; i < NUM_HANDLERS; i++) { 126 events[i] = new base::WaitableEvent(false, false); 127 base::WorkerPool::PostTask( 128 FROM_HERE, 129 new ConcurrencyTestTask(events[i], "some challenge", &results[i]), 130 true); 131 } 132 133 for (int i = 0; i < NUM_HANDLERS; i++) { 134 // Make sure the job completed 135 bool signaled = events[i]->Wait(); 136 EXPECT_TRUE(signaled); 137 delete events[i]; 138 events[i] = NULL; 139 140 VLOG(1) << "KeygenHandler " << i << " produced: " << results[i]; 141 AssertValidSignedPublicKeyAndChallenge(results[i], "some challenge"); 142 } 143} 144 145} // namespace 146 147} // namespace net 148