Files
herolib/lib/crypt/secp256k1/secp256k1.v
2024-12-25 12:38:51 +01:00

352 lines
9.7 KiB
V

@[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())
}