You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

292 lines
9.2 KiB

#include "internal.h"
#include <include/wally_crypto.h>
#include "secp256k1/include/secp256k1_schnorr.h"
#include "ccan/ccan/build_assert/build_assert.h"
#include <stdbool.h>
#define EC_FLAGS_TYPES (EC_FLAG_ECDSA | EC_FLAG_SCHNORR)
#define EC_FLAGS_ALL (EC_FLAG_ECDSA | EC_FLAG_SCHNORR)
#define MSG_ALL_FLAGS (BITCOIN_MESSAGE_FLAG_HASH)
static const char MSG_PREFIX[] = "\x18" "Bitcoin Signed Message:\n";
/* LCOV_EXCL_START */
/* Check assumptions we expect to hold true */
static void assert_assumptions(void)
{
BUILD_ASSERT(sizeof(secp256k1_ecdsa_signature) == EC_SIGNATURE_LEN);
}
/* LCOV_EXCL_STOP */
static bool is_valid_ec_type(uint32_t flags)
{
return ((flags & EC_FLAGS_TYPES) == EC_FLAG_ECDSA) ||
((flags & EC_FLAGS_TYPES) == EC_FLAG_SCHNORR);
}
int wally_ec_private_key_verify(const unsigned char *priv_key, size_t priv_key_len)
{
const secp256k1_context *ctx = secp_ctx();
if (!ctx)
return WALLY_ENOMEM;
if (!priv_key || priv_key_len != EC_PRIVATE_KEY_LEN)
return WALLY_EINVAL;
return secp256k1_ec_seckey_verify(ctx, priv_key) ? WALLY_OK : WALLY_EINVAL;
}
int wally_ec_public_key_from_private_key(const unsigned char *priv_key, size_t priv_key_len,
unsigned char *bytes_out, size_t len)
{
secp256k1_pubkey pub;
size_t len_in_out = EC_PUBLIC_KEY_LEN;
const secp256k1_context *ctx = secp_ctx();
bool ok;
if (!ctx)
return WALLY_ENOMEM;
ok = priv_key && priv_key_len == EC_PRIVATE_KEY_LEN &&
bytes_out && len == EC_PUBLIC_KEY_LEN &&
pubkey_create(ctx, &pub, priv_key) &&
pubkey_serialize(ctx, bytes_out, &len_in_out, &pub, PUBKEY_COMPRESSED) &&
len_in_out == EC_PUBLIC_KEY_LEN;
if (!ok && bytes_out)
clear(bytes_out, len);
clear(&pub, sizeof(pub));
return ok ? WALLY_OK : WALLY_EINVAL;
}
int wally_ec_public_key_decompress(const unsigned char *pub_key, size_t pub_key_len,
unsigned char *bytes_out, size_t len)
{
secp256k1_pubkey pub;
size_t len_in_out = EC_PUBLIC_KEY_UNCOMPRESSED_LEN;
const secp256k1_context *ctx = secp_ctx();
bool ok;
if (!ctx)
return WALLY_ENOMEM;
ok = pub_key && pub_key_len == EC_PUBLIC_KEY_LEN &&
bytes_out && len == EC_PUBLIC_KEY_UNCOMPRESSED_LEN &&
pubkey_parse(ctx, &pub, pub_key, pub_key_len) &&
pubkey_serialize(ctx, bytes_out, &len_in_out, &pub, PUBKEY_UNCOMPRESSED) &&
len_in_out == EC_PUBLIC_KEY_UNCOMPRESSED_LEN;
if (!ok && bytes_out)
clear(bytes_out, len);
clear(&pub, sizeof(pub));
return ok ? WALLY_OK : WALLY_EINVAL;
}
int wally_ec_sig_normalize(const unsigned char *sig_in, size_t sig_in_len,
unsigned char *bytes_out, size_t len)
{
secp256k1_ecdsa_signature sig, sig_low;
const secp256k1_context *ctx = secp_ctx();
bool ok;
if (!ctx)
return WALLY_ENOMEM;
ok = sig_in && sig_in_len == EC_SIGNATURE_LEN &&
bytes_out && len == EC_SIGNATURE_LEN &&
secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sig_in);
if (ok) {
/* Note no error is returned, just whether the sig was changed */
secp256k1_ecdsa_signature_normalize(ctx, &sig_low, &sig);
ok = secp256k1_ecdsa_signature_serialize_compact(ctx, bytes_out,
&sig_low);
}
if (!ok && bytes_out)
clear(bytes_out, len);
clear_n(2, &sig, sizeof(sig), &sig_low, sizeof(sig_low));
return ok ? WALLY_OK : WALLY_EINVAL;
}
int wally_ec_sig_to_der(const unsigned char *sig_in, size_t sig_in_len,
unsigned char *bytes_out, size_t len, size_t *written)
{
secp256k1_ecdsa_signature sig;
size_t len_in_out = len;
const secp256k1_context *ctx = secp_ctx();
bool ok;
if (written)
*written = 0;
if (!ctx)
return WALLY_ENOMEM;
ok = sig_in && sig_in_len == EC_SIGNATURE_LEN &&
bytes_out && len == EC_SIGNATURE_DER_MAX_LEN && written &&
secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sig_in) &&
secp256k1_ecdsa_signature_serialize_der(ctx, bytes_out,
&len_in_out, &sig);
if (!ok && bytes_out)
clear(bytes_out, len);
if (ok)
*written = len_in_out;
clear(&sig, sizeof(sig));
return ok ? WALLY_OK : WALLY_EINVAL;
}
int wally_ec_sig_from_der(const unsigned char *bytes_in, size_t len_in,
unsigned char *bytes_out, size_t len)
{
secp256k1_ecdsa_signature sig;
const secp256k1_context *ctx = secp_ctx();
bool ok;
if (!ctx)
return WALLY_ENOMEM;
ok = bytes_in && len_in && bytes_out && len == EC_SIGNATURE_LEN &&
secp256k1_ecdsa_signature_parse_der(ctx, &sig, bytes_in, len_in) &&
secp256k1_ecdsa_signature_serialize_compact(ctx, bytes_out, &sig);
if (!ok && bytes_out)
clear(bytes_out, len);
clear(&sig, sizeof(sig));
return ok ? WALLY_OK : WALLY_EINVAL;
}
int wally_ec_sig_from_bytes(const unsigned char *priv_key, size_t priv_key_len,
const unsigned char *bytes_in, size_t len_in,
uint32_t flags,
unsigned char *bytes_out, size_t len)
{
wally_ec_nonce_t nonce_fn = wally_ops()->ec_nonce_fn;
const secp256k1_context *ctx = secp_ctx();
if (!priv_key || priv_key_len != EC_PRIVATE_KEY_LEN ||
!bytes_in || len_in != EC_MESSAGE_HASH_LEN ||
!is_valid_ec_type(flags) || flags & ~EC_FLAGS_ALL ||
!bytes_out || len != EC_SIGNATURE_LEN)
return WALLY_EINVAL;
if (!ctx)
return WALLY_ENOMEM;
if (flags & EC_FLAG_SCHNORR) {
if (!secp256k1_schnorr_sign(ctx, bytes_out, bytes_in,
priv_key, nonce_fn, NULL))
return WALLY_EINVAL; /* Failed to sign */
return WALLY_OK;
} else {
secp256k1_ecdsa_signature sig;
if (!secp256k1_ecdsa_sign(ctx, &sig, bytes_in, priv_key, nonce_fn, NULL)) {
clear(&sig, sizeof(sig));
if (secp256k1_ec_seckey_verify(ctx, priv_key))
return WALLY_ERROR; /* Nonce function failed */
return WALLY_EINVAL; /* invalid priv_key */
}
/* Note this function is documented as never failing */
secp256k1_ecdsa_signature_serialize_compact(ctx, bytes_out, &sig);
clear(&sig, sizeof(sig));
}
return WALLY_OK;
}
int wally_ec_sig_verify(const unsigned char *pub_key, size_t pub_key_len,
const unsigned char *bytes_in, size_t len_in,
uint32_t flags,
const unsigned char *sig_in, size_t sig_in_len)
{
secp256k1_pubkey pub;
secp256k1_ecdsa_signature sig;
const secp256k1_context *ctx = secp_ctx();
bool ok;
if (!pub_key || pub_key_len != EC_PUBLIC_KEY_LEN ||
!bytes_in || len_in != EC_MESSAGE_HASH_LEN ||
!is_valid_ec_type(flags) || flags & ~EC_FLAGS_ALL ||
!sig_in || sig_in_len != EC_SIGNATURE_LEN)
return WALLY_EINVAL;
if (!ctx)
return WALLY_ENOMEM;
ok = pubkey_parse(ctx, &pub, pub_key, pub_key_len);
if (flags & EC_FLAG_SCHNORR)
ok = ok && secp256k1_schnorr_verify(ctx, sig_in, bytes_in, &pub);
else
ok = ok && secp256k1_ecdsa_signature_parse_compact(ctx, &sig, sig_in) &&
secp256k1_ecdsa_verify(ctx, &sig, bytes_in, &pub);
clear_n(2, &pub, sizeof(pub), &sig, sizeof(sig));
return ok ? WALLY_OK : WALLY_EINVAL;
}
static inline size_t varint_len(size_t len_in) {
return len_in < 0xfd ? 1u : 3u;
}
int wally_format_bitcoin_message(const unsigned char *bytes_in, size_t len_in,
uint32_t flags,
unsigned char *bytes_out, size_t len,
size_t *written)
{
unsigned char buf[256], *msg_buf = bytes_out, *out;
const bool do_hash = (flags & BITCOIN_MESSAGE_FLAG_HASH);
size_t msg_len;
if (written)
*written = 0;
if (!bytes_in || !len_in || len_in > BITCOIN_MESSAGE_MAX_LEN ||
(flags & ~MSG_ALL_FLAGS) || !bytes_out || !written)
return WALLY_EINVAL;
msg_len = sizeof(MSG_PREFIX) - 1 + varint_len(len_in) + len_in;
*written = do_hash ? SHA256_LEN : msg_len;
if (len < *written)
return WALLY_OK; /* Not enough output space, return required size */
if (do_hash) {
/* Ensure we have a suitable temporary buffer to serialise into */
msg_buf = buf;
if (msg_len > sizeof(buf)) {
msg_buf = wally_malloc(msg_len);
if (!msg_buf) {
*written = 0;
return WALLY_ENOMEM;
}
}
}
/* Serialise the message */
out = msg_buf;
memcpy(out, MSG_PREFIX, sizeof(MSG_PREFIX) - 1);
out += sizeof(MSG_PREFIX) - 1;
if (len_in < 0xfd)
*out++ = len_in;
else {
*out++ = 0xfd;
*out++ = len_in & 0xff;
*out++ = len_in >> 8;
}
memcpy(out, bytes_in, len_in);
if (do_hash) {
wally_sha256d(msg_buf, msg_len, bytes_out, SHA256_LEN);
clear(msg_buf, msg_len);
if (msg_buf != buf)
wally_free(msg_buf);
}
return WALLY_OK;
}