libsecp256k1 is the library that Bitcoin Core uses for low-level cryptographic operations such as signing and verification.
This PR adds usage examples for several common use cases: ECDSA signatures, Schnorr signatures, and (elliptic curve) Diffie-Hellman key exchange. There are no existing usage examples in the library.
The examples are intended to clarify the API by supplementing the documentation in the various header files.
Can you compile and run the example? Note that the examples are not compiled
by default. Therefore, you must run ./configure with the
--enable-examples arguments. Moreover, the ECDH and Schnorr signature
examples are only compiled if the modules are enabled, which is done by
providing the --enable-module-ecdh and --enable-experimental
--enable-module-schnorrsig arguments to ./configure.
Why do the examples demonstrate how to obtain randomness? Is this a good
idea?
What are the recommendations for obtaining randomness on the supported
operating systems?
Do the examples correctly follow these recommendations?
Can you follow the examples? Is it clear how they should be generalized to a
production system?
Is there anything missing in the examples (e.g.
context_randomize,
ec_seckey_verify,
return value check, setting secrets to 0, etcâŠ)?
Are the comments in the examples helpful?
Are the comments sufficient (e.g. ECDSA takes message hash, not message)?
Is it a good idea to retry (in a loop) operations that could only fail with
negligible probability (see this
comment
for context)?
Is the build system correctly adjusted for the examples? Are examples
disabled by default? Does the configure output show whether examples are
enabled?
Further Questions
These questions are intended to guide deeper exploration of libsecp256k1.
We may not have time to cover them in the review session, but they can help with above questions.
<nickler> Ok, I volunteered to be the host this session, but this is my first time doing this so please let me know if this session is not going in the right direction.
<nickler> Perhaps you've noticed that we split the questions into two: 1) questions that are directly relevant to reviewing the PR 2) "further" questions that may help exploration of libsecp concepts We'll start with the first set of questions. Let's see how far we come.
<robot-dreams> glozow: I was able to debug as well, but I did get `ecdh_example was compiled with optimization - stepping may behave oddly; variables may not be available.` so I think larryruane's `CLAGS='-O0'` is still helpful
<real_or_random> robot-dreams: but then you need CFLAGS='-O0 -g'. We only have -g in our *default* CFLAGS but if the user sets CFLAGS they have the last word
<larryruane> Doesn't the library enable the user to provide the randomness? (Not built into the library) ... hence it's good to show the user the best ways to do it
<stickies-v> if e.g. nonce generation is not truly random, it can be trivial for an attacker to derive your private key. A lot of RNGs are not truly random, because of bad implementation or because of different requirements (e.g. speed > security)
<glozow> security of these schemes relies on the assumption that keys, nonces, salts, etc. are secret and/or uniformly distributed, so it makes sense to call attention to it
<siv2r[m]> a good PRNG is required to avoid cryptographic attack ig. Ex: if schnorrsig use a LCG random generator we can derive the private key since, the nonces will be linearly related
<glozow> The recommendation can become outdated if a vulnerability is found, the library is no longer maintained, etc. so it's nice that there's a warning message there
<jnewbery> perhaps that comment at the top of the file should actually be split up and put inline inside the fill_random() function, so if the OSs change or another is added, there's only one place that needs to be changed
<jimmysong> so question about the context randomization, how come it's a separate call instead of it being done for you as part of the context creation?
<lightlike> doesn't bitcoin core have some more elaborate algorithm for gathering entropy/randomess than just using the OS syscall, combining randomness from multiple sources?
<nickler> What are the recommendations for obtaining randomness on the supported operating systems? Do the examples correctly follow these recommendations?
<real_or_random> it's a litte strange: for bitcoin core as the main user of secp26k1, it does not matter. core has its functions and they work. the examples are intended for other users
<real_or_random> (the strange part is that this is a crypto library which is "bring your own randomness" and this is the part where a lot of people screw up)
<elichai2> michaelfolkson: Notice that secp currently doesn't generate randomness anywhere, it expects the user to provide random strings. This is just in the examples. (but it could be argued that libsecp should maintain a "getrandom" function)
<jimmysong> real_or_random: so trying to clear up something from the discussion. Are we supposed to call the context_randomize function before each new signing?
<real_or_random> jimmysong: yes, it will make signing more secure by adding side-channel protection. (though that does not mean that signing is insecure if you don't do it)
<theStack_> as for obtaining randomness on OpenBSD, i think arc4random(3) should be preferred over getentropy(2), at least that's what the man pages suggest (see also https://github.com/bitcoin/bitcoin/pull/24238)
<stickies-v> larryruane nickler the inline documentation in fill_random states that "/* If `getrandom(2)` is not available you should fallback to /dev/urandom */", but then it doesn't actually fallback?
<robot-dreams> stickies-v: Are you suggesting that `fill_random` should also try to read from `/dev/urandom` (or whatever the OS-specific fallback is)?
<elichai2> stickies-v: you're right. `random.h` only contains "best practice" random generation. it doesn't contain any fallbacks for older machines, as that will require a lot more effort
<siv2r[m]> Most applications should use getentropy. The getrandom function is intended for low-level applications which need additional control over blocking behavior.
<elichai2> stickies-v: At the top of `random.h` it states that the file only contains best practices. But it's probably not clear enough, so please comment a suggestion :)
<real_or_random> one issue with these man pages is that sometimes they're wrong, too. for example the linux manpage was suggesting /dev/random over /dev/urandom for a while (fixed now)
<jesseposner> dhruv: That's a good example of entropy sources that wouldn't necessarily be applicable to other applications (the time and checksum of a p2p message).
<stickies-v> nickler yeah I found the examples very straightforward to follow (disclaimer - only did ecdsa.h but they all seem very similarly structured)
<elichai2> siv2r[m]: I think there's a reason I didn't use getentropy but I can't remember it (been almost 2 years haha), I'll recheck later and respond to your comment
<robot-dreams> nickler: The examples seem quite clear to me; they don't include (1) hashing the message or (2) exchanging public keys over a network (ECDH) but I think those are straightforward for a reader to infer from context
<robot-dreams> michaelfolkson: I'm guessing it's because we don't want to (1) take a dependency on a cryptography library, or (2) add a SHA256 implementation to the example code
<theStack_> nickler: (re: arc4random vs getentropy on OpenBSD) yes i did. I'd rather assume that the mentioned projects are not following best practices. OTOH i don't think getentropy() is wrong either
<nickler> compared to the Core code it seems like the Schnorr examples do not specifically mention that the sig should be verified before giving it to the verifier.
<robot-dreams> engraving: here's a specific example where it gets optimized out. Note that in the assembly version, there is no `memset` call: https://godbolt.org/z/WeMfon6jh
<jesseposner> calling secp256k1_sha256 from within the ecdsa example could be useful for documentation purposes, and it would also help emphasize the hashing requirement which can be a footgun
<theStack_> nickler: i think RC4 hasn't been used internally since OpenBSD 5.5. Nowadays arc4random doesn't have anything to do with RC4 anymore, it's a mnemonic for "A Replacement Call For Random" :D
<glozow> larryruane: i imagine it's more important to sanity-check that your signature is correct than try to save time. and yeah we don't sign nearly as often as we verify
<sipa> Kaizen_Kintsugi_: It used to hold a lot of precomputed tables to accelerate signing/verification, but since recently all those tables are now built-in to the binary at compile time.
<sipa> So right now the context doesn't hold that much anymore; it holds randomization state as well as callbacks for errors, if you want to use that feature.
<robot-dreams> The "randomization state" is a way to scramble your arithmetic operations to protect against side channel attacks that larryruane referenced
<real_or_random> glozow: until recently, this struct had tables, yes and they would be created on context creation. now the tables are precomputed at build time (or actually in the repo)
<glozow> was looking for those multiplication tables and couldn't find them. thought i'd see some magic like the minisketch linear transformation tables
<jesseposner> nickler: if you don't call seckey_verify, then you risk, with negligible probability, that the secret key is invalid because not all 256 bit numbers are valid secp256k1 keys (because valid secret keys are limited to scalars within the cyclical subgroup of the generator)
<sipa> If people are interested in what EC multiplication algorithms are actually used, we should do a separate review club on that (or probably several...).
<engraving> a bit off topic so ignore if need be: are we aware of active attempts of side channel attacks or are the precautions merely cause we know they have/can be done and so our implementations must use anti-side channel designs
<theStack_> i noticed that on the schnorr example, seckey_verify is not called. i guess this is just to save an extra call, since keypair_create checks whether the private key is valid anyways?
<robot-dreams> engraving: I'm also curious about this question, e.g. what's a canonical scenario people have in mind when thinking about side channel (e.g. maybe a hardware wallet running secp256k1 code, an attacker stole it)
<siv2r[m]> jesseposner: nickler: in what order is this neglible probablity in? I tried calculating it like (p-n)/2^256. Here, p = field size and n = group order
<nickler> Of course, the libsecp repo has many more open PRs! Some of them only require a bit of context to review. If you enjoyed today's session perhaps that's something for you.
<engraving> obviously attackers aren't going to telegraph they're about to attack you but thought I'd float the question if anyone had seen any interesting attacks
<elichai2> Please feel free to reask any question that wasn't unanswered (there were so many observations and questions it was hard to follow haha) in the PR itself, in #secp256k1 or in private if you prefer :)
<sipa> engraving: So libsecp256k1 (on major platforms) should be completely free of timing attacks (all operations on secret data are constant time, have memory accesses and code paths that do not depend on secrets). For things like power leaks we rely on blinding.
<sipa> I shouldn't say we rely on blinding. libsecp256k1 (or probably, any pure-software system) cannot guarantee any protection against DPA or things like that, because who knows what an attacker can do if they can observe intermediary values in your CPU. Still, we use blinding for some best-effort to make it harder.
<michaelfolkson> robot-dreams: I haven't used radioactive decay unfortunately. The Coldcard dice look cool. My entropy generation has been decidedly vanilla thus far