Does the libsecp256k1 API expose low level functionality such as group algebra (e.g. generator point, point at infinity)? If not, why not?
This was asked by ProofOfKeags in this libsecp256k1 issue. These questions 1, 2 are also related.
Bitcoin Stack Exchange is a question and answer site for Bitcoin crypto-currency enthusiasts. It only takes a minute to sign up.
Sign up to join this communityDoes the libsecp256k1 API expose low level functionality such as group algebra (e.g. generator point, point at infinity)?
No
If not, why not?
There is always a trade-off between security, safety and flexibility, functionality when it comes to API design. A library that is used for educational purposes and not intended to be used in production could take a different choice to a library that is intended to be used in production and doesn't want to encourage usages of that library that could lead to bugs and unsafe implementations of cryptographic protocols.
One of the libsecp256k1 maintainers Pieter Wuille explains in the issue:
The idea is to have APIs for cryptographic protocols, not primitives (think, ECDSA, Schnorr, BIP32, ... rather than group operations and field operations).
The tweak APIs do not support infinity, because infinity is not a valid public key, and these APIs are intended for tweaking public keys and nothing else. They are in the library because BIP32 needs additive tweaking, and BIP38 needs multiplicative tweaking. That's considered a historic mistake because they're lower level than what we generally aim for: ideally we'd just have a bip32 module and a bip38 module that include the specific hashing steps needed for these specific protocols inside the library, but at the time they were added, including hashing inside the library wasn't considered in scope.
There are two reasons for not exposing cryptographic primitives:
API safety: by providing APIs, the obvious way to use the library is a safe one. Much more can go wrong when you're building higher-level constructions yourself directly. To give one example, if you'd implement ECDH using the multiplicative pubkey tweaking, you're likely to fail constant-timeness guarantees, because APIs for public keys do not need such protections (it treats neither keys nor tweaks as secret), but ECDH does need to treat the tweak as secret.
Performance: if you're going to use the API exposed by this library (e.g. using functions for key generations, and additive and multiplicative tweaking) for implementing group algebra, and then build things on top of that, you're going to see abysmal performance. This is because those operations operate on public keys, which use affine curve coordinates. Internally, group elements are represented using Jacobian coordinates and overcomplete field representations, which permit delaying expensive normalization operations until a public key needs to be returned. For something like point addition, I'd expect a 10x performance drop or so.
Of course, I can't stop you from writing bindings and exposing public keys as a group algebra, but the libsecp256k1 API is very much not designed for that. If you want something for experimentation with group laws, and don't care as much about safety, I'd suggest using a more exposed library that actually lets you operate on Jacobian group elements at least.
There has been a suggestion before to split out the internal field/group code in libsecp256k1 into a separate library, which would then permit things like building constructions, though the result would need a very different approach than we have now (among other things, because the representation of group/field elements isn't stable across versions and architectures, and that instability may need to be communicated very clearly to users, or create a problem for future performance improvements).
Another libsecp256k1 maintainer Tim Ruffing adds:
I think it really depends on what "security" is for the tweaking functions. Let me try to explain. So we can look at:
"Algorithmic" security: The tweaking functions are designed to error out on exceptional values, e.g., when they would output an infinity pubkey. This is based on a best guess that infinity pubkeys are bad in most cryptographic applications and protocols: For example, the infinity pubkey is invalid in ECDSA and other algorithms, and sometimes doesn't even have a valid serialization (see for example SEC 1, 3.2.2.1). When the tweaking functions were written, they were designed with BIP32 in mind, which is method to derive ECDSA pubkeys. In this case, it's really a good idea to inform that caller that they arrived at an invalid key. Typical callers would want to abort in this case.
Sidechannel security: Aborts are a big sidechannel as you note (probably much more observable than timing). However, first of all, maybe this is used in a setting where the attacker will anyway learn the result of the computation, i.e., the tweaked pubkey. It is a pubkey after all. So then it doesn't matter if the attacker could observe the infinity pubkey or the abort, it would learn the same. Moreover, sidechannels due to the aborts in exceptional cases are not an issue in practice because they will happen only with negligible (= astronomically small) probably for proper inputs. And even for attacker-controlled inputs, if the attacker know which values lead to the infinity pubkey, then the attacker anyway knows your secrets. For example, in the attacker knows which tweak would make
secp256k1_eckey_pubkey_tweak_mul
abort due to the result being infinity with your specific pubkey, then it knows the secret key corresponding to the public key.Anyway, sidechannel security is to protect against an attacker that can observe sidechannel information and learn something about a secret value. So this is all about protecting secrets. Now you'd need to ask what the secret here is. This really gets hairy in the case of the pubkey tweaking functions, because we don't really know what the caller has in mind (maybe the input public key is actually a secret, even though it's a public key). At the moment the tweaking functions are not really written in mind with sidechannel protection. And note how this is just an assumption because we as a library can only guess what they will be used for.
And this is exactly one of the problem with too generic functions. We don't actually know the intent of the caller, and so we don't know what the caller considers a secret and what not. (As mentioned above, we don't even really know if aborting is a good idea.) sipa's example was: If you're doing ECDH, then the result of the computation is a group element (a "pubkey" if you want) but in ECDH it's the secret! So if you were to use
secp256k1_eckey_pubkey_tweak_add
, you'd get it wrong. (See this for more background on the various internal point multiplication functions).