From f77c7ba87410f0348bc420553327a65d97a90e4d Mon Sep 17 00:00:00 2001 From: despiegk Date: Wed, 25 Dec 2024 12:38:51 +0100 Subject: [PATCH] the base --- lib/core/base/baseconfig.v | 2 +- lib/core/base/configurator.v | 2 +- lib/core/base/context.v | 20 +- lib/core/base/context_session.v | 4 +- lib/core/base/factory_context.v | 6 +- lib/core/base/readme.md | 2 +- lib/core/base/session.v | 14 +- lib/core/base/session_error.v | 4 +- lib/core/base/session_logger.v | 4 +- lib/crypt/aes_symmetric/symmetric.v | 79 ++++ lib/crypt/aes_symmetric/symmetric_test.v | 7 + lib/crypt/crpgp/README.md | 105 ++++++ lib/crypt/crpgp/crpgp-manual.v | 13 + lib/crypt/crpgp/crpgp.v | 108 ++++++ lib/crypt/crpgp/err.v | 10 + lib/crypt/crpgp/install.sh | 53 +++ lib/crypt/crpgp/pgp.v | 113 ++++++ lib/crypt/crpgp/types.v | 292 ++++++++++++++ lib/crypt/crpgp/utils.v | 20 + lib/crypt/crypt.v | 1 + lib/crypt/ed25519/privkey25519.v | 85 +++++ lib/crypt/ed25519/readme.md | 5 + lib/crypt/keychain/readme.md | 17 + lib/crypt/keysafe/keysafe.v | 202 ++++++++++ lib/crypt/keysafe/privkey.v | 45 +++ lib/crypt/keysafe/pubkey.v | 41 ++ lib/crypt/keysafe/readme.md | 47 +++ lib/crypt/keysafe/verifkey.v | 30 ++ lib/crypt/openssl/generate.v | 36 ++ lib/crypt/openssl/generate_ca.v | 69 ++++ lib/crypt/openssl/get.v | 60 +++ lib/crypt/openssl/key.v | 16 + lib/crypt/openssl/openssl.v | 23 ++ lib/crypt/pgp/pgp.v | 33 ++ lib/crypt/pgp/pgp_cmds.v | 13 + lib/crypt/pgp/pgp_instance.v | 60 +++ lib/crypt/pgp/pgp_model.v | 37 ++ lib/crypt/pgp/readme.md | 6 + lib/crypt/secp256k1/README.md | 147 ++++++++ lib/crypt/secp256k1/secp256k1.v | 351 +++++++++++++++++ lib/crypt/secp256k1/secp256k1mod.c | 460 +++++++++++++++++++++++ lib/crypt/secp256k1/secp256k1mod.h | 61 +++ lib/crypt/secp256k1/secp256k_test.v | 112 ++++++ lib/crypt/secp256k1/v.mod | 8 + lib/crypt/secrets/encrypt_decrypt.v | 21 ++ lib/crypt/secrets/factory.v | 33 ++ lib/crypt/secrets/readme.md | 53 +++ lib/crypt/secrets/secrets.v | 76 ++++ lib/crypt/secrets/secrets_test.v | 28 ++ lib/osal/done.v | 6 +- 50 files changed, 3008 insertions(+), 32 deletions(-) create mode 100644 lib/crypt/aes_symmetric/symmetric.v create mode 100644 lib/crypt/aes_symmetric/symmetric_test.v create mode 100644 lib/crypt/crpgp/README.md create mode 100644 lib/crypt/crpgp/crpgp-manual.v create mode 100644 lib/crypt/crpgp/crpgp.v create mode 100644 lib/crypt/crpgp/err.v create mode 100755 lib/crypt/crpgp/install.sh create mode 100644 lib/crypt/crpgp/pgp.v create mode 100644 lib/crypt/crpgp/types.v create mode 100644 lib/crypt/crpgp/utils.v create mode 100644 lib/crypt/crypt.v create mode 100644 lib/crypt/ed25519/privkey25519.v create mode 100644 lib/crypt/ed25519/readme.md create mode 100644 lib/crypt/keychain/readme.md create mode 100644 lib/crypt/keysafe/keysafe.v create mode 100644 lib/crypt/keysafe/privkey.v create mode 100644 lib/crypt/keysafe/pubkey.v create mode 100644 lib/crypt/keysafe/readme.md create mode 100644 lib/crypt/keysafe/verifkey.v create mode 100644 lib/crypt/openssl/generate.v create mode 100644 lib/crypt/openssl/generate_ca.v create mode 100644 lib/crypt/openssl/get.v create mode 100644 lib/crypt/openssl/key.v create mode 100644 lib/crypt/openssl/openssl.v create mode 100644 lib/crypt/pgp/pgp.v create mode 100644 lib/crypt/pgp/pgp_cmds.v create mode 100644 lib/crypt/pgp/pgp_instance.v create mode 100644 lib/crypt/pgp/pgp_model.v create mode 100644 lib/crypt/pgp/readme.md create mode 100644 lib/crypt/secp256k1/README.md create mode 100644 lib/crypt/secp256k1/secp256k1.v create mode 100644 lib/crypt/secp256k1/secp256k1mod.c create mode 100644 lib/crypt/secp256k1/secp256k1mod.h create mode 100644 lib/crypt/secp256k1/secp256k_test.v create mode 100644 lib/crypt/secp256k1/v.mod create mode 100644 lib/crypt/secrets/encrypt_decrypt.v create mode 100644 lib/crypt/secrets/factory.v create mode 100644 lib/crypt/secrets/readme.md create mode 100644 lib/crypt/secrets/secrets.v create mode 100644 lib/crypt/secrets/secrets_test.v diff --git a/lib/core/base/baseconfig.v b/lib/core/base/baseconfig.v index 22484ab9..41202666 100644 --- a/lib/core/base/baseconfig.v +++ b/lib/core/base/baseconfig.v @@ -1,7 +1,7 @@ module base 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 // T is the Config Object diff --git a/lib/core/base/configurator.v b/lib/core/base/configurator.v index cad63a51..db27b64d 100644 --- a/lib/core/base/configurator.v +++ b/lib/core/base/configurator.v @@ -1,7 +1,7 @@ module base import json -import freeflowuniverse.crystallib.ui.console +import freeflowuniverse.herolib.ui.console @[heap] pub struct Configurator[T] { diff --git a/lib/core/base/context.v b/lib/core/base/context.v index 0490d277..36b7f8c7 100644 --- a/lib/core/base/context.v +++ b/lib/core/base/context.v @@ -1,15 +1,15 @@ module base -import freeflowuniverse.crystallib.data.paramsparser -import freeflowuniverse.crystallib.clients.redisclient -import freeflowuniverse.crystallib.data.dbfs -// import freeflowuniverse.crystallib.crypt.secp256k1 -import freeflowuniverse.crystallib.crypt.aes_symmetric -import freeflowuniverse.crystallib.ui -import freeflowuniverse.crystallib.ui.console -import freeflowuniverse.crystallib.core.pathlib -import freeflowuniverse.crystallib.core.texttools -import freeflowuniverse.crystallib.core.rootpath +import freeflowuniverse.herolib.data.paramsparser +import freeflowuniverse.herolib.clients.redisclient +import freeflowuniverse.herolib.data.dbfs +// import freeflowuniverse.herolib.crypt.secp256k1 +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 diff --git a/lib/core/base/context_session.v b/lib/core/base/context_session.v index eec17062..3e626114 100644 --- a/lib/core/base/context_session.v +++ b/lib/core/base/context_session.v @@ -1,7 +1,7 @@ module base -import freeflowuniverse.crystallib.data.ourtime -import freeflowuniverse.crystallib.data.paramsparser +import freeflowuniverse.herolib.data.ourtime +import freeflowuniverse.herolib.data.paramsparser import json @[params] diff --git a/lib/core/base/factory_context.v b/lib/core/base/factory_context.v index 856297e3..6edd7d4b 100644 --- a/lib/core/base/factory_context.v +++ b/lib/core/base/factory_context.v @@ -1,8 +1,8 @@ module base -import freeflowuniverse.crystallib.data.paramsparser -import freeflowuniverse.crystallib.ui -import freeflowuniverse.crystallib.ui.console +import freeflowuniverse.herolib.data.paramsparser +import freeflowuniverse.herolib.ui +import freeflowuniverse.herolib.ui.console import crypto.md5 @[params] diff --git a/lib/core/base/readme.md b/lib/core/base/readme.md index 344d87db..be64e276 100644 --- a/lib/core/base/readme.md +++ b/lib/core/base/readme.md @@ -25,7 +25,7 @@ Session id is $contextid:$sessionid (e.g. 10:111) - coderoot string //this will define where all code is checked out ```v -import freeflowuniverse.crystallib.core.base +import freeflowuniverse.herolib.core.base mut session:=context_new( coderoot:'/tmp/code' diff --git a/lib/core/base/session.v b/lib/core/base/session.v index 9a199db4..cbe4428c 100644 --- a/lib/core/base/session.v +++ b/lib/core/base/session.v @@ -1,13 +1,13 @@ module base -import freeflowuniverse.crystallib.data.ourtime -// import freeflowuniverse.crystallib.core.texttools -import freeflowuniverse.crystallib.data.paramsparser -import freeflowuniverse.crystallib.data.dbfs +import freeflowuniverse.herolib.data.ourtime +// import freeflowuniverse.herolib.core.texttools +import freeflowuniverse.herolib.data.paramsparser +import freeflowuniverse.herolib.data.dbfs import json -// import freeflowuniverse.crystallib.core.pathlib -// import freeflowuniverse.crystallib.develop.gittools -// import freeflowuniverse.crystallib.ui.console +// import freeflowuniverse.herolib.core.pathlib +// import freeflowuniverse.herolib.develop.gittools +// import freeflowuniverse.herolib.ui.console @[heap] pub struct Session { diff --git a/lib/core/base/session_error.v b/lib/core/base/session_error.v index 70dbbf23..63d82264 100644 --- a/lib/core/base/session_error.v +++ b/lib/core/base/session_error.v @@ -1,7 +1,7 @@ module base -import freeflowuniverse.crystallib.data.ourtime -import freeflowuniverse.crystallib.core.texttools +import freeflowuniverse.herolib.data.ourtime +import freeflowuniverse.herolib.core.texttools pub struct ErrorArgs { pub mut: diff --git a/lib/core/base/session_logger.v b/lib/core/base/session_logger.v index 8f7846cf..8bf7f665 100644 --- a/lib/core/base/session_logger.v +++ b/lib/core/base/session_logger.v @@ -1,7 +1,7 @@ module base -import freeflowuniverse.crystallib.data.ourtime -import freeflowuniverse.crystallib.core.texttools +import freeflowuniverse.herolib.data.ourtime +import freeflowuniverse.herolib.core.texttools @[heap] pub struct Logger { diff --git a/lib/crypt/aes_symmetric/symmetric.v b/lib/crypt/aes_symmetric/symmetric.v new file mode 100644 index 00000000..60b29210 --- /dev/null +++ b/lib/crypt/aes_symmetric/symmetric.v @@ -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] +} diff --git a/lib/crypt/aes_symmetric/symmetric_test.v b/lib/crypt/aes_symmetric/symmetric_test.v new file mode 100644 index 00000000..ef7ffb08 --- /dev/null +++ b/lib/crypt/aes_symmetric/symmetric_test.v @@ -0,0 +1,7 @@ +module aes_symmetric + +fn test_check() { + d := encrypt_str('data', 'mysecret') + d2 := decrypt_str(d, 'mysecret') + assert d2 == 'data' +} diff --git a/lib/crypt/crpgp/README.md b/lib/crypt/crpgp/README.md new file mode 100644 index 00000000..ac769010 --- /dev/null +++ b/lib/crypt/crpgp/README.md @@ -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: , email: ) ! +``` + +- RSA key + +```v +// key_length 3072 if not passed +ssk := crpgp.generate_key(name: , email: , key_type: .rsa) ! +``` +or +```v +ssk := crpgp.generate_key(name: , email: , key_type: .rsa, key_length: ) ! +``` + +2. Import signed secret key from file + +```v +ssk := crpgp.import_secretkey_from_file() ! +``` + +- 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() ! +``` + +- 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 ! diff --git a/lib/crypt/crpgp/crpgp-manual.v b/lib/crypt/crpgp/crpgp-manual.v new file mode 100644 index 00000000..bb8da377 --- /dev/null +++ b/lib/crypt/crpgp/crpgp-manual.v @@ -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 +} diff --git a/lib/crypt/crpgp/crpgp.v b/lib/crypt/crpgp/crpgp.v new file mode 100644 index 00000000..f3ec2d0a --- /dev/null +++ b/lib/crypt/crpgp/crpgp.v @@ -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 diff --git a/lib/crypt/crpgp/err.v b/lib/crypt/crpgp/err.v new file mode 100644 index 00000000..382a0762 --- /dev/null +++ b/lib/crypt/crpgp/err.v @@ -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) +} diff --git a/lib/crypt/crpgp/install.sh b/lib/crypt/crpgp/install.sh new file mode 100755 index 00000000..a935afc7 --- /dev/null +++ b/lib/crypt/crpgp/install.sh @@ -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!" diff --git a/lib/crypt/crpgp/pgp.v b/lib/crypt/crpgp/pgp.v new file mode 100644 index 00000000..5828917d --- /dev/null +++ b/lib/crypt/crpgp/pgp.v @@ -0,0 +1,113 @@ +module crpgp + +import os +import encoding.hex + +#flag -lcrpgp +#include + +// 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) +} diff --git a/lib/crypt/crpgp/types.v b/lib/crypt/crpgp/types.v new file mode 100644 index 00000000..52bb2db2 --- /dev/null +++ b/lib/crypt/crpgp/types.v @@ -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 + } +} diff --git a/lib/crypt/crpgp/utils.v b/lib/crypt/crpgp/utils.v new file mode 100644 index 00000000..28149fb2 --- /dev/null +++ b/lib/crypt/crpgp/utils.v @@ -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 +} diff --git a/lib/crypt/crypt.v b/lib/crypt/crypt.v new file mode 100644 index 00000000..7a426ac3 --- /dev/null +++ b/lib/crypt/crypt.v @@ -0,0 +1 @@ +module crypt diff --git a/lib/crypt/ed25519/privkey25519.v b/lib/crypt/ed25519/privkey25519.v new file mode 100644 index 00000000..1c724c05 --- /dev/null +++ b/lib/crypt/ed25519/privkey25519.v @@ -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 +} diff --git a/lib/crypt/ed25519/readme.md b/lib/crypt/ed25519/readme.md new file mode 100644 index 00000000..0cf8abde --- /dev/null +++ b/lib/crypt/ed25519/readme.md @@ -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 \ No newline at end of file diff --git a/lib/crypt/keychain/readme.md b/lib/crypt/keychain/readme.md new file mode 100644 index 00000000..27ae76a6 --- /dev/null +++ b/lib/crypt/keychain/readme.md @@ -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 + + diff --git a/lib/crypt/keysafe/keysafe.v b/lib/crypt/keysafe/keysafe.v new file mode 100644 index 00000000..b81dcac9 --- /dev/null +++ b/lib/crypt/keysafe/keysafe.v @@ -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) +} diff --git a/lib/crypt/keysafe/privkey.v b/lib/crypt/keysafe/privkey.v new file mode 100644 index 00000000..2b02717b --- /dev/null +++ b/lib/crypt/keysafe/privkey.v @@ -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)) +} diff --git a/lib/crypt/keysafe/pubkey.v b/lib/crypt/keysafe/pubkey.v new file mode 100644 index 00000000..7484649b --- /dev/null +++ b/lib/crypt/keysafe/pubkey.v @@ -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 +} diff --git a/lib/crypt/keysafe/readme.md b/lib/crypt/keysafe/readme.md new file mode 100644 index 00000000..e493c1fb --- /dev/null +++ b/lib/crypt/keysafe/readme.md @@ -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. diff --git a/lib/crypt/keysafe/verifkey.v b/lib/crypt/keysafe/verifkey.v new file mode 100644 index 00000000..2a5bf9c8 --- /dev/null +++ b/lib/crypt/keysafe/verifkey.v @@ -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) +} diff --git a/lib/crypt/openssl/generate.v b/lib/crypt/openssl/generate.v new file mode 100644 index 00000000..151bdd5b --- /dev/null +++ b/lib/crypt/openssl/generate.v @@ -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 +} diff --git a/lib/crypt/openssl/generate_ca.v b/lib/crypt/openssl/generate_ca.v new file mode 100644 index 00000000..768d863e --- /dev/null +++ b/lib/crypt/openssl/generate_ca.v @@ -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 +} diff --git a/lib/crypt/openssl/get.v b/lib/crypt/openssl/get.v new file mode 100644 index 00000000..06743dec --- /dev/null +++ b/lib/crypt/openssl/get.v @@ -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)! + } +} diff --git a/lib/crypt/openssl/key.v b/lib/crypt/openssl/key.v new file mode 100644 index 00000000..762bb959 --- /dev/null +++ b/lib/crypt/openssl/key.v @@ -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 +} diff --git a/lib/crypt/openssl/openssl.v b/lib/crypt/openssl/openssl.v new file mode 100644 index 00000000..6a595024 --- /dev/null +++ b/lib/crypt/openssl/openssl.v @@ -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 +} diff --git a/lib/crypt/pgp/pgp.v b/lib/crypt/pgp/pgp.v new file mode 100644 index 00000000..b71b2e91 --- /dev/null +++ b/lib/crypt/pgp/pgp.v @@ -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 +} diff --git a/lib/crypt/pgp/pgp_cmds.v b/lib/crypt/pgp/pgp_cmds.v new file mode 100644 index 00000000..29683cb5 --- /dev/null +++ b/lib/crypt/pgp/pgp_cmds.v @@ -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() { +} diff --git a/lib/crypt/pgp/pgp_instance.v b/lib/crypt/pgp/pgp_instance.v new file mode 100644 index 00000000..76c58150 --- /dev/null +++ b/lib/crypt/pgp/pgp_instance.v @@ -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 { +} diff --git a/lib/crypt/pgp/pgp_model.v b/lib/crypt/pgp/pgp_model.v new file mode 100644 index 00000000..9abce8bd --- /dev/null +++ b/lib/crypt/pgp/pgp_model.v @@ -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? diff --git a/lib/crypt/pgp/readme.md b/lib/crypt/pgp/readme.md new file mode 100644 index 00000000..7d80409b --- /dev/null +++ b/lib/crypt/pgp/readme.md @@ -0,0 +1,6 @@ +# PGP interface + +> not implemented yet + +> could use crpgp for it, but it became somewhat complicated + diff --git a/lib/crypt/secp256k1/README.md b/lib/crypt/secp256k1/README.md new file mode 100644 index 00000000..a9e44fab --- /dev/null +++ b/lib/crypt/secp256k1/README.md @@ -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 +``` diff --git a/lib/crypt/secp256k1/secp256k1.v b/lib/crypt/secp256k1/secp256k1.v new file mode 100644 index 00000000..3e19fbdf --- /dev/null +++ b/lib/crypt/secp256k1/secp256k1.v @@ -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()) +} diff --git a/lib/crypt/secp256k1/secp256k1mod.c b/lib/crypt/secp256k1/secp256k1mod.c new file mode 100644 index 00000000..c282dd85 --- /dev/null +++ b/lib/crypt/secp256k1/secp256k1mod.c @@ -0,0 +1,460 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#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 diff --git a/lib/crypt/secp256k1/secp256k1mod.h b/lib/crypt/secp256k1/secp256k1mod.h new file mode 100644 index 00000000..c3016293 --- /dev/null +++ b/lib/crypt/secp256k1/secp256k1mod.h @@ -0,0 +1,61 @@ +#ifndef SECP256K1_V_MOD + #define SECP256K1_V_MOD + + #include + #include + #include + #include + + 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 + diff --git a/lib/crypt/secp256k1/secp256k_test.v b/lib/crypt/secp256k1/secp256k_test.v new file mode 100644 index 00000000..3c220338 --- /dev/null +++ b/lib/crypt/secp256k1/secp256k_test.v @@ -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)}') +} diff --git a/lib/crypt/secp256k1/v.mod b/lib/crypt/secp256k1/v.mod new file mode 100644 index 00000000..cfc98b28 --- /dev/null +++ b/lib/crypt/secp256k1/v.mod @@ -0,0 +1,8 @@ +Module { + name: 'secp256k1' + description: 'secp256k1 in v' + version: '0.2.0' + license: 'MIT' + dependencies: [] +} + diff --git a/lib/crypt/secrets/encrypt_decrypt.v b/lib/crypt/secrets/encrypt_decrypt.v new file mode 100644 index 00000000..7fafbb2e --- /dev/null +++ b/lib/crypt/secrets/encrypt_decrypt.v @@ -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) +} diff --git a/lib/crypt/secrets/factory.v b/lib/crypt/secrets/factory.v new file mode 100644 index 00000000..b5ddf303 --- /dev/null +++ b/lib/crypt/secrets/factory.v @@ -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 + } +} diff --git a/lib/crypt/secrets/readme.md b/lib/crypt/secrets/readme.md new file mode 100644 index 00000000..3b3321cf --- /dev/null +++ b/lib/crypt/secrets/readme.md @@ -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()! + +``` + + + + +test_string2:=box.replace(txt:test_string,defaults:{"MYAPP.SOMETHING.A":secrets.DefaultSecretArgs{secret:"AAA"}})! + +println(test_string2) + +``` + + diff --git a/lib/crypt/secrets/secrets.v b/lib/crypt/secrets/secrets.v new file mode 100644 index 00000000..ee50f426 --- /dev/null +++ b/lib/crypt/secrets/secrets.v @@ -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) + } +} diff --git a/lib/crypt/secrets/secrets_test.v b/lib/crypt/secrets/secrets_test.v new file mode 100644 index 00000000..ac1c13ee --- /dev/null +++ b/lib/crypt/secrets/secrets_test.v @@ -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 +} diff --git a/lib/osal/done.v b/lib/osal/done.v index 6ed2a945..217f70d5 100644 --- a/lib/osal/done.v +++ b/lib/osal/done.v @@ -1,8 +1,8 @@ module osal -import freeflowuniverse.crystallib.core.base -import freeflowuniverse.crystallib.data.dbfs -import freeflowuniverse.crystallib.ui.console +import freeflowuniverse.herolib.core.base +import freeflowuniverse.herolib.data.dbfs +import freeflowuniverse.herolib.ui.console fn donedb() !&dbfs.DB { mut context := base.context()!