From 8002b1ddd277921340c02fc1325959dda834c4a7 Mon Sep 17 00:00:00 2001 From: b5 Date: Tue, 7 Sep 2021 20:23:15 -0400 Subject: [PATCH] fix: support ED25519 keys --- didkey/key.go | 52 +++++++++++++++++++--- example_test.go | 4 +- go.mod | 4 +- go.sum | 12 +++-- token.go | 115 +++++++++++++++++++++--------------------------- token_test.go | 35 +++++++++++++-- 6 files changed, 139 insertions(+), 83 deletions(-) diff --git a/didkey/key.go b/didkey/key.go index 6291894..bbbff5b 100644 --- a/didkey/key.go +++ b/didkey/key.go @@ -4,6 +4,9 @@ package didkey import ( + "crypto/ed25519" + "crypto/rsa" + "crypto/x509" "fmt" "strings" @@ -15,10 +18,8 @@ import ( const ( // KeyPrefix indicates a decentralized identifier that uses the key method KeyPrefix = "did:key" - // MulticodecKindRSAPubKey IS NOT A REAL MULTICODEC PREFIX. - // pulled from: https://github.com/w3c-ccg/lds-ed25519-2018/issues/3 because - // it's only slighly better than picking a random available byte prefix - MulticodecKindRSAPubKey = 0x5d + // MulticodecKindRSAPubKey rsa-x509-pub https://github.com/multiformats/multicodec/pull/226 + MulticodecKindRSAPubKey = 0x1205 // MulticodecKindEd25519PubKey ed25519-pub MulticodecKindEd25519PubKey = 0xed ) @@ -28,6 +29,16 @@ 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: + 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() { @@ -61,6 +72,31 @@ func (id ID) String() string { 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 + 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 @@ -70,9 +106,13 @@ func Parse(keystr string) (ID, error) { keystr = strings.TrimPrefix(keystr, KeyPrefix+":") - _, data, err := mb.Decode(keystr) + enc, data, err := mb.Decode(keystr) if err != nil { - return id, err + 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) diff --git a/example_test.go b/example_test.go index 50c5441..d52f37a 100644 --- a/example_test.go +++ b/example_test.go @@ -56,9 +56,9 @@ func Example() { panicIfError(err) // Output: - // cid of root UCAN: bafkreidhsvhlctwylgeibl2eeapdvbl3qm3mbqcqhxhvy4grmr25ji77hu + // cid of root UCAN: bafkreih6guuxohv47s2e366l6jn6stlsukgoerkdvtsni3kxr4jjmkaf3y // scope of ucan attenuations must be less than it's parent - // cid of derived UCAN: bafkreifglbwtr27fbzmv3uardlygvggr722fckusfvfyfsonwkroca7efu + // cid of derived UCAN: bafkreihpk5474uoolkqrge3yk5uy2s7rarhn5xwxfoiobcy6ye7vfxetgm } func panicIfError(err error) { diff --git a/go.mod b/go.mod index 813abc0..b023fdb 100644 --- a/go.mod +++ b/go.mod @@ -10,6 +10,6 @@ require ( github.com/multiformats/go-multibase v0.0.3 github.com/multiformats/go-multihash v0.0.14 github.com/multiformats/go-varint v0.0.6 - golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 // indirect - golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae // indirect + golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect + golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect ) diff --git a/go.sum b/go.sum index 315db59..bad317a 100644 --- a/go.sum +++ b/go.sum @@ -82,8 +82,8 @@ go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= -golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= @@ -95,6 +95,7 @@ golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -105,10 +106,13 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae h1:Ih9Yo4hSPImZOpfGuA4bR/ORKTAbhZo2AbWNRCnevdo= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A= +golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= diff --git a/token.go b/token.go index ff60b27..b3b0048 100644 --- a/token.go +++ b/token.go @@ -10,18 +10,18 @@ package ucan import ( "context" + "crypto/ed25519" "crypto/rsa" "crypto/x509" - "encoding/base64" "errors" "fmt" - "strings" "time" "github.com/golang-jwt/jwt" "github.com/ipfs/go-cid" "github.com/libp2p/go-libp2p-core/crypto" mh "github.com/multiformats/go-multihash" + "github.com/qri-io/ucan/didkey" ) // ErrInvalidToken indicates an access token is invalid @@ -125,12 +125,8 @@ type pkSource struct { issuerDID string signingMethod jwt.SigningMethod - verifyKey *rsa.PublicKey - signKey *rsa.PrivateKey - - ap AttenuationConstructorFunc - resolver CIDBytesResolver - store TokenStore + verifyKey interface{} // one of: *rsa.PublicKey, *edsa.PublicKey + signKey interface{} // one of: *rsa.PrivateKey, } // assert pkSource implements tokens at compile time @@ -139,41 +135,51 @@ var _ Source = (*pkSource)(nil) // NewPrivKeySource creates an authentication interface backed by a single // private key. Intended for a node running as remote, or providing a public API func NewPrivKeySource(privKey crypto.PrivKey) (Source, error) { - methodStr := "" - keyType := privKey.Type() + rawPrivBytes, err := privKey.Raw() + if err != nil { + return nil, fmt.Errorf("getting private key bytes: %w", err) + } + + var ( + methodStr = "" + keyType = privKey.Type() + signKey interface{} + verifyKey interface{} + ) + switch keyType { case crypto.RSA: methodStr = "RS256" + // TODO(b5) - detect if key is encoded as PEM block, here we're assuming it is + signKey, err = x509.ParsePKCS1PrivateKey(rawPrivBytes) + if err != nil { + return nil, err + } + rawPubBytes, err := privKey.GetPublic().Raw() + if err != nil { + return nil, fmt.Errorf("getting raw public key bytes: %w", err) + } + verifyKeyiface, err := x509.ParsePKIXPublicKey(rawPubBytes) + if err != nil { + return nil, fmt.Errorf("parsing public key bytes: %w", err) + } + var ok bool + verifyKey, ok = verifyKeyiface.(*rsa.PublicKey) + if !ok { + return nil, fmt.Errorf("public key is not an RSA key. got type: %T", verifyKeyiface) + } case crypto.Ed25519: methodStr = "EdDSA" + signKey = ed25519.PrivateKey(rawPrivBytes) + rawPubBytes, err := privKey.GetPublic().Raw() + if err != nil { + return nil, fmt.Errorf("getting raw public key bytes: %w", err) + } + verifyKey = ed25519.PublicKey(rawPubBytes) default: return nil, fmt.Errorf("unsupported key type for token creation: %q", keyType) } - signingMethod := jwt.GetSigningMethod(methodStr) - - rawPrivBytes, err := privKey.Raw() - if err != nil { - return nil, err - } - signKey, err := x509.ParsePKCS1PrivateKey(rawPrivBytes) - if err != nil { - return nil, err - } - - rawPubBytes, err := privKey.GetPublic().Raw() - if err != nil { - return nil, err - } - 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) - } - issuerDID, err := DIDStringFromPublicKey(privKey.GetPublic()) if err != nil { return nil, err @@ -181,7 +187,7 @@ func NewPrivKeySource(privKey crypto.PrivKey) (Source, error) { return &pkSource{ pk: privKey, - signingMethod: signingMethod, + signingMethod: jwt.GetSigningMethod(methodStr), verifyKey: verifyKey, signKey: signKey, issuerDID: issuerDID, @@ -253,16 +259,16 @@ func (a *pkSource) newToken(subjectDID string, prf []Proof, att Attenuations, fc // DIDPubKeyResolver turns did:key Decentralized IDentifiers into a public key, // possibly using a network request type DIDPubKeyResolver interface { - ResolveDIDKey(ctx context.Context, did string) (crypto.PubKey, error) + ResolveDIDKey(ctx context.Context, did string) (didkey.ID, error) } // DIDStringFromPublicKey creates a did:key identifier string from a public key func DIDStringFromPublicKey(pub crypto.PubKey) (string, error) { - rawPubBytes, err := pub.Raw() + id, err := didkey.NewID(pub) if err != nil { return "", err } - return fmt.Sprintf("did:key:%s", base64.URLEncoding.EncodeToString(rawPubBytes)), nil + return id.String(), nil } // StringDIDPubKeyResolver implements the DIDPubKeyResolver interface without @@ -271,18 +277,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) (crypto.PubKey, error) { - // id, err := did.Parse(didStr) - // if err != nil { - // return nil, fmt.Errorf("invalid DID: %w", err) - // } - - data, err := base64.URLEncoding.DecodeString(strings.TrimPrefix(didStr, "did:key:")) - if err != nil { - return nil, err - } - - return crypto.UnmarshalRsaPublicKey(data) +func (StringDIDPubKeyResolver) ResolveDIDKey(ctx context.Context, didStr string) (didkey.ID, error) { + return didkey.Parse(didStr) } // TokenParser parses a raw string into a Token @@ -309,7 +305,7 @@ func (p *TokenParser) ParseAndVerify(ctx context.Context, raw string) (*Token, e func (p *TokenParser) parseAndVerify(ctx context.Context, raw string, child *Token) (*Token, error) { tok, err := jwt.Parse(raw, p.matchVerifyKeyFunc(ctx)) if err != nil { - return nil, err + return nil, fmt.Errorf("parsing UCAN: %w", err) } mc, ok := tok.Claims.(jwt.MapClaims) @@ -366,24 +362,11 @@ func (p *TokenParser) matchVerifyKeyFunc(ctx context.Context) func(tok *jwt.Toke return nil, fmt.Errorf(`"iss" claims key is required`) } - pubKey, err := p.didr.ResolveDIDKey(ctx, iss) + id, err := p.didr.ResolveDIDKey(ctx, iss) if err != nil { return nil, err } - rawPubBytes, err := pubKey.Raw() - if err != nil { - return nil, err - } - 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 + return id.VerifyKey() } } diff --git a/token_test.go b/token_test.go index f099881..c77b572 100644 --- a/token_test.go +++ b/token_test.go @@ -62,7 +62,7 @@ func TestPrivKeySource(t *testing.T) { t.Fatal(err) } - expect := `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVjdiI6IjAuNC4wIn0.eyJpc3MiOiJkaWQ6a2V5Ok1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBb2FkUjRtY1U3QzBBbWg1bHRfM0hObVEyYVlDOEotYU5mNTJUNEtrLTBzbHh6LVc1LXhrREJ0NUR4RUZuSmVKNGJTMV9ZWkt3UkxKQjYzU0phcWZjMXhUTUFYMnJmcW44d3NwUmd2MEFReGU4RV9icGkzZTUyNnU2UU1VRjdYbDRKN2JkbVlZT0lCUDVCSk83eU1pX2pfU3FWaVdmOG82Y3BJTEF3dXpUNTY2X0ttUWFOclM5QmVNUHQ5NTJZUk1lejZlMFoycXR0aVRQS3hmalJ3b0VwRklldDVhZTFZY0p2VDBLQnJiZEYwNXhDc2F6RUoxSm52eUlSamNiUE9FYVljUjNPZnAxdW8ySTRKdVczQ2FKeHNqMU8yNnZyLWRUSzlqcGVFVTl5X1dUU1lNOUVsazBwZ0xZZ1M4ZHE4aTYwNDVnejByemU4QzV2YkZoSFZwa1ZRSURBUUFCIiwic3ViIjoiZGlkOmtleTpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQW9hZFI0bWNVN0MwQW1oNWx0XzNITm1RMmFZQzhKLWFOZjUyVDRLay0wc2x4ei1XNS14a0RCdDVEeEVGbkplSjRiUzFfWVpLd1JMSkI2M1NKYXFmYzF4VE1BWDJyZnFuOHdzcFJndjBBUXhlOEVfYnBpM2U1MjZ1NlFNVUY3WGw0SjdiZG1ZWU9JQlA1QkpPN3lNaV9qX1NxVmlXZjhvNmNwSUxBd3V6VDU2Nl9LbVFhTnJTOUJlTVB0OTUyWVJNZXo2ZTBaMnF0dGlUUEt4ZmpSd29FcEZJZXQ1YWUxWWNKdlQwS0JyYmRGMDV4Q3NhekVKMUpudnlJUmpjYlBPRWFZY1IzT2ZwMXVvMkk0SnVXM0NhSnhzajFPMjZ2ci1kVEs5anBlRVU5eV9XVFNZTTlFbGswcGdMWWdTOGRxOGk2MDQ1Z3owcnplOEM1dmJGaEhWcGtWUUlEQVFBQiIsImF0dCI6W3siYXBpIjoiKiIsImNhcCI6IlNVUEVSX1VTRVIifSx7ImNhcCI6IlNVUEVSX1VTRVIiLCJkYXRhc2V0IjoiYjU6d29ybGRfYmFua19wb3B1bGF0aW9uOioifV19.Z32-i-pGAtPRsG0JW4ZS8-c17x3mX3kFrmZ0BYhyWk2JH4QMwXFRtkUl8xVQtrC3JigeQeaDiz-WTUSFqJIs5dunL1Xf_SXqq8SZ7NCh6u6OEo2L1BnQkwdO8kDsFoiF42byWDBwzHRog0N-pRXgMhlo8si6Pek4KAZokQ5F-8FuLb3MXXxc9-FnhGRsKgGt_bNWS322h5gXCaXJAzbdAHwGSlORCCJI4CrbWUHs03i4viun2Ht01JO-p4ySlut6YyQ_vW4NGNSAAXGeR-ggkB0B6TGgt695CxX1zgQKV7X6JZx-NF_J-OXCIWngCfr6VdRv1_ADce9s1ODEm2N7eA` + expect := `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVjdiI6IjAuNC4wIn0.eyJpc3MiOiJkaWQ6a2V5OnoyTUd3NGdrODRVU290YVdmNEFrSjgzRGNucmZnR2FjZUY4NktRWFJZTWZRN3hxblVGRVREN3RGNGprTkd2QUV0dlZ6Rm5RdTkydEZ3ZkNyb2ZWZ0c2WnkyZVlKZURSckVVZ1c5WXZCazJXSHVUUnJFZ0FTZnBxR2tKUkdnR3FiNmNidjFxR2N2Nm1FSFF5YlR5M2JOeXFxd3VRWXA3c3BuSlFObVJhUGk0Z3F1czVEWnVOc2NRZjFSMXhCdVN6WHk1YnNCaFlSZzdFcDZmNkJad0U2cHZCamt5ellqWERVYlF2a0VpeG0zcHR3RTRnZ2VZU21oUXFxbU1ac1lwcG1lY1VFMjhuTTdFekx2Q3hRZEZ1QndXZ2U3QURVYzdxVGYxeXNpUzl1YXdOTnA1aER2aHl2cXRDaWg3a3FvTHVzTGVnd2pHZTJTcDhDcUZmdUNRNWgxdHh4WHozdEdtRGZEUDE3Nm15R1htc3R0eDV5MjVTOXpwejg1ZEc4WnRrRnZ4bXNjOFltaXZlRUMycWFkY2FrWEoiLCJzdWIiOiJkaWQ6a2V5OnoyTUd3NGdrODRVU290YVdmNEFrSjgzRGNucmZnR2FjZUY4NktRWFJZTWZRN3hxblVGRVREN3RGNGprTkd2QUV0dlZ6Rm5RdTkydEZ3ZkNyb2ZWZ0c2WnkyZVlKZURSckVVZ1c5WXZCazJXSHVUUnJFZ0FTZnBxR2tKUkdnR3FiNmNidjFxR2N2Nm1FSFF5YlR5M2JOeXFxd3VRWXA3c3BuSlFObVJhUGk0Z3F1czVEWnVOc2NRZjFSMXhCdVN6WHk1YnNCaFlSZzdFcDZmNkJad0U2cHZCamt5ellqWERVYlF2a0VpeG0zcHR3RTRnZ2VZU21oUXFxbU1ac1lwcG1lY1VFMjhuTTdFekx2Q3hRZEZ1QndXZ2U3QURVYzdxVGYxeXNpUzl1YXdOTnA1aER2aHl2cXRDaWg3a3FvTHVzTGVnd2pHZTJTcDhDcUZmdUNRNWgxdHh4WHozdEdtRGZEUDE3Nm15R1htc3R0eDV5MjVTOXpwejg1ZEc4WnRrRnZ4bXNjOFltaXZlRUMycWFkY2FrWEoiLCJhdHQiOlt7ImFwaSI6IioiLCJjYXAiOiJTVVBFUl9VU0VSIn0seyJjYXAiOiJTVVBFUl9VU0VSIiwiZGF0YXNldCI6ImI1OndvcmxkX2JhbmtfcG9wdWxhdGlvbjoqIn1dfQ.SwctK9sJpUtgyOSKN3BMI1BfNT_X6xxy5Oy6aTCvGvdj-vJIxsioz2Po3TlFxpmo9uSJ_nGoUTW7fr7Hc5m8qqrUFnPrtPXOnSNluExh9sh0aXY1ACGNoDknhbnsU_5nkEhDSTOMQ0Y0MoqvkV_Ti-7ZjISEuGukvl4-FFXZVGl6zY7ii8-J3RQu60cMbR4xK-VnlXcZUnhREr8JQtPKfBnmjIvbPclnaOdXLSACgEPXsAX4gekYAb2HR8CduHmaFWqAq_Th-QbqgQVmqUw-VIwtwuxxfxpc-PTNV4L0eDH5jO9i7jVzU3KaWM8NzQ2vux_zhscjBQFR2SmpQuGO7A` if expect != root.Raw { t.Errorf("token mismatch. expected: %q.\ngot: %q", expect, root.Raw) } @@ -77,7 +77,7 @@ func TestPrivKeySource(t *testing.T) { } cidStr := mustCidString(t, derivedToken) - expectCID := "bafkreifglbwtr27fbzmv3uardlygvggr722fckusfvfyfsonwkroca7efu" + expectCID := "bafkreihpk5474uoolkqrge3yk5uy2s7rarhn5xwxfoiobcy6ye7vfxetgm" if expectCID != cidStr { t.Errorf("derived token CID mismatch. expected: %q.\ngot: %q", expectCID, cidStr) @@ -90,6 +90,35 @@ func TestPrivKeySource(t *testing.T) { // } } +func TestED25519PrivKeySource(t *testing.T) { + keyOne, _, err := crypto.GenerateKeyPair(crypto.Ed25519, 123) + if err != nil { + t.Fatal(err) + } + + source, err := ucan.NewPrivKeySource(keyOne) + if err != nil { + t.Fatal(err) + } + + subjectDID, err := ucan.DIDStringFromPublicKey(keyOne.GetPublic()) + if err != nil { + panic(err) + } + + zero := time.Time{} + + // create a root UCAN + origin, err := source.NewOriginToken(subjectDID, nil, nil, zero, zero) + if err != nil { + panic(err) + } + + if _, err = origin.CID(); err != nil { + t.Fatal(err) + } +} + func TestTokenSource(t *testing.T) { // ucan_spec.AssertTokenSourceSpec(t, func(ctx context.Context) ucan.TokenSource { // source, err := ucan.NewPrivKeyTokenSource(peerInfo.PrivKey) @@ -101,7 +130,7 @@ func TestTokenSource(t *testing.T) { } func TestTokenParse(t *testing.T) { - raw := `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVjdiI6IjAuNC4wIn0.eyJpc3MiOiJkaWQ6a2V5Ok1JSUJJakFOQmdrcWhraUc5dzBCQVFFRkFBT0NBUThBTUlJQkNnS0NBUUVBb2FkUjRtY1U3QzBBbWg1bHRfM0hObVEyYVlDOEotYU5mNTJUNEtrLTBzbHh6LVc1LXhrREJ0NUR4RUZuSmVKNGJTMV9ZWkt3UkxKQjYzU0phcWZjMXhUTUFYMnJmcW44d3NwUmd2MEFReGU4RV9icGkzZTUyNnU2UU1VRjdYbDRKN2JkbVlZT0lCUDVCSk83eU1pX2pfU3FWaVdmOG82Y3BJTEF3dXpUNTY2X0ttUWFOclM5QmVNUHQ5NTJZUk1lejZlMFoycXR0aVRQS3hmalJ3b0VwRklldDVhZTFZY0p2VDBLQnJiZEYwNXhDc2F6RUoxSm52eUlSamNiUE9FYVljUjNPZnAxdW8ySTRKdVczQ2FKeHNqMU8yNnZyLWRUSzlqcGVFVTl5X1dUU1lNOUVsazBwZ0xZZ1M4ZHE4aTYwNDVnejByemU4QzV2YkZoSFZwa1ZRSURBUUFCIiwic3ViIjoiZGlkOmtleTpNSUlCSWpBTkJna3Foa2lHOXcwQkFRRUZBQU9DQVE4QU1JSUJDZ0tDQVFFQW9hZFI0bWNVN0MwQW1oNWx0XzNITm1RMmFZQzhKLWFOZjUyVDRLay0wc2x4ei1XNS14a0RCdDVEeEVGbkplSjRiUzFfWVpLd1JMSkI2M1NKYXFmYzF4VE1BWDJyZnFuOHdzcFJndjBBUXhlOEVfYnBpM2U1MjZ1NlFNVUY3WGw0SjdiZG1ZWU9JQlA1QkpPN3lNaV9qX1NxVmlXZjhvNmNwSUxBd3V6VDU2Nl9LbVFhTnJTOUJlTVB0OTUyWVJNZXo2ZTBaMnF0dGlUUEt4ZmpSd29FcEZJZXQ1YWUxWWNKdlQwS0JyYmRGMDV4Q3NhekVKMUpudnlJUmpjYlBPRWFZY1IzT2ZwMXVvMkk0SnVXM0NhSnhzajFPMjZ2ci1kVEs5anBlRVU5eV9XVFNZTTlFbGswcGdMWWdTOGRxOGk2MDQ1Z3owcnplOEM1dmJGaEhWcGtWUUlEQVFBQiIsImF0dCI6W3siYXBpIjoiKiIsImNhcCI6IlNVUEVSX1VTRVIifSx7ImNhcCI6IlNVUEVSX1VTRVIiLCJkYXRhc2V0IjoiYjU6d29ybGRfYmFua19wb3B1bGF0aW9uOioifV19.Z32-i-pGAtPRsG0JW4ZS8-c17x3mX3kFrmZ0BYhyWk2JH4QMwXFRtkUl8xVQtrC3JigeQeaDiz-WTUSFqJIs5dunL1Xf_SXqq8SZ7NCh6u6OEo2L1BnQkwdO8kDsFoiF42byWDBwzHRog0N-pRXgMhlo8si6Pek4KAZokQ5F-8FuLb3MXXxc9-FnhGRsKgGt_bNWS322h5gXCaXJAzbdAHwGSlORCCJI4CrbWUHs03i4viun2Ht01JO-p4ySlut6YyQ_vW4NGNSAAXGeR-ggkB0B6TGgt695CxX1zgQKV7X6JZx-NF_J-OXCIWngCfr6VdRv1_ADce9s1ODEm2N7eA` + raw := `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsInVjdiI6IjAuNC4wIn0.eyJpc3MiOiJkaWQ6a2V5OnoyTUd3NGdrODRVU290YVdmNEFrSjgzRGNucmZnR2FjZUY4NktRWFJZTWZRN3hxblVGRVREN3RGNGprTkd2QUV0dlZ6Rm5RdTkydEZ3ZkNyb2ZWZ0c2WnkyZVlKZURSckVVZ1c5WXZCazJXSHVUUnJFZ0FTZnBxR2tKUkdnR3FiNmNidjFxR2N2Nm1FSFF5YlR5M2JOeXFxd3VRWXA3c3BuSlFObVJhUGk0Z3F1czVEWnVOc2NRZjFSMXhCdVN6WHk1YnNCaFlSZzdFcDZmNkJad0U2cHZCamt5ellqWERVYlF2a0VpeG0zcHR3RTRnZ2VZU21oUXFxbU1ac1lwcG1lY1VFMjhuTTdFekx2Q3hRZEZ1QndXZ2U3QURVYzdxVGYxeXNpUzl1YXdOTnA1aER2aHl2cXRDaWg3a3FvTHVzTGVnd2pHZTJTcDhDcUZmdUNRNWgxdHh4WHozdEdtRGZEUDE3Nm15R1htc3R0eDV5MjVTOXpwejg1ZEc4WnRrRnZ4bXNjOFltaXZlRUMycWFkY2FrWEoiLCJzdWIiOiJkaWQ6a2V5OnoyTUd3NGdrODRVU290YVdmNEFrSjgzRGNucmZnR2FjZUY4NktRWFJZTWZRN3hxblVGRVREN3RGNGprTkd2QUV0dlZ6Rm5RdTkydEZ3ZkNyb2ZWZ0c2WnkyZVlKZURSckVVZ1c5WXZCazJXSHVUUnJFZ0FTZnBxR2tKUkdnR3FiNmNidjFxR2N2Nm1FSFF5YlR5M2JOeXFxd3VRWXA3c3BuSlFObVJhUGk0Z3F1czVEWnVOc2NRZjFSMXhCdVN6WHk1YnNCaFlSZzdFcDZmNkJad0U2cHZCamt5ellqWERVYlF2a0VpeG0zcHR3RTRnZ2VZU21oUXFxbU1ac1lwcG1lY1VFMjhuTTdFekx2Q3hRZEZ1QndXZ2U3QURVYzdxVGYxeXNpUzl1YXdOTnA1aER2aHl2cXRDaWg3a3FvTHVzTGVnd2pHZTJTcDhDcUZmdUNRNWgxdHh4WHozdEdtRGZEUDE3Nm15R1htc3R0eDV5MjVTOXpwejg1ZEc4WnRrRnZ4bXNjOFltaXZlRUMycWFkY2FrWEoiLCJhdHQiOlt7ImFwaSI6IioiLCJjYXAiOiJTVVBFUl9VU0VSIn0seyJjYXAiOiJTVVBFUl9VU0VSIiwiZGF0YXNldCI6ImI1OndvcmxkX2JhbmtfcG9wdWxhdGlvbjoqIn1dfQ.SwctK9sJpUtgyOSKN3BMI1BfNT_X6xxy5Oy6aTCvGvdj-vJIxsioz2Po3TlFxpmo9uSJ_nGoUTW7fr7Hc5m8qqrUFnPrtPXOnSNluExh9sh0aXY1ACGNoDknhbnsU_5nkEhDSTOMQ0Y0MoqvkV_Ti-7ZjISEuGukvl4-FFXZVGl6zY7ii8-J3RQu60cMbR4xK-VnlXcZUnhREr8JQtPKfBnmjIvbPclnaOdXLSACgEPXsAX4gekYAb2HR8CduHmaFWqAq_Th-QbqgQVmqUw-VIwtwuxxfxpc-PTNV4L0eDH5jO9i7jVzU3KaWM8NzQ2vux_zhscjBQFR2SmpQuGO7A` caps := ucan.NewNestedCapabilities("SUPER_USER", "OVERWRITE", "SOFT_DELETE", "REVISE", "CREATE")