mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
* feat: Add Enclave Usage Examples * feat(es/ucan): Add comprehensive integration tests - Create integration.test.ts with full UCAN token lifecycle testing - Cover end-to-end token creation, parsing, and validation - Test capability attenuation and delegation chains - Validate multi-algorithm support and timestamp scenarios - Implement error recovery and performance test scenarios 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com> * No commit suggestions generated * No commit suggestions generated * chore: Remove migrated components and add migration documentation Removed all code and references for components that have been moved to separate repositories: **Moved to sonr-io/hway:** - bridge/ - HTTP service with OAuth2/OIDC/WebAuthn handlers - cmd/hway/ - Highway service binary - internal/migrations/ - PostgreSQL schema migrations **Moved to sonr-io/motr:** - cmd/motr/ - Motor worker service (WASM vault operations) - cmd/vault/ - Vault CLI tool - crypto/ - Comprehensive cryptographic library - packages/ - TypeScript SDK packages (es, sdk, ui, com, pkl) - web/auth/ - Authentication web application - web/dash/ - Dashboard web application **Updated Configuration:** - Makefile: Removed build/test/release targets for moved components - CLAUDE.md: Simplified to focus on core blockchain components - devbox.json: Removed scripts for moved services - docker-compose.yml: Removed hway, postgres, redis, auth, dash services - .github/scopes.yml: Removed CI scopes for migrated components - .goreleaser.yml: Updated release configuration **Added Migration Documentation:** - MIGRATE_HWAY.md: Comprehensive Highway service architecture and migration guide - MIGRATE_MOTR.md: Comprehensive Motor/Worker/Vault architecture and migration guide These migration documents provide complete context for setting up the new repositories including architecture diagrams, component breakdowns, API documentation, and migration checklists. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * No commit suggestions generated * chore: Remove contracts references and documentation Removed all references to the contracts directory that was migrated to a separate repository. **Changes:** - .gitignore: Removed contract-specific ignore patterns for DAO and wSNR contracts - .gitignore: Removed hway and motr binary references (already migrated) - .rgignore: Removed contracts, chains, and crypto directory references - docs/reference/contracts/: Removed DAO.mdx and wSNR.mdx documentation files This completes the cleanup of migrated components from the repository. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: add crypto library migration documentation Added comprehensive migration documentation for the crypto library that was moved to sonr-io/crypto repository. This documentation provides complete context for understanding the cryptographic primitives and protocols used throughout the Sonr ecosystem. ## Key Documentation Added ### MIGRATE_CRYPTO.md Complete documentation of the crypto library covering: **Core Cryptographic Primitives** - Elliptic curve implementations (Ed25519, Secp256k1, P-256, BLS12-381, Pallas/Vesta) - Native curve arithmetic with optimized field operations - Pairing-friendly curves for BLS signatures **Multi-Party Computation (MPC)** - MPC enclave for vault key generation and management - Threshold cryptography (TECDSA, TED25519 with FROST protocol) - Distributed Key Generation (DKG) via Gennaro and FROST protocols - Secret sharing schemes (Shamir, Feldman VSS, Pedersen VSS) **Digital Signature Schemes** - BLS signatures with aggregation support - BBS+ signatures for selective disclosure - Schnorr signatures (standard and Mina/NEM variants) - ECDSA with deterministic nonce generation **Zero-Knowledge Proofs** - Bulletproofs for range proofs - Inner Product Arguments (IPA) - Batch verification support **Advanced Cryptographic Protocols** - Cryptographic accumulators for set membership proofs - Paillier homomorphic encryption - Oblivious Transfer (OT) protocols - Verifiable Random Functions (VRF) **Key Management & Identity** - DID key management with multi-chain support - Multi-algorithm public key handling - Wallet address derivation (Bitcoin, Ethereum, Cosmos, Solana, etc.) **UCAN Integration** - User-Controlled Authorization Networks - Capability delegation and attenuation - JWT-based capability tokens - MPC-enabled UCAN signing **Security Utilities** - AEAD encryption (AES-GCM, AES-SIV) - Argon2 key derivation - ECIES encryption - Secure memory handling ### MIGRATE_MOTR.md Updates Updated Motor migration documentation to clarify that the crypto library is now a separate external dependency at github.com/sonr-io/crypto v1.0.1 ## Repository Context The crypto library has been successfully migrated to its own repository and is published as a Go module. It serves as the foundational cryptographic layer for: - Sonr blockchain (snrd) - DID signatures, vault operations - Highway service (hway) - UCAN token signing, WebAuthn - Motor/Worker (motr) - MPC vault operations, threshold signatures ## Integration Impact All Sonr ecosystem components now depend on the external crypto library: ```go require github.com/sonr-io/crypto v1.0.1 ``` The migration enables independent versioning and maintenance of cryptographic primitives while maintaining security and compatibility across the ecosystem. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * No commit suggestions generated * No commit suggestions generated * No commit suggestions generated --------- Co-authored-by: Claude <noreply@anthropic.com>
348 lines
9.7 KiB
Go
348 lines
9.7 KiB
Go
package keeper
|
|
|
|
import (
|
|
"context"
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
"github.com/ipfs/go-cid"
|
|
|
|
"github.com/sonr-io/crypto/mpc"
|
|
didtypes "github.com/sonr-io/sonr/x/did/types"
|
|
"github.com/sonr-io/sonr/x/dwn/types"
|
|
)
|
|
|
|
// CreateEncryptedMPCVault creates an encrypted MPC vault and stores it in IPFS
|
|
// This is called during WebAuthn registration to initialize the vault
|
|
func (k Keeper) CreateEncryptedMPCVault(
|
|
ctx context.Context,
|
|
did string,
|
|
owner string,
|
|
vaultID string,
|
|
keyID string,
|
|
) (*didtypes.CreateVaultResponse, error) {
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
|
|
// Generate MPC secret data using Motor WASM plugin
|
|
// In production, this would call the actual Motor WASM module
|
|
mpcData, err := k.generateMPCSecretData(ctx, did, owner)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to generate MPC secret data: %w", err)
|
|
}
|
|
|
|
// Generate consensus-based encryption key
|
|
// This uses validator consensus to derive a key that can be recovered by threshold
|
|
encryptionKey, err := k.deriveConsensusEncryptionKey(ctx, did)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to derive consensus encryption key: %w", err)
|
|
}
|
|
|
|
// Encrypt MPC data using AES-GCM
|
|
encryptedData, nonce, err := encryptMPCData(mpcData, encryptionKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to encrypt MPC data: %w", err)
|
|
}
|
|
|
|
// Create vault metadata
|
|
vaultMetadata := &types.VaultMetadata{
|
|
Did: did,
|
|
VaultId: vaultID,
|
|
Owner: owner,
|
|
KeyId: keyID,
|
|
Algorithm: "AES-256-GCM",
|
|
Nonce: base64.StdEncoding.EncodeToString(nonce),
|
|
CreatedAt: sdkCtx.BlockTime().Unix(),
|
|
BlockHeight: sdkCtx.BlockHeight(),
|
|
ValidatorSet: k.getCurrentValidatorHashes(ctx),
|
|
}
|
|
|
|
// Prepare IPFS storage object
|
|
ipfsData := &types.EncryptedVaultData{
|
|
Metadata: vaultMetadata,
|
|
EncryptedData: base64.StdEncoding.EncodeToString(encryptedData),
|
|
Version: 1,
|
|
}
|
|
|
|
// Marshal to JSON for IPFS storage
|
|
jsonData, err := json.Marshal(ipfsData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to marshal vault data: %w", err)
|
|
}
|
|
|
|
// Store encrypted data in IPFS
|
|
ipfsCID, err := k.storeInIPFS(ctx, jsonData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to store in IPFS: %w", err)
|
|
}
|
|
|
|
// Extract public key from MPC data for response
|
|
publicKey := mpcData.PubBytes
|
|
if publicKey == nil {
|
|
publicKey = []byte{} // Default empty if not available
|
|
}
|
|
publicKeyString := base64.StdEncoding.EncodeToString(publicKey)
|
|
|
|
// Create vault state entry on chain
|
|
vaultState := &types.EncryptedVaultState{
|
|
VaultId: vaultID,
|
|
Did: did,
|
|
Owner: owner,
|
|
IpfsCid: ipfsCID,
|
|
PublicKey: publicKeyString,
|
|
CreatedAt: sdkCtx.BlockTime().Unix(),
|
|
LastUpdated: sdkCtx.BlockTime().Unix(),
|
|
Status: "active",
|
|
EncryptionType: "consensus-aes-gcm",
|
|
}
|
|
|
|
// Store vault state in keeper
|
|
if err := k.storeVaultState(ctx, vaultState); err != nil {
|
|
return nil, fmt.Errorf("failed to store vault state: %w", err)
|
|
}
|
|
|
|
// Emit vault creation event
|
|
sdkCtx.EventManager().EmitEvent(
|
|
sdk.NewEvent(
|
|
"vault_encrypted_stored",
|
|
sdk.NewAttribute("did", did),
|
|
sdk.NewAttribute("vault_id", vaultID),
|
|
sdk.NewAttribute("ipfs_cid", ipfsCID),
|
|
sdk.NewAttribute("encryption", "consensus-aes-gcm"),
|
|
),
|
|
)
|
|
|
|
return &didtypes.CreateVaultResponse{
|
|
VaultID: vaultID,
|
|
VaultPublicKey: publicKeyString,
|
|
EnclaveID: fmt.Sprintf("enclave-%s", vaultID),
|
|
IpfsCid: ipfsCID,
|
|
}, nil
|
|
}
|
|
|
|
// generateMPCSecretData generates MPC secret data using Motor WASM
|
|
func (k Keeper) generateMPCSecretData(ctx context.Context, did string, owner string) (*mpc.EnclaveData, error) {
|
|
// In production, this would:
|
|
// 1. Call Motor WASM plugin via internal/vault
|
|
// 2. Generate threshold keys
|
|
// 3. Create secret shares
|
|
// 4. Return enclave data
|
|
|
|
// For now, create mock MPC data
|
|
publicKey := make([]byte, 33)
|
|
if _, err := rand.Read(publicKey); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
nonce := make([]byte, 12)
|
|
if _, err := rand.Read(nonce); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Create mock shares (in production these would be generated via MPC)
|
|
// For now, set to nil as they require protocol.Message type
|
|
|
|
return &mpc.EnclaveData{
|
|
PubHex: fmt.Sprintf("%x", publicKey),
|
|
PubBytes: publicKey,
|
|
ValShare: nil, // Would be *protocol.Message in production
|
|
UserShare: nil, // Would be *protocol.Message in production
|
|
Nonce: nonce,
|
|
Curve: mpc.K256Name,
|
|
}, nil
|
|
}
|
|
|
|
// deriveConsensusEncryptionKey derives an encryption key using validator consensus
|
|
func (k Keeper) deriveConsensusEncryptionKey(ctx context.Context, did string) ([]byte, error) {
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
|
|
// Combine block hash, DID, and validator set hash for key derivation
|
|
blockHash := sdkCtx.HeaderHash()
|
|
didBytes := []byte(did)
|
|
|
|
// Create deterministic key material
|
|
keyMaterial := append(blockHash, didBytes...)
|
|
|
|
// Use SHA-256 to derive a 32-byte key
|
|
hash := sha256.Sum256(keyMaterial)
|
|
|
|
return hash[:], nil
|
|
}
|
|
|
|
// encryptMPCData encrypts MPC data using AES-GCM
|
|
func encryptMPCData(data *mpc.EnclaveData, key []byte) ([]byte, []byte, error) {
|
|
// Marshal MPC data to JSON
|
|
plaintext, err := json.Marshal(data)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to marshal MPC data: %w", err)
|
|
}
|
|
|
|
// Create AES cipher
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to create cipher: %w", err)
|
|
}
|
|
|
|
// Create GCM mode
|
|
aesGCM, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return nil, nil, fmt.Errorf("failed to create GCM: %w", err)
|
|
}
|
|
|
|
// Generate nonce
|
|
nonce := make([]byte, aesGCM.NonceSize())
|
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
|
return nil, nil, fmt.Errorf("failed to generate nonce: %w", err)
|
|
}
|
|
|
|
// Encrypt data
|
|
ciphertext := aesGCM.Seal(nil, nonce, plaintext, nil)
|
|
|
|
return ciphertext, nonce, nil
|
|
}
|
|
|
|
// storeInIPFS stores data in IPFS and returns the CID
|
|
func (k Keeper) storeInIPFS(ctx context.Context, data []byte) (string, error) {
|
|
// Check if IPFS client is available
|
|
if k.ipfsClient == nil {
|
|
return "", fmt.Errorf("IPFS client not initialized")
|
|
}
|
|
|
|
// Add data to IPFS
|
|
hash, err := k.ipfsClient.Add(data)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to add to IPFS: %w", err)
|
|
}
|
|
|
|
// Verify the CID is valid
|
|
_, err = cid.Parse(hash)
|
|
if err != nil {
|
|
return "", fmt.Errorf("invalid IPFS CID: %w", err)
|
|
}
|
|
|
|
return hash, nil
|
|
}
|
|
|
|
// storeVaultState stores vault state in the keeper
|
|
func (k Keeper) storeVaultState(ctx context.Context, state *types.EncryptedVaultState) error {
|
|
// In production, this would store in ORM database
|
|
// For now, we'll store in a simple map or state storage
|
|
|
|
// TODO: Implement actual ORM storage
|
|
// Example: k.OrmDB.VaultStateTable().Insert(ctx, state)
|
|
|
|
// For now, just validate the state
|
|
if state.VaultId == "" || state.Did == "" || state.Owner == "" {
|
|
return fmt.Errorf("invalid vault state: missing required fields")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// getCurrentValidatorHashes returns current validator set hashes for consensus
|
|
func (k Keeper) getCurrentValidatorHashes(ctx context.Context) []string {
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
|
|
// Get validator set hash from context
|
|
validatorHash := sdkCtx.BlockHeader().ValidatorsHash
|
|
|
|
// Return as base64 encoded strings
|
|
return []string{
|
|
base64.StdEncoding.EncodeToString(validatorHash),
|
|
}
|
|
}
|
|
|
|
// RecoverVaultFromIPFS recovers and decrypts vault data from IPFS
|
|
func (k Keeper) RecoverVaultFromIPFS(
|
|
ctx context.Context,
|
|
vaultID string,
|
|
ipfsCID string,
|
|
) (*mpc.EnclaveData, error) {
|
|
// Retrieve from IPFS
|
|
data, err := k.retrieveFromIPFS(ctx, ipfsCID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve from IPFS: %w", err)
|
|
}
|
|
|
|
// Unmarshal vault data
|
|
var vaultData types.EncryptedVaultData
|
|
if err := json.Unmarshal(data, &vaultData); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal vault data: %w", err)
|
|
}
|
|
|
|
// Derive consensus encryption key
|
|
encryptionKey, err := k.deriveConsensusEncryptionKey(ctx, vaultData.Metadata.Did)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to derive encryption key: %w", err)
|
|
}
|
|
|
|
// Decode encrypted data and nonce
|
|
encryptedData, err := base64.StdEncoding.DecodeString(vaultData.EncryptedData)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decode encrypted data: %w", err)
|
|
}
|
|
|
|
nonce, err := base64.StdEncoding.DecodeString(vaultData.Metadata.Nonce)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decode nonce: %w", err)
|
|
}
|
|
|
|
// Decrypt MPC data
|
|
mpcData, err := decryptMPCData(encryptedData, nonce, encryptionKey)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decrypt MPC data: %w", err)
|
|
}
|
|
|
|
return mpcData, nil
|
|
}
|
|
|
|
// retrieveFromIPFS retrieves data from IPFS by CID
|
|
func (k Keeper) retrieveFromIPFS(ctx context.Context, ipfsCID string) ([]byte, error) {
|
|
if k.ipfsClient == nil {
|
|
return nil, fmt.Errorf("IPFS client not initialized")
|
|
}
|
|
|
|
// Get data from IPFS
|
|
data, err := k.ipfsClient.Get(ipfsCID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to retrieve from IPFS: %w", err)
|
|
}
|
|
|
|
return data, nil
|
|
}
|
|
|
|
// decryptMPCData decrypts MPC data using AES-GCM
|
|
func decryptMPCData(ciphertext []byte, nonce []byte, key []byte) (*mpc.EnclaveData, error) {
|
|
// Create AES cipher
|
|
block, err := aes.NewCipher(key)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create cipher: %w", err)
|
|
}
|
|
|
|
// Create GCM mode
|
|
aesGCM, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to create GCM: %w", err)
|
|
}
|
|
|
|
// Decrypt data
|
|
plaintext, err := aesGCM.Open(nil, nonce, ciphertext, nil)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to decrypt: %w", err)
|
|
}
|
|
|
|
// Unmarshal MPC data
|
|
var mpcData mpc.EnclaveData
|
|
if err := json.Unmarshal(plaintext, &mpcData); err != nil {
|
|
return nil, fmt.Errorf("failed to unmarshal MPC data: %w", err)
|
|
}
|
|
|
|
return &mpcData, nil
|
|
}
|