Skip to content

crypt.secp256k1 #

libsecp256k1

This is a lib256k1 binding for vlang.

Requirements

make sure the lib is installed

macOS

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

pacman -Su extra/libsecp256k1

Gentoo

emerge dev-libs/libsecp256k1

Features

  • Generate EC keys
  • Load existing EC keys
  • Serialize keys
  • Derivate shared key
  • Sign using ECDSA
  • Verify ECDSA signature
  • Sign using Schnorr
  • 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:

import freeflowuniverse.crystallib.crypt.secp256k1
secp256k1.new()

Constructor without any arguments, will generate a new private and public key

secp256k1.new(privkey: '0x4a21f247ff3744e211e95ec478d5aba94a1d6d8bed613e8a9faece6d048399fc')

Using privkey argument, this will create an object from private key and generate corresponding public key

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:

bob := secp256k1.new(privhex: '0x478b45390befc3097e3e6e1a74d78a34a113f4b9ab17deb87e9b48f43893af83')!
alicepub := secp256k1.new(pubkey: '0x034a87ad6fbf83d89a91c257d4cc038828c6ed9104738ffd4bb7e5069858d4767b')!

shared := bob.sharedkeys(alicepub)
// shared = 0xf114df29d930f0cd37f62cbca36c46773a42bf87e12edcb35d47c4bfbd20514d

This works the same in the opposite direction:

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.

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.

schnorr_sstr := alice.schnorr_sign_str('Hello World !')
valid := alicepub.schnorr_verify_str(schnorr_sstr, 'Hello World !')
// valid = true

fn new #

fn new(args_ Secp256NewArgs) !Secp256k1

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 .

struct Secp256NewArgs #

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

struct Secp256k1 #

struct Secp256k1 {
	cctx &Secp256k1_t
}

fn (Secp256k1) export #

fn (s Secp256k1) export() string

export private key backward compatibility, please use private_key() and public_key() methods

fn (Secp256k1) sharedkeys #

fn (s Secp256k1) sharedkeys(target Secp256k1) []u8

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.

fn (Secp256k1) sharedkeys_hex #

fn (s Secp256k1) sharedkeys_hex(target Secp256k1) string

fn (Secp256k1) sharedkeys_base64 #

fn (s Secp256k1) sharedkeys_base64(target Secp256k1) string

fn (Secp256k1) private_key_hex #

fn (s Secp256k1) private_key_hex() string

returns private key in hex format

fn (Secp256k1) private_key_base64 #

fn (s Secp256k1) private_key_base64() string

fn (Secp256k1) public_key_hex #

fn (s Secp256k1) public_key_hex() string

return public key in hex format

fn (Secp256k1) public_key_base64 #

fn (s Secp256k1) public_key_base64() string

fn (Secp256k1) sign_data #

fn (s Secp256k1) sign_data(data []u8) []u8

sign (ecdsa) data- we force user to pass data to ensure we hash the right waydata to ensure signature is valid and safe

fn (Secp256k1) sign_data_hex #

fn (s Secp256k1) sign_data_hex(data []u8) string

return a hex string of the signature

fn (Secp256k1) sign_data_base64 #

fn (s Secp256k1) sign_data_base64(data []u8) string

fn (Secp256k1) sign_str #

fn (s Secp256k1) sign_str(data string) []u8

fn (Secp256k1) sign_str_hex #

fn (s Secp256k1) sign_str_hex(data string) string

return a hex string of the signature

fn (Secp256k1) sign_str_base64 #

fn (s Secp256k1) sign_str_base64(data string) string

fn (Secp256k1) verify_data #

fn (s Secp256k1) verify_data(signature []u8, data []u8) bool

verify a signature

fn (Secp256k1) verify_str_base64 #

fn (s Secp256k1) verify_str_base64(signature string, input string) bool

fn (Secp256k1) verify_str_hex #

fn (s Secp256k1) verify_str_hex(signature string, input string) bool

fn (Secp256k1) schnorr_sign_data #

fn (s Secp256k1) schnorr_sign_data(data []u8) []u8

sign (schnorr) data- we force user to pass data to ensure we hash the right waydata to ensure signature is valid and safe

fn (Secp256k1) schnorr_sign_data_hex #

fn (s Secp256k1) schnorr_sign_data_hex(data []u8) string

return a hex string of the signature

fn (Secp256k1) schnorr_sign_str #

fn (s Secp256k1) schnorr_sign_str(data string) []u8

fn (Secp256k1) schnorr_sign_str_hex #

fn (s Secp256k1) schnorr_sign_str_hex(data string) string

return a hex string of the signature

fn (Secp256k1) schnorr_verify_data #

fn (s Secp256k1) schnorr_verify_data(signature []u8, data []u8) bool

verify a signature

fn (Secp256k1) schnorr_verify_str #

fn (s Secp256k1) schnorr_verify_str(signature []u8, input string) bool