Go API Reference
Complete API documentation for MetaMUI Crypto Primitives in Go.
Installation
Using Go Modules
go get github.com/metamui/crypto@v3.0.0
Import in Your Code
import (
"github.com/metamui/crypto"
"github.com/metamui/crypto/hash"
"github.com/metamui/crypto/signature"
"github.com/metamui/crypto/encryption"
)
Quick Start
package main
import (
"fmt"
"log"
"github.com/metamui/crypto/signature/ed25519"
"github.com/metamui/crypto/encryption/chacha20poly1305"
)
func main() {
// Generate Ed25519 keypair
keypair, err := ed25519.GenerateKeypair()
if err != nil {
log.Fatal(err)
}
// Sign a message
message := []byte("Hello, World!")
signature, err := keypair.Sign(message)
if err != nil {
log.Fatal(err)
}
// Verify signature
valid := keypair.PublicKey.Verify(message, signature)
fmt.Printf("Signature valid: %v\n", valid)
}
API Documentation
For detailed API documentation, see:
Package Organization
// Core packages
import (
"github.com/metamui/crypto" // Core types and interfaces
"github.com/metamui/crypto/hash" // Hash functions
"github.com/metamui/crypto/signature" // Digital signatures
"github.com/metamui/crypto/encryption"// Symmetric encryption
"github.com/metamui/crypto/kex" // Key exchange
"github.com/metamui/crypto/kdf" // Key derivation
"github.com/metamui/crypto/pqc" // Post-quantum algorithms
"github.com/metamui/crypto/random" // Secure random
"github.com/metamui/crypto/encoding" // Encoding utilities
)
Goroutine Safety
All functions in the MetaMUI Crypto library are goroutine-safe unless explicitly noted.
Concurrent Operations
package main
import (
"sync"
"github.com/metamui/crypto/hash/blake3"
)
func processFiles(files []string) [][]byte {
var wg sync.WaitGroup
hashes := make([][]byte, len(files))
for i, file := range files {
wg.Add(1)
go func(index int, filename string) {
defer wg.Done()
data, _ := os.ReadFile(filename)
hashes[index] = blake3.Sum256(data)
}(i, file)
}
wg.Wait()
return hashes
}
Thread-Safe Contexts
// Each goroutine needs its own hasher instance
func hashConcurrently(chunks [][]byte) [][]byte {
results := make([][]byte, len(chunks))
var wg sync.WaitGroup
for i, chunk := range chunks {
wg.Add(1)
go func(index int, data []byte) {
defer wg.Done()
// Create hasher per goroutine
h := blake3.New()
h.Write(data)
results[index] = h.Sum(nil)
}(i, chunk)
}
wg.Wait()
return results
}
Error Handling
Error Types
import "github.com/metamui/crypto/errors"
// Common error types
var (
ErrInvalidKey = errors.New("invalid key size or format")
ErrDecryptionFailed = errors.New("decryption failed")
ErrInvalidSignature = errors.New("signature verification failed")
ErrInvalidNonce = errors.New("invalid nonce size")
)
// Error checking
result, err := someOperation()
if err != nil {
switch {
case errors.Is(err, crypto.ErrInvalidKey):
// Handle invalid key
case errors.Is(err, crypto.ErrDecryptionFailed):
// Handle decryption failure
default:
// Handle other errors
}
}
Error Wrapping
func processData(data []byte) error {
key, err := generateKey()
if err != nil {
return fmt.Errorf("key generation failed: %w", err)
}
encrypted, err := encrypt(data, key)
if err != nil {
return fmt.Errorf("encryption failed: %w", err)
}
return nil
}
Interface Design
Common Interfaces
// Hasher interface
type Hasher interface {
Write(p []byte) (n int, err error)
Sum(b []byte) []byte
Reset()
Size() int
BlockSize() int
}
// Signer interface
type Signer interface {
Sign(message []byte) ([]byte, error)
PublicKey() PublicKey
}
// Cipher interface
type Cipher interface {
Encrypt(plaintext []byte) ([]byte, error)
Decrypt(ciphertext []byte) ([]byte, error)
}
Interface Implementation
// Ensure type implements interface
var _ Hasher = (*Blake3)(nil)
var _ Signer = (*Ed25519PrivateKey)(nil)
var _ Cipher = (*ChaCha20Poly1305)(nil)
Hash Functions
SHA-256/512
import "github.com/metamui/crypto/hash/sha256"
import "github.com/metamui/crypto/hash/sha512"
// Simple hashing
hash256 := sha256.Sum256(data)
hash512 := sha512.Sum512(data)
// Streaming hash
h := sha256.New()
h.Write(chunk1)
h.Write(chunk2)
hash := h.Sum(nil)
// HMAC
mac := sha256.HMAC(key, message)
valid := sha256.VerifyHMAC(key, message, mac)
BLAKE2b/2s
import "github.com/metamui/crypto/hash/blake2b"
import "github.com/metamui/crypto/hash/blake2s"
// Standard BLAKE2b (64 bytes)
hash := blake2b.Sum512(data)
// Custom output size
hash32 := blake2b.Sum256(data)
// Keyed hashing
mac, err := blake2b.SumKeyed(data, key)
// With personalization
h, _ := blake2b.NewWithParams(&blake2b.Config{
Size: 32,
Key: key,
Salt: salt,
Person: []byte("MyApp"),
})
h.Write(data)
hash := h.Sum(nil)
BLAKE3
import "github.com/metamui/crypto/hash/blake3"
// Standard hash
hash := blake3.Sum256(data)
// Keyed hash
keyedHash := blake3.SumKeyed(data, key)
// Derive key
derivedKey := blake3.DeriveKey(
"my-context",
inputKeyMaterial,
32, // output size
)
// Streaming with New()
h := blake3.New()
for _, chunk := range chunks {
h.Write(chunk)
}
hash := h.Sum(nil)
// Extended output (XOF)
xof := blake3.NewXOF()
xof.Write(data)
output := make([]byte, 100) // Any size
xof.Read(output)
Digital Signatures
Ed25519
import "github.com/metamui/crypto/signature/ed25519"
// Generate keypair
keypair, err := ed25519.GenerateKeypair()
// Generate from seed
seed := make([]byte, 32)
rand.Read(seed)
keypair = ed25519.NewKeypairFromSeed(seed)
// Sign
signature, err := keypair.Sign(message)
// Verify
valid := keypair.PublicKey.Verify(message, signature)
// Export/Import keys
publicBytes := keypair.PublicKey.Bytes()
privateBytes := keypair.PrivateKey.Bytes()
publicKey, err := ed25519.PublicKeyFromBytes(publicBytes)
privateKey, err := ed25519.PrivateKeyFromBytes(privateBytes)
// Batch verification
signatures := [][]byte{sig1, sig2, sig3}
messages := [][]byte{msg1, msg2, msg3}
publicKeys := []*ed25519.PublicKey{pk1, pk2, pk3}
results := ed25519.BatchVerify(signatures, messages, publicKeys)
Sr25519
import "github.com/metamui/crypto/signature/sr25519"
// Generate keypair
keypair, err := sr25519.GenerateKeypair()
// Mini secret key from seed
miniSecret := sr25519.MiniSecretFromSeed(seed)
keypair = miniSecret.ExpandToKeypair()
// Sign with context
signature, err := keypair.SignWithContext(message, []byte("substrate"))
// Verify with context
valid := keypair.PublicKey.VerifyWithContext(
message, signature, []byte("substrate"))
// VRF (Verifiable Random Function)
output, proof, err := keypair.VRFSign(message)
valid, output2 := keypair.PublicKey.VRFVerify(message, proof)
Encryption
ChaCha20-Poly1305
import "github.com/metamui/crypto/encryption/chacha20poly1305"
// Create cipher
cipher, err := chacha20poly1305.New(key)
// Generate nonce
nonce := make([]byte, chacha20poly1305.NonceSize)
rand.Read(nonce)
// Encrypt with associated data
ciphertext := cipher.Seal(nil, nonce, plaintext, associatedData)
// Decrypt and verify
plaintext, err := cipher.Open(nil, nonce, ciphertext, associatedData)
// Using XChaCha20-Poly1305 (24-byte nonce)
xcipher, err := chacha20poly1305.NewX(key)
xnonce := make([]byte, chacha20poly1305.XNonceSize)
AES-256-GCM
import "github.com/metamui/crypto/encryption/aes"
// Create cipher
block, err := aes.NewCipher(key)
gcm, err := aes.NewGCM(block)
// Generate nonce
nonce := make([]byte, gcm.NonceSize())
rand.Read(nonce)
// Encrypt
ciphertext := gcm.Seal(nil, nonce, plaintext, associatedData)
// Decrypt
plaintext, err := gcm.Open(nil, nonce, ciphertext, associatedData)
// Check hardware support
if aes.HasAESNI() {
fmt.Println("Using AES-NI hardware acceleration")
}
Key Exchange
X25519
import "github.com/metamui/crypto/kex/x25519"
// Generate keypairs
alicePrivate, alicePublic, _ := x25519.GenerateKeypair()
bobPrivate, bobPublic, _ := x25519.GenerateKeypair()
// Compute shared secrets
aliceShared, _ := x25519.SharedSecret(alicePrivate, bobPublic)
bobShared, _ := x25519.SharedSecret(bobPrivate, alicePublic)
// aliceShared == bobShared
// From seed
privateKey := x25519.NewPrivateKeyFromSeed(seed)
publicKey := x25519.PublicKeyFromPrivate(privateKey)
Post-Quantum Algorithms
ML-KEM-768
import "github.com/metamui/crypto/pqc/mlkem"
// Generate keypair
keypair, err := mlkem.GenerateKeypair768()
// Encapsulation (sender)
ciphertext, sharedSecret, err := mlkem.Encapsulate768(
keypair.PublicKey)
// Decapsulation (receiver)
sharedSecret2, err := mlkem.Decapsulate768(
ciphertext, keypair.PrivateKey)
// Both parties have same shared secret
if !bytes.Equal(sharedSecret, sharedSecret2) {
panic("shared secrets don't match")
}
// Serialize keys
publicBytes := keypair.PublicKey.Bytes()
privateBytes := keypair.PrivateKey.Bytes()
Dilithium
import "github.com/metamui/crypto/pqc/dilithium"
// Generate keypair
keypair, err := dilithium.GenerateKeypair3()
// Sign message
signature, err := dilithium.Sign3(message, keypair.PrivateKey)
// Verify signature
valid := dilithium.Verify3(message, signature, keypair.PublicKey)
// Deterministic signatures
signature = dilithium.SignDeterministic3(
message, keypair.PrivateKey)
// Serialize
publicBytes := keypair.PublicKey.Marshal()
privateBytes := keypair.PrivateKey.Marshal()
// Deserialize
publicKey, _ := dilithium.UnmarshalPublicKey3(publicBytes)
privateKey, _ := dilithium.UnmarshalPrivateKey3(privateBytes)
Key Derivation
Argon2
import "github.com/metamui/crypto/kdf/argon2"
// Generate salt
salt := make([]byte, 16)
rand.Read(salt)
// Hash password
hash := argon2.Hash(
password,
salt,
&argon2.Config{
Time: 3, // iterations
Memory: 64*1024,// 64 MB
Threads: 4, // parallelism
KeyLen: 32, // output length
},
)
// Verify password
valid := argon2.Verify(password, hash)
// IDKey variant
key := argon2.IDKey(
password, salt,
3, 64*1024, 4, 32,
)
PBKDF2
import "github.com/metamui/crypto/kdf/pbkdf2"
// Derive key
key := pbkdf2.Key(
password,
salt,
100000, // iterations
32, // key length
sha256.New,// PRF
)
// With progress callback
key := pbkdf2.KeyWithProgress(
password, salt, 100000, 32, sha256.New,
func(current, total int) {
fmt.Printf("Progress: %d/%d\n", current, total)
},
)
HKDF
import "github.com/metamui/crypto/kdf/hkdf"
// Extract
prk := hkdf.Extract(sha256.New, inputKeyMaterial, salt)
// Expand
okm := make([]byte, 42)
err := hkdf.Expand(sha256.New, prk, info, okm)
// One-shot
reader := hkdf.New(sha256.New, inputKeyMaterial, salt, info)
key := make([]byte, 32)
_, err := io.ReadFull(reader, key)
Context Support
Using context.Context
import (
"context"
"time"
)
// Operations with timeout
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
// Argon2 with context (can be cancelled)
hash, err := argon2.HashContext(ctx, password, salt, config)
if err == context.DeadlineExceeded {
fmt.Println("Operation timed out")
}
// Batch operations with context
results, err := ed25519.BatchVerifyContext(
ctx, signatures, messages, publicKeys)
// Stream processing with context
func processStream(ctx context.Context, r io.Reader) ([]byte, error) {
h := blake3.New()
buf := make([]byte, 4096)
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
n, err := r.Read(buf)
if err == io.EOF {
return h.Sum(nil), nil
}
if err != nil {
return nil, err
}
h.Write(buf[:n])
}
}
}
Performance
Buffer Reuse
import "sync"
// Buffer pool for encryption
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 4096)
},
}
func encryptData(cipher *chacha20poly1305.Cipher, data []byte) []byte {
// Get buffer from pool
buf := bufferPool.Get().([]byte)
defer bufferPool.Put(buf)
// Use buffer for encryption
nonce := buf[:12]
rand.Read(nonce)
return cipher.Seal(data[:0], nonce, data, nil)
}
Zero-Allocation APIs
// In-place operations
func xorInPlace(dst, src []byte) {
for i := range dst {
dst[i] ^= src[i]
}
}
// Reuse output buffer
output := make([]byte, 0, len(input)+16)
output = cipher.Seal(output, nonce, input, nil)
// Stack allocation for small buffers
var key [32]byte
rand.Read(key[:])
Parallel Processing
import "golang.org/x/sync/errgroup"
// Parallel file hashing
func hashFiles(files []string) ([][]byte, error) {
g := new(errgroup.Group)
hashes := make([][]byte, len(files))
for i, file := range files {
i, file := i, file // capture loop variables
g.Go(func() error {
data, err := os.ReadFile(file)
if err != nil {
return err
}
hashes[i] = blake3.Sum256(data)
return nil
})
}
if err := g.Wait(); err != nil {
return nil, err
}
return hashes, nil
}
Build Constraints and Tags
Platform-Specific Code
// +build amd64,!noasm
package blake3
// Assembly-optimized implementation for AMD64
// +build !amd64 noasm
package blake3
// Pure Go implementation
Feature Flags
# Build with pure Go implementations
go build -tags noasm
# Build with CGO disabled
CGO_ENABLED=0 go build
# Build with specific features
go build -tags "argon2 blake3 pqc"
CGO Integration
Using C Libraries
// #cgo LDFLAGS: -lcrypto
// #include <openssl/evp.h>
import "C"
func hashWithOpenSSL(data []byte) []byte {
ctx := C.EVP_MD_CTX_new()
defer C.EVP_MD_CTX_free(ctx)
C.EVP_DigestInit_ex(ctx, C.EVP_sha256(), nil)
C.EVP_DigestUpdate(ctx, unsafe.Pointer(&data[0]), C.size_t(len(data)))
hash := make([]byte, 32)
var size C.uint
C.EVP_DigestFinal_ex(ctx, (*C.uchar)(&hash[0]), &size)
return hash
}
Utility Functions
Secure Random
import "github.com/metamui/crypto/random"
// Generate random bytes
randomBytes := random.Bytes(32)
// Generate random integer
randomInt := random.Int()
randomIntN := random.Intn(100) // 0-99
// Fill existing buffer
buffer := make([]byte, 64)
random.Read(buffer)
// Cryptographically secure random reader
reader := random.Reader
io.ReadFull(reader, buffer)
Constant-Time Operations
import "github.com/metamui/crypto/subtle"
// Constant-time comparison
equal := subtle.ConstantTimeCompare(a, b) == 1
// Constant-time selection
subtle.ConstantTimeSelect(1, x, y) // returns x
subtle.ConstantTimeSelect(0, x, y) // returns y
// Constant-time copy
subtle.ConstantTimeCopy(1, dst, src) // copies src to dst
subtle.ConstantTimeCopy(0, dst, src) // doesn't copy
// Clear sensitive data
subtle.SecureZero(sensitiveData)
Encoding
import "github.com/metamui/crypto/encoding"
// Hex encoding
hexStr := encoding.HexEncode(data)
decoded, err := encoding.HexDecode(hexStr)
// Base64
b64Str := encoding.Base64Encode(data)
decoded, err := encoding.Base64Decode(b64Str)
// Base64 URL-safe
b64url := encoding.Base64URLEncode(data)
decoded, err := encoding.Base64URLDecode(b64url)
// Base32
b32Str := encoding.Base32Encode(data)
decoded, err := encoding.Base32Decode(b32Str)
Security Best Practices
Secure Memory Handling
import (
"runtime"
"github.com/metamui/crypto/secure"
)
// Lock memory to prevent swapping
func lockMemory(data []byte) {
secure.Mlock(data)
defer secure.Munlock(data)
// Use data...
}
// Clear sensitive data
func clearSensitive(data []byte) {
defer secure.Zero(data)
// Use data...
}
// Prevent compiler optimizations
func useVolatile(data []byte) {
runtime.KeepAlive(data)
}
Timing Attack Prevention
// DON'T: Variable-time comparison
if bytes.Equal(computed, expected) { // BAD!
// ...
}
// DO: Constant-time comparison
if subtle.ConstantTimeCompare(computed, expected) == 1 { // GOOD!
// ...
}
// DON'T: Early return on mismatch
for i := range computed {
if computed[i] != expected[i] { // BAD!
return false
}
}
// DO: Always check all bytes
equal := 1
for i := range computed {
equal &= subtle.ConstantTimeByteEq(computed[i], expected[i])
}
return equal == 1
Examples
Encrypted Communication Protocol
package main
import (
"github.com/metamui/crypto/pqc/mlkem"
"github.com/metamui/crypto/signature/ed25519"
"github.com/metamui/crypto/encryption/chacha20poly1305"
"github.com/metamui/crypto/kdf/hkdf"
)
type SecureChannel struct {
sessionKey []byte
cipher *chacha20poly1305.Cipher
nonce uint64
}
func EstablishChannel(
ourIdentity *ed25519.Keypair,
theirPublicKey *ed25519.PublicKey,
theirKEMKey []byte,
) (*SecureChannel, error) {
// Generate ephemeral KEM key
ciphertext, sharedSecret, err := mlkem.Encapsulate768(theirKEMKey)
if err != nil {
return nil, err
}
// Sign the ciphertext
signature, err := ourIdentity.Sign(ciphertext)
if err != nil {
return nil, err
}
// Send ciphertext and signature to peer...
// Receive their response...
// Derive session key
reader := hkdf.New(sha256.New, sharedSecret, nil, []byte("session"))
sessionKey := make([]byte, 32)
io.ReadFull(reader, sessionKey)
// Create cipher
cipher, err := chacha20poly1305.New(sessionKey)
if err != nil {
return nil, err
}
return &SecureChannel{
sessionKey: sessionKey,
cipher: cipher,
}, nil
}
File Encryption Tool
package main
import (
"io"
"os"
"github.com/metamui/crypto/encryption/chacha20poly1305"
"github.com/metamui/crypto/kdf/argon2"
"github.com/metamui/crypto/random"
)
func EncryptFile(inputPath, outputPath, password string) error {
// Generate salt
salt := random.Bytes(16)
// Derive key from password
key := argon2.IDKey(
[]byte(password), salt,
3, 64*1024, 4, 32,
)
// Create cipher
cipher, err := chacha20poly1305.NewX(key)
if err != nil {
return err
}
// Open files
input, err := os.Open(inputPath)
if err != nil {
return err
}
defer input.Close()
output, err := os.Create(outputPath)
if err != nil {
return err
}
defer output.Close()
// Write salt
output.Write(salt)
// Generate nonce
nonce := random.Bytes(24)
output.Write(nonce)
// Encrypt file in chunks
buffer := make([]byte, 64*1024)
for {
n, err := input.Read(buffer)
if err == io.EOF {
break
}
if err != nil {
return err
}
encrypted := cipher.Seal(nil, nonce, buffer[:n], nil)
output.Write(encrypted)
// Increment nonce
incrementNonce(nonce)
}
return nil
}
JWT Token Handling
package jwt
import (
"encoding/json"
"time"
"github.com/metamui/crypto/signature/ed25519"
"github.com/metamui/crypto/encoding"
)
type Claims map[string]interface{}
func CreateToken(claims Claims, key *ed25519.PrivateKey) (string, error) {
// Add standard claims
claims["iat"] = time.Now().Unix()
claims["exp"] = time.Now().Add(time.Hour).Unix()
// Create header
header := map[string]string{
"alg": "EdDSA",
"typ": "JWT",
}
// Encode header and payload
headerJSON, _ := json.Marshal(header)
headerB64 := encoding.Base64URLEncode(headerJSON)
payloadJSON, _ := json.Marshal(claims)
payloadB64 := encoding.Base64URLEncode(payloadJSON)
// Create signature
message := headerB64 + "." + payloadB64
signature, err := key.Sign([]byte(message))
if err != nil {
return "", err
}
// Return complete JWT
return message + "." + encoding.Base64URLEncode(signature), nil
}
func VerifyToken(token string, key *ed25519.PublicKey) (Claims, error) {
// Split token
parts := strings.Split(token, ".")
if len(parts) != 3 {
return nil, errors.New("invalid token format")
}
// Verify signature
message := parts[0] + "." + parts[1]
signature, err := encoding.Base64URLDecode(parts[2])
if err != nil {
return nil, err
}
if !key.Verify([]byte(message), signature) {
return nil, errors.New("invalid signature")
}
// Decode claims
payloadJSON, err := encoding.Base64URLDecode(parts[1])
if err != nil {
return nil, err
}
var claims Claims
err = json.Unmarshal(payloadJSON, &claims)
return claims, err
}
Testing
Unit Tests
package crypto_test
import (
"testing"
"bytes"
"github.com/metamui/crypto/signature/ed25519"
)
func TestEd25519SignVerify(t *testing.T) {
keypair, err := ed25519.GenerateKeypair()
if err != nil {
t.Fatal(err)
}
message := []byte("test message")
signature, err := keypair.Sign(message)
if err != nil {
t.Fatal(err)
}
if !keypair.PublicKey.Verify(message, signature) {
t.Error("signature verification failed")
}
// Modify message
message[0] ^= 1
if keypair.PublicKey.Verify(message, signature) {
t.Error("modified message verification should fail")
}
}
func BenchmarkBlake3(b *testing.B) {
data := make([]byte, 1024)
b.SetBytes(int64(len(data)))
b.ResetTimer()
for i := 0; i < b.N; i++ {
_ = blake3.Sum256(data)
}
}
Fuzz Testing
// +build gofuzzer
package crypto
import "github.com/metamui/crypto/encryption/chacha20poly1305"
func Fuzz(data []byte) int {
if len(data) < 32 {
return -1
}
key := data[:32]
cipher, err := chacha20poly1305.New(key)
if err != nil {
return 0
}
// Try to encrypt/decrypt
nonce := make([]byte, 12)
encrypted := cipher.Seal(nil, nonce, data[32:], nil)
decrypted, err := cipher.Open(nil, nonce, encrypted, nil)
if err != nil {
return 0
}
if !bytes.Equal(decrypted, data[32:]) {
panic("decryption mismatch")
}
return 1
}