STMF (Sovereign Form Encryption): - X25519 ECDH + ChaCha20-Poly1305 hybrid encryption - Go library (pkg/stmf/) with encrypt/decrypt and HTTP middleware - WASM module for client-side browser encryption - JavaScript wrapper with TypeScript types (js/borg-stmf/) - PHP library for server-side decryption (php/borg-stmf/) - Full cross-platform interoperability (Go <-> PHP) SMSG (Secure Message): - Password-based ChaCha20-Poly1305 message encryption - Support for attachments, metadata, and PKI reply keys - WASM bindings for browser-based decryption Demos: - index.html: Form encryption demo with modern dark UI - support-reply.html: Decrypt password-protected messages - examples/smsg-reply/: CLI tool for creating encrypted replies 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
107 lines
2.9 KiB
Go
107 lines
2.9 KiB
Go
package stmf
|
|
|
|
import (
|
|
"crypto/ecdh"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"fmt"
|
|
)
|
|
|
|
// KeyPair represents an X25519 keypair for STMF encryption
|
|
type KeyPair struct {
|
|
privateKey *ecdh.PrivateKey
|
|
publicKey *ecdh.PublicKey
|
|
}
|
|
|
|
// GenerateKeyPair generates a new X25519 keypair
|
|
func GenerateKeyPair() (*KeyPair, error) {
|
|
curve := ecdh.X25519()
|
|
privateKey, err := curve.GenerateKey(rand.Reader)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrKeyGenerationFailed, err)
|
|
}
|
|
|
|
return &KeyPair{
|
|
privateKey: privateKey,
|
|
publicKey: privateKey.PublicKey(),
|
|
}, nil
|
|
}
|
|
|
|
// PublicKey returns the raw public key bytes (32 bytes)
|
|
func (k *KeyPair) PublicKey() []byte {
|
|
return k.publicKey.Bytes()
|
|
}
|
|
|
|
// PrivateKey returns the raw private key bytes (32 bytes)
|
|
func (k *KeyPair) PrivateKey() []byte {
|
|
return k.privateKey.Bytes()
|
|
}
|
|
|
|
// PublicKeyBase64 returns the public key as a base64-encoded string
|
|
func (k *KeyPair) PublicKeyBase64() string {
|
|
return base64.StdEncoding.EncodeToString(k.publicKey.Bytes())
|
|
}
|
|
|
|
// PrivateKeyBase64 returns the private key as a base64-encoded string
|
|
func (k *KeyPair) PrivateKeyBase64() string {
|
|
return base64.StdEncoding.EncodeToString(k.privateKey.Bytes())
|
|
}
|
|
|
|
// LoadPublicKey loads a public key from raw bytes
|
|
func LoadPublicKey(data []byte) (*ecdh.PublicKey, error) {
|
|
curve := ecdh.X25519()
|
|
pub, err := curve.NewPublicKey(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrInvalidPublicKey, err)
|
|
}
|
|
return pub, nil
|
|
}
|
|
|
|
// LoadPublicKeyBase64 loads a public key from a base64-encoded string
|
|
func LoadPublicKeyBase64(encoded string) (*ecdh.PublicKey, error) {
|
|
data, err := base64.StdEncoding.DecodeString(encoded)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid base64: %v", ErrInvalidPublicKey, err)
|
|
}
|
|
return LoadPublicKey(data)
|
|
}
|
|
|
|
// LoadPrivateKey loads a private key from raw bytes
|
|
func LoadPrivateKey(data []byte) (*ecdh.PrivateKey, error) {
|
|
curve := ecdh.X25519()
|
|
priv, err := curve.NewPrivateKey(data)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: %v", ErrInvalidPrivateKey, err)
|
|
}
|
|
return priv, nil
|
|
}
|
|
|
|
// LoadPrivateKeyBase64 loads a private key from a base64-encoded string
|
|
func LoadPrivateKeyBase64(encoded string) (*ecdh.PrivateKey, error) {
|
|
data, err := base64.StdEncoding.DecodeString(encoded)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid base64: %v", ErrInvalidPrivateKey, err)
|
|
}
|
|
return LoadPrivateKey(data)
|
|
}
|
|
|
|
// LoadKeyPair loads a keypair from raw private key bytes
|
|
func LoadKeyPair(privateKeyBytes []byte) (*KeyPair, error) {
|
|
priv, err := LoadPrivateKey(privateKeyBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &KeyPair{
|
|
privateKey: priv,
|
|
publicKey: priv.PublicKey(),
|
|
}, nil
|
|
}
|
|
|
|
// LoadKeyPairBase64 loads a keypair from a base64-encoded private key
|
|
func LoadKeyPairBase64(privateKeyBase64 string) (*KeyPair, error) {
|
|
data, err := base64.StdEncoding.DecodeString(privateKeyBase64)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("%w: invalid base64: %v", ErrInvalidPrivateKey, err)
|
|
}
|
|
return LoadKeyPair(data)
|
|
}
|