Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(283)

Unified Diff: crypto/cup_unittest.cc

Issue 15793005: Per discussion, implement the Omaha Client Update Protocol (CUP) in src/crypto. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« crypto/cup_nss.cc ('K') | « crypto/cup_openssl.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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)
+
« crypto/cup_nss.cc ('K') | « crypto/cup_openssl.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698