blob: 71c7441c213e9a186cf9f28334b2cd685e707c96 [file] [log] [blame]
sorin58086d52016-02-02 18:30:461// Copyright 2016 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
dchenga0ee5fb2016-04-26 02:46:555#include "components/client_update_protocol/ecdsa.h"
6
sorin58086d52016-02-02 18:30:467#include <stdint.h>
8
9#include <limits>
dchenga0ee5fb2016-04-26 02:46:5510#include <memory>
sorin58086d52016-02-02 18:30:4611#include <vector>
12
13#include "base/base64.h"
sorin58086d52016-02-02 18:30:4614#include "base/strings/string_piece.h"
15#include "base/strings/stringprintf.h"
sorin58086d52016-02-02 18:30:4616#include "crypto/random.h"
17#include "crypto/secure_util.h"
18#include "testing/gtest/include/gtest/gtest.h"
19
mabbb61b52a2016-03-17 23:37:2320namespace client_update_protocol {
sorin1bc5eff2016-02-17 18:45:1721
sorin58086d52016-02-02 18:30:4622namespace {
23
24std::string GetPublicKeyForTesting() {
25 // How to generate this key:
26 // openssl ecparam -genkey -name prime256v1 -out ecpriv.pem
27 // openssl ec -in ecpriv.pem -pubout -out ecpub.pem
28
29 static const char kCupEcdsaTestKey_Base64[] =
30 "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEJNOjKyN6UHyUGkGow+xCmQthQXUo"
31 "9sd7RIXSpVIM768UlbGb/5JrnISjSYejCc/pxQooI6mJTzWL3pZb5TA1DA==";
32
33 std::string result;
34 if (!base::Base64Decode(std::string(kCupEcdsaTestKey_Base64), &result))
35 return std::string();
36
37 return result;
38}
39
40} // end namespace
41
42class CupEcdsaTest : public testing::Test {
43 protected:
44 void SetUp() override {
mabbb61b52a2016-03-17 23:37:2345 cup_ = Ecdsa::Create(8, GetPublicKeyForTesting());
sorin58086d52016-02-02 18:30:4646 ASSERT_TRUE(cup_.get());
47 }
48
Zinovy Nisaa5aee392018-05-08 16:18:4149 Ecdsa& CUP() { return *cup_; }
sorin58086d52016-02-02 18:30:4650
51 private:
dchenga0ee5fb2016-04-26 02:46:5552 std::unique_ptr<Ecdsa> cup_;
sorin58086d52016-02-02 18:30:4653};
54
55TEST_F(CupEcdsaTest, SignRequest) {
56 static const char kRequest[] = "TestSequenceForCupEcdsaUnitTest";
57 static const char kRequestHash[] =
58 "&cup2hreq="
59 "cde1f7dc1311ed96813057ca321c2f5a17ea2c9c776ee0eb31965f7985a3074a";
60 static const char kKeyId[] = "cup2key=8:";
61
62 std::string query;
63 CUP().SignRequest(kRequest, &query);
64 std::string query2;
65 CUP().SignRequest(kRequest, &query2);
66
67 EXPECT_FALSE(query.empty());
68 EXPECT_FALSE(query2.empty());
69 EXPECT_EQ(0UL, query.find(kKeyId));
70 EXPECT_EQ(0UL, query2.find(kKeyId));
71 EXPECT_NE(std::string::npos, query.find(kRequestHash));
72 EXPECT_NE(std::string::npos, query2.find(kRequestHash));
73
74 // In theory, this is a flaky test, as there's nothing preventing the RNG
75 // from returning the same nonce twice in a row. In practice, this should
76 // be fine.
77 EXPECT_NE(query, query2);
78}
79
80TEST_F(CupEcdsaTest, ValidateResponse_TestETagParsing) {
81 // Invalid ETags must be gracefully rejected without a crash.
82 std::string query_discard;
83 CUP().SignRequest("Request_A", &query_discard);
mab2f07cb92016-05-10 20:55:5184 CUP().OverrideNonceForTesting(8, 12345);
sorin58086d52016-02-02 18:30:4685
86 // Expect a pass for a well-formed etag.
87 EXPECT_TRUE(CUP().ValidateResponse(
88 "Response_A",
89 "3044"
90 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
91 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
92 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
93
94 // Reject empty etags.
95 EXPECT_FALSE(CUP().ValidateResponse("Response_A", ""));
96
97 // Reject etags with zero-length hashes or signatures, even if the other
98 // component is wellformed.
99 EXPECT_FALSE(CUP().ValidateResponse("Response_A", ":"));
100 EXPECT_FALSE(CUP().ValidateResponse(
101 "Response_A",
102 "3044"
103 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
104 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
105 ":"));
106 EXPECT_FALSE(CUP().ValidateResponse(
107 "Response_A",
108 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
109
110 // Reject etags with non-hex content in either component.
111 EXPECT_FALSE(CUP().ValidateResponse(
112 "Response_A",
113 "3044"
114 "02207fb15d24e66c168ac150458__ae51f843c4858e27d41be3f9396d4919bbd5656"
115 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
116 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
117 EXPECT_FALSE(CUP().ValidateResponse(
118 "Response_A",
119 "3044"
120 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
121 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
122 ":2727bc2b3c33feb6800a830f4055901d__7d65a84184c5fbeb3f816db0a243f5"));
123
124 // Reject etags where either/both component has a length that's not a
125 // multiple of 2 (i.e. not a valid hex encoding).
126 EXPECT_FALSE(CUP().ValidateResponse(
127 "Response_A",
128 "3044"
129 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
130 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10"
131 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
132 EXPECT_FALSE(CUP().ValidateResponse(
133 "Response_A",
134 "3044"
135 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
136 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
137 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f"));
138 EXPECT_FALSE(CUP().ValidateResponse(
139 "Response_A",
140 "3044"
141 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
142 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10"
143 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f"));
144
145 // Reject etags where the hash is even, but not 256 bits.
146 EXPECT_FALSE(CUP().ValidateResponse(
147 "Response_A",
148 "3044"
149 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
150 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
151 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
152 EXPECT_FALSE(CUP().ValidateResponse(
153 "Response_A",
154 "3044"
155 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
156 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
157 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5ff"));
158
159 // Reject etags where the signature field is too small to be valid. (Note that
160 // the case isn't even a signature -- it's a validly encoded ASN.1 NULL.)
161 EXPECT_FALSE(CUP().ValidateResponse(
162 "Response_A",
163 "0500"
164 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
165
166 // Reject etags where the signature field is too big to be a valid signature.
167 // (This is a validly formed structure, but both ints are over 256 bits.)
168 EXPECT_FALSE(CUP().ValidateResponse(
169 "Response_A",
170 "3048"
171 "202207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
172 "202207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
173 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5ff"));
174
175 // Reject etags where the signature is valid DER-encoded ASN.1, but is not
176 // an ECDSA signature. (This is actually stressing crypto's SignatureValidator
177 // library, and not CUP's use of it, but it's worth testing here.) Cases:
178 // * Something that's not a sequence
179 // * Sequences that contain things other than ints (i.e. octet strings)
180 // * Sequences that contain a negative int.
181 EXPECT_FALSE(CUP().ValidateResponse(
182 "Response_A",
183 "0406020100020100"
184 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
185 EXPECT_FALSE(CUP().ValidateResponse(
186 "Response_A",
187 "3044"
188 "06200123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
189 "06200123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"
190 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
191 EXPECT_FALSE(CUP().ValidateResponse(
192 "Response_A",
193 "3046"
194 "02047fffffff"
195 "0220ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
196 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
197
198 // Reject etags where the signature is not a valid DER encoding. (Again, this
199 // is stressing SignatureValidator.) Test cases are:
200 // * No length field
201 // * Zero length field
202 // * One of the ints has truncated content
203 // * One of the ints has content longer than its length field
204 // * A positive int is improperly zero-padded
205 EXPECT_FALSE(CUP().ValidateResponse(
206 "Response_A",
207 "30"
208 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
209 EXPECT_FALSE(CUP().ValidateResponse(
210 "Response_A",
211 "3000"
212 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
213 EXPECT_FALSE(CUP().ValidateResponse(
214 "Response_A",
215 "3044"
216 "02207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
217 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
218 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
219 EXPECT_FALSE(CUP().ValidateResponse(
220 "Response_A",
221 "3044"
222 "02207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00"
223 "02207fb15d24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
224 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243"));
225 EXPECT_FALSE(CUP().ValidateResponse(
226 "Response_A",
227 "3044"
228 "022000007f24e66c168ac150458c7ae51f843c4858e27d41be3f9396d4919bbd5656"
229 "02202291bae598e4a41118ea1df24ce8494d4055b2842dc046e0223f5e17e86bd10e"
230 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
231}
232
233TEST_F(CupEcdsaTest, ValidateResponse_TestSigning) {
234 std::string query_discard;
235 CUP().SignRequest("Request_A", &query_discard);
mab2f07cb92016-05-10 20:55:51236 CUP().OverrideNonceForTesting(8, 12345);
sorin58086d52016-02-02 18:30:46237
238 // How to generate an ECDSA signature:
239 // echo -n Request_A | sha256sum | cut -d " " -f 1 > h
240 // echo -n Response_A | sha256sum | cut -d " " -f 1 >> h
241 // cat h | xxd -r -p > hbin
242 // echo -n 8:12345 >> hbin
243 // sha256sum hbin | cut -d " " -f 1 | xxd -r -p > hbin2
244 // openssl dgst -hex -sha256 -sign ecpriv.pem hbin2 | cut -d " " -f 2 > sig
245 // echo -n :Request_A | sha256sum | cut -d " " -f 1 >> sig
246 // cat sig
247 // It's useful to throw this in a bash script and parameterize it if you're
248 // updating this unit test.
249
250 // Valid case:
251 // * Send "Request_A" with key 8 / nonce 12345 to server.
252 // * Receive "Response_A", signature, and observed request hash from server.
253 // * Signature signs HASH(Request_A) | HASH(Response_A) | 8:12345.
254 // * Observed hash matches HASH(Request_A).
255 EXPECT_TRUE(CUP().ValidateResponse(
256 "Response_A",
257 "3045022077a2d004f1643a92af5d356877c3434c46519ce32882d6e30ef6d154ee9775e3"
258 "022100aca63c77d34152bdc0918ae0629e82b59314e5459f607cdc5ac95f1a4b7c31a2"
259 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
260
261 // Failure case: "Request_A" made it to the server intact, but the response
262 // body is modified to "Response_B" on return. The signature is now invalid.
263 EXPECT_FALSE(CUP().ValidateResponse(
264 "Response_B",
265 "3045022077a2d004f1643a92af5d356877c3434c46519ce32882d6e30ef6d154ee9775e3"
266 "022100aca63c77d34152bdc0918ae0629e82b59314e5459f607cdc5ac95f1a4b7c31a2"
267 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
268
269 // Failure case: Request body was modified to "Request_B" before it reached
270 // the server. Test a fast reject based on the observed_hash parameter.
271 EXPECT_FALSE(CUP().ValidateResponse(
272 "Response_B",
273 "304402206289a7765f0371c7c48796779747f1166707d5937a99af518845f44af95876"
274 "8c0220139fe935fde3e6b416ee742f91c6a480113762d78d889a2661de37576866d21c"
275 ":80e3ef1b373efe5f2a8383a0cf9c89fb2e0cbb8e85db4813655ff5dc05009e7e"));
276
277 // Failure case: Request body was modified to "Request_B" before it reached
278 // the server. Test a slow reject based on a signature mismatch.
279 EXPECT_FALSE(CUP().ValidateResponse(
280 "Response_B",
281 "304402206289a7765f0371c7c48796779747f1166707d5937a99af518845f44af95876"
282 "8c0220139fe935fde3e6b416ee742f91c6a480113762d78d889a2661de37576866d21c"
283 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
284
285 // Failure case: Request/response are intact, but the signature is invalid
286 // because it was signed against a different nonce (67890).
287 EXPECT_FALSE(CUP().ValidateResponse(
288 "Response_A",
289 "3046022100d3bbb1fb4451c8e04a07fe95404cc39121ed0e0bc084f87de19d52eee50a97"
290 "bf022100dd7d41d467be2af98d9116b0c7ba09740d54578c02a02f74da5f089834be3403"
291 ":2727bc2b3c33feb6800a830f4055901dd87d65a84184c5fbeb3f816db0a243f5"));
292}
sorin1bc5eff2016-02-17 18:45:17293
mabbb61b52a2016-03-17 23:37:23294} // namespace client_update_protocol