Skip to content

hero.crypt #

HeroDB AGE Encryption (hero.crypt)

This module provides client-side access to HeroDB's AGE encryption features, offering two distinct modes of operation:

  • Stateless Mode: Client manages keys, nothing stored on server
  • Key-Managed Mode: Server stores named keys

Installation

Ensure HeroDB is running with encryption enabled:

herodb --dir /path/to/data --port 6381 --encrypt --encryption-key yoursecretkey

To start a db see

https://git.ourworld.tf/herocode/herodb

to do:

hero git pull https://git.ourworld.tf/herocode/herodb
~/code/git.ourworld.tf/herocode/herodb/run.sh

Usage

Creating an AGE Client

import incubaid.herolib.crypt.herocrypt

// Connect to default Redis instance (127.0.0.1:6381)
mut client := herocrypt.new_default()!

Stateless Encryption (Client-Managed Keys)

In stateless mode, the client generates and manages keys. The server never stores any keys.

Generate a Keypair

// Generate a new encryption keypair
enc_keypair := client.gen_enc_keypair()!
recipient_pub := enc_keypair[0]
identity_sec := enc_keypair[1]
println('Public key: ${recipient_pub}')
println('Private key: ${identity_sec}')

// Important: Store the identity (private key) securely!

Encrypt and Decrypt

// Encrypt a message with the public key
message := 'Hello, encrypted world!'
ciphertext := client.encrypt(recipient_pub, message)!
println('Encrypted: ${ciphertext}')

// Decrypt with the private key
decrypted_message := client.decrypt(identity_sec, ciphertext)!
println('Decrypted: ${decrypted_message}')

Signing and Verification

// Generate a signing keypair
sign_keypair := client.gen_sign_keypair()!
verify_pub_b64 := sign_keypair[0]
sign_sec_b64 := sign_keypair[1]

// Sign a message
message := 'This message is authentic'
signature := client.sign(sign_sec_b64, message)!
println('Signature: ${signature}')

// Verify the signature
is_valid := client.verify(verify_pub_b64, message, signature)!
println('Signature valid: ${is_valid}')

Key-Managed Encryption (Server-Stored Keys)

In key-managed mode, the server generates and stores named keys. Clients reference keys by name.

Create Named Keys

// Create a named encryption keypair
enc_key_name := 'app1_encryption'
client.keygen(enc_key_name)!
println('Created encryption keypair: "${enc_key_name}"')

// Create a named signing keypair
sign_key_name := 'app1_signing'
client.sign_keygen(sign_key_name)!
println('Created signing keypair: "${sign_key_name}"')

Encrypt and Decrypt with Named Keys

// Encrypt with a named key
message := 'This message is encrypted with a named key'
encrypted := client.encrypt_by_name('app1_encryption', message)!
println('Encrypted: ${encrypted}')

// Decrypt with a named key
decrypted := client.decrypt_by_name('app1_encryption', encrypted)!
println('Decrypted: ${decrypted}')

Sign and Verify with Named Keys

// Sign with a named key
message := 'This message is signed with a named key'
signature := client.sign_by_name('app1_signing', message)!
println('Signature: ${signature}')

// Verify with a named key
is_valid := client.verify_by_name('app1_signing', message, signature)!
println('Signature valid: ${is_valid}')

List Stored Keys

// List all stored keys
// keys := client.list_keys()!
// println('Stored keys: ${keys}')

Choosing Between Modes

Use Stateless Mode When

  • You want complete control over key management
  • You don't want the server to store any private keys
  • You need to use the same keys across multiple servers

Use Key-Managed Mode When

  • You want simpler client code (no need to manage keys)
  • You trust the server to securely store private keys
  • You need centralized key management with rotation options

Security Considerations

  1. Private Keys: In stateless mode, you are responsible for securely storing private keys.
  2. Server Security: In key-managed mode, ensure your HeroDB server is properly secured.
  3. Encryption at Rest: HeroDB supports database-level encryption separate from AGE commands.
  4. Transport Security: Consider using SSL/TLS to protect data in transit.

Examples

Secure Message Exchange

// Sender (Alice)
mut alice_client := crypt.new_age_client()!
alice_keypair := alice_client.generate_keypair()!

// Bob gets Alice's public key (recipient)
mut bob_client := crypt.new_age_client()!
bob_message := 'Secret message for Alice'
encrypted := bob_client.encrypt(alice_keypair.recipient, bob_message)!

// Alice decrypts Bob's message
decrypted := alice_client.decrypt(alice_keypair.identity, encrypted.ciphertext)!
assert decrypted == bob_message

Document Signing

// Author
mut author_client := crypt.new_age_client()!
author_keypair := author_client.create_named_signing_keypair('document_author')!
document := 'This is an official document'
signature := author_client.sign_with_named_key('document_author', document)!

// Verifier
mut verifier_client := crypt.new_age_client()!
is_authentic := verifier_client.verify_with_named_key('document_author', document, signature.signature)!

fn new_age_client #

fn new_age_client(config AGEClientConfig) !&AGEClient

new_age_client creates a new AGE encryption client

struct AGEClient #

struct AGEClient {
pub mut:
	redis &redisclient.Redis @[str: skip]
}

AGEClient provides access to the Age encryption features in HeroDB

fn (AGEClient) create_named_keypair #

fn (mut client AGEClient) create_named_keypair(name string) !KeyPair

create_named_keypair creates and stores a named encryption key pair

fn (AGEClient) create_named_signing_keypair #

fn (mut client AGEClient) create_named_signing_keypair(name string) !SigningKeyPair

create_named_signing_keypair creates and stores a named signing key pair

fn (AGEClient) decrypt #

fn (mut client AGEClient) decrypt(identity string, ciphertext string) !string

decrypt decrypts a message with the identity (private key)

fn (AGEClient) decrypt_with_named_key #

fn (mut client AGEClient) decrypt_with_named_key(key_name string, ciphertext string) !string

decrypt_with_named_key decrypts a message using a named key

fn (AGEClient) encrypt #

fn (mut client AGEClient) encrypt(recipient string, message string) !EncryptionResult

encrypt encrypts a message with the recipient's public key

fn (AGEClient) encrypt_with_named_key #

fn (mut client AGEClient) encrypt_with_named_key(key_name string, message string) !EncryptionResult

encrypt_with_named_key encrypts a message using a named key

fn (AGEClient) generate_keypair #

fn (mut client AGEClient) generate_keypair() !KeyPair

generate_keypair creates a new Age encryption key pair

fn (AGEClient) generate_signing_keypair #

fn (mut client AGEClient) generate_signing_keypair() !SigningKeyPair

generate_signing_keypair creates a new Age signing key pair

fn (AGEClient) list_keys #

fn (mut client AGEClient) list_keys() ![]string

list_keys lists all stored AGE keys

fn (AGEClient) sign #

fn (mut client AGEClient) sign(sign_key string, message string) !SignatureResult

sign signs a message with the signing key

fn (AGEClient) sign_with_named_key #

fn (mut client AGEClient) sign_with_named_key(key_name string, message string) !SignatureResult

sign_with_named_key signs a message using a named signing key

fn (AGEClient) verify #

fn (mut client AGEClient) verify(verify_key string, message string, signature string) !bool

verify verifies a signature with the verification key

fn (AGEClient) verify_with_named_key #

fn (mut client AGEClient) verify_with_named_key(key_name string, message string, signature string) !bool

verify_with_named_key verifies a signature using a named verification key

struct AGEClientConfig #

@[params]
struct AGEClientConfig {
pub mut:
	redis_url redisclient.RedisURL = redisclient.RedisURL{
		address: '127.0.0.1'
		port:    6381
	}
	redis     ?&redisclient.Redis
}

struct EncryptionResult #

struct EncryptionResult {
pub:
	ciphertext string // Base64-encoded encrypted data
}

EncryptionResult represents the result of an encryption operation

struct KeyPair #

struct KeyPair {
pub:
	recipient string // Public key (can be shared)
	identity  string // Private key (must be kept secret)
}

KeyPair represents an Age encryption key pair

struct SignatureResult #

struct SignatureResult {
pub:
	signature string // Base64-encoded signature
}

SignatureResult represents the result of a signing operation

struct SigningKeyPair #

struct SigningKeyPair {
pub:
	verify_key string // Public verification key
	sign_key   string // Private signing key
}

SigningKeyPair represents an Age signing key pair