David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 1 | /* ==================================================================== |
| 2 | * Copyright (c) 1998-2005 The OpenSSL Project. All rights reserved. |
| 3 | * |
| 4 | * Redistribution and use in source and binary forms, with or without |
| 5 | * modification, are permitted provided that the following conditions |
| 6 | * are met: |
| 7 | * |
| 8 | * 1. Redistributions of source code must retain the above copyright |
| 9 | * notice, this list of conditions and the following disclaimer. |
| 10 | * |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
| 12 | * notice, this list of conditions and the following disclaimer in |
| 13 | * the documentation and/or other materials provided with the |
| 14 | * distribution. |
| 15 | * |
| 16 | * 3. All advertising materials mentioning features or use of this |
| 17 | * software must display the following acknowledgment: |
| 18 | * "This product includes software developed by the OpenSSL Project |
| 19 | * for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)" |
| 20 | * |
| 21 | * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to |
| 22 | * endorse or promote products derived from this software without |
| 23 | * prior written permission. For written permission, please contact |
| 24 | * openssl-core@OpenSSL.org. |
| 25 | * |
| 26 | * 5. Products derived from this software may not be called "OpenSSL" |
| 27 | * nor may "OpenSSL" appear in their names without prior written |
| 28 | * permission of the OpenSSL Project. |
| 29 | * |
| 30 | * 6. Redistributions of any form whatsoever must retain the following |
| 31 | * acknowledgment: |
| 32 | * "This product includes software developed by the OpenSSL Project |
| 33 | * for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)" |
| 34 | * |
| 35 | * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY |
| 36 | * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 37 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
| 38 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OpenSSL PROJECT OR |
| 39 | * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
| 40 | * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 41 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| 42 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 43 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| 44 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| 45 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED |
| 46 | * OF THE POSSIBILITY OF SUCH DAMAGE. |
| 47 | * ==================================================================== |
| 48 | * |
| 49 | * This product includes cryptographic software written by Eric Young |
| 50 | * (eay@cryptsoft.com). This product includes software written by Tim |
| 51 | * Hudson (tjh@cryptsoft.com). */ |
| 52 | |
| 53 | #include <openssl/ecdsa.h> |
| 54 | |
| 55 | #include <vector> |
| 56 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 57 | #include <gtest/gtest.h> |
| 58 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 59 | #include <openssl/bn.h> |
| 60 | #include <openssl/crypto.h> |
| 61 | #include <openssl/ec.h> |
| 62 | #include <openssl/err.h> |
| 63 | #include <openssl/mem.h> |
David Benjamin | 9819367 | 2016-03-25 18:07:11 -0400 | [diff] [blame] | 64 | #include <openssl/nid.h> |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 65 | #include <openssl/rand.h> |
| 66 | |
David Benjamin | 42a8cbe | 2017-11-13 14:45:26 +0800 | [diff] [blame] | 67 | #include "../ec/internal.h" |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 68 | #include "../../test/file_test.h" |
David Benjamin | 8640b41 | 2021-01-31 18:23:39 -0500 | [diff] [blame] | 69 | #include "../../test/test_util.h" |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 70 | |
| 71 | |
David Benjamin | fbec517 | 2018-11-08 15:18:17 -0600 | [diff] [blame] | 72 | static bssl::UniquePtr<BIGNUM> HexToBIGNUM(const char *hex) { |
| 73 | BIGNUM *bn = nullptr; |
| 74 | BN_hex2bn(&bn, hex); |
| 75 | return bssl::UniquePtr<BIGNUM>(bn); |
| 76 | } |
| 77 | |
| 78 | // Though we do not support secp160r1, it is reachable from the deprecated |
| 79 | // custom curve APIs and has some unique properties (n is larger than p with the |
| 80 | // difference crossing a word boundary on 32-bit), so test it explicitly. |
| 81 | static bssl::UniquePtr<EC_GROUP> NewSecp160r1Group() { |
| 82 | static const char kP[] = "ffffffffffffffffffffffffffffffff7fffffff"; |
| 83 | static const char kA[] = "ffffffffffffffffffffffffffffffff7ffffffc"; |
| 84 | static const char kB[] = "1c97befc54bd7a8b65acf89f81d4d4adc565fa45"; |
| 85 | static const char kX[] = "4a96b5688ef573284664698968c38bb913cbfc82"; |
| 86 | static const char kY[] = "23a628553168947d59dcc912042351377ac5fb32"; |
| 87 | static const char kN[] = "0100000000000000000001f4c8f927aed3ca752257"; |
| 88 | |
| 89 | bssl::UniquePtr<BIGNUM> p = HexToBIGNUM(kP), a = HexToBIGNUM(kA), |
| 90 | b = HexToBIGNUM(kB), x = HexToBIGNUM(kX), |
| 91 | y = HexToBIGNUM(kY), n = HexToBIGNUM(kN); |
| 92 | if (!p || !a || !b || !x || !y || !n) { |
| 93 | return nullptr; |
| 94 | } |
| 95 | |
| 96 | bssl::UniquePtr<EC_GROUP> group( |
| 97 | EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), nullptr)); |
| 98 | if (!group) { |
| 99 | return nullptr; |
| 100 | } |
| 101 | bssl::UniquePtr<EC_POINT> g(EC_POINT_new(group.get())); |
| 102 | if (!g || |
| 103 | !EC_POINT_set_affine_coordinates_GFp(group.get(), g.get(), x.get(), |
| 104 | y.get(), nullptr) || |
| 105 | !EC_GROUP_set_generator(group.get(), g.get(), n.get(), BN_value_one())) { |
| 106 | return nullptr; |
| 107 | } |
| 108 | return group; |
| 109 | } |
| 110 | |
David Benjamin | 238c274 | 2017-11-08 16:30:58 -0500 | [diff] [blame] | 111 | enum API { |
| 112 | kEncodedAPI, |
| 113 | kRawAPI, |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 114 | }; |
| 115 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 116 | // VerifyECDSASig checks that verifying |ecdsa_sig| gives |expected_result|. |
David Benjamin | 238c274 | 2017-11-08 16:30:58 -0500 | [diff] [blame] | 117 | static void VerifyECDSASig(API api, const uint8_t *digest, size_t digest_len, |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 118 | const ECDSA_SIG *ecdsa_sig, EC_KEY *eckey, |
| 119 | int expected_result) { |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 120 | switch (api) { |
David Benjamin | 238c274 | 2017-11-08 16:30:58 -0500 | [diff] [blame] | 121 | case kEncodedAPI: { |
David Benjamin | 183eba4 | 2015-06-12 01:27:46 -0400 | [diff] [blame] | 122 | uint8_t *der; |
| 123 | size_t der_len; |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 124 | ASSERT_TRUE(ECDSA_SIG_to_bytes(&der, &der_len, ecdsa_sig)); |
Matt Braithwaite | d17d74d | 2016-08-17 20:10:28 -0700 | [diff] [blame] | 125 | bssl::UniquePtr<uint8_t> delete_der(der); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 126 | EXPECT_EQ(expected_result, |
| 127 | ECDSA_verify(0, digest, digest_len, der, der_len, eckey)); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 128 | break; |
| 129 | } |
| 130 | |
David Benjamin | 238c274 | 2017-11-08 16:30:58 -0500 | [diff] [blame] | 131 | case kRawAPI: |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 132 | EXPECT_EQ(expected_result, |
| 133 | ECDSA_do_verify(digest, digest_len, ecdsa_sig, eckey)); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 134 | break; |
| 135 | |
| 136 | default: |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 137 | FAIL() << "Unknown API type."; |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 138 | } |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 139 | } |
| 140 | |
| 141 | // TestTamperedSig verifies that signature verification fails when a valid |
| 142 | // signature is tampered with. |ecdsa_sig| must be a valid signature, which will |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 143 | // be modified. |
David Benjamin | 238c274 | 2017-11-08 16:30:58 -0500 | [diff] [blame] | 144 | static void TestTamperedSig(API api, const uint8_t *digest, |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 145 | size_t digest_len, ECDSA_SIG *ecdsa_sig, |
| 146 | EC_KEY *eckey, const BIGNUM *order) { |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 147 | SCOPED_TRACE(api); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 148 | // Modify a single byte of the signature: to ensure we don't |
| 149 | // garble the ASN1 structure, we read the raw signature and |
| 150 | // modify a byte in one of the bignums directly. |
| 151 | |
| 152 | // Store the two BIGNUMs in raw_buf. |
| 153 | size_t r_len = BN_num_bytes(ecdsa_sig->r); |
| 154 | size_t s_len = BN_num_bytes(ecdsa_sig->s); |
| 155 | size_t bn_len = BN_num_bytes(order); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 156 | ASSERT_LE(r_len, bn_len); |
| 157 | ASSERT_LE(s_len, bn_len); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 158 | size_t buf_len = 2 * bn_len; |
| 159 | std::vector<uint8_t> raw_buf(buf_len); |
| 160 | // Pad the bignums with leading zeroes. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 161 | ASSERT_TRUE(BN_bn2bin_padded(raw_buf.data(), bn_len, ecdsa_sig->r)); |
| 162 | ASSERT_TRUE(BN_bn2bin_padded(raw_buf.data() + bn_len, bn_len, ecdsa_sig->s)); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 163 | |
| 164 | // Modify a single byte in the buffer. |
| 165 | size_t offset = raw_buf[10] % buf_len; |
| 166 | uint8_t dirt = raw_buf[11] ? raw_buf[11] : 1; |
| 167 | raw_buf[offset] ^= dirt; |
| 168 | // Now read the BIGNUMs back in from raw_buf. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 169 | ASSERT_TRUE(BN_bin2bn(raw_buf.data(), bn_len, ecdsa_sig->r)); |
| 170 | ASSERT_TRUE(BN_bin2bn(raw_buf.data() + bn_len, bn_len, ecdsa_sig->s)); |
| 171 | VerifyECDSASig(api, digest, digest_len, ecdsa_sig, eckey, 0); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 172 | |
| 173 | // Sanity check: Undo the modification and verify signature. |
| 174 | raw_buf[offset] ^= dirt; |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 175 | ASSERT_TRUE(BN_bin2bn(raw_buf.data(), bn_len, ecdsa_sig->r)); |
| 176 | ASSERT_TRUE(BN_bin2bn(raw_buf.data() + bn_len, bn_len, ecdsa_sig->s)); |
| 177 | VerifyECDSASig(api, digest, digest_len, ecdsa_sig, eckey, 1); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 178 | } |
| 179 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 180 | TEST(ECDSATest, BuiltinCurves) { |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 181 | // Fill digest values with some random data. |
| 182 | uint8_t digest[20], wrong_digest[20]; |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 183 | ASSERT_TRUE(RAND_bytes(digest, 20)); |
| 184 | ASSERT_TRUE(RAND_bytes(wrong_digest, 20)); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 185 | |
| 186 | static const struct { |
| 187 | int nid; |
| 188 | const char *name; |
| 189 | } kCurves[] = { |
| 190 | { NID_secp224r1, "secp224r1" }, |
| 191 | { NID_X9_62_prime256v1, "secp256r1" }, |
| 192 | { NID_secp384r1, "secp384r1" }, |
| 193 | { NID_secp521r1, "secp521r1" }, |
David Benjamin | fbec517 | 2018-11-08 15:18:17 -0600 | [diff] [blame] | 194 | { NID_secp160r1, "secp160r1" }, |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 195 | }; |
| 196 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 197 | for (const auto &curve : kCurves) { |
| 198 | SCOPED_TRACE(curve.name); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 199 | |
David Benjamin | fbec517 | 2018-11-08 15:18:17 -0600 | [diff] [blame] | 200 | bssl::UniquePtr<EC_GROUP> group; |
| 201 | if (curve.nid == NID_secp160r1) { |
| 202 | group = NewSecp160r1Group(); |
| 203 | } else { |
| 204 | group.reset(EC_GROUP_new_by_curve_name(curve.nid)); |
| 205 | } |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 206 | ASSERT_TRUE(group); |
Brian Smith | a3d9de0 | 2015-11-18 17:07:14 -1000 | [diff] [blame] | 207 | const BIGNUM *order = EC_GROUP_get0_order(group.get()); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 208 | |
| 209 | // Create a new ECDSA key. |
Matt Braithwaite | d17d74d | 2016-08-17 20:10:28 -0700 | [diff] [blame] | 210 | bssl::UniquePtr<EC_KEY> eckey(EC_KEY_new()); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 211 | ASSERT_TRUE(eckey); |
| 212 | ASSERT_TRUE(EC_KEY_set_group(eckey.get(), group.get())); |
| 213 | ASSERT_TRUE(EC_KEY_generate_key(eckey.get())); |
| 214 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 215 | // Create a second key. |
Matt Braithwaite | d17d74d | 2016-08-17 20:10:28 -0700 | [diff] [blame] | 216 | bssl::UniquePtr<EC_KEY> wrong_eckey(EC_KEY_new()); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 217 | ASSERT_TRUE(wrong_eckey); |
| 218 | ASSERT_TRUE(EC_KEY_set_group(wrong_eckey.get(), group.get())); |
| 219 | ASSERT_TRUE(EC_KEY_generate_key(wrong_eckey.get())); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 220 | |
| 221 | // Check the key. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 222 | EXPECT_TRUE(EC_KEY_check_key(eckey.get())); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 223 | |
| 224 | // Test ASN.1-encoded signatures. |
| 225 | // Create a signature. |
David Benjamin | 0f222e6 | 2023-07-03 12:24:51 -0400 | [diff] [blame] | 226 | std::vector<uint8_t> signature(ECDSA_size(eckey.get())); |
| 227 | unsigned sig_len; |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 228 | ASSERT_TRUE( |
| 229 | ECDSA_sign(0, digest, 20, signature.data(), &sig_len, eckey.get())); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 230 | signature.resize(sig_len); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 231 | |
David Benjamin | 8640b41 | 2021-01-31 18:23:39 -0500 | [diff] [blame] | 232 | // ECDSA signing should be non-deterministic. This does not verify k is |
| 233 | // generated securely but at least checks it was randomized at all. |
David Benjamin | 0f222e6 | 2023-07-03 12:24:51 -0400 | [diff] [blame] | 234 | std::vector<uint8_t> signature2(ECDSA_size(eckey.get())); |
David Benjamin | 8640b41 | 2021-01-31 18:23:39 -0500 | [diff] [blame] | 235 | ASSERT_TRUE( |
| 236 | ECDSA_sign(0, digest, 20, signature2.data(), &sig_len, eckey.get())); |
| 237 | signature2.resize(sig_len); |
| 238 | EXPECT_NE(Bytes(signature), Bytes(signature2)); |
| 239 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 240 | // Verify the signature. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 241 | EXPECT_TRUE(ECDSA_verify(0, digest, 20, signature.data(), signature.size(), |
| 242 | eckey.get())); |
| 243 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 244 | // Verify the signature with the wrong key. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 245 | EXPECT_FALSE(ECDSA_verify(0, digest, 20, signature.data(), signature.size(), |
| 246 | wrong_eckey.get())); |
| 247 | ERR_clear_error(); |
| 248 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 249 | // Verify the signature using the wrong digest. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 250 | EXPECT_FALSE(ECDSA_verify(0, wrong_digest, 20, signature.data(), |
| 251 | signature.size(), eckey.get())); |
| 252 | ERR_clear_error(); |
| 253 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 254 | // Verify a truncated signature. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 255 | EXPECT_FALSE(ECDSA_verify(0, digest, 20, signature.data(), |
| 256 | signature.size() - 1, eckey.get())); |
| 257 | ERR_clear_error(); |
| 258 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 259 | // Verify a tampered signature. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 260 | bssl::UniquePtr<ECDSA_SIG> ecdsa_sig( |
| 261 | ECDSA_SIG_from_bytes(signature.data(), signature.size())); |
| 262 | ASSERT_TRUE(ecdsa_sig); |
David Benjamin | 238c274 | 2017-11-08 16:30:58 -0500 | [diff] [blame] | 263 | TestTamperedSig(kEncodedAPI, digest, 20, ecdsa_sig.get(), eckey.get(), |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 264 | order); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 265 | |
| 266 | // Test ECDSA_SIG signing and verification. |
| 267 | // Create a signature. |
| 268 | ecdsa_sig.reset(ECDSA_do_sign(digest, 20, eckey.get())); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 269 | ASSERT_TRUE(ecdsa_sig); |
| 270 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 271 | // Verify the signature using the correct key. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 272 | EXPECT_TRUE(ECDSA_do_verify(digest, 20, ecdsa_sig.get(), eckey.get())); |
| 273 | |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 274 | // Verify the signature with the wrong key. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 275 | EXPECT_FALSE( |
| 276 | ECDSA_do_verify(digest, 20, ecdsa_sig.get(), wrong_eckey.get())); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 277 | ERR_clear_error(); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 278 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 279 | // Verify the signature using the wrong digest. |
| 280 | EXPECT_FALSE( |
| 281 | ECDSA_do_verify(wrong_digest, 20, ecdsa_sig.get(), eckey.get())); |
| 282 | ERR_clear_error(); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 283 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 284 | // Verify a tampered signature. |
David Benjamin | 238c274 | 2017-11-08 16:30:58 -0500 | [diff] [blame] | 285 | TestTamperedSig(kRawAPI, digest, 20, ecdsa_sig.get(), eckey.get(), order); |
David Benjamin | 7244fa4 | 2015-06-10 16:52:59 -0400 | [diff] [blame] | 286 | } |
David Benjamin | 7244fa4 | 2015-06-10 16:52:59 -0400 | [diff] [blame] | 287 | } |
| 288 | |
Brian Smith | 168297e | 2016-01-30 13:16:06 -1000 | [diff] [blame] | 289 | static size_t BitsToBytes(size_t bits) { |
| 290 | return (bits / 8) + (7 + (bits % 8)) / 8; |
| 291 | } |
| 292 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 293 | TEST(ECDSATest, MaxSigLen) { |
| 294 | static const size_t kBits[] = {224, 256, 384, 521, 10000}; |
| 295 | for (size_t bits : kBits) { |
| 296 | SCOPED_TRACE(bits); |
| 297 | size_t order_len = BitsToBytes(bits); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 298 | |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 299 | // Create the largest possible |ECDSA_SIG| of the given constraints. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 300 | bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new()); |
| 301 | ASSERT_TRUE(sig); |
| 302 | std::vector<uint8_t> bytes(order_len, 0xff); |
| 303 | ASSERT_TRUE(BN_bin2bn(bytes.data(), bytes.size(), sig->r)); |
| 304 | ASSERT_TRUE(BN_bin2bn(bytes.data(), bytes.size(), sig->s)); |
David Benjamin | 808f832 | 2017-08-18 14:06:02 -0400 | [diff] [blame] | 305 | // Serialize it. |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 306 | uint8_t *der; |
| 307 | size_t der_len; |
| 308 | ASSERT_TRUE(ECDSA_SIG_to_bytes(&der, &der_len, sig.get())); |
| 309 | OPENSSL_free(der); |
| 310 | |
| 311 | EXPECT_EQ(der_len, ECDSA_SIG_max_len(order_len)); |
| 312 | } |
| 313 | } |
| 314 | |
| 315 | static bssl::UniquePtr<EC_GROUP> GetCurve(FileTest *t, const char *key) { |
| 316 | std::string curve_name; |
| 317 | if (!t->GetAttribute(&curve_name, key)) { |
| 318 | return nullptr; |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 319 | } |
| 320 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 321 | if (curve_name == "P-224") { |
David Benjamin | 70be012 | 2023-02-13 19:05:19 -0500 | [diff] [blame^] | 322 | return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p224())); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 323 | } |
| 324 | if (curve_name == "P-256") { |
David Benjamin | 70be012 | 2023-02-13 19:05:19 -0500 | [diff] [blame^] | 325 | return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p256())); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 326 | } |
| 327 | if (curve_name == "P-384") { |
David Benjamin | 70be012 | 2023-02-13 19:05:19 -0500 | [diff] [blame^] | 328 | return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p384())); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 329 | } |
| 330 | if (curve_name == "P-521") { |
David Benjamin | 70be012 | 2023-02-13 19:05:19 -0500 | [diff] [blame^] | 331 | return bssl::UniquePtr<EC_GROUP>(const_cast<EC_GROUP *>(EC_group_p521())); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 332 | } |
David Benjamin | fbec517 | 2018-11-08 15:18:17 -0600 | [diff] [blame] | 333 | if (curve_name == "secp160r1") { |
| 334 | return NewSecp160r1Group(); |
| 335 | } |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 336 | |
| 337 | ADD_FAILURE() << "Unknown curve: " << curve_name; |
| 338 | return nullptr; |
| 339 | } |
| 340 | |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 341 | static bssl::UniquePtr<EC_GROUP> MakeCustomClone(const EC_GROUP *group) { |
| 342 | bssl::UniquePtr<BN_CTX> ctx(BN_CTX_new()); |
| 343 | bssl::UniquePtr<BIGNUM> p(BN_new()), a(BN_new()), b(BN_new()), x(BN_new()), |
| 344 | y(BN_new()); |
| 345 | if (!ctx || !p || !a || !b || !x || !y || |
| 346 | !EC_GROUP_get_curve_GFp(group, p.get(), a.get(), b.get(), ctx.get()) || |
| 347 | !EC_POINT_get_affine_coordinates_GFp( |
| 348 | group, EC_GROUP_get0_generator(group), x.get(), y.get(), ctx.get())) { |
| 349 | return nullptr; |
| 350 | } |
| 351 | bssl::UniquePtr<EC_GROUP> ret( |
| 352 | EC_GROUP_new_curve_GFp(p.get(), a.get(), b.get(), ctx.get())); |
| 353 | if (!ret) { |
| 354 | return nullptr; |
| 355 | } |
| 356 | bssl::UniquePtr<EC_POINT> g(EC_POINT_new(ret.get())); |
| 357 | if (!g || |
| 358 | !EC_POINT_set_affine_coordinates_GFp(ret.get(), g.get(), x.get(), y.get(), |
| 359 | ctx.get()) || |
| 360 | !EC_GROUP_set_generator(ret.get(), g.get(), EC_GROUP_get0_order(group), |
| 361 | BN_value_one())) { |
| 362 | return nullptr; |
| 363 | } |
| 364 | return ret; |
| 365 | } |
| 366 | |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 367 | static bssl::UniquePtr<BIGNUM> GetBIGNUM(FileTest *t, const char *key) { |
| 368 | std::vector<uint8_t> bytes; |
| 369 | if (!t->GetBytes(&bytes, key)) { |
| 370 | return nullptr; |
| 371 | } |
| 372 | |
| 373 | return bssl::UniquePtr<BIGNUM>(BN_bin2bn(bytes.data(), bytes.size(), nullptr)); |
| 374 | } |
| 375 | |
| 376 | TEST(ECDSATest, VerifyTestVectors) { |
| 377 | FileTestGTest("crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt", |
| 378 | [](FileTest *t) { |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 379 | for (bool custom_group : {false, true}) { |
| 380 | SCOPED_TRACE(custom_group); |
| 381 | bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve"); |
| 382 | ASSERT_TRUE(group); |
| 383 | if (custom_group) { |
| 384 | group = MakeCustomClone(group.get()); |
| 385 | ASSERT_TRUE(group); |
| 386 | } |
| 387 | bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X"); |
| 388 | ASSERT_TRUE(x); |
| 389 | bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y"); |
| 390 | ASSERT_TRUE(y); |
| 391 | bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R"); |
| 392 | ASSERT_TRUE(r); |
| 393 | bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S"); |
| 394 | ASSERT_TRUE(s); |
| 395 | std::vector<uint8_t> digest; |
| 396 | ASSERT_TRUE(t->GetBytes(&digest, "Digest")); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 397 | |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 398 | bssl::UniquePtr<EC_KEY> key(EC_KEY_new()); |
| 399 | ASSERT_TRUE(key); |
| 400 | bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get())); |
| 401 | ASSERT_TRUE(pub_key); |
| 402 | bssl::UniquePtr<ECDSA_SIG> sig(ECDSA_SIG_new()); |
| 403 | ASSERT_TRUE(sig); |
| 404 | ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get())); |
| 405 | ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp( |
| 406 | group.get(), pub_key.get(), x.get(), y.get(), nullptr)); |
| 407 | ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get())); |
| 408 | ASSERT_TRUE(BN_copy(sig->r, r.get())); |
| 409 | ASSERT_TRUE(BN_copy(sig->s, s.get())); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 410 | |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 411 | EXPECT_EQ( |
| 412 | t->HasAttribute("Invalid") ? 0 : 1, |
| 413 | ECDSA_do_verify(digest.data(), digest.size(), sig.get(), key.get())); |
| 414 | } |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 415 | }); |
| 416 | } |
| 417 | |
| 418 | TEST(ECDSATest, SignTestVectors) { |
| 419 | FileTestGTest("crypto/fipsmodule/ecdsa/ecdsa_sign_tests.txt", |
| 420 | [](FileTest *t) { |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 421 | for (bool custom_group : {false, true}) { |
| 422 | SCOPED_TRACE(custom_group); |
| 423 | bssl::UniquePtr<EC_GROUP> group = GetCurve(t, "Curve"); |
| 424 | ASSERT_TRUE(group); |
| 425 | if (custom_group) { |
| 426 | group = MakeCustomClone(group.get()); |
| 427 | ASSERT_TRUE(group); |
| 428 | } |
| 429 | bssl::UniquePtr<BIGNUM> priv_key = GetBIGNUM(t, "Private"); |
| 430 | ASSERT_TRUE(priv_key); |
| 431 | bssl::UniquePtr<BIGNUM> x = GetBIGNUM(t, "X"); |
| 432 | ASSERT_TRUE(x); |
| 433 | bssl::UniquePtr<BIGNUM> y = GetBIGNUM(t, "Y"); |
| 434 | ASSERT_TRUE(y); |
David Benjamin | 409ea28 | 2021-01-31 18:04:07 -0500 | [diff] [blame] | 435 | std::vector<uint8_t> k; |
| 436 | ASSERT_TRUE(t->GetBytes(&k, "K")); |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 437 | bssl::UniquePtr<BIGNUM> r = GetBIGNUM(t, "R"); |
| 438 | ASSERT_TRUE(r); |
| 439 | bssl::UniquePtr<BIGNUM> s = GetBIGNUM(t, "S"); |
| 440 | ASSERT_TRUE(s); |
| 441 | std::vector<uint8_t> digest; |
| 442 | ASSERT_TRUE(t->GetBytes(&digest, "Digest")); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 443 | |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 444 | bssl::UniquePtr<EC_KEY> key(EC_KEY_new()); |
| 445 | ASSERT_TRUE(key); |
| 446 | bssl::UniquePtr<EC_POINT> pub_key(EC_POINT_new(group.get())); |
| 447 | ASSERT_TRUE(pub_key); |
| 448 | ASSERT_TRUE(EC_KEY_set_group(key.get(), group.get())); |
| 449 | ASSERT_TRUE(EC_KEY_set_private_key(key.get(), priv_key.get())); |
| 450 | ASSERT_TRUE(EC_POINT_set_affine_coordinates_GFp( |
| 451 | group.get(), pub_key.get(), x.get(), y.get(), nullptr)); |
| 452 | ASSERT_TRUE(EC_KEY_set_public_key(key.get(), pub_key.get())); |
| 453 | ASSERT_TRUE(EC_KEY_check_key(key.get())); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 454 | |
David Benjamin | 42a8cbe | 2017-11-13 14:45:26 +0800 | [diff] [blame] | 455 | bssl::UniquePtr<ECDSA_SIG> sig( |
David Benjamin | 409ea28 | 2021-01-31 18:04:07 -0500 | [diff] [blame] | 456 | ECDSA_sign_with_nonce_and_leak_private_key_for_testing( |
| 457 | digest.data(), digest.size(), key.get(), k.data(), k.size())); |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 458 | ASSERT_TRUE(sig); |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 459 | |
David Benjamin | 8db94be | 2017-11-13 14:43:22 +0800 | [diff] [blame] | 460 | EXPECT_EQ(0, BN_cmp(r.get(), sig->r)); |
| 461 | EXPECT_EQ(0, BN_cmp(s.get(), sig->s)); |
| 462 | } |
David Benjamin | d91e1ef | 2017-06-06 10:22:22 -0400 | [diff] [blame] | 463 | }); |
David Benjamin | daf2c5d | 2015-04-15 15:56:27 -0400 | [diff] [blame] | 464 | } |