mirror of
https://github.com/sonr-io/crypto.git
synced 2026-01-12 04:09:13 +00:00
225 lines
5.5 KiB
Go
225 lines
5.5 KiB
Go
// Package wasm provides cryptographic verification for WebAssembly modules
|
|
package wasm
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"sync"
|
|
)
|
|
|
|
// HashVerifier provides SHA256 hash verification for WASM modules
|
|
type HashVerifier struct {
|
|
// trustedHashes stores SHA256 hashes of trusted WASM modules
|
|
trustedHashes map[string]string
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// NewHashVerifier creates a new WASM hash verifier
|
|
func NewHashVerifier() *HashVerifier {
|
|
return &HashVerifier{
|
|
trustedHashes: make(map[string]string),
|
|
}
|
|
}
|
|
|
|
// ComputeHash calculates SHA256 hash of WASM bytecode
|
|
func (v *HashVerifier) ComputeHash(wasmBytes []byte) string {
|
|
hash := sha256.Sum256(wasmBytes)
|
|
return hex.EncodeToString(hash[:])
|
|
}
|
|
|
|
// AddTrustedHash adds a trusted hash for a named WASM module
|
|
func (v *HashVerifier) AddTrustedHash(name, hash string) {
|
|
v.mu.Lock()
|
|
defer v.mu.Unlock()
|
|
v.trustedHashes[name] = hash
|
|
}
|
|
|
|
// VerifyHash verifies WASM bytecode against trusted hash
|
|
func (v *HashVerifier) VerifyHash(name string, wasmBytes []byte) error {
|
|
v.mu.RLock()
|
|
trustedHash, exists := v.trustedHashes[name]
|
|
v.mu.RUnlock()
|
|
|
|
if !exists {
|
|
return fmt.Errorf("no trusted hash found for WASM module: %s", name)
|
|
}
|
|
|
|
computedHash := v.ComputeHash(wasmBytes)
|
|
if computedHash != trustedHash {
|
|
return fmt.Errorf(
|
|
"WASM hash verification failed for %s: expected %s, got %s",
|
|
name, trustedHash, computedHash,
|
|
)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// VerifyHashWithFallback verifies against primary hash or fallback list
|
|
func (v *HashVerifier) VerifyHashWithFallback(name string, wasmBytes []byte, fallbackHashes []string) error {
|
|
// Try primary verification first
|
|
if err := v.VerifyHash(name, wasmBytes); err == nil {
|
|
return nil
|
|
}
|
|
|
|
// Check against fallback hashes
|
|
computedHash := v.ComputeHash(wasmBytes)
|
|
for _, fallbackHash := range fallbackHashes {
|
|
if computedHash == fallbackHash {
|
|
// Update trusted hash for future use
|
|
v.AddTrustedHash(name, computedHash)
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf(
|
|
"WASM hash verification failed: computed hash %s not in trusted set",
|
|
computedHash,
|
|
)
|
|
}
|
|
|
|
// GetTrustedHash retrieves the trusted hash for a module
|
|
func (v *HashVerifier) GetTrustedHash(name string) (string, bool) {
|
|
v.mu.RLock()
|
|
defer v.mu.RUnlock()
|
|
hash, exists := v.trustedHashes[name]
|
|
return hash, exists
|
|
}
|
|
|
|
// ClearTrustedHashes removes all trusted hashes
|
|
func (v *HashVerifier) ClearTrustedHashes() {
|
|
v.mu.Lock()
|
|
defer v.mu.Unlock()
|
|
v.trustedHashes = make(map[string]string)
|
|
}
|
|
|
|
// HashChain provides hash chain verification for plugin updates
|
|
type HashChain struct {
|
|
chain []HashEntry
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
// HashEntry represents a single entry in the hash chain
|
|
type HashEntry struct {
|
|
Version string `json:"version"`
|
|
Hash string `json:"hash"`
|
|
PreviousHash string `json:"previous_hash"`
|
|
Timestamp int64 `json:"timestamp"`
|
|
}
|
|
|
|
// NewHashChain creates a new hash chain
|
|
func NewHashChain() *HashChain {
|
|
return &HashChain{
|
|
chain: make([]HashEntry, 0),
|
|
}
|
|
}
|
|
|
|
// AddEntry adds a new entry to the hash chain
|
|
func (hc *HashChain) AddEntry(version, hash string, timestamp int64) error {
|
|
hc.mu.Lock()
|
|
defer hc.mu.Unlock()
|
|
|
|
previousHash := ""
|
|
if len(hc.chain) > 0 {
|
|
previousHash = hc.chain[len(hc.chain)-1].Hash
|
|
}
|
|
|
|
entry := HashEntry{
|
|
Version: version,
|
|
Hash: hash,
|
|
PreviousHash: previousHash,
|
|
Timestamp: timestamp,
|
|
}
|
|
|
|
hc.chain = append(hc.chain, entry)
|
|
return nil
|
|
}
|
|
|
|
// VerifyChain verifies the integrity of the hash chain
|
|
func (hc *HashChain) VerifyChain() error {
|
|
hc.mu.RLock()
|
|
defer hc.mu.RUnlock()
|
|
|
|
if len(hc.chain) == 0 {
|
|
return nil
|
|
}
|
|
|
|
// First entry should have empty previous hash
|
|
if hc.chain[0].PreviousHash != "" {
|
|
return fmt.Errorf("invalid hash chain: first entry has non-empty previous hash")
|
|
}
|
|
|
|
// Verify chain continuity
|
|
for i := 1; i < len(hc.chain); i++ {
|
|
if hc.chain[i].PreviousHash != hc.chain[i-1].Hash {
|
|
return fmt.Errorf(
|
|
"hash chain broken at version %s: expected previous hash %s, got %s",
|
|
hc.chain[i].Version,
|
|
hc.chain[i-1].Hash,
|
|
hc.chain[i].PreviousHash,
|
|
)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetLatestEntry returns the most recent hash chain entry
|
|
func (hc *HashChain) GetLatestEntry() (*HashEntry, error) {
|
|
hc.mu.RLock()
|
|
defer hc.mu.RUnlock()
|
|
|
|
if len(hc.chain) == 0 {
|
|
return nil, fmt.Errorf("hash chain is empty")
|
|
}
|
|
|
|
latest := hc.chain[len(hc.chain)-1]
|
|
return &latest, nil
|
|
}
|
|
|
|
// VerificationError represents a WASM verification failure
|
|
type VerificationError struct {
|
|
Module string
|
|
ExpectedHash string
|
|
ActualHash string
|
|
Reason string
|
|
}
|
|
|
|
// Error implements the error interface
|
|
func (e *VerificationError) Error() string {
|
|
return fmt.Sprintf(
|
|
"WASM verification failed for %s: %s (expected: %s, actual: %s)",
|
|
e.Module, e.Reason, e.ExpectedHash, e.ActualHash,
|
|
)
|
|
}
|
|
|
|
// SecurityPolicy defines verification requirements
|
|
type SecurityPolicy struct {
|
|
RequireHashVerification bool
|
|
RequireSignature bool
|
|
AllowedHashes []string
|
|
MaxModuleSize int64
|
|
}
|
|
|
|
// DefaultSecurityPolicy returns a secure default policy
|
|
func DefaultSecurityPolicy() *SecurityPolicy {
|
|
return &SecurityPolicy{
|
|
RequireHashVerification: true,
|
|
RequireSignature: false, // Will be enabled in next phase
|
|
AllowedHashes: []string{},
|
|
MaxModuleSize: 10 * 1024 * 1024, // 10MB max
|
|
}
|
|
}
|
|
|
|
// Validate checks if WASM module meets security policy
|
|
func (p *SecurityPolicy) Validate(wasmBytes []byte) error {
|
|
if p.MaxModuleSize > 0 && int64(len(wasmBytes)) > p.MaxModuleSize {
|
|
return fmt.Errorf(
|
|
"WASM module size %d exceeds maximum allowed size %d",
|
|
len(wasmBytes), p.MaxModuleSize,
|
|
)
|
|
}
|
|
return nil
|
|
}
|