This commit is contained in:
2025-08-16 10:07:35 +02:00
parent 27bc172257
commit be19609855
13 changed files with 61 additions and 1313 deletions

View File

@@ -1,147 +0,0 @@
# libsecp256k1
This is a lib256k1 binding for vlang.
## Requirements
make sure the lib is installed
### macOS
```bash
brew install secp256k1
```
### Ubuntu
Compile latest release, version included in Ubuntu is outdated.
```
apt-get install -y build-essential wget autoconf libtool
wget https://github.com/bitcoin-core/secp256k1/archive/refs/tags/v0.3.2.tar.gz
tar -xvf v0.3.2.tar.gz
cd secp256k1-0.3.2/
./autogen.sh
./configure
make -j 5
make install
```
### Arch
```bash
pacman -Su extra/libsecp256k1
```
### Gentoo
```bash
emerge dev-libs/libsecp256k1
```
## Features
- [x] Generate EC keys
- [x] Load existing EC keys
- [x] Serialize keys
- [x] Derivate shared key
- [x] Sign using ECDSA
- [x] Verify ECDSA signature
- [x] Sign using Schnorr
- [x] Verify a Schnorr signature
- [ ] Support multi-signature with Schnorr
## How to use
There are 4 differents things / features to understand in this secp256k1 implementation (wrapper).
### Public and Privaye keys for secp256k1
This is a simple private/public key schema. This wrapper deals with hexdump of keys.
- Private key is `32 bytes` long (eg: `0x4a21f247ff3744e211e95ec478d5aba94a1d6d8bed613e8a9faece6d048399fc`)
- Public key is `33 bytes` long (eg: `0x02df72fc4fa607ca3478446750bf9f8510242c4fa5849e77373d71104cd0c82ea0`)
In this library, you can instanciate a secp256k1 object from 3 ways:
```vlang
import freeflowuniverse.herolib.crypt.secp256k1
secp256k1.new()
```
Constructor without any arguments, will generate a new private and public key
```vlang
secp256k1.new(privkey: '0x4a21f247ff3744e211e95ec478d5aba94a1d6d8bed613e8a9faece6d048399fc')
```
Using `privkey` argument, this will create an object from private key and generate corresponding public key
```vlang
secp256k1.new(pubkey: '0x02df72fc4fa607ca3478446750bf9f8510242c4fa5849e77373d71104cd0c82ea0')
```
Using `privkey` argument, this will create an object with only the public key,
which can be used for shared key or signature verification
### Shared Keys
Library `secp256k1` have one feature which allows you to derivate a `shared intermediate common key` from
the private key of one party and the public key from the other party.
Example:
- Shared key from `Bob Private Key` + `Alice Public Key` = `Shared Key`
- Shared key from `Alice Private Key` + `Bob Public Key` = `Shared Key` (the same)
Using this feature, with your private key and target public key, you can derivate a `shared (secret) key`
that only you both knows. This is really interresting to switch to a symetric encryption using that key
as encryption key or use any well known secret without exchanging it.
To use the shared key feature, just call the `sharedkeys()` method:
```vlang
bob := secp256k1.new(privhex: '0x478b45390befc3097e3e6e1a74d78a34a113f4b9ab17deb87e9b48f43893af83')!
alicepub := secp256k1.new(pubkey: '0x034a87ad6fbf83d89a91c257d4cc038828c6ed9104738ffd4bb7e5069858d4767b')!
shared := bob.sharedkeys(alicepub)
// shared = 0xf114df29d930f0cd37f62cbca36c46773a42bf87e12edcb35d47c4bfbd20514d
```
This works the same in the opposite direction:
```vlang
alice := secp256k1.new(privhex: '0x8225825815f42e1c24a2e98714d99fee1a20b5ac864fbcb7a103cd0f37f0ffec')!
bobpub := secp256k1.new(pubkey: '0x03310ec949bd4f7fc24f823add1394c78e1e9d70949ccacf094c027faa20d99e21')!
shared := alice.sharedkeys(bobpub)
// shared = 0xf114df29d930f0cd37f62cbca36c46773a42bf87e12edcb35d47c4bfbd20514d (same shared key)
```
### ECDSA Signature
This is the default signature method. When doing a signature, you don't sign the actual data but you
have to sign a hash (sha256) of the data. This payload needs to be fixed length. The return signature
is a `64 bytes` long response.
When doing a signature using ecdsa method, you sign using the private key and verify using the public key
of the same party. If **Bob** sign something, you have to verify using **Bob** public key is the signature matches.
If signature matches, that mean that is really **Bob** who signed the hash.
Here, you need the signature and the message separately.
```vlang
sstr := alice.sign_str("Hello World !")
valid := alicepub.verify_str(sstr, "Hello World !")
// valid = true
```
### Schnorr Signature
This is the new prefered signature method. In theory, this method can in addition be able to sign
using multiple parties without storing signature of everyone, signature can be chained but this is not
implemented in this wrapper (lack of source documentation and understanding).
In practice, code wide, wrapper take care to handle everything for you and this really looks like
the same way than ecdsa.
```vlang
schnorr_sstr := alice.schnorr_sign_str("Hello World !")
valid := alicepub.schnorr_verify_str(schnorr_sstr, "Hello World !")
// valid = true
```

View File

@@ -1,351 +0,0 @@
@[translated]
module secp256k1
import encoding.hex
import crypto.sha256
import encoding.base64
#include "@VMODROOT/secp256k1mod.h"
#flag @VMODROOT/secp256k1mod.o
#flag -lsecp256k1
#flag -DNO_SECP_MAIN
#flag darwin -I/opt/homebrew/include
#flag darwin -L/opt/homebrew/lib
// linux: require libsecp256k1-dev
// macos: require brew install secp256k1
//
// struct definitions
//
struct Secp256k1_pubkey {
data [64]u8
}
struct Secp256k1_xonly_pubkey {
data [64]u8
}
struct Secp256k1_ecdsa_signature {
data [64]u8
}
struct Secp256k1_keypair {
data [96]u8
}
struct Secp256k1_t {
kntxt &C.secp256k1_context
seckey &u8
compressed &u8
pubkey Secp256k1_pubkey
xcompressed &u8
xpubkey Secp256k1_xonly_pubkey
keypair Secp256k1_keypair
}
struct Secp256k1_sign_t {
sig Secp256k1_ecdsa_signature
serialized &u8
length usize
}
struct Secp256k1_signature {
cctx &C.secp256k1_sign_t
}
pub struct Secp256k1 {
cctx &Secp256k1_t
}
//
// prototypes
//
fn C.secp256k1_new() &Secp256k1_t
fn C.secp256k1_schnorr_verify(secp &Secp256k1_t, signature &u8, siglen usize, hash &u8, hashlen usize) int
fn C.secp256k1_schnorr_sign_hash(secp &Secp256k1_t, hash &u8, length usize) &u8
fn C.secp256k1_sign_verify(secp &Secp256k1_t, signature &Secp256k1_sign_t, hash &u8, length usize) int
fn C.secp256k1_sign_free(signature &Secp256k1_sign_t)
fn C.secp256k1_load_signature(secp &Secp256k1_t, serialized &u8, length usize) &Secp256k1_sign_t
fn C.secp256k1_sign_hash(secp &Secp256k1_t, hash &u8, length usize) &u8
fn C.secp265k1_shared_key(private &Secp256k1_t, public &Secp256k1_t) &u8
fn C.secp256k1_load_key(secp &Secp256k1_t, key &u8) int
fn C.secp256k1_load_private_key(secp &Secp256k1_t, key &u8) int
fn C.secp256k1_load_public_key(secp &Secp256k1_t, key &u8) int
fn C.secp256k1_free(secp &Secp256k1_t)
fn C.secp256k1_dumps(secp &Secp256k1_t)
fn C.secp256k1_export(secp &Secp256k1_t) &u8
fn C.secp256k1_private_key(secp &Secp256k1_t) &u8
fn C.secp256k1_public_key(secp &Secp256k1_t) &u8
fn C.secp256k1_generate_key(secp &Secp256k1_t) int
@[params]
pub struct Secp256NewArgs {
pub:
pubhex string // public key hex (eg 03310ec949bd4f7fc24f823add1394c78e1e9d70949ccacf094c027faa20d99e21)
privhex string // private key hex (eg 478b45390befc3097e3e6e1a74d78a34a113f4b9ab17deb87e9b48f43893af83)
pubbase64 string
privbase64 string
// key []u8 // is in binary form (not implemented)
}
// get a Secp256k1 key, can start from an existing key in string hex format (starts with 0x)
// parameters:
// privhex: private key in hex format (full features will be available)
// pubhex: public key in hex format (reduced features available)
//
// keyhex string // e.g. 0x478b45390befc3097e3e6e1a74d78a34a113f4b9ab17deb87e9b48f43893af83
// // keyhex is still supported for _backward_ compatibility only, please do not use anymore
//
// key []u8 // is in binary form (not implemented)
// generate bool = true // default will generate a new key .
pub fn new(args_ Secp256NewArgs) !Secp256k1 {
mut args := args_
secp := Secp256k1{}
secp.cctx = C.secp256k1_new()
// if args.key.len > 0 && args.privhex.len > 0 {
// return error('cannot specify privhex and key at same time')
// }
if args.privhex.len > 0 && args.pubhex.len > 0 {
return error('cannot specify private and public key at same time')
}
if args.privhex.len > 0 {
// same as keyhex (backward compatibility)
// load key from hex like 0x478b45390befc3097e3e6e1a74d78a34a113f4b9ab17deb87e9b48f43893af83
// key is the private key
if !(args.privhex.starts_with('0x')) {
args.privhex = '0x${args.privhex}'
}
load := C.secp256k1_load_private_key(secp.cctx, args.privhex.str)
if load > 0 {
return error('invalid private key')
}
} else if args.pubhex.len > 0 {
// load key from hex like 0x478b45390befc3097e3e6e1a74d78a34a113f4b9ab17deb87e9b48f43893af83
// key is the public key, this only allow signature check, shared keys, etc.
if !(args.pubhex.starts_with('0x')) {
args.pubhex = '0x${args.pubhex}'
}
load := C.secp256k1_load_public_key(secp.cctx, args.pubhex.str)
if load > 0 {
return error('invalid public key')
}
} else if args.privbase64.len > 0 {
keybin := base64.decode(args.privbase64)
keyhex := hex.encode(keybin)
keyhex2 := '0x${keyhex}'
return new(privhex: keyhex2)!
} else if args.pubbase64.len > 0 {
keybin := base64.decode(args.pubbase64)
keyhex := hex.encode(keybin)
keyhex2 := '0x${keyhex}'
return new(pubhex: keyhex2)!
} else {
C.secp256k1_generate_key(secp.cctx)
}
// TODO: implement the binary key input
// TODO: check format in side and report properly
// dumps keys for debugging purpose
// secp.keys()
return secp
}
// request keys dump from low level library
// this basically prints keys from internal objects (private, public, shared, x-only, ...)
// warning: this is for debug purpose
fn (s Secp256k1) keys() {
C.secp256k1_dumps(s.cctx)
}
// export private key
// backward compatibility, please use private_key() and public_key() methods
pub fn (s Secp256k1) export() string {
key := C.secp256k1_export(s.cctx)
return unsafe { key.vstring() }
}
// with a private key in pair with a public key, secp256k1 can derivate a shared
// key which is the same for both parties, this is really interresting to use for example
// that shared keys for symetric encryption key since it's private but common
//
// example: sharedkey(bobpriv + alicepub) = abcdef
// sharedkey(alicepriv + bobpub) = abcdef
//
// both parties can use their own private key with target public key to derivate the same
// shared commun key, this key is unique with that pair.
pub fn (s Secp256k1) sharedkeys(target Secp256k1) []u8 {
shr := C.secp265k1_shared_key(s.cctx, target.cctx)
return unsafe { shr.vbytes(32) } // 32 bytes shared key
}
pub fn (s Secp256k1) sharedkeys_hex(target Secp256k1) string {
keybin := s.sharedkeys(target)
return hex.encode(keybin)
}
pub fn (s Secp256k1) sharedkeys_base64(target Secp256k1) string {
keybin := s.sharedkeys(target)
return base64.encode(keybin)
}
// returns private key in hex format
pub fn (s Secp256k1) private_key_hex() string {
key := C.secp256k1_private_key(s.cctx)
return unsafe { key.vstring()[2..] }
}
pub fn (s Secp256k1) private_key_base64() string {
key := s.private_key_hex()
keybin := hex.decode(key) or { panic("can't decode hex") }
return base64.encode(keybin)
}
// return public key in hex format
pub fn (s Secp256k1) public_key_hex() string {
key := C.secp256k1_public_key(s.cctx)
return unsafe { key.vstring()[2..] }
}
pub fn (s Secp256k1) public_key_base64() string {
key := s.public_key_hex()
keybin := hex.decode(key) or { panic("can't decode hex") }
return base64.encode(keybin)
}
//
// sign (ecdsa) data
// - we force user to pass data to ensure we hash the right way
// data to ensure signature is valid and safe
//
pub fn (s Secp256k1) sign_data(data []u8) []u8 {
// hash data
h256 := sha256.sum(data)
signature := C.secp256k1_sign_hash(s.cctx, h256.data, h256.len)
return unsafe { signature.vbytes(64) } // 64 bytes signature
}
// return a hex string of the signature
pub fn (s Secp256k1) sign_data_hex(data []u8) string {
payload := s.sign_data(data)
return hex.encode(payload)
}
pub fn (s Secp256k1) sign_data_base64(data []u8) string {
payload := s.sign_data(data)
return base64.encode(payload)
}
pub fn (s Secp256k1) sign_str(data string) []u8 {
return s.sign_data(data.bytes())
}
// return a hex string of the signature
pub fn (s Secp256k1) sign_str_hex(data string) string {
return s.sign_data_hex(data.bytes())
}
pub fn (s Secp256k1) sign_str_base64(data string) string {
payload := s.sign_data(data.bytes())
return base64.encode(payload)
}
//
// verify a signature
//
pub fn (s Secp256k1) verify_data(signature []u8, data []u8) bool {
// todo: check size signature
sig := Secp256k1_signature{}
sig.cctx = C.secp256k1_load_signature(s.cctx, signature.data, signature.len)
// compute data hash to ensure we do it correctly
// - do not trust the user, do it ourself -
h256 := sha256.sum(data)
valid := C.secp256k1_sign_verify(s.cctx, sig.cctx, h256.data, h256.len)
if valid == 1 {
return true
}
return false
}
pub fn (s Secp256k1) verify_str_base64(signature string, input string) bool {
signature2 := base64.decode(signature)
return s.verify_data(signature2, input.bytes())
}
pub fn (s Secp256k1) verify_str_hex(signature string, input string) bool {
signature2 := hex.decode(signature) or { panic("couldn't decode 64") }
return s.verify_data(signature2, input.bytes())
}
//
// sign (schnorr) data
// - we force user to pass data to ensure we hash the right way
// data to ensure signature is valid and safe
//
pub fn (s Secp256k1) schnorr_sign_data(data []u8) []u8 {
// hash data
h256 := sha256.sum(data)
signature := C.secp256k1_schnorr_sign_hash(s.cctx, h256.data, h256.len)
return unsafe { signature.vbytes(64) } // 64 bytes signature
}
// return a hex string of the signature
pub fn (s Secp256k1) schnorr_sign_data_hex(data []u8) string {
payload := s.schnorr_sign_data(data)
return hex.encode(payload)
}
pub fn (s Secp256k1) schnorr_sign_str(data string) []u8 {
return s.schnorr_sign_data(data.bytes())
}
// return a hex string of the signature
pub fn (s Secp256k1) schnorr_sign_str_hex(data string) string {
return s.schnorr_sign_data_hex(data.bytes())
}
//
// verify a signature
//
pub fn (s Secp256k1) schnorr_verify_data(signature []u8, data []u8) bool {
// compute data hash to ensure we do it correctly
// - do not trust the user, do it ourself -
h256 := sha256.sum(data)
valid := C.secp256k1_schnorr_verify(s.cctx, signature.data, signature.len, h256.data,
h256.len)
if valid == 1 {
return true
}
return false
}
pub fn (s Secp256k1) schnorr_verify_str(signature []u8, input string) bool {
return s.schnorr_verify_data(signature, input.bytes())
}

View File

@@ -1,460 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#include <sys/random.h>
#include <stddef.h>
#include <limits.h>
#include <stdio.h>
#include "secp256k1mod.h"
static int fill_random(unsigned char* data, size_t size) {
#if defined(__linux__) || defined(__FreeBSD__)
ssize_t res = getrandom(data, size, 0);
if(res < 0 || (size_t) res != size) {
return 0;
} else {
return 1;
}
#elif defined(__APPLE__) || defined(__OpenBSD__)
int res = getentropy(data, size);
if(res == 0) {
return 1;
} else {
return 0;
}
#endif
return 0;
}
static void dumphex(unsigned char *data, size_t size) {
size_t i;
printf("0x");
for(i = 0; i < size; i++) {
printf("%02x", data[i]);
}
printf("\n");
}
static char *hexifier(unsigned char *data, size_t size) {
char *target = calloc(sizeof(char), (size * 2) + 4);
char buffer[8];
strcpy(target, "0x");
memset(buffer, 0, sizeof(buffer));
for(size_t i = 0; i < size; i++) {
sprintf(buffer, "%02x", data[i]);
strcat(target, buffer);
}
return target;
}
static unsigned char *hexparse(char *input) {
if(strncmp(input, "0x", 2) != 0)
return NULL;
size_t length = strlen(input);
unsigned char *target = calloc(sizeof(char), length);
char *pos = input + 2;
for(size_t count = 0; count < length - 2; count++) {
sscanf(pos, "%2hhx", &target[count]);
pos += 2;
}
return target;
}
static void secp256k1_erase(unsigned char *target, size_t length) {
#if defined(__GNUC__)
// memory barrier to avoid memset optimization
memset(target, 0, length);
__asm__ __volatile__("" : : "r"(target) : "memory");
#else
// if we can't, fill with random, still better than
// risking avoid memset
fill_random(target, length);
#endif
}
static void secp256k1_erase_free(unsigned char *target, size_t length) {
secp256k1_erase(target, length);
free(target);
}
secp256k1_t *secp256k1_new() {
secp256k1_t *secp = malloc(sizeof(secp256k1_t));
unsigned char randomize[32];
secp->kntxt = secp256k1_context_create(SECP256K1_CONTEXT_NONE);
if(!fill_random(randomize, sizeof(randomize))) {
printf("[-] failed to generate randomness\n");
return NULL;
}
// side-channel protection
int val = secp256k1_context_randomize(secp->kntxt, randomize);
assert(val);
// allocate keys and initialize them empty
secp->seckey = calloc(sizeof(char), SECKEY_SIZE);
secp->compressed = calloc(sizeof(char), COMPPUB_SIZE);
secp->xcompressed = calloc(sizeof(char), XSERPUB_SIZE);
return secp;
}
void secp256k1_free(secp256k1_t *secp) {
secp256k1_context_destroy(secp->kntxt);
secp256k1_erase_free(secp->seckey, SECKEY_SIZE);
secp256k1_erase_free(secp->compressed, COMPPUB_SIZE);
secp256k1_erase_free(secp->xcompressed, XSERPUB_SIZE);
free(secp);
}
static int secp256k1_populate_public_key(secp256k1_t *secp) {
int retval;
retval = secp256k1_xonly_pubkey_from_pubkey(secp->kntxt, &secp->xpubkey, NULL, &secp->pubkey);
assert(retval);
retval = secp256k1_xonly_pubkey_serialize(secp->kntxt, secp->xcompressed, &secp->xpubkey);
assert(retval);
return 0;
}
static int secp256k1_populate_key(secp256k1_t *secp) {
int retval;
retval = secp256k1_ec_pubkey_create(secp->kntxt, &secp->pubkey, secp->seckey);
assert(retval);
size_t len = COMPPUB_SIZE;
retval = secp256k1_ec_pubkey_serialize(secp->kntxt, secp->compressed, &len, &secp->pubkey, SECP256K1_EC_COMPRESSED);
assert(retval);
// always compute the xonly pubkey as well, so we don't need to compute
// it later for schnorr
retval = secp256k1_keypair_create(secp->kntxt, &secp->keypair, secp->seckey);
assert(retval);
return secp256k1_populate_public_key(secp);
}
int secp256k1_generate_key(secp256k1_t *secp) {
while(1) {
if(!fill_random(secp->seckey, SECKEY_SIZE)) {
printf("[-] failed to generate randomness\n");
return 1;
}
if(secp256k1_ec_seckey_verify(secp->kntxt, secp->seckey) == 0) {
// try again
continue;
}
return secp256k1_populate_key(secp);
}
return 1;
}
// backward compatibility
int secp256k1_load_key(secp256k1_t *secp, char *key) {
// only allow valid key size
if(strlen(key) != (SECKEY_SIZE * 2) + 2)
return 1;
unsigned char *binkey = hexparse(key);
free(secp->seckey);
secp->seckey = binkey;
if(secp256k1_ec_seckey_verify(secp->kntxt, secp->seckey) == 0) {
// invalid key
return 1;
}
return secp256k1_populate_key(secp);
}
int secp256k1_load_private_key(secp256k1_t *secp, char *key) {
return secp256k1_load_key(secp, key);
}
int secp256k1_load_public_key(secp256k1_t *secp, char *key) {
// only allow valid key size
if(strlen(key) != (COMPPUB_SIZE * 2) + 2)
return 1;
unsigned char *binkey = hexparse(key);
free(secp->compressed);
secp->compressed = binkey;
if(!secp256k1_ec_pubkey_parse(secp->kntxt, &secp->pubkey, secp->compressed, COMPPUB_SIZE)) {
printf("[-] failed to load public key\n");
return 1;
}
return secp256k1_populate_public_key(secp);;
}
unsigned char *secp265k1_shared_key(secp256k1_t *private, secp256k1_t *public) {
unsigned char *shared = malloc(sizeof(unsigned char) * SHARED_SIZE);
int val = secp256k1_ecdh(private->kntxt, shared, &public->pubkey, private->seckey, NULL, NULL);
assert(val);
return shared;
}
unsigned char *secp256k1_sign_hash(secp256k1_t *secp, unsigned char *hash, size_t length) {
secp256k1_sign_t signature;
int retval;
if(length != SHA256_SIZE) {
printf("[-] warning: you should only sign sha-256 hash, size mismatch\n");
printf("[-] warning: you get warned\n");
}
retval = secp256k1_ecdsa_sign(secp->kntxt, &signature.sig, hash, secp->seckey, NULL, NULL);
assert(retval);
signature.serialized = malloc(sizeof(unsigned char) * SERSIG_SIZE);
retval = secp256k1_ecdsa_signature_serialize_compact(secp->kntxt, signature.serialized, &signature.sig);
assert(retval);
return signature.serialized;
}
secp256k1_sign_t *secp256k1_load_signature(secp256k1_t *secp, unsigned char *serialized, size_t length) {
secp256k1_sign_t *signature;
if(length != SERSIG_SIZE) {
printf("[-] serialized signature length mismatch, expected %u bytes\n", SERSIG_SIZE);
return NULL;
}
signature = calloc(sizeof(secp256k1_sign_t), 1);
signature->length = length;
signature->serialized = malloc(length);
memcpy(signature->serialized, serialized, length);
if(!secp256k1_ecdsa_signature_parse_compact(secp->kntxt, &signature->sig, signature->serialized)) {
printf("[-] failed to parse the signature\n");
// FIXME: cleanup
return NULL;
}
return signature;
}
void secp256k1_sign_free(secp256k1_sign_t *signature) {
secp256k1_erase_free(signature->serialized, signature->length);
free(signature);
}
int secp256k1_sign_verify(secp256k1_t *secp, secp256k1_sign_t *signature, unsigned char *hash, size_t length) {
if(length != SHA256_SIZE) {
printf("[-] warning: you should only check sha-256 hash, size mismatch\n");
}
return secp256k1_ecdsa_verify(secp->kntxt, &signature->sig, hash, &secp->pubkey);
}
unsigned char *secp256k1_schnorr_sign_hash(secp256k1_t *secp, unsigned char *hash, size_t length) {
unsigned char aux[32];
unsigned char *signature;
int retval;
if(length != SHA256_SIZE) {
printf("[-] warning: you should only sign sha-256 hash, size mismatch\n");
printf("[-] warning: you get warned\n");
}
if(!fill_random(aux, sizeof(aux))) {
printf("[-] failed to generate randomness\n");
return NULL;
}
signature = malloc(sizeof(unsigned char) * SCHSIG_SIZE);
retval = secp256k1_schnorrsig_sign32(secp->kntxt, signature, hash, &secp->keypair, aux);
assert(retval);
return signature;
}
int secp256k1_schnorr_verify(secp256k1_t *secp, unsigned char *signature, size_t siglen, unsigned char *hash, size_t hashlen) {
if(hashlen != SHA256_SIZE) {
printf("[-] warning: you should only check sha-256 hash, size mismatch\n");
}
if(siglen != SCHSIG_SIZE) {
printf("[-] invalid signature length, should be %u bytes\n", SCHSIG_SIZE);
return 2;
}
return secp256k1_schnorrsig_verify(secp->kntxt, signature, hash, hashlen, &secp->xpubkey);
}
void secp256k1_dumps(secp256k1_t *secp) {
printf("Private Key: ");
dumphex(secp->seckey, SECKEY_SIZE);
printf("Public Key : ");
dumphex(secp->compressed, COMPPUB_SIZE);
printf("X-Only Key : ");
dumphex(secp->xcompressed, XSERPUB_SIZE);
}
// backward compatibility
char *secp256k1_export(secp256k1_t *secp) {
return hexifier(secp->seckey, SECKEY_SIZE);
}
// return private key in hex format
char *secp256k1_private_key(secp256k1_t *secp) {
return secp256k1_export(secp);
}
char *secp256k1_public_key(secp256k1_t *secp) {
return hexifier(secp->compressed, COMPPUB_SIZE);
}
#ifndef NO_SECP_MAIN
int main() {
secp256k1_t *wendy = secp256k1_new();
secp256k1_generate_key(wendy);
printf("Wendy:\n");
dumphex(wendy->seckey, SECKEY_SIZE);
dumphex(wendy->compressed, COMPPUB_SIZE);
dumphex(wendy->xcompressed, XSERPUB_SIZE);
// bob
secp256k1_t *bob = secp256k1_new();
secp256k1_load_key(bob, "0x478b45390befc3097e3e6e1a74d78a34a113f4b9ab17deb87e9b48f43893af83");
printf("\n");
printf("Bob:\n");
dumphex(bob->seckey, SECKEY_SIZE);
dumphex(bob->compressed, COMPPUB_SIZE);
dumphex(bob->xcompressed, XSERPUB_SIZE);
// export functions
char *priv = secp256k1_private_key(bob);
char *pubk = secp256k1_public_key(bob);
printf("Private export: %s\n", priv);
printf("Public export: %s\n", pubk);
free(priv);
secp256k1_t *bobpub = secp256k1_new();
int val = secp256k1_load_public_key(bobpub, "0x03310ec949bd4f7fc24f823add1394c78e1e9d70949ccacf094c027faa20d99e21");
printf("Public key loader: %d\n", val);
secp256k1_dumps(bobpub);
// alice
secp256k1_t *alice = secp256k1_new();
secp256k1_load_key(alice, "0x8225825815f42e1c24a2e98714d99fee1a20b5ac864fbcb7a103cd0f37f0ffec");
printf("\n");
printf("Alice:\n");
dumphex(alice->seckey, SECKEY_SIZE);
dumphex(alice->compressed, COMPPUB_SIZE);
dumphex(alice->xcompressed, XSERPUB_SIZE);
unsigned char *shared1 = secp265k1_shared_key(bob, alice);
unsigned char *shared2 = secp265k1_shared_key(alice, bob);
printf("\n");
printf("Shared Key:\n");
dumphex(shared1, SHARED_SIZE);
dumphex(shared2, SHARED_SIZE);
secp256k1_erase_free(shared1, SHARED_SIZE);
secp256k1_erase_free(shared2, SHARED_SIZE);
// Hello, world!
unsigned char hash[32] = {
0x31, 0x5F, 0x5B, 0xDB, 0x76, 0xD0, 0x78, 0xC4,
0x3B, 0x8A, 0xC0, 0x06, 0x4E, 0x4A, 0x01, 0x64,
0x61, 0x2B, 0x1F, 0xCE, 0x77, 0xC8, 0x69, 0x34,
0x5B, 0xFC, 0x94, 0xC7, 0x58, 0x94, 0xED, 0xD3,
};
unsigned char *sign = secp256k1_sign_hash(bob, hash, sizeof(hash));
printf("\n");
printf("Signature (ecdsa):\n");
dumphex(sign, SERSIG_SIZE);
secp256k1_sign_t *sigobj = secp256k1_load_signature(bob, sign, SERSIG_SIZE);
int valid = secp256k1_sign_verify(bob, sigobj, hash, sizeof(hash));
printf("\n");
printf("Signature valid: %d\n", valid);
secp256k1_sign_free(sigobj);
// using bobpub
sigobj = secp256k1_load_signature(bobpub, sign, SERSIG_SIZE);
valid = secp256k1_sign_verify(bobpub, sigobj, hash, sizeof(hash));
printf("\n");
printf("Signature valid (using bob public key only): %d\n", valid);
secp256k1_erase_free(sign, SERSIG_SIZE);
secp256k1_sign_free(sigobj);
sign = secp256k1_schnorr_sign_hash(bob, hash, sizeof(hash));
printf("\n");
printf("Signature (schnorr):\n");
dumphex(sign, SCHSIG_SIZE);
valid = secp256k1_schnorr_verify(bob, sign, SCHSIG_SIZE, hash, sizeof(hash));
printf("\n");
printf("Signature valid: %d\n", valid);
valid = secp256k1_schnorr_verify(bobpub, sign, SCHSIG_SIZE, hash, sizeof(hash));
printf("\n");
printf("Signature valid (using bob pubkey key only): %d\n", valid);
secp256k1_erase_free(sign, SCHSIG_SIZE);
printf("\n");
printf("Wendy Export:\n");
char *export = secp256k1_export(wendy);
printf(">> %s\n", export);
free(export);
printf("\n");
printf("Wendy Keys dump:\n");
secp256k1_dumps(wendy);
secp256k1_free(bob);
secp256k1_free(alice);
secp256k1_free(wendy);
return 0;
}
#endif

View File

@@ -1,61 +0,0 @@
#ifndef SECP256K1_V_MOD
#define SECP256K1_V_MOD
#include <secp256k1.h>
#include <secp256k1_ecdh.h>
#include <secp256k1_extrakeys.h>
#include <secp256k1_schnorrsig.h>
typedef struct secp256k1_t {
secp256k1_context *kntxt; // library context
unsigned char *seckey; // ec private key
unsigned char *compressed; // ec public key serialized
secp256k1_pubkey pubkey; // ec public key
unsigned char *xcompressed; // x-only serialized key
secp256k1_xonly_pubkey xpubkey; // x-only public key
secp256k1_keypair keypair; // keypair opaque representation
// needed for schnorr
} secp256k1_t;
typedef struct secp256k1_sign_t {
secp256k1_ecdsa_signature sig;
unsigned char *serialized;
size_t length;
} secp256k1_sign_t;
#define SECKEY_SIZE 32 // secret key size
#define SHARED_SIZE 32 // ecdh shared key size
#define COMPPUB_SIZE 33 // compressed public key size
#define XSERPUB_SIZE 32 // x-only public key serialized size
#define SERSIG_SIZE 64 // serialized signature size
#define SCHSIG_SIZE 64 // internal schnorr signature size
#define SHA256_SIZE 32 // sha-256 digest length
secp256k1_t *secp256k1_new();
void secp256k1_free(secp256k1_t *secp);
int secp256k1_generate_key(secp256k1_t *secp);
unsigned char *secp265k1_shared_key(secp256k1_t *private, secp256k1_t *public);
unsigned char *secp256k1_sign_hash(secp256k1_t *secp, unsigned char *hash, size_t length);
secp256k1_sign_t *secp256k1_load_signature(secp256k1_t *secp, unsigned char *serialized, size_t length);
int secp256k1_sign_verify(secp256k1_t *secp, secp256k1_sign_t *signature, unsigned char *hash, size_t length);
unsigned char *secp256k1_schnorr_sign_hash(secp256k1_t *secp, unsigned char *hash, size_t length);
int secp256k1_schnorr_verify(secp256k1_t *secp, unsigned char *signature, size_t siglen, unsigned char *hash, size_t hashlen);
void secp256k1_sign_free(secp256k1_sign_t *signature);
char *secp256k1_export(secp256k1_t *secp);
char *secp256k1_private_key(secp256k1_t *secp);
char *secp256k1_public_key(secp256k1_t *secp);
void secp256k1_dumps(secp256k1_t *secp);
int secp256k1_load_key(secp256k1_t *secp, char *key);
int secp256k1_load_private_key(secp256k1_t *secp, char *key);
int secp256k1_load_public_key(secp256k1_t *secp, char *key);
#endif

View File

@@ -1,112 +0,0 @@
module secp256k1
import encoding.hex
import crypto.sha256
import freeflowuniverse.herolib.crypt.secp256k1
fn test_check() {
println('${'[+] initializing libsecp256 vlang wrapper'}')
wendy := secp256k1.new()!
webdy_priv_key := wendy.private_key_hex()
webdy_pub_key := wendy.public_key_hex()
println('-------')
println('Wendy Private: ${webdy_priv_key}')
println('Wendy Public: ${webdy_pub_key}')
println('-------')
// create 'bob' from a private key, full features will be available
bob := secp256k1.new(
privhex: '0x478b45390befc3097e3e6e1a74d78a34a113f4b9ab17deb87e9b48f43893af83'
)!
// create 'alice' from a private key, full features will be available
alice := secp256k1.new(
privhex: '0x8225825815f42e1c24a2e98714d99fee1a20b5ac864fbcb7a103cd0f37f0ffec'
)!
// create 'bobpub' from bob only public key, reduced features available (only sign check, shared keys, etc.)
bobpub := secp256k1.new(
pubhex: bob.public_key_hex()
)!
// create 'alicepub' from alice only public key, reduced features available
alicepub := secp256k1.new(
pubhex: alice.public_key_hex()
)!
shr1 := bob.sharedkeys(alice)
println('${shr1}')
shr2 := alice.sharedkeys(bob)
println('${shr2}')
// example in real world, where private key is available and only target public key
shr1pub := bob.sharedkeys(alicepub)
println('${shr1pub}')
shr2pub := alice.sharedkeys(bobpub)
println('${shr2pub}')
println('-----')
mut message := 'Hello world, this is my awesome message'
message += message
message += message
message += message
message += message
h256 := sha256.hexhash(message)
println('${h256}')
println('${h256.len}')
println('${sha256.sum(message.bytes())}')
parsed := hex.decode(h256) or { panic(err) }
println('${parsed}')
println('${parsed.len}')
//
// signature (ecdca)
//
signed := alice.sign_data(message.bytes())
println('${signed}')
signed_hex := alice.sign_data_hex(message.bytes())
println('${signed_hex}')
println('${signed_hex.len}')
signed_str := alice.sign_str(message)
println('${signed_str}')
println('${signed_str.len}')
signed_str_hex := alice.sign_str_hex(message)
assert signed_str_hex == '656699dde22d8b89d91070dee4fc8dba136172fb54e6de475024c40e4f8d5111562212c8976b5a4ccd530bdb7f40c5d9bd2cdeeec1473656566fbb9c4576ed8c'
assert signed_str_hex.len == 128
// instanciate alice with only her public key
assert alicepub.verify_data(signed, message.bytes()) == true
assert alicepub.verify_str_hex(signed_str_hex, message) == true
assert alicepub.verify_str_hex(signed_str_hex, message + 's') == false
//
// signature (schnorr)
//
// schnorr_signed := alice.schnorr_sign_data(message.bytes())
// println('${schnorr_signed}')
// schnorr_signed_hex := alice.schnorr_sign_data_hex(message.bytes())
// println('${schnorr_signed_hex}')
// schnorr_signed_str := alice.schnorr_sign_str(message)
// println('${schnorr_signed_str}')
// schnorr_signed_str_hex := alice.schnorr_sign_str_hex(message)
// println('${schnorr_signed_str_hex}')
// println('${alicepub.schnorr_verify_data(schnorr_signed, message.bytes())}')
// println('${alicepub.schnorr_verify_str(schnorr_signed_str, message)}')
// // should fails, it's not the right signature method (ecdsa / schnorr)
// println('${alicepub.verify_data(schnorr_signed, message.bytes())}')
// println('${alicepub.verify_str(schnorr_signed_str, message)}')
}

View File

@@ -1,8 +0,0 @@
Module {
name: 'secp256k1'
description: 'secp256k1 in v'
version: '0.2.0'
license: 'MIT'
dependencies: []
}