keygen_handler_unittest.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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 "build/build_config.h" // Needs to be imported early for USE_NSS 8 9#if defined(USE_NSS) 10#include <private/pprthred.h> // PR_DetachThread 11#endif 12 13#include <string> 14 15#include "base/base64.h" 16#include "base/logging.h" 17#include "base/nss_util.h" 18#include "base/task.h" 19#include "base/waitable_event.h" 20#include "base/worker_pool.h" 21#include "testing/gtest/include/gtest/gtest.h" 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 base::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(), 580U); 53 ASSERT_LE(spkac.length(), 600U); 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, FLAKY_SmokeTest) { 76 KeygenHandler handler(2048, "some challenge"); 77 handler.set_stores_key(false); // Don't leave the key-pair behind 78 std::string result = handler.GenKeyAndSignChallenge(); 79 LOG(INFO) << "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 KeygenHandler handler(2048, "some challenge"); 94 handler.set_stores_key(false); // Don't leave the key-pair behind. 95 *result_ = handler.GenKeyAndSignChallenge(); 96 event_->Signal(); 97#if defined(USE_NSS) 98 // Detach the thread from NSPR. 99 // Calling NSS functions attaches the thread to NSPR, which stores 100 // the NSPR thread ID in thread-specific data. 101 // The threads in our thread pool terminate after we have called 102 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets 103 // segfaults on shutdown when the threads' thread-specific data 104 // destructors run. 105 PR_DetachThread(); 106#endif 107 } 108 109 private: 110 base::WaitableEvent* event_; 111 std::string challenge_; 112 std::string* result_; 113}; 114 115// We asynchronously generate the keys so as not to hang up the IO thread. This 116// test tries to catch concurrency problems in the keygen implementation. 117TEST_F(KeygenHandlerTest, ConcurrencyTest) { 118 const int NUM_HANDLERS = 5; 119 base::WaitableEvent* events[NUM_HANDLERS] = { NULL }; 120 std::string results[NUM_HANDLERS]; 121 for (int i = 0; i < NUM_HANDLERS; i++) { 122 events[i] = new base::WaitableEvent(false, false); 123 WorkerPool::PostTask(FROM_HERE, 124 new ConcurrencyTestTask(events[i], "some challenge", 125 &results[i]), 126 true); 127 } 128 129 for (int i = 0; i < NUM_HANDLERS; i++) { 130 // Make sure the job completed 131 bool signaled = events[i]->Wait(); 132 EXPECT_TRUE(signaled); 133 delete events[i]; 134 events[i] = NULL; 135 136 LOG(INFO) << "KeygenHandler " << i << " produced: " << results[i]; 137 AssertValidSignedPublicKeyAndChallenge(results[i], "some challenge"); 138 } 139} 140 141} // namespace 142 143} // namespace net 144