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