key.cpp raw
1 // Copyright (c) 2020-2022 The Limenka developers
2 // Distributed under the MIT software license, see the accompanying
3 // file COPYING or http://www.opensource.org/licenses/mit-license.php.
4
5 #include <chainparams.h>
6 #include <key.h>
7 #include <key_io.h>
8 #include <outputtype.h>
9 #include <policy/policy.h>
10 #include <pubkey.h>
11 #include <rpc/util.h>
12 #include <script/keyorigin.h>
13 #include <script/script.h>
14 #include <script/sign.h>
15 #include <script/signingprovider.h>
16 #include <script/solver.h>
17 #include <streams.h>
18 #include <test/fuzz/FuzzedDataProvider.h>
19 #include <test/fuzz/fuzz.h>
20 #include <test/fuzz/util.h>
21 #include <test/util/random.h>
22 #include <util/chaintype.h>
23 #include <util/strencodings.h>
24
25 #include <array>
26 #include <cassert>
27 #include <cstddef>
28 #include <cstdint>
29 #include <numeric>
30 #include <optional>
31 #include <string>
32 #include <vector>
33
34 void initialize_key()
35 {
36 static ECC_Context ecc_context{};
37 SelectParams(ChainType::REGTEST);
38 }
39
40 FUZZ_TARGET(key, .init = initialize_key)
41 {
42 SeedRandomStateForTest(SeedRand::ZEROS);
43 const CKey key = [&] {
44 CKey k;
45 k.Set(buffer.begin(), buffer.end(), true);
46 return k;
47 }();
48 if (!key.IsValid()) {
49 return;
50 }
51
52 {
53 assert(key.begin() + key.size() == key.end());
54 assert(key.IsCompressed());
55 assert(key.size() == 32);
56 assert(DecodeSecret(EncodeSecret(key)) == key);
57 }
58
59 {
60 CKey invalid_key;
61 assert(!(invalid_key == key));
62 assert(!invalid_key.IsCompressed());
63 assert(!invalid_key.IsValid());
64 assert(invalid_key.size() == 0);
65 }
66
67 {
68 CKey uncompressed_key;
69 uncompressed_key.Set(buffer.begin(), buffer.end(), false);
70 assert(!(uncompressed_key == key));
71 assert(!uncompressed_key.IsCompressed());
72 assert(key.size() == 32);
73 assert(uncompressed_key.begin() + uncompressed_key.size() == uncompressed_key.end());
74 assert(uncompressed_key.IsValid());
75 }
76
77 {
78 CKey copied_key;
79 copied_key.Set(key.begin(), key.end(), key.IsCompressed());
80 assert(copied_key == key);
81 }
82
83 const uint256 random_uint256 = Hash(buffer);
84
85 {
86 CKey child_key;
87 ChainCode child_chaincode;
88 const bool ok = key.Derive(child_key, child_chaincode, 0, random_uint256);
89 assert(ok);
90 assert(child_key.IsValid());
91 assert(!(child_key == key));
92 assert(child_chaincode != random_uint256);
93 }
94
95 const CPubKey pubkey = key.GetPubKey();
96
97 {
98 assert(pubkey.size() == 33);
99 assert(key.VerifyPubKey(pubkey));
100 assert(pubkey.GetHash() != random_uint256);
101 assert(pubkey.begin() + pubkey.size() == pubkey.end());
102 assert(pubkey.data() == pubkey.begin());
103 assert(pubkey.IsCompressed());
104 assert(pubkey.IsValid());
105 assert(pubkey.IsFullyValid());
106 assert(HexToPubKey(HexStr(pubkey)) == pubkey);
107 assert(GetAllDestinationsForKey(pubkey).size() == 3);
108 }
109
110 {
111 DataStream data_stream{};
112 pubkey.Serialize(data_stream);
113
114 CPubKey pubkey_deserialized;
115 pubkey_deserialized.Unserialize(data_stream);
116 assert(pubkey_deserialized == pubkey);
117 }
118
119 {
120 const CScript tx_pubkey_script = GetScriptForRawPubKey(pubkey);
121 assert(!tx_pubkey_script.IsPayToScriptHash());
122 assert(!tx_pubkey_script.IsPayToWitnessScriptHash());
123 assert(!tx_pubkey_script.IsPushOnly());
124 assert(!tx_pubkey_script.IsUnspendable());
125 assert(tx_pubkey_script.HasValidOps());
126 assert(tx_pubkey_script.size() == 35);
127
128 const CScript tx_multisig_script = GetScriptForMultisig(1, {pubkey});
129 assert(!tx_multisig_script.IsPayToScriptHash());
130 assert(!tx_multisig_script.IsPayToWitnessScriptHash());
131 assert(!tx_multisig_script.IsPushOnly());
132 assert(!tx_multisig_script.IsUnspendable());
133 assert(tx_multisig_script.HasValidOps());
134 assert(tx_multisig_script.size() == 37);
135
136 FillableSigningProvider fillable_signing_provider;
137 assert(!IsSegWitOutput(fillable_signing_provider, tx_pubkey_script));
138 assert(!IsSegWitOutput(fillable_signing_provider, tx_multisig_script));
139 assert(fillable_signing_provider.GetKeys().size() == 0);
140 assert(!fillable_signing_provider.HaveKey(pubkey.GetID()));
141
142 const bool ok_add_key = fillable_signing_provider.AddKey(key);
143 assert(ok_add_key);
144 assert(fillable_signing_provider.HaveKey(pubkey.GetID()));
145
146 FillableSigningProvider fillable_signing_provider_pub;
147 assert(!fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
148
149 const bool ok_add_key_pubkey = fillable_signing_provider_pub.AddKeyPubKey(key, pubkey);
150 assert(ok_add_key_pubkey);
151 assert(fillable_signing_provider_pub.HaveKey(pubkey.GetID()));
152
153 TxoutType which_type_tx_pubkey;
154 const bool is_standard_tx_pubkey = IsStandard(tx_pubkey_script, std::nullopt, which_type_tx_pubkey);
155 assert(is_standard_tx_pubkey);
156 assert(which_type_tx_pubkey == TxoutType::PUBKEY);
157
158 TxoutType which_type_tx_multisig;
159 const bool is_standard_tx_multisig = IsStandard(tx_multisig_script, std::nullopt, which_type_tx_multisig);
160 assert(is_standard_tx_multisig);
161 assert(which_type_tx_multisig == TxoutType::MULTISIG);
162
163 std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
164 const TxoutType outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
165 assert(outtype_tx_pubkey == TxoutType::PUBKEY);
166 assert(v_solutions_ret_tx_pubkey.size() == 1);
167 assert(v_solutions_ret_tx_pubkey[0].size() == 33);
168
169 std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
170 const TxoutType outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
171 assert(outtype_tx_multisig == TxoutType::MULTISIG);
172 assert(v_solutions_ret_tx_multisig.size() == 3);
173 assert(v_solutions_ret_tx_multisig[0].size() == 1);
174 assert(v_solutions_ret_tx_multisig[1].size() == 33);
175 assert(v_solutions_ret_tx_multisig[2].size() == 1);
176
177 OutputType output_type{};
178 const CTxDestination tx_destination = GetDestinationForKey(pubkey, output_type);
179 assert(output_type == OutputType::LEGACY);
180 assert(IsValidDestination(tx_destination));
181 assert(PKHash{pubkey} == *std::get_if<PKHash>(&tx_destination));
182
183 const CScript script_for_destination = GetScriptForDestination(tx_destination);
184 assert(script_for_destination.size() == 25);
185
186 const std::string destination_address = EncodeDestination(tx_destination);
187 assert(DecodeDestination(destination_address) == tx_destination);
188
189 const CPubKey pubkey_from_address_string = AddrToPubKey(fillable_signing_provider, destination_address);
190 assert(pubkey_from_address_string == pubkey);
191
192 CKeyID key_id = pubkey.GetID();
193 assert(!key_id.IsNull());
194 assert(key_id == CKeyID{key_id});
195 assert(key_id == GetKeyForDestination(fillable_signing_provider, tx_destination));
196
197 CPubKey pubkey_out;
198 const bool ok_get_pubkey = fillable_signing_provider.GetPubKey(key_id, pubkey_out);
199 assert(ok_get_pubkey);
200
201 CKey key_out;
202 const bool ok_get_key = fillable_signing_provider.GetKey(key_id, key_out);
203 assert(ok_get_key);
204 assert(fillable_signing_provider.GetKeys().size() == 1);
205 assert(fillable_signing_provider.HaveKey(key_id));
206
207 KeyOriginInfo key_origin_info;
208 const bool ok_get_key_origin = fillable_signing_provider.GetKeyOrigin(key_id, key_origin_info);
209 assert(!ok_get_key_origin);
210 }
211
212 {
213 const std::vector<unsigned char> vch_pubkey{pubkey.begin(), pubkey.end()};
214 assert(CPubKey::ValidSize(vch_pubkey));
215 assert(!CPubKey::ValidSize({pubkey.begin(), pubkey.begin() + pubkey.size() - 1}));
216
217 const CPubKey pubkey_ctor_1{vch_pubkey};
218 assert(pubkey == pubkey_ctor_1);
219
220 const CPubKey pubkey_ctor_2{vch_pubkey.begin(), vch_pubkey.end()};
221 assert(pubkey == pubkey_ctor_2);
222
223 CPubKey pubkey_set;
224 pubkey_set.Set(vch_pubkey.begin(), vch_pubkey.end());
225 assert(pubkey == pubkey_set);
226 }
227
228 {
229 const CPubKey invalid_pubkey{};
230 assert(!invalid_pubkey.IsValid());
231 assert(!invalid_pubkey.IsFullyValid());
232 assert(!(pubkey == invalid_pubkey));
233 assert(pubkey != invalid_pubkey);
234 assert(pubkey < invalid_pubkey);
235 }
236
237 {
238 // Cover CPubKey's operator[](unsigned int pos)
239 unsigned int sum = 0;
240 for (size_t i = 0; i < pubkey.size(); ++i) {
241 sum += pubkey[i];
242 }
243 assert(std::accumulate(pubkey.begin(), pubkey.end(), 0U) == sum);
244 }
245
246 {
247 CPubKey decompressed_pubkey = pubkey;
248 assert(decompressed_pubkey.IsCompressed());
249
250 const bool ok = decompressed_pubkey.Decompress();
251 assert(ok);
252 assert(!decompressed_pubkey.IsCompressed());
253 assert(decompressed_pubkey.size() == 65);
254 }
255
256 {
257 std::vector<unsigned char> vch_sig;
258 const bool ok = key.Sign(random_uint256, vch_sig, false);
259 assert(ok);
260 assert(pubkey.Verify(random_uint256, vch_sig));
261 assert(CPubKey::CheckLowS(vch_sig));
262
263 const std::vector<unsigned char> vch_invalid_sig{vch_sig.begin(), vch_sig.begin() + vch_sig.size() - 1};
264 assert(!pubkey.Verify(random_uint256, vch_invalid_sig));
265 assert(!CPubKey::CheckLowS(vch_invalid_sig));
266 }
267
268 {
269 std::vector<unsigned char> vch_compact_sig;
270 const bool ok_sign_compact = key.SignCompact(random_uint256, vch_compact_sig);
271 assert(ok_sign_compact);
272
273 CPubKey recover_pubkey;
274 const bool ok_recover_compact = recover_pubkey.RecoverCompact(random_uint256, vch_compact_sig);
275 assert(ok_recover_compact);
276 assert(recover_pubkey == pubkey);
277 }
278
279 {
280 CPubKey child_pubkey;
281 ChainCode child_chaincode;
282 const bool ok = pubkey.Derive(child_pubkey, child_chaincode, 0, random_uint256);
283 assert(ok);
284 assert(child_pubkey != pubkey);
285 assert(child_pubkey.IsCompressed());
286 assert(child_pubkey.IsFullyValid());
287 assert(child_pubkey.IsValid());
288 assert(child_pubkey.size() == 33);
289 assert(child_chaincode != random_uint256);
290 }
291
292 const CPrivKey priv_key = key.GetPrivKey();
293
294 {
295 for (const bool skip_check : {true, false}) {
296 CKey loaded_key;
297 const bool ok = loaded_key.Load(priv_key, pubkey, skip_check);
298 assert(ok);
299 assert(key == loaded_key);
300 }
301 }
302 }
303
304 FUZZ_TARGET(ellswift_roundtrip, .init = initialize_key)
305 {
306 FuzzedDataProvider fdp{buffer.data(), buffer.size()};
307
308 CKey key = ConsumePrivateKey(fdp, /*compressed=*/true);
309 if (!key.IsValid()) return;
310
311 auto ent32 = fdp.ConsumeBytes<std::byte>(32);
312 ent32.resize(32);
313
314 auto encoded_ellswift = key.EllSwiftCreate(ent32);
315 auto decoded_pubkey = encoded_ellswift.Decode();
316
317 uint256 hash{ConsumeUInt256(fdp)};
318 std::vector<unsigned char> sig;
319 key.Sign(hash, sig);
320 assert(decoded_pubkey.Verify(hash, sig));
321 }
322
323 FUZZ_TARGET(bip324_ecdh, .init = initialize_key)
324 {
325 FuzzedDataProvider fdp{buffer.data(), buffer.size()};
326
327 // We generate private key, k1.
328 CKey k1 = ConsumePrivateKey(fdp, /*compressed=*/true);
329 if (!k1.IsValid()) return;
330
331 // They generate private key, k2.
332 CKey k2 = ConsumePrivateKey(fdp, /*compressed=*/true);
333 if (!k2.IsValid()) return;
334
335 // We construct an ellswift encoding for our key, k1_ellswift.
336 auto ent32_1 = fdp.ConsumeBytes<std::byte>(32);
337 ent32_1.resize(32);
338 auto k1_ellswift = k1.EllSwiftCreate(ent32_1);
339
340 // They construct an ellswift encoding for their key, k2_ellswift.
341 auto ent32_2 = fdp.ConsumeBytes<std::byte>(32);
342 ent32_2.resize(32);
343 auto k2_ellswift = k2.EllSwiftCreate(ent32_2);
344
345 // They construct another (possibly distinct) ellswift encoding for their key, k2_ellswift_bad.
346 auto ent32_2_bad = fdp.ConsumeBytes<std::byte>(32);
347 ent32_2_bad.resize(32);
348 auto k2_ellswift_bad = k2.EllSwiftCreate(ent32_2_bad);
349 assert((ent32_2_bad == ent32_2) == (k2_ellswift_bad == k2_ellswift));
350
351 // Determine who is who.
352 bool initiating = fdp.ConsumeBool();
353
354 // We compute our shared secret using our key and their public key.
355 auto ecdh_secret_1 = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, initiating);
356 // They compute their shared secret using their key and our public key.
357 auto ecdh_secret_2 = k2.ComputeBIP324ECDHSecret(k1_ellswift, k2_ellswift, !initiating);
358 // Those must match, as everyone is behaving correctly.
359 assert(ecdh_secret_1 == ecdh_secret_2);
360
361 if (k1_ellswift != k2_ellswift) {
362 // Unless the two keys are exactly identical, acting as the wrong party breaks things.
363 auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift, k1_ellswift, !initiating);
364 assert(ecdh_secret_bad != ecdh_secret_1);
365 }
366
367 if (k2_ellswift_bad != k2_ellswift) {
368 // Unless both encodings created by them are identical, using the second one breaks things.
369 auto ecdh_secret_bad = k1.ComputeBIP324ECDHSecret(k2_ellswift_bad, k1_ellswift, initiating);
370 assert(ecdh_secret_bad != ecdh_secret_1);
371 }
372 }
373