| Index: crypto/cup_unittest.cc
|
| ===================================================================
|
| --- crypto/cup_unittest.cc (revision 0)
|
| +++ crypto/cup_unittest.cc (revision 0)
|
| @@ -0,0 +1,310 @@
|
| +// Copyright (c) 2013 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 <limits>
|
| +#include <vector>
|
| +
|
| +#include "base/sha1.h"
|
| +#include "crypto/cup.h"
|
| +#include "crypto/random.h"
|
| +#include "crypto/secure_util.h"
|
| +#include "testing/gtest/include/gtest/gtest.h"
|
| +
|
| +namespace {
|
| +
|
| +// How to generate this key:
|
| +// openssl genpkey -out cr.pem -outform PEM -algorithm RSA
|
| +// -pkeyopt rsa_keygen_pubexp:3
|
| +// openssl rsa -in cr.pem -pubout -out cr_pub.pem
|
| +
|
| +const char kCupTestKey1024PEM[] =
|
| + "MIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC7ct1JhLSol2DkBcJdNjR3KkEA"
|
| + "ZfXpF22lDD2WZu5JAZ4NiZqnHsKGJNPUbCH4AhFsXmuW5wEHhUVNhsMP6F9mQ06D"
|
| + "i+ygwZ8aXlklmW4S0Et+SNg3i73fnYn0KDQzrzJnMu46s/CFPhjr4f0TH9b7oHkU"
|
| + "XbqNZtG6gwaN1bmzFwIBAw==";
|
| +
|
| +const size_t kRsaKeySize = 128;
|
| +
|
| +} // end namespace
|
| +
|
| +#if defined(USE_OPENSSL)
|
| +
|
| +// Once CUP is implemented for OpenSSL, remove this #if block.
|
| +TEST(CupUnitTest, OpenSSLStub) {
|
| + crypto::ClientUpdateProtocol cup;
|
| + EXPECT_FALSE(cup.Init(8, kCupTestKey1024PEM));
|
| +}
|
| +
|
| +#else
|
| +
|
| +namespace crypto {
|
| +
|
| +class CupUnitTest : public testing::Test {
|
| + protected:
|
| + virtual void SetUp() {
|
| + // How to generate this key:
|
| + // openssl genpkey -out cr.pem -outform PEM -algorithm RSA
|
| + // -pkeyopt rsa_keygen_pubexp:3
|
| + // openssl rsa -in cr.pem -pubout -out cr_pub.pem
|
| +
|
| + EXPECT_TRUE(cup_.Init(8, kCupTestKey1024PEM));
|
| + }
|
| +
|
| + // Test RsaPad for a random message of num_bytes length.
|
| + void TestRsaPad(size_t num_bytes) {
|
| + std::vector<uint8> data(num_bytes);
|
| + crypto::RandBytes(&data.front(), data.size());
|
| +
|
| + std::vector<uint8> m = ClientUpdateProtocol::RsaPad(kRsaKeySize, data);
|
| + ASSERT_EQ(m.size(), kRsaKeySize);
|
| +
|
| + std::vector<uint8> hash(base::kSHA1Length);
|
| + base::SHA1HashBytes(&m.front(),
|
| + static_cast<int>(m.size() - base::kSHA1Length),
|
| + &hash.front());
|
| +
|
| + EXPECT_TRUE(SecureMemEqual(&hash.front(),
|
| + &m[kRsaKeySize - base::kSHA1Length],
|
| + base::kSHA1Length));
|
| +
|
| + EXPECT_FALSE(m[0] & 0x80); // msb is always reset.
|
| + EXPECT_TRUE(m[0] & 0x60); // bit next to msb is always set.
|
| + }
|
| +
|
| + void TestUrlSafeB64Encode(const char* expected_input, const char* input) {
|
| + std::string expected(expected_input);
|
| + EXPECT_EQ(expected, UrlSafeB64Encode(reinterpret_cast<const uint8*>(input),
|
| + strlen(input)));
|
| + }
|
| +
|
| + std::string UrlSafeB64Encode(const uint8* in_bytes, size_t length) {
|
| + std::vector<uint8> in(in_bytes, in_bytes + length);
|
| + return ClientUpdateProtocol::UrlSafeB64Encode(in);
|
| + }
|
| +
|
| + std::vector<uint8> HashBin(const base::StringPiece& in_str) {
|
| + return ClientUpdateProtocol::Hash(in_str);
|
| + }
|
| +
|
| + std::string Hash(const std::vector<uint8>& in_bytes) {
|
| + return BytesToHex(ClientUpdateProtocol::Hash(in_bytes));
|
| + }
|
| +
|
| + std::string Hash(const base::StringPiece& in_str) {
|
| + return BytesToHex(ClientUpdateProtocol::Hash(in_str));
|
| + }
|
| +
|
| + std::string SymSign(const std::vector<uint8>& key,
|
| + uint8 id,
|
| + const std::vector<uint8>* h1,
|
| + const std::vector<uint8>* h2,
|
| + const std::vector<uint8>* h3) {
|
| + return BytesToHex(ClientUpdateProtocol::SymSign(key, id, h1, h2, h3));
|
| + }
|
| +
|
| + std::vector<uint8> SymSignRaw(const std::vector<uint8>& key,
|
| + uint8 id,
|
| + const std::vector<uint8>* h1,
|
| + const std::vector<uint8>* h2,
|
| + const std::vector<uint8>* h3) {
|
| + return ClientUpdateProtocol::SymSign(key, id, h1, h2, h3);
|
| + }
|
| +
|
| + std::string BytesToHex(const std::vector<uint8>& bytes) {
|
| + std::string result;
|
| + if (bytes.size() < std::numeric_limits<size_t>::max() / 2) {
|
| + static const char kHexChars[] = "0123456789abcdef";
|
| + for (size_t i = 0; i < bytes.size(); ++i) {
|
| + result.push_back(kHexChars[(bytes[i] >> 4)]);
|
| + result.push_back(kHexChars[(bytes[i] & 0xf)]);
|
| + }
|
| + }
|
| + return result;
|
| + }
|
| +
|
| + void OverrideRAndRebuildKeys() {
|
| + static const char kFixedR[] =
|
| + "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do "
|
| + "eiusmod tempor incididunt ut labore et dolore magna aliqua. ";
|
| +
|
| + const std::string plaintext(kFixedR);
|
| + cup_.r_.assign(plaintext.begin(), plaintext.end());
|
| + EXPECT_TRUE(cup_.BuildSharedKey());
|
| + }
|
| +
|
| + const std::string& ExamineVW() {
|
| + return cup_.vw_;
|
| + }
|
| +
|
| + ClientUpdateProtocol& GetCup() {
|
| + return cup_;
|
| + }
|
| +
|
| +
|
| + private:
|
| + ClientUpdateProtocol cup_;
|
| +};
|
| +
|
| +TEST_F(CupUnitTest, RsaPad) {
|
| + ASSERT_GE(kRsaKeySize, base::kSHA1Length);
|
| +
|
| + TestRsaPad(1); // 1 byte.
|
| + TestRsaPad(base::kSHA1Length); // 20 bytes.
|
| + TestRsaPad(kRsaKeySize); // 128 bytes.
|
| + TestRsaPad(1000); // 1000 bytes.
|
| +}
|
| +
|
| +TEST_F(CupUnitTest, B64Encode) {
|
| + TestUrlSafeB64Encode("MQ", "1");
|
| + TestUrlSafeB64Encode("MTI", "12");
|
| + TestUrlSafeB64Encode("MTIz", "123");
|
| + TestUrlSafeB64Encode("PHA-SGk_PC9wPg", "<p>Hi?</p>");
|
| + TestUrlSafeB64Encode("R29vZ2xlIEluYw", "Google Inc");
|
| +}
|
| +
|
| +TEST_F(CupUnitTest, Hash) {
|
| + static const char kPlainText[] =
|
| + "The quick brown fox jumps over the lazy dog";
|
| +
|
| + // Empty vector.
|
| + std::vector<uint8> data;
|
| + EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
| + Hash(data));
|
| +
|
| + // Non-empty vector.
|
| + const uint8* first = reinterpret_cast<const uint8*>(kPlainText);
|
| + const uint8* last = first + strlen(kPlainText);
|
| + data.clear();
|
| + data.insert(data.begin(), first, last);
|
| + EXPECT_EQ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
|
| + Hash(data));
|
| +
|
| + // Empty string.
|
| + EXPECT_EQ("da39a3ee5e6b4b0d3255bfef95601890afd80709",
|
| + Hash(std::string()));
|
| +
|
| + // Non-empty string.
|
| + EXPECT_EQ("2fd4e1c67a2d28fced849ee1bb76e7391b93eb12",
|
| + Hash(std::string(kPlainText)));
|
| +}
|
| +
|
| +TEST_F(CupUnitTest, SymSign) {
|
| + static const char kPlainText[] =
|
| + "The quick brown fox jumps over the lazy dog";
|
| +
|
| + std::vector<uint8> key;
|
| + key.push_back(7);
|
| +
|
| + std::vector<uint8> hash = HashBin(std::string(kPlainText));
|
| +
|
| + // Verify that NULL buffers are not included in the hash.
|
| + EXPECT_EQ("1c6fa359c0a7b11421ae4d31a92f9de8a4ef8d2d",
|
| + SymSign(key, 1, &hash, NULL, NULL));
|
| + EXPECT_EQ("1c6fa359c0a7b11421ae4d31a92f9de8a4ef8d2d",
|
| + SymSign(key, 1, NULL, &hash, NULL));
|
| + EXPECT_EQ("1c6fa359c0a7b11421ae4d31a92f9de8a4ef8d2d",
|
| + SymSign(key, 1, NULL, &hash, NULL));
|
| +
|
| + // Verify that all three buffers are included in the hash.
|
| + EXPECT_EQ("fef3e343795946cd47ff4e07eca5f3f09b051d86",
|
| + SymSign(key, 1, &hash, &hash, &hash));
|
| +}
|
| +
|
| +TEST_F(CupUnitTest, EncryptSharedKey) {
|
| + // Given a fixed public key set in our test fixture, if we override (r) with
|
| + // a known data, we should be able to test (w) against an expected output.
|
| + //
|
| + // This expected output can be generated using this command line, where
|
| + // plaintext.bin is the contents of kFixedR[] in OverrideRAndRebuildKeys():
|
| + //
|
| + // openssl rsautl -inkey cr2_pub.pem -pubin -encrypt -raw
|
| + // -in plaintext.bin | base64
|
| + //
|
| + // Remember to prepend the key version number, and fix up the Base64
|
| + // afterwards to be URL-safe.
|
| +
|
| + static const char kExpectedVW[] =
|
| + "8:lMmNR3mVbOitbq8ceYGStFBwrJcpvY-sauFSbMVe6VONS9x42xTOLY_KdqsWCy"
|
| + "KuiJBiQziQLOybPUyA9vk0N5kMnC90LIh2nP2FgFG0M0Z22qjB3drsdJPi7TQZbb"
|
| + "Xhqm587M8vjc6VlM_eoC0qYwCPaXBqXjsyiHnXetcn5X0";
|
| +
|
| + EXPECT_NE(kExpectedVW, ExamineVW());
|
| + OverrideRAndRebuildKeys();
|
| + EXPECT_EQ(kExpectedVW, ExamineVW());
|
| +}
|
| +
|
| +TEST_F(CupUnitTest, SignRequest) {
|
| + static const char kUrlHost[] = "testserver.chromium.org";
|
| + static const char kUrlPath[] = "update";
|
| + static const char kUrlQuery[] = "?junk=present";
|
| + static const char kRequest[] = "testbody";
|
| +
|
| + static const char kExpectedCP[] = "hIxzNIHv4pg03NcXPLuATX8G2SI";
|
| +
|
| + OverrideRAndRebuildKeys();
|
| +
|
| + std::string cup_query;
|
| + std::string cp;
|
| +
|
| + // Check the case with a preexisting query string.
|
| + EXPECT_TRUE(GetCup().SignRequest(kUrlHost, kUrlPath, kUrlQuery, kRequest,
|
| + &cup_query, &cp));
|
| +
|
| + std::string expected_query(kUrlQuery);
|
| + expected_query.append("&w=");
|
| + expected_query.append(ExamineVW());
|
| + EXPECT_EQ(expected_query, cup_query);
|
| + EXPECT_EQ(kExpectedCP, cp);
|
| +
|
| + // Check the case with no query string.
|
| + std::string cp2;
|
| + EXPECT_TRUE(GetCup().SignRequest(kUrlHost, kUrlPath, std::string(), kRequest,
|
| + &cup_query, &cp2));
|
| + expected_query = "?w=";
|
| + expected_query.append(ExamineVW());
|
| + EXPECT_EQ(expected_query, cup_query);
|
| +
|
| + // Changes in the URL should result in changes in the client proof.
|
| + EXPECT_NE(cp, cp2);
|
| +}
|
| +
|
| +TEST_F(CupUnitTest, ValidateResponse) {
|
| + static const char kUrlHost[] = "testserver.chromium.org";
|
| + static const char kUrlPath[] = "update";
|
| + static const char kUrlQuery[] = "?junk=present";
|
| + static const char kRequest[] = "testbody";
|
| +
|
| + static const char kGoodResponse[] = "intact_response";
|
| + static const char kGoodC[] = "c=EncryptedDataFromTheUpdateServer";
|
| + static const char kGoodSP[] = "5rMFMPL9Hgqb-2J8kL3scsHeNgg";
|
| +
|
| + static const char kBadResponse[] = "tampered_response";
|
| + static const char kBadC[] = "c=TotalJunkThatAnAttackerCouldSend";
|
| + static const char kBadSP[] = "Base64TamperedShaOneHash";
|
| +
|
| + OverrideRAndRebuildKeys();
|
| +
|
| + // We should return false if no calls to SignRequest() have been made.
|
| + EXPECT_FALSE(GetCup().ValidateResponse(kGoodResponse, kGoodC, kGoodSP));
|
| +
|
| + EXPECT_TRUE(GetCup().SignRequest(kUrlHost, kUrlPath, kUrlQuery, kRequest,
|
| + NULL, NULL));
|
| +
|
| + // Return true on a valid response and server proof.
|
| + EXPECT_TRUE(GetCup().ValidateResponse(kGoodResponse, kGoodC, kGoodSP));
|
| +
|
| + // Return false on anything invalid.
|
| + EXPECT_FALSE(GetCup().ValidateResponse(kBadResponse, kGoodC, kGoodSP));
|
| + EXPECT_FALSE(GetCup().ValidateResponse(kGoodResponse, kBadC, kGoodSP));
|
| + EXPECT_FALSE(GetCup().ValidateResponse(kGoodResponse, kGoodC, kBadSP));
|
| + EXPECT_FALSE(GetCup().ValidateResponse(kGoodResponse, kBadC, kBadSP));
|
| + EXPECT_FALSE(GetCup().ValidateResponse(kBadResponse, kGoodC, kBadSP));
|
| + EXPECT_FALSE(GetCup().ValidateResponse(kBadResponse, kBadC, kGoodSP));
|
| + EXPECT_FALSE(GetCup().ValidateResponse(kBadResponse, kBadC, kBadSP));
|
| +}
|
| +
|
| +} // namespace crypto
|
| +
|
| +#endif // !defined(USE_OPENSSL)
|
| +
|
|
|