Add a sanity check to prevent cosmic rays from flipping a bit in the generated public key, or bugs in the elliptic curve code. This is simply done by signing a (randomized) message, and verifying the result.
Add sanity check after key generation #5224
pull sipa wants to merge 2 commits into bitcoin:master from sipa:sanitygen changing 5 files +53 −5-
sipa commented at 9:19 AM on November 6, 2014: member
-
in src/key.cpp:None in 2637ba2ec5 outdated
122 | @@ -123,6 +123,20 @@ bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool lo 123 | #endif 124 | } 125 | 126 | +bool CKey::VerifyPubKey(const CPubKey& pubkey) const { 127 | + if ((pubkey.size() == 33) != fCompressed) {
laanwj commented at 9:28 AM on November 6, 2014:Maybe
if (pubkey.IsCompressed() != fCompressed) {
sipa commented at 9:51 AM on November 6, 2014:Done.
in src/key.h:None in 2637ba2ec5 outdated
135 | @@ -136,6 +136,12 @@ class CKey 136 | //! Derive BIP32 child key. 137 | bool Derive(CKey& keyChild, unsigned char ccChild[32], unsigned int nChild, const unsigned char cc[32]) const; 138 | 139 | + /** 140 | + * Verify thoroughly whether a private key and a public key match. 141 | + * This is doing using a different mechanism than just regenerating it.
laanwj commented at 9:29 AM on November 6, 2014:s/doing/done
sipa commented at 9:51 AM on November 6, 2014:Done doing the s/doing/done/.
in src/key.cpp:None in 2637ba2ec5 outdated
129 | + } 130 | + unsigned char rnd[8]; 131 | + std::string str = "Bitcoin key verification\n"; 132 | + GetRandBytes(rnd, 8); 133 | + uint256 hash; 134 | + CHash256().Write((unsigned char*)&str[0], str.size()).Write(rnd, sizeof(rnd)).Finalize((unsigned char*)&hash);
laanwj commented at 9:31 AM on November 6, 2014:please use str.data() instead of &str[0]
sipa commented at 9:51 AM on November 6, 2014:Done.
in src/key.cpp:None in 2637ba2ec5 outdated
127 | + if ((pubkey.size() == 33) != fCompressed) { 128 | + return false; 129 | + } 130 | + unsigned char rnd[8]; 131 | + std::string str = "Bitcoin key verification\n"; 132 | + GetRandBytes(rnd, 8);
laanwj commented at 9:32 AM on November 6, 2014:s/8/sizeof(rnd)/ ? (as you use that below too)
sipa commented at 9:51 AM on November 6, 2014:Done.
laanwj added the label Priority High on Nov 6, 2014laanwj added the label Wallet on Nov 6, 2014in src/key.cpp:None in 2637ba2ec5 outdated
169 | @@ -156,16 +170,10 @@ bool CKey::Load(CPrivKey &privkey, CPubKey &vchPubKey, bool fSkipCheck=false) { 170 | return false; 171 | key.GetSecretBytes(vch); 172 | #endif 173 | - fCompressed = vchPubKey.IsCompressed(); 174 | - fValid = true;
laanwj commented at 9:42 AM on November 6, 2014:fCompressed / fValid no longer set?
sipa commented at 9:51 AM on November 6, 2014:Ugh. Thankfully the tests caught this...
sipa force-pushed on Nov 6, 2014in src/key.cpp:None in baf4dfede9 outdated
122 | @@ -123,6 +123,20 @@ bool CKey::Sign(const uint256 &hash, std::vector<unsigned char>& vchSig, bool lo 123 | #endif 124 | } 125 | 126 | +bool CKey::VerifyPubKey(const CPubKey& pubkey) const { 127 | + if (pubkey.IsCompressed() != fCompressed) { 128 | + return false; 129 | + } 130 | + unsigned char rnd[8]; 131 | + std::string str = "Bitcoin key verification\n";
Diapolo commented at 7:20 PM on November 6, 2014:What use has the \n here ;)?
sipa commented at 7:21 PM on November 6, 2014:Exactly as much as the 24 character before it.
in src/key.cpp:None in baf4dfede9 outdated
127 | + if (pubkey.IsCompressed() != fCompressed) { 128 | + return false; 129 | + } 130 | + unsigned char rnd[8]; 131 | + std::string str = "Bitcoin key verification\n"; 132 | + GetRandBytes(rnd, sizeof(rnd));
Diapolo commented at 7:22 PM on November 6, 2014:As GetRandBytes() can fail and this is intended as an intensive check you should perhaps consider that.
laanwj commented at 12:32 PM on November 7, 2014:Huh - I remember changing
GetRandBytesto never fail, but stop with an assertion instead. There is no way bitcoin can handle a failing RNG gracefully. Seems that never got merged?in src/key.cpp:None in baf4dfede9 outdated
129 | + } 130 | + unsigned char rnd[8]; 131 | + std::string str = "Bitcoin key verification\n"; 132 | + GetRandBytes(rnd, sizeof(rnd)); 133 | + uint256 hash; 134 | + CHash256().Write((unsigned char*)str.data(), str.size()).Write(rnd, sizeof(rnd)).Finalize((unsigned char*)&hash);
Diapolo commented at 7:22 PM on November 6, 2014:Just asking, could (unsigned char*)str.data() be changed to just .c_str()? I guess no, because it's not unsigned ^^.
TheBlueMatt commented at 7:24 PM on November 6, 2014: memberI'd vote this wait on #5220, though concept ACK 100%.
laanwj commented at 10:35 AM on November 7, 2014: member@TheBlueMatt What rationale to wait? This seems self-contained and a useful sanity check no matter what.
gmaxwell commented at 4:56 PM on November 7, 2014: contributorDo we want to also add something in init.cpp that generates a rnadom key, verifies, and bombs out if it fails? (so that broken code is more likely to fail at startup with an explicable error message?)
Not just totaly hypotetical: right now fedora's openssl compiles, links, and starts but fails at runtime the first time something tries to actually use the EC code.
TheBlueMatt commented at 5:47 PM on November 7, 2014: member@laanwj This just leaks even more shit via sidechannels...I'd assume OpenSSL's transform-priv-to-pub does too, but I'd prefer to not add to it. At least secp256k1 tries to not leak too much.
gmaxwell commented at 8:20 PM on November 7, 2014: contributorThe non-constant time ness in signing for openssl is the r*G multply and the computation of k^-1 (which is very not constant time), but since we never publish that data it would be hard to make use of it. (E.g. you leak data about a random number, ... which might be bad if you published the resulting signature, but you don't). So, with this in mind it doesn't concern me much.
sipa force-pushed on Nov 19, 2014sipa commented at 2:41 PM on November 19, 2014: memberRebased.
gmaxwell commented at 5:05 PM on November 19, 2014: contributorIt looks like CWallet::AddKeyPubKey also needs to call VerifyPubKey so that rpc imported and newly generated keys get tested too.
sipa commented at 5:12 PM on November 19, 2014: memberDo we want to apply the check to every key loaded at startup? That's probably a significant slowdown...
gmaxwell commented at 5:16 PM on November 19, 2014: contributorI think so, just once (e.g. not every unlock). The generation of the pubkey each time is already slow, doing this basically quadruples it. Any idea how long it would take for 100k keys?
sipa commented at 5:21 PM on November 19, 2014: memberFor unecnrypted wallets we use a hash-based mechanism now; we used to re-derive the public key for every private key before, and apparently that was too slow (to @phantomcircuit, I believe).
gmaxwell commented at 5:28 PM on November 19, 2014: contributorOkay, well if that was too slow, verifying will be too slow.
sipa added this to the milestone 0.10.0 on Nov 21, 2014d0c41a7350Add sanity check after key generation
Add a sanity check to prevent cosmic rays from flipping a bit in the generated public key, or bugs in the elliptic curve code. This is simply done by signing a (randomized) message, and verifying the result.
Add key generation/verification to ECC sanity check f321d6bfffsipa force-pushed on Nov 23, 2014gmaxwell commented at 10:38 AM on November 23, 2014: contributorACK
jgarzik commented at 2:45 PM on November 23, 2014: contributorut ACK
laanwj merged this on Nov 24, 2014laanwj closed this on Nov 24, 2014laanwj referenced this in commit 6582f323f0 on Nov 24, 2014MarcoFalke locked this on Sep 8, 2021
github-metadata-mirror
This is a metadata mirror of the GitHub repository bitcoin/bitcoin. This site is not affiliated with GitHub. Content is generated from a GitHub metadata backup.
generated: 2026-04-19 09:15 UTC
This site is hosted by @0xB10C
More mirrored repositories can be found on mirror.b10c.me