Files
sonr/x/dwn/keeper/vault_operations.go
T
40eadc995e Feat/1285 es ucan formatting (#1302)
* 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>
2025-10-10 11:47:18 -04:00

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
}