diff options
author | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-29 21:45:51 +0000 |
---|---|---|
committer | abarth@chromium.org <abarth@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-04-29 21:45:51 +0000 |
commit | 4eb4f8ba70cd40195461d4981c90e55bc95c07d3 (patch) | |
tree | 1bc9d4122856035d767b334e358dfd1b767b5323 /net | |
parent | 5c8eb2e6b391311f02b6aafd4e19421f70367313 (diff) | |
download | chromium_src-4eb4f8ba70cd40195461d4981c90e55bc95c07d3.zip chromium_src-4eb4f8ba70cd40195461d4981c90e55bc95c07d3.tar.gz chromium_src-4eb4f8ba70cd40195461d4981c90e55bc95c07d3.tar.bz2 |
MAC Cookies (2 of N)
This CL contains the algorithmic guts of MAC cookies, including generating
the canonical represntation of the request and signing it using HMAC. This
CL does not include support for body_hash, which requires some more thought.
Review URL: http://codereview.chromium.org/6901121
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@83600 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r-- | net/http/http_mac_signature.cc | 172 | ||||
-rw-r--r-- | net/http/http_mac_signature.h | 64 | ||||
-rw-r--r-- | net/http/http_mac_signature_unittest.cc | 109 | ||||
-rw-r--r-- | net/net.gyp | 3 |
4 files changed, 348 insertions, 0 deletions
diff --git a/net/http/http_mac_signature.cc b/net/http/http_mac_signature.cc new file mode 100644 index 0000000..76cbe51 --- /dev/null +++ b/net/http/http_mac_signature.cc @@ -0,0 +1,172 @@ +// Copyright (c) 2011 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 "net/http/http_mac_signature.h" + +#include "base/base64.h" +#include "base/rand_util.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/time.h" +#include "crypto/hmac.h" + +namespace net { + +namespace { + +const char kSHA1Name[] = "hmac-sha-1"; +const char kSHA256Name[] = "hmac-sha-256"; +const int kNonceLength = 256 / 8; + +size_t LengthForHMACAlgorithm(crypto::HMAC::HashAlgorithm algorithm) { + if (algorithm == crypto::HMAC::SHA1) + return 20; + if (algorithm == crypto::HMAC::SHA256) + return 32; + NOTREACHED(); + return 20; +} + +bool IsPlainStringCharacter(char character) { + return character == 0x20 || character == 0x21 || + (character >= 0x23 && character <= 0x5B) || + (character >= 0x5D && character <= 0x7E); +} + +bool IsPlainString(const std::string& string) { + for (size_t i = 0; i < string.size(); ++i) { + if (!IsPlainStringCharacter(string[i])) + return false; + } + return true; +} + +std::string GenerateNonce() { + std::string nonce; + bool result = base::Base64Encode( + base::RandBytesAsString(kNonceLength), &nonce); + DCHECK(result); + return nonce; +} + +} + +HttpMacSignature::HttpMacSignature() + : mac_algorithm_(crypto::HMAC::SHA1) { +} + +HttpMacSignature::~HttpMacSignature() { +} + +bool HttpMacSignature::AddStateInfo(const std::string& id, + const std::string& mac_key, + const std::string& mac_algorithm, + const std::string& issuer) { + DCHECK(id_.empty()); + + if (!IsPlainString(id) || id.empty() + || mac_key.empty() + || mac_algorithm.empty() + || !IsPlainString(issuer) || issuer.empty()) + return false; + + if (mac_algorithm == kSHA1Name) + mac_algorithm_ = crypto::HMAC::SHA1; + else if (mac_algorithm == kSHA256Name) + mac_algorithm_ = crypto::HMAC::SHA256; + else + return false; + + id_ = id; + mac_key_ = mac_key; + issuer_ = issuer; + return true; +} + +bool HttpMacSignature::AddHttpInfo(const std::string& method, + const std::string& request_uri, + const std::string& host, + int port) { + DCHECK(method_.empty()); + + if (!IsPlainString(method) || method.empty() + || !IsPlainString(request_uri) || request_uri.empty() + || !IsPlainString(host) || host.empty() + || port <= 0 + || port > 65535) + return false; + + method_ = StringToUpperASCII(method); + request_uri_ = request_uri; + host_ = StringToLowerASCII(host); + port_ = base::IntToString(port); + return true; +} + +std::string HttpMacSignature::GenerateAuthorizationHeader() { + DCHECK(!id_.empty()) << "Call AddStateInfo first."; + DCHECK(!method_.empty()) << "Call AddHttpInfo first."; + + std::string timestamp = base::IntToString((base::Time::Now() - + base::Time::UnixEpoch()).InSeconds()); + std::string nonce = GenerateNonce(); + + return GenerateHeaderString(timestamp, nonce); +} + +std::string HttpMacSignature::GenerateHeaderString( + const std::string& timestamp, + const std::string& nonce) { + std::string mac = GenerateMAC(timestamp, nonce); + + DCHECK(IsPlainString(timestamp)); + DCHECK(IsPlainString(nonce)); + DCHECK(IsPlainString(mac)); + + return "MAC id=\"" + id_ + + "\", issuer=\"" + issuer_ + + "\", timestamp=\"" + timestamp + + "\", nonce=\"" + nonce + + "\", mac=\"" + mac + "\""; +} + +std::string HttpMacSignature::GenerateNormalizedRequest( + const std::string& timestamp, + const std::string& nonce) { + static const std::string kNewLine = "\n"; + + std::string normalized_request = id_ + kNewLine; + normalized_request += issuer_ + kNewLine; + normalized_request += timestamp + kNewLine; + normalized_request += nonce + kNewLine; + normalized_request += method_ + kNewLine; + normalized_request += request_uri_ + kNewLine; + normalized_request += host_ + kNewLine; + normalized_request += port_ + kNewLine; + + return normalized_request; +} + +std::string HttpMacSignature::GenerateMAC(const std::string& timestamp, + const std::string& nonce) { + std::string request = GenerateNormalizedRequest(timestamp, nonce); + + crypto::HMAC hmac(mac_algorithm_); + hmac.Init(mac_key_); + + std::string signature; + size_t length = LengthForHMACAlgorithm(mac_algorithm_); + char* buffer = WriteInto(&signature, length); + bool result = hmac.Sign(request, + reinterpret_cast<unsigned char*>(buffer), + length); + DCHECK(result); + + std::string encoded_signature; + result = base::Base64Encode(signature, &encoded_signature); + DCHECK(result); + return encoded_signature; +} + +} // namespace net diff --git a/net/http/http_mac_signature.h b/net/http/http_mac_signature.h new file mode 100644 index 0000000..1049240 --- /dev/null +++ b/net/http/http_mac_signature.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 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. + +#ifndef NET_HTTP_MAC_SIGNATURE_H_ +#define NET_HTTP_MAC_SIGNATURE_H_ +#pragma once + +#include <string> + +#include "base/basictypes.h" +#include "base/gtest_prod_util.h" +#include "crypto/hmac.h" + +namespace net { + +class HttpMacSignature { + public: + HttpMacSignature(); + ~HttpMacSignature(); + + // Returns whether this information is valid. + bool AddStateInfo(const std::string& id, + const std::string& mac_key, + const std::string& mac_algorithm, + const std::string& issuer); + + // Returns whether this information is valid. + bool AddHttpInfo(const std::string& method, + const std::string& request_uri, + const std::string& host, + int port); + + std::string GenerateAuthorizationHeader(); + + private: + FRIEND_TEST_ALL_PREFIXES(HttpMacSignatureTest, GenerateHeaderString); + FRIEND_TEST_ALL_PREFIXES(HttpMacSignatureTest, GenerateNormalizedRequest); + FRIEND_TEST_ALL_PREFIXES(HttpMacSignatureTest, GenerateMAC); + + std::string GenerateHeaderString(const std::string& timestamp, + const std::string& nonce); + std::string GenerateNormalizedRequest(const std::string& timestamp, + const std::string& nonce); + std::string GenerateMAC(const std::string& timestamp, + const std::string& nonce); + + std::string id_; + std::string mac_key_; + crypto::HMAC::HashAlgorithm mac_algorithm_; + std::string issuer_; + + std::string method_; + std::string request_uri_; + std::string host_; + std::string port_; + // TODO(abarth): body_hash_ + + DISALLOW_COPY_AND_ASSIGN(HttpMacSignature); +}; + +} // namespace net + +#endif // NET_HTTP_MAC_SIGNATURE_H_ diff --git a/net/http/http_mac_signature_unittest.cc b/net/http/http_mac_signature_unittest.cc new file mode 100644 index 0000000..5019e4b9 --- /dev/null +++ b/net/http/http_mac_signature_unittest.cc @@ -0,0 +1,109 @@ +// Copyright (c) 2011 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 "testing/gtest/include/gtest/gtest.h" +#include "net/http/http_mac_signature.h" + +namespace net { + +TEST(HttpMacSignatureTest, BogusAddStateInfo) { + HttpMacSignature signature; + EXPECT_FALSE(signature.AddStateInfo("exciting-id", + "the-mac-key", + "bogus-hmac-algorithm", + "the-issuer")); + EXPECT_FALSE(signature.AddStateInfo("", + "the-mac-key", + "hmac-sha-1", + "the-issuer")); + EXPECT_FALSE(signature.AddStateInfo("exciting-id", + "", + "hmac-sha-1", + "the-issuer")); + EXPECT_FALSE(signature.AddStateInfo("exciting-id", + "the-mac-key", + "", + "the-issuer")); + EXPECT_FALSE(signature.AddStateInfo("exciting-id", + "the-mac-key", + "hmac-sha-1", + "")); +} + +TEST(HttpMacSignatureTest, BogusAddHttpInfo) { + HttpMacSignature signature; + EXPECT_FALSE(signature.AddHttpInfo("GET", "/requested", "example.com", 0)); + EXPECT_FALSE(signature.AddHttpInfo( + "GET", "/requested", "example.com", 29088983)); + EXPECT_FALSE(signature.AddHttpInfo("", "/requested", "example.com", 80)); + EXPECT_FALSE(signature.AddHttpInfo("GET", "", "example.com", 80)); + EXPECT_FALSE(signature.AddHttpInfo("GET", "/requested", "", 80)); +} + +TEST(HttpMacSignatureTest, GenerateHeaderString) { + HttpMacSignature signature; + EXPECT_TRUE(signature.AddStateInfo("dfoi30j0qnf", + "adiMf03j0f3nOenc003r", + "hmac-sha-1", + "login.eXampLe.com:443")); + EXPECT_TRUE(signature.AddHttpInfo("GeT", + "/pAth?to=%22enlightenment%22&dest=magic", + "eXaMple.com", + 80)); + + std::string timestamp = "239034"; + std::string nonce = "mn4302j0n+32r2/f3r="; + + EXPECT_EQ("MAC id=\"dfoi30j0qnf\", " + "issuer=\"login.eXampLe.com:443\", " + "timestamp=\"239034\", " + "nonce=\"mn4302j0n+32r2/f3r=\", " + "mac=\"zQWLNI5eHOfY5/wCJ6yzZ8bXDw==\"", + signature.GenerateHeaderString(timestamp, nonce)); +} + + +TEST(HttpMacSignatureTest, GenerateNormalizedRequest) { + HttpMacSignature signature; + EXPECT_TRUE(signature.AddStateInfo("dfoi30j0qnf", + "adiMf03j0f3nOenc003r", + "hmac-sha-1", + "login.eXampLe.com:443")); + EXPECT_TRUE(signature.AddHttpInfo("GeT", + "/pAth?to=%22enlightenment%22&dest=magic", + "eXaMple.com", + 80)); + + std::string timestamp = "239034"; + std::string nonce = "mn4302j0n+32r2/f3r="; + + EXPECT_EQ("dfoi30j0qnf\n" + "login.eXampLe.com:443\n" + "239034\n" + "mn4302j0n+32r2/f3r=\n" + "GET\n" + "/pAth?to=%22enlightenment%22&dest=magic\n" + "example.com\n" + "80\n", + signature.GenerateNormalizedRequest(timestamp, nonce)); +} + +TEST(HttpMacSignatureTest, GenerateMAC) { + HttpMacSignature signature; + EXPECT_TRUE(signature.AddStateInfo("dfoi30j0qnf", + "adiMf03j0f3nOenc003r", + "hmac-sha-1", + "login.eXampLe.com:443")); + EXPECT_TRUE(signature.AddHttpInfo("GeT", + "/pAth?to=%22enlightenment%22&dest=magic", + "eXaMple.com", + 80)); + + std::string timestamp = "239034"; + std::string nonce = "mn4302j0n+32r2/f3r="; + + EXPECT_EQ("zQWLNI5eHOfY5/wCJ6yzZ8bXDw==", + signature.GenerateMAC(timestamp, nonce)); +} +} diff --git a/net/net.gyp b/net/net.gyp index 3254e1b..ea23f7a 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -500,6 +500,8 @@ 'http/http_cache_transaction.h', 'http/http_chunked_decoder.cc', 'http/http_chunked_decoder.h', + 'http/http_mac_signature.cc', + 'http/http_mac_signature.h', 'http/http_net_log_params.cc', 'http/http_net_log_params.h', 'http/http_network_layer.cc', @@ -967,6 +969,7 @@ 'http/http_byte_range_unittest.cc', 'http/http_cache_unittest.cc', 'http/http_chunked_decoder_unittest.cc', + 'http/http_mac_signature_unittest.cc', 'http/http_network_layer_unittest.cc', 'http/http_network_transaction_unittest.cc', 'http/http_proxy_client_socket_pool_unittest.cc', |