* clear

* feat: Add everything

* fix: Commenht
This commit is contained in:
Prad Nukala
2025-10-03 14:45:52 -04:00
committed by GitHub
parent 43b4a11c06
commit 13e6c3e84d
1935 changed files with 655061 additions and 40058 deletions
+18
View File
@@ -0,0 +1,18 @@
[tool.commitizen]
name = "cz_customize"
tag_format = "vault/v$version"
ignored_tag_formats = ["*/v${version}", "v${version}"]
version_scheme = "semver"
version_provider = "scm"
update_changelog_on_bump = true
changelog_file = "CHANGELOG.md"
major_version_zero = true
annotated_tag = true
pre_bump_hooks = ["bash scripts/hook-bump-pre.sh"]
post_bump_hooks = ["goreleaser release --clean -f cmd/vault/.goreleaser.yml"]
[tool.commitizen.customize]
bump_pattern = "^(feat|fix|refactor|perf|BREAKING CHANGE)"
bump_map = { "BREAKING CHANGE" = "MAJOR", "feat" = "MINOR", "fix" = "PATCH", "refactor" = "PATCH", "perf" = "PATCH" }
default_bump = "PATCH"
changelog_pattern = "^(feat|fix|refactor|docs|build)\\(vault\\)(!)?:"
+71
View File
@@ -0,0 +1,71 @@
# yaml-language-server: $schema=https://goreleaser.com/static/schema-pro.json
---
version: 2
dist: dist/vault
monorepo:
tag_prefix: vault/
dir: cmd/vault
project_name: vault
before:
hooks:
- go mod download
builds:
- id: vault
main: main.go
binary: vault
no_unique_dist_dir: true
hooks:
post:
- cp dist/vault/vault.wasm x/dwn/client/plugin/vault.wasm
- cp dist/vault/vault.wasm packages/es/src/plugin/plugin.wasm
mod_timestamp: "{{ .CommitTimestamp }}"
env:
- CGO_ENABLED=0
goos:
- wasip1
goarch:
- wasm
flags:
- -mod=readonly
- -trimpath
ldflags:
- -s -w
- -X main.version={{.Version}}
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}
archives:
- id: vault-wasm-archive
name_template: "vault_wasm_{{ .Version }}"
formats: ["binary"]
wrap_in_directory: true
blobs:
- provider: s3
endpoint: https://eb37925850388bca807b7fab964c12bb.r2.cloudflarestorage.com
bucket: releases
region: auto
directory: "vault/{{ .Tag }}"
release:
disable: false
github:
owner: sonr-io
name: sonr
name_template: "{{.ProjectName}}/{{ .Tag }}"
draft: false
replace_existing_draft: false # Don't replace drafts
replace_existing_artifacts: false # Append, don't replace
mode: append # Explicitly set to append mode
checksum:
name_template: "vault_checksums.txt"
snapshot:
version_template: "{{ incpatch .Version }}-dev"
# Changelog configuration
changelog:
sort: asc
+120
View File
@@ -0,0 +1,120 @@
#!/usr/bin/make -f
# Output configuration - dual output for both plugin and ES package
GIT_ROOT := $(shell git rev-parse --show-toplevel)
PLUGIN_DIR := $(GIT_ROOT)/x/dwn/client/plugin
ES_PACKAGE_DIR := $(GIT_ROOT)/packages/es/src/plugin
OUTPUT_FILE := plugin.wasm
PLUGIN_PATH := $(PLUGIN_DIR)/vault.wasm
ES_PATH := $(ES_PACKAGE_DIR)/$(OUTPUT_FILE)
# Build configuration for WASM
GOOS := wasip1
GOARCH := wasm
# Version information
VERSION := $(shell echo $(shell git describe --tags 2>/dev/null || echo "dev") | sed 's/^v//')
COMMIT := $(shell git log -1 --format='%H')
.PHONY: all build clean install test version help
all: build
build:
@echo "Building vault WASM module..."
@echo "Targets: Plugin and ES package"
@mkdir -p $(PLUGIN_DIR)
@mkdir -p $(ES_PACKAGE_DIR)
@GOOS=$(GOOS) GOARCH=$(GOARCH) go build -o $(PLUGIN_PATH) main.go
@cp $(PLUGIN_PATH) $(ES_PATH)
@echo "✅ Vault WASM module built successfully"
@echo "Plugin output: $(PLUGIN_PATH)"
@echo "ES package output: $(ES_PATH)"
@ls -lh $(PLUGIN_PATH) | awk '{print "Plugin size: " $$5}'
@ls -lh $(ES_PATH) | awk '{print "ES package size: " $$5}'
tidy:
@echo "Tidying vault build artifacts..."
@go mod tidy
@echo "✅ Tidy complete"
test:
@echo "Running vault tests..."
@go test -v ./...
@cd $(GIT_ROOT) && go test -C . -mod=readonly -v github.com/sonr-io/sonr/x/dwn/client/plugin/...
clean:
@echo "Cleaning vault build artifacts..."
@rm -f $(PLUGIN_PATH)
@rm -f $(ES_PATH)
@echo "✅ Clean complete"
release:
@echo "Creating vault release..."
@cd $(GIT_ROOT) && cz --config cmd/vault/.cz.toml --no-raise 6,21 bump --yes --increment PATCH
snapshot:
@echo "Dry-Run Bumping Vault version..."
@cd $(GIT_ROOT) && cz --config cmd/vault/.cz.toml bump --yes --dry-run --no-verify --increment PATCH
@echo "Creating vault snapshots for all platforms..."
@cd $(GIT_ROOT) && goreleaser release --snapshot --clean -f cmd/vault/.goreleaser.yml
version:
@echo "Vault WASM Module"
@echo "================="
@echo "Version: $(VERSION)"
@echo "Commit: $(COMMIT)"
@echo "Target OS: $(GOOS)"
@echo "Target Arch: $(GOARCH)"
@echo "Plugin output: $(PLUGIN_PATH)"
@echo "ES output: $(ES_PATH)"
verify: build
@echo "Verifying WASM modules..."
@if [ -f "$(PLUGIN_PATH)" ]; then \
file "$(PLUGIN_PATH)"; \
echo "✅ Plugin WASM file exists"; \
else \
echo "❌ Plugin WASM file not found"; \
exit 1; \
fi
@if [ -f "$(ES_PATH)" ]; then \
file "$(ES_PATH)"; \
echo "✅ ES package WASM file exists"; \
else \
echo "❌ ES package WASM file not found"; \
exit 1; \
fi
@echo "Plugin size: $$(du -h $(PLUGIN_PATH) | cut -f1)"
@echo "ES size: $$(du -h $(ES_PATH) | cut -f1)"
@echo "✅ Verification complete"
help:
@echo "Vault WASM Module Makefile"
@echo "=========================="
@echo ""
@echo "This Makefile builds the vault WebAssembly module for both the"
@echo "Sonr blockchain plugin system and the @sonr.io/es package."
@echo ""
@echo "Available targets:"
@echo " build - Build vault WASM module (default)"
@echo " clean - Remove built WASM artifacts"
@echo " test - Run vault tests"
@echo " tidy - Tidy Go module dependencies"
@echo " version - Display version information"
@echo " verify - Build and verify the WASM modules"
@echo " help - Show this help message"
@echo ""
@echo "Output locations:"
@echo " Plugin: $(PLUGIN_PATH)"
@echo " ES Package: $(ES_PATH)"
@echo ""
@echo "Integration:"
@echo " The WASM module is built for two purposes:"
@echo " 1. Plugin system in x/dwn/client/plugin"
@echo " 2. ES package for browser distribution via jsDelivr"
@echo ""
@echo "Examples:"
@echo " make build # Build WASM module to both locations"
@echo " make verify # Build and verify both outputs"
@echo " make clean # Remove all artifacts"
+477
View File
@@ -0,0 +1,477 @@
# Vault - WebAssembly Vault Plugin
Vault is a WebAssembly-based vault system for the Sonr blockchain that provides secure, isolated execution of cryptographic operations. Built using the Extism framework, Vault enables secure multi-party computation (MPC) and vault management within a sandboxed WebAssembly environment.
## Overview
Vault serves as a cryptographic vault system that:
- Provides secure enclave-based key generation and management
- Supports multi-chain transaction signing (Cosmos, EVM)
- Implements WebAuthn-based authentication
- Offers secure import/export functionality via IPFS
- Enables isolated execution through WebAssembly
## Architecture
### Core Components
- **MPC Enclave**: Multi-party computation system for secure key operations
- **Vault Management**: Create, unlock, and manage cryptographic vaults
- **IPFS Integration**: Secure backup and restore of encrypted vault data
- **WebAuthn Support**: Passwordless authentication for vault operations
- **Multi-Chain Support**: Transaction signing for different blockchain networks
### Build Configuration
Vault is built specifically for WebAssembly:
```go
//go:build js && wasm
// +build js,wasm
```
## API Reference
### Core Enclave Operations
#### `generate`
```go
//go:wasmexport generate
func generate() int32
```
Creates a new MPC enclave and returns the enclave data and public key.
**Input**: `GenerateRequest`
```json
{
"id": "string"
}
```
**Output**: `GenerateResponse`
```json
{
"data": "EnclaveData",
"public_key": "[]byte"
}
```
#### `refresh`
```go
//go:wasmexport refresh
func refresh() int32
```
Refreshes an existing enclave with new cryptographic material.
**Input**: `RefreshRequest`
```json
{
"enclave": "EnclaveData"
}
```
**Output**: `RefreshResponse`
```json
{
"okay": "bool",
"data": "EnclaveData"
}
```
#### `sign`
```go
//go:wasmexport sign
func sign() int32
```
Signs a message using the enclave's private key.
**Input**: `SignRequest`
```json
{
"message": "[]byte",
"enclave": "EnclaveData"
}
```
**Output**: `SignResponse`
```json
{
"signature": "[]byte"
}
```
#### `verify`
```go
//go:wasmexport verify
func verify() int32
```
Verifies a signature against a message and public key.
**Input**: `VerifyRequest`
```json
{
"public_key": "[]byte",
"message": "[]byte",
"signature": "[]byte"
}
```
**Output**: `VerifyResponse`
```json
{
"valid": "bool"
}
```
### Vault Import/Export Operations
#### `export`
```go
//go:wasmexport export
func export() int32
```
Encrypts and exports vault data to IPFS, returning a Content ID (CID).
**Input**: `ExportRequest`
```json
{
"enclave": "EnclaveData",
"password": "[]byte"
}
```
**Output**: `ExportResponse`
```json
{
"cid": "string",
"success": "bool"
}
```
#### `import`
```go
//go:wasmexport import
func importVault() int32
```
Retrieves and decrypts vault data from IPFS using a CID and password.
**Input**: `ImportRequest`
```json
{
"cid": "string",
"password": "[]byte"
}
```
**Output**: `ImportResponse`
```json
{
"enclave": "EnclaveData",
"success": "bool"
}
```
### Advanced Vault Operations
#### `create_vault_enclave`
```go
//go:wasmexport create_vault_enclave
func createVaultEnclave() int32
```
Creates a new vault enclave with advanced configuration options.
**Input**: `EnclaveConfig`
```json
{
"vault_id": "string",
"key_derivation_method": "string",
"encryption_algorithm": "string",
"signing_algorithm": "string",
"webauthn_enabled": "bool",
"auto_lock_timeout": "int64",
"key_rotation_interval": "int64",
"supported_chains": ["string"],
"max_concurrent_ops": "int",
"memory_limit": "uint64"
}
```
#### `unlock_vault_enclave`
```go
//go:wasmexport unlock_vault_enclave
func unlockVaultEnclave() int32
```
Unlocks a vault enclave, optionally using WebAuthn authentication.
#### `lock_vault_enclave`
```go
//go:wasmexport lock_vault_enclave
func lockVaultEnclave() int32
```
Locks a vault enclave to prevent unauthorized access.
#### `rotate_vault_key`
```go
//go:wasmexport rotate_vault_key
func rotateVaultKey() int32
```
Rotates the cryptographic keys within a vault enclave.
### Multi-Chain Transaction Signing
#### `sign_cosmos_transaction`
```go
//go:wasmexport sign_cosmos_transaction
func signCosmosTransaction() int32
```
Signs transactions for Cosmos SDK-based blockchains.
#### `sign_evm_transaction`
```go
//go:wasmexport sign_evm_transaction
func signEvmTransaction() int32
```
Signs transactions for Ethereum Virtual Machine compatible chains.
#### `sign_message`
```go
//go:wasmexport sign_message
func signMessage() int32
```
Signs arbitrary messages using the vault's private key.
### Health and Monitoring
#### `get_vault_health`
```go
//go:wasmexport get_vault_health
func getVaultHealth() int32
```
Returns the health status of a vault enclave.
**Output**: `EnclaveHealth`
```json
{
"vault_id": "string",
"status": "string",
"last_activity": "int64",
"key_rotation_due": "bool",
"attestation_valid": "bool"
}
```
## Configuration
### Environment Variables
Motor supports configuration through Extism variables:
- `chain_id`: Blockchain network identifier (default: "sonr-testnet-1")
- `password`: Default password for enclave operations (default: "password")
- `gateway`: IPFS gateway URL (default: "https://ipfs.did.run/ipfs/")
Access these via helper functions:
```go
func GetChainID() string
func GetPassword() []byte
func GetGateway() string
```
### IPFS Integration
Motor integrates with IPFS for secure vault backup and restore:
- **Storage Endpoint**: `http://127.0.0.1:5001/api/v0/add`
- **Retrieval Endpoint**: `http://127.0.0.1:5001/api/v0/cat`
- **Data Format**: Encrypted vault data stored as content-addressed objects
- **Security**: All vault data is encrypted before IPFS storage
## Security Features
### Enclave Isolation
- WebAssembly sandbox provides memory isolation
- Secure execution environment prevents side-channel attacks
- Attestation mechanisms ensure enclave integrity
### Authentication
- WebAuthn support for passwordless authentication
- Challenge-response authentication flows
- Automatic vault locking with configurable timeouts
### Key Management
- Multi-party computation for enhanced security
- Automatic key rotation with configurable intervals
- Secure key derivation and storage
### Data Protection
- AES encryption for sensitive data
- Password-based encryption for import/export
- Secure memory handling within WASM environment
## Usage Examples
### Basic Enclave Operations
```javascript
// Generate new enclave
const generateReq = { id: "my-vault" };
const result = call_wasm_function("generate", generateReq);
// Sign a message
const signReq = {
message: new Uint8Array([1, 2, 3, 4]),
enclave: result.data,
};
const signature = call_wasm_function("sign", signReq);
```
### Vault Management
```javascript
// Create vault with configuration
const config = {
vault_id: "user-vault-001",
webauthn_enabled: true,
auto_lock_timeout: 300,
supported_chains: ["cosmos", "ethereum"],
};
const vault = call_wasm_function("create_vault_enclave", config);
// Sign Cosmos transaction
const cosmosReq = {
vault_id: "user-vault-001",
chain_type: "cosmos",
chain_id: "cosmoshub-4",
message: transactionBytes,
};
const cosmosResult = call_wasm_function("sign_cosmos_transaction", cosmosReq);
```
### Import/Export Operations
```javascript
// Export vault to IPFS
const exportReq = {
enclave: vaultData,
password: new Uint8Array([
/* password bytes */
]),
};
const exportResult = call_wasm_function("export", exportReq);
console.log("Vault exported to CID:", exportResult.cid);
// Import vault from IPFS
const importReq = {
cid: "QmXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXxXx",
password: new Uint8Array([
/* password bytes */
]),
};
const importResult = call_wasm_function("import", importReq);
```
## Building and Deployment
### Prerequisites
- Go 1.24.4+
- Extism runtime
- IPFS node (for import/export functionality)
### Build Commands
```bash
# Build WebAssembly module
GOOS=js GOARCH=wasm go build -o motr.wasm main.go
# Build via Makefile
make motr
```
### Integration
Motor is designed to be integrated with:
- **Highway Service**: PostgreSQL-backed HTTP API
- **Sonr Blockchain**: Cosmos SDK-based blockchain node
- **IPFS Network**: Decentralized storage system
- **WebAuthn Infrastructure**: Passwordless authentication
## Error Handling
All functions return `int32` status codes:
- `0`: Success
- `1`: Error (details available via `pdk.SetError`)
Error information is logged using Extism's logging system:
```go
pdk.Log(pdk.LogError, "Error message")
pdk.Log(pdk.LogInfo, "Info message")
```
## Dependencies
### Core Dependencies
- `github.com/extism/go-pdk`: WebAssembly plugin development kit
- `github.com/sonr-io/sonr/crypto/mpc`: Multi-party computation library
### Cryptographic Libraries
- `filippo.io/edwards25519`: Edwards25519 elliptic curve
- `github.com/btcsuite/btcd/btcec/v2`: Bitcoin cryptography
- `github.com/consensys/gnark-crypto`: Zero-knowledge proof cryptography
## License
Motor is part of the Sonr blockchain project. See the project's main license for terms and conditions.
+26
View File
@@ -0,0 +1,26 @@
module vault
go 1.24.7
replace github.com/sonr-io/sonr/crypto => ../../crypto/
require (
github.com/extism/go-pdk v1.1.3
github.com/golang-jwt/jwt/v5 v5.3.0
github.com/sonr-io/sonr/crypto v0.0.0-00010101000000-000000000000
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/bits-and-blooms/bitset v1.24.0 // indirect
github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect
github.com/bwesterb/go-ristretto v1.2.3 // indirect
github.com/consensys/gnark-crypto v0.19.0 // indirect
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 // indirect
github.com/gtank/merlin v0.1.1 // indirect
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect
github.com/pkg/errors v0.9.1 // indirect
golang.org/x/crypto v0.42.0 // indirect
golang.org/x/sys v0.36.0 // indirect
)
+38
View File
@@ -0,0 +1,38 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/bits-and-blooms/bitset v1.24.0 h1:H4x4TuulnokZKvHLfzVRTHJfFfnHEeSYJizujEZvmAM=
github.com/bits-and-blooms/bitset v1.24.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ=
github.com/btcsuite/btcd/btcec/v2 v2.3.4/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/JMzngw=
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/consensys/gnark-crypto v0.19.0 h1:zXCqeY2txSaMl6G5wFpZzMWJU9HPNh8qxPnYJ1BL9vA=
github.com/consensys/gnark-crypto v0.19.0/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM=
github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8=
github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ=
github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4=
github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=
github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is=
github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s=
github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4=
github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0=
github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
+483
View File
@@ -0,0 +1,483 @@
//go:build js && wasm
// +build js,wasm
package main
import (
"crypto/sha256"
"encoding/json"
"fmt"
"time"
"github.com/extism/go-pdk"
"github.com/golang-jwt/jwt/v5"
"github.com/sonr-io/sonr/crypto/mpc"
)
const (
KeyChainID = "chain_id"
KeyEnclave = "enclave"
KeyVaultConfig = "vault_config"
)
// GetChainID returns the chain ID to use for unlocking the enclave
func GetChainID() string {
v := pdk.GetVar(KeyChainID)
if v == nil {
return "sonr-testnet-1"
}
return string(v)
}
// GetEnclaveData loads MPC enclave data from PDK environment
func GetEnclaveData() (*mpc.EnclaveData, error) {
v := pdk.GetVar(KeyEnclave)
if v == nil {
return nil, fmt.Errorf("enclave data not provided in environment")
}
var data mpc.EnclaveData
if err := json.Unmarshal(v, &data); err != nil {
return nil, fmt.Errorf("failed to unmarshal enclave data: %w", err)
}
return &data, nil
}
// GetVaultConfig loads vault configuration from PDK environment
func GetVaultConfig() map[string]any {
v := pdk.GetVar(KeyVaultConfig)
if v == nil {
return make(map[string]any)
}
var config map[string]any
if err := json.Unmarshal(v, &config); err != nil {
pdk.Log(pdk.LogWarn, fmt.Sprintf("Failed to parse vault config: %v", err))
return make(map[string]any)
}
return config
}
// UCAN Token Request/Response types
type NewOriginTokenRequest struct {
AudienceDID string `json:"audience_did"`
Attenuations []map[string]any `json:"attenuations,omitempty"`
Facts []string `json:"facts,omitempty"`
NotBefore int64 `json:"not_before,omitempty"`
ExpiresAt int64 `json:"expires_at,omitempty"`
}
type NewAttenuatedTokenRequest struct {
ParentToken string `json:"parent_token"`
AudienceDID string `json:"audience_did"`
Attenuations []map[string]any `json:"attenuations,omitempty"`
Facts []string `json:"facts,omitempty"`
NotBefore int64 `json:"not_before,omitempty"`
ExpiresAt int64 `json:"expires_at,omitempty"`
}
type UCANTokenResponse struct {
Token string `json:"token"`
Issuer string `json:"issuer"`
Address string `json:"address"`
Error string `json:"error,omitempty"`
}
type SignDataRequest struct {
Data []byte `json:"data"`
}
type SignDataResponse struct {
Signature []byte `json:"signature"`
Error string `json:"error,omitempty"`
}
type VerifyDataRequest struct {
Data []byte `json:"data"`
Signature []byte `json:"signature"`
}
type VerifyDataResponse struct {
Valid bool `json:"valid"`
Error string `json:"error,omitempty"`
}
type GetIssuerDIDResponse struct {
IssuerDID string `json:"issuer_did"`
Address string `json:"address"`
ChainCode string `json:"chain_code"`
Error string `json:"error,omitempty"`
}
var (
enclave mpc.Enclave
issuerDID string
address string
)
func main() {
// Initialize MPC enclave from PDK environment
if err := initializeEnclave(); err != nil {
pdk.SetError(fmt.Errorf("failed to initialize enclave: %w", err))
return
}
pdk.Log(pdk.LogInfo, "Motor plugin initialized as MPC-based UCAN source")
}
// initializeEnclave initializes the MPC enclave from PDK environment
func initializeEnclave() error {
// Load enclave data from PDK environment
enclaveData, err := GetEnclaveData()
if err != nil {
return fmt.Errorf("failed to get enclave data: %w", err)
}
// Import MPC enclave from data
enclave, err = mpc.ImportEnclave(mpc.WithEnclaveData(enclaveData))
if err != nil {
return fmt.Errorf("failed to import enclave: %w", err)
}
// Derive issuer DID and address from enclave public key
pubKeyBytes := enclave.PubKeyBytes()
issuerDID, address, err = deriveIssuerDIDFromBytes(pubKeyBytes)
if err != nil {
return fmt.Errorf("failed to derive issuer DID: %w", err)
}
return nil
}
//go:wasmexport new_origin_token
func newOriginToken() int32 {
if !enclave.IsValid() {
pdk.SetError(fmt.Errorf("enclave not initialized"))
return 1
}
req := &NewOriginTokenRequest{}
err := pdk.InputJSON(req)
if err != nil {
pdk.SetError(fmt.Errorf("failed to parse request: %w", err))
return 1
}
// Convert timestamps
var notBefore, expiresAt time.Time
if req.NotBefore > 0 {
notBefore = time.Unix(req.NotBefore, 0)
}
if req.ExpiresAt > 0 {
expiresAt = time.Unix(req.ExpiresAt, 0)
}
// Create origin token using MPC signing
tokenString, err := createUCANToken(
req.AudienceDID,
nil,
req.Attenuations,
req.Facts,
notBefore,
expiresAt,
)
if err != nil {
resp := &UCANTokenResponse{Error: err.Error()}
pdk.OutputJSON(resp)
return 1
}
resp := &UCANTokenResponse{
Token: tokenString,
Issuer: issuerDID,
Address: address,
}
pdk.OutputJSON(resp)
return 0
}
//go:wasmexport new_attenuated_token
func newAttenuatedToken() int32 {
if !enclave.IsValid() {
pdk.SetError(fmt.Errorf("enclave not initialized"))
return 1
}
req := &NewAttenuatedTokenRequest{}
err := pdk.InputJSON(req)
if err != nil {
pdk.SetError(fmt.Errorf("failed to parse request: %w", err))
return 1
}
// Convert timestamps
var notBefore, expiresAt time.Time
if req.NotBefore > 0 {
notBefore = time.Unix(req.NotBefore, 0)
}
if req.ExpiresAt > 0 {
expiresAt = time.Unix(req.ExpiresAt, 0)
}
// Create proofs from parent token
proofs := []string{req.ParentToken}
// Create attenuated token using MPC signing
tokenString, err := createUCANToken(
req.AudienceDID,
proofs,
req.Attenuations,
req.Facts,
notBefore,
expiresAt,
)
if err != nil {
resp := &UCANTokenResponse{Error: err.Error()}
pdk.OutputJSON(resp)
return 1
}
resp := &UCANTokenResponse{
Token: tokenString,
Issuer: issuerDID,
Address: address,
}
pdk.OutputJSON(resp)
return 0
}
//go:wasmexport sign_data
func signData() int32 {
if !enclave.IsValid() {
pdk.SetError(fmt.Errorf("enclave not initialized"))
return 1
}
req := &SignDataRequest{}
err := pdk.InputJSON(req)
if err != nil {
pdk.SetError(fmt.Errorf("failed to parse request: %w", err))
return 1
}
// Sign data using MPC enclave
signature, err := enclave.Sign(req.Data)
if err != nil {
resp := &SignDataResponse{Error: err.Error()}
pdk.OutputJSON(resp)
return 1
}
resp := &SignDataResponse{Signature: signature}
pdk.OutputJSON(resp)
return 0
}
//go:wasmexport verify_data
func verifyData() int32 {
if !enclave.IsValid() {
pdk.SetError(fmt.Errorf("enclave not initialized"))
return 1
}
req := &VerifyDataRequest{}
err := pdk.InputJSON(req)
if err != nil {
pdk.SetError(fmt.Errorf("failed to parse request: %w", err))
return 1
}
// Verify data using MPC enclave
valid, err := enclave.Verify(req.Data, req.Signature)
if err != nil {
resp := &VerifyDataResponse{Error: err.Error()}
pdk.OutputJSON(resp)
return 1
}
resp := &VerifyDataResponse{Valid: valid}
pdk.OutputJSON(resp)
return 0
}
//go:wasmexport get_issuer_did
func getIssuerDID() int32 {
if !enclave.IsValid() {
pdk.SetError(fmt.Errorf("enclave not initialized"))
return 1
}
// Get chain code for deterministic derivation
chainCode, err := getChainCode()
if err != nil {
resp := &GetIssuerDIDResponse{Error: err.Error()}
pdk.OutputJSON(resp)
return 1
}
resp := &GetIssuerDIDResponse{
IssuerDID: issuerDID,
Address: address,
ChainCode: fmt.Sprintf("%x", chainCode),
}
pdk.OutputJSON(resp)
return 0
}
// UCAN token creation and MPC signing implementation
// MPCSigningMethod implements JWT signing using MPC enclaves
type MPCSigningMethod struct {
Name string
enclave mpc.Enclave
}
// Alg returns the signing method algorithm name
func (m *MPCSigningMethod) Alg() string {
return m.Name
}
// Sign signs a JWT string using the MPC enclave
func (m *MPCSigningMethod) Sign(signingString string, key any) ([]byte, error) {
// Hash the signing string
hasher := sha256.New()
hasher.Write([]byte(signingString))
digest := hasher.Sum(nil)
// Use MPC enclave to sign the digest
sig, err := m.enclave.Sign(digest)
if err != nil {
return nil, fmt.Errorf("failed to sign with MPC: %w", err)
}
return sig, nil
}
// Verify verifies a JWT signature using the MPC enclave
func (m *MPCSigningMethod) Verify(signingString string, sig []byte, key any) error {
// Hash the signing string
hasher := sha256.New()
hasher.Write([]byte(signingString))
digest := hasher.Sum(nil)
// Use MPC enclave to verify signature
valid, err := m.enclave.Verify(digest, sig)
if err != nil {
return fmt.Errorf("failed to verify signature: %w", err)
}
if !valid {
return fmt.Errorf("signature verification failed")
}
return nil
}
// createUCANToken creates a UCAN token using MPC signing
func createUCANToken(
audienceDID string,
proofs []string,
attenuations []map[string]any,
facts []string,
notBefore, expiresAt time.Time,
) (string, error) {
// Validate audience DID
if audienceDID == "" {
return "", fmt.Errorf("audience DID is required")
}
// Create MPC signing method
signingMethod := &MPCSigningMethod{
Name: "MPC256",
enclave: enclave,
}
// Create JWT token
token := jwt.New(signingMethod)
// Set UCAN version in header
token.Header["ucv"] = "0.9.0"
// Prepare time claims
var nbfUnix, expUnix int64
if !notBefore.IsZero() {
nbfUnix = notBefore.Unix()
}
if !expiresAt.IsZero() {
expUnix = expiresAt.Unix()
}
// Set claims
claims := jwt.MapClaims{
"iss": issuerDID,
"aud": audienceDID,
}
// Add attenuations if provided
if len(attenuations) > 0 {
claims["att"] = attenuations
}
// Add proofs if provided
if len(proofs) > 0 {
claims["prf"] = proofs
}
// Add facts if provided
if len(facts) > 0 {
claims["fct"] = facts
}
// Add time claims
if nbfUnix > 0 {
claims["nbf"] = nbfUnix
}
if expUnix > 0 {
claims["exp"] = expUnix
}
token.Claims = claims
// Sign the token using MPC enclave (key parameter is ignored for MPC signing)
tokenString, err := token.SignedString(nil)
if err != nil {
return "", fmt.Errorf("failed to sign token with MPC: %w", err)
}
return tokenString, nil
}
// deriveIssuerDIDFromBytes creates issuer DID and address from public key bytes
func deriveIssuerDIDFromBytes(pubKeyBytes []byte) (string, string, error) {
if len(pubKeyBytes) == 0 {
return "", "", fmt.Errorf("empty public key bytes")
}
// Generate address from public key (simplified implementation)
address := fmt.Sprintf("sonr1%x", pubKeyBytes[:20])
// Create DID from address (simplified implementation)
issuerDID := fmt.Sprintf("did:sonr:%s", address)
return issuerDID, address, nil
}
// getChainCode derives a deterministic chain code from the enclave
func getChainCode() ([]byte, error) {
if !enclave.IsValid() {
return nil, fmt.Errorf("enclave is not valid")
}
// Sign the address to create a deterministic chain code
sig, err := enclave.Sign([]byte(address))
if err != nil {
return nil, fmt.Errorf("failed to sign address for chain code: %w", err)
}
// Hash the signature to create a 32-byte chain code
hasher := sha256.New()
hasher.Write(sig)
hash := hasher.Sum(nil)
return hash[:32], nil
}
+4
View File
@@ -0,0 +1,4 @@
package main
// Version is set by commitizen during release process
var Version = "dev"