feature/1120 leverage service authorization (#1188)

* refactor: remove redundant branch trigger for scheduled releases

* refactor: simplify process-compose commands and improve logging

* refactor: remove redundant  command

* refactor: remove unused error variables and simplify database configuration

* feat: introduce task runner for project automation

* refactor: Remove hardcoded action and method from form components

* refactor: move server setup to main.go and add prometheus metrics

* refactor: move index handlers to render handlers

* refactor: improve user identification logic in gateway and vault handlers

* refactor: rename TitleDescription to TitleDesc for consistency

* feat: integrate go-useragent library for enhanced user agent parsing

* feat: enhance initial view rendering based on device type

* feat: Add support for PostgreSQL database

* fix: Use formatPsqlDSN() to properly set PostgreSQL DSN from command flags

* feat: Add PostgreSQL support with fallback to SQLite in NewGormDB

* feat: Add PostgreSQL connection validation with SQLite fallback

* chore: update golang.org/x/crypto dependency to v0.31.0

* feat: add PKL-based configuration initialization

* refactor: improve file naming consistency in cmd/sonrd

* refactor: Improve init-pkl command with safer config file generation and error handling

* fix: add logging for pkl evaluation results

* refactor: Move credential handling to gateway context

* refactor: Migrate session models to gateway package

* refactor: rename models and update User model

* chore: initial commit for address and pubkey functionality

* refactor: move pubkey package to keys package

* refactor: Rename models and add resolver service

* feat: add gRPC clients for bank, DID, DWN, and SVC modules

* refactor: Migrate title and description components from text package to hero package

* refactor: improve file naming conventions

* feat: add user credential validation

* refactor: rename registration handlers and routes for clarity

* <no value>

* refactor: Decouple database and IPFS interactions from server setup

* refactor: Migrate configuration from class-based to TOML-based structure

* refactor: move network configuration files to sonr.net module

* feature/1120-leverage-service-authorization

* fix: correct DID identifier creation function name

* feat: add compressed and uncompressed public keys to keyset

* refactor: move address packages to crypto/address

* feat: implement pubkey verification

* refactor: remove ECDSA-related functions from keyshare and protocol modules

* feat: Implement ECDSA signature serialization

* <no value>

* feat: add vault service for IPFS token storage

* refactor: update ucan codec to use new DID generation method

* refactor: refactor key management and move address parsers to keys package

* refactor: rename key parsers and move to parsers package

* fix: resolved import issues with the new spec

* feat: improve user onboarding experience by updating button text and functionality

* refactor: update point marshaling and unmarshaling methods to use JSON

* refactor: remove unnecessary DID method from PubKey

* refactor: Rename and refactor MPC key generation functions

* test: Add comprehensive test suite for keyshare generation and validation

* test: Fix keyshare role validation and encoding tests

* feat: Update key share role tests with enclave initialization validation

* test(mpc): refactor tests to focus on public API and remove internal role checks

* refactor: Remove unnecessary role check in initKeyEnclave function

* fix: Enforce strict order for validator and user keyshares in enclave initialization

* fix: Update codec_test to match latest codec implementation

* refactor: Update KeyEnclave to use string-based key shares and improve error handling

* fix: Refactor MPC enclave to use string-based encoding and simplify key management

* refactor: Remove redundant keyshare decoding tests in codec_test.go

* fix: Resolve type conversion issues in MPC crypto enclave initialization

* fix: Convert CID to byte slice in addEnclaveIPFS function

* fix: Resolve type conversion and constant definition errors in MPC crypto utils

* refactor: Simplify KeyShare encoding and role handling in MPC codec

* fix: Resolve JSON unmarshaling type mismatch in KeyShare.Message()

* fix: Refactor KeyEnclave to use struct and Enclave interface

* fix: Resolve type and naming conflicts in MPC crypto package

* refactor: Update codec_test.go to use new KeyEnclave struct fields

* refactor: remove keyshare encoding and decoding logic

* refactor: Remove unused JSON marshaling functions for curve points

* fix: Improve signature serialization and deserialization in MPC crypto

This commit addresses several issues with signature handling:
- Fixed signature length to 65 bytes
- Added proper padding for R and S values
- Added nil and zero value checks
- Improved error messages for signature parsing

The changes ensure more robust signature encoding and decoding, preventing potential nil pointer and invalid signature issues.

* fix: Update signature serialization to match protocol test approach

* refactor: Simplify KeyEnclave struct and improve message handling

* fix: Improve signature serialization and verification in MPC crypto module

* refactor: Simplify enclave validation using IsValid method in test

* refactor: Add marshaling and comprehensive tests for KeyEnclave

* feat: Add JSON marshaling support for Point in KeyEnclave

* refactor: Rename KeyEnclave to Enclave and update related functions

* refactor: Update PubKey verification to use SHA3-256 hashing

* test: Add comprehensive tests for DID and PubKey implementations

* refactor: simplify DID key retrieval

* test: refactor CI workflow and remove unused DIDAuth middleware

* The changes look good! The updated workflows will now:

1. Run tests on push to master
2. Bump the version if the commit doesn't already start with 'bump:'
3. Trigger a release workflow automatically with the new version tag
4. Create and publish the release

A few things to note:
- Make sure you have the `peter-evans/repository-dispatch` action installed/available
- The `commitizen-tools/commitizen-action` should output the new tag for this to work
- Ensure your release workflow can handle the repository dispatch event

Would you like me to review or suggest any additional modifications to the workflows?

* ci(github actions): add build stage dependency for tests

* fix(workflow): update workflow to trigger on PR edits

* test: Update unit test dependencies

* ci: Add GoReleaser dry-run check for merge group events

* test: remove unnecessary dependencies between test jobs

* ci: Make race and coverage tests depend on build tests
This commit is contained in:
Prad Nukala
2024-12-13 20:10:27 +00:00
committed by GitHub
parent ee12bf684e
commit ac8c153b28
132 changed files with 2879 additions and 3397 deletions
@@ -1 +0,0 @@
package capability
@@ -1,33 +0,0 @@
package resourcetype
// Resource is a unique identifier for a thing, usually stored state. Resources
// are organized by string types
type Resource interface {
Type() ResourceType
Value() string
Contains(b Resource) bool
}
type stringResource struct {
t ResourceType
v string
}
func (r stringResource) Type() ResourceType {
return r.t
}
func (r stringResource) Value() string {
return r.v
}
func (r stringResource) Contains(b Resource) bool {
return r.Type() == b.Type() && len(r.Value()) <= len(b.Value())
}
func NewResource(typ ResourceType, val string) Resource {
return stringResource{
t: typ,
v: val,
}
}
+2 -5
View File
@@ -98,12 +98,9 @@ func ServiceCapabilities() []string {
// NewVault creates default attenuations for a smart account
func NewVault(
kss mpc.Keyset,
kss mpc.KeyEnclave,
) Attenuations {
accountAddr, err := mpc.ComputeSonrAddr(kss.User().GetPublicKey())
if err != nil {
return nil
}
accountAddr := kss.Address()
caps := VaultPermissions.GetCapabilities()
return Attenuations{
// Owner capabilities
-157
View File
@@ -1,157 +0,0 @@
package didkey
import (
"crypto/ed25519"
"crypto/rsa"
"crypto/x509"
"fmt"
"strings"
"github.com/libp2p/go-libp2p/core/crypto"
mb "github.com/multiformats/go-multibase"
varint "github.com/multiformats/go-varint"
)
const (
// KeyPrefix indicates a decentralized identifier that uses the key method
KeyPrefix = "did:key"
// MulticodecKindRSAPubKey rsa-x509-pub https://github.com/multiformats/multicodec/pull/226
MulticodecKindRSAPubKey = 0x1205
// MulticodecKindEd25519PubKey ed25519-pub
MulticodecKindEd25519PubKey = 0xed
// MulticodecKindSecp256k1PubKey secp256k1-pub
MulticodecKindSecp256k1PubKey = 0x1206
)
// ID is a DID:key identifier
type ID struct {
crypto.PubKey
}
// NewID constructs an Identifier from a public key
func NewID(pub crypto.PubKey) (ID, error) {
switch pub.Type() {
case crypto.Ed25519, crypto.RSA, crypto.Secp256k1:
return ID{PubKey: pub}, nil
default:
return ID{}, fmt.Errorf("unsupported key type: %s", pub.Type())
}
}
// MulticodecType indicates the type for this multicodec
func (id ID) MulticodecType() uint64 {
switch id.Type() {
case crypto.RSA:
return MulticodecKindRSAPubKey
case crypto.Ed25519:
return MulticodecKindEd25519PubKey
case crypto.Secp256k1:
return MulticodecKindSecp256k1PubKey
default:
panic("unexpected crypto type")
}
}
// String returns this did:key formatted as a string
func (id ID) String() string {
raw, err := id.Raw()
if err != nil {
return ""
}
t := id.MulticodecType()
size := varint.UvarintSize(t)
data := make([]byte, size+len(raw))
n := varint.PutUvarint(data, t)
copy(data[n:], raw)
b58BKeyStr, err := mb.Encode(mb.Base58BTC, data)
if err != nil {
return ""
}
return fmt.Sprintf("%s:%s", KeyPrefix, b58BKeyStr)
}
// VerifyKey returns the backing implementation for a public key, one of:
// *rsa.PublicKey, ed25519.PublicKey
func (id ID) VerifyKey() (interface{}, error) {
rawPubBytes, err := id.PubKey.Raw()
if err != nil {
return nil, err
}
switch id.PubKey.Type() {
case crypto.RSA:
verifyKeyiface, err := x509.ParsePKIXPublicKey(rawPubBytes)
if err != nil {
return nil, err
}
verifyKey, ok := verifyKeyiface.(*rsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not an RSA key. got type: %T", verifyKeyiface)
}
return verifyKey, nil
case crypto.Ed25519:
return ed25519.PublicKey(rawPubBytes), nil
case crypto.Secp256k1:
// Handle both compressed and uncompressed Secp256k1 public keys
if len(rawPubBytes) == 65 || len(rawPubBytes) == 33 {
return rawPubBytes, nil
}
return nil, fmt.Errorf("invalid Secp256k1 public key length: %d", len(rawPubBytes))
default:
return nil, fmt.Errorf("unrecognized Public Key type: %s", id.PubKey.Type())
}
}
// Parse turns a string into a key method ID
func Parse(keystr string) (ID, error) {
var id ID
if !strings.HasPrefix(keystr, KeyPrefix) {
return id, fmt.Errorf("decentralized identifier is not a 'key' type")
}
keystr = strings.TrimPrefix(keystr, KeyPrefix+":")
enc, data, err := mb.Decode(keystr)
if err != nil {
return id, fmt.Errorf("decoding multibase: %w", err)
}
if enc != mb.Base58BTC {
return id, fmt.Errorf("unexpected multibase encoding: %s", mb.EncodingToStr[enc])
}
keyType, n, err := varint.FromUvarint(data)
if err != nil {
return id, err
}
switch keyType {
case MulticodecKindRSAPubKey:
pub, err := crypto.UnmarshalRsaPublicKey(data[n:])
if err != nil {
return id, err
}
return ID{pub}, nil
case MulticodecKindEd25519PubKey:
pub, err := crypto.UnmarshalEd25519PublicKey(data[n:])
if err != nil {
return id, err
}
return ID{pub}, nil
case MulticodecKindSecp256k1PubKey:
// Handle both compressed and uncompressed formats
keyData := data[n:]
if len(keyData) != 33 && len(keyData) != 65 {
return id, fmt.Errorf("invalid Secp256k1 public key length: %d", len(keyData))
}
pub, err := crypto.UnmarshalSecp256k1PublicKey(keyData)
if err != nil {
return id, fmt.Errorf("failed to unmarshal Secp256k1 key: %w", err)
}
return ID{pub}, nil
}
return id, fmt.Errorf("unrecognized key type multicodec prefix: %x", data[0])
}
-86
View File
@@ -1,86 +0,0 @@
package didkey
import (
"log"
"testing"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/onsonr/sonr/crypto/mpc"
)
func TestID_Parse(t *testing.T) {
keyStrED := "did:key:z6MkpTHR8VNsBxYAAWHut2Geadd9jSwuBV8xRoAnwWsdvktH"
id, err := Parse(keyStrED)
if err != nil {
t.Fatal(err)
}
if id.String() != keyStrED {
t.Errorf("string mismatch.\nwant: %q\ngot: %q", keyStrED, id.String())
}
keyStrRSA := "did:key:z2MGw4gk84USotaWf4AkJ83DcnrfgGaceF86KQXRYMfQ7xqnUFp38UZ6Le8JPfkb4uCLGjHBzKpjEXb9hx9n2ftecQWCHXKtKszkke4FmENdTZ7i9sqRmL3pLnEEJ774r3HMuuC7tNRQ6pqzrxatXx2WinCibdhUmvh3FobnA9ygeqkSGtV6WLa7NVFw9cAvnv8Y6oHcaoZK7fNP4ASGs6AHmSC6ydSR676aKYMe95QmEAj4xJptDsSxG7zLAGzAdwCgm56M4fTno8GdWNmU6Pdghnuf6fWyYus9ASwdfwyaf3SDf4uo5T16PRJssHkQh6DJHfK4Rka7RNQLjzfGBPjFLHbUSvmf4EdbHasbVaveAArD68ZfazRCCvjdovQjWr6uyLCwSAQLPUFZBTT8mW"
id, err = Parse(keyStrRSA)
if err != nil {
t.Fatal(err)
}
if id.String() != keyStrRSA {
t.Errorf("string mismatch.\nwant: %q\ngot: %q", keyStrRSA, id.String())
}
}
func TestID_FromMPCKey(t *testing.T) {
// Generate new MPC keyset
ks, err := mpc.NewKeyset()
if err != nil {
t.Fatalf("failed to generate MPC keyset: %v", err)
}
// Get public key from validator share
pubKey := ks.Val().PublicKey()
if len(pubKey) != 65 {
t.Fatalf("expected 65-byte uncompressed public key, got %d bytes", len(pubKey))
}
// Create crypto.PubKey from raw bytes
cryptoPubKey, err := crypto.UnmarshalSecp256k1PublicKey(pubKey)
if err != nil {
t.Fatalf("failed to unmarshal public key: %v", err)
}
// Create DID Key ID
id, err := NewID(cryptoPubKey)
if err != nil {
t.Fatalf("failed to create DID Key ID: %v", err)
}
log.Printf("%s\n", id.String())
// Verify the key can be parsed back
parsed, err := Parse(id.String())
if err != nil {
t.Fatalf("failed to parse DID Key string: %v", err)
}
// Verify the parsed key matches original
if parsed.String() != id.String() {
t.Errorf("parsed key doesn't match original.\nwant: %q\ngot: %q",
id.String(), parsed.String())
}
// Verify we can get back a valid verify key
verifyKey, err := id.VerifyKey()
if err != nil {
t.Fatalf("failed to get verify key: %v", err)
}
// Verify the key is the right type and length
rawKey, ok := verifyKey.([]byte)
if !ok {
t.Fatalf("expected []byte verify key, got %T", verifyKey)
}
if len(rawKey) != 65 && len(rawKey) != 33 {
t.Errorf("invalid key length %d, expected 65 or 33 bytes", len(rawKey))
}
}
-98
View File
@@ -1,98 +0,0 @@
package spec
import (
"crypto/sha256"
"encoding/base64"
"fmt"
"github.com/golang-jwt/jwt"
"github.com/onsonr/sonr/crypto/mpc"
)
// MPCSigningMethod implements the SigningMethod interface for MPC-based signing
type MPCSigningMethod struct {
Name string
ks ucanKeyshare
}
// NewJWTSigningMethod creates a new MPC signing method with the given keyshare source
func NewJWTSigningMethod(name string, ks ucanKeyshare) *MPCSigningMethod {
return &MPCSigningMethod{
Name: name,
ks: ks,
}
}
// Alg returns the signing method's name
func (m *MPCSigningMethod) Alg() string {
return m.Name
}
// Verify verifies the signature using the MPC public key
func (m *MPCSigningMethod) Verify(signingString, signature string, key interface{}) error {
// Decode the signature
sig, err := base64.RawURLEncoding.DecodeString(signature)
if err != nil {
return err
}
// Hash the signing string
hasher := sha256.New()
hasher.Write([]byte(signingString))
digest := hasher.Sum(nil)
// Verify using the keyshare's public key
valid, err := mpc.VerifySignature(m.ks.valShare.PublicKey(), digest, sig)
if err != nil {
return fmt.Errorf("failed to verify signature: %w", err)
}
if !valid {
return fmt.Errorf("invalid signature")
}
return nil
}
// Sign signs the data using MPC
func (m *MPCSigningMethod) Sign(signingString string, key interface{}) (string, error) {
// Hash the signing string
hasher := sha256.New()
hasher.Write([]byte(signingString))
digest := hasher.Sum(nil)
// Create signing functions
signFunc, err := m.ks.userShare.SignFunc(digest)
if err != nil {
return "", fmt.Errorf("failed to create sign function: %w", err)
}
valSignFunc, err := m.ks.valShare.SignFunc(digest)
if err != nil {
return "", fmt.Errorf("failed to create validator sign function: %w", err)
}
// Run the signing protocol
sig, err := mpc.ExecuteSigning(valSignFunc, signFunc)
if err != nil {
return "", fmt.Errorf("failed to run sign protocol: %w", err)
}
// Serialize the signature
sigBytes, err := mpc.SerializeSignature(sig)
if err != nil {
return "", fmt.Errorf("failed to serialize signature: %w", err)
}
// Encode the signature
encoded := base64.RawURLEncoding.EncodeToString(sigBytes)
return encoded, nil
}
func init() {
// Register the MPC signing method
jwt.RegisterSigningMethod("MPC256", func() jwt.SigningMethod {
return &MPCSigningMethod{
Name: "MPC256",
}
})
}
-132
View File
@@ -1,132 +0,0 @@
package spec
import (
"context"
"fmt"
"time"
"github.com/onsonr/sonr/crypto/mpc"
"github.com/onsonr/sonr/crypto/ucan"
"github.com/onsonr/sonr/crypto/ucan/didkey"
"lukechampine.com/blake3"
)
type KeyshareSource interface {
ucan.Source
Address() string
Issuer() string
ChainCode() ([]byte, error)
OriginToken() (*Token, error)
SignData(data []byte) ([]byte, error)
VerifyData(data []byte, sig []byte) (bool, error)
UCANParser() *ucan.TokenParser
}
func NewSource(ks mpc.Keyset) (KeyshareSource, error) {
val := ks.Val()
user := ks.User()
iss, addr, err := ComputeIssuerDID(val.GetPublicKey())
if err != nil {
return nil, err
}
return ucanKeyshare{
userShare: user,
valShare: val,
addr: addr,
issuerDID: iss,
}, nil
}
// Address returns the address of the keyshare
func (k ucanKeyshare) Address() string {
return k.addr
}
// Issuer returns the DID of the issuer of the keyshare
func (k ucanKeyshare) Issuer() string {
return k.issuerDID
}
// ChainCode returns the chain code of the keyshare
func (k ucanKeyshare) ChainCode() ([]byte, error) {
sig, err := k.SignData([]byte(k.addr))
if err != nil {
return nil, err
}
hash := blake3.Sum256(sig)
// Return the first 32 bytes of the hash
return hash[:32], nil
}
// DefaultOriginToken returns a default token with the keyshare's issuer as the audience
func (k ucanKeyshare) OriginToken() (*Token, error) {
att := ucan.NewSmartAccount(k.addr)
zero := time.Time{}
return k.NewOriginToken(k.issuerDID, att, nil, zero, zero)
}
func (k ucanKeyshare) SignData(data []byte) ([]byte, error) {
// Create signing functions
signFunc, err := k.userShare.SignFunc(data)
if err != nil {
return nil, fmt.Errorf("failed to create sign function: %w", err)
}
valSignFunc, err := k.valShare.SignFunc(data)
if err != nil {
return nil, fmt.Errorf("failed to create validator sign function: %w", err)
}
// Run the signing protocol
sig, err := mpc.ExecuteSigning(valSignFunc, signFunc)
if err != nil {
return nil, fmt.Errorf("failed to run sign protocol: %w", err)
}
return mpc.SerializeSignature(sig)
}
func (k ucanKeyshare) VerifyData(data []byte, sig []byte) (bool, error) {
return mpc.VerifySignature(k.userShare.PublicKey(), data, sig)
}
// TokenParser returns a token parser that can be used to parse tokens
func (k ucanKeyshare) UCANParser() *ucan.TokenParser {
caps := ucan.AccountPermissions.GetCapabilities()
ac := func(m map[string]interface{}) (ucan.Attenuation, error) {
var (
cap string
rsc ucan.Resource
)
for key, vali := range m {
val, ok := vali.(string)
if !ok {
return ucan.Attenuation{}, fmt.Errorf(`expected attenuation value to be a string`)
}
if key == ucan.CapKey {
cap = val
} else {
rsc = ucan.NewStringLengthResource(key, val)
}
}
return ucan.Attenuation{
Rsc: rsc,
Cap: caps.Cap(cap),
}, nil
}
store := ucan.NewMemTokenStore()
return ucan.NewTokenParser(ac, customDIDPubKeyResolver{}, store.(ucan.CIDBytesResolver))
}
// customDIDPubKeyResolver implements the DIDPubKeyResolver interface without
// any network backing. Works if the key string given contains the public key
// itself
type customDIDPubKeyResolver struct{}
// ResolveDIDKey extracts a public key from a did:key string
func (customDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (didkey.ID, error) {
return didkey.Parse(didStr)
}
-116
View File
@@ -1,116 +0,0 @@
// go:build jwx_es256k
package spec
import (
"fmt"
"time"
"github.com/cosmos/cosmos-sdk/types/bech32"
"github.com/golang-jwt/jwt"
"github.com/onsonr/sonr/crypto/mpc"
"github.com/onsonr/sonr/crypto/ucan"
)
type (
Token = ucan.Token
Claims = ucan.Claims
Proof = ucan.Proof
Attenuations = ucan.Attenuations
Fact = ucan.Fact
)
var (
UCANVersion = ucan.UCANVersion
UCANVersionKey = ucan.UCANVersionKey
PrfKey = ucan.PrfKey
FctKey = ucan.FctKey
AttKey = ucan.AttKey
CapKey = ucan.CapKey
)
type ucanKeyshare struct {
userShare *mpc.UserKeyshare
valShare *mpc.ValKeyshare
addr string
issuerDID string
}
func (k ucanKeyshare) NewOriginToken(audienceDID string, att Attenuations, fct []Fact, notBefore, expires time.Time) (*ucan.Token, error) {
return k.newToken(audienceDID, nil, att, fct, notBefore, expires)
}
func (k ucanKeyshare) NewAttenuatedToken(parent *Token, audienceDID string, att ucan.Attenuations, fct []ucan.Fact, nbf, exp time.Time) (*Token, error) {
if !parent.Attenuations.Contains(att) {
return nil, fmt.Errorf("scope of ucan attenuations must be less than it's parent")
}
return k.newToken(audienceDID, append(parent.Proofs, Proof(parent.Raw)), att, fct, nbf, exp)
}
func (k ucanKeyshare) newToken(audienceDID string, prf []Proof, att Attenuations, fct []Fact, nbf, exp time.Time) (*ucan.Token, error) {
t := jwt.New(NewJWTSigningMethod("MPC256", k))
// if _, err := did.Parse(audienceDID); err != nil {
// return nil, fmt.Errorf("invalid audience DID: %w", err)
// }
t.Header[UCANVersionKey] = UCANVersion
var (
nbfUnix int64
expUnix int64
)
if !nbf.IsZero() {
nbfUnix = nbf.Unix()
}
if !exp.IsZero() {
expUnix = exp.Unix()
}
// set our claims
t.Claims = &Claims{
StandardClaims: &jwt.StandardClaims{
Issuer: k.issuerDID,
Audience: audienceDID,
NotBefore: nbfUnix,
// set the expire time
// see http://tools.ietf.org/html/draft-ietf-oauth-json-web-token-20#section-4.1.4
ExpiresAt: expUnix,
},
Attenuations: att,
Facts: fct,
Proofs: prf,
}
raw, err := t.SignedString(nil)
if err != nil {
return nil, err
}
return &Token{
Raw: raw,
Attenuations: att,
Facts: fct,
Proofs: prf,
}, nil
}
// ComputeIssuerDID computes the issuer DID from a public key
func ComputeIssuerDID(pk []byte) (string, string, error) {
addr, err := ComputeSonrAddr(pk)
if err != nil {
return "", "", err
}
return fmt.Sprintf("did:sonr:%s", addr), addr, nil
}
// ComputeSonrAddr computes the Sonr address from a public key
func ComputeSonrAddr(pk []byte) (string, error) {
sonrAddr, err := bech32.ConvertAndEncode("idx", pk)
if err != nil {
return "", err
}
return sonrAddr, nil
}
-143
View File
@@ -1,143 +0,0 @@
package store
import (
"context"
"fmt"
"sort"
"sync"
"github.com/golang-jwt/jwt"
"github.com/ipfs/go-cid"
"github.com/onsonr/sonr/crypto/ucan"
"github.com/onsonr/sonr/crypto/ucan/didkey"
"github.com/onsonr/sonr/pkg/common/ipfs"
)
type IPFSTokenStore interface {
ucan.TokenStore
ResolveCIDBytes(ctx context.Context, id cid.Cid) ([]byte, error)
ResolveDIDKey(ctx context.Context, did string) (didkey.ID, error)
}
// ipfsTokenStore is a token store that uses IPFS to store tokens. It uses the memory store as a cache
// for CID strings to be used as keys for retrieving tokens.
type ipfsTokenStore struct {
sync.Mutex
ipfs ipfs.Client
cache map[string]string
}
// NewIPFSTokenStore creates a new IPFS-backed token store
func NewIPFSTokenStore(ipfsClient ipfs.Client) IPFSTokenStore {
return &ipfsTokenStore{
ipfs: ipfsClient,
cache: make(map[string]string),
}
}
func (st *ipfsTokenStore) PutToken(ctx context.Context, key string, raw string) error {
// Validate token format
p := &jwt.Parser{
UseJSONNumber: true,
SkipClaimsValidation: false,
}
if _, _, err := p.ParseUnverified(raw, jwt.MapClaims{}); err != nil {
return fmt.Errorf("%w: %s", ucan.ErrInvalidToken, err)
}
// Store token in IPFS
cid, err := st.ipfs.Add([]byte(raw))
if err != nil {
return fmt.Errorf("failed to store token in IPFS: %w", err)
}
// Update cache
st.Lock()
defer st.Unlock()
st.cache[key] = cid
return nil
}
func (st *ipfsTokenStore) RawToken(ctx context.Context, key string) (string, error) {
st.Lock()
cid, exists := st.cache[key]
st.Unlock()
if !exists {
return "", ucan.ErrTokenNotFound
}
// Retrieve token from IPFS
data, err := st.ipfs.Get(cid)
if err != nil {
return "", fmt.Errorf("failed to retrieve token from IPFS: %w", err)
}
return string(data), nil
}
func (st *ipfsTokenStore) DeleteToken(ctx context.Context, key string) error {
st.Lock()
defer st.Unlock()
cid, exists := st.cache[key]
if !exists {
return ucan.ErrTokenNotFound
}
// Unpin from IPFS
if err := st.ipfs.Unpin(cid); err != nil {
return fmt.Errorf("failed to unpin token from IPFS: %w", err)
}
delete(st.cache, key)
return nil
}
func (st *ipfsTokenStore) ListTokens(ctx context.Context, offset, limit int) ([]ucan.RawToken, error) {
st.Lock()
defer st.Unlock()
tokens := make(ucan.RawTokens, 0, len(st.cache))
for key, cid := range st.cache {
data, err := st.ipfs.Get(cid)
if err != nil {
continue // Skip invalid tokens
}
tokens = append(tokens, ucan.RawToken{
Key: key,
Raw: string(data),
})
}
// Sort tokens
sort.Sort(tokens)
// Apply pagination
if offset >= len(tokens) {
return []ucan.RawToken{}, nil
}
end := offset + limit
if end > len(tokens) || limit <= 0 {
end = len(tokens)
}
return tokens[offset:end], nil
}
func (st *ipfsTokenStore) ResolveCIDBytes(ctx context.Context, id cid.Cid) ([]byte, error) {
data, err := st.ipfs.Get(id.String())
if err != nil {
return nil, fmt.Errorf("failed to resolve CID bytes: %w", err)
}
return data, nil
}
func (st *ipfsTokenStore) ResolveDIDKey(ctx context.Context, did string) (didkey.ID, error) {
id, err := didkey.Parse(did)
if err != nil {
return didkey.ID{}, fmt.Errorf("failed to parse DID: %w", err)
}
return id, nil
}
+11 -11
View File
@@ -21,7 +21,7 @@ import (
"github.com/ipfs/go-cid"
"github.com/libp2p/go-libp2p/core/crypto"
mh "github.com/multiformats/go-multihash"
"github.com/onsonr/sonr/crypto/ucan/didkey"
"github.com/onsonr/sonr/crypto/keys"
)
// ErrInvalidToken indicates an access token is invalid
@@ -47,8 +47,8 @@ const (
type Token struct {
// Entire UCAN as a signed JWT string
Raw string
Issuer didkey.ID
Audience didkey.ID
Issuer keys.DID
Audience keys.DID
// the "inputs" to this token, a chain UCAN tokens with broader scopes &
// deadlines than this token
Proofs []Proof `json:"prf,omitempty"`
@@ -261,12 +261,12 @@ func (a *pkSource) newToken(audienceDID string, prf []Proof, att Attenuations, f
// DIDPubKeyResolver turns did:key Decentralized IDentifiers into a public key,
// possibly using a network request
type DIDPubKeyResolver interface {
ResolveDIDKey(ctx context.Context, did string) (didkey.ID, error)
ResolveDIDKey(ctx context.Context, did string) (keys.DID, error)
}
// DIDStringFromPublicKey creates a did:key identifier string from a public key
func DIDStringFromPublicKey(pub crypto.PubKey) (string, error) {
id, err := didkey.NewID(pub)
id, err := keys.NewDID(pub)
if err != nil {
return "", err
}
@@ -279,8 +279,8 @@ func DIDStringFromPublicKey(pub crypto.PubKey) (string, error) {
type StringDIDPubKeyResolver struct{}
// ResolveDIDKey extracts a public key from a did:key string
func (StringDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (didkey.ID, error) {
return didkey.Parse(didStr)
func (StringDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (keys.DID, error) {
return keys.Parse(didStr)
}
// TokenParser parses a raw string into a Token
@@ -315,11 +315,11 @@ func (p *TokenParser) parseAndVerify(ctx context.Context, raw string, child *Tok
return nil, fmt.Errorf("parser fail")
}
var iss didkey.ID
var iss keys.DID
// TODO(b5): we're double parsing here b/c the jwt lib we're using doesn't expose
// an API (that I know of) for storing parsed issuer / audience
if issStr, ok := mc["iss"].(string); ok {
iss, err = didkey.Parse(issStr)
iss, err = keys.Parse(issStr)
if err != nil {
return nil, err
}
@@ -327,11 +327,11 @@ func (p *TokenParser) parseAndVerify(ctx context.Context, raw string, child *Tok
return nil, fmt.Errorf(`"iss" key is not in claims`)
}
var aud didkey.ID
var aud keys.DID
// TODO(b5): we're double parsing here b/c the jwt lib we're using doesn't expose
// an API (that I know of) for storing parsed issuer / audience
if audStr, ok := mc["aud"].(string); ok {
aud, err = didkey.Parse(audStr)
aud, err = keys.Parse(audStr)
if err != nil {
return nil, err
}