Borg/pkg/stmf/keypair.go
Snider b3755da69d feat: Add STMF form encryption and SMSG secure message packages
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>
2025-12-27 00:49:07 +00:00

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)
}