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

@@ -3,6 +3,7 @@ module giteaclient
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.core.playbook { PlayBook }
import freeflowuniverse.herolib.ui.console
import json
__global (
giteaclient_global map[string]&GiteaClient
@@ -31,10 +32,14 @@ pub fn get(args ArgsGet) !&GiteaClient {
mut context := base.context()!
giteaclient_default = args.name
if args.fromdb || args.name !in giteaclient_global {
if context.hero_config_exists('giteaclient', args.name) {
heroscript := context.hero_config_get('giteaclient', args.name)!
mut obj_ := heroscript_loads(heroscript)!
set_in_mem(obj_)!
mut r := context.redis()!
if r.hexists('context:giteaclient', args.name)! {
data := r.hget('context:giteaclient', args.name)!
if data.len == 0 {
return error('giteaclient with name:${args.name} does not exist, prob bug.')
}
mut obj := json.decode(GiteaClient,data)!
set_in_mem(obj)!
}else{
if args.create {
new(args)!
@@ -54,20 +59,22 @@ pub fn set(o GiteaClient) ! {
set_in_mem(o)!
giteaclient_default = o.name
mut context := base.context()!
heroscript := heroscript_dumps(o)!
context.hero_config_set('giteaclient', o.name, heroscript)!
mut r := context.redis()!
r.hset('context:giteaclient', o.name, json.encode(o))!
}
// does the config exists?
pub fn exists(args ArgsGet) !bool {
mut context := base.context()!
return context.hero_config_exists('giteaclient', args.name)
mut r := context.redis()!
return r.hexists('context:giteaclient', args.name)!
}
pub fn delete(args ArgsGet) ! {
mut context := base.context()!
giteaclient_global.delete(args.name)
context.hero_config_delete('giteaclient', args.name)!
mut r := context.redis()!
r.hdel('context:giteaclient', args.name)!
}
@[params]
@@ -86,11 +93,11 @@ pub fn list(args ArgsList) ![]&GiteaClient {
giteaclient_default = ''
}
if args.fromdb {
for name in context.hero_config_list('giteaclient')!{
mut hscript := context.hero_config_get('giteaclient', name)!
mut obj := heroscript_loads(hscript)!
set_in_mem(obj)!
res << &obj
mut r := context.redis()!
mut l := r.hkeys('context:giteaclient')!
for name in l{
res << get(name:name,fromdb:true)!
}
return res
} else {

View File

@@ -57,13 +57,4 @@ fn obj_init(mycfg_ GiteaClient) !GiteaClient {
return mycfg
}
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj GiteaClient) !string {
return encoderhero.encode[GiteaClient](obj)!
}
pub fn heroscript_loads(heroscript string) !GiteaClient {
mut obj := encoderhero.decode[GiteaClient](heroscript)!
return obj
}
pub

View File

@@ -2,23 +2,14 @@ module base
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.data.dbfs
import freeflowuniverse.herolib.crypt.aes_symmetric
import freeflowuniverse.herolib.ui
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.core.rootpath
import json
import os
import crypto.md5
@[heap]
pub struct Context {
mut:
// priv_key_ ?&secp256k1.Secp256k1 @[skip; str: skip]
params_ ?&paramsparser.Params
dbcollection_ ?&dbfs.DBCollection @[skip; str: skip]
redis_ ?&redisclient.Redis @[skip; str: skip]
path_ ?pathlib.Path
pub mut:
@@ -34,10 +25,10 @@ pub mut:
params string
coderoot string
interactive bool
secret string // is hashed secret
priv_key string // encrypted version
db_path string // path to dbcollection
encrypt bool
// secret string // is hashed secret
// priv_key string // encrypted version
// db_path string // path to dbcollection
// encrypt bool
}
// return the gistructure as is being used in context
@@ -97,105 +88,44 @@ fn (mut self Context) cfg_redis_exists() !bool {
return r.exists('context:config')!
}
// return db collection
pub fn (mut self Context) dbcollection() !&dbfs.DBCollection {
mut dbc2 := self.dbcollection_ or {
if self.config.db_path.len == 0 {
self.config.db_path = '${os.home_dir()}/hero/db/${self.config.id}'
}
mut dbc := dbfs.get(
contextid: self.config.id
dbpath: self.config.db_path
secret: self.config.secret
)!
self.dbcollection_ = &dbc
&dbc
}
return dbc2
}
pub fn (mut self Context) db_get(dbname string) !dbfs.DB {
mut dbc := self.dbcollection()!
return dbc.db_get_create(name: dbname, withkeys: true)!
}
// always return the config db which is the same for all apps in context
pub fn (mut self Context) db_config_get() !dbfs.DB {
mut dbc := self.dbcollection()!
return dbc.db_get_create(name: 'config', withkeys: true)!
}
// pub fn (mut self Context) hero_config_set(cat string, name string, content_ string) ! {
// mut content := texttools.dedent(content_)
// content = rootpath.shell_expansion(content)
// path := '${self.path()!.path}/${cat}/${name}.json'
// mut config_file := pathlib.get_file(path: path,create: true)!
// config_file.write(content)!
// pub fn (mut self Context) secret_encrypt(txt string) !string {
// return aes_symmetric.encrypt_str(txt, self.secret_get()!)
// }
// pub fn (mut self Context) hero_config_delete(cat string, name string) ! {
// path := '${self.path()!.path}/${cat}/${name}.json'
// mut config_file := pathlib.get_file(path: path)!
// config_file.delete()!
// pub fn (mut self Context) secret_decrypt(txt string) !string {
// return aes_symmetric.decrypt_str(txt, self.secret_get()!)
// }
// pub fn (mut self Context) hero_config_exists(cat string, name string) bool {
// path := '${os.home_dir()}/hero/context/${self.config.name}/${cat}/${name}.json'
// return os.exists(path)
// pub fn (mut self Context) secret_get() !string {
// mut secret := self.config.secret
// if secret == '' {
// self.secret_configure()!
// secret = self.config.secret
// self.save()!
// }
// if secret == '' {
// return error("can't get secret")
// }
// return secret
// }
// pub fn (mut self Context) hero_config_get(cat string, name string) !string {
// path := '${self.path()!.path}/${cat}/${name}.json'
// mut config_file := pathlib.get_file(path: path, create: false)!
// return config_file.read()!
// // show a UI in console to configure the secret
// pub fn (mut self Context) secret_configure() ! {
// mut myui := ui.new()!
// console.clear()
// secret_ := myui.ask_question(question: 'Please enter your hero secret string:')!
// self.secret_set(secret_)!
// }
// pub fn (mut self Context) hero_config_list(cat string) ![]string {
// path := '${self.path()!.path}/${cat}'
// mut config_files := os.ls(path)!
// config_files = config_files.filter(it.ends_with('.json').map(it.split('.')[0] or {panic("bug")})
// return config_files
// // unhashed secret
// pub fn (mut self Context) secret_set(secret_ string) ! {
// secret := secret_.trim_space()
// secret2 := md5.hexhash(secret)
// self.config.secret = secret2
// self.save()!
// }
pub fn (mut self Context) secret_encrypt(txt string) !string {
return aes_symmetric.encrypt_str(txt, self.secret_get()!)
}
pub fn (mut self Context) secret_decrypt(txt string) !string {
return aes_symmetric.decrypt_str(txt, self.secret_get()!)
}
pub fn (mut self Context) secret_get() !string {
mut secret := self.config.secret
if secret == '' {
self.secret_configure()!
secret = self.config.secret
self.save()!
}
if secret == '' {
return error("can't get secret")
}
return secret
}
// show a UI in console to configure the secret
pub fn (mut self Context) secret_configure() ! {
mut myui := ui.new()!
console.clear()
secret_ := myui.ask_question(question: 'Please enter your hero secret string:')!
self.secret_set(secret_)!
}
// unhashed secret
pub fn (mut self Context) secret_set(secret_ string) ! {
secret := secret_.trim_space()
secret2 := md5.hexhash(secret)
self.config.secret = secret2
self.save()!
}
pub fn (mut self Context) path() !pathlib.Path {
return self.path_ or {
path2 := '${os.home_dir()}/hero/context/${self.config.name}'

View File

@@ -36,30 +36,12 @@ pub fn context_new(args_ ContextConfigArgs) !&Context {
params: args_.params
coderoot: args_.coderoot
interactive: args_.interactive
secret: args_.secret
encrypt: args_.encrypt
}
if args.encrypt && args.secret == '' && args.interactive {
mut myui := ui.new()!
console.clear()
args.secret = myui.ask_question(question: 'Please enter your hero secret string:')!
}
if args.encrypt && args.secret.len > 0 {
args.secret = md5.hexhash(args.secret)
}
mut c := Context{
config: args
}
// if args_.priv_key_hex.len > 0 {
// c.privkey_set(args_.priv_key_hex)!
// }
// c.save()!
if args.params.len > 0 {
mut p := paramsparser.new('')!
c.params_ = &p

View File

@@ -3,7 +3,6 @@ module base
import freeflowuniverse.herolib.data.ourtime
// import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.herolib.data.dbfs
import freeflowuniverse.herolib.core.logger
import json
import freeflowuniverse.herolib.core.pathlib
@@ -32,15 +31,6 @@ pub mut:
// return 'hero:sessions:${self.guid()}'
// }
// get db of the session, is unique per session
pub fn (mut self Session) db_get() !dbfs.DB {
return self.context.db_get('session_${self.name}')!
}
// get the db of the config, is unique per context
pub fn (mut self Session) db_config_get() !dbfs.DB {
return self.context.db_get('config')!
}
// load the params from redis
pub fn (mut self Session) load() ! {

View File

@@ -66,14 +66,3 @@ fn configure() ! {
}
@end
/////////////NORMALLY NO NEED TO TOUCH
pub fn heroscript_dumps(obj ${args.classname}) !string {
return encoderhero.encode[${args.classname} ](obj)!
}
pub fn heroscript_loads(heroscript string) !${args.classname} {
mut obj := encoderhero.decode[${args.classname}](heroscript)!
return obj
}

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: []
}

View File

@@ -1,29 +1,27 @@
module core
import freeflowuniverse.herolib.core.base
import freeflowuniverse.herolib.data.dbfs
import freeflowuniverse.herolib.core.redisclient
import freeflowuniverse.herolib.ui.console
fn donedb() !&dbfs.DB {
fn donedb() !&redisclient.Redis {
mut context := base.context()!
mut collection := context.dbcollection()!
mut db := collection.db_get_create(name: 'todo', withkeys: true)!
return &db
return context.redis()!
}
pub fn done_set(key string, val string) ! {
mut db := donedb()!
db.set(key: key, value: val)!
db.hset("context:done",key, val)!
}
pub fn done_get(key string) ?string {
mut db := donedb() or { panic(err) }
return db.get(key: key) or { return none }
return db.hget("context:done", key) or { return none }
}
pub fn done_delete(key string) ! {
mut db := donedb()!
db.delete(key: key)!
db.hdel("context:done", key)!
}
pub fn done_get_str(key string) string {
@@ -38,7 +36,7 @@ pub fn done_get_int(key string) int {
pub fn done_exists(key string) bool {
mut db := donedb() or { panic(err) }
return db.exists(key: key) or { false }
return db.hexists("context:done", key) or { false }
}
pub fn done_print() ! {
@@ -54,5 +52,5 @@ pub fn done_print() ! {
pub fn done_reset() ! {
mut db := donedb()!
db.destroy()!
db.del("context:done")!
}