OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2013 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 <limits> |
| 6 #include <vector> |
| 7 |
| 8 #include "base/sha1.h" |
| 9 #include "crypto/cup.h" |
| 10 #include "crypto/random.h" |
| 11 #include "crypto/secure_util.h" |
| 12 #include "testing/gtest/include/gtest/gtest.h" |
| 13 |
| 14 namespace { |
| 15 |
| 16 // How to generate this key: |
| 17 // openssl genpkey -out cr.pem -outform PEM -algorithm RSA |
| 18 // -pkeyopt rsa_keygen_pubexp:3 |
| 19 // openssl rsa -in cr.pem -pubout -out cr_pub.pem |
| 20 |
| 21 const char kCupTestKey1024PEM[] = |
| 22 "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC7ct1JhLSol2DkBcJdNjR3KkEA" |
| 23 "ZfXpF22lDD2WZu5JAZ4NiZqnHsKGJNPUbCH4AhFsXmuW5wEHhUVNhsMP6F9mQ06D" |
| 24 "i+ygwZ8aXlklmW4S0Et+SNg3i73fnYn0KDQzrzJnMu46s/CFPhjr4f0TH9b7oHkU" |
| 25 "XbqNZtG6gwaN1bmzFwIBAw=="; |
| 26 |
| 27 const size_t kRsaKeySize = 128; |
| 28 |
| 29 } // end namespace |
| 30 |
| 31 #if defined(USE_OPENSSL) |
| 32 |
| 33 // Once CUP is implemented for OpenSSL, remove this #if block. |
| 34 TEST(CupUnitTest, OpenSSLStub) { |
| 35 crypto::ClientUpdateProtocol cup; |
| 36 EXPECT_FALSE(cup.Init(8, kCupTestKey1024PEM)); |
| 37 } |
| 38 |
| 39 #else |
| 40 |
| 41 namespace crypto { |
| 42 |
| 43 class CupUnitTest : public testing::Test { |
| 44 protected: |
| 45 virtual void SetUp() { |
| 46 // How to generate this key: |
| 47 // openssl genpkey -out cr.pem -outform PEM -algorithm RSA |
| 48 // -pkeyopt rsa_keygen_pubexp:3 |
| 49 // openssl rsa -in cr.pem -pubout -out cr_pub.pem |
| 50 |
| 51 EXPECT_TRUE(cup_.Init(8, kCupTestKey1024PEM)); |
| 52 } |
| 53 |
| 54 // Test RsaPad for a random message of num_bytes length. |
| 55 void TestRsaPad(size_t num_bytes) { |
| 56 std::vector<uint8> data(num_bytes); |
| 57 crypto::RandBytes(&data.front(), data.size()); |
| 58 |
| 59 std::vector<uint8> m = ClientUpdateProtocol::RsaPad(kRsaKeySize, data); |
| 60 ASSERT_EQ(m.size(), kRsaKeySize); |
| 61 |
| 62 std::vector<uint8> hash(base::kSHA1Length); |
| 63 base::SHA1HashBytes(&m.front(), |
| 64 static_cast<int>(m.size() - base::kSHA1Length), |
| 65 &hash.front()); |
| 66 |
| 67 EXPECT_TRUE(SecureMemEqual(&hash.front(), |
| 68 &m[kRsaKeySize - base::kSHA1Length], |
| 69 base::kSHA1Length)); |
| 70 |
| 71 EXPECT_FALSE(m[0] & 0x80); // msb is always reset. |
| 72 EXPECT_TRUE(m[0] & 0x60); // bit next to msb is always set. |
| 73 } |
| 74 |
| 75 void TestUrlSafeB64Encode(const char* expected_input, const char* input) { |
| 76 std::string expected(expected_input); |
| 77 EXPECT_EQ(expected, UrlSafeB64Encode(reinterpret_cast<const uint8*>(input), |
| 78 strlen(input))); |
| 79 } |
| 80 |
| 81 std::string UrlSafeB64Encode(const uint8* in_bytes, size_t length) { |
| 82 std::vector<uint8> in(in_bytes, in_bytes + length); |
| 83 return ClientUpdateProtocol::UrlSafeB64Encode(in); |
| 84 } |
| 85 |
| 86 std::vector<uint8> HashBin(const base::StringPiece& in_str) { |
| 87 return ClientUpdateProtocol::Hash(in_str); |
| 88 } |
| 89 |
| 90 std::string Hash(const std::vector<uint8>& in_bytes) { |
| 91 return BytesToHex(ClientUpdateProtocol::Hash(in_bytes)); |
| 92 } |
| 93 |
| 94 std::string Hash(const base::StringPiece& in_str) { |
| 95 return BytesToHex(ClientUpdateProtocol::Hash(in_str)); |
| 96 } |
| 97 |
| 98 std::string SymSign(const std::vector<uint8>& key, |
| 99 uint8 id, |
| 100 const std::vector<uint8>* h1, |
| 101 const std::vector<uint8>* h2, |
| 102 const std::vector<uint8>* h3) { |
| 103 return BytesToHex(ClientUpdateProtocol::SymSign(key, id, h1, h2, h3)); |
| 104 } |
| 105 |
| 106 std::vector<uint8> SymSignRaw(const std::vector<uint8>& key, |
| 107 uint8 id, |
| 108 const std::vector<uint8>* h1, |
| 109 const std::vector<uint8>* h2, |
| 110 const std::vector<uint8>* h3) { |
| 111 return ClientUpdateProtocol::SymSign(key, id, h1, h2, h3); |
| 112 } |
| 113 |
| 114 std::string BytesToHex(const std::vector<uint8>& bytes) { |
| 115 std::string result; |
| 116 if (bytes.size() < std::numeric_limits<size_t>::max() / 2) { |
| 117 static const char kHexChars[] = "0123456789abcdef"; |
| 118 for (size_t i = 0; i < bytes.size(); ++i) { |
| 119 result.push_back(kHexChars[(bytes[i] >> 4)]); |
| 120 result.push_back(kHexChars[(bytes[i] & 0xf)]); |
| 121 } |
| 122 } |
| 123 return result; |
| 124 } |
| 125 |
| 126 void OverrideRAndRebuildKeys() { |
| 127 static const char kFixedR[] = |
| 128 "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do " |
| 129 "eiusmod tempor incididunt ut labore et dolore magna aliqua. "; |
| 130 |
| 131 const std::string plaintext(kFixedR); |
| 132 cup_.r_.assign(plaintext.begin(), plaintext.end()); |
| 133 EXPECT_TRUE(cup_.BuildSharedKey()); |
| 134 } |
| 135 |
| 136 const std::string& ExamineVW() { |
| 137 return cup_.vw_; |
| 138 } |
| 139 |
| 140 ClientUpdateProtocol& GetCup() { |
| 141 return cup_; |
| 142 } |
| 143 |
| 144 |
| 145 private: |
| 146 ClientUpdateProtocol cup_; |
| 147 }; |
| 148 |
| 149 TEST_F(CupUnitTest, RsaPad) { |
| 150 ASSERT_GE(kRsaKeySize, base::kSHA1Length); |
| 151 |
| 152 TestRsaPad(1); // 1 byte. |
| 153 TestRsaPad(base::kSHA1Length); // 20 bytes. |
| 154 TestRsaPad(kRsaKeySize); // 128 bytes. |
| 155 TestRsaPad(1000); // 1000 bytes. |
| 156 } |
| 157 |
| 158 TEST_F(CupUnitTest, B64Encode) { |
| 159 TestUrlSafeB64Encode("MQ", "1"); |
| 160 TestUrlSafeB64Encode("MTI", "12"); |
| 161 TestUrlSafeB64Encode("MTIz", "123"); |
| 162 TestUrlSafeB64Encode("PHA-SGk_PC9wPg", "<p>Hi?</p>"); |
| 163 TestUrlSafeB64Encode("R29vZ2xlIEluYw", "Google Inc"); |
| 164 } |
| 165 |
| 166 TEST_F(CupUnitTest, Hash) { |
| 167 static const char kPlainText[] = |
| 168 "The quick brown fox jumps over the lazy dog"; |
| 169 |
| 170 // Empty vector. |
| 171 std::vector<uint8> data; |
| 172 EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709", |
| 173 Hash(data)); |
| 174 |
| 175 // Non-empty vector. |
| 176 const uint8* first = reinterpret_cast<const uint8*>(kPlainText); |
| 177 const uint8* last = first + strlen(kPlainText); |
| 178 data.clear(); |
| 179 data.insert(data.begin(), first, last); |
| 180 EXPECT_EQ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", |
| 181 Hash(data)); |
| 182 |
| 183 // Empty string. |
| 184 EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709", |
| 185 Hash(std::string())); |
| 186 |
| 187 // Non-empty string. |
| 188 EXPECT_EQ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12", |
| 189 Hash(std::string(kPlainText))); |
| 190 } |
| 191 |
| 192 TEST_F(CupUnitTest, SymSign) { |
| 193 static const char kPlainText[] = |
| 194 "The quick brown fox jumps over the lazy dog"; |
| 195 |
| 196 std::vector<uint8> key; |
| 197 key.push_back(7); |
| 198 |
| 199 std::vector<uint8> hash = HashBin(std::string(kPlainText)); |
| 200 |
| 201 // Verify that NULL buffers are not included in the hash. |
| 202 EXPECT_EQ("1c6fa359c0a7b11421ae4d31a92f9de8a4ef8d2d", |
| 203 SymSign(key, 1, &hash, NULL, NULL)); |
| 204 EXPECT_EQ("1c6fa359c0a7b11421ae4d31a92f9de8a4ef8d2d", |
| 205 SymSign(key, 1, NULL, &hash, NULL)); |
| 206 EXPECT_EQ("1c6fa359c0a7b11421ae4d31a92f9de8a4ef8d2d", |
| 207 SymSign(key, 1, NULL, &hash, NULL)); |
| 208 |
| 209 // Verify that all three buffers are included in the hash. |
| 210 EXPECT_EQ("fef3e343795946cd47ff4e07eca5f3f09b051d86", |
| 211 SymSign(key, 1, &hash, &hash, &hash)); |
| 212 } |
| 213 |
| 214 TEST_F(CupUnitTest, EncryptSharedKey) { |
| 215 // Given a fixed public key set in our test fixture, if we override (r) with |
| 216 // a known data, we should be able to test (w) against an expected output. |
| 217 // |
| 218 // This expected output can be generated using this command line, where |
| 219 // plaintext.bin is the contents of kFixedR[] in OverrideRAndRebuildKeys(): |
| 220 // |
| 221 // openssl rsautl -inkey cr2_pub.pem -pubin -encrypt -raw |
| 222 // -in plaintext.bin | base64 |
| 223 // |
| 224 // Remember to prepend the key version number, and fix up the Base64 |
| 225 // afterwards to be URL-safe. |
| 226 |
| 227 static const char kExpectedVW[] = |
| 228 "8:lMmNR3mVbOitbq8ceYGStFBwrJcpvY-sauFSbMVe6VONS9x42xTOLY_KdqsWCy" |
| 229 "KuiJBiQziQLOybPUyA9vk0N5kMnC90LIh2nP2FgFG0M0Z22qjB3drsdJPi7TQZbb" |
| 230 "Xhqm587M8vjc6VlM_eoC0qYwCPaXBqXjsyiHnXetcn5X0"; |
| 231 |
| 232 EXPECT_NE(kExpectedVW, ExamineVW()); |
| 233 OverrideRAndRebuildKeys(); |
| 234 EXPECT_EQ(kExpectedVW, ExamineVW()); |
| 235 } |
| 236 |
| 237 TEST_F(CupUnitTest, SignRequest) { |
| 238 static const char kUrlHost[] = "testserver.chromium.org"; |
| 239 static const char kUrlPath[] = "update"; |
| 240 static const char kUrlQuery[] = "?junk=present"; |
| 241 static const char kRequest[] = "testbody"; |
| 242 |
| 243 static const char kExpectedCP[] = "hIxzNIHv4pg03NcXPLuATX8G2SI"; |
| 244 |
| 245 OverrideRAndRebuildKeys(); |
| 246 |
| 247 std::string cup_query; |
| 248 std::string cp; |
| 249 |
| 250 // Check the case with a preexisting query string. |
| 251 EXPECT_TRUE(GetCup().SignRequest(kUrlHost, kUrlPath, kUrlQuery, kRequest, |
| 252 &cup_query, &cp)); |
| 253 |
| 254 std::string expected_query(kUrlQuery); |
| 255 expected_query.append("&w="); |
| 256 expected_query.append(ExamineVW()); |
| 257 EXPECT_EQ(expected_query, cup_query); |
| 258 EXPECT_EQ(kExpectedCP, cp); |
| 259 |
| 260 // Check the case with no query string. |
| 261 std::string cp2; |
| 262 EXPECT_TRUE(GetCup().SignRequest(kUrlHost, kUrlPath, std::string(), kRequest, |
| 263 &cup_query, &cp2)); |
| 264 expected_query = "?w="; |
| 265 expected_query.append(ExamineVW()); |
| 266 EXPECT_EQ(expected_query, cup_query); |
| 267 |
| 268 // Changes in the URL should result in changes in the client proof. |
| 269 EXPECT_NE(cp, cp2); |
| 270 } |
| 271 |
| 272 TEST_F(CupUnitTest, ValidateResponse) { |
| 273 static const char kUrlHost[] = "testserver.chromium.org"; |
| 274 static const char kUrlPath[] = "update"; |
| 275 static const char kUrlQuery[] = "?junk=present"; |
| 276 static const char kRequest[] = "testbody"; |
| 277 |
| 278 static const char kGoodResponse[] = "intact_response"; |
| 279 static const char kGoodC[] = "c=EncryptedDataFromTheUpdateServer"; |
| 280 static const char kGoodSP[] = "5rMFMPL9Hgqb-2J8kL3scsHeNgg"; |
| 281 |
| 282 static const char kBadResponse[] = "tampered_response"; |
| 283 static const char kBadC[] = "c=TotalJunkThatAnAttackerCouldSend"; |
| 284 static const char kBadSP[] = "Base64TamperedShaOneHash"; |
| 285 |
| 286 OverrideRAndRebuildKeys(); |
| 287 |
| 288 // We should return false if no calls to SignRequest() have been made. |
| 289 EXPECT_FALSE(GetCup().ValidateResponse(kGoodResponse, kGoodC, kGoodSP)); |
| 290 |
| 291 EXPECT_TRUE(GetCup().SignRequest(kUrlHost, kUrlPath, kUrlQuery, kRequest, |
| 292 NULL, NULL)); |
| 293 |
| 294 // Return true on a valid response and server proof. |
| 295 EXPECT_TRUE(GetCup().ValidateResponse(kGoodResponse, kGoodC, kGoodSP)); |
| 296 |
| 297 // Return false on anything invalid. |
| 298 EXPECT_FALSE(GetCup().ValidateResponse(kBadResponse, kGoodC, kGoodSP)); |
| 299 EXPECT_FALSE(GetCup().ValidateResponse(kGoodResponse, kBadC, kGoodSP)); |
| 300 EXPECT_FALSE(GetCup().ValidateResponse(kGoodResponse, kGoodC, kBadSP)); |
| 301 EXPECT_FALSE(GetCup().ValidateResponse(kGoodResponse, kBadC, kBadSP)); |
| 302 EXPECT_FALSE(GetCup().ValidateResponse(kBadResponse, kGoodC, kBadSP)); |
| 303 EXPECT_FALSE(GetCup().ValidateResponse(kBadResponse, kBadC, kGoodSP)); |
| 304 EXPECT_FALSE(GetCup().ValidateResponse(kBadResponse, kBadC, kBadSP)); |
| 305 } |
| 306 |
| 307 } // namespace crypto |
| 308 |
| 309 #endif // !defined(USE_OPENSSL) |
| 310 |
OLD | NEW |