mirror of
https://github.com/sonr-io/sonr.git
synced 2026-08-02 17:31:39 +00:00
feature/simplify ucan mpc did (#1195)
* feat: enable DID auth middleware * feat: implement passkey creation flow * feat: persist user address in cookie and retrieve user profile using address cookie * feat: implement human verification challenge during session initialization * refactor: remove unnecessary random number generation in profile creation * refactor: rename credential validation handler and update related routes * feat: improve profile validation and user experience * feat: add page rendering for profile and passkey creation * refactor: remove unused register handler and update routes * refactor: remove unused imports and simplify credential validation * fix: Correct insecure gRPC client connection * refactor: rename models files for better organization * refactor: refactor grpc client creation and management * refactor: refactor common clients package * <no value> * feat: add CapAccount, CapInterchain, CapVault enums * feat: add ChainId to ResAccount and ResInterchain * feat: add asset code to resource account enumeration * refactor: rename services package to providers * feat: implement gateway database interactions * refactor: move gateway repository to internal/gateway * refactor: Migrate database provider to use sqlx * refactor: Rename Vaults to VaultProvider in HTTPContext struct * refactor: Migrate from GORM to sqlc Queries in database context methods * refactor: Replace GORM with standard SQL and simplify database initialization * refactor: Migrate session management from GORM to sqlc with type conversion * refactor: Update import paths and model references in context package * fix: Resolve session type conversion and middleware issues * refactor: Migrate database from GORM to sqlx * refactor: Move models to pkg/common, improve code structure * refactor: move repository package to internal directory * refactor: move gateway internal packages to context directory * refactor: migrate database provider to use sqlx queries * feat: add session ID to HTTP context and use it to load session data * feat: implement vault creation API endpoint * feat: add DIDKey generation from PubKey * refactor: remove unused DIDAuth components * refactor: move DID auth controller to vault context * chore: remove unused DIDAuth package * refactor: improve clarity of enclave refresh function * feat: implement nonce-based key encryption for improved security * feat: Add Export and Import methods with comprehensive tests for Enclave * fix: Validate AES key length in keyshare encryption and decryption * fix: Resolve key length validation by hashing input keys * refactor: Update keyshare import to use protocol decoding * feat: Refactor enclave encryption to support full enclave export/import * refactor: Simplify Enclave interface methods by removing role parameter * refactor: remove unnecessary serialization from enclave interface * refactor: rename models package in gateway context * refactor: rename keystore vault constants * refactor: remove context parameter from Resolver methods * feat: add CurrentBlock context function and update related components * refactor: rename resolver.go to resolvers.go * feat: Add SQLite random() generation for session and profile initialization * refactor: Update SQL queries to use SQLite-style parameter placeholders * refactor: Replace '?' placeholders with '$n' PostgreSQL parameter syntax * <no value> * refactor: refactor gateway to use middleware for database interactions and improve modularity * feat: implement gateway for Sonr highway * refactor: Remove unused gateway context and refactor cookie/header handling * refactor: improve server initialization and middleware handling * feat: implement human verification for profile creation * feat: implement session management middleware * refactor: refactor common models and config to internal package * refactor: move env config to internal/config * refactor: move database-related code to directory * refactor: move IPFS client to common package and improve code structure * refactor: move querier to common package and rename to chain_query * refactor: move webworker model to internal/models * feat: add initial view template for Sonr.ID * docs(concepts): Add documentation for cosmos-proto * docs: move IBC transfer documentation to tools section * refactor: rename initpkl.go to pkl_init.go for better naming consistency * docs(theme): update dark mode toggle icons * refactor: update sqlite3 driver to ncruces/go-sqlite3 * feat: add Vault model and database interactions * refactor: Improve SQLite schema with better constraints and indexes * chore: update project dependencies * fix: use grpc.WithInsecure() for gRPC connection * config: set localhost as default Sonr gRPC URL * refactor: improve gateway middleware and refactor server initialization * refactor: Remove foreign key pragma from schema SQL * refactor: Remove foreign key constraints from database schema * refactor: Convert primary key columns from INTEGER to TEXT * refactor: Remove unnecessary redirect in error handling
This commit is contained in:
@@ -38,6 +38,11 @@ func NewDID(pub crypto.PubKey) (DID, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// NewFromPubKey constructs an Identifier from a public key
|
||||
func NewFromPubKey(pub PubKey) DID {
|
||||
return DID{PubKey: pub}
|
||||
}
|
||||
|
||||
// MulticodecType indicates the type for this multicodec
|
||||
func (id DID) MulticodecType() uint64 {
|
||||
switch id.Type() {
|
||||
|
||||
+27
-3
@@ -1,16 +1,21 @@
|
||||
package keys
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
|
||||
p2pcrypto "github.com/libp2p/go-libp2p/core/crypto"
|
||||
p2ppb "github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||
"github.com/onsonr/sonr/crypto/core/curves"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
type PubKey interface {
|
||||
Bytes() []byte
|
||||
Type() string
|
||||
Raw() ([]byte, error)
|
||||
Equals(b p2pcrypto.Key) bool
|
||||
Type() p2ppb.KeyType
|
||||
Hex() string
|
||||
Verify(msg []byte, sig []byte) (bool, error)
|
||||
}
|
||||
@@ -30,12 +35,31 @@ func (p pubKey) Bytes() []byte {
|
||||
return p.publicPoint.ToAffineCompressed()
|
||||
}
|
||||
|
||||
func (p pubKey) Raw() ([]byte, error) {
|
||||
return p.publicPoint.ToAffineCompressed(), nil
|
||||
}
|
||||
|
||||
func (p pubKey) Equals(b p2pcrypto.Key) bool {
|
||||
if b == nil {
|
||||
return false
|
||||
}
|
||||
apbz, err := b.Raw()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
bbz, err := p.Raw()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return bytes.Equal(apbz, bbz)
|
||||
}
|
||||
|
||||
func (p pubKey) Hex() string {
|
||||
return hex.EncodeToString(p.publicPoint.ToAffineCompressed())
|
||||
}
|
||||
|
||||
func (p pubKey) Type() string {
|
||||
return "secp256k1"
|
||||
func (p pubKey) Type() p2ppb.KeyType {
|
||||
return p2ppb.KeyType_Secp256k1
|
||||
}
|
||||
|
||||
func (p pubKey) Verify(data []byte, sigBz []byte) (bool, error) {
|
||||
|
||||
@@ -3,6 +3,7 @@ package mpc
|
||||
import (
|
||||
"github.com/onsonr/sonr/crypto/core/curves"
|
||||
"github.com/onsonr/sonr/crypto/core/protocol"
|
||||
"github.com/onsonr/sonr/crypto/keys"
|
||||
"github.com/onsonr/sonr/crypto/tecdsa/dklsv1/dkg"
|
||||
)
|
||||
|
||||
@@ -20,3 +21,22 @@ type (
|
||||
RefreshFunc interface{ protocol.Iterator } // RefreshFunc is the type for the refresh function
|
||||
SignFunc interface{ protocol.Iterator } // SignFunc is the type for the sign function
|
||||
)
|
||||
|
||||
const (
|
||||
RoleVal = "validator"
|
||||
RoleUser = "user"
|
||||
)
|
||||
|
||||
// Enclave defines the interface for key management operations
|
||||
type Enclave interface {
|
||||
Address() string // Address returns the Sonr address of the keyEnclave
|
||||
DID() keys.DID // DID returns the DID of the keyEnclave
|
||||
Export(key []byte) ([]byte, error) // Export returns encrypted enclave data
|
||||
Import(data []byte, key []byte) error // Import decrypts and loads enclave data
|
||||
IsValid() bool // IsValid returns true if the keyEnclave is valid
|
||||
PubKey() keys.PubKey // PubKey returns the public key of the keyEnclave
|
||||
Refresh() (Enclave, error) // Refresh returns a new keyEnclave
|
||||
Serialize() ([]byte, error) // Serialize returns the serialized keyEnclave
|
||||
Sign(data []byte) ([]byte, error) // Sign returns the signature of the data
|
||||
Verify(data []byte, sig []byte) (bool, error) // Verify returns true if the signature is valid
|
||||
}
|
||||
|
||||
+69
-14
@@ -1,6 +1,7 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@@ -8,22 +9,74 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func randNonce() []byte {
|
||||
nonce := make([]byte, 12)
|
||||
rand.Read(nonce)
|
||||
return nonce
|
||||
}
|
||||
|
||||
func TestKeyShareGeneration(t *testing.T) {
|
||||
t.Run("Generate Valid Enclave", func(t *testing.T) {
|
||||
nonce := randNonce()
|
||||
// Generate enclave
|
||||
enclave, err := GenEnclave()
|
||||
enclave, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, enclave)
|
||||
|
||||
// Validate enclave contents
|
||||
assert.True(t, enclave.IsValid())
|
||||
})
|
||||
|
||||
t.Run("Export and Import", func(t *testing.T) {
|
||||
nonce := randNonce()
|
||||
// Generate original enclave
|
||||
original, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test key for encryption/decryption (32 bytes)
|
||||
testKey := []byte("test-key-12345678-test-key-123456")
|
||||
|
||||
// Test Export/Import
|
||||
t.Run("Full Enclave", func(t *testing.T) {
|
||||
// Export enclave
|
||||
data, err := original.Export(testKey)
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, data)
|
||||
|
||||
// Create new empty enclave
|
||||
newEnclave, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Import enclave
|
||||
err = newEnclave.Import(data, testKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify the imported enclave works by signing
|
||||
testData := []byte("test message")
|
||||
sig, err := newEnclave.Sign(testData)
|
||||
require.NoError(t, err)
|
||||
valid, err := newEnclave.Verify(testData, sig)
|
||||
require.NoError(t, err)
|
||||
assert.True(t, valid)
|
||||
})
|
||||
|
||||
// Test Invalid Key
|
||||
t.Run("Invalid Key", func(t *testing.T) {
|
||||
data, err := original.Export(testKey)
|
||||
require.NoError(t, err)
|
||||
|
||||
wrongKey := []byte("wrong-key-12345678")
|
||||
err = original.Import(data, wrongKey)
|
||||
assert.Error(t, err)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestEnclaveOperations(t *testing.T) {
|
||||
t.Run("Signing and Verification", func(t *testing.T) {
|
||||
nonce := randNonce()
|
||||
// Generate valid enclave
|
||||
enclave, err := GenEnclave()
|
||||
enclave, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test signing
|
||||
@@ -45,7 +98,8 @@ func TestEnclaveOperations(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Address and Public Key", func(t *testing.T) {
|
||||
enclave, err := GenEnclave()
|
||||
nonce := randNonce()
|
||||
enclave, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test Address
|
||||
@@ -60,17 +114,18 @@ func TestEnclaveOperations(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("Refresh Operation", func(t *testing.T) {
|
||||
enclave, err := GenEnclave()
|
||||
nonce := randNonce()
|
||||
enclave, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Test refresh
|
||||
refreshedEnclave, err := enclave.Refresh()
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, refreshedEnclave)
|
||||
|
||||
|
||||
// Verify refreshed enclave is valid
|
||||
assert.True(t, refreshedEnclave.IsValid())
|
||||
|
||||
|
||||
// Verify it maintains the same address
|
||||
assert.Equal(t, enclave.Address(), refreshedEnclave.Address())
|
||||
})
|
||||
@@ -78,28 +133,28 @@ func TestEnclaveOperations(t *testing.T) {
|
||||
|
||||
func TestEnclaveSerialization(t *testing.T) {
|
||||
t.Run("Marshal and Unmarshal", func(t *testing.T) {
|
||||
nonce := randNonce()
|
||||
// Generate original enclave
|
||||
original, err := GenEnclave()
|
||||
original, err := GenEnclave(nonce)
|
||||
require.NoError(t, err)
|
||||
require.NotNil(t, original)
|
||||
|
||||
// Marshal
|
||||
keyEnclave, ok := original.(*KeyEnclave)
|
||||
keyclave, ok := original.(*keyEnclave)
|
||||
require.True(t, ok)
|
||||
|
||||
data, err := keyEnclave.Marshal()
|
||||
|
||||
data, err := keyclave.Serialize()
|
||||
require.NoError(t, err)
|
||||
require.NotEmpty(t, data)
|
||||
|
||||
// Unmarshal
|
||||
restored := &KeyEnclave{}
|
||||
restored := &keyEnclave{}
|
||||
err = restored.Unmarshal(data)
|
||||
require.NoError(t, err)
|
||||
|
||||
// Verify restored enclave
|
||||
assert.Equal(t, keyEnclave.Addr, restored.Addr)
|
||||
assert.True(t, keyEnclave.PubPoint.Equal(restored.PubPoint))
|
||||
assert.Equal(t, keyEnclave.VaultCID, restored.VaultCID)
|
||||
assert.Equal(t, keyclave.Addr, restored.Addr)
|
||||
assert.True(t, keyclave.PubPoint.Equal(restored.PubPoint))
|
||||
assert.True(t, restored.IsValid())
|
||||
})
|
||||
}
|
||||
|
||||
+98
-70
@@ -1,63 +1,31 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/ecdsa"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/sonr/crypto/core/curves"
|
||||
"github.com/onsonr/sonr/crypto/core/protocol"
|
||||
"github.com/onsonr/sonr/crypto/keys"
|
||||
"github.com/onsonr/sonr/crypto/tecdsa/dklsv1"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
// Enclave defines the interface for key management operations
|
||||
type Enclave interface {
|
||||
Address() string
|
||||
IsValid() bool
|
||||
PubKey() keys.PubKey
|
||||
Refresh() (Enclave, error)
|
||||
Sign(data []byte) ([]byte, error)
|
||||
Verify(data []byte, sig []byte) (bool, error)
|
||||
}
|
||||
|
||||
// KeyEnclave implements the Enclave interface
|
||||
type KeyEnclave struct {
|
||||
// keyEnclave implements the Enclave interface
|
||||
type keyEnclave struct {
|
||||
// Serialized fields
|
||||
Addr string `json:"address"`
|
||||
PubPoint curves.Point `json:"-"`
|
||||
PubBytes []byte `json:"pub_key"`
|
||||
ValShare Message `json:"val_share"`
|
||||
UserShare Message `json:"user_share"`
|
||||
VaultCID string `json:"vault_cid,omitempty"`
|
||||
|
||||
// Extra fields
|
||||
nonce []byte
|
||||
}
|
||||
|
||||
// Marshal returns the JSON encoding of KeyEnclave
|
||||
func (k *KeyEnclave) Marshal() ([]byte, error) {
|
||||
// Store compressed public point bytes before marshaling
|
||||
k.PubBytes = k.PubPoint.ToAffineCompressed()
|
||||
return json.Marshal(k)
|
||||
}
|
||||
|
||||
// Unmarshal parses the JSON-encoded data and stores the result
|
||||
func (k *KeyEnclave) Unmarshal(data []byte) error {
|
||||
if err := json.Unmarshal(data, k); err != nil {
|
||||
return err
|
||||
}
|
||||
// Reconstruct Point from bytes
|
||||
curve := curves.K256()
|
||||
point, err := curve.NewIdentityPoint().FromAffineCompressed(k.PubBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.PubPoint = point
|
||||
return nil
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) IsValid() bool {
|
||||
return k.PubPoint != nil && k.ValShare != nil && k.UserShare != nil && k.Addr != ""
|
||||
}
|
||||
|
||||
func initKeyEnclave(valShare, userShare Message) (*KeyEnclave, error) {
|
||||
func newEnclave(valShare, userShare Message, nonce []byte) (Enclave, error) {
|
||||
pubPoint, err := getAlicePubPoint(valShare)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -67,47 +35,105 @@ func initKeyEnclave(valShare, userShare Message) (*KeyEnclave, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &KeyEnclave{
|
||||
return &keyEnclave{
|
||||
Addr: addr,
|
||||
PubPoint: pubPoint,
|
||||
ValShare: valShare,
|
||||
UserShare: userShare,
|
||||
nonce: nonce,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) Address() string {
|
||||
// Address returns the Sonr address of the keyEnclave
|
||||
func (k *keyEnclave) Address() string {
|
||||
return k.Addr
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) PubKey() keys.PubKey {
|
||||
// DID returns the DID of the keyEnclave
|
||||
func (k *keyEnclave) DID() keys.DID {
|
||||
return keys.NewFromPubKey(k.PubKey())
|
||||
}
|
||||
|
||||
// Export returns encrypted enclave data
|
||||
func (k *keyEnclave) Export(key []byte) ([]byte, error) {
|
||||
data, err := k.Serialize()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to serialize enclave: %w", err)
|
||||
}
|
||||
|
||||
hashedKey := hashKey(key)
|
||||
block, err := aes.NewCipher(hashedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return aesgcm.Seal(nil, k.nonce, data, nil), nil
|
||||
}
|
||||
|
||||
// Import decrypts and loads enclave data
|
||||
func (k *keyEnclave) Import(data []byte, key []byte) error {
|
||||
hashedKey := hashKey(key)
|
||||
block, err := aes.NewCipher(hashedKey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
decrypted, err := aesgcm.Open(nil, k.nonce, data, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return k.Unmarshal(decrypted)
|
||||
}
|
||||
|
||||
// IsValid returns true if the keyEnclave is valid
|
||||
func (k *keyEnclave) IsValid() bool {
|
||||
return k.PubPoint != nil && k.ValShare != nil && k.UserShare != nil && k.Addr != ""
|
||||
}
|
||||
|
||||
// PubKey returns the public key of the keyEnclave
|
||||
func (k *keyEnclave) PubKey() keys.PubKey {
|
||||
return keys.NewPubKey(k.PubPoint)
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) Refresh() (Enclave, error) {
|
||||
refreshFuncVal, err := k.valRefreshFunc()
|
||||
// Refresh returns a new keyEnclave
|
||||
func (k *keyEnclave) Refresh() (Enclave, error) {
|
||||
refreshFuncVal, err := valRefreshFunc(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
refreshFuncUser, err := k.userRefreshFunc()
|
||||
refreshFuncUser, err := userRefreshFunc(k)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ExecuteRefresh(refreshFuncVal, refreshFuncUser)
|
||||
return ExecuteRefresh(refreshFuncVal, refreshFuncUser, k.nonce)
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) Sign(data []byte) ([]byte, error) {
|
||||
userSign, err := k.userSignFunc(data)
|
||||
// Sign returns the signature of the data
|
||||
func (k *keyEnclave) Sign(data []byte) ([]byte, error) {
|
||||
userSign, err := userSignFunc(k, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valSign, err := k.valSignFunc(data)
|
||||
valSign, err := valSignFunc(k, data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return ExecuteSigning(valSign, userSign)
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) Verify(data []byte, sig []byte) (bool, error) {
|
||||
// Verify returns true if the signature is valid
|
||||
func (k *keyEnclave) Verify(data []byte, sig []byte) (bool, error) {
|
||||
edSig, err := deserializeSignature(sig)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -121,31 +147,33 @@ func (k *KeyEnclave) Verify(data []byte, sig []byte) (bool, error) {
|
||||
X: ePub.X,
|
||||
Y: ePub.Y,
|
||||
}
|
||||
|
||||
|
||||
// Hash the message using SHA3-256
|
||||
hash := sha3.New256()
|
||||
hash.Write(data)
|
||||
digest := hash.Sum(nil)
|
||||
|
||||
|
||||
return ecdsa.Verify(pk, digest, edSig.R, edSig.S), nil
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) userSignFunc(bz []byte) (SignFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewBobSign(curve, sha3.New256(), bz, k.UserShare, protocol.Version1)
|
||||
// Marshal returns the JSON encoding of keyEnclave
|
||||
func (k *keyEnclave) Serialize() ([]byte, error) {
|
||||
// Store compressed public point bytes before marshaling
|
||||
k.PubBytes = k.PubPoint.ToAffineCompressed()
|
||||
return json.Marshal(k)
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) userRefreshFunc() (RefreshFunc, error) {
|
||||
// Unmarshal parses the JSON-encoded data and stores the result
|
||||
func (k *keyEnclave) Unmarshal(data []byte) error {
|
||||
if err := json.Unmarshal(data, k); err != nil {
|
||||
return err
|
||||
}
|
||||
// Reconstruct Point from bytes
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewBobRefresh(curve, k.UserShare, protocol.Version1)
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) valSignFunc(bz []byte) (SignFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewAliceSign(curve, sha3.New256(), bz, k.ValShare, protocol.Version1)
|
||||
}
|
||||
|
||||
func (k *KeyEnclave) valRefreshFunc() (RefreshFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewAliceRefresh(curve, k.ValShare, protocol.Version1)
|
||||
point, err := curve.NewIdentityPoint().FromAffineCompressed(k.PubBytes)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
k.PubPoint = point
|
||||
return nil
|
||||
}
|
||||
|
||||
+4
-29
@@ -1,14 +1,13 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"github.com/ipfs/kubo/client/rpc"
|
||||
"github.com/onsonr/sonr/crypto/core/curves"
|
||||
"github.com/onsonr/sonr/crypto/core/protocol"
|
||||
"github.com/onsonr/sonr/crypto/tecdsa/dklsv1"
|
||||
)
|
||||
|
||||
// GenEnclave generates a new MPC keyshare
|
||||
func GenEnclave() (Enclave, error) {
|
||||
func GenEnclave(nonce []byte) (Enclave, error) {
|
||||
curve := curves.K256()
|
||||
valKs := dklsv1.NewAliceDkg(curve, protocol.Version1)
|
||||
userKs := dklsv1.NewBobDkg(curve, protocol.Version1)
|
||||
@@ -24,31 +23,7 @@ func GenEnclave() (Enclave, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return initKeyEnclave(valRes, userRes)
|
||||
}
|
||||
|
||||
// GenEnclaveIPFS generates a new MPC keyshare
|
||||
func GenEnclaveIPFS(ipc *rpc.HttpApi) (Enclave, error) {
|
||||
curve := curves.K256()
|
||||
valKs := dklsv1.NewAliceDkg(curve, protocol.Version1)
|
||||
userKs := dklsv1.NewBobDkg(curve, protocol.Version1)
|
||||
aErr, bErr := RunProtocol(userKs, valKs)
|
||||
if err := checkIteratedErrors(aErr, bErr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
valRes, err := valKs.Result(protocol.Version1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userRes, err := userKs.Result(protocol.Version1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
e, err := initKeyEnclave(valRes, userRes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return addEnclaveIPFS(e, ipc)
|
||||
return newEnclave(valRes, userRes, nonce)
|
||||
}
|
||||
|
||||
// ExecuteSigning runs the MPC signing protocol
|
||||
@@ -73,7 +48,7 @@ func ExecuteSigning(signFuncVal SignFunc, signFuncUser SignFunc) ([]byte, error)
|
||||
}
|
||||
|
||||
// ExecuteRefresh runs the MPC refresh protocol
|
||||
func ExecuteRefresh(refreshFuncVal RefreshFunc, refreshFuncUser RefreshFunc) (*KeyEnclave, error) {
|
||||
func ExecuteRefresh(refreshFuncVal RefreshFunc, refreshFuncUser RefreshFunc, nonce []byte) (Enclave, error) {
|
||||
aErr, bErr := RunProtocol(refreshFuncVal, refreshFuncUser)
|
||||
if err := checkIteratedErrors(aErr, bErr); err != nil {
|
||||
return nil, err
|
||||
@@ -86,7 +61,7 @@ func ExecuteRefresh(refreshFuncVal RefreshFunc, refreshFuncUser RefreshFunc) (*K
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return initKeyEnclave(valRefreshResult, userRefreshResult)
|
||||
return newEnclave(valRefreshResult, userRefreshResult, nonce)
|
||||
}
|
||||
|
||||
// For DKG bob starts first. For refresh and sign, Alice starts first.
|
||||
|
||||
@@ -57,9 +57,10 @@ func (k ucanKeyshare) ChainCode() ([]byte, error) {
|
||||
|
||||
// DefaultOriginToken returns a default token with the keyshare's issuer as the audience
|
||||
func (k ucanKeyshare) OriginToken() (*Token, error) {
|
||||
att := ucan.NewSmartAccount(k.addr)
|
||||
// att := ucan.NewSmartAccount(k.addr)
|
||||
zero := time.Time{}
|
||||
return k.NewOriginToken(k.issuerDID, att, nil, zero, zero)
|
||||
// return k.NewOriginToken(k.issuerDID, att, nil, zero, zero)
|
||||
return k.newToken(k.issuerDID, nil, nil, nil, zero, zero)
|
||||
}
|
||||
|
||||
func (k ucanKeyshare) SignData(data []byte) ([]byte, error) {
|
||||
@@ -101,7 +102,6 @@ func (k ucanKeyshare) UCANParser() *ucan.TokenParser {
|
||||
if key == ucan.CapKey {
|
||||
cap = val
|
||||
} else {
|
||||
rsc = ucan.NewStringLengthResource(key, val)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+64
-18
@@ -1,34 +1,19 @@
|
||||
package mpc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/cosmos/cosmos-sdk/types/bech32"
|
||||
"github.com/ipfs/boxo/files"
|
||||
"github.com/ipfs/kubo/client/rpc"
|
||||
"github.com/onsonr/sonr/crypto/core/curves"
|
||||
"github.com/onsonr/sonr/crypto/core/protocol"
|
||||
"github.com/onsonr/sonr/crypto/tecdsa/dklsv1"
|
||||
"golang.org/x/crypto/sha3"
|
||||
)
|
||||
|
||||
func addEnclaveIPFS(enclave *KeyEnclave, ipc *rpc.HttpApi) (Enclave, error) {
|
||||
jsonEnclave, err := json.Marshal(enclave)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Save enclave to IPFS
|
||||
cid, err := ipc.Unixfs().Add(context.Background(), files.NewBytesFile(jsonEnclave))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
enclave.VaultCID = cid.String()
|
||||
return enclave, nil
|
||||
}
|
||||
|
||||
func checkIteratedErrors(aErr, bErr error) error {
|
||||
if aErr == protocol.ErrProtocolFinished && bErr == protocol.ErrProtocolFinished {
|
||||
return nil
|
||||
@@ -51,6 +36,47 @@ func computeSonrAddr(pp Point) (string, error) {
|
||||
return sonrAddr, nil
|
||||
}
|
||||
|
||||
func hashKey(key []byte) []byte {
|
||||
hash := sha3.New256()
|
||||
hash.Write(key)
|
||||
return hash.Sum(nil)[:32] // Use first 32 bytes of hash
|
||||
}
|
||||
|
||||
func decryptKeyshare(msg []byte, key []byte, nonce []byte) ([]byte, error) {
|
||||
hashedKey := hashKey(key)
|
||||
block, err := aes.NewCipher(hashedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
plaintext, err := aesgcm.Open(nil, nonce, msg, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return plaintext, nil
|
||||
}
|
||||
|
||||
func encryptKeyshare(msg Message, key []byte, nonce []byte) ([]byte, error) {
|
||||
hashedKey := hashKey(key)
|
||||
msgBytes, err := protocol.EncodeMessage(msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
block, err := aes.NewCipher(hashedKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
aesgcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ciphertext := aesgcm.Seal(nil, nonce, []byte(msgBytes), nil)
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
func getAliceOut(msg *protocol.Message) (AliceOut, error) {
|
||||
return dklsv1.DecodeAliceDkgResult(msg)
|
||||
}
|
||||
@@ -122,3 +148,23 @@ func deserializeSignature(sigBytes []byte) (*curves.EcdsaSignature, error) {
|
||||
S: s,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func userSignFunc(k *keyEnclave, bz []byte) (SignFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewBobSign(curve, sha3.New256(), bz, k.UserShare, protocol.Version1)
|
||||
}
|
||||
|
||||
func userRefreshFunc(k *keyEnclave) (RefreshFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewBobRefresh(curve, k.UserShare, protocol.Version1)
|
||||
}
|
||||
|
||||
func valSignFunc(k *keyEnclave, bz []byte) (SignFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewAliceSign(curve, sha3.New256(), bz, k.ValShare, protocol.Version1)
|
||||
}
|
||||
|
||||
func valRefreshFunc(k *keyEnclave) (RefreshFunc, error) {
|
||||
curve := curves.K256()
|
||||
return dklsv1.NewAliceRefresh(curve, k.ValShare, protocol.Version1)
|
||||
}
|
||||
|
||||
@@ -73,32 +73,6 @@ type Resource interface {
|
||||
Contains(b Resource) bool
|
||||
}
|
||||
|
||||
type stringLengthRsc struct {
|
||||
t string
|
||||
v string
|
||||
}
|
||||
|
||||
// NewStringLengthResource is a silly implementation of resource to use while
|
||||
// I figure out what an OR filter on strings is. Don't use this.
|
||||
func NewStringLengthResource(typ, val string) Resource {
|
||||
return stringLengthRsc{
|
||||
t: typ,
|
||||
v: val,
|
||||
}
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Type() string {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Value() string {
|
||||
return r.v
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Contains(b Resource) bool {
|
||||
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
|
||||
}
|
||||
|
||||
// Capability is an action users can perform
|
||||
type Capability interface {
|
||||
// A Capability must be expressable as a string
|
||||
|
||||
@@ -1,96 +0,0 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestAttenuationsContains(t *testing.T) {
|
||||
aContains := [][2]string{
|
||||
{
|
||||
`[
|
||||
{ "cap": "SUPER_USER", "dataset": "b5/world_bank_population"},
|
||||
{ "cap": "OVERWRITE", "api": "https://api.qri.cloud" }
|
||||
]`,
|
||||
`[
|
||||
{"cap": "SOFT_DELETE", "dataset": "b5/world_bank_population" }
|
||||
]`,
|
||||
},
|
||||
{
|
||||
`[
|
||||
{ "cap": "SUPER_USER", "dataset": "b5/world_bank_population"},
|
||||
{ "cap": "OVERWRITE", "api": "https://api.qri.cloud" }
|
||||
]`,
|
||||
`[
|
||||
{"cap": "SUPER_USER", "dataset": "b5/world_bank_population" }
|
||||
]`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range aContains {
|
||||
t.Run(fmt.Sprintf("contains_%d", i), func(t *testing.T) {
|
||||
a := testAttenuations(c[0])
|
||||
b := testAttenuations(c[1])
|
||||
if !a.Contains(b) {
|
||||
t.Errorf("expected a attenuations to contain b attenuations")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
aNotContains := [][2]string{
|
||||
{
|
||||
`[
|
||||
{ "cap": "SUPER_USER", "dataset": "b5/world_bank_population"},
|
||||
{ "cap": "OVERWRITE", "api": "https://api.qri.cloud" }
|
||||
]`,
|
||||
`[
|
||||
{ "cap": "CREATE", "dataset": "b5" }
|
||||
]`,
|
||||
},
|
||||
}
|
||||
|
||||
for i, c := range aNotContains {
|
||||
t.Run(fmt.Sprintf("not_contains_%d", i), func(t *testing.T) {
|
||||
a := testAttenuations(c[0])
|
||||
b := testAttenuations(c[1])
|
||||
if a.Contains(b) {
|
||||
t.Errorf("expected a attenuations to NOT contain b attenuations")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mustJSON(data string, v interface{}) {
|
||||
if err := json.Unmarshal([]byte(data), v); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func testAttenuations(data string) Attenuations {
|
||||
caps := NewNestedCapabilities("SUPER_USER", "OVERWRITE", "SOFT_DELETE", "REVISE", "CREATE")
|
||||
|
||||
v := []map[string]string{}
|
||||
mustJSON(data, &v)
|
||||
|
||||
var att Attenuations
|
||||
for _, x := range v {
|
||||
var cap Capability
|
||||
var rsc Resource
|
||||
for key, val := range x {
|
||||
switch key {
|
||||
case CapKey:
|
||||
cap = caps.Cap(val)
|
||||
default:
|
||||
rsc = NewStringLengthResource(key, val)
|
||||
}
|
||||
}
|
||||
att = append(att, Attenuation{cap, rsc})
|
||||
}
|
||||
|
||||
return att
|
||||
}
|
||||
|
||||
func TestNestedCapabilities(t *testing.T) {
|
||||
|
||||
}
|
||||
@@ -1,79 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package capability
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Capability string
|
||||
|
||||
const (
|
||||
CAPOWNER Capability = "CAP_OWNER"
|
||||
CAPOPERATOR Capability = "CAP_OPERATOR"
|
||||
CAPOBSERVER Capability = "CAP_OBSERVER"
|
||||
CAPAUTHENTICATE Capability = "CAP_AUTHENTICATE"
|
||||
CAPAUTHORIZE Capability = "CAP_AUTHORIZE"
|
||||
CAPDELEGATE Capability = "CAP_DELEGATE"
|
||||
CAPINVOKE Capability = "CAP_INVOKE"
|
||||
CAPEXECUTE Capability = "CAP_EXECUTE"
|
||||
CAPPROPOSE Capability = "CAP_PROPOSE"
|
||||
CAPSIGN Capability = "CAP_SIGN"
|
||||
CAPSETPOLICY Capability = "CAP_SET_POLICY"
|
||||
CAPSETTHRESHOLD Capability = "CAP_SET_THRESHOLD"
|
||||
CAPRECOVER Capability = "CAP_RECOVER"
|
||||
CAPSOCIAL Capability = "CAP_SOCIAL"
|
||||
CAPVOTE Capability = "CAP_VOTE"
|
||||
CAPRESOLVER Capability = "CAP_RESOLVER"
|
||||
CAPPRODUCER Capability = "CAP_PRODUCER"
|
||||
)
|
||||
|
||||
// String returns the string representation of Capability
|
||||
func (rcv Capability) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(Capability)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for Capability.
|
||||
func (rcv *Capability) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "CAP_OWNER":
|
||||
*rcv = CAPOWNER
|
||||
case "CAP_OPERATOR":
|
||||
*rcv = CAPOPERATOR
|
||||
case "CAP_OBSERVER":
|
||||
*rcv = CAPOBSERVER
|
||||
case "CAP_AUTHENTICATE":
|
||||
*rcv = CAPAUTHENTICATE
|
||||
case "CAP_AUTHORIZE":
|
||||
*rcv = CAPAUTHORIZE
|
||||
case "CAP_DELEGATE":
|
||||
*rcv = CAPDELEGATE
|
||||
case "CAP_INVOKE":
|
||||
*rcv = CAPINVOKE
|
||||
case "CAP_EXECUTE":
|
||||
*rcv = CAPEXECUTE
|
||||
case "CAP_PROPOSE":
|
||||
*rcv = CAPPROPOSE
|
||||
case "CAP_SIGN":
|
||||
*rcv = CAPSIGN
|
||||
case "CAP_SET_POLICY":
|
||||
*rcv = CAPSETPOLICY
|
||||
case "CAP_SET_THRESHOLD":
|
||||
*rcv = CAPSETTHRESHOLD
|
||||
case "CAP_RECOVER":
|
||||
*rcv = CAPRECOVER
|
||||
case "CAP_SOCIAL":
|
||||
*rcv = CAPSOCIAL
|
||||
case "CAP_VOTE":
|
||||
*rcv = CAPVOTE
|
||||
case "CAP_RESOLVER":
|
||||
*rcv = CAPRESOLVER
|
||||
case "CAP_PRODUCER":
|
||||
*rcv = CAPPRODUCER
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid Capability`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package capaccount
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CapAccount string
|
||||
|
||||
const (
|
||||
ExecBroadcast CapAccount = "exec/broadcast"
|
||||
ExecQuery CapAccount = "exec/query"
|
||||
ExecSimulate CapAccount = "exec/simulate"
|
||||
ExecVote CapAccount = "exec/vote"
|
||||
ExecDelegate CapAccount = "exec/delegate"
|
||||
ExecInvoke CapAccount = "exec/invoke"
|
||||
ExecSend CapAccount = "exec/send"
|
||||
)
|
||||
|
||||
// String returns the string representation of CapAccount
|
||||
func (rcv CapAccount) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(CapAccount)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapAccount.
|
||||
func (rcv *CapAccount) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "exec/broadcast":
|
||||
*rcv = ExecBroadcast
|
||||
case "exec/query":
|
||||
*rcv = ExecQuery
|
||||
case "exec/simulate":
|
||||
*rcv = ExecSimulate
|
||||
case "exec/vote":
|
||||
*rcv = ExecVote
|
||||
case "exec/delegate":
|
||||
*rcv = ExecDelegate
|
||||
case "exec/invoke":
|
||||
*rcv = ExecInvoke
|
||||
case "exec/send":
|
||||
*rcv = ExecSend
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid CapAccount`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package capaccount
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func NewCap(ty CapAccount) ucan.Capability {
|
||||
return ucan.Capability(ty)
|
||||
}
|
||||
|
||||
func (c CapAccount) Contains(b ucan.Capability) bool {
|
||||
return c.String() == b.String()
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package capinterchain
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CapInterchain string
|
||||
|
||||
const (
|
||||
TransferSwap CapInterchain = "transfer/swap"
|
||||
TransferSend CapInterchain = "transfer/send"
|
||||
TransferAtomic CapInterchain = "transfer/atomic"
|
||||
TransferBatch CapInterchain = "transfer/batch"
|
||||
TransferP2p CapInterchain = "transfer/p2p"
|
||||
)
|
||||
|
||||
// String returns the string representation of CapInterchain
|
||||
func (rcv CapInterchain) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(CapInterchain)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapInterchain.
|
||||
func (rcv *CapInterchain) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "transfer/swap":
|
||||
*rcv = TransferSwap
|
||||
case "transfer/send":
|
||||
*rcv = TransferSend
|
||||
case "transfer/atomic":
|
||||
*rcv = TransferAtomic
|
||||
case "transfer/batch":
|
||||
*rcv = TransferBatch
|
||||
case "transfer/p2p":
|
||||
*rcv = TransferP2p
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid CapInterchain`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package capinterchain
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func NewCap(ty CapInterchain) ucan.Capability {
|
||||
return ucan.Capability(ty)
|
||||
}
|
||||
|
||||
func (c CapInterchain) Contains(b ucan.Capability) bool {
|
||||
return c.String() == b.String()
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package capvault
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type CapVault string
|
||||
|
||||
const (
|
||||
CrudAsset CapVault = "crud/asset"
|
||||
CrudAuthzgrant CapVault = "crud/authzgrant"
|
||||
CrudProfile CapVault = "crud/profile"
|
||||
CrudRecord CapVault = "crud/record"
|
||||
UseRecovery CapVault = "use/recovery"
|
||||
UseSync CapVault = "use/sync"
|
||||
UseSigner CapVault = "use/signer"
|
||||
)
|
||||
|
||||
// String returns the string representation of CapVault
|
||||
func (rcv CapVault) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(CapVault)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for CapVault.
|
||||
func (rcv *CapVault) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "crud/asset":
|
||||
*rcv = CrudAsset
|
||||
case "crud/authzgrant":
|
||||
*rcv = CrudAuthzgrant
|
||||
case "crud/profile":
|
||||
*rcv = CrudProfile
|
||||
case "crud/record":
|
||||
*rcv = CrudRecord
|
||||
case "use/recovery":
|
||||
*rcv = UseRecovery
|
||||
case "use/sync":
|
||||
*rcv = UseSync
|
||||
case "use/signer":
|
||||
*rcv = UseSigner
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid CapVault`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package capvault
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func NewCap(ty CapVault) ucan.Capability {
|
||||
return ucan.Capability(ty)
|
||||
}
|
||||
|
||||
func (c CapVault) Contains(b ucan.Capability) bool {
|
||||
return c.String() == b.String()
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
// Package attns implements the UCAN resource and capability types
|
||||
package attns
|
||||
|
||||
import (
|
||||
"github.com/onsonr/sonr/crypto/ucan"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/capaccount"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/capinterchain"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/capvault"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/resaccount"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/resinterchain"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/resvault"
|
||||
)
|
||||
|
||||
// Capability hierarchy for sonr network
|
||||
// -------------------------------------
|
||||
// VAULT (DWN)
|
||||
//
|
||||
// └─ CRUD/ASSET
|
||||
// └─ CRUD/AUTHZGRANT
|
||||
// └─ CRUD/PROFILE
|
||||
// └─ CRUD/RECORD
|
||||
// └─ USE/RECOVERY
|
||||
// └─ USE/SYNC
|
||||
// └─ USE/SIGNER
|
||||
//
|
||||
// ACCOUNT (DID)
|
||||
//
|
||||
// └─ EXEC/BROADCAST
|
||||
// └─ EXEC/QUERY
|
||||
// └─ EXEC/SIMULATE
|
||||
// └─ EXEC/VOTE
|
||||
// └─ EXEC/DELEGATE
|
||||
// └─ EXEC/INVOKE
|
||||
// └─ EXEC/SEND
|
||||
//
|
||||
// INTERCHAIN
|
||||
//
|
||||
// └─ TRANSFER/SWAP
|
||||
// └─ TRANSFER/SEND
|
||||
// └─ TRANSFER/ATOMIC
|
||||
// └─ TRANSFER/BATCH
|
||||
// └─ TRANSFER/P2P
|
||||
// └─ TRANSFER/SEND
|
||||
|
||||
type Capability string
|
||||
|
||||
const (
|
||||
CapExecBroadcast = capaccount.ExecBroadcast
|
||||
CapExecQuery = capaccount.ExecQuery
|
||||
CapExecSimulate = capaccount.ExecSimulate
|
||||
CapExecVote = capaccount.ExecVote
|
||||
CapExecDelegate = capaccount.ExecDelegate
|
||||
CapExecInvoke = capaccount.ExecInvoke
|
||||
CapExecSend = capaccount.ExecSend
|
||||
|
||||
CapTransferSwap = capinterchain.TransferSwap
|
||||
CapTransferSend = capinterchain.TransferSend
|
||||
CapTransferAtomic = capinterchain.TransferAtomic
|
||||
CapTransferBatch = capinterchain.TransferBatch
|
||||
CapTransferP2P = capinterchain.TransferP2p
|
||||
|
||||
CapCrudAsset = capvault.CrudAsset
|
||||
CapCrudAuthzgrant = capvault.CrudAuthzgrant
|
||||
CapCrudProfile = capvault.CrudProfile
|
||||
CapCrudRecord = capvault.CrudRecord
|
||||
CapUseRecovery = capvault.UseRecovery
|
||||
CapUseSync = capvault.UseSync
|
||||
CapUseSigner = capvault.UseSigner
|
||||
)
|
||||
|
||||
type NewCapFunc func(string) ucan.Capability
|
||||
|
||||
type BuildResourceFunc func(string, string) ucan.Resource
|
||||
|
||||
func CreateArray(attns ...ucan.Attenuation) ucan.Attenuations {
|
||||
return ucan.Attenuations(attns)
|
||||
}
|
||||
|
||||
func New(cap ucan.Capability, rsc ucan.Resource) ucan.Attenuation {
|
||||
return ucan.Attenuation{
|
||||
Cap: cap,
|
||||
Rsc: rsc,
|
||||
}
|
||||
}
|
||||
|
||||
// NewAccountCap creates a new account capability
|
||||
func NewAccountCap(ty capaccount.CapAccount) ucan.Capability {
|
||||
return capaccount.NewCap(ty)
|
||||
}
|
||||
|
||||
// NewInterchainCap creates a new interchain capability
|
||||
func NewInterchainCap(ty capinterchain.CapInterchain) ucan.Capability {
|
||||
return capinterchain.NewCap(ty)
|
||||
}
|
||||
|
||||
// NewVaultCap creates a new vault capability
|
||||
func NewVaultCap(ty capvault.CapVault) ucan.Capability {
|
||||
return capvault.NewCap(ty)
|
||||
}
|
||||
|
||||
// BuildAccountResource creates a new account resource
|
||||
func BuildAccountResource(ty resaccount.ResAccount, value string) ucan.Resource {
|
||||
return resaccount.Build(ty, value)
|
||||
}
|
||||
|
||||
// BuildInterchainResource creates a new interchain resource
|
||||
func BuildInterchainResource(ty resinterchain.ResInterchain, value string) ucan.Resource {
|
||||
return resinterchain.Build(ty, value)
|
||||
}
|
||||
|
||||
// BuildVaultResource creates a new vault resource
|
||||
func BuildVaultResource(ty resvault.ResVault, value string) ucan.Resource {
|
||||
return resvault.Build(ty, value)
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package policytype
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type PolicyType string
|
||||
|
||||
const (
|
||||
POLICYTHRESHOLD PolicyType = "POLICY_THRESHOLD"
|
||||
POLICYTIMELOCK PolicyType = "POLICY_TIMELOCK"
|
||||
POLICYWHITELIST PolicyType = "POLICY_WHITELIST"
|
||||
POLICYKEYGEN PolicyType = "POLICY_KEYGEN"
|
||||
)
|
||||
|
||||
// String returns the string representation of PolicyType
|
||||
func (rcv PolicyType) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(PolicyType)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for PolicyType.
|
||||
func (rcv *PolicyType) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "POLICY_THRESHOLD":
|
||||
*rcv = POLICYTHRESHOLD
|
||||
case "POLICY_TIMELOCK":
|
||||
*rcv = POLICYTIMELOCK
|
||||
case "POLICY_WHITELIST":
|
||||
*rcv = POLICYWHITELIST
|
||||
case "POLICY_KEYGEN":
|
||||
*rcv = POLICYKEYGEN
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid PolicyType`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package resaccount
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResAccount string
|
||||
|
||||
const (
|
||||
AccSequence ResAccount = "acc/sequence"
|
||||
AccNumber ResAccount = "acc/number"
|
||||
ChainId ResAccount = "chain/id"
|
||||
AssetCode ResAccount = "asset/code"
|
||||
AuthzGrant ResAccount = "authz/grant"
|
||||
)
|
||||
|
||||
// String returns the string representation of ResAccount
|
||||
func (rcv ResAccount) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(ResAccount)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResAccount.
|
||||
func (rcv *ResAccount) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "acc/sequence":
|
||||
*rcv = AccSequence
|
||||
case "acc/number":
|
||||
*rcv = AccNumber
|
||||
case "chain/id":
|
||||
*rcv = ChainId
|
||||
case "asset/code":
|
||||
*rcv = AssetCode
|
||||
case "authz/grant":
|
||||
*rcv = AuthzGrant
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid ResAccount`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package resaccount
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func Build(ty ResAccount, value string) ucan.Resource {
|
||||
return newStringLengthResource(ty.String(), value)
|
||||
}
|
||||
|
||||
type stringLengthRsc struct {
|
||||
t string
|
||||
v string
|
||||
}
|
||||
|
||||
// NewStringLengthResource is a silly implementation of resource to use while
|
||||
// I figure out what an OR filter on strings is. Don't use this.
|
||||
func newStringLengthResource(typ, val string) ucan.Resource {
|
||||
return stringLengthRsc{
|
||||
t: typ,
|
||||
v: val,
|
||||
}
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Type() string {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Value() string {
|
||||
return r.v
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Contains(b ucan.Resource) bool {
|
||||
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package resinterchain
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResInterchain string
|
||||
|
||||
const (
|
||||
ChannnelPort ResInterchain = "channnel/port"
|
||||
ChainId ResInterchain = "chain/id"
|
||||
ChainName ResInterchain = "chain/name"
|
||||
AccHost ResInterchain = "acc/host"
|
||||
AccController ResInterchain = "acc/controller"
|
||||
)
|
||||
|
||||
// String returns the string representation of ResInterchain
|
||||
func (rcv ResInterchain) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(ResInterchain)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResInterchain.
|
||||
func (rcv *ResInterchain) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "channnel/port":
|
||||
*rcv = ChannnelPort
|
||||
case "chain/id":
|
||||
*rcv = ChainId
|
||||
case "chain/name":
|
||||
*rcv = ChainName
|
||||
case "acc/host":
|
||||
*rcv = AccHost
|
||||
case "acc/controller":
|
||||
*rcv = AccController
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid ResInterchain`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package resinterchain
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func Build(ty ResInterchain, value string) ucan.Resource {
|
||||
return newStringLengthResource(ty.String(), value)
|
||||
}
|
||||
|
||||
type stringLengthRsc struct {
|
||||
t string
|
||||
v string
|
||||
}
|
||||
|
||||
// NewStringLengthResource is a silly implementation of resource to use while
|
||||
// I figure out what an OR filter on strings is. Don't use this.
|
||||
func newStringLengthResource(typ, val string) ucan.Resource {
|
||||
return stringLengthRsc{
|
||||
t: typ,
|
||||
v: val,
|
||||
}
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Type() string {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Value() string {
|
||||
return r.v
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Contains(b ucan.Resource) bool {
|
||||
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package resourcetype
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResourceType string
|
||||
|
||||
const (
|
||||
RESACCOUNT ResourceType = "RES_ACCOUNT"
|
||||
RESTRANSACTION ResourceType = "RES_TRANSACTION"
|
||||
RESPOLICY ResourceType = "RES_POLICY"
|
||||
RESRECOVERY ResourceType = "RES_RECOVERY"
|
||||
RESVAULT ResourceType = "RES_VAULT"
|
||||
RESIPFS ResourceType = "RES_IPFS"
|
||||
RESIPNS ResourceType = "RES_IPNS"
|
||||
RESKEYSHARE ResourceType = "RES_KEYSHARE"
|
||||
)
|
||||
|
||||
// String returns the string representation of ResourceType
|
||||
func (rcv ResourceType) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(ResourceType)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResourceType.
|
||||
func (rcv *ResourceType) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "RES_ACCOUNT":
|
||||
*rcv = RESACCOUNT
|
||||
case "RES_TRANSACTION":
|
||||
*rcv = RESTRANSACTION
|
||||
case "RES_POLICY":
|
||||
*rcv = RESPOLICY
|
||||
case "RES_RECOVERY":
|
||||
*rcv = RESRECOVERY
|
||||
case "RES_VAULT":
|
||||
*rcv = RESVAULT
|
||||
case "RES_IPFS":
|
||||
*rcv = RESIPFS
|
||||
case "RES_IPNS":
|
||||
*rcv = RESIPNS
|
||||
case "RES_KEYSHARE":
|
||||
*rcv = RESKEYSHARE
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid ResourceType`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
// Code generated from Pkl module `sonr.orm.UCAN`. DO NOT EDIT.
|
||||
package resvault
|
||||
|
||||
import (
|
||||
"encoding"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type ResVault string
|
||||
|
||||
const (
|
||||
KsEnclave ResVault = "ks/enclave"
|
||||
LocCid ResVault = "loc/cid"
|
||||
LocEntity ResVault = "loc/entity"
|
||||
LocIpns ResVault = "loc/ipns"
|
||||
AddrSonr ResVault = "addr/sonr"
|
||||
ChainCode ResVault = "chain/code"
|
||||
)
|
||||
|
||||
// String returns the string representation of ResVault
|
||||
func (rcv ResVault) String() string {
|
||||
return string(rcv)
|
||||
}
|
||||
|
||||
var _ encoding.BinaryUnmarshaler = new(ResVault)
|
||||
|
||||
// UnmarshalBinary implements encoding.BinaryUnmarshaler for ResVault.
|
||||
func (rcv *ResVault) UnmarshalBinary(data []byte) error {
|
||||
switch str := string(data); str {
|
||||
case "ks/enclave":
|
||||
*rcv = KsEnclave
|
||||
case "loc/cid":
|
||||
*rcv = LocCid
|
||||
case "loc/entity":
|
||||
*rcv = LocEntity
|
||||
case "loc/ipns":
|
||||
*rcv = LocIpns
|
||||
case "addr/sonr":
|
||||
*rcv = AddrSonr
|
||||
case "chain/code":
|
||||
*rcv = ChainCode
|
||||
default:
|
||||
return fmt.Errorf(`illegal: "%s" is not a valid ResVault`, str)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
package resvault
|
||||
|
||||
import "github.com/onsonr/sonr/crypto/ucan"
|
||||
|
||||
func Build(ty ResVault, value string) ucan.Resource {
|
||||
return newStringLengthResource(ty.String(), value)
|
||||
}
|
||||
|
||||
type stringLengthRsc struct {
|
||||
t string
|
||||
v string
|
||||
}
|
||||
|
||||
// NewStringLengthResource is a silly implementation of resource to use while
|
||||
// I figure out what an OR filter on strings is. Don't use this.
|
||||
func newStringLengthResource(typ, val string) ucan.Resource {
|
||||
return stringLengthRsc{
|
||||
t: typ,
|
||||
v: val,
|
||||
}
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Type() string {
|
||||
return r.t
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Value() string {
|
||||
return r.v
|
||||
}
|
||||
|
||||
func (r stringLengthRsc) Contains(b ucan.Resource) bool {
|
||||
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/sonr/crypto/mpc"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/capability"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/policytype"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/resourcetype"
|
||||
)
|
||||
|
||||
// NewSmartAccount creates default attenuations for a smart account
|
||||
func NewSmartAccount(
|
||||
accountAddr string,
|
||||
) Attenuations {
|
||||
caps := AccountPermissions.GetCapabilities()
|
||||
return Attenuations{
|
||||
// Owner capabilities
|
||||
{Cap: caps.Cap(CapOwner.String()), Rsc: NewResource(ResAccount, accountAddr)},
|
||||
|
||||
// Operation capabilities
|
||||
{Cap: caps.Cap(capability.CAPEXECUTE.String()), Rsc: NewResource(ResTransaction, fmt.Sprintf("%s:*", accountAddr))},
|
||||
{Cap: caps.Cap(capability.CAPPROPOSE.String()), Rsc: NewResource(ResTransaction, fmt.Sprintf("%s:*", accountAddr))},
|
||||
{Cap: caps.Cap(capability.CAPSIGN.String()), Rsc: NewResource(ResTransaction, fmt.Sprintf("%s:*", accountAddr))},
|
||||
|
||||
// Policy capabilities
|
||||
{Cap: caps.Cap(capability.CAPSETPOLICY.String()), Rsc: NewResource(ResPolicy, fmt.Sprintf("%s:*", accountAddr))},
|
||||
{Cap: caps.Cap(capability.CAPSETTHRESHOLD.String()), Rsc: NewResource(ResPolicy, fmt.Sprintf("%s:threshold", accountAddr))},
|
||||
}
|
||||
}
|
||||
|
||||
// NewSmartAccountPolicy creates attenuations for policy management
|
||||
func NewSmartAccountPolicy(
|
||||
accountAddr string,
|
||||
policyType policytype.PolicyType,
|
||||
) Attenuations {
|
||||
caps := AccountPermissions.GetCapabilities()
|
||||
return Attenuations{
|
||||
{
|
||||
Cap: caps.Cap(capability.CAPSETPOLICY.String()),
|
||||
Rsc: NewResource(
|
||||
ResPolicy,
|
||||
fmt.Sprintf("%s:%s", accountAddr, policyType),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SmartAccountCapabilities defines the capability hierarchy
|
||||
func SmartAccountCapabilities() []string {
|
||||
return []string{
|
||||
CapOwner.String(),
|
||||
CapOperator.String(),
|
||||
CapObserver.String(),
|
||||
CapExecute.String(),
|
||||
CapPropose.String(),
|
||||
CapSign.String(),
|
||||
CapSetPolicy.String(),
|
||||
CapSetThreshold.String(),
|
||||
CapRecover.String(),
|
||||
CapSocial.String(),
|
||||
}
|
||||
}
|
||||
|
||||
// CreateVaultAttenuations creates default attenuations for a smart account
|
||||
func NewService(
|
||||
origin string,
|
||||
) Attenuations {
|
||||
caps := ServicePermissions.GetCapabilities()
|
||||
return Attenuations{
|
||||
// Owner capabilities
|
||||
{Cap: caps.Cap(capability.CAPOWNER.String()), Rsc: NewResource(resourcetype.RESACCOUNT, origin)},
|
||||
|
||||
// Operation capabilities
|
||||
{Cap: caps.Cap(capability.CAPEXECUTE.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", origin))},
|
||||
{Cap: caps.Cap(capability.CAPPROPOSE.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", origin))},
|
||||
{Cap: caps.Cap(capability.CAPSIGN.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", origin))},
|
||||
|
||||
// Policy capabilities
|
||||
{Cap: caps.Cap(capability.CAPSETPOLICY.String()), Rsc: NewResource(resourcetype.RESPOLICY, fmt.Sprintf("%s:*", origin))},
|
||||
{Cap: caps.Cap(capability.CAPSETTHRESHOLD.String()), Rsc: NewResource(resourcetype.RESPOLICY, fmt.Sprintf("%s:threshold", origin))},
|
||||
}
|
||||
}
|
||||
|
||||
// ServiceCapabilities defines the capability hierarchy
|
||||
func ServiceCapabilities() []string {
|
||||
return []string{
|
||||
CapOwner.String(),
|
||||
CapOperator.String(),
|
||||
CapObserver.String(),
|
||||
CapExecute.String(),
|
||||
CapPropose.String(),
|
||||
CapSign.String(),
|
||||
CapResolver.String(),
|
||||
CapProducer.String(),
|
||||
}
|
||||
}
|
||||
|
||||
// NewVault creates default attenuations for a smart account
|
||||
func NewVault(
|
||||
kss mpc.KeyEnclave,
|
||||
) Attenuations {
|
||||
accountAddr := kss.Address()
|
||||
caps := VaultPermissions.GetCapabilities()
|
||||
return Attenuations{
|
||||
// Owner capabilities
|
||||
{Cap: caps.Cap(capability.CAPOWNER.String()), Rsc: NewResource(resourcetype.RESACCOUNT, accountAddr)},
|
||||
|
||||
// Operation capabilities
|
||||
{Cap: caps.Cap(capability.CAPEXECUTE.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", accountAddr))},
|
||||
{Cap: caps.Cap(capability.CAPPROPOSE.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", accountAddr))},
|
||||
{Cap: caps.Cap(capability.CAPSIGN.String()), Rsc: NewResource(resourcetype.RESTRANSACTION, fmt.Sprintf("%s:*", accountAddr))},
|
||||
|
||||
// Policy capabilities
|
||||
{Cap: caps.Cap(capability.CAPSETPOLICY.String()), Rsc: NewResource(resourcetype.RESPOLICY, fmt.Sprintf("%s:*", accountAddr))},
|
||||
{Cap: caps.Cap(capability.CAPSETTHRESHOLD.String()), Rsc: NewResource(resourcetype.RESPOLICY, fmt.Sprintf("%s:threshold", accountAddr))},
|
||||
}
|
||||
}
|
||||
|
||||
// NewVaultPolicy creates attenuations for policy management
|
||||
func NewVaultPolicy(
|
||||
accountAddr string,
|
||||
policyType policytype.PolicyType,
|
||||
) Attenuations {
|
||||
caps := VaultPermissions.GetCapabilities()
|
||||
return Attenuations{
|
||||
{
|
||||
Cap: caps.Cap(capability.CAPSETPOLICY.String()),
|
||||
Rsc: NewResource(
|
||||
resourcetype.RESPOLICY,
|
||||
fmt.Sprintf("%s:%s", accountAddr, policyType),
|
||||
),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// VaultCapabilities defines the capability hierarchy
|
||||
func VaultCapabilities() []string {
|
||||
return []string{
|
||||
CapOwner.String(),
|
||||
CapOperator.String(),
|
||||
CapObserver.String(),
|
||||
CapAuthenticate.String(),
|
||||
CapAuthorize.String(),
|
||||
CapDelegate.String(),
|
||||
CapInvoke.String(),
|
||||
CapExecute.String(),
|
||||
CapRecover.String(),
|
||||
}
|
||||
}
|
||||
+57
-18
@@ -1,27 +1,66 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// CtxKey defines a distinct type for context keys used by the access
|
||||
// package
|
||||
type CtxKey string
|
||||
|
||||
// TokenCtxKey is the key for adding an access UCAN to a context.Context
|
||||
const TokenCtxKey CtxKey = "UCAN"
|
||||
|
||||
// CtxWithToken adds a UCAN value to a context
|
||||
func CtxWithToken(ctx context.Context, t Token) context.Context {
|
||||
return context.WithValue(ctx, TokenCtxKey, t)
|
||||
var EmptyAttenuation = Attenuation{
|
||||
Cap: Capability(nil),
|
||||
Rsc: Resource(nil),
|
||||
}
|
||||
|
||||
// FromCtx extracts a token from a given context if one is set, returning nil
|
||||
// otherwise
|
||||
func FromCtx(ctx context.Context) *Token {
|
||||
iface := ctx.Value(TokenCtxKey)
|
||||
if ref, ok := iface.(*Token); ok {
|
||||
return ref
|
||||
// Permissions represents the type of attenuation
|
||||
type Permissions string
|
||||
|
||||
const (
|
||||
// AccountPermissions represents the smart account attenuation
|
||||
AccountPermissions = Permissions("account")
|
||||
|
||||
// ServicePermissions represents the service attenuation
|
||||
ServicePermissions = Permissions("service")
|
||||
|
||||
// VaultPermissions represents the vault attenuation
|
||||
VaultPermissions = Permissions("vault")
|
||||
)
|
||||
|
||||
// Cap returns the capability for the given AttenuationPreset
|
||||
func (a Permissions) NewCap(c string) Capability {
|
||||
return a.GetCapabilities().Cap(c)
|
||||
}
|
||||
|
||||
// NestedCapabilities returns the nested capabilities for the given AttenuationPreset
|
||||
func (a Permissions) GetCapabilities() NestedCapabilities {
|
||||
var caps []string
|
||||
switch a {
|
||||
case AccountPermissions:
|
||||
// caps = SmartAccountCapabilities()
|
||||
case VaultPermissions:
|
||||
// caps = VaultCapabilities()
|
||||
}
|
||||
return nil
|
||||
return NewNestedCapabilities(caps...)
|
||||
}
|
||||
|
||||
// Equals returns true if the given AttenuationPreset is equal to the receiver
|
||||
func (a Permissions) Equals(b Permissions) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
// String returns the string representation of the AttenuationPreset
|
||||
func (a Permissions) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// ParseAttenuationData parses raw attenuation data into a structured format
|
||||
func ParseAttenuationData(data map[string]interface{}) (Permissions, map[string]interface{}, error) {
|
||||
typeRaw, ok := data["preset"]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("missing preset type in attenuation data")
|
||||
}
|
||||
|
||||
presetType, ok := typeRaw.(string)
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("invalid preset type format")
|
||||
}
|
||||
|
||||
return Permissions(presetType), data, nil
|
||||
}
|
||||
|
||||
@@ -1,164 +0,0 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/capability"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/policytype"
|
||||
"github.com/onsonr/sonr/crypto/ucan/attns/resourcetype"
|
||||
)
|
||||
|
||||
var EmptyAttenuation = Attenuation{
|
||||
Cap: Capability(nil),
|
||||
Rsc: Resource(nil),
|
||||
}
|
||||
|
||||
const (
|
||||
// Owner
|
||||
CapOwner = capability.CAPOWNER
|
||||
CapOperator = capability.CAPOPERATOR
|
||||
CapObserver = capability.CAPOBSERVER
|
||||
|
||||
// Auth
|
||||
CapAuthenticate = capability.CAPAUTHENTICATE
|
||||
CapAuthorize = capability.CAPAUTHORIZE
|
||||
CapDelegate = capability.CAPDELEGATE
|
||||
CapInvoke = capability.CAPINVOKE
|
||||
CapExecute = capability.CAPEXECUTE
|
||||
CapPropose = capability.CAPPROPOSE
|
||||
CapSign = capability.CAPSIGN
|
||||
CapSetPolicy = capability.CAPSETPOLICY
|
||||
CapSetThreshold = capability.CAPSETTHRESHOLD
|
||||
CapRecover = capability.CAPRECOVER
|
||||
CapSocial = capability.CAPSOCIAL
|
||||
CapResolver = capability.CAPRESOLVER
|
||||
CapProducer = capability.CAPPRODUCER
|
||||
|
||||
// Resources
|
||||
ResAccount = resourcetype.RESACCOUNT
|
||||
ResTransaction = resourcetype.RESTRANSACTION
|
||||
ResPolicy = resourcetype.RESPOLICY
|
||||
ResRecovery = resourcetype.RESRECOVERY
|
||||
ResVault = resourcetype.RESVAULT
|
||||
ResIPFS = resourcetype.RESIPFS
|
||||
ResIPNS = resourcetype.RESIPNS
|
||||
ResKeyShare = resourcetype.RESKEYSHARE
|
||||
|
||||
// PolicyTypes
|
||||
PolicyThreshold = policytype.POLICYTHRESHOLD
|
||||
PolicyTimelock = policytype.POLICYTIMELOCK
|
||||
PolicyWhitelist = policytype.POLICYWHITELIST
|
||||
PolicyKeyShare = policytype.POLICYKEYGEN
|
||||
)
|
||||
|
||||
// NewVaultResource creates a new resource identifier
|
||||
func NewResource(resType resourcetype.ResourceType, path string) Resource {
|
||||
return NewStringLengthResource(string(resType), path)
|
||||
}
|
||||
|
||||
// Permissions represents the type of attenuation
|
||||
type Permissions string
|
||||
|
||||
const (
|
||||
// AccountPermissions represents the smart account attenuation
|
||||
AccountPermissions = Permissions("account")
|
||||
|
||||
// ServicePermissions represents the service attenuation
|
||||
ServicePermissions = Permissions("service")
|
||||
|
||||
// VaultPermissions represents the vault attenuation
|
||||
VaultPermissions = Permissions("vault")
|
||||
)
|
||||
|
||||
// Cap returns the capability for the given AttenuationPreset
|
||||
func (a Permissions) NewCap(c capability.Capability) Capability {
|
||||
return a.GetCapabilities().Cap(c.String())
|
||||
}
|
||||
|
||||
// NestedCapabilities returns the nested capabilities for the given AttenuationPreset
|
||||
func (a Permissions) GetCapabilities() NestedCapabilities {
|
||||
var caps []string
|
||||
switch a {
|
||||
case AccountPermissions:
|
||||
caps = SmartAccountCapabilities()
|
||||
case VaultPermissions:
|
||||
caps = VaultCapabilities()
|
||||
}
|
||||
return NewNestedCapabilities(caps...)
|
||||
}
|
||||
|
||||
// Equals returns true if the given AttenuationPreset is equal to the receiver
|
||||
func (a Permissions) Equals(b Permissions) bool {
|
||||
return a == b
|
||||
}
|
||||
|
||||
// String returns the string representation of the AttenuationPreset
|
||||
func (a Permissions) String() string {
|
||||
return string(a)
|
||||
}
|
||||
|
||||
// GetConstructor returns the AttenuationConstructorFunc for a Permission
|
||||
func (a Permissions) GetConstructor() AttenuationConstructorFunc {
|
||||
return NewAttenuationFromPreset(a)
|
||||
}
|
||||
|
||||
// NewAttenuationFromPreset creates an AttenuationConstructorFunc for the given preset
|
||||
func NewAttenuationFromPreset(preset Permissions) AttenuationConstructorFunc {
|
||||
return func(v map[string]interface{}) (Attenuation, error) {
|
||||
// Extract capability and resource from map
|
||||
capStr, ok := v["cap"].(string)
|
||||
if !ok {
|
||||
return EmptyAttenuation, fmt.Errorf("missing or invalid capability in attenuation data")
|
||||
}
|
||||
|
||||
resType, ok := v["type"].(string)
|
||||
if !ok {
|
||||
return EmptyAttenuation, fmt.Errorf("missing or invalid resource type in attenuation data")
|
||||
}
|
||||
|
||||
path, ok := v["path"].(string)
|
||||
if !ok {
|
||||
path = "/" // Default path if not specified
|
||||
}
|
||||
|
||||
// Create capability from preset
|
||||
cap := preset.NewCap(capability.Capability(capStr))
|
||||
if cap == nil {
|
||||
return EmptyAttenuation, fmt.Errorf("invalid capability %s for preset %s", capStr, preset)
|
||||
}
|
||||
|
||||
// Create resource
|
||||
resource := NewResource(resourcetype.ResourceType(resType), path)
|
||||
|
||||
return Attenuation{
|
||||
Cap: cap,
|
||||
Rsc: resource,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// GetPresetConstructor returns the appropriate AttenuationConstructorFunc for a given type
|
||||
func GetPresetConstructor(attType string) (AttenuationConstructorFunc, error) {
|
||||
preset := Permissions(attType)
|
||||
switch preset {
|
||||
case AccountPermissions, ServicePermissions, VaultPermissions:
|
||||
return NewAttenuationFromPreset(preset), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown attenuation preset: %s", attType)
|
||||
}
|
||||
}
|
||||
|
||||
// ParseAttenuationData parses raw attenuation data into a structured format
|
||||
func ParseAttenuationData(data map[string]interface{}) (Permissions, map[string]interface{}, error) {
|
||||
typeRaw, ok := data["preset"]
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("missing preset type in attenuation data")
|
||||
}
|
||||
|
||||
presetType, ok := typeRaw.(string)
|
||||
if !ok {
|
||||
return "", nil, fmt.Errorf("invalid preset type format")
|
||||
}
|
||||
|
||||
return Permissions(presetType), data, nil
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
package ucan
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestAttenuationPresetConstructor(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
data map[string]interface{}
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid smart account attenuation",
|
||||
data: map[string]interface{}{
|
||||
"preset": "account",
|
||||
"cap": string(CapOwner),
|
||||
"type": string(ResAccount),
|
||||
"path": "/accounts/123",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "valid vault attenuation",
|
||||
data: map[string]interface{}{
|
||||
"preset": "vault",
|
||||
"cap": string(CapOperator),
|
||||
"type": string(ResVault),
|
||||
"path": "/vaults/456",
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
preset, data, err := ParseAttenuationData(tt.data)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
constructor, err := GetPresetConstructor(preset.String())
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
|
||||
attenuation, err := constructor(data)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, attenuation)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user