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:
Prad Nukala
2024-12-16 20:29:54 +00:00
committed by GitHub
parent 6d27b926f6
commit 7c4586ce90
196 changed files with 4480 additions and 3192 deletions
+5
View File
@@ -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
View File
@@ -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) {
+20
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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.
+3 -3
View File
@@ -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
View File
@@ -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)
}
-26
View File
@@ -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
-96
View File
@@ -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
}
+11
View File
@@ -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
}
+11
View File
@@ -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
}
+11
View File
@@ -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()
}
+114
View File
@@ -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
}
+33
View File
@@ -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
}
+33
View File
@@ -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())
}
-150
View File
@@ -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
View File
@@ -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
}
-164
View File
@@ -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
}
-62
View File
@@ -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)
})
}
}