Line | Count | Source |
1 | | // Copyright (c) 2009-2010 Satoshi Nakamoto |
2 | | // Copyright (c) 2009-present The Bitcoin Core developers |
3 | | // Copyright (c) 2017 The Zcash developers |
4 | | // Distributed under the MIT software license, see the accompanying |
5 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
6 | | |
7 | | #ifndef BITCOIN_KEY_H |
8 | | #define BITCOIN_KEY_H |
9 | | |
10 | | #include <attributes.h> |
11 | | #include <pubkey.h> |
12 | | #include <script/keyorigin.h> |
13 | | #include <serialize.h> |
14 | | #include <support/allocators/secure.h> |
15 | | #include <support/cleanse.h> |
16 | | #include <uint256.h> |
17 | | |
18 | | #include <array> |
19 | | #include <cassert> |
20 | | #include <optional> |
21 | | #include <span> |
22 | | #include <stdexcept> |
23 | | #include <utility> |
24 | | #include <vector> |
25 | | |
26 | | struct secp256k1_context_struct; |
27 | | typedef struct secp256k1_context_struct secp256k1_context; |
28 | | struct secp256k1_keypair; |
29 | | |
30 | | /** |
31 | | * CPrivKey is a serialized private key, with all parameters included |
32 | | * (SIZE bytes) |
33 | | */ |
34 | | typedef std::vector<unsigned char, secure_allocator<unsigned char> > CPrivKey; |
35 | | |
36 | | /** Size of ECDH shared secrets. */ |
37 | | inline constexpr size_t ECDH_SECRET_SIZE = CSHA256::OUTPUT_SIZE; |
38 | | |
39 | | // Used to represent ECDH shared secret (ECDH_SECRET_SIZE bytes) |
40 | | using ECDHSecret = std::array<std::byte, ECDH_SECRET_SIZE>; |
41 | | |
42 | | class KeyPair; |
43 | | |
44 | | /** An encapsulated private key. */ |
45 | | class CKey |
46 | | { |
47 | | public: |
48 | | /** |
49 | | * secp256k1: |
50 | | */ |
51 | | static constexpr unsigned int SIZE{279}; |
52 | | static constexpr unsigned int COMPRESSED_SIZE{214}; |
53 | | /** |
54 | | * see www.keylength.com |
55 | | * script supports up to 75 for single byte push |
56 | | */ |
57 | | static_assert( |
58 | | SIZE >= COMPRESSED_SIZE, |
59 | | "COMPRESSED_SIZE is larger than SIZE"); |
60 | | |
61 | | private: |
62 | | /** Internal data container for private key material. */ |
63 | | using KeyType = std::array<unsigned char, 32>; |
64 | | |
65 | | //! Whether the public key corresponding to this private key is (to be) compressed. |
66 | | bool fCompressed{false}; |
67 | | |
68 | | //! The actual byte data. nullptr for invalid keys. |
69 | | secure_unique_ptr<KeyType> keydata; |
70 | | |
71 | | //! Check whether the 32-byte array pointed to by vch is valid keydata. |
72 | | bool static Check(const unsigned char* vch); |
73 | | |
74 | | void MakeKeyData() |
75 | 498k | { |
76 | 498k | if (!keydata) keydata = make_secure_unique<KeyType>(); |
77 | 498k | } |
78 | | |
79 | | void ClearKeyData() |
80 | 2 | { |
81 | 2 | keydata.reset(); |
82 | 2 | } |
83 | | |
84 | | public: |
85 | 433k | CKey() noexcept = default; |
86 | 8.44k | CKey(CKey&&) noexcept = default; |
87 | 1.39k | CKey& operator=(CKey&&) noexcept = default; |
88 | | |
89 | | CKey& operator=(const CKey& other) |
90 | 343k | { |
91 | 343k | if (this != &other) { |
92 | 343k | if (other.keydata) { |
93 | 343k | MakeKeyData(); |
94 | 343k | *keydata = *other.keydata; |
95 | 18.4E | } else { |
96 | 18.4E | ClearKeyData(); |
97 | 18.4E | } |
98 | 343k | fCompressed = other.fCompressed; |
99 | 343k | } |
100 | 343k | return *this; |
101 | 343k | } |
102 | | |
103 | 116k | CKey(const CKey& other) { *this = other; } |
104 | | |
105 | | friend bool operator==(const CKey& a, const CKey& b) |
106 | 273 | { |
107 | 273 | return a.fCompressed == b.fCompressed && |
108 | 273 | a.size() == b.size() && |
109 | 273 | memcmp(a.data(), b.data(), a.size()) == 0; |
110 | 273 | } |
111 | | |
112 | | //! Initialize using begin and end iterators to byte data. |
113 | | template <typename T> |
114 | | void Set(const T pbegin, const T pend, bool fCompressedIn) |
115 | 150k | { |
116 | 150k | if (size_t(pend - pbegin) != std::tuple_size_v<KeyType>) { |
117 | 0 | ClearKeyData(); |
118 | 150k | } else if (Check(UCharCast(&pbegin[0]))) { |
119 | 150k | MakeKeyData(); |
120 | 150k | memcpy(keydata->data(), (unsigned char*)&pbegin[0], keydata->size()); |
121 | 150k | fCompressed = fCompressedIn; |
122 | 150k | } else { |
123 | 2 | ClearKeyData(); |
124 | 2 | } |
125 | 150k | } void CKey::Set<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, secure_allocator<unsigned char>>>>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, secure_allocator<unsigned char>>>, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, secure_allocator<unsigned char>>>, bool) Line | Count | Source | 115 | 4.84k | { | 116 | 4.84k | if (size_t(pend - pbegin) != std::tuple_size_v<KeyType>) { | 117 | 0 | ClearKeyData(); | 118 | 4.84k | } else if (Check(UCharCast(&pbegin[0]))) { | 119 | 4.84k | MakeKeyData(); | 120 | 4.84k | memcpy(keydata->data(), (unsigned char*)&pbegin[0], keydata->size()); | 121 | 4.84k | fCompressed = fCompressedIn; | 122 | 4.84k | } else { | 123 | 2 | ClearKeyData(); | 124 | 2 | } | 125 | 4.84k | } |
void CKey::Set<__gnu_cxx::__normal_iterator<unsigned char const*, std::vector<unsigned char, std::allocator<unsigned char>>>>(__gnu_cxx::__normal_iterator<unsigned char const*, std::vector<unsigned char, std::allocator<unsigned char>>>, __gnu_cxx::__normal_iterator<unsigned char const*, std::vector<unsigned char, std::allocator<unsigned char>>>, bool) Line | Count | Source | 115 | 7 | { | 116 | 7 | if (size_t(pend - pbegin) != std::tuple_size_v<KeyType>) { | 117 | 0 | ClearKeyData(); | 118 | 7 | } else if (Check(UCharCast(&pbegin[0]))) { | 119 | 7 | MakeKeyData(); | 120 | 7 | memcpy(keydata->data(), (unsigned char*)&pbegin[0], keydata->size()); | 121 | 7 | fCompressed = fCompressedIn; | 122 | 7 | } else { | 123 | 0 | ClearKeyData(); | 124 | 0 | } | 125 | 7 | } |
void CKey::Set<__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char>>>>(__gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char>>>, __gnu_cxx::__normal_iterator<unsigned char*, std::vector<unsigned char, std::allocator<unsigned char>>>, bool) Line | Count | Source | 115 | 1.65k | { | 116 | 1.65k | if (size_t(pend - pbegin) != std::tuple_size_v<KeyType>) { | 117 | 0 | ClearKeyData(); | 118 | 1.65k | } else if (Check(UCharCast(&pbegin[0]))) { | 119 | 1.65k | MakeKeyData(); | 120 | 1.65k | memcpy(keydata->data(), (unsigned char*)&pbegin[0], keydata->size()); | 121 | 1.65k | fCompressed = fCompressedIn; | 122 | 1.65k | } else { | 123 | 0 | ClearKeyData(); | 124 | 0 | } | 125 | 1.65k | } |
void CKey::Set<unsigned char*>(unsigned char*, unsigned char*, bool) Line | Count | Source | 115 | 1.06k | { | 116 | 1.06k | if (size_t(pend - pbegin) != std::tuple_size_v<KeyType>) { | 117 | 0 | ClearKeyData(); | 118 | 1.06k | } else if (Check(UCharCast(&pbegin[0]))) { | 119 | 1.06k | MakeKeyData(); | 120 | 1.06k | memcpy(keydata->data(), (unsigned char*)&pbegin[0], keydata->size()); | 121 | 1.06k | fCompressed = fCompressedIn; | 122 | 1.06k | } else { | 123 | 0 | ClearKeyData(); | 124 | 0 | } | 125 | 1.06k | } |
void CKey::Set<unsigned char const*>(unsigned char const*, unsigned char const*, bool) Line | Count | Source | 115 | 91 | { | 116 | 91 | if (size_t(pend - pbegin) != std::tuple_size_v<KeyType>) { | 117 | 0 | ClearKeyData(); | 118 | 91 | } else if (Check(UCharCast(&pbegin[0]))) { | 119 | 91 | MakeKeyData(); | 120 | 91 | memcpy(keydata->data(), (unsigned char*)&pbegin[0], keydata->size()); | 121 | 91 | fCompressed = fCompressedIn; | 122 | 91 | } else { | 123 | 0 | ClearKeyData(); | 124 | 0 | } | 125 | 91 | } |
void CKey::Set<std::byte const*>(std::byte const*, std::byte const*, bool) Line | Count | Source | 115 | 143k | { | 116 | 143k | if (size_t(pend - pbegin) != std::tuple_size_v<KeyType>) { | 117 | 0 | ClearKeyData(); | 118 | 143k | } else if (Check(UCharCast(&pbegin[0]))) { | 119 | 143k | MakeKeyData(); | 120 | 143k | memcpy(keydata->data(), (unsigned char*)&pbegin[0], keydata->size()); | 121 | 143k | fCompressed = fCompressedIn; | 122 | 143k | } else { | 123 | 0 | ClearKeyData(); | 124 | 0 | } | 125 | 143k | } |
|
126 | | |
127 | | //! Simple read-only vector-like interface. |
128 | 111k | unsigned int size() const { return keydata ? keydata->size() : 0; } |
129 | 964k | const std::byte* data() const { return keydata ? reinterpret_cast<const std::byte*>(keydata->data()) : nullptr; } |
130 | 959k | const std::byte* begin() const { return data(); } |
131 | 747 | const std::byte* end() const { return data() + size(); } |
132 | | |
133 | | //! Check whether this private key is valid. |
134 | 204k | bool IsValid() const { return !!keydata; } |
135 | | |
136 | | //! Check whether the public key corresponding to this private key is (to be) compressed. |
137 | 154k | bool IsCompressed() const { return fCompressed; } |
138 | | |
139 | | //! Generate a new private key using a cryptographic PRNG. |
140 | | void MakeNewKey(bool fCompressed); |
141 | | |
142 | | /** |
143 | | * Convert the private key to a CPrivKey (serialized OpenSSL private key data). |
144 | | * This is expensive. |
145 | | */ |
146 | | CPrivKey GetPrivKey() const; |
147 | | |
148 | | /** |
149 | | * Compute the public key from a private key. |
150 | | * This is expensive. |
151 | | */ |
152 | | CPubKey GetPubKey() const; |
153 | | |
154 | | /** |
155 | | * Create a DER-serialized signature. |
156 | | * The test_case parameter tweaks the deterministic nonce. |
157 | | */ |
158 | | bool Sign(const uint256& hash, std::vector<unsigned char>& vchSig, bool grind = true, uint32_t test_case = 0) const; |
159 | | |
160 | | /** |
161 | | * Create a compact signature (65 bytes), which allows reconstructing the used public key. |
162 | | * The format is one header byte, followed by two times 32 bytes for the serialized r and s values. |
163 | | * The header byte: 0x1B = first key with even y, 0x1C = first key with odd y, |
164 | | * 0x1D = second key with even y, 0x1E = second key with odd y, |
165 | | * add 0x04 for compressed keys. |
166 | | */ |
167 | | bool SignCompact(const uint256& hash, std::vector<unsigned char>& vchSig) const; |
168 | | |
169 | | /** |
170 | | * Create a BIP-340 Schnorr signature, for the xonly-pubkey corresponding to *this, |
171 | | * optionally tweaked by *merkle_root. Additional nonce entropy is provided through |
172 | | * aux. |
173 | | * |
174 | | * merkle_root is used to optionally perform tweaking of the private key, as specified |
175 | | * in BIP341: |
176 | | * - If merkle_root == nullptr: no tweaking is done, sign with key directly (this is |
177 | | * used for signatures in BIP342 script). |
178 | | * - If merkle_root->IsNull(): sign with key + H_TapTweak(pubkey) (this is used for |
179 | | * key path spending when no scripts are present). |
180 | | * - Otherwise: sign with key + H_TapTweak(pubkey || *merkle_root) |
181 | | * (this is used for key path spending, with specific |
182 | | * Merkle root of the script tree). |
183 | | */ |
184 | | bool SignSchnorr(const uint256& hash, std::span<unsigned char> sig, const uint256* merkle_root, const uint256& aux) const; |
185 | | |
186 | | //! Derive BIP32 child key. |
187 | | [[nodiscard]] bool Derive(CKey& keyChild, ChainCode &ccChild, unsigned int nChild, const ChainCode& cc) const; |
188 | | |
189 | | /** |
190 | | * Verify thoroughly whether a private key and a public key match. |
191 | | * This is done using a different mechanism than just regenerating it. |
192 | | */ |
193 | | bool VerifyPubKey(const CPubKey& vchPubKey) const; |
194 | | |
195 | | //! Load private key and check that public key matches. |
196 | | bool Load(const CPrivKey& privkey, const CPubKey& vchPubKey, bool fSkipCheck); |
197 | | |
198 | | /** Create an ellswift-encoded public key for this key, with specified entropy. |
199 | | * |
200 | | * entropy must be a 32-byte span with additional entropy to use in the encoding. Every |
201 | | * public key has ~2^256 different encodings, and this function will deterministically pick |
202 | | * one of them, based on entropy. Note that even without truly random entropy, the |
203 | | * resulting encoding will be indistinguishable from uniform to any adversary who does not |
204 | | * know the private key (because the private key itself is always used as entropy as well). |
205 | | */ |
206 | | EllSwiftPubKey EllSwiftCreate(std::span<const std::byte> entropy) const; |
207 | | |
208 | | /** Compute a BIP324-style ECDH shared secret. |
209 | | * |
210 | | * - their_ellswift: EllSwiftPubKey that was received from the other side. |
211 | | * - our_ellswift: EllSwiftPubKey that was sent to the other side (must have been generated |
212 | | * from *this using EllSwiftCreate()). |
213 | | * - initiating: whether we are the initiating party (true) or responding party (false). |
214 | | */ |
215 | | ECDHSecret ComputeBIP324ECDHSecret(const EllSwiftPubKey& their_ellswift, |
216 | | const EllSwiftPubKey& our_ellswift, |
217 | | bool initiating) const; |
218 | | /** Compute a KeyPair |
219 | | * |
220 | | * Wraps a `secp256k1_keypair` type. |
221 | | * |
222 | | * `merkle_root` is used to optionally perform tweaking of |
223 | | * the internal key, as specified in BIP341: |
224 | | * |
225 | | * - If merkle_root == nullptr: no tweaking is done, use the internal key directly (this is |
226 | | * used for signatures in BIP342 script). |
227 | | * - If merkle_root->IsNull(): tweak the internal key with H_TapTweak(pubkey) (this is used for |
228 | | * key path spending when no scripts are present). |
229 | | * - Otherwise: tweak the internal key with H_TapTweak(pubkey || *merkle_root) |
230 | | * (this is used for key path spending with the |
231 | | * Merkle root of the script tree). |
232 | | */ |
233 | | KeyPair ComputeKeyPair(const uint256* merkle_root) const; |
234 | | }; |
235 | | |
236 | | CKey GenerateRandomKey(bool compressed = true) noexcept; |
237 | | |
238 | | struct CExtKey { |
239 | | unsigned char nDepth; |
240 | | KeyFingerprint fingerprint; |
241 | | unsigned int nChild; |
242 | | ChainCode chaincode; |
243 | | CKey key; |
244 | | |
245 | | friend bool operator==(const CExtKey& a, const CExtKey& b) |
246 | 36 | { |
247 | 36 | return a.nDepth == b.nDepth && |
248 | 36 | a.fingerprint == b.fingerprint && |
249 | 36 | a.nChild == b.nChild && |
250 | 36 | a.chaincode == b.chaincode && |
251 | 36 | a.key == b.key; |
252 | 36 | } |
253 | | |
254 | 132k | CExtKey() = default; |
255 | 115 | CExtKey(const CExtPubKey& xpub, const CKey& key_in) : nDepth(xpub.nDepth), fingerprint(xpub.fingerprint), nChild(xpub.nChild), chaincode(xpub.chaincode), key(key_in) {} |
256 | | |
257 | | KeyFingerprint id_key_fingerprint() const |
258 | 143k | { |
259 | 143k | return key.GetPubKey().GetID().fingerprint(); |
260 | 143k | } |
261 | | |
262 | | //! BIP32 serialization without the version bytes (BIP32_EXTKEY_SIZE bytes) |
263 | | template <typename Stream> |
264 | | void Serialize(Stream& s) const |
265 | 1.10k | { |
266 | 1.10k | assert(key.size() == 32); |
267 | 1.10k | s << nDepth << fingerprint << Using<BigEndianFormatter<4>>(nChild) << chaincode << uint8_t{0} << std::span{key.data(), key.size()}; |
268 | 1.10k | } void CExtKey::Serialize<DataStream>(DataStream&) const Line | Count | Source | 265 | 18 | { | 266 | 18 | assert(key.size() == 32); | 267 | 18 | s << nDepth << fingerprint << Using<BigEndianFormatter<4>>(nChild) << chaincode << uint8_t{0} << std::span{key.data(), key.size()}; | 268 | 18 | } |
void CExtKey::Serialize<VectorWriter>(VectorWriter&) const Line | Count | Source | 265 | 1.09k | { | 266 | 1.09k | assert(key.size() == 32); | 267 | 1.09k | s << nDepth << fingerprint << Using<BigEndianFormatter<4>>(nChild) << chaincode << uint8_t{0} << std::span{key.data(), key.size()}; | 268 | 1.09k | } |
|
269 | | template <typename Stream> |
270 | | void Unserialize(Stream& s) |
271 | 1.03k | { |
272 | 1.03k | uint8_t key_prefix; |
273 | 1.03k | std::vector<unsigned char, secure_allocator<unsigned char>> ser_key(32); |
274 | 1.03k | s >> nDepth >> fingerprint >> Using<BigEndianFormatter<4>>(nChild) >> chaincode >> key_prefix >> std::span{ser_key}; |
275 | 1.03k | key.Set(ser_key.begin(), ser_key.end(), true); |
276 | 1.03k | if ((nDepth == 0 && (nChild != 0 || fingerprint != KeyFingerprint{})) || key_prefix != 0) key = CKey(); |
277 | 1.03k | } void CExtKey::Unserialize<DataStream>(DataStream&) Line | Count | Source | 271 | 95 | { | 272 | 95 | uint8_t key_prefix; | 273 | 95 | std::vector<unsigned char, secure_allocator<unsigned char>> ser_key(32); | 274 | 95 | s >> nDepth >> fingerprint >> Using<BigEndianFormatter<4>>(nChild) >> chaincode >> key_prefix >> std::span{ser_key}; | 275 | 95 | key.Set(ser_key.begin(), ser_key.end(), true); | 276 | 95 | if ((nDepth == 0 && (nChild != 0 || fingerprint != KeyFingerprint{})) || key_prefix != 0) key = CKey(); | 277 | 95 | } |
void CExtKey::Unserialize<SpanReader>(SpanReader&) Line | Count | Source | 271 | 937 | { | 272 | 937 | uint8_t key_prefix; | 273 | 937 | std::vector<unsigned char, secure_allocator<unsigned char>> ser_key(32); | 274 | 937 | s >> nDepth >> fingerprint >> Using<BigEndianFormatter<4>>(nChild) >> chaincode >> key_prefix >> std::span{ser_key}; | 275 | 937 | key.Set(ser_key.begin(), ser_key.end(), true); | 276 | 937 | if ((nDepth == 0 && (nChild != 0 || fingerprint != KeyFingerprint{})) || key_prefix != 0) key = CKey(); | 277 | 937 | } |
|
278 | | [[nodiscard]] bool Derive(CExtKey& out, unsigned int nChild) const; |
279 | | CExtPubKey Neuter() const; |
280 | | void SetSeed(std::span<const std::byte> seed); |
281 | | }; |
282 | | |
283 | | //! Get extended key and origin info for a given path |
284 | | //! @param[in] ext_key The extended private key to derive from |
285 | | //! @param[in] path The BIP 32 path |
286 | | //! @return the resulting extended private key and origin info |
287 | | std::optional<std::pair<CExtKey, KeyOriginInfo>> DeriveExtKey(const CExtKey& ext_key, const std::vector<uint32_t>& path); |
288 | | |
289 | | /** KeyPair |
290 | | * |
291 | | * Wraps a `secp256k1_keypair` type, an opaque data structure for holding a secret and public key. |
292 | | * This is intended for BIP340 keys and allows us to easily determine if the secret key needs to |
293 | | * be negated by checking the parity of the public key. This class primarily intended for passing |
294 | | * secret keys to libsecp256k1 functions expecting a `secp256k1_keypair`. For all other cases, |
295 | | * CKey should be preferred. |
296 | | * |
297 | | * A KeyPair can be created from a CKey with an optional merkle_root tweak (per BIP342). See |
298 | | * CKey::ComputeKeyPair for more details. |
299 | | */ |
300 | | class KeyPair |
301 | | { |
302 | | public: |
303 | | KeyPair() noexcept = default; |
304 | 11 | KeyPair(KeyPair&&) noexcept = default; |
305 | | KeyPair& operator=(KeyPair&&) noexcept = default; |
306 | | KeyPair& operator=(const KeyPair& other) |
307 | 0 | { |
308 | 0 | if (this != &other) { |
309 | 0 | if (other.m_keypair) { |
310 | 0 | MakeKeyPairData(); |
311 | 0 | *m_keypair = *other.m_keypair; |
312 | 0 | } else { |
313 | 0 | ClearKeyPairData(); |
314 | 0 | } |
315 | 0 | } |
316 | 0 | return *this; |
317 | 0 | } |
318 | | |
319 | 0 | KeyPair(const KeyPair& other) { *this = other; } |
320 | | |
321 | | friend KeyPair CKey::ComputeKeyPair(const uint256* merkle_root) const; |
322 | | [[nodiscard]] bool SignSchnorr(const uint256& hash, std::span<unsigned char> sig, const uint256& aux) const; |
323 | | |
324 | | //! Pointer to this KeyPair's internal `secp256k1_keypair` data or nullptr if invalid. |
325 | | const secp256k1_keypair* GetSecpKeypair() const LIFETIMEBOUND |
326 | 9 | { |
327 | 9 | return IsValid() ? reinterpret_cast<const secp256k1_keypair*>(m_keypair->data()) : nullptr; |
328 | 9 | } |
329 | | |
330 | | //! Check whether this keypair is valid. |
331 | 1.44k | bool IsValid() const { return !!m_keypair; } |
332 | | |
333 | | private: |
334 | | KeyPair(const CKey& key, const uint256* merkle_root); |
335 | | |
336 | | using KeyType = std::array<unsigned char, 96>; |
337 | | secure_unique_ptr<KeyType> m_keypair; |
338 | | |
339 | | void MakeKeyPairData() |
340 | 1.44k | { |
341 | 1.44k | if (!m_keypair) m_keypair = make_secure_unique<KeyType>(); |
342 | 1.44k | } |
343 | | |
344 | | void ClearKeyPairData() |
345 | 0 | { |
346 | 0 | m_keypair.reset(); |
347 | 0 | } |
348 | | }; |
349 | | |
350 | | /** Check that required EC support is available at runtime. */ |
351 | | bool ECC_InitSanityCheck(); |
352 | | |
353 | | /** Access the secp256k1 context used for signing and MuSig2 nonce generation. */ |
354 | | secp256k1_context* GetSecp256k1SignContext(); |
355 | | |
356 | | /** |
357 | | * RAII class initializing and deinitializing global state for elliptic curve support. |
358 | | * Only one instance may be initialized at a time. |
359 | | * |
360 | | * In the future global ECC state could be removed, and this class could contain |
361 | | * state and be passed as an argument to ECC key functions. |
362 | | */ |
363 | | class ECC_Context |
364 | | { |
365 | | public: |
366 | | ECC_Context(); |
367 | | ~ECC_Context(); |
368 | | }; |
369 | | |
370 | | #endif // BITCOIN_KEY_H |