I merged secp256k1 into our open source code base back in July 2015. Since then, I notice that there has been considerable work and activity, most of it for the better - great job! However, there are some choices that I think might have overlooked some things. In particular a parsed public key must be stored in this type:
typedef struct {
unsigned char data[64];
} secp256k1_pubkey;
I think this was a mistake. It forces callers into one of 3 unpalatable choices:
- Limit the containers used to hold public keys
- Perform a buffer copy from an arbitrary container to a temporary
secp256k1_pubkey - Write code which could cause undefined behavior (e.g.
reinterpret_castor a C-style cast)
Consider a simple case where a std::vector is used to store a single application defined byte, followed by a public key:
std::vector<unsigned char> v;
v.resize(65);
v[0] = 42; // application defined value
unsigned char const* pubkey = &v[1]; // 64 byte public key
...
How do we pass the public key to secp256k1_ec_pubkey_serialize? There are two ways:
secp256k1_pubkey temp;
std::memcpy(&temp.data[0], pubkey, sizeof(temp.data));
secp256k1_ec_pubkey_serialize(..., &temp, ...);
or
secp256k1_ec_pubkey_serialize(...,
reinterpret_cast<secp256k1_pubkey const*>(pubkey), ...);
In the first case we have an unnecessary buffer copy. In the second case, we have the potential for undefined behavior due to misalignment. Furthermore, use of reinterpret_cast is a code smell (this could just have easily been a C-style cast).
The third option is not to use std::vector in that fashion, but this means the API choices of secp256k1 functions constrain callers' choice of containers used to hold cryptographic primitives.
I think it would have been better simply to use void const* pubkey as the parameter type in functions that currently accept secp256k1_pubkey*.