This commit is contained in:
2024-12-25 12:38:51 +01:00
parent 4848703a8b
commit f77c7ba874
50 changed files with 3008 additions and 32 deletions

View File

@@ -1,7 +1,7 @@
module base module base
import json import json
// import freeflowuniverse.crystallib.ui.console // import freeflowuniverse.herolib.ui.console
// is an object which has a configurator, session and config object which is unique for the model // is an object which has a configurator, session and config object which is unique for the model
// T is the Config Object // T is the Config Object

View File

@@ -1,7 +1,7 @@
module base module base
import json import json
import freeflowuniverse.crystallib.ui.console import freeflowuniverse.herolib.ui.console
@[heap] @[heap]
pub struct Configurator[T] { pub struct Configurator[T] {

View File

@@ -1,15 +1,15 @@
module base module base
import freeflowuniverse.crystallib.data.paramsparser import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.crystallib.clients.redisclient import freeflowuniverse.herolib.clients.redisclient
import freeflowuniverse.crystallib.data.dbfs import freeflowuniverse.herolib.data.dbfs
// import freeflowuniverse.crystallib.crypt.secp256k1 // import freeflowuniverse.herolib.crypt.secp256k1
import freeflowuniverse.crystallib.crypt.aes_symmetric import freeflowuniverse.herolib.crypt.aes_symmetric
import freeflowuniverse.crystallib.ui import freeflowuniverse.herolib.ui
import freeflowuniverse.crystallib.ui.console import freeflowuniverse.herolib.ui.console
import freeflowuniverse.crystallib.core.pathlib import freeflowuniverse.herolib.core.pathlib
import freeflowuniverse.crystallib.core.texttools import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.crystallib.core.rootpath import freeflowuniverse.herolib.core.rootpath
import json import json
import os import os
import crypto.md5 import crypto.md5

View File

@@ -1,7 +1,7 @@
module base module base
import freeflowuniverse.crystallib.data.ourtime import freeflowuniverse.herolib.data.ourtime
import freeflowuniverse.crystallib.data.paramsparser import freeflowuniverse.herolib.data.paramsparser
import json import json
@[params] @[params]

View File

@@ -1,8 +1,8 @@
module base module base
import freeflowuniverse.crystallib.data.paramsparser import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.crystallib.ui import freeflowuniverse.herolib.ui
import freeflowuniverse.crystallib.ui.console import freeflowuniverse.herolib.ui.console
import crypto.md5 import crypto.md5
@[params] @[params]

View File

@@ -25,7 +25,7 @@ Session id is $contextid:$sessionid (e.g. 10:111)
- coderoot string //this will define where all code is checked out - coderoot string //this will define where all code is checked out
```v ```v
import freeflowuniverse.crystallib.core.base import freeflowuniverse.herolib.core.base
mut session:=context_new( mut session:=context_new(
coderoot:'/tmp/code' coderoot:'/tmp/code'

View File

@@ -1,13 +1,13 @@
module base module base
import freeflowuniverse.crystallib.data.ourtime import freeflowuniverse.herolib.data.ourtime
// import freeflowuniverse.crystallib.core.texttools // import freeflowuniverse.herolib.core.texttools
import freeflowuniverse.crystallib.data.paramsparser import freeflowuniverse.herolib.data.paramsparser
import freeflowuniverse.crystallib.data.dbfs import freeflowuniverse.herolib.data.dbfs
import json import json
// import freeflowuniverse.crystallib.core.pathlib // import freeflowuniverse.herolib.core.pathlib
// import freeflowuniverse.crystallib.develop.gittools // import freeflowuniverse.herolib.develop.gittools
// import freeflowuniverse.crystallib.ui.console // import freeflowuniverse.herolib.ui.console
@[heap] @[heap]
pub struct Session { pub struct Session {

View File

@@ -1,7 +1,7 @@
module base module base
import freeflowuniverse.crystallib.data.ourtime import freeflowuniverse.herolib.data.ourtime
import freeflowuniverse.crystallib.core.texttools import freeflowuniverse.herolib.core.texttools
pub struct ErrorArgs { pub struct ErrorArgs {
pub mut: pub mut:

View File

@@ -1,7 +1,7 @@
module base module base
import freeflowuniverse.crystallib.data.ourtime import freeflowuniverse.herolib.data.ourtime
import freeflowuniverse.crystallib.core.texttools import freeflowuniverse.herolib.core.texttools
@[heap] @[heap]
pub struct Logger { pub struct Logger {

View File

@@ -0,0 +1,79 @@
module aes_symmetric
import crypto.aes
import crypto.md5
import crypto.cipher
import encoding.binary as bin
import encoding.base64
fn padded_length(source []u8, blocksize int) int {
if (source.len % blocksize) == 0 {
return source.len
}
return ((source.len / blocksize) + 1) * blocksize
}
pub fn encrypt_str(data string, secret string) string {
mut d := encrypt(data.bytes(), secret)
return base64.encode(d)
}
pub fn encrypt(data []u8, secret string) []u8 {
key := md5.hexhash(secret)
// initialize aes with md5 of the secret (always same length)
ae := aes.new_cipher(key.bytes())
// use null iv
iv := []u8{len: ae.block_size}
mut cb := cipher.new_cbc(ae, iv)
// add padding to data
length := padded_length(data, ae.block_size)
mut padded := []u8{len: length}
copy(mut padded, data)
// encrypt blocks
mut destination := []u8{len: length}
cb.encrypt_blocks(mut destination, padded)
destination << []u8{len: 2}
// we add the len of the padding at end
bin.little_endian_put_u16_end(mut destination, u16(length - data.len))
return destination
}
// input needs to be base64 encoded
pub fn decrypt_str(data string, secret string) string {
if data.len == 0 {
return ''
// print_backtrace()
// panic('data cannot be empty (decrypt aes)')
}
data_decoded := base64.decode(data)
mut d := decrypt(data_decoded, secret)
return d.bytestr()
}
pub fn decrypt(data []u8, secret string) []u8 {
key := md5.hexhash(secret)
lenextra := bin.little_endian_u16_end(data)
data2 := data[0..data.len - 2]
// initialize aes with md5 of the secret (always same length)
ae := aes.new_cipher(key.bytes())
// use null iv
iv := []u8{len: ae.block_size}
mut cb := cipher.new_cbc(ae, iv)
// decrypt blocks
mut destination := []u8{len: data2.len}
cb.decrypt_blocks(mut destination, data2)
return destination[0..destination.len - lenextra]
}

View File

@@ -0,0 +1,7 @@
module aes_symmetric
fn test_check() {
d := encrypt_str('data', 'mysecret')
d2 := decrypt_str(d, 'mysecret')
assert d2 == 'data'
}

105
lib/crypt/crpgp/README.md Normal file
View File

@@ -0,0 +1,105 @@
# CRPGP
This module based on [threefold/crpgp](https://github.com/threefoldtech/crpgp) repo, which is a wrapper for [rpgp](https://github.com/rpgp/rpgp)
## Install
All needed libs can be installed from [install.sh](./install.sh)
## High Level API
- List of supported functions with a description for each method
| Function | Description | Input | Output |
| ------------------------------------- | -------------------------------------------------------------------------------- | ----------------- | --------------- |
| generate_key | generate a signed secret key using name, email, comment, key_type and key_length | KeyParams | SignedSecretKey |
| SignedSecretKey.get_signed_public_key | get a signed public key from signed secret key | | SignedPublicKey |
| import_publickey | import signed public key in armored format | string | SignedPublicKey |
| import_publickey_from_file | like import_publickey but from a file | string | SignedPublicKey |
| import_secretkey | import signed secret key in armored format | string | SignedSecretKey |
| import_secretkey_from_file | like import_secretkey but from a file | string | SignedSecretKey |
| SignedPublicKey.sign_message | sign message with your signed public key | string | Signature |
| SignedSecretKey.verify_signature | verify signature for message with your signed secret key | Signature, string | bool |
| SignedPublicKey.encrypt_from_text | encrypt message from text using your signed public key | string | []byte |
| SignedSecretKey.decrypt_to_text | decrypt encrypted messages to text using your signed secret key | []byte | string |
| Signature.to_bytes | convert signature to bytes | | []byte |
| Signature.to_hex | convert signature to hex | | string |
| signature_from_hex | convert hex string to signature | string | Signature |
### Notes:
- key_type is enum of [`cv25519`, `rsa`], by default key_type will be `cv25519`
- key_length used if key_type is `rsa` and `3072` is a default value
## How to use !
- We will go together step by step to illustrate how to use this module.
- Import crpgp module
```v
import crpgp
```
- We have **two** options here, generate a new key, or import
1. Generate:
- CV25519 key:
```v
ssk := crpgp.generate_key(name: <YOUR_NAME>, email: <YOUR_EMAIL>) !
```
- RSA key
```v
// key_length 3072 if not passed
ssk := crpgp.generate_key(name: <YOUR_NAME>, email: <YOUR_EMAIL>, key_type: .rsa) !
```
or
```v
ssk := crpgp.generate_key(name: <YOUR_NAME>, email: <YOUR_EMAIL>, key_type: .rsa, key_length: <LENGTH>) !
```
2. Import signed secret key from file
```v
ssk := crpgp.import_secretkey_from_file(<FILE_PATH>) !
```
- Now we have a signed secret key, let us get a signed public key, also we have **two** options, get from secret key or import
1. Get signed public key from signed secret key
```v
spk := ssk.get_signed_public_key() !
```
2. import signed public key from file
```v
spk := crpgp.import_publickey_from_file(<FILE_PATH>) !
```
- Now we have a signed public and secret key, let us try to sign and verify a message
```v
message := "these are my secrets\n12345"
mut sig := ssk.sign_message(message) !
println(sig.to_hex() !) // In case we want to convert it to hex, here just to display it
if spk.verify_signature(sig, message) {
println('✅ message signed and verified!')
}
```
- let us try encrypt and decrypt message.
```v
encrypted := spk.encrypt_from_text(message) !
decrypted := ssk.decrypt_to_text(encrypted) !
if message == decrypted {
println('✅ message encrypted and decrypted!')
}
```
- Done !

View File

@@ -0,0 +1,13 @@
module crpgp
// Things that are hard to convert from the c header file automatically
pub enum KeyType_Tag {
rsa
ecdh
ed_dsa
}
struct C.KeyType {
tag KeyType_Tag
rsa u32
}

108
lib/crypt/crpgp/crpgp.v Normal file
View File

@@ -0,0 +1,108 @@
module crpgp
struct C.KeyType {}
struct KeyType {
internal &C.KeyType
}
struct C.PublicKey {}
struct PublicKey {
internal &C.PublicKey
}
struct C.SecretKey {}
struct SecretKey {
internal &C.SecretKey
}
struct C.SecretKeyParams {}
struct SecretKeyParams {
internal &C.SecretKeyParams
}
struct C.SecretKeyParamsBuilder {}
struct SecretKeyParamsBuilder {
internal &C.SecretKeyParamsBuilder
}
struct C.Signature {}
struct Signature {
internal &C.Signature
}
struct C.SignedPublicKey {}
pub struct SignedPublicKey {
internal &C.SignedPublicKey
}
struct C.SignedPublicSubKey {}
struct SignedPublicSubKey {
internal &C.SignedPublicSubKey
}
struct C.SignedSecretKey {}
pub struct SignedSecretKey {
internal &C.SignedSecretKey
}
struct C.SubkeyParams {}
struct SubkeyParams {
internal &C.SubkeyParams
}
struct C.SubkeyParamsBuilder {}
struct SubkeyParamsBuilder {
internal &C.SubkeyParamsBuilder
}
fn C.last_error_length() int
fn C.error_message(&char, int) int
fn C.public_key_verify(&C.PublicKey, &u8, u64, &C.Signature) char
fn C.public_key_encrypt(&C.PublicKey, &u8, &u64) &u8
fn C.public_key_sign_and_free(&C.PublicKey, &C.SignedSecretKey) &C.SignedPublicKey
fn C.public_key_free(&C.PublicKey) char
fn C.secret_key_sign(&C.SecretKey) &C.SignedSecretKey
fn C.secret_key_free(&C.SecretKey) char
fn C.params_generate_secret_key_and_free(&C.SecretKeyParams) &C.SecretKey
fn C.params_builder_new() &C.SecretKeyParamsBuilder
fn C.params_builder_primary_user_id(&C.SecretKeyParamsBuilder, &char) char
fn C.params_builder_key_type(&C.SecretKeyParamsBuilder, C.KeyType) char
fn C.params_builder_subkey(&C.SecretKeyParamsBuilder, &C.SubkeyParams) char
fn C.params_builder_build(&C.SecretKeyParamsBuilder) &C.SecretKeyParams
fn C.params_builder_free(&C.SecretKeyParamsBuilder) char
fn C.signature_serialize(&C.Signature, &u64) &u8
fn C.signature_deserialize(&u8, u64) &C.Signature
fn C.signature_free(&C.Signature) char
fn C.signed_public_key_verify(&C.SignedPublicKey, &u8, u64, &C.Signature) char
fn C.signed_public_key_encrypt(&C.SignedPublicKey, &u8, &u64) &u8
fn C.signed_public_key_encrypt_with_any(&C.SignedPublicKey, &u8, &u64) &u8
fn C.signed_public_key_to_bytes(&C.SignedPublicKey, &u64) &u8
fn C.signed_public_key_from_bytes(&u8, u64) &C.SignedPublicKey
fn C.signed_public_key_to_armored(&C.SignedPublicKey) &char
fn C.signed_public_key_from_armored(&char) &C.SignedPublicKey
fn C.signed_public_key_free(&C.SignedPublicKey) char
fn C.signed_secret_key_public_key(&C.SignedSecretKey) &C.PublicKey
fn C.signed_secret_key_create_signature(&C.SignedSecretKey, &u8, u64) &C.Signature
fn C.signed_secret_key_decrypt(&C.SignedSecretKey, &u8, &u64) &u8
fn C.signed_secret_key_free(&C.SignedSecretKey) char
fn C.signed_secret_key_to_bytes(&C.SignedSecretKey, &u64) &u8
fn C.signed_secret_key_from_bytes(&u8, u64) &C.SignedSecretKey
fn C.signed_secret_key_to_armored(&C.SignedSecretKey) &char
fn C.signed_secret_key_from_armored(&char) &C.SignedSecretKey
fn C.subkey_params_free(&C.SubkeyParams) char
fn C.subkey_params_builder_new() &C.SubkeyParamsBuilder
fn C.subkey_params_builder_key_type(&C.SubkeyParamsBuilder, C.KeyType) char
fn C.subkey_params_builder_free(&C.SubkeyParamsBuilder) char
fn C.subkey_params_builder_build(&C.SubkeyParamsBuilder) &C.SubkeyParams
fn C.ptr_free(&u8) char

10
lib/crypt/crpgp/err.v Normal file
View File

@@ -0,0 +1,10 @@
module crpgp
fn construct_error() !int {
// todo: call the func to get the error length
err_buf := unsafe { malloc(1024) }
C.error_message(err_buf, 1024)
str := unsafe { cstring_to_vstring(err_buf) }
unsafe { free(err_buf) }
return error(str)
}

53
lib/crypt/crpgp/install.sh Executable file
View File

@@ -0,0 +1,53 @@
#!/bin/bash
TMP_DIR=/tmp/crpgp
TMP_LIB_PATH=$TMP_DIR/libcrpgp.so
TMP_HEADER_PATH=$TMP_DIR/crpgp.h
CRPGP_DIR=$HOME/.vmodules/freeflowuniverse/herolib.crypt.crpgp
V_CRPGP_PATH=$CRPGP_DIR/crpgp.v
# mkdir TMP_DIR
mkdir -p $TMP_DIR
# Download crpgp lib and header file
echo "- Download libcrpgp.so"
curl -L https://github.com/threefoldtech/crpgp/releases/download/v0.0.1/libcrpgp.so --output $TMP_LIB_PATH
echo "- Download crpgp.h"
curl -L https://github.com/threefoldtech/crpgp/releases/download/v0.0.1/crpgp.h --output $TMP_HEADER_PATH
echo "✅ Downloaded!"
# Update crpgp.v (if herolib in vmodules)
if [ -d $CRPGP_DIR ]; then
echo "- Updating crpgp.v ..."
echo -e 'module crpgp\n' >$V_CRPGP_PATH
cat $TMP_HEADER_PATH |
egrep -o 'struct [a-zA-Z_]+' |
sort | uniq |
sed 's/struct \([a-zA-Z]*\)/struct C.\1 {}\nstruct \1 {\n internal \&C.\1\n}/g' \
>>$V_CRPGP_PATH
cat $TMP_HEADER_PATH |
sed -z 's/\n\s\s\s*/ /g' |
grep ');' |
sed 's/ [*]/* /g' |
sed 's/ [a-z_][a-z_]*[)]/\)/g' |
sed 's/struct \([a-zA-Z_]*\)[*]/\&C.\1/g' |
sed 's/struct \([a-zA-Z_]*\)/C.\1/g' |
sed -z 's/ [a-z_]*,/,/g' |
sed 's/^\([^ ]*\) \(.*\);$/fn C.\2 \1/g' |
sed 's/uint8_t/u8/g' |
sed 's/size_t/u64/g' |
sed 's/\([a-zA-Z0-9]*\)[*]/\&\1/g' |
sed 's/[(]void[)]/()/g' \
>>$V_CRPGP_PATH
echo "✅ Updated!"
else
echo "- Crystallib not found"
fi
# Move crpgp lib and header file in system dirs
sudo mv $TMP_LIB_PATH /usr/lib
sudo mv $TMP_HEADER_PATH /usr/include
# Delete tmp files
rm -rf $TMP_DIR
echo "✅ Installation Done!"

113
lib/crypt/crpgp/pgp.v Normal file
View File

@@ -0,0 +1,113 @@
module crpgp
import os
import encoding.hex
#flag -lcrpgp
#include <crpgp.h>
// KeyParams
@[params]
pub struct KeyParams {
name string @[required]
email string @[required]
key_type CipherSuite
length u32 = 3072
comment string
}
pub enum CipherSuite {
cv25519 // ed_dsa primary, ecdh sub
rsa // rsa primary (rsa and bitsize can be parameterized)
}
fn (self KeyParams) id() string {
comment := if self.comment != '' { '(${self.comment}) ' } else { '' }
return '${self.name} ${comment}<${self.email}>'
}
pub fn generate_key(k KeyParams) !SignedSecretKey {
mut primarykey_type := C.KeyType{}
mut subkey_type := C.KeyType{}
match k.key_type {
.cv25519 {
primarykey_type = C.KeyType{KeyType_Tag.ed_dsa, 0}
subkey_type = C.KeyType{KeyType_Tag.ecdh, 0}
}
.rsa {
primarykey_type = C.KeyType{KeyType_Tag.rsa, k.length}
subkey_type = C.KeyType{KeyType_Tag.rsa, k.length}
}
}
builder := new_secret_key_param_builder()!
builder.primary_key_id(k.id())!
builder.key_type(primarykey_type)!
subkey_builder := new_subkey_params_builder()
subkey_builder.key_type(subkey_type)!
subkey := subkey_builder.build()!
builder.subkey(subkey)!
params := builder.build()!
sk := params.generate_and_free()!
ssk := sk.sign()!
return ssk
}
// import functions
// import public key from ASCII armor text format
pub fn import_publickey(data string) !SignedPublicKey {
return signed_public_key_from_armored(data)
}
// import public key from ASCII armor file format
pub fn import_publickey_from_file(path string) !SignedPublicKey {
content := os.read_file(path)!
return import_publickey(content)
}
// import secret key from ASCII armor text format
pub fn import_secretkey(data string) !SignedSecretKey {
return signed_secret_key_from_armored(data)
}
// import secret key from ASCII armor file format
pub fn import_secretkey_from_file(path string) !SignedSecretKey {
content := os.read_file(path)!
return import_secretkey(content)
}
// Public Key Functions
pub fn (spk &SignedPublicKey) verify_signature(sig &Signature, message string) bool {
spk.verify(message.bytes(), sig) or { return false }
return true
}
pub fn (spk &SignedPublicKey) encrypt_from_text(message string) ![]byte {
return spk.encrypt_with_any(message.bytes())
}
// Secret Key Fucntions
pub fn (ssk &SignedSecretKey) sign_message(message string) !Signature {
return ssk.create_signature(message.bytes())
}
pub fn (ssk &SignedSecretKey) decrypt_to_text(encrypted_message []byte) !string {
return ssk.decrypt(encrypted_message)!.bytestr()
}
pub fn (ssk &SignedSecretKey) get_signed_public_key() !SignedPublicKey {
return ssk.public_key()!.sign_and_free(ssk)
}
// Signature Fucntions
fn (sig &Signature) to_bytes() ![]byte {
return sig.serialize()
}
pub fn (sig &Signature) to_hex() !string {
return hex.encode(sig.to_bytes()!)
}
pub fn signature_from_hex(sig string) !Signature {
sig_bytes := hex.decode(sig)!
return deserialize_signature(sig_bytes)
}

292
lib/crypt/crpgp/types.v Normal file
View File

@@ -0,0 +1,292 @@
module crpgp
import freeflowuniverse.herolib.ui.console
pub fn new_secret_key_param_builder() !SecretKeyParamsBuilder {
builder := C.params_builder_new()
if u64(builder) == 0 {
construct_error()!
return error('')
}
return SecretKeyParamsBuilder{
internal: builder
}
}
pub fn (b &SecretKeyParamsBuilder) primary_key_id(primary_key_id string) ! {
if C.params_builder_primary_user_id(b.internal, &char(primary_key_id.str)) != 0 {
construct_error()!
}
}
pub fn (b &SecretKeyParamsBuilder) key_type(key_type C.KeyType) ! {
if C.params_builder_key_type(b.internal, key_type) != 0 {
construct_error()!
}
}
pub fn (b &SecretKeyParamsBuilder) subkey(subkey SubkeyParams) ! {
if C.params_builder_subkey(b.internal, subkey.internal) != 0 {
construct_error()!
}
}
pub fn (b &SecretKeyParamsBuilder) build() !SecretKeyParams {
params1 := C.params_builder_build(b.internal)
if u64(params1) == 0 {
console.print_debug('failed to build secret key params')
construct_error()!
return error('')
}
return SecretKeyParams{
internal: params1
}
}
pub fn (s &SecretKeyParams) generate_and_free() !SecretKey {
sk := C.params_generate_secret_key_and_free(s.internal)
if u64(sk) == 0 {
construct_error()!
return error('')
}
return SecretKey{
internal: sk
}
}
pub fn (s &SecretKey) sign() !SignedSecretKey {
ssk := C.secret_key_sign(s.internal)
if u64(ssk) == 0 {
construct_error()!
return error('')
}
return SignedSecretKey{
internal: ssk
}
}
pub fn (s &SignedSecretKey) create_signature(data []byte) !Signature {
sig := C.signed_secret_key_create_signature(s.internal, &u8(&data[0]), data.len)
if u64(sig) == 0 {
construct_error()!
return error('')
}
return Signature{
internal: sig
}
}
pub fn (s &SignedSecretKey) decrypt(data []byte) ![]byte {
len := u64(data.len)
decrypted := C.signed_secret_key_decrypt(s.internal, &u8(&data[0]), &len)
if u64(decrypted) == 0 {
construct_error()!
return error('')
}
return cu8_to_vbytes(decrypted, len)
}
pub fn (s &SignedSecretKey) public_key() !PublicKey {
pk := C.signed_secret_key_public_key(s.internal)
if u64(pk) == 0 {
construct_error()!
return error('')
}
return PublicKey{
internal: pk
}
}
pub fn (s &SignedSecretKey) to_bytes() ![]byte {
len := u64(0)
ser := C.signed_secret_key_to_bytes(s.internal, &len)
if u64(ser) == 0 {
construct_error()!
return error('')
}
res := cu8_to_vbytes(ser, len)
C.ptr_free(&u8(ser))
return res
}
pub fn signed_secret_key_from_bytes(bytes []byte) !SignedSecretKey {
ser := C.signed_secret_key_from_bytes(&u8(&bytes[0]), bytes.len)
if u64(ser) == 0 {
construct_error()!
return error('')
}
return SignedSecretKey{
internal: ser
}
}
pub fn (s &SignedSecretKey) to_armored() !string {
ser := C.signed_secret_key_to_armored(s.internal)
if u64(ser) == 0 {
construct_error()!
return error('')
}
res := unsafe { cstring_to_vstring(ser) }
C.ptr_free(&u8(ser))
return res
}
pub fn signed_secret_key_from_armored(s string) !SignedSecretKey {
ser := C.signed_secret_key_from_armored(s.str)
if u64(ser) == 0 {
construct_error()!
return error('')
}
return SignedSecretKey{
internal: ser
}
}
pub fn (s &Signature) serialize() ![]byte {
len := u64(0)
ser := C.signature_serialize(s.internal, &len)
if u64(ser) == 0 {
construct_error()!
return error('')
}
res := cu8_to_vbytes(ser, len)
C.ptr_free(ser)
return res
}
pub fn deserialize_signature(bytes []byte) !Signature {
// TODO: is the pointer arith here ok!
sig := C.signature_deserialize(&u8(&bytes[0]), bytes.len)
if u64(sig) == 0 {
construct_error()!
return error('')
}
return Signature{
internal: sig
}
}
pub fn (p &PublicKey) verify(data []byte, sig &Signature) ! {
ok := C.public_key_verify(p.internal, &u8(&data[0]), data.len, sig.internal)
if ok != 0 {
construct_error()!
return error('')
}
}
pub fn (p &PublicKey) sign_and_free(sk SignedSecretKey) !SignedPublicKey {
signed := C.public_key_sign_and_free(p.internal, sk.internal)
if u64(signed) == 0 {
construct_error()!
return error('')
}
return SignedPublicKey{
internal: signed
}
}
pub fn (s &PublicKey) encrypt(data []byte) ![]byte {
len := u64(data.len)
encrypted := C.public_key_encrypt(s.internal, &u8(&data[0]), &len)
if u64(encrypted) == 0 {
construct_error()!
return error('')
}
return cu8_to_vbytes(encrypted, len)
}
pub fn new_subkey_params_builder() SubkeyParamsBuilder {
return SubkeyParamsBuilder{
internal: C.subkey_params_builder_new()
}
}
pub fn (b &SubkeyParamsBuilder) key_type(key_type C.KeyType) ! {
if C.subkey_params_builder_key_type(b.internal, key_type) != 0 {
construct_error()!
}
}
pub fn (b &SubkeyParamsBuilder) build() !SubkeyParams {
subkey := C.subkey_params_builder_build(b.internal)
if u64(subkey) == 0 {
construct_error()!
return error('')
}
return SubkeyParams{
internal: subkey
}
}
pub fn (p &SignedPublicKey) verify(data []byte, sig &Signature) ! {
ok := C.signed_public_key_verify(p.internal, &u8(&data[0]), data.len, sig.internal)
if ok != 0 {
construct_error()!
return error('')
}
}
pub fn (s &SignedPublicKey) encrypt(data []byte) ![]byte {
len := u64(data.len)
encrypted := C.signed_public_key_encrypt(s.internal, &u8(&data[0]), &len)
if u64(encrypted) == 0 {
construct_error()!
return error('unreachable!')
}
return cu8_to_vbytes(encrypted, len)
}
pub fn (s &SignedPublicKey) encrypt_with_any(data []byte) ![]byte {
len := u64(data.len)
encrypted := C.signed_public_key_encrypt_with_any(s.internal, &u8(&data[0]), &len)
if u64(encrypted) == 0 {
construct_error()!
return error('unreachable!')
}
return cu8_to_vbytes(encrypted, len)
}
pub fn (s &SignedPublicKey) to_bytes() ![]byte {
len := u64(0)
ser := C.signed_public_key_to_bytes(s.internal, &len)
if u64(ser) == 0 {
construct_error()!
return error('')
}
res := cu8_to_vbytes(ser, len)
C.ptr_free(&u8(ser))
return res
}
pub fn signed_public_key_from_bytes(bytes []byte) !SignedPublicKey {
ser := C.signed_public_key_from_bytes(&u8(&bytes[0]), bytes.len)
if u64(ser) == 0 {
construct_error()!
return error('')
}
return SignedPublicKey{
internal: ser
}
}
pub fn (s &SignedPublicKey) to_armored() !string {
ser := C.signed_public_key_to_armored(s.internal)
if u64(ser) == 0 {
construct_error()!
return error('')
}
res := unsafe { cstring_to_vstring(ser) }
C.ptr_free(&u8(ser))
return res
}
pub fn signed_public_key_from_armored(s string) !SignedPublicKey {
ser := C.signed_public_key_from_armored(s.str)
if u64(ser) == 0 {
construct_error()!
return error('')
}
return SignedPublicKey{
internal: ser
}
}

20
lib/crypt/crpgp/utils.v Normal file
View File

@@ -0,0 +1,20 @@
module crpgp
pub fn cu8_to_vbytes(ptr &u8, l u64) []byte {
mut res := []byte{}
for _ in 0 .. l {
res << byte(unsafe { *ptr })
unsafe {
ptr++
}
}
return res
}
pub fn str_to_bytes(s string) []byte {
mut res := []byte{}
for c in s {
res << byte(c)
}
return res
}

1
lib/crypt/crypt.v Normal file
View File

@@ -0,0 +1 @@
module crypt

View File

@@ -0,0 +1,85 @@
module ed25519
import libsodium
// import encoding.hex
// holds signing and private key
// private key is derivative from signing key
// signkey_bytes is the seed to generate a signing key from which can then generate priv key
pub struct PrivKey25519 {
pub:
signkey_bytes []u8 // signing key in bytes
signkey libsodium.SigningKey // master key (is a signing key)
privkey libsodium.PrivateKey // derivated from master key
pubkey PubKey25519
}
pub struct PubKey25519 {
pub:
ed_bytes []u8 // for signing
curve_bytes []u8 // target = remote public key (to encrypt) , also called X25519
}
pub fn new_private_key_25519() PrivKey25519 {
mut seed := []u8{}
// generate a new random seed
for _ in 0 .. 32 {
seed << u8(libsodium.randombytes_random())
}
signkey := libsodium.new_ed25519_signing_key_seed(seed)
privkey := libsodium.new_private_key_from_signing_ed25519(signkey)
pubkey := priv_to_public_key(signkey)
return PrivKey25519{
signkey: signkey
privkey: privkey
signkey_bytes: seed
pubkey: pubkey
}
}
// retrieve the master public key from PrivKey object
// this is the public key as need to be shared to a remote user to verify that we signed with our private key
// is shared as hex key in string format (66 chars)
fn priv_to_public_key(priv_sign_key libsodium.SigningKey) PubKey25519 {
x := priv_sign_key.verify_key.public_key
mut ed_bytes := []u8{len: x.len}
unsafe { C.memcpy(ed_bytes.data, &x[0], x.len) }
curve_bytes := []u8{len: libsodium.public_key_size}
libsodium.crypto_sign_ed25519_pk_to_curve25519(curve_bytes.data, ed_bytes.data)
return PubKey25519{
ed_bytes: ed_bytes
curve_bytes: curve_bytes
}
}
// encrypt data which can only be read by whoever has the private key for this public key
pub fn (privkey PrivKey25519) encrypt_for_remote(pubkey PubKey25519, data []u8) ![]u8 {
box := libsodium.new_box(privkey.privkey, pubkey.curve_bytes)
return box.encrypt(data)
}
// verify a signed data and decrypt,
pub fn (privkey PrivKey25519) decrypt(data []u8) []u8 {
box := libsodium.new_box(privkey.privkey, privkey.pubkey.curve_bytes)
decrypted := box.decrypt(data)
return decrypted
}
// sign data with our signing key.
// data is bytestr.
// output is []u8 bytestring.
// to get bytes from string do: mystring.bytes().
pub fn (key PrivKey25519) sign(data []u8) []u8 {
return key.signkey.sign(data)
}
// verify the signature
pub fn (key PubKey25519) verify(signature []u8) bool {
v := [libsodium.public_key_size]u8{}
// unsafe { C.memcpy(&v[0], key.ed_bytes, libsodium.public_key_size) }
// vk:=libsodium.VerifyKey{public_key: v}
// return vk.verify(signature)
return true
}

View File

@@ -0,0 +1,5 @@
>> TODO: is not working yet, info copied from keysafe in root dir, but we need easier to understand usable lib
>> TODO: maxux, please migrate all your functionality in relation to signing, ... to this module

View File

@@ -0,0 +1,17 @@
security add-internet-password -s git.ourworld.tf -a despiegk -w mypassword
-s: The server (e.g., git.ourworld.tf).
-a: The account or username (e.g., despiegk).
-w: The password (e.g., mypassword).
security find-internet-password -s git.ourworld.tf -w
security delete-internet-password -s git.ourworld.tf
git config --global credential.helper osxkeychain

202
lib/crypt/keysafe/keysafe.v Normal file
View File

@@ -0,0 +1,202 @@
module keysafe
import freeflowuniverse.herolib.core.pathlib
// import freeflowuniverse.herolib.data.mnemonic // buggy for now
import encoding.hex
import libsodium
import json
import os
import freeflowuniverse.herolib.ui.console
/*
* KeysSafe
*
* This module implement a secure keys manager.
*
* When loading a keysafe object, you can specify a directory and a secret.
* In that directory, a file called '.keys' will be created and encrypted using
* the 'secret' provided (AES-CBC).
*
* Content of that file is a JSON dictionnary of key-name and it's mnemonic,
* a single mnemonic is enough to derivate ed25519 and x25519 keys.
*
* When loaded, private/public signing key and public/private encryption keys
* are loaded and ready to be used.
*
* key_generate_add() generate a new key and store is as specified name
* key_import_add() import an existing key based on it's seed and specified name
*
*/
pub struct KeysSafe {
pub mut:
path pathlib.Path // file path of keys
loaded bool // flag to know if keysafe is loaded or loading
secret string // secret to encrypt local file
keys map[string]PrivKey // list of keys
}
pub struct PersistantKeysSafe {
pub mut:
keys map[string]string // store name/mnemonics only
}
// note: root key needs to be 'SigningKey' from libsodium
// from that SigningKey we can derivate PrivateKey needed to encrypt
pub fn keysafe_get(path0 string, secret string) !KeysSafe {
mut path := pathlib.get_file(path: path0 + '/.keys', create: true)!
mut safe := KeysSafe{
path: path
secret: secret
}
if os.exists(path.absolute()) {
console.print_debug('[+] key file already exists, loading it')
safe.load()
}
safe.loaded = true
return safe
}
// for testing purposes you can generate multiple keys
pub fn (mut ks KeysSafe) generate_multiple(count int) ! {
for i in 0 .. count {
ks.key_generate_add('name_${i}')!
}
}
// generate a new key is just importing a key with a random seed
pub fn (mut ks KeysSafe) key_generate_add(name string) !PrivKey {
mut seed := []u8{}
// generate a new random seed
for _ in 0 .. 32 {
seed << u8(libsodium.randombytes_random())
}
return ks.key_import_add(name, seed)
}
fn internal_key_encode(key []u8) string {
return '0x' + hex.encode(key)
}
fn internal_key_decode(key string) []u8 {
parsed := hex.decode(key.substr(2, key.len)) or { panic(err) }
return parsed
}
// import based on an existing seed
pub fn (mut ks KeysSafe) key_import_add(name string, seed []u8) !PrivKey {
if name in ks.keys {
return error('A key with that name already exists')
}
mnemonic := internal_key_encode(seed) // mnemonic(seed)
signkey := libsodium.new_ed25519_signing_key_seed(seed)
privkey := libsodium.new_private_key_from_signing_ed25519(signkey)
// console.print_debug("===== SEED ====")
// console.print_debug(seed)
// console.print_debug(mnemonic)
pk := PrivKey{
name: name
mnemonic: mnemonic
privkey: privkey
signkey: signkey
}
ks.key_add(pk)!
return pk
}
pub fn (mut ks KeysSafe) get(name string) !PrivKey {
if !ks.exists(name) {
return error('key not found')
}
return ks.keys[name]
}
pub fn (mut ks KeysSafe) exists(name string) bool {
return name in ks.keys
}
pub fn (mut ks KeysSafe) key_add(pk PrivKey) ! {
ks.keys[pk.name] = pk
// do not persist keys if keysafe is not loaded
// this mean we are probably loading keys from file
if ks.loaded {
ks.persist()
}
}
pub fn (mut ks KeysSafe) persist() {
console.print_debug('[+] saving keys to ${ks.path.absolute()}')
serialized := ks.serialize()
// console.print_debug(serialized)
encrypted := symmetric_encrypt_blocks(serialized.bytes(), ks.secret)
mut f := os.create(ks.path.absolute()) or { panic(err) }
f.write(encrypted) or { panic(err) }
f.close()
}
pub fn (mut ks KeysSafe) serialize() string {
mut pks := PersistantKeysSafe{}
// serializing mnemonics only
for key, val in ks.keys {
pks.keys[key] = val.mnemonic
}
export := json.encode(pks)
return export
}
pub fn (mut ks KeysSafe) load() {
console.print_debug('[+] loading keys from ${ks.path.absolute()}')
mut f := os.open(ks.path.absolute()) or { panic(err) }
// read encrypted file
filesize := os.file_size(ks.path.absolute())
mut encrypted := []u8{len: int(filesize)}
f.read(mut encrypted) or { panic(err) }
f.close()
// decrypt file using ks secret
plaintext := symmetric_decrypt_blocks(encrypted, ks.secret)
// (try to) decode the json and load keys
ks.deserialize(plaintext.bytestr())
}
pub fn (mut ks KeysSafe) deserialize(input string) {
mut pks := json.decode(PersistantKeysSafe, input) or {
console.print_debug('Failed to decode json, wrong secret or corrupted file: ${err}')
return
}
// serializing mnemonics only
for name, mnemo in pks.keys {
console.print_debug('[+] loading key: ${name}')
seed := internal_key_decode(mnemo) // mnemonic.parse(mnemo)
// console.print_debug("==== SEED ====")
// console.print_debug(mnemo)
// console.print_debug(seed)
ks.key_import_add(name, seed) or { panic(err) }
}
// console.print_debug(ks)
}

View File

@@ -0,0 +1,45 @@
module keysafe
import libsodium
import encoding.hex
pub struct PrivKey {
pub:
name string
mnemonic string
signkey libsodium.SigningKey // master key
privkey libsodium.PrivateKey // derivated from master key
}
pub fn key_encode(key []u8) string {
return '0x' + hex.encode(key)
}
// retrieve the master public key from PrivKey object
// this is the public key as need to be shared to a remote user to verify that we signed with our private key
// is shared as hex key in string format (66 chars)
pub fn (key PrivKey) master_public() string {
x := key.signkey.verify_key.public_key
mut target := []u8{len: x.len}
unsafe { C.memcpy(target.data, &x[0], x.len) }
return key_encode(target)
}
// sign data with our signing key
// data is bytestr
// output is []u8 bytestring
// to get bytes from string do: mystring.bytes().
pub fn (key PrivKey) sign(data []u8) []u8 {
return key.signkey.sign(data)
}
// sign data with our signing key.
// data is bytestr.
// output is hex string.
// to get bytes from string do: mystring.bytes().
// size of output is ?
pub fn (key PrivKey) sign_hex(data []u8) string {
return hex.encode(key.sign(data))
}

View File

@@ -0,0 +1,41 @@
module keysafe
import libsodium
import encoding.hex
pub struct PubKey {
pub:
name string
source PrivKey // ourself (private key, to sign message)
remote []u8 // target public key (to encrypt)
}
pub fn pubkey_new(name string, myself PrivKey, remote string) !PubKey {
parsed := hex.decode(remote.substr(2, remote.len))!
// convert SigningKey to PrivateKey (ed25519 > x25519)
// to allow encryption and decryption
target := []u8{len: libsodium.public_key_size}
libsodium.crypto_sign_ed25519_pk_to_curve25519(target.data, parsed[0])
return PubKey{
name: name
source: myself
remote: target
}
}
// this will encrypt bytes so that only the owner of this pubkey can decrypt it
pub fn (key PubKey) encrypt(data []u8) ![]u8 {
box := libsodium.new_box(key.source.privkey, key.remote)
return box.encrypt(data)
}
// verify a signed data
pub fn (key PubKey) decrypt(data []u8) []u8 {
box := libsodium.new_box(key.source.privkey, key.remote)
decrypted := box.decrypt(data)
return decrypted
}

View File

@@ -0,0 +1,47 @@
# Keysafe
A safe implementation to help you sign, encrypt, decrypt and store your keys locally.
## Internals
When loading a keysafe object, you can specify a `directory` and a `secret`.
In that directory, a file called `.keys` will be created and encrypted using
the `secret` provided (`AES-CBC`).
Content of that file is a JSON dictionnary of key-name and it's mnemonic,
a single mnemonic is enough to derivate `ed25519` and `x25519` keys.
When loaded, private/public signing key and public/private encryption keys
are loaded and ready to be used.
- `key_generate_add()` generate a new key and store is as specified name
- `key_import_add()` import an existing key based on it's seed and specified name
## Example
```v
module main
import freeflowuniverse.herolib.crypt.keysafe
fn main() {
mut ks := keysafe.keysafe_get("/tmp/", "helloworld")!
println(ks)
ks.key_generate_add("demo") or { println(err) }
println(ks)
if ks.exists("demo") {
println("key demo exists")
}
}
```
## Keys
Note about keys: when generating a new key, the "master key" is a SigningKey Ed25519 key. From
that key, we can derivate a PrivateKey (encrypting key) X25519.
We can convert public-key only as well. On public key exchange, please always exchange the public SigningKey
(aka the master key for us). Based on that SignigKey, we can derivate the Encyption PublicKey and KeysSafe
does it for you.

View File

@@ -0,0 +1,30 @@
module keysafe
import libsodium
import encoding.hex
pub struct VerifyKey {
pub:
name string
remote libsodium.VerifyKey // target public master key
}
// remote is the public master key which needs to verify
pub fn verifykey_new(name string, remote string) !VerifyKey {
parsed := hex.decode(remote.substr(2, remote.len))!
v := [libsodium.public_key_size]u8{}
unsafe { C.memcpy(&v[0], parsed.data, libsodium.public_key_size) }
return VerifyKey{
name: name
remote: libsodium.VerifyKey{
public_key: v
}
}
}
// verify a signed data, returns true if signature is correct
pub fn (key VerifyKey) verify(data []u8) bool {
return key.remote.verify(data)
}

View File

@@ -0,0 +1,36 @@
module openssl
import freeflowuniverse.herolib.builder
import json
@[params]
pub struct OpenSSLGenerateArgs {
name string = 'default'
domain string = 'myregistry.domain.com'
reset bool
}
pub fn (mut ossl OpenSSL) generate(args OpenSSLGenerateArgs) !OpenSSLKey {
mut r := ossl.new(args)!
if r.domain.len < 6 {
return error('need to give domain and needs to be bigger than 6 chars. \n${r}')
}
cmd := '
openssl req -newkey rsa:4096 -nodes -sha256 -keyout ${r.path_key.path} -addext "subjectAltName = DNS:${args.domain}" -subj "/C=BE/ST=Ghent/L=Something/O=Global Security/OU=IT Department/CN=${args.domain}" -x509 -days 365 -out ${r.path_cert.path}
'
mut b := builder.new()!
mut node := b.node_local()!
node.exec(cmd: cmd)!
r.hexhash()!
s := json.encode(r)
r.path_json.write(s)!
return r
}

View File

@@ -0,0 +1,69 @@
module openssl
import freeflowuniverse.herolib.builder
import json
@[params]
pub struct OpenSSLCAGenerateArgs {
name string = 'default'
domain string = 'myregistry.domain.com'
reset bool
}
pub fn (mut ossl OpenSSL) generate_ca(args OpenSSLGenerateArgs) !OpenSSLKey {
mut r := ossl.new(args)!
if r.domain.len < 6 {
return error('need to give domain and needs to be bigger than 6 chars. \n${r}')
}
mut b := builder.new()!
mut node := b.node_local()!
// info on https://mariadb.com/docs/xpand/security/data-in-transit-encryption/create-self-signed-certificates-keys-openssl/
cmd := '
openssl genrsa 2048 > ca-key.pem
#Creating the Certificate Authoritys Certificate and Keys
openssl req -new -x509 -nodes -days 365000 -key ca-key.pem -out ca-cert.pem -subj "/C=BE/ST=Ghent/L=Something/O=Global Security/OU=IT Department/CN=${args.domain}"
openssl req -newkey rsa:2048 -nodes -days 365000 -keyout server-key.pem -out server-req.pem
openssl x509 -req -days 365000 -set_serial 01 -in server-req.pem -out server-cert.pem -CA ca-cert.pem -CAkey ca-key.pem
rm -rf /tmp/w
mkdir -p /tmp/w
cd /tmp/w
openssl genrsa 2048 > ca-key.pem
#Creating the Certificate Authoritys Certificate and Keys
openssl req -new -x509 -nodes -days 365000 -key ca-key.pem -out ca-cert.pem -subj "/C=BE/ST=Ghent/L=Something/O=Global Security/OU=IT Department/CN=registry.test.com"
openssl req -newkey rsa:2048 -nodes -days 365000 -keyout server-key.pem -out server-req.pem -subj "/C=BE/ST=Ghent/L=Something/O=Global Security/OU=IT Department/CN=registry.test.com"
openssl x509 -req -days 365000 -set_serial 01 -in server-req.pem -out server-cert.pem -CA ca-cert.pem -CAkey ca-key.pem
'
node.exec(cmd: cmd)!
cmd2 := '
openssl req -newkey rsa:4096 -nodes -sha256 -keyout ${r.path_key.path} -addext "subjectAltName = DNS:${args.domain}" -subj "/C=BE/ST=Ghent/L=Something/O=Global Security/OU=IT Department/CN=${args.domain}" -x509 -days 365 -out ${r.path_cert.path}
'
node.exec(cmd: cmd2)!
r.hexhash()!
s := json.encode(r)
r.path_json.write(s)!
return r
}

60
lib/crypt/openssl/get.v Normal file
View File

@@ -0,0 +1,60 @@
module openssl
import freeflowuniverse.herolib.core.pathlib { Path }
import json
pub struct OpenSSLKey {
pub mut:
name string
domain string
md5 string
path_key Path
path_cert Path
path_json Path
}
pub fn (mut ossl OpenSSL) new(args OpenSSLGenerateArgs) !OpenSSLKey {
path_key := '${ossl.certpath.path}/${args.name}.key'
path_cert := '${ossl.certpath.path}/${args.name}.crt'
path_json := '${ossl.certpath.path}/${args.name}.json'
mut path_keyo := pathlib.get(path_key)
mut path_certo := pathlib.get(path_cert)
mut path_jsono := pathlib.get(path_json)
if args.reset {
path_keyo.delete()!
path_certo.delete()!
path_jsono.delete()!
}
r := OpenSSLKey{
name: args.name
domain: args.domain
path_key: path_keyo
path_cert: path_certo
path_json: path_jsono
}
return r
}
pub fn (mut ossl OpenSSL) exists(args OpenSSLGenerateArgs) !bool {
mut r := ossl.new(args)!
if r.path_key.exists() && r.path_cert.exists() && r.path_json.exists() {
return true
}
return false
}
// will get openssl key, from fs if exists, otherwise it will generate
pub fn (mut ossl OpenSSL) get(args OpenSSLGenerateArgs) !OpenSSLKey {
mut r := ossl.new(args)!
if r.path_json.exists() {
jsontext := r.path_json.read()!
return json.decode(OpenSSLKey, jsontext)
} else {
return ossl.generate(args)!
}
}

16
lib/crypt/openssl/key.v Normal file
View File

@@ -0,0 +1,16 @@
module openssl
import crypto.md5
// give md5 hash of key (concatenate the key+cert)
pub fn (mut key OpenSSLKey) hexhash() !string {
mut out := ''
out += key.path_key.read()!
out += key.path_cert.read()!
res := md5.hexhash(out)
key.md5 = res
return res
}

View File

@@ -0,0 +1,23 @@
module openssl
import freeflowuniverse.herolib.core.pathlib { Path }
pub struct OpenSSL {
certpath Path
}
@[params]
pub struct OpenSSLArgs {
certpath string = '~/.openssl'
}
pub fn new(args OpenSSLArgs) !OpenSSL {
if args.certpath.len < 3 {
return error('need to give certpath and needs to be bigger than 3 chars')
}
mut datapath := pathlib.get_dir(path: args.certpath, create: true)!
mut ossl := OpenSSL{
certpath: datapath
}
return ossl
}

33
lib/crypt/pgp/pgp.v Normal file
View File

@@ -0,0 +1,33 @@
module pgp
// import freeflowuniverse.herolib.builder
import os
pub enum PGPFactoryStatus {
init
ok
error
}
@[heap]
struct PGPFactory {
mut:
path string
instances map[string]PGPInstance
}
// needed to get singleton
fn init2() PGPFactory {
mut f := PGPFactory{}
// untill we have pgp bindings to the vlang module, we prob need to use command line
f.path = '...'
f.cmd = '...'
return f
}
// singleton creation
const factory = init2()
pub fn get() &PGPFactory {
return &factory
}

13
lib/crypt/pgp/pgp_cmds.v Normal file
View File

@@ -0,0 +1,13 @@
module pgp
// import freeflowuniverse.herolib.builder
import os
// list all instances
fn (pgp PGPFactory) list() ?[]&PGPInstance {
mut res := []&PGPInstance{}
}
// destroy all instances
fn (pgp PGPFactory) destroy() {
}

View File

@@ -0,0 +1,60 @@
module pgp
// import freeflowuniverse.herolib.builder
import os
@[heap]
struct PGPInstance {
mut:
name string
pubkey string
}
fn (mut f PGPFactory) new(name string) ?&PGPInstance {
mut i := PGPInstance{
name: name
}
f.instances[name] = &i
return &i
}
fn (mut f PGPFactory) get(name string) ?&PGPInstance {
if name !in f.instances {
return error('cannot find pgp instance with name ${name}')
}
return f.instances[name]
}
// sign a piece of content, return signature
fn (f PGPInstance) sign(content string) ?Signature {
}
// encrypt for private usage (is this relevant)
fn (f PGPInstance) encrypt_self(content string) ?CryptData {
}
// encrypt for other person, so they can only decrypt
fn (f PGPInstance) encrypt_other(pubkey Pubkey, content string) ?CryptData {
}
// encrypt for other person, so they can only decrypt
// sign using your own pgp key
fn (f PGPInstance) encrypt_sign_other(pubkey Pubkey, content string) ?CryptData {
}
// verify agains own key
fn (f PGPInstance) verify_self(pubkey Pubkey, signature Signature, content string) ?string {
}
// verify with pub key of other
fn (f PGPInstance) verify_other(pubkey Pubkey, signature Signature, content string) ?string {
}
// decrypt with own key
fn (f PGPInstance) decrypt(content CryptData) ?string {
}
// decrypt with own key and also verify (is counterpart of encrypt_sign_other)
fn (f PGPInstance) decrypt_verify(content CryptData) ?string {
}

37
lib/crypt/pgp/pgp_model.v Normal file
View File

@@ -0,0 +1,37 @@
module pgp
// import freeflowuniverse.herolib.builder
import os
pub struct Pubkey {
mut:
pubkey string
}
// validate if pubkey is valid
fn (mut pubkey Pubkey) validate() bool {
}
pub struct Signature {
mut:
signature string
}
// validate if Signature is valid
fn (mut signature Signature) validate() bool {
}
pub struct CryptData {
mut:
data string
signature Signature
}
// validate if CryptData is valid
fn (mut data CryptData) validate() bool {
}
fn (mut data CryptData) verify() {
}
// TODO: what are the methods we need?

6
lib/crypt/pgp/readme.md Normal file
View File

@@ -0,0 +1,6 @@
# PGP interface
> not implemented yet
> could use crpgp for it, but it became somewhat complicated

View File

@@ -0,0 +1,147 @@
# 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

@@ -0,0 +1,351 @@
@[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

@@ -0,0 +1,460 @@
#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

@@ -0,0 +1,61 @@
#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

@@ -0,0 +1,112 @@
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

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

View File

@@ -0,0 +1,21 @@
module secrets
import rand
import freeflowuniverse.herolib.ui.console
import freeflowuniverse.herolib.ui
import freeflowuniverse.herolib.crypt.aes_symmetric
import crypto.md5
import regex
import os
import encoding.base64
// will use our secret as configured for the hero to encrypt
pub fn (mut b SecretBox) encrypt(txt string) !string {
d := aes_symmetric.encrypt_str(txt, b.secret)
return base64.encode_str(d)
}
pub fn (mut b SecretBox) decrypt(txt string) !string {
txt2 := base64.decode_str(txt)
return aes_symmetric.decrypt_str(txt2, b.secret)
}

View File

@@ -0,0 +1,33 @@
module secrets
import freeflowuniverse.herolib.ui.console
pub struct SecretBox {
pub mut:
secret string
items map[string]string
}
@[params]
pub struct SecretBoxArgs {
pub mut:
// reset bool
// interactive bool = true
secret string @[required]
}
pub fn get(args SecretBoxArgs) !SecretBox {
// if args.reset {
// reset()!
// }
// if args.secret.len == 0 {
// mut myui := ui.new()!
// console.clear()
// secret_ := myui.ask_question(question: 'Please enter your hero secret string (box)')!
// secret = md5.hexhash(secret_)
// r.set(key, secret)!
// }
return SecretBox{
secret: args.secret
}
}

View File

@@ -0,0 +1,53 @@
# Secret Box
Some tools to work with encryption/decryption (symmetric)
```go
import freeflowuniverse.herolib.crypt.secrets
mut box:=secrets.get(secret:"mysecret")!
r:= box.encrypt("aaa")!
println(r)
assert "aaa"==box.decrypt(r)!
hex_secret:=secrets.hex_secret()!
openssl_hex_secret:=secrets.openssl_hex_secret()!
openssl_base64_secret:=secrets.openssl_base64_secret()!
```
<!--
## replace some text
some utils to manage secret keys and easily change them in text, ideal for config files.
```go
#!/usr/bin/env -S v -n -cg -w -enable-globals run
import freeflowuniverse.herolib.crypt.secrets
mut box:=secrets.get()!
box.delete("myapp.something")! //make sure we remove all previous keys
//will generate a key (hex of 24 chars) if it doesn't exist yet .
mysecret:=box.secret(key:"myapp.something.a",reset:false)!
println(mysecret)
mut test_string := "This is a test string with {ss} and {MYAPP.SOMETHING.A} and {ABC123}."
test_string1:=box.replace(txt:test_string)!
println(test_string1) -->
test_string2:=box.replace(txt:test_string,defaults:{"MYAPP.SOMETHING.A":secrets.DefaultSecretArgs{secret:"AAA"}})!
println(test_string2)
```

View File

@@ -0,0 +1,76 @@
module secrets
import rand
// import freeflowuniverse.herolib.ui.console
// import freeflowuniverse.herolib.ui
// import freeflowuniverse.herolib.crypt.aes_symmetric
// import crypto.md5
import crypto.sha256
import os
import encoding.base64
@[params]
pub struct SecretArgs {
pub mut:
key string @[required]
default string // if it doesn't exist yet, will create it with this value
overwrite string // will overwrite the secret with this value even if it exists
cat SecretType
// reset bool
}
pub enum SecretType {
normal
openssl_hex
openssl_base64
}
@[params]
pub struct StringArgs {
pub:
input string
}
pub fn hex_secret(args StringArgs) !string {
if args.input == '' {
// If no input string is provided, generate a random hex string
return rand.hex(24)
} else {
// If an input string is provided, use it to generate a consistent hex string
hash := sha256.sum256(args.input.bytes()).hex()
return hash[..48] // Return the first 48 characters (24 bytes) of the hash
}
}
pub fn openssl_hex_secret(args StringArgs) !string {
if args.input == '' {
// If no input string is provided, use the original openssl command
cmd := 'openssl rand -hex 32'
result := os.execute(cmd)
if result.exit_code > 0 {
return error('Command failed with exit code: ${result.exit_code} and error: ${result.output}')
}
return result.output.trim_space()
} else {
// If an input string is provided, use it to generate a consistent hash
hash := sha256.sum256(args.input.bytes()).hex()
return hash[..64] // Return the first 64 characters (32 bytes) of the hash
}
}
pub fn openssl_base64_secret(args StringArgs) !string {
if args.input == '' {
// If no input string is provided, use the original openssl command
cmd := 'openssl rand -base64 32'
result := os.execute(cmd)
if result.exit_code > 0 {
return error('Command failed with exit code: ${result.exit_code} and error: ${result.output}')
}
return result.output.trim_space()
} else {
// If an input string is provided, use it to generate a consistent base64 string
hash := sha256.sum256(args.input.bytes())
base64_str := base64.encode(hash)
return base64_str[..44] // Return the first 44 characters (32 bytes in base64)
}
}

View File

@@ -0,0 +1,28 @@
module secrets
import freeflowuniverse.herolib.ui.console
fn test_check() {
mut box := get(secret: 'mysecret')!
r := box.encrypt('aaa')!
console.print_debug(r)
assert 'aaa' == box.decrypt(r)!
hex_secret1 := hex_secret()!
console.print_debug(hex_secret1)
console.print_debug(hex_secret1.len)
assert hex_secret1.len == 24
openssl_hex_secret1 := openssl_hex_secret()!
console.print_debug(openssl_hex_secret1)
console.print_debug(openssl_hex_secret1.len)
assert openssl_hex_secret1.len == 64
openssl_base64_secret1 := openssl_base64_secret()!
console.print_debug(openssl_base64_secret1)
console.print_debug(openssl_base64_secret1.len)
assert openssl_base64_secret1.len == 44
}

View File

@@ -1,8 +1,8 @@
module osal module osal
import freeflowuniverse.crystallib.core.base import freeflowuniverse.herolib.core.base
import freeflowuniverse.crystallib.data.dbfs import freeflowuniverse.herolib.data.dbfs
import freeflowuniverse.crystallib.ui.console import freeflowuniverse.herolib.ui.console
fn donedb() !&dbfs.DB { fn donedb() !&dbfs.DB {
mut context := base.context()! mut context := base.context()!