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
}

See Also