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>
302 lines
9.4 KiB
Go
302 lines
9.4 KiB
Go
package keeper
|
|
|
|
import (
|
|
"context"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"slices"
|
|
"time"
|
|
|
|
"cosmossdk.io/collections"
|
|
sdk "github.com/cosmos/cosmos-sdk/types"
|
|
webauthn "github.com/sonr-io/common/webauthn"
|
|
"github.com/sonr-io/common/webauthn/webauthncbor"
|
|
"github.com/sonr-io/sonr/x/did/types"
|
|
)
|
|
|
|
// WebAuthnRegistrationData represents the data from a WebAuthn registration ceremony
|
|
type WebAuthnRegistrationData struct {
|
|
CredentialID string
|
|
RawID string
|
|
ClientDataJSON string
|
|
AttestationObject string
|
|
Username string
|
|
PublicKey []byte
|
|
Algorithm int32
|
|
Origin string
|
|
}
|
|
|
|
// ProcessWebAuthnRegistration processes a WebAuthn credential and creates a DID document
|
|
func (k Keeper) ProcessWebAuthnRegistration(
|
|
ctx context.Context,
|
|
regData *WebAuthnRegistrationData,
|
|
) (*types.DIDDocument, error) {
|
|
sdkCtx := sdk.UnwrapSDKContext(ctx)
|
|
|
|
// Generate a new DID
|
|
did := k.generateDID(regData.Username)
|
|
|
|
// Create WebAuthn credential with full attestation data
|
|
webAuthnCredential := &types.WebAuthnCredential{
|
|
CredentialId: regData.CredentialID,
|
|
RawId: regData.RawID,
|
|
ClientDataJson: regData.ClientDataJSON,
|
|
AttestationObject: regData.AttestationObject,
|
|
PublicKey: regData.PublicKey,
|
|
Algorithm: regData.Algorithm,
|
|
AttestationType: "none", // For most platform authenticators
|
|
Origin: regData.Origin,
|
|
CreatedAt: sdkCtx.BlockTime().Unix(),
|
|
}
|
|
|
|
// Validate the WebAuthn credential using local types validation
|
|
if err := types.ValidateStructure(webAuthnCredential); err != nil {
|
|
return nil, fmt.Errorf("WebAuthn credential validation failed: %w", err)
|
|
}
|
|
|
|
// Check for credential uniqueness to prevent replay attacks
|
|
if k.HasExistingCredential(sdkCtx, regData.CredentialID) {
|
|
return nil, fmt.Errorf("WebAuthn credential already exists: %s", regData.CredentialID)
|
|
}
|
|
|
|
// Create verification method with WebAuthn credential
|
|
verificationMethod := &types.VerificationMethod{
|
|
Id: fmt.Sprintf("%s#webauthn-1", did),
|
|
Controller: did,
|
|
VerificationMethodKind: "WebAuthnCredential2024",
|
|
WebauthnCredential: webAuthnCredential,
|
|
}
|
|
|
|
// Create verification method references
|
|
authRef := &types.VerificationMethodReference{
|
|
VerificationMethodId: verificationMethod.Id,
|
|
}
|
|
assertRef := &types.VerificationMethodReference{
|
|
VerificationMethodId: verificationMethod.Id,
|
|
}
|
|
capInvRef := &types.VerificationMethodReference{
|
|
VerificationMethodId: verificationMethod.Id,
|
|
}
|
|
|
|
// Create DID document
|
|
didDoc := &types.DIDDocument{
|
|
Id: did,
|
|
PrimaryController: "", // Will be set to the cosmos address later
|
|
VerificationMethod: []*types.VerificationMethod{
|
|
verificationMethod,
|
|
},
|
|
Authentication: []*types.VerificationMethodReference{
|
|
authRef,
|
|
},
|
|
AssertionMethod: []*types.VerificationMethodReference{
|
|
assertRef,
|
|
},
|
|
KeyAgreement: []*types.VerificationMethodReference{},
|
|
CapabilityInvocation: []*types.VerificationMethodReference{
|
|
capInvRef,
|
|
},
|
|
CapabilityDelegation: []*types.VerificationMethodReference{},
|
|
Service: []*types.Service{},
|
|
}
|
|
|
|
// Store the DID document
|
|
if err := k.storeDIDDocument(ctx, didDoc); err != nil {
|
|
return nil, fmt.Errorf("failed to store DID document: %w", err)
|
|
}
|
|
|
|
return didDoc, nil
|
|
}
|
|
|
|
// CreateWebAuthnChallenge creates a challenge for WebAuthn registration
|
|
func (k Keeper) CreateWebAuthnChallenge(ctx context.Context, username string) (string, error) {
|
|
// Generate cryptographically secure challenge
|
|
challengeBytes := make([]byte, 32)
|
|
if _, err := rand.Read(challengeBytes); err != nil {
|
|
return "", fmt.Errorf("failed to generate random challenge: %w", err)
|
|
}
|
|
|
|
challenge := base64.URLEncoding.EncodeToString(challengeBytes)
|
|
|
|
// Store challenge with expiration (in production, use proper session storage)
|
|
// For now, we'll rely on the server-side session management
|
|
|
|
return challenge, nil
|
|
}
|
|
|
|
// VerifyWebAuthnRegistration verifies a WebAuthn registration response
|
|
func (k Keeper) VerifyWebAuthnRegistration(
|
|
ctx context.Context,
|
|
regData *WebAuthnRegistrationData,
|
|
challenge string,
|
|
) error {
|
|
// Decode and verify client data
|
|
clientDataBytes, err := base64.URLEncoding.DecodeString(regData.ClientDataJSON)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decode client data JSON: %w", err)
|
|
}
|
|
|
|
var clientData struct {
|
|
Type string `json:"type"`
|
|
Challenge string `json:"challenge"`
|
|
Origin string `json:"origin"`
|
|
}
|
|
|
|
if err := json.Unmarshal(clientDataBytes, &clientData); err != nil {
|
|
return fmt.Errorf("failed to parse client data: %w", err)
|
|
}
|
|
|
|
// Verify type
|
|
if clientData.Type != "webauthn.create" {
|
|
return fmt.Errorf("invalid client data type: %s", clientData.Type)
|
|
}
|
|
|
|
// Verify challenge
|
|
if clientData.Challenge != challenge {
|
|
return fmt.Errorf("challenge mismatch")
|
|
}
|
|
|
|
// Verify origin (should be localhost for CLI usage)
|
|
if clientData.Origin != "http://localhost" &&
|
|
!k.isValidLocalhost(clientData.Origin) {
|
|
return fmt.Errorf("invalid origin: %s", clientData.Origin)
|
|
}
|
|
|
|
// Parse attestation object and extract public key using CBOR
|
|
publicKey, algorithm, err := k.extractPublicKeyFromAttestation(regData.AttestationObject)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to extract public key: %w", err)
|
|
}
|
|
|
|
// Update registration data with extracted information
|
|
regData.PublicKey = publicKey
|
|
regData.Algorithm = algorithm
|
|
regData.Origin = clientData.Origin
|
|
|
|
return nil
|
|
}
|
|
|
|
// generateDID generates a new DID identifier
|
|
func (k Keeper) generateDID(username string) string {
|
|
// For now, generate a simple DID based on username and timestamp
|
|
// In production, this should be more sophisticated
|
|
return fmt.Sprintf("did:sonr:%s-%d", username, time.Now().Unix())
|
|
}
|
|
|
|
// storeDIDDocument stores a DID document in the state
|
|
func (k Keeper) storeDIDDocument(ctx context.Context, didDoc *types.DIDDocument) error {
|
|
// Convert to ORM format and store
|
|
ormDoc := didDoc.ToORM()
|
|
|
|
// Store in the ORM database
|
|
if err := k.OrmDB.DIDDocumentTable().Insert(ctx, ormDoc); err != nil {
|
|
return fmt.Errorf("failed to insert DID document: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// isValidLocalhost checks if the origin is a valid localhost URL
|
|
func (k Keeper) isValidLocalhost(origin string) bool {
|
|
validOrigins := []string{
|
|
"http://localhost:8080",
|
|
"http://localhost:8081",
|
|
"http://localhost:8082",
|
|
"http://localhost:8083",
|
|
"http://localhost:8084",
|
|
"http://localhost:8085",
|
|
"http://localhost:8086",
|
|
"http://localhost:8087",
|
|
"http://localhost:8088",
|
|
"http://localhost:8089",
|
|
}
|
|
|
|
return slices.Contains(validOrigins, origin)
|
|
}
|
|
|
|
// extractPublicKeyFromAttestation extracts the public key from WebAuthn attestation object
|
|
// Now leverages the full WebAuthn protocol implementation for proper CBOR parsing
|
|
func (k Keeper) extractPublicKeyFromAttestation(attestationObject string) ([]byte, int32, error) {
|
|
// Use the local types validation to extract public key
|
|
if err := types.ValidateAttestationObjectFormat(attestationObject); err != nil {
|
|
return nil, 0, fmt.Errorf("invalid attestation object format: %w", err)
|
|
}
|
|
|
|
// Decode the attestation object using the full WebAuthn protocol
|
|
attestationBytes, err := base64.RawURLEncoding.DecodeString(attestationObject)
|
|
if err != nil {
|
|
return nil, 0, fmt.Errorf("failed to decode attestation object: %w", err)
|
|
}
|
|
|
|
// Parse the attestation object using CBOR
|
|
var attestationObj webauthn.AttestationObject
|
|
if err := webauthncbor.Unmarshal(attestationBytes, &attestationObj); err != nil {
|
|
return nil, 0, fmt.Errorf("failed to unmarshal attestation object: %w", err)
|
|
}
|
|
|
|
// Unmarshal the authenticator data
|
|
if err := attestationObj.AuthData.Unmarshal(attestationObj.RawAuthData); err != nil {
|
|
return nil, 0, fmt.Errorf("failed to unmarshal authenticator data: %w", err)
|
|
}
|
|
|
|
// Extract the attested credential data
|
|
if !attestationObj.AuthData.Flags.HasAttestedCredentialData() {
|
|
return nil, 0, fmt.Errorf("attestation object missing attested credential data")
|
|
}
|
|
|
|
publicKey := attestationObj.AuthData.AttData.CredentialPublicKey
|
|
if len(publicKey) == 0 {
|
|
return nil, 0, fmt.Errorf("no public key found in attested credential data")
|
|
}
|
|
|
|
// For now, assume ES256 algorithm. In the future, this could be extracted
|
|
// from the COSE key format in the public key bytes
|
|
algorithm := int32(-7) // ES256
|
|
|
|
return publicKey, algorithm, nil
|
|
}
|
|
|
|
// GetWebAuthnCredentialsByDID retrieves all WebAuthn credentials for a DID
|
|
func (k Keeper) GetWebAuthnCredentialsByDID(
|
|
ctx context.Context,
|
|
did string,
|
|
) ([]*types.WebAuthnCredential, error) {
|
|
// Get DID document
|
|
ormDoc, err := k.OrmDB.DIDDocumentTable().Get(ctx, did)
|
|
if err != nil {
|
|
if err == collections.ErrNotFound {
|
|
return nil, fmt.Errorf("DID document not found: %s", did)
|
|
}
|
|
return nil, fmt.Errorf("failed to get DID document: %w", err)
|
|
}
|
|
|
|
didDoc := types.DIDDocumentFromORM(ormDoc)
|
|
|
|
var credentials []*types.WebAuthnCredential
|
|
for _, vm := range didDoc.VerificationMethod {
|
|
if vm.WebauthnCredential != nil {
|
|
credentials = append(credentials, vm.WebauthnCredential)
|
|
}
|
|
}
|
|
|
|
return credentials, nil
|
|
}
|
|
|
|
// ValidateWebAuthnCredential validates a WebAuthn credential exists and is valid
|
|
func (k Keeper) ValidateWebAuthnCredential(ctx context.Context, did, credentialID string) error {
|
|
credentials, err := k.GetWebAuthnCredentialsByDID(ctx, did)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for _, cred := range credentials {
|
|
if cred.CredentialId == credentialID {
|
|
// Credential found and valid
|
|
return nil
|
|
}
|
|
}
|
|
|
|
return fmt.Errorf("WebAuthn credential %s not found for DID %s", credentialID, did)
|
|
}
|