Compare commits
1 Commits
attestatio
...
v1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dddb67a2b7 |
4
.github/workflows/bench.yml
vendored
4
.github/workflows/bench.yml
vendored
@@ -2,7 +2,7 @@ name: go continuous benchmark
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- v1
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -14,7 +14,7 @@ jobs:
|
|||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v4
|
||||||
with:
|
with:
|
||||||
go-version: "stable"
|
go-version: "stable"
|
||||||
- name: Run benchmark
|
- name: Run benchmark
|
||||||
|
|||||||
4
.github/workflows/gotest.yml
vendored
4
.github/workflows/gotest.yml
vendored
@@ -13,8 +13,8 @@ jobs:
|
|||||||
runs-on: ${{ matrix.os }}-latest
|
runs-on: ${{ matrix.os }}-latest
|
||||||
name: ${{ matrix.os}} (go ${{ matrix.go }})
|
name: ${{ matrix.os}} (go ${{ matrix.go }})
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v2
|
||||||
with:
|
with:
|
||||||
go-version: ${{ matrix.go }}
|
go-version: ${{ matrix.go }}
|
||||||
- name: Go information
|
- name: Go information
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<img alt="Go benchmarks" src="https://img.shields.io/badge/Benchmarks-go-blue">
|
<img alt="Go benchmarks" src="https://img.shields.io/badge/Benchmarks-go-blue">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://github.com/ucan-wg/go-ucan/blob/v1/LICENSE.md">
|
<a href="https://github.com/ucan-wg/go-ucan/blob/v1/LICENSE.md">
|
||||||
<img alt="Apache 2.0 OR MIT License" src="https://img.shields.io/badge/License-Apache--2.0_OR_MIT-green">
|
<img alt="Apache 2.0 + MIT License" src="https://img.shields.io/badge/License-Apache--2.0+MIT-green">
|
||||||
</a>
|
</a>
|
||||||
<a href="https://pkg.go.dev/github.com/ucan-wg/go-ucan">
|
<a href="https://pkg.go.dev/github.com/ucan-wg/go-ucan">
|
||||||
<img src="https://img.shields.io/badge/Docs-godoc-blue" alt="Docs">
|
<img src="https://img.shields.io/badge/Docs-godoc-blue" alt="Docs">
|
||||||
@@ -41,7 +41,6 @@ The UCAN specification is separated in multiple sub-spec:
|
|||||||
- [Main specification](https://github.com/ucan-wg/spec)
|
- [Main specification](https://github.com/ucan-wg/spec)
|
||||||
- [Delegation](https://github.com/ucan-wg/delegation/tree/v1_ipld)
|
- [Delegation](https://github.com/ucan-wg/delegation/tree/v1_ipld)
|
||||||
- [Invocation](https://github.com/ucan-wg/invocation)
|
- [Invocation](https://github.com/ucan-wg/invocation)
|
||||||
- [Container](https://github.com/ucan-wg/container)
|
|
||||||
|
|
||||||
Not implemented yet:
|
Not implemented yet:
|
||||||
- [Revocation](https://github.com/ucan-wg/revocation/tree/first-draft)
|
- [Revocation](https://github.com/ucan-wg/revocation/tree/first-draft)
|
||||||
@@ -54,9 +53,11 @@ Not implemented yet:
|
|||||||
|
|
||||||
## Status
|
## Status
|
||||||
|
|
||||||
`go-ucan` currently support the required parts of the UCAN specification: the main specification, delegation and invocation. It leverages the sibling project [`go-did-it`](https://github.com/MetaMask/go-did-it) for easy and extensible DID support.
|
`go-ucan` currently support the required parts of the UCAN specification: the main specification, delegation and invocation.
|
||||||
|
|
||||||
Besides that, `go-ucan` also includes:
|
Besides that, `go-ucan` also includes:
|
||||||
|
- a simplified [DID](https://www.w3.org/TR/did-core/) and [did-key](https://w3c-ccg.github.io/did-method-key/) implementation
|
||||||
|
- a [token container](https://github.com/ucan-wg/go-ucan/tree/v1/pkg/container) with CBOR and CAR format, to package and carry tokens together
|
||||||
- support for encrypted values in token's metadata
|
- support for encrypted values in token's metadata
|
||||||
|
|
||||||
## Getting Help
|
## Getting Help
|
||||||
@@ -73,4 +74,4 @@ Artwork by [Bruno Monts](https://www.instagram.com/bruno_monts). Thank you [Rene
|
|||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
This project is licensed under the dual license [Apache 2.0 OR MIT](https://github.com/ucan-wg/go-ucan/blob/v1/LICENSE.md).
|
This project is licensed under the double license [Apache 2.0 + MIT](https://github.com/ucan-wg/go-ucan/blob/v1/LICENSE.md).
|
||||||
|
|||||||
31
did/README.md
Normal file
31
did/README.md
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
## did
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
The test suite for this package includes test vectors provided by the
|
||||||
|
authors of the [`did:key` method specification](https://w3c-ccg.github.io/did-method-key/).
|
||||||
|
Some of these tests provide the public key associated with a `did:key`
|
||||||
|
as JWKs and an extra (test-only) dependency has been added to unmarshal
|
||||||
|
the JWK into a Go `struct`. Support for the `secp256k1` encryption
|
||||||
|
algorithm is experimental (but stable in my experience) and requires the
|
||||||
|
addition of the following build tag to properly run:
|
||||||
|
|
||||||
|
```
|
||||||
|
// go:build jwx_es256k
|
||||||
|
```
|
||||||
|
|
||||||
|
WARNING: These tests will not run by default!
|
||||||
|
|
||||||
|
To include these tests from the CLI, execute the following command:
|
||||||
|
|
||||||
|
```
|
||||||
|
go test -v ./did -tags jwx_es256k
|
||||||
|
```
|
||||||
|
|
||||||
|
It should also be possible to configure your IDE to run these tests. For
|
||||||
|
instance, in Codium, add the following JSON snippet to your local project
|
||||||
|
configuration:
|
||||||
|
|
||||||
|
```
|
||||||
|
"go.testTags": "jwx_es256k",
|
||||||
|
```
|
||||||
231
did/crypto.go
Normal file
231
did/crypto.go
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
package did
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
crypto "github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
"github.com/multiformats/go-multicodec"
|
||||||
|
"github.com/multiformats/go-varint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GenerateEd25519 generates an Ed25519 private key and the matching DID.
|
||||||
|
// This is the RECOMMENDED algorithm.
|
||||||
|
func GenerateEd25519() (crypto.PrivKey, DID, error) {
|
||||||
|
priv, pub, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, Undef, nil
|
||||||
|
}
|
||||||
|
did, err := FromPubKey(pub)
|
||||||
|
return priv, did, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateRSA generates a RSA private key and the matching DID.
|
||||||
|
func GenerateRSA() (crypto.PrivKey, DID, error) {
|
||||||
|
// NIST Special Publication 800-57 Part 1 Revision 5
|
||||||
|
// Section 5.6.1.1 (Table 2)
|
||||||
|
// Paraphrased: 2048-bit RSA keys are secure until 2030 and 3072-bit keys are recommended for longer-term security.
|
||||||
|
const keyLength = 3072
|
||||||
|
|
||||||
|
priv, pub, err := crypto.GenerateRSAKeyPair(keyLength, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, Undef, nil
|
||||||
|
}
|
||||||
|
did, err := FromPubKey(pub)
|
||||||
|
return priv, did, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateEd25519 generates a Secp256k1 private key and the matching DID.
|
||||||
|
func GenerateSecp256k1() (crypto.PrivKey, DID, error) {
|
||||||
|
priv, pub, err := crypto.GenerateSecp256k1Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, Undef, nil
|
||||||
|
}
|
||||||
|
did, err := FromPubKey(pub)
|
||||||
|
return priv, did, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateECDSA generates an ECDSA private key and the matching DID
|
||||||
|
// for the default P256 curve.
|
||||||
|
func GenerateECDSA() (crypto.PrivKey, DID, error) {
|
||||||
|
return GenerateECDSAWithCurve(P256)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateECDSAWithCurve generates an ECDSA private key and matching
|
||||||
|
// DID for the user-supplied curve
|
||||||
|
func GenerateECDSAWithCurve(code multicodec.Code) (crypto.PrivKey, DID, error) {
|
||||||
|
var curve elliptic.Curve
|
||||||
|
|
||||||
|
switch code {
|
||||||
|
case P256:
|
||||||
|
curve = elliptic.P256()
|
||||||
|
case P384:
|
||||||
|
curve = elliptic.P384()
|
||||||
|
case P521:
|
||||||
|
curve = elliptic.P521()
|
||||||
|
default:
|
||||||
|
return nil, Undef, errors.New("unsupported ECDSA curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
priv, pub, err := crypto.GenerateECDSAKeyPairWithCurve(curve, rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
did, err := FromPubKey(pub)
|
||||||
|
|
||||||
|
return priv, did, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPrivKey is a convenience function that returns the DID associated
|
||||||
|
// with the public key associated with the provided private key.
|
||||||
|
func FromPrivKey(privKey crypto.PrivKey) (DID, error) {
|
||||||
|
return FromPubKey(privKey.GetPublic())
|
||||||
|
}
|
||||||
|
|
||||||
|
// FromPubKey returns a did:key constructed from the provided public key.
|
||||||
|
func FromPubKey(pubKey crypto.PubKey) (DID, error) {
|
||||||
|
var code multicodec.Code
|
||||||
|
|
||||||
|
switch pubKey.Type() {
|
||||||
|
case pb.KeyType_Ed25519:
|
||||||
|
code = multicodec.Ed25519Pub
|
||||||
|
case pb.KeyType_RSA:
|
||||||
|
code = RSA
|
||||||
|
case pb.KeyType_Secp256k1:
|
||||||
|
code = Secp256k1
|
||||||
|
case pb.KeyType_ECDSA:
|
||||||
|
var err error
|
||||||
|
if code, err = codeForCurve(pubKey); err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return Undef, errors.New("unsupported key type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if pubKey.Type() == pb.KeyType_ECDSA && code == Secp256k1 {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
pubKey, err = coerceECDSAToSecp256k1(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var bytes []byte
|
||||||
|
|
||||||
|
switch pubKey.Type() {
|
||||||
|
case pb.KeyType_ECDSA:
|
||||||
|
pkix, err := pubKey.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, err := x509.ParsePKIXPublicKey(pkix)
|
||||||
|
if err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ecdsaPublicKey := publicKey.(*ecdsa.PublicKey)
|
||||||
|
|
||||||
|
bytes = elliptic.MarshalCompressed(ecdsaPublicKey.Curve, ecdsaPublicKey.X, ecdsaPublicKey.Y)
|
||||||
|
case pb.KeyType_Ed25519, pb.KeyType_Secp256k1:
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if bytes, err = pubKey.Raw(); err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
case pb.KeyType_RSA:
|
||||||
|
var err error
|
||||||
|
|
||||||
|
pkix, err := pubKey.Raw()
|
||||||
|
if err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
publicKey, err := x509.ParsePKIXPublicKey(pkix)
|
||||||
|
if err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
|
bytes = x509.MarshalPKCS1PublicKey(publicKey.(*rsa.PublicKey))
|
||||||
|
}
|
||||||
|
|
||||||
|
return DID{
|
||||||
|
code: code,
|
||||||
|
bytes: string(append(varint.ToUvarint(uint64(code)), bytes...)),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToPubKey returns the crypto.PubKey encapsulated in the DID formed by
|
||||||
|
// parsing the provided string.
|
||||||
|
func ToPubKey(s string) (crypto.PubKey, error) {
|
||||||
|
id, err := Parse(s)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return id.PubKey()
|
||||||
|
}
|
||||||
|
|
||||||
|
func codeForCurve(pubKey crypto.PubKey) (multicodec.Code, error) {
|
||||||
|
stdPub, err := crypto.PubKeyToStdKey(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return multicodec.Identity, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ecdsaPub, ok := stdPub.(*ecdsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return multicodec.Identity, errors.New("failed to assert type for code to curve")
|
||||||
|
}
|
||||||
|
|
||||||
|
switch ecdsaPub.Curve {
|
||||||
|
case elliptic.P256():
|
||||||
|
return P256, nil
|
||||||
|
case elliptic.P384():
|
||||||
|
return P384, nil
|
||||||
|
case elliptic.P521():
|
||||||
|
return P521, nil
|
||||||
|
case secp256k1.S256():
|
||||||
|
return Secp256k1, nil
|
||||||
|
default:
|
||||||
|
return multicodec.Identity, fmt.Errorf("unsupported ECDSA curve: %s", ecdsaPub.Curve.Params().Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// secp256k1.S256 is a valid ECDSA curve, but the go-libp2p/core/crypto
|
||||||
|
// package treats it as a different type and has a different format for
|
||||||
|
// the raw bytes of the public key.
|
||||||
|
//
|
||||||
|
// If a valid ECDSA public key was created using the secp256k1.S256 curve,
|
||||||
|
// this function will "convert" it from a crypto.ECDSAPubKey to a
|
||||||
|
// crypto.Secp256k1PublicKey.
|
||||||
|
func coerceECDSAToSecp256k1(pubKey crypto.PubKey) (crypto.PubKey, error) {
|
||||||
|
stdPub, err := crypto.PubKeyToStdKey(pubKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
ecdsaPub, ok := stdPub.(*ecdsa.PublicKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, errors.New("failed to assert type for secp256k1 coersion")
|
||||||
|
}
|
||||||
|
|
||||||
|
ecdsaPubBytes := append([]byte{0x04}, append(ecdsaPub.X.Bytes(), ecdsaPub.Y.Bytes()...)...)
|
||||||
|
|
||||||
|
secp256k1Pub, err := secp256k1.ParsePubKey(ecdsaPubBytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
cryptoPub := crypto.Secp256k1PublicKey(*secp256k1Pub)
|
||||||
|
|
||||||
|
return &cryptoPub, nil
|
||||||
|
}
|
||||||
108
did/crypto_test.go
Normal file
108
did/crypto_test.go
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package did_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto/pb"
|
||||||
|
"github.com/multiformats/go-multicodec"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
exampleDIDStr = "did:key:z6MkhaXgBZDvotDkL5257faiztiGiC2QtKLGpbnnEGta2doK"
|
||||||
|
examplePubKeyStr = "Lm/M42cB3HkUiODQsXRcweM6TByfzEHGO9ND274JcOY="
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFromPubKey(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
_, ecdsaP256, err := crypto.GenerateECDSAKeyPairWithCurve(elliptic.P256(), rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, ecdsaP384, err := crypto.GenerateECDSAKeyPairWithCurve(elliptic.P384(), rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, ecdsaP521, err := crypto.GenerateECDSAKeyPairWithCurve(elliptic.P521(), rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, ecdsaSecp256k1, err := crypto.GenerateECDSAKeyPairWithCurve(secp256k1.S256(), rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, ed25519, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, rsa, err := crypto.GenerateRSAKeyPair(2048, rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
_, secp256k1PubKey1, err := crypto.GenerateSecp256k1Key(rand.Reader)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
test := func(pub crypto.PubKey, code multicodec.Code) func(t *testing.T) {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
return func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
id, err := did.FromPubKey(pub)
|
||||||
|
require.NoError(t, err)
|
||||||
|
p, err := id.PubKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Equal(t, pub, p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
t.Run("ECDSA with P256 curve", test(ecdsaP256, did.P256))
|
||||||
|
t.Run("ECDSA with P384 curve", test(ecdsaP384, did.P384))
|
||||||
|
t.Run("ECDSA with P521 curve", test(ecdsaP521, did.P521))
|
||||||
|
t.Run("Ed25519", test(ed25519, did.Ed25519))
|
||||||
|
t.Run("RSA", test(rsa, did.RSA))
|
||||||
|
t.Run("secp256k1", test(secp256k1PubKey1, did.Secp256k1))
|
||||||
|
|
||||||
|
t.Run("ECDSA with secp256k1 curve (coerced)", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
id, err := did.FromPubKey(ecdsaSecp256k1)
|
||||||
|
require.NoError(t, err)
|
||||||
|
p, err := id.PubKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, pb.KeyType_Secp256k1, p.Type())
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("unmarshaled example key (secp256k1)", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
id, err := did.FromPubKey(examplePubKey(t))
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, exampleDID(t), id)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestToPubKey(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
pubKey, err := did.ToPubKey(exampleDIDStr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, examplePubKey(t), pubKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func exampleDID(t *testing.T) did.DID {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
id, err := did.Parse(exampleDIDStr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return id
|
||||||
|
}
|
||||||
|
|
||||||
|
func examplePubKey(t *testing.T) crypto.PubKey {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
pubKeyCfg, err := crypto.ConfigDecodeKey(examplePubKeyStr)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
pubKey, err := crypto.UnmarshalEd25519PublicKey(pubKeyCfg)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return pubKey
|
||||||
|
}
|
||||||
140
did/did.go
Normal file
140
did/did.go
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
package did
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/x509"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
crypto "github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
mbase "github.com/multiformats/go-multibase"
|
||||||
|
"github.com/multiformats/go-multicodec"
|
||||||
|
varint "github.com/multiformats/go-varint"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Signature algorithms from the [did:key specification]
|
||||||
|
//
|
||||||
|
// [did:key specification]: https://w3c-ccg.github.io/did-method-key/#signature-method-creation-algorithm
|
||||||
|
const (
|
||||||
|
X25519 = multicodec.X25519Pub
|
||||||
|
Ed25519 = multicodec.Ed25519Pub // UCAN required/recommended
|
||||||
|
P256 = multicodec.P256Pub // UCAN required
|
||||||
|
P384 = multicodec.P384Pub
|
||||||
|
P521 = multicodec.P521Pub
|
||||||
|
Secp256k1 = multicodec.Secp256k1Pub // UCAN required
|
||||||
|
RSA = multicodec.RsaPub
|
||||||
|
)
|
||||||
|
|
||||||
|
// Undef can be used to represent a nil or undefined DID, using DID{}
|
||||||
|
// directly is also acceptable.
|
||||||
|
var Undef = DID{}
|
||||||
|
|
||||||
|
// DID is a Decentralized Identifier of the did:key type, directly holding a cryptographic public key.
|
||||||
|
// [did:key format]: https://w3c-ccg.github.io/did-method-key/
|
||||||
|
type DID struct {
|
||||||
|
code multicodec.Code
|
||||||
|
bytes string // as string instead of []byte to allow the == operator
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse returns the DID from the string representation or an error if
|
||||||
|
// the prefix and method are incorrect, if an unknown encryption algorithm
|
||||||
|
// is specified or if the method-specific-identifier's bytes don't
|
||||||
|
// represent a public key for the specified encryption algorithm.
|
||||||
|
func Parse(str string) (DID, error) {
|
||||||
|
const keyPrefix = "did:key:"
|
||||||
|
|
||||||
|
if !strings.HasPrefix(str, keyPrefix) {
|
||||||
|
return Undef, fmt.Errorf("must start with 'did:key'")
|
||||||
|
}
|
||||||
|
|
||||||
|
baseCodec, bytes, err := mbase.Decode(str[len(keyPrefix):])
|
||||||
|
if err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
if baseCodec != mbase.Base58BTC {
|
||||||
|
return Undef, fmt.Errorf("not Base58BTC encoded")
|
||||||
|
}
|
||||||
|
code, _, err := varint.FromUvarint(bytes)
|
||||||
|
if err != nil {
|
||||||
|
return Undef, err
|
||||||
|
}
|
||||||
|
switch multicodec.Code(code) {
|
||||||
|
case Ed25519, P256, Secp256k1, RSA:
|
||||||
|
return DID{bytes: string(bytes), code: multicodec.Code(code)}, nil
|
||||||
|
default:
|
||||||
|
return Undef, fmt.Errorf("unsupported did:key multicodec: 0x%x", code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MustParse is like Parse but panics instead of returning an error.
|
||||||
|
func MustParse(str string) DID {
|
||||||
|
did, err := Parse(str)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return did
|
||||||
|
}
|
||||||
|
|
||||||
|
// Defined tells if the DID is defined, not equal to Undef.
|
||||||
|
func (d DID) Defined() bool {
|
||||||
|
return d.code != 0 || len(d.bytes) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// PubKey returns the public key encapsulated by the did:key.
|
||||||
|
func (d DID) PubKey() (crypto.PubKey, error) {
|
||||||
|
unmarshaler, ok := map[multicodec.Code]crypto.PubKeyUnmarshaller{
|
||||||
|
X25519: crypto.UnmarshalEd25519PublicKey,
|
||||||
|
Ed25519: crypto.UnmarshalEd25519PublicKey,
|
||||||
|
P256: ecdsaPubKeyUnmarshaler(elliptic.P256()),
|
||||||
|
P384: ecdsaPubKeyUnmarshaler(elliptic.P384()),
|
||||||
|
P521: ecdsaPubKeyUnmarshaler(elliptic.P521()),
|
||||||
|
Secp256k1: crypto.UnmarshalSecp256k1PublicKey,
|
||||||
|
RSA: rsaPubKeyUnmarshaller,
|
||||||
|
}[d.code]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("unsupported multicodec: %d", d.code)
|
||||||
|
}
|
||||||
|
|
||||||
|
codeSize := varint.UvarintSize(uint64(d.code))
|
||||||
|
return unmarshaler([]byte(d.bytes)[codeSize:])
|
||||||
|
}
|
||||||
|
|
||||||
|
// String formats the decentralized identity document (DID) as a string.
|
||||||
|
func (d DID) String() string {
|
||||||
|
key, _ := mbase.Encode(mbase.Base58BTC, []byte(d.bytes))
|
||||||
|
return "did:key:" + key
|
||||||
|
}
|
||||||
|
|
||||||
|
func ecdsaPubKeyUnmarshaler(curve elliptic.Curve) crypto.PubKeyUnmarshaller {
|
||||||
|
return func(data []byte) (crypto.PubKey, error) {
|
||||||
|
x, y := elliptic.UnmarshalCompressed(curve, data)
|
||||||
|
|
||||||
|
ecdsaPublicKey := &ecdsa.PublicKey{
|
||||||
|
Curve: curve,
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
}
|
||||||
|
|
||||||
|
pkix, err := x509.MarshalPKIXPublicKey(ecdsaPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.UnmarshalECDSAPublicKey(pkix)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func rsaPubKeyUnmarshaller(data []byte) (crypto.PubKey, error) {
|
||||||
|
rsaPublicKey, err := x509.ParsePKCS1PublicKey(data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pkix, err := x509.MarshalPKIXPublicKey(rsaPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.UnmarshalRsaPublicKey(pkix)
|
||||||
|
}
|
||||||
41
did/did_test.go
Normal file
41
did/did_test.go
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
package did
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseDIDKey(t *testing.T) {
|
||||||
|
str := "did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z"
|
||||||
|
d, err := Parse(str)
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.Equal(t, str, d.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMustParseDIDKey(t *testing.T) {
|
||||||
|
str := "did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z"
|
||||||
|
require.NotPanics(t, func() {
|
||||||
|
d := MustParse(str)
|
||||||
|
require.Equal(t, str, d.String())
|
||||||
|
})
|
||||||
|
str = "did:key:z7Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z"
|
||||||
|
require.Panics(t, func() {
|
||||||
|
MustParse(str)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestEquivalence(t *testing.T) {
|
||||||
|
undef0 := DID{}
|
||||||
|
undef1 := Undef
|
||||||
|
|
||||||
|
did0, err := Parse("did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
did1, err := Parse("did:key:z6Mkod5Jr3yd5SC7UDueqK4dAAw5xYJYjksy722tA9Boxc4z")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
require.True(t, undef0 == undef1)
|
||||||
|
require.False(t, undef0 == did0)
|
||||||
|
require.True(t, did0 == did1)
|
||||||
|
require.False(t, undef1 == did1)
|
||||||
|
}
|
||||||
127
did/didtest/crypto.go
Normal file
127
did/didtest/crypto.go
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
// Package didtest provides Personas that can be used for testing. Each
|
||||||
|
// Persona has a name, crypto.PrivKey and associated crypto.PubKey and
|
||||||
|
// did.DID.
|
||||||
|
package didtest
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
alicePrivKeyB64 = "CAESQHdNJLBBiuc1AdwPHBkubB2KS1p0cv2JEF7m8tfwtrcm5ajaYPm+XmVCmtcHOF2lGDlmaiDA7emfwD3IrcyES0M="
|
||||||
|
bobPrivKeyB64 = "CAESQHBz+AIop1g+9iBDj+ufUc/zm9/ry7c6kDFO8Wl/D0+H63V9hC6s9l4npf3pYEFCjBtlR0AMNWMoFQKSlYNKo20="
|
||||||
|
carolPrivKeyB64 = "CAESQPrCgkcHnYFXDT9AlAydhPECBEivEuuVx9dJxLjVvDTmJIVNivfzg6H4mAiPfYS+5ryVVUZTHZBzvMuvvvG/Ks0="
|
||||||
|
danPrivKeyB64 = "CAESQCgNhzofKhC+7hW6x+fNd7iMPtQHeEmKRhhlduf/I7/TeOEFYAEflbJ0sAhMeDJ/HQXaAvsWgHEbJ3ZLhP8q2B0="
|
||||||
|
erinPrivKeyB64 = "CAESQKhCJo5UBpQcthko8DKMFsbdZ+qqQ5oc01CtLCqrE90dF2GfRlrMmot3WPHiHGCmEYi5ZMEHuiSI095e/6O4Bpw="
|
||||||
|
frankPrivKeyB64 = "CAESQDlXPKsy3jHh7OWTWQqyZF95Ueac5DKo7xD0NOBE5F2BNr1ZVxRmJ2dBELbOt8KP9sOACcO9qlCB7uMA1UQc7sk="
|
||||||
|
)
|
||||||
|
|
||||||
|
// Persona is a generic participant used for cryptographic testing.
|
||||||
|
type Persona int
|
||||||
|
|
||||||
|
// The provided Personas were selected from the first few generic
|
||||||
|
// participants listed in this [table].
|
||||||
|
//
|
||||||
|
// [table]: https://en.wikipedia.org/wiki/Alice_and_Bob#Cryptographic_systems
|
||||||
|
const (
|
||||||
|
PersonaAlice Persona = iota
|
||||||
|
PersonaBob
|
||||||
|
PersonaCarol
|
||||||
|
PersonaDan
|
||||||
|
PersonaErin
|
||||||
|
PersonaFrank
|
||||||
|
)
|
||||||
|
|
||||||
|
var privKeys map[Persona]crypto.PrivKey
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
privKeys = make(map[Persona]crypto.PrivKey, 6)
|
||||||
|
for persona, privKeyCfg := range privKeyB64() {
|
||||||
|
privKeyMar, err := crypto.ConfigDecodeKey(privKeyCfg)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
privKey, err := crypto.UnmarshalPrivateKey(privKeyMar)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
privKeys[persona] = privKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DID returns a did.DID based on the Persona's Ed25519 public key.
|
||||||
|
func (p Persona) DID() did.DID {
|
||||||
|
d, err := did.FromPrivKey(p.PrivKey())
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return d
|
||||||
|
}
|
||||||
|
|
||||||
|
// Name returns the username of the Persona.
|
||||||
|
func (p Persona) Name() string {
|
||||||
|
name, ok := map[Persona]string{
|
||||||
|
PersonaAlice: "Alice",
|
||||||
|
PersonaBob: "Bob",
|
||||||
|
PersonaCarol: "Carol",
|
||||||
|
PersonaDan: "Dan",
|
||||||
|
PersonaErin: "Erin",
|
||||||
|
PersonaFrank: "Frank",
|
||||||
|
}[p]
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Sprintf("Unknown persona: %v", p))
|
||||||
|
}
|
||||||
|
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrivKey returns the Ed25519 private key for the Persona.
|
||||||
|
func (p Persona) PrivKey() crypto.PrivKey {
|
||||||
|
return privKeys[p]
|
||||||
|
}
|
||||||
|
|
||||||
|
// PubKey returns the Ed25519 public key for the Persona.
|
||||||
|
func (p Persona) PubKey() crypto.PubKey {
|
||||||
|
return p.PrivKey().GetPublic()
|
||||||
|
}
|
||||||
|
|
||||||
|
// PubKeyConfig returns the marshaled and encoded Ed25519 public key
|
||||||
|
// for the Persona.
|
||||||
|
func (p Persona) PubKeyConfig(t *testing.T) string {
|
||||||
|
pubKeyMar, err := crypto.MarshalPublicKey(p.PrivKey().GetPublic())
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
return crypto.ConfigEncodeKey(pubKeyMar)
|
||||||
|
}
|
||||||
|
|
||||||
|
func privKeyB64() map[Persona]string {
|
||||||
|
return map[Persona]string{
|
||||||
|
PersonaAlice: alicePrivKeyB64,
|
||||||
|
PersonaBob: bobPrivKeyB64,
|
||||||
|
PersonaCarol: carolPrivKeyB64,
|
||||||
|
PersonaDan: danPrivKeyB64,
|
||||||
|
PersonaErin: erinPrivKeyB64,
|
||||||
|
PersonaFrank: frankPrivKeyB64,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Personas returns an (alphabetically) ordered list of the defined
|
||||||
|
// Persona values.
|
||||||
|
func Personas() []Persona {
|
||||||
|
return []Persona{
|
||||||
|
PersonaAlice,
|
||||||
|
PersonaBob,
|
||||||
|
PersonaCarol,
|
||||||
|
PersonaDan,
|
||||||
|
PersonaErin,
|
||||||
|
PersonaFrank,
|
||||||
|
}
|
||||||
|
}
|
||||||
82
did/key_spec_test.go
Normal file
82
did/key_spec_test.go
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
//go:build jwx_es256k
|
||||||
|
|
||||||
|
package did_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
"github.com/ucan-wg/go-ucan/did/testvectors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// TestDidKeyVectors executes tests read from the [test vector files] provided
|
||||||
|
// as part of the DID Key method's [specification].
|
||||||
|
//
|
||||||
|
// [test vector files]: https://github.com/w3c-ccg/did-method-key/tree/main/test-vectors
|
||||||
|
// [specification]: https://w3c-ccg.github.io/did-method-key
|
||||||
|
func TestDidKeyVectors(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for _, f := range []string{
|
||||||
|
// TODO: These test vectors are not supported by go-libp2p/core/crypto
|
||||||
|
// "bls12381.json",
|
||||||
|
"ed25519-x25519.json",
|
||||||
|
"nist-curves.json",
|
||||||
|
"rsa.json",
|
||||||
|
"secp256k1.json",
|
||||||
|
// This test vector only contains a DID Document
|
||||||
|
// "x25519.json",
|
||||||
|
} {
|
||||||
|
vectors := loadTestVectors(t, f)
|
||||||
|
|
||||||
|
t.Run(f, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
for k, vector := range vectors {
|
||||||
|
t.Run(k, func(t *testing.T) {
|
||||||
|
// round-trip pubkey-->did-->pubkey, verified against the test vectors.
|
||||||
|
|
||||||
|
exp := vectorPubKey(t, vector)
|
||||||
|
|
||||||
|
id, err := did.FromPubKey(exp)
|
||||||
|
require.NoError(t, err, f, k)
|
||||||
|
act, err := id.PubKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
assert.Equal(t, k, id.String(), f, k)
|
||||||
|
assert.Equal(t, exp, act)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadTestVectors(t *testing.T, filename string) testvectors.Vectors {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
data, err := os.ReadFile(filepath.Join("testvectors", filename))
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
var vs testvectors.Vectors
|
||||||
|
|
||||||
|
require.NoError(t, json.Unmarshal(data, &vs))
|
||||||
|
|
||||||
|
return vs
|
||||||
|
}
|
||||||
|
|
||||||
|
func vectorPubKey(t *testing.T, v testvectors.Vector) crypto.PubKey {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
|
pubKey, err := v.PubKey()
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotZero(t, pubKey)
|
||||||
|
|
||||||
|
return pubKey
|
||||||
|
}
|
||||||
231
did/testvectors/bls12381.json
Normal file
231
did/testvectors/bls12381.json
Normal file
@@ -0,0 +1,231 @@
|
|||||||
|
{
|
||||||
|
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY": {
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||||
|
"publicKeyBase58": "25EEkQtcLKsEzQ6JTo9cg4W7NHpaurn4Wg6LaNPFq6JQXnrP91SDviUz7KrJVMJd76CtAZFsRLYzvgX2JGxo2ccUHtuHk7ELCWwrkBDfrXCFVfqJKDootee9iVaF6NpdJtBE",
|
||||||
|
"privateKeyBase58": "8TXrPTbhefHvcz2vkGsDLBZT2UMeemveLKbdh5JZCvvn"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY",
|
||||||
|
"publicKeyBase58": "25EEkQtcLKsEzQ6JTo9cg4W7NHpaurn4Wg6LaNPFq6JQXnrP91SDviUz7KrJVMJd76CtAZFsRLYzvgX2JGxo2ccUHtuHk7ELCWwrkBDfrXCFVfqJKDootee9iVaF6NpdJtBE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY#zUC7K4ndUaGZgV7Cp2yJy6JtMoUHY6u7tkcSYUvPrEidqBmLCTLmi6d5WvwnUqejscAkERJ3bfjEiSYtdPkRSE8kSa11hFBr4sTgnbZ95SJj19PN2jdvJjyzpSZgxkyyxNnBNnY"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY": {
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||||
|
"publicKeyBase58": "t5QqHdxR4C6QJWJAnk3qVd2DMr4MVFEefdP43i7fLbR5A2qJkE5bqgEtyzpNsDViGEsMKHMdpo7fKbPMhGihbfxz3Dv2Hw36XvprLHBA5DDFSphmy91oHQFdahQMei2HjoE",
|
||||||
|
"privateKeyBase58": "URWBZN9g2ZfKVdAz1L8pvVwEBqCbGBozt4p8Cootb35"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY",
|
||||||
|
"publicKeyBase58": "t5QqHdxR4C6QJWJAnk3qVd2DMr4MVFEefdP43i7fLbR5A2qJkE5bqgEtyzpNsDViGEsMKHMdpo7fKbPMhGihbfxz3Dv2Hw36XvprLHBA5DDFSphmy91oHQFdahQMei2HjoE"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY#zUC77uxiMKceQoxciSy1xgk3nvP8c8NZXDnaY1xsXZaU5UmsZdnwStUke8Ca8zAdPX3MQTHEMhDTCgfdGU7UrY4RRdVhqZp8FaAaoaXFEVp2ZAM7oj3P45BuTCfc3t9FEGBAEQY"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW": {
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||||
|
"publicKeyBase58": "25VFRgQEfbJ3Pit6Z3mnZbKPK9BdQYGwdmfdcmderjYZ12BFNQYeowjMN1AYKKKcacF3UH35ZNpBqCR8y8QLeeaGLL7UKdKLcFje3VQnosesDNHsU8jBvtvYmLJusxXsSUBC",
|
||||||
|
"privateKeyBase58": "48FTGTBBhezV7Ldk5g392NSxP2hwgEgWiSZQkMoNri7E"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW",
|
||||||
|
"publicKeyBase58": "25VFRgQEfbJ3Pit6Z3mnZbKPK9BdQYGwdmfdcmderjYZ12BFNQYeowjMN1AYKKKcacF3UH35ZNpBqCR8y8QLeeaGLL7UKdKLcFje3VQnosesDNHsU8jBvtvYmLJusxXsSUBC"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW#zUC7KKoJk5ttwuuc8pmQDiUmtckEPTwcaFVZe4DSFV7fURuoRnD17D3xkBK3A9tZqdADkTTMKSwNkhjo9Hs6HfgNUXo48TNRaxU6XPLSPdRgMc15jCD5DfN34ixjoVemY62JxnW"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU": {
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||||
|
"publicKeyBase58": "21LWABB5R6mqxvcU6LWMMt9yCAVyt8C1mHREs1EAX23fLcAEPMK4dWx59Jd6RpJ5geGt881vH9yPzZyC8WpHhS2g296mumPxJA3Aghp9jMoACE13rtTie8FYdgzgUw24eboA",
|
||||||
|
"privateKeyBase58": "86rp8w6Q7zgDdKqYxZsdTyhZogzwbcR7wf3VQrhV3xLG"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU",
|
||||||
|
"publicKeyBase58": "21LWABB5R6mqxvcU6LWMMt9yCAVyt8C1mHREs1EAX23fLcAEPMK4dWx59Jd6RpJ5geGt881vH9yPzZyC8WpHhS2g296mumPxJA3Aghp9jMoACE13rtTie8FYdgzgUw24eboA"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU#zUC7FB43ErjeTPiBLZ8wWT3aBTL7QnJ6AAZh9opgV5dKkw291mC23yTnKQ2pTcSgLbdKnVJ1ARn6XrwxWqvFg5dRFzCjwSg1j35nRgs5c2nbqkJ4auPTyPtkJ3xcABRNWaDX6QU"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA": {
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||||
|
"publicKeyBase58": "21XhJ3o4ZSgDgRoyP4Pp8agXMwLycuRa1U6fM4ZzJBxH3gJEQbiuwP3Qh2zNoofNrBKPqp3FgXxGvW84cFwMD29oA7Q9w3L8Sjcc3e9mZqFgs8iWxSsDNRcbQdoYtGaxu11r",
|
||||||
|
"privateKeyBase58": "5LjJ3yibKGP4zKbNgqeiQ284g8LJYnbF7ZBve7Ke9qZ5"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/bls12381-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||||
|
"type": "Bls12381G2Key2020",
|
||||||
|
"controller": "did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA",
|
||||||
|
"publicKeyBase58": "21XhJ3o4ZSgDgRoyP4Pp8agXMwLycuRa1U6fM4ZzJBxH3gJEQbiuwP3Qh2zNoofNrBKPqp3FgXxGvW84cFwMD29oA7Q9w3L8Sjcc3e9mZqFgs8iWxSsDNRcbQdoYtGaxu11r"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA#zUC7FNFB7UinoJ5tqkeEELWLsytHBdHpwQ7wLVFAYRT6vqdr5uC3JPK6BVNNByj4KxvVKXoirT7VuqptSznjRCgvr7Ksuk42zyFw1GJSYNQSKCpjVcrZXoPUbR1P6zHmr97mVdA"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk": {
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "BLS12381_G1",
|
||||||
|
"x": "im0OQGMTkh4YEhAl16hQwUQTcOaRqIqThqtSwksFK7WaH6Qywypmc3VIDyydmYTe"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "BLS12381_G1",
|
||||||
|
"x": "im0OQGMTkh4YEhAl16hQwUQTcOaRqIqThqtSwksFK7WaH6Qywypmc3VIDyydmYTe",
|
||||||
|
"d": "S7Z1TuL05WHge8od0_mW8b3sRM747caCffsLwS6JZ-c"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "BLS12381_G1",
|
||||||
|
"x": "im0OQGMTkh4YEhAl16hQwUQTcOaRqIqThqtSwksFK7WaH6Qywypmc3VIDyydmYTe"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z5TcCmGLu7HrkT5FTnejDTKcH11LPMQLXMPHTRyzY4KdRvqpPLprH7s1ddWFD38cAkZoiDtofUmJVZyEweUTfwjG5H3znk3ir4tzmuDBUSNbNQ7U6jJqj5bkQLKRaQB1bpFJKGLEq3EBwsfPutL5D7p78kFeLNHznqbf5oGpik7ScaDbGLaTLh1Jtadi6VmPNNd44Cojk#z3tEEysHYz5kkgpfDAByfDVgAuvtSFLHSqoMWmmSZBU1LZtN2sDsAS6RVQSevfxv39kyty"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
293
did/testvectors/ed25519-x25519.json
Normal file
293
did/testvectors/ed25519-x25519.json
Normal file
@@ -0,0 +1,293 @@
|
|||||||
|
{
|
||||||
|
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp": {
|
||||||
|
"seed": "0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||||
|
"type": "Ed25519VerificationKey2018",
|
||||||
|
"controller": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||||
|
"publicKeyBase58": "4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS"
|
||||||
|
},
|
||||||
|
"keyAgreementKeyPair": {
|
||||||
|
"id": "#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||||
|
"publicKeyBase58": "7By6kV2t2d188odEM4ExAve1UithKT6dLva4dwsDT3ak",
|
||||||
|
"privateKeyBase58": "6QN8DfuN9hjgHgPvLXqgzqYE3jRRGRrmJQZkd5tL8paR"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/ed25519-2018/v1",
|
||||||
|
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||||
|
"type": "Ed25519VerificationKey2018",
|
||||||
|
"controller": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||||
|
"publicKeyBase58": "4zvwRjXUKGfvwnParsHAS3HuSVzV5cA4McphgmoCtajS"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp",
|
||||||
|
"publicKeyBase58": "7By6kV2t2d188odEM4ExAve1UithKT6dLva4dwsDT3ak"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6MkiTBz1ymuepAQ4HEHYSF1H8quG5GLVVQR3djdX3mDooWp#z6LShs9GGnqk85isEBzzshkuVWrVKsRp24GnDuHk8QWkARMW"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG": {
|
||||||
|
"seed": "0000000000000000000000000000000000000000000000000000000000000001",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||||
|
"type": "Ed25519VerificationKey2018",
|
||||||
|
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||||
|
"publicKeyBase58": "6ASf5EcmmEHTgDJ4X4ZT5vT6iHVJBXPg5AN5YoTCpGWt"
|
||||||
|
},
|
||||||
|
"keyAgreementKeyPair": {
|
||||||
|
"id": "#z6LSrHyXiPBhUbvPUtyUCdf32sniiMGPTAesgHrtEa4FePtr",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||||
|
"publicKeyBase58": "FcoNC5NqP9CePWbhfz95iHaEsCjGkZUioK9Ck7Qiw286",
|
||||||
|
"privateKeyBase58": "HBTcN2MrXNRj9xF9oi8QqYyuEPv3JLLjQKuEgW9oxVKP"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/ed25519-2018/v1",
|
||||||
|
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||||
|
"type": "Ed25519VerificationKey2018",
|
||||||
|
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||||
|
"publicKeyBase58": "6ASf5EcmmEHTgDJ4X4ZT5vT6iHVJBXPg5AN5YoTCpGWt"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6LSrHyXiPBhUbvPUtyUCdf32sniiMGPTAesgHrtEa4FePtr",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG",
|
||||||
|
"publicKeyBase58": "FcoNC5NqP9CePWbhfz95iHaEsCjGkZUioK9Ck7Qiw286"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6MkjchhfUsD6mmvni8mCdXHw216Xrm9bQe2mBH1P5RDjVJG#z6LSrHyXiPBhUbvPUtyUCdf32sniiMGPTAesgHrtEa4FePtr"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf": {
|
||||||
|
"seed": "0000000000000000000000000000000000000000000000000000000000000002",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||||
|
"type": "Ed25519VerificationKey2018",
|
||||||
|
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||||
|
"publicKeyBase58": "8pM1DN3RiT8vbom5u1sNryaNT1nyL8CTTW3b5PwWXRBH"
|
||||||
|
},
|
||||||
|
"keyAgreementKeyPair": {
|
||||||
|
"id": "#z6LSkkqoZRC34AEpbkhZCqLDcHQVAxuLpQ7kC8XCXMVUfvjE",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||||
|
"publicKeyBase58": "A5fe37PAxhX5WNKngBpGHhC1KpNE7nwbK9oX2tqwxYxU",
|
||||||
|
"privateKeyBase58": "ACa4PPJ1LnPNq1iwS33V3Akh7WtnC71WkKFZ9ccM6sX2"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/ed25519-2018/v1",
|
||||||
|
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||||
|
"type": "Ed25519VerificationKey2018",
|
||||||
|
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||||
|
"publicKeyBase58": "8pM1DN3RiT8vbom5u1sNryaNT1nyL8CTTW3b5PwWXRBH"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6LSkkqoZRC34AEpbkhZCqLDcHQVAxuLpQ7kC8XCXMVUfvjE",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf",
|
||||||
|
"publicKeyBase58": "A5fe37PAxhX5WNKngBpGHhC1KpNE7nwbK9oX2tqwxYxU"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6MknGc3ocHs3zdPiJbnaaqDi58NGb4pk1Sp9WxWufuXSdxf#z6LSkkqoZRC34AEpbkhZCqLDcHQVAxuLpQ7kC8XCXMVUfvjE"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ": {
|
||||||
|
"seed": "0000000000000000000000000000000000000000000000000000000000000003",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||||
|
"type": "Ed25519VerificationKey2018",
|
||||||
|
"controller": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||||
|
"publicKeyBase58": "HPYVwAQmskwT1qEEeRzhoomyfyupJGASQQtCXSNG8XS2"
|
||||||
|
},
|
||||||
|
"keyAgreementKeyPair": {
|
||||||
|
"id": "#z6LSiUo6AEDat8Ze4nQzDo67SGuHLLwsUGkxndHGUjsywHow",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||||
|
"publicKeyBase58": "7ocvdvQinfqtyQ3Dh9aA7ggoVCQkmfaoueZazHETDv3B",
|
||||||
|
"privateKeyBase58": "FZrzd1osCnbK6y6MJzMBW1RcVfL524sNKhSbqRwMuwHT"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/ed25519-2018/v1",
|
||||||
|
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||||
|
"type": "Ed25519VerificationKey2018",
|
||||||
|
"controller": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||||
|
"publicKeyBase58": "HPYVwAQmskwT1qEEeRzhoomyfyupJGASQQtCXSNG8XS2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6LSiUo6AEDat8Ze4nQzDo67SGuHLLwsUGkxndHGUjsywHow",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ",
|
||||||
|
"publicKeyBase58": "7ocvdvQinfqtyQ3Dh9aA7ggoVCQkmfaoueZazHETDv3B"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6MkvqoYXQfDDJRv8L4wKzxYeuKyVZBfi9Qo6Ro8MiLH3kDQ#z6LSiUo6AEDat8Ze4nQzDo67SGuHLLwsUGkxndHGUjsywHow"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU": {
|
||||||
|
"seed": "0000000000000000000000000000000000000000000000000000000000000005",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "Ed25519",
|
||||||
|
"x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "Ed25519",
|
||||||
|
"x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8",
|
||||||
|
"d": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"keyAgreementKeyPair": {
|
||||||
|
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6LSmArkPSdTKjEESsExHRrSwUzYUHgDuWDewXc4nocasvFU",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "X25519",
|
||||||
|
"x": "jRIz3oriXDNZmnb35XQb7K1UIlz3ae1ao1YSqLeBXHs"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "X25519",
|
||||||
|
"x": "jRIz3oriXDNZmnb35XQb7K1UIlz3ae1ao1YSqLeBXHs",
|
||||||
|
"d": "aEAAB3VBFPCQtgF3N__wRiXhMOgeiRGstpPC3gnJ1Eo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "Ed25519",
|
||||||
|
"x": "_eT7oDCtAC98L31MMx9J0T-w7HR-zuvsY08f9MvKne8"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6LSmArkPSdTKjEESsExHRrSwUzYUHgDuWDewXc4nocasvFU",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "X25519",
|
||||||
|
"x": "jRIz3oriXDNZmnb35XQb7K1UIlz3ae1ao1YSqLeBXHs"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6MkwYMhwTvsq376YBAcJHy3vyRWzBgn5vKfVqqDCgm7XVKU#z6LSmArkPSdTKjEESsExHRrSwUzYUHgDuWDewXc4nocasvFU"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
371
did/testvectors/nist-curves.json
Normal file
371
did/testvectors/nist-curves.json
Normal file
@@ -0,0 +1,371 @@
|
|||||||
|
{
|
||||||
|
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv": {
|
||||||
|
"verificationMethod": {
|
||||||
|
"id": "#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-256",
|
||||||
|
"x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns",
|
||||||
|
"y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-256",
|
||||||
|
"x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns",
|
||||||
|
"y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM",
|
||||||
|
"d": "gPh-VvVS8MbvKQ9LSVVmfnxnKjHn4Tqj0bmbpehRlpc"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-256",
|
||||||
|
"x": "igrFmi0whuihKnj9R3Om1SoMph72wUGeFaBbzG2vzns",
|
||||||
|
"y": "efsX5b10x8yjyrj4ny3pGfLcY7Xby1KzgqOdqnsrJIM"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv#zDnaerx9CtbPJ1q36T5Ln5wYt3MQYeGRG5ehnPAmxcf5mDZpv"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169": {
|
||||||
|
"verificationMethod": {
|
||||||
|
"id": "#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-256",
|
||||||
|
"x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
|
||||||
|
"y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-256",
|
||||||
|
"x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
|
||||||
|
"y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU",
|
||||||
|
"d": "YjRs6vNvw4sYrzVVY8ipkEpDAD9PFqw1sUnvPRMA-WI"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-256",
|
||||||
|
"x": "fyNYMN0976ci7xqiSdag3buk-ZCwgXU4kz9XNkBlNUI",
|
||||||
|
"y": "hW2ojTNfH7Jbi8--CJUo3OCbH3y5n91g-IMA9MLMbTU"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169#zDnaerDaTF5BXEavCrfRZEk316dpbLsfPDZ3WJ5hRTPFU2169"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9": {
|
||||||
|
"verificationMethod": {
|
||||||
|
"id": "#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-384",
|
||||||
|
"x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc",
|
||||||
|
"y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-384",
|
||||||
|
"x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc",
|
||||||
|
"y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv",
|
||||||
|
"d": "hAyGZNj9031guBCdpAOaZkO-E5m-LKLYnMIq0-msrp8JLctseaOeNTHmP3uKVWwX"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-384",
|
||||||
|
"x": "lInTxl8fjLKp_UCrxI0WDklahi-7-_6JbtiHjiRvMvhedhKVdHBfi2HCY8t_QJyc",
|
||||||
|
"y": "y6N1IC-2mXxHreETBW7K3mBcw0qGr3CWHCs-yl09yCQRLcyfGv7XhqAngHOu51Zv"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9#z82Lm1MpAkeJcix9K8TMiLd5NMAhnwkjjCBeWHXyu3U4oT2MVJJKXkcVBgjGhnLBn2Kaau9"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54": {
|
||||||
|
"verificationMethod": {
|
||||||
|
"id": "#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-384",
|
||||||
|
"x": "CA-iNoHDg1lL8pvX3d1uvExzVfCz7Rn6tW781Ub8K5MrDf2IMPyL0RTDiaLHC1JT",
|
||||||
|
"y": "Kpnrn8DkXUD3ge4mFxi-DKr0DYO2KuJdwNBrhzLRtfMa3WFMZBiPKUPfJj8dYNl_"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-384",
|
||||||
|
"x": "CA-iNoHDg1lL8pvX3d1uvExzVfCz7Rn6tW781Ub8K5MrDf2IMPyL0RTDiaLHC1JT",
|
||||||
|
"y": "Kpnrn8DkXUD3ge4mFxi-DKr0DYO2KuJdwNBrhzLRtfMa3WFMZBiPKUPfJj8dYNl_",
|
||||||
|
"d": "Xe1HHeh-UsrJPRNLR_Y06VTrWpZYBXi7a7kiRqCgwnAOlJZPwE-xzL3DIIVMavAL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-384",
|
||||||
|
"x": "CA-iNoHDg1lL8pvX3d1uvExzVfCz7Rn6tW781Ub8K5MrDf2IMPyL0RTDiaLHC1JT",
|
||||||
|
"y": "Kpnrn8DkXUD3ge4mFxi-DKr0DYO2KuJdwNBrhzLRtfMa3WFMZBiPKUPfJj8dYNl_"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54#z82LkvCwHNreneWpsgPEbV3gu1C6NFJEBg4srfJ5gdxEsMGRJUz2sG9FE42shbn2xkZJh54"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7": {
|
||||||
|
"verificationMethod": {
|
||||||
|
"id": "#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-521",
|
||||||
|
"x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
|
||||||
|
"y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-521",
|
||||||
|
"x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
|
||||||
|
"y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC",
|
||||||
|
"d": "AHwRaNaGs0jkj_pT6PK2aHep7dJK-yxyoL2bIfVRAceq1baxoiFDo3W14c8E2YZn1k5S53r4a11flhQdaB5guJ_X"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-521",
|
||||||
|
"x": "ASUHPMyichQ0QbHZ9ofNx_l4y7luncn5feKLo3OpJ2nSbZoC7mffolj5uy7s6KSKXFmnNWxGJ42IOrjZ47qqwqyS",
|
||||||
|
"y": "AW9ziIC4ZQQVSNmLlp59yYKrjRY0_VqO-GOIYQ9tYpPraBKUloEId6cI_vynCzlZWZtWpgOM3HPhYEgawQ703RjC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7#z2J9gaYxrKVpdoG9A4gRnmpnRCcxU6agDtFVVBVdn1JedouoZN7SzcyREXXzWgt3gGiwpoHq7K68X4m32D8HgzG8wv3sY5j7"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f": {
|
||||||
|
"verificationMethod": {
|
||||||
|
"id": "#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-521",
|
||||||
|
"x": "AQgyFy6EwH3_u_KXPw8aTXTY7WSVytmbuJeFpq4U6LipxtSmBJe_jjRzms9qubnwm_fGoHMQlvQ1vzS2YLusR2V0",
|
||||||
|
"y": "Ab06MCcgoG7dM2I-VppdLV1k3lDoeHMvyYqHVfP05Ep2O7Zu0Qwd6IVzfZi9K0KMDud22wdnGUpUtFukZo0EeO15"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-521",
|
||||||
|
"x": "AQgyFy6EwH3_u_KXPw8aTXTY7WSVytmbuJeFpq4U6LipxtSmBJe_jjRzms9qubnwm_fGoHMQlvQ1vzS2YLusR2V0",
|
||||||
|
"y": "Ab06MCcgoG7dM2I-VppdLV1k3lDoeHMvyYqHVfP05Ep2O7Zu0Qwd6IVzfZi9K0KMDud22wdnGUpUtFukZo0EeO15",
|
||||||
|
"d": "AbheZ-AA58LP4BpopCGCLH8ZoMdkdJaVOS6KK2NNmDCisr5_Ifxl-qcunrkOJ0CSauA4LJyNbCWcy28Bo6zgHTXQ"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "P-521",
|
||||||
|
"x": "AQgyFy6EwH3_u_KXPw8aTXTY7WSVytmbuJeFpq4U6LipxtSmBJe_jjRzms9qubnwm_fGoHMQlvQ1vzS2YLusR2V0",
|
||||||
|
"y": "Ab06MCcgoG7dM2I-VppdLV1k3lDoeHMvyYqHVfP05Ep2O7Zu0Qwd6IVzfZi9K0KMDud22wdnGUpUtFukZo0EeO15"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f#z2J9gcGdb2nEyMDmzQYv2QZQcM1vXktvy1Pw4MduSWxGabLZ9XESSWLQgbuPhwnXN7zP7HpTzWqrMTzaY5zWe6hpzJ2jnw4f"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb": {
|
||||||
|
"verificationMethod": {
|
||||||
|
"id": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||||
|
"type": "P256Key2021",
|
||||||
|
"controller": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||||
|
"publicKeyBase58": "ekVhkcBFq3w7jULLkBVye6PwaTuMbhJYuzwFnNcgQAPV",
|
||||||
|
"privateKeyBase58": "9p4VRzdmhsnq869vQjVCTrRry7u4TtfRxhvBFJTGU2Cp"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/multikey-2021/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||||
|
"type": "P256Key2021",
|
||||||
|
"controller": "did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb",
|
||||||
|
"publicKeyBase58": "ekVhkcBFq3w7jULLkBVye6PwaTuMbhJYuzwFnNcgQAPV"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb#zDnaeTiq1PdzvZXUaMdezchcMJQpBdH2VN4pgrrEhMCCbmwSb"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
106
did/testvectors/rsa.json
Normal file
106
did/testvectors/rsa.json
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
{
|
||||||
|
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i": {
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "sbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR_9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE_thEmfBvDzm_VVkOIYfxu-Ipuo9J_S5XDNDjczx2v-3oDh5-CIHkU46hvFeCvpUS-L8TJSbgX0kjVk_m4eIb9wh63rtmD6Uz_KBtCo5mmR4TEtcLZKYdqMp3wCjN-TlgHiz_4oVXWbHUefCEe8rFnX1iQnpDHU49_SaXQoud1jCaexFn25n-Aa8f8bc5Vm-5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm-wJ1gxwWJEYPQ",
|
||||||
|
"e": "AQAB"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "sbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR_9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE_thEmfBvDzm_VVkOIYfxu-Ipuo9J_S5XDNDjczx2v-3oDh5-CIHkU46hvFeCvpUS-L8TJSbgX0kjVk_m4eIb9wh63rtmD6Uz_KBtCo5mmR4TEtcLZKYdqMp3wCjN-TlgHiz_4oVXWbHUefCEe8rFnX1iQnpDHU49_SaXQoud1jCaexFn25n-Aa8f8bc5Vm-5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm-wJ1gxwWJEYPQ",
|
||||||
|
"e": "AQAB",
|
||||||
|
"d": "Eym3sT4KLwBzo5pl5nY83-hAti92iLQRizkrKe22RbNi9Y1kKOBatdtGaJqFVztZZu5ERGKNuTd5VdsjJeekSbXviVGRtdHNCvgmRZlWA5261AgIUPxMmKW062GmGJbKQvscFfziBgHK6tyDBd8cZavqMFHi-7ilMYF7IsFBcJKM85x_30pnfd4YwhGQIc9hzv238aOwYKg8c-MzYhEVUnL273jaiLVlfZWQ5ca-GXJHmdOb_Y4fE5gpXfPFBseqleXsMp0VuXxCEsN30LIJHYscdPtbzLD3LFbuMJglFbQqYqssqymILGqJ7Tc2mB2LmXevfqRWz5D7A_K1WzvuoQ",
|
||||||
|
"p": "ANwlk-eVXPQplCmr7VddX8MAlN5YWvfXkbJe2KOhyS7naSlfMyeW6I0z6q6MAI4h8cs9yEzwmN1oEl_6tZ_-NPd1Oda2Hq5jHx0Jq2P5exIMMbzTTHbB-LjMB4c-b1DZLOrL7ZpCS-CcEHvBz4phzHa7gqz2SrNIGozufbjS_tK5",
|
||||||
|
"q": "AM6nKRFqRgHiUtGc0xJawpXJeokGhJQFfinDlakjkptuRQNv0BOz8fRUxk6zwwYrx-T_Yk-0oAFsD8qWIgiXg8Wf0bdRW0L0dIH4c6ff3mSREXeAT2h3XDaF0F1YKns08WyYWtOuIiYWChyO9sweK7AUuaOJ-6lr6lElzTGHVf-l",
|
||||||
|
"dp": "AIHFBPK2cRzchaIq3rVpLVHdveNzYexG_nOOxVVvwRANCUiB_b2Qj3Ts7aIGlS0zhTyxJql0Cig5eNtrBjVRvBdC2t1ebaeOdoC_enBsV8fDuG3-gExg-ySz4JwwiZ2252tg2qbb_a5hULYjARwpmkVDMzyR0mbsUfpRe3q_pcbB",
|
||||||
|
"dq": "Id2bCVOVLXHdiKReor9k7A8cmaAL0gYkasu2lwVRXU9w1-NXAiOXHydVaEhlSXmbRJflkJJVNmZzIAwCf830tko-oAAhKJPPFA2XRoeVdn2fkynf2YrV_cloICP2skI23kkJeW8sAXnTJmL3ZvP6zNxYn8hZCaa5u5qqSdeX7FE",
|
||||||
|
"qi": "WKIToXXnjl7GDbz7jCNbX9nWYOE5BDNzVmwiVOnyGoTZfwJ_qtgizj7pOapxi6dT9S9mMavmeAi6LAsEe1WUWtaKSNhbNh0PUGGXlXHGlhkS8jI1ot0e-scrHAuACE567YQ4VurpNorPKtZ5UENXIn74DEmt4l5m6902VF3X5Wo"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "sbX82NTV6IylxCh7MfV4hlyvaniCajuP97GyOqSvTmoEdBOflFvZ06kR_9D6ctt45Fk6hskfnag2GG69NALVH2o4RCR6tQiLRpKcMRtDYE_thEmfBvDzm_VVkOIYfxu-Ipuo9J_S5XDNDjczx2v-3oDh5-CIHkU46hvFeCvpUS-L8TJSbgX0kjVk_m4eIb9wh63rtmD6Uz_KBtCo5mmR4TEtcLZKYdqMp3wCjN-TlgHiz_4oVXWbHUefCEe8rFnX1iQnpDHU49_SaXQoud1jCaexFn25n-Aa8f8bc5Vm-5SeRwidHa6ErvEhTvf1dz6GoNPp2iRvm-wJ1gxwWJEYPQ",
|
||||||
|
"e": "AQAB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i#z4MXj1wBzi9jUstyPMS4jQqB6KdJaiatPkAtVtGc6bQEQEEsKTic4G7Rou3iBf9vPmT5dbkm9qsZsuVNjq8HCuW1w24nhBFGkRE4cd2Uf2tfrB3N7h4mnyPp1BF3ZttHTYv3DLUPi1zMdkULiow3M1GfXkoC6DoxDUm1jmN6GBj22SjVsr6dxezRVQc7aj9TxE7JLbMH1wh5X3kA58H3DFW8rnYMakFGbca5CB2Jf6CnGQZmL7o5uJAdTwXfy2iiiyPxXEGerMhHwhjTA1mKYobyk2CpeEcmvynADfNZ5MBvcCS7m3XkFCMNUYBS9NQ3fze6vMSUPsNa6GVYmKx2x6JrdEjCk3qRMMmyjnjCMfR4pXbRMZa3i"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2": {
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "qMCkFFRFWtzUyZeK8mgJdyM6SEQcXC5E6JwCRVDld-jlJs8sXNOE_vliexq34wZRQ4hk53-JPFlvZ_QjRgIxdUxSMiZ3S5hlNVvvRaue6SMakA9ugQhnfXaWORro0UbPuHLms-bg5StDP8-8tIezu9c1H1FjwPcdbV6rAvKhyhnsM10qP3v2CPbdE0q3FOsihoKuTelImtO110E7N6fLn4U3EYbC4OyViqlrP1o_1M-R-tiM1cb4pD7XKJnIs6ryZdfOQSPBJwjNqSdN6Py_tdrFgPDTyacSSdpTVADOM2IMAoYbhV1N5APhnjOHBRFyKkF1HffQKpmXQLBqvUNNjuhmpVKWBtrTdcCKrglFXiw0cKGHKxIirjmiOlB_HYHg5UdosyE3_1Txct2U7-WBB6QXak1UgxCzgKYBDI8UPA0RlkUuHHP_Zg0fVXrXIInHO04MYxUeSps5qqyP6dJBu_v_BDn3zUq6LYFwJ_-xsU7zbrKYB4jaRlHPoCj_eDC-rSA2uQ4KXHBB8_aAqNFC9ukWxc26Ifz9dF968DLuL30bi-ZAa2oUh492Pw1bg89J7i4qTsOOfpQvGyDV7TGhKuUG3Hbumfr2w16S-_3EI2RIyd1nYsflE6ZmCkZQMG_lwDAFXaqfyGKEDouJuja4XH8r4fGWeGTrozIoniXT1HU",
|
||||||
|
"e": "AQAB"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "qMCkFFRFWtzUyZeK8mgJdyM6SEQcXC5E6JwCRVDld-jlJs8sXNOE_vliexq34wZRQ4hk53-JPFlvZ_QjRgIxdUxSMiZ3S5hlNVvvRaue6SMakA9ugQhnfXaWORro0UbPuHLms-bg5StDP8-8tIezu9c1H1FjwPcdbV6rAvKhyhnsM10qP3v2CPbdE0q3FOsihoKuTelImtO110E7N6fLn4U3EYbC4OyViqlrP1o_1M-R-tiM1cb4pD7XKJnIs6ryZdfOQSPBJwjNqSdN6Py_tdrFgPDTyacSSdpTVADOM2IMAoYbhV1N5APhnjOHBRFyKkF1HffQKpmXQLBqvUNNjuhmpVKWBtrTdcCKrglFXiw0cKGHKxIirjmiOlB_HYHg5UdosyE3_1Txct2U7-WBB6QXak1UgxCzgKYBDI8UPA0RlkUuHHP_Zg0fVXrXIInHO04MYxUeSps5qqyP6dJBu_v_BDn3zUq6LYFwJ_-xsU7zbrKYB4jaRlHPoCj_eDC-rSA2uQ4KXHBB8_aAqNFC9ukWxc26Ifz9dF968DLuL30bi-ZAa2oUh492Pw1bg89J7i4qTsOOfpQvGyDV7TGhKuUG3Hbumfr2w16S-_3EI2RIyd1nYsflE6ZmCkZQMG_lwDAFXaqfyGKEDouJuja4XH8r4fGWeGTrozIoniXT1HU",
|
||||||
|
"e": "AQAB",
|
||||||
|
"d": "TMq1H-clVG7PihkjCqJbRFLMj9wmx6_qfauYwPBKK-HYfWujdW5vxBO6Q-jpqy7RxhiISmxYCBVuw_BuKMqQtR8Q_G9StBzaWYjHfn3Vp6Poz4umLqOjbI2NWNks_ybpGbd30oAK8V5ZkO04ozJpkN4i92hzK3mIc5-z1HiTNUPMn6cStab0VCn6em_ylltV774CEcRJ3OLgid7OUspRt_rID3qyreYbOulTu5WXHIGEnZDzrciIlz1dbcVldpUhD0VAP5ZErD2uUP5oztBNcTTn0YBF8CrOALuQVdaz_t_sNS3P0kWeT1eQ0QwDskO5Hw-Aey2tFeWk1bQyLoQ1A0jsw8mDbkO2zrGfJoxmVBkueTK-q64_n1kV7W1aeJFRj4NwEWmwcrs8GSOGOn38fGB_Y3Kci04qvD6L0QZbFkAVzcJracnxbTdHCEX0jsAAPbYC8M_8PyrPJvPC4IAAWTRrSRbysb7r7viRf4A1vTK9VT7uYyxj7Kzx2cU12d9QBXYfdQ2744bUE7HqN-Vh2rHvv2l5v6vzBRoZ5_OhHHVeUYwC9LouE9lSVAObbFM-Qe1SvzbbwN91LziI7UzUc_xMAEiNwt6PpnIAWAhdvSRawEllTwUcn89udHd5UhiAcm-RQOqXIdA9Aly6d8TT8R1p-ZnQ_gbZyBZeS39AuvU=",
|
||||||
|
"p": "1p4cypsJeTyVXXc5bQpvzVenPy78OHXtGcFQnbTjW8x1GsvJ-rlHAcjUImd44pgNQNe-iYpeUg3KqfONeedNgQCFd8kP7GoVAd45mEvsGBXvjoCXOBMQlsf8UU_hm_LKhVvTvTmMGoudnNv5qYNDMCGJGzwoG-aSvROlIoXzHmDnusZ-hKsDxM9j0PPz21t99Y_Fr30Oq3FIWXPVmLYmfyZYQkxm9a9WNMkqRbwJuMwGI6V9ABsQ1dW_KJzp_aEBbJLcDr9DsWhm9ErLeAlzyaDYEai6wCtKm9em4LDwCbKhJq3hWEp1sIG-hwx1sk7N4i-b8lBijjEQE-dbSQxUlw==",
|
||||||
|
"q": "yUqMejfrttGujadj7Uf7q91KM7nbQGny4TjD-CqibcFE-s2_DExCgP1wfhUPfJr2uPQDIe4g12uaNoa5GbCSDaQwEmQpurC_5mazt-z-_tbI24hoPQm5Hq67fZz-jDE_3OccLPLIWtajJqmxHbbB5VqskMuXo8KDxPRfBQBhykmb9_5M8pY2ggZOV4shCUn5E9nOnvibvw5Wx4CBtWUtca4rhpd3mVen1d8xCe4xTG_ni_w1lwdxzU1GmRFqgTuZWzL0r2FKzJg7hju1SOEe4tKMxQ-xs2HyNaMM__SLsNmS3lsYZ8r2hqcjEMQQZI0T_O-3BjIpyg986P8j055E0w==",
|
||||||
|
"dp": "DujzJRw6P0L3OYQT6EBmXgSt6NTRzvZaX4SvnhU4CmOc6xynTpTamwQhwLYhjtRzb0LNyO5k-RxeLQpvlL1-A-1OWHEOeyUvim6u36a-ozm659KFLu8cIu2H2PpMuTHX4gXsIuRBmIKEk6YwpRcqbsiVpt-6BZ4yKZKY0Vou9rhSwQYTOhJLc7vYumaIVX_4szumxzdP8pcvKI_EkhRtfj3iudBnAsCIo6gqGKgkoMMD1iwkEALRW5m66w5jrywlVi6pvRiKkmOna2da1V8KvUJAYJGxT7JyP3tu64M_Wd0gFvjTg_fAT1_kJau27YlOAl2-Xso43poH_OoAzIVfxw==",
|
||||||
|
"dq": "XI6Z76z9BxB9mgcpTLc3wzw63XQNnB3bn7JRcjBwhdVD2at3uLjsL5HaAy-98kbzQfJ56kUr9sI0o_Po8yYc0ob3z80c3wpdAx2gb-dbDWVH8KJVhBOPestPzR--cEpJGlNuwkBU3mgplyKaHZamq8a46M-lB5jurEbN1mfpj3GvdSYKzdVCdSFfLqP76eCI1pblinW4b-6w-oVdn0JJ1icHPpkxVmJW-2Hok69iHcqrBtRO9AZpTsTEvKekeI4mIyhYGLi9AzzQyhV0c3GImTXFoutng5t7GyzBUoRpI0W4YeQzYa6TEzGRTylIfGPemATF_OReENp0TlLbb3gsHw==",
|
||||||
|
"qi": "m7uZk4AsOfJ1V2RY8lmEF518toCV7juKuS_b_OUx8B0dRG0_kbF1cH-Tmrgsya3bwkYx5HeZG81rX7SRjh-0nVPOMW3tGqU5U9f59DXqvOItJIJ6wvWvWXnuna2-NstYCotFQWadIKjk4wjEKj-a4NJt4D_F4csyeyqWOH2DiUFzBGGxxdEoD5t_HEeNXuWQ6-SiV0x5ZVMln3TSh7IOMl70Smm8HcQF5mOsWg3N0wIg-yffxPrs6r15TRuW1MfT-bZk2GLrtHF1TkIoT1e00jWK4eBl2oRxiJGONUBMTEHV85Fr0yztnA99AgHnrMbE_4ehvev4h5DEWvFyFuJN_g=="
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "RSA",
|
||||||
|
"n": "qMCkFFRFWtzUyZeK8mgJdyM6SEQcXC5E6JwCRVDld-jlJs8sXNOE_vliexq34wZRQ4hk53-JPFlvZ_QjRgIxdUxSMiZ3S5hlNVvvRaue6SMakA9ugQhnfXaWORro0UbPuHLms-bg5StDP8-8tIezu9c1H1FjwPcdbV6rAvKhyhnsM10qP3v2CPbdE0q3FOsihoKuTelImtO110E7N6fLn4U3EYbC4OyViqlrP1o_1M-R-tiM1cb4pD7XKJnIs6ryZdfOQSPBJwjNqSdN6Py_tdrFgPDTyacSSdpTVADOM2IMAoYbhV1N5APhnjOHBRFyKkF1HffQKpmXQLBqvUNNjuhmpVKWBtrTdcCKrglFXiw0cKGHKxIirjmiOlB_HYHg5UdosyE3_1Txct2U7-WBB6QXak1UgxCzgKYBDI8UPA0RlkUuHHP_Zg0fVXrXIInHO04MYxUeSps5qqyP6dJBu_v_BDn3zUq6LYFwJ_-xsU7zbrKYB4jaRlHPoCj_eDC-rSA2uQ4KXHBB8_aAqNFC9ukWxc26Ifz9dF968DLuL30bi-ZAa2oUh492Pw1bg89J7i4qTsOOfpQvGyDV7TGhKuUG3Hbumfr2w16S-_3EI2RIyd1nYsflE6ZmCkZQMG_lwDAFXaqfyGKEDouJuja4XH8r4fGWeGTrozIoniXT1HU",
|
||||||
|
"e": "AQAB"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2#zgghBUVkqmWS8e1ioRVp2WN9Vw6x4NvnE9PGAyQsPqM3fnfPf8EdauiRVfBTcVDyzhqM5FFC7ekAvuV1cJHawtfgB9wDcru1hPDobk3hqyedijhgWmsYfJCmodkiiFnjNWATE7PvqTyoCjcmrc8yMRXmFPnoASyT5beUd4YZxTE9VfgmavcPy3BSouNmASMQ8xUXeiRwjb7xBaVTiDRjkmyPD7NYZdXuS93gFhyDFr5b3XLg7Rfj9nHEqtHDa7NmAX7iwDAbMUFEfiDEf9hrqZmpAYJracAjTTR8Cvn6mnDXMLwayNG8dcsXFodxok2qksYF4D8ffUxMRmyyQVQhhhmdSi4YaMPqTnC1J6HTG9Yfb98yGSVaWi4TApUhLXFow2ZvB6vqckCNhjCRL2R4MDUSk71qzxWHgezKyDeyThJgdxydrn1osqH94oSeA346eipkJvKqYREXBKwgB5VL6WF4qAK6sVZxJp2dQBfCPVZ4EbsBQaJXaVK7cNcWG8tZBFWZ79gG9Cu6C4u8yjBS8Ux6dCcJPUTLtixQu4z2n5dCsVSNdnP1EEs8ZerZo5pBgc68w4Yuf9KL3xVxPnAB1nRCBfs9cMU6oL1EdyHbqrTfnjE8HpY164akBqe92LFVsk8RusaGsVPrMekT8emTq5y8v8CabuZg5rDs3f9NPEtogjyx49wiub1FecM5B7QqEcZSYiKHgF4mfkteT2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
257
did/testvectors/secp256k1.json
Normal file
257
did/testvectors/secp256k1.json
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
{
|
||||||
|
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme": {
|
||||||
|
"seed": "9085d2bef69286a6cbb51623c8fa258629945cd55ca705cc4e66700396894e0c",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||||
|
"publicKeyBase58": "23o6Sau8NxxzXcgSc3PLcNxrzrZpbLeBn1izfv3jbKhuv",
|
||||||
|
"privateKeyBase58": "AjA4cyPUbbfW5wr6iZeRbJLhgH3qDt6q6LMkRw36KpxT"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme",
|
||||||
|
"publicKeyBase58": "23o6Sau8NxxzXcgSc3PLcNxrzrZpbLeBn1izfv3jbKhuv"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme#zQ3shokFTS3brHcDQrn82RUDfCZESWL1ZdCEJwekUDPQiYBme"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2": {
|
||||||
|
"seed": "f0f4df55a2b3ff13051ea814a8f24ad00f2e469af73c363ac7e9fb999a9072ed",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||||
|
"publicKeyBase58": "291KzQhqCPC18PqH83XKhxv1HdqrdnxyS7dh15t2uNRzJ",
|
||||||
|
"privateKeyBase58": "HDbR1D5W3CoNbUKYzUbHH2PRF1atshtVupXgXTQhNB9E"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2",
|
||||||
|
"publicKeyBase58": "291KzQhqCPC18PqH83XKhxv1HdqrdnxyS7dh15t2uNRzJ"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2#zQ3shtxV1FrJfhqE1dvxYRcCknWNjHc3c5X1y3ZSoPDi2aur2"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N": {
|
||||||
|
"seed": "6b0b91287ae3348f8c2f2552d766f30e3604867e34adc37ccbb74a8e6b893e02",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||||
|
"publicKeyBase58": "oesQ92MLiAkt2pjBcJFbW7H4DvzKJv22cotjYbmC2JEe",
|
||||||
|
"privateKeyBase58": "8CrrWVdzDnvaS7vS5dd2HetFSebwEN46XEFrNDdtWZSZ"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N",
|
||||||
|
"publicKeyBase58": "oesQ92MLiAkt2pjBcJFbW7H4DvzKJv22cotjYbmC2JEe"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N#zQ3shZc2QzApp2oymGvQbzP8eKheVshBHbU4ZYjeXqwSKEn6N"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy": {
|
||||||
|
"seed": "c0a6a7c560d37d7ba81ecee9543721ff48fea3e0fb827d42c1868226540fac15",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||||
|
"publicKeyBase58": "pg3p1vprqePgUoqfAQ1TTgxhL6zLYhHyzooR1pqLxo9F",
|
||||||
|
"privateKeyBase58": "Dy2fnt8ba4NmbRBXas9bo1BtYgpYFr6ThpFhJbuA3PRn"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy",
|
||||||
|
"publicKeyBase58": "pg3p1vprqePgUoqfAQ1TTgxhL6zLYhHyzooR1pqLxo9F"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy#zQ3shadCps5JLAHcZiuX5YUtWHHL8ysBJqFLWvjZDKAWUBGzy"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj": {
|
||||||
|
"seed": "175a232d440be1e0788f25488a73d9416c04b6f924bea6354bf05dd2f1a75133",
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||||
|
"publicKeyBase58": "24waDFAUAS16UpZwQQTXVEAmm17rQRjadjuAeBDW8aqL1",
|
||||||
|
"privateKeyBase58": "2aA6WgZnPiVMBX3LvKSTg3KaFKyzfKpvEacixB3yyTgv"
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/secp256k1-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||||
|
"type": "EcdsaSecp256k1VerificationKey2019",
|
||||||
|
"controller": "did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj",
|
||||||
|
"publicKeyBase58": "24waDFAUAS16UpZwQQTXVEAmm17rQRjadjuAeBDW8aqL1"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj#zQ3shptjE6JwdkeKN4fcpnYQY3m9Cet3NiHdAfpvSUZBFoKBj"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS": {
|
||||||
|
"verificationKeyPair": {
|
||||||
|
"id": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "secp256k1",
|
||||||
|
"x": "TEIJN9vnTq1EXMkqzo7yN_867-foKc2pREv45Fw_QA8",
|
||||||
|
"y": "9yiymlzdxKCiRbYq7p-ArRB-C1ytjHE-eb7RDTi6rVc"
|
||||||
|
},
|
||||||
|
"privateKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "secp256k1",
|
||||||
|
"x": "TEIJN9vnTq1EXMkqzo7yN_867-foKc2pREv45Fw_QA8",
|
||||||
|
"y": "9yiymlzdxKCiRbYq7p-ArRB-C1ytjHE-eb7RDTi6rVc",
|
||||||
|
"d": "J5yKm7OXFsXDEutteGYeT0CAfQJwIlHLSYkQxKtgiyo"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"didDocument": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "EC",
|
||||||
|
"crv": "secp256k1",
|
||||||
|
"x": "TEIJN9vnTq1EXMkqzo7yN_867-foKc2pREv45Fw_QA8",
|
||||||
|
"y": "9yiymlzdxKCiRbYq7p-ArRB-C1ytjHE-eb7RDTi6rVc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"assertionMethod": [
|
||||||
|
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||||
|
],
|
||||||
|
"authentication": [
|
||||||
|
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||||
|
],
|
||||||
|
"capabilityInvocation": [
|
||||||
|
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||||
|
],
|
||||||
|
"capabilityDelegation": [
|
||||||
|
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS#zQ3shjmnWpSDEbYKpaFm4kTs9kXyqG6N2QwCYHNPP4yubqgJS"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
163
did/testvectors/vectors.go
Normal file
163
did/testvectors/vectors.go
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
//go:build jwx_es256k
|
||||||
|
|
||||||
|
package testvectors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"github.com/decred/dcrd/dcrec/secp256k1/v4"
|
||||||
|
"github.com/lestrrat-go/jwx/v2/jwk"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
"github.com/mr-tron/base58"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Vectors map[string]Vector
|
||||||
|
|
||||||
|
// This is pretty gross but the structure allows the repeated Verifier,
|
||||||
|
// PublicKeyJwk and PublicKeyBase58 account for the fact that the test
|
||||||
|
// files are very inconsistent.
|
||||||
|
type Vector struct {
|
||||||
|
VerificationKeyPair Verifier
|
||||||
|
VerificationMethod Verifier
|
||||||
|
PublicKeyJwk json.RawMessage
|
||||||
|
DidDocument json.RawMessage // TODO: if we start producing DID documents, we should test this too
|
||||||
|
}
|
||||||
|
|
||||||
|
type Verifier struct {
|
||||||
|
ID string
|
||||||
|
Type string
|
||||||
|
PublicKeyBase58 string
|
||||||
|
PublicKeyJwk json.RawMessage
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vector) PubKey() (crypto.PubKey, error) {
|
||||||
|
// If the public key is in base58
|
||||||
|
if pubB58 := v.PubKeyBase58(); len(pubB58) > 0 {
|
||||||
|
pubBytes, err := base58.Decode(pubB58)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
t, err := v.PubKeyType()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var unmarshaler crypto.PubKeyUnmarshaller
|
||||||
|
|
||||||
|
switch t {
|
||||||
|
case "Ed25519VerificationKey2018":
|
||||||
|
unmarshaler = crypto.UnmarshalEd25519PublicKey
|
||||||
|
case "EcdsaSecp256k1VerificationKey2019":
|
||||||
|
unmarshaler = crypto.UnmarshalSecp256k1PublicKey
|
||||||
|
// This is weak as it assumes the P256 curve - that's all the vectors contain (for now)
|
||||||
|
case "P256Key2021":
|
||||||
|
unmarshaler = compressedEcdsaPublicKeyUnmarshaler
|
||||||
|
default:
|
||||||
|
return nil, errors.New("failed to resolve unmarshaler")
|
||||||
|
}
|
||||||
|
|
||||||
|
return unmarshaler(pubBytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the public key is in a JWK
|
||||||
|
if pubJwk := v.PubKeyJwk(); len(pubJwk) > 0 {
|
||||||
|
key, err := jwk.ParseKey(pubJwk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var a any
|
||||||
|
|
||||||
|
if err := key.Raw(&a); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch a.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
epub := a.(*ecdsa.PublicKey)
|
||||||
|
|
||||||
|
if epub.Curve == secp256k1.S256() {
|
||||||
|
bytes := append([]byte{0x04}, append(epub.X.Bytes(), epub.Y.Bytes()...)...)
|
||||||
|
|
||||||
|
return crypto.UnmarshalSecp256k1PublicKey(bytes)
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1, err := x509.MarshalPKIXPublicKey(epub)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.UnmarshalECDSAPublicKey(asn1)
|
||||||
|
case ed25519.PublicKey:
|
||||||
|
return crypto.UnmarshalEd25519PublicKey(a.(ed25519.PublicKey))
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
asn1, err := x509.MarshalPKIXPublicKey(a.(*rsa.PublicKey))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.UnmarshalRsaPublicKey(asn1)
|
||||||
|
default:
|
||||||
|
return nil, errors.New("unsupported key type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we don't find a public key at all
|
||||||
|
return nil, errors.New("vector's public key not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vector) PubKeyBase58() string {
|
||||||
|
if len(v.VerificationKeyPair.PublicKeyBase58) > 0 {
|
||||||
|
return v.VerificationKeyPair.PublicKeyBase58
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.VerificationMethod.PublicKeyBase58
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vector) PubKeyJwk() json.RawMessage {
|
||||||
|
if len(v.VerificationKeyPair.PublicKeyJwk) > 0 {
|
||||||
|
return v.VerificationKeyPair.PublicKeyJwk
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v.VerificationMethod.PublicKeyJwk) > 0 {
|
||||||
|
return v.VerificationMethod.PublicKeyJwk
|
||||||
|
}
|
||||||
|
|
||||||
|
return v.PublicKeyJwk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Vector) PubKeyType() (string, error) {
|
||||||
|
if len(v.VerificationKeyPair.Type) > 0 {
|
||||||
|
return v.VerificationKeyPair.Type, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(v.VerificationMethod.Type) > 0 {
|
||||||
|
return v.VerificationMethod.Type, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", errors.New("vector's type not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressedEcdsaPublicKeyUnmarshaler(data []byte) (crypto.PubKey, error) {
|
||||||
|
x, y := elliptic.UnmarshalCompressed(elliptic.P256(), data)
|
||||||
|
|
||||||
|
ecdsaPublicKey := ecdsa.PublicKey{
|
||||||
|
Curve: elliptic.P256(),
|
||||||
|
X: x,
|
||||||
|
Y: y,
|
||||||
|
}
|
||||||
|
|
||||||
|
asn1, err := x509.MarshalPKIXPublicKey(&ecdsaPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return crypto.UnmarshalECDSAPublicKey(asn1)
|
||||||
|
}
|
||||||
80
did/testvectors/x25519.json
Normal file
80
did/testvectors/x25519.json
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
{
|
||||||
|
"didDocument": {
|
||||||
|
"did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F",
|
||||||
|
"publicKeyBase58": "4Dy8E9UaZscuPUf2GLxV44RCNL7oxmEXXkgWXaug1WKV"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F#z6LSeu9HkTHSfLLeUs2nnzUSNedgDUevfNQgQjQC23ZCit6F"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha#z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha",
|
||||||
|
"publicKeyBase58": "J3PiFeuSyLugy4DKn87TwK5cnruRgPtxouzXUqg99Avp"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha#z6LStiZsmxiK4odS4Sb6JmdRFuJ6e1SYP157gtiCyJKfrYha"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/x25519-2019/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ#z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ",
|
||||||
|
"type": "X25519KeyAgreementKey2019",
|
||||||
|
"controller": "did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ",
|
||||||
|
"publicKeyBase58": "CgTbngDMe7yHHfxPMvhpaFRpFoQWKgXAgwenJj8PsFDe"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ#z6LSoMdmJz2Djah2P4L9taDmtqeJ6wwd2HhKZvNToBmvaczQ"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz": {
|
||||||
|
"@context": [
|
||||||
|
"https://www.w3.org/ns/did/v1",
|
||||||
|
"https://w3id.org/security/suites/jws-2020/v1"
|
||||||
|
],
|
||||||
|
"id": "did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz",
|
||||||
|
"verificationMethod": [
|
||||||
|
{
|
||||||
|
"id": "did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz#z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz",
|
||||||
|
"type": "JsonWebKey2020",
|
||||||
|
"controller": "did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz",
|
||||||
|
"publicKeyJwk": {
|
||||||
|
"kty": "OKP",
|
||||||
|
"crv": "X25519",
|
||||||
|
"x": "467ap28wHJGEXJAb4mLrokqq8A-txA_KmoQTcj31XzU"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"keyAgreement": [
|
||||||
|
"did:key:z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz#z6LSrzxMVydCourtpA6JLEYupT7ZUQ34hLfQZfRN5H47zLdz"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
go.mod
34
go.mod
@@ -1,35 +1,43 @@
|
|||||||
module github.com/ucan-wg/go-ucan
|
module github.com/ucan-wg/go-ucan
|
||||||
|
|
||||||
go 1.24.4
|
go 1.23
|
||||||
|
|
||||||
toolchain go1.24.5
|
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/MetaMask/go-did-it v1.0.0-pre1
|
github.com/dave/jennifer v1.7.1
|
||||||
github.com/avast/retry-go/v4 v4.6.1
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0
|
||||||
github.com/ipfs/go-cid v0.5.0
|
github.com/ipfs/go-cid v0.4.1
|
||||||
github.com/ipld/go-ipld-prime v0.21.0
|
github.com/ipld/go-ipld-prime v0.21.0
|
||||||
|
github.com/lestrrat-go/jwx/v2 v2.1.1
|
||||||
|
github.com/libp2p/go-libp2p v0.36.3
|
||||||
|
github.com/mr-tron/base58 v1.2.0
|
||||||
github.com/multiformats/go-multibase v0.2.0
|
github.com/multiformats/go-multibase v0.2.0
|
||||||
github.com/multiformats/go-multicodec v0.9.0
|
github.com/multiformats/go-multicodec v0.9.0
|
||||||
github.com/multiformats/go-multihash v0.2.3
|
github.com/multiformats/go-multihash v0.2.3
|
||||||
github.com/multiformats/go-varint v0.0.7
|
github.com/multiformats/go-varint v0.0.7
|
||||||
github.com/stretchr/testify v1.10.0
|
github.com/stretchr/testify v1.9.0
|
||||||
github.com/ucan-wg/go-varsig v1.0.0
|
gotest.tools/v3 v3.5.1
|
||||||
golang.org/x/crypto v0.40.0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 // indirect
|
github.com/google/go-cmp v0.5.9 // indirect
|
||||||
|
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.2 // indirect
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1 // indirect
|
||||||
|
github.com/lestrrat-go/httprc v1.0.6 // indirect
|
||||||
|
github.com/lestrrat-go/iter v1.0.2 // indirect
|
||||||
|
github.com/lestrrat-go/option v1.0.1 // indirect
|
||||||
github.com/minio/sha256-simd v1.0.1 // indirect
|
github.com/minio/sha256-simd v1.0.1 // indirect
|
||||||
github.com/mr-tron/base58 v1.2.0 // indirect
|
|
||||||
github.com/multiformats/go-base32 v0.1.0 // indirect
|
github.com/multiformats/go-base32 v0.1.0 // indirect
|
||||||
github.com/multiformats/go-base36 v0.2.0 // indirect
|
github.com/multiformats/go-base36 v0.2.0 // indirect
|
||||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
github.com/polydawn/refmt v0.89.0 // indirect
|
github.com/polydawn/refmt v0.89.0 // indirect
|
||||||
|
github.com/segmentio/asm v1.2.0 // indirect
|
||||||
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
github.com/spaolacci/murmur3 v1.1.0 // indirect
|
||||||
golang.org/x/sys v0.34.0 // indirect
|
golang.org/x/crypto v0.25.0 // indirect
|
||||||
|
golang.org/x/sys v0.22.0 // indirect
|
||||||
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
lukechampine.com/blake3 v1.3.0 // indirect
|
lukechampine.com/blake3 v1.3.0 // indirect
|
||||||
|
|||||||
70
go.sum
70
go.sum
@@ -1,34 +1,51 @@
|
|||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/MetaMask/go-did-it v1.0.0-pre1 h1:NTGAC7z52TwFegEF7c+csUr/6Al1nAo6ValAAxOsjto=
|
|
||||||
github.com/MetaMask/go-did-it v1.0.0-pre1/go.mod h1:7m9syDnXFTg5GmUEcydpO4Rs3eYT4McFH7vCw5fp3A4=
|
|
||||||
github.com/avast/retry-go/v4 v4.6.1 h1:VkOLRubHdisGrHnTu89g08aQEWEgRU7LVEop3GbIcMk=
|
|
||||||
github.com/avast/retry-go/v4 v4.6.1/go.mod h1:V6oF8njAwxJ5gRo1Q7Cxab24xs5NCWZBeaHHBklR8mA=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||||
|
github.com/dave/jennifer v1.7.1 h1:B4jJJDHelWcDhlRQxWeo0Npa/pYKBLrirAQoTN45txo=
|
||||||
|
github.com/dave/jennifer v1.7.1/go.mod h1:nXbxhEmQfOZhWml3D1cDK5M1FLnMSozpbFN/m3RmGZc=
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
|
github.com/decred/dcrd/crypto/blake256 v1.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y=
|
||||||
github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg=
|
||||||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
|
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
github.com/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0=
|
||||||
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||||
github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
|
github.com/ipfs/go-cid v0.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s=
|
||||||
github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk=
|
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk=
|
||||||
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
|
github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH9C2E=
|
||||||
github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=
|
github.com/ipld/go-ipld-prime v0.21.0/go.mod h1:3RLqy//ERg/y5oShXXdx5YIp50cFGOanyMctpPjsvxQ=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
|
||||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
|
github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
|
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.2 h1:Cg2gVSc9h7sz9NOByczrbUvLopQmXrfFx//N+AkAr5k=
|
||||||
|
github.com/lestrrat-go/blackmagic v1.0.2/go.mod h1:UrEqBzIR2U6CnzVyUtfM6oZNMt/7O7Vohk2J0OGSAtU=
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE=
|
||||||
|
github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E=
|
||||||
|
github.com/lestrrat-go/httprc v1.0.6 h1:qgmgIRhpvBqexMJjA/PmwSvhNk679oqD1RbovdCGW8k=
|
||||||
|
github.com/lestrrat-go/httprc v1.0.6/go.mod h1:mwwz3JMTPBjHUkkDv/IGJ39aALInZLrhBp0X7KGUZlo=
|
||||||
|
github.com/lestrrat-go/iter v1.0.2 h1:gMXo1q4c2pHmC3dn8LzRhJfP1ceCbgSiT9lUydIzltI=
|
||||||
|
github.com/lestrrat-go/iter v1.0.2/go.mod h1:Momfcq3AnRlRjI5b5O8/G5/BvpzrhoFTZcn06fEOPt4=
|
||||||
|
github.com/lestrrat-go/jwx/v2 v2.1.1 h1:Y2ltVl8J6izLYFs54BVcpXLv5msSW4o8eXwnzZLI32E=
|
||||||
|
github.com/lestrrat-go/jwx/v2 v2.1.1/go.mod h1:4LvZg7oxu6Q5VJwn7Mk/UwooNRnTHUpXBj2C4j3HNx0=
|
||||||
|
github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU=
|
||||||
|
github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I=
|
||||||
|
github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8=
|
||||||
|
github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg=
|
||||||
|
github.com/libp2p/go-libp2p v0.36.3 h1:NHz30+G7D8Y8YmznrVZZla0ofVANrvBl2c+oARfMeDQ=
|
||||||
|
github.com/libp2p/go-libp2p v0.36.3/go.mod h1:4Y5vFyCUiJuluEPmpnKYf6WFx5ViKPUYs/ixe9ANFZ8=
|
||||||
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM=
|
||||||
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8=
|
||||||
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o=
|
||||||
@@ -37,6 +54,8 @@ github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aG
|
|||||||
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI=
|
||||||
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
github.com/multiformats/go-base36 v0.2.0 h1:lFsAbNOGeKtuKozrtBsAkSVhv1p9D0/qedU9rQyccr0=
|
||||||
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
github.com/multiformats/go-base36 v0.2.0/go.mod h1:qvnKE++v+2MWCfePClUEjE78Z7P2a1UV0xHgWc0hkp4=
|
||||||
|
github.com/multiformats/go-multiaddr v0.13.0 h1:BCBzs61E3AGHcYYTv8dqRH43ZfyrqM8RXVPT8t13tLQ=
|
||||||
|
github.com/multiformats/go-multiaddr v0.13.0/go.mod h1:sBXrNzucqkFJhvKOiwwLyqamGa/P5EIXNPLovyhQCII=
|
||||||
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
|
github.com/multiformats/go-multibase v0.2.0 h1:isdYCVLvksgWlMW9OZRYJEa9pZETFivncJHmHnnd87g=
|
||||||
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
|
github.com/multiformats/go-multibase v0.2.0/go.mod h1:bFBZX4lKCA/2lyOFSAoKH5SS6oPyjtnzK/XTFDPkNuk=
|
||||||
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
|
github.com/multiformats/go-multicodec v0.9.0 h1:pb/dlPnzee/Sxv/j4PmkDRxCOi3hXTz3IbPKOXWJkmg=
|
||||||
@@ -52,6 +71,8 @@ github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX
|
|||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
||||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||||
|
github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
|
||||||
|
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
|
||||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||||
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
|
github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs=
|
||||||
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
|
||||||
@@ -59,29 +80,38 @@ github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hg
|
|||||||
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
|
github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
|
||||||
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
|
||||||
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/ucan-wg/go-varsig v1.0.0 h1:Hrc437Zg+B5Eoajg+qZQZI3Q3ocPyjlnp3/Bz9ZnlWw=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/ucan-wg/go-varsig v1.0.0/go.mod h1:Sakln6IPooDPH+ClQ0VvR09TuwUhHcfLqcPiPkMZGh0=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||||
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=
|
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0 h1:GDDkbFiaK8jsSDJfjId/PEGEShv6ugrt4kYsC5UIDaQ=
|
||||||
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
|
github.com/warpfork/go-wish v0.0.0-20220906213052-39a1cc7a02d0/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM=
|
golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
||||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||||
|
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA=
|
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
||||||
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||||
|
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||||
|
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||||
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||||
|
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||||
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE=
|
||||||
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=
|
||||||
|
|||||||
@@ -4,9 +4,7 @@
|
|||||||
package args
|
package args
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"iter"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
@@ -16,12 +14,9 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
"github.com/ipld/go-ipld-prime/printer"
|
"github.com/ipld/go-ipld-prime/printer"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNotFound = errors.New("key not found in meta")
|
|
||||||
|
|
||||||
// Args are the Command's arguments when an invocation Token is processed by the executor.
|
// Args are the Command's arguments when an invocation Token is processed by the executor.
|
||||||
// This also serves as a way to construct the underlying IPLD data with minimum allocations
|
// This also serves as a way to construct the underlying IPLD data with minimum allocations
|
||||||
// and transformations, while hiding the IPLD complexity from the caller.
|
// and transformations, while hiding the IPLD complexity from the caller.
|
||||||
@@ -40,16 +35,6 @@ func New() *Args {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetNode retrieves a value as a raw IPLD node.
|
|
||||||
// Returns ErrNotFound if the given key is missing.
|
|
||||||
func (a *Args) GetNode(key string) (ipld.Node, error) {
|
|
||||||
v, ok := a.Values[key]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add inserts a key/value pair in the Args set.
|
// Add inserts a key/value pair in the Args set.
|
||||||
//
|
//
|
||||||
// Accepted types for val are any CBOR compatible type, or directly IPLD values.
|
// Accepted types for val are any CBOR compatible type, or directly IPLD values.
|
||||||
@@ -63,51 +48,27 @@ func (a *Args) Add(key string, val any) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := limits.ValidateIntegerBoundsIPLD(node); err != nil {
|
|
||||||
return fmt.Errorf("value for key %q: %w", key, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.Values[key] = node
|
a.Values[key] = node
|
||||||
a.Keys = append(a.Keys, key)
|
a.Keys = append(a.Keys, key)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type Iterator interface {
|
|
||||||
Iter() iter.Seq2[string, ipld.Node]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include merges the provided arguments into the existing arguments.
|
// Include merges the provided arguments into the existing arguments.
|
||||||
//
|
//
|
||||||
// If duplicate keys are encountered, the new value is silently dropped
|
// If duplicate keys are encountered, the new value is silently dropped
|
||||||
// without causing an error.
|
// without causing an error.
|
||||||
func (a *Args) Include(other Iterator) {
|
func (a *Args) Include(other *Args) {
|
||||||
for key, value := range other.Iter() {
|
for _, key := range other.Keys {
|
||||||
if _, ok := a.Values[key]; ok {
|
if _, ok := a.Values[key]; ok {
|
||||||
// don't overwrite
|
// don't overwrite
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
a.Values[key] = value
|
a.Values[key] = other.Values[key]
|
||||||
a.Keys = append(a.Keys, key)
|
a.Keys = append(a.Keys, key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Len return the number of arguments.
|
|
||||||
func (a *Args) Len() int {
|
|
||||||
return len(a.Keys)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iter iterates over the args key/values
|
|
||||||
func (a *Args) Iter() iter.Seq2[string, ipld.Node] {
|
|
||||||
return func(yield func(string, ipld.Node) bool) {
|
|
||||||
for _, key := range a.Keys {
|
|
||||||
if !yield(key, a.Values[key]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToIPLD wraps an instance of an Args with an ipld.Node.
|
// ToIPLD wraps an instance of an Args with an ipld.Node.
|
||||||
func (a *Args) ToIPLD() (ipld.Node, error) {
|
func (a *Args) ToIPLD() (ipld.Node, error) {
|
||||||
sort.Strings(a.Keys)
|
sort.Strings(a.Keys)
|
||||||
@@ -174,14 +135,3 @@ func (a *Args) Clone() *Args {
|
|||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validate checks that all values in the Args are valid according to UCAN specs
|
|
||||||
func (a *Args) Validate() error {
|
|
||||||
for key, value := range a.Values {
|
|
||||||
if err := limits.ValidateIntegerBoundsIPLD(value); err != nil {
|
|
||||||
return fmt.Errorf("value for key %q: %w", key, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package args_test
|
package args_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"maps"
|
|
||||||
"sync"
|
"sync"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -9,13 +8,11 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
|
||||||
"github.com/ipld/go-ipld-prime/schema"
|
"github.com/ipld/go-ipld-prime/schema"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/args"
|
"github.com/ucan-wg/go-ucan/pkg/args"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -136,138 +133,23 @@ func TestArgs_Include(t *testing.T) {
|
|||||||
assert.Equal(t, "val4", must(argsIn.Values["key4"].AsString()))
|
assert.Equal(t, "val4", must(argsIn.Values["key4"].AsString()))
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIterCloneEquals(t *testing.T) {
|
|
||||||
a := args.New()
|
|
||||||
|
|
||||||
require.NoError(t, a.Add("foo", "bar"))
|
|
||||||
require.NoError(t, a.Add("baz", 1234))
|
|
||||||
|
|
||||||
expected := map[string]ipld.Node{
|
|
||||||
"foo": basicnode.NewString("bar"),
|
|
||||||
"baz": basicnode.NewInt(1234),
|
|
||||||
}
|
|
||||||
|
|
||||||
// args -> iter
|
|
||||||
require.Equal(t, expected, maps.Collect(a.Iter()))
|
|
||||||
|
|
||||||
// readonly -> iter
|
|
||||||
ro := a.ReadOnly()
|
|
||||||
require.Equal(t, expected, maps.Collect(ro.Iter()))
|
|
||||||
|
|
||||||
// args -> clone -> iter
|
|
||||||
clone := a.Clone()
|
|
||||||
require.Equal(t, expected, maps.Collect(clone.Iter()))
|
|
||||||
|
|
||||||
// readonly -> WriteableClone -> iter
|
|
||||||
wclone := ro.WriteableClone()
|
|
||||||
require.Equal(t, expected, maps.Collect(wclone.Iter()))
|
|
||||||
|
|
||||||
require.True(t, a.Equals(wclone))
|
|
||||||
require.True(t, ro.Equals(wclone.ReadOnly()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInclude(t *testing.T) {
|
|
||||||
a1 := args.New()
|
|
||||||
|
|
||||||
require.NoError(t, a1.Add("samekey", "bar"))
|
|
||||||
require.NoError(t, a1.Add("baz", 1234))
|
|
||||||
|
|
||||||
a2 := args.New()
|
|
||||||
|
|
||||||
require.NoError(t, a2.Add("samekey", "othervalue")) // check no overwrite
|
|
||||||
require.NoError(t, a2.Add("otherkey", 1234))
|
|
||||||
|
|
||||||
a1.Include(a2)
|
|
||||||
|
|
||||||
require.Equal(t, map[string]ipld.Node{
|
|
||||||
"samekey": basicnode.NewString("bar"),
|
|
||||||
"baz": basicnode.NewInt(1234),
|
|
||||||
"otherkey": basicnode.NewInt(1234),
|
|
||||||
}, maps.Collect(a1.Iter()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestArgsIntegerBounds(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
key string
|
|
||||||
val int64
|
|
||||||
wantErr string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid int",
|
|
||||||
key: "valid",
|
|
||||||
val: 42,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "max safe integer",
|
|
||||||
key: "max",
|
|
||||||
val: limits.MaxInt53,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "min safe integer",
|
|
||||||
key: "min",
|
|
||||||
val: limits.MinInt53,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "exceeds max safe integer",
|
|
||||||
key: "tooBig",
|
|
||||||
val: limits.MaxInt53 + 1,
|
|
||||||
wantErr: "exceeds safe integer bounds",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "below min safe integer",
|
|
||||||
key: "tooSmall",
|
|
||||||
val: limits.MinInt53 - 1,
|
|
||||||
wantErr: "exceeds safe integer bounds",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "duplicate key",
|
|
||||||
key: "duplicate",
|
|
||||||
val: 42,
|
|
||||||
wantErr: "duplicate key",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
a := args.New()
|
|
||||||
require.NoError(t, a.Add("duplicate", 1)) // tests duplicate key
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
err := a.Add(tt.key, tt.val)
|
|
||||||
if tt.wantErr != "" {
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Contains(t, err.Error(), tt.wantErr)
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
val, err := a.GetNode(tt.key)
|
|
||||||
require.NoError(t, err)
|
|
||||||
i, err := val.AsInt()
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, tt.val, i)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
argsSchema = "type Args { String : Any }"
|
argsSchema = "type Args { String : Any }"
|
||||||
argsName = "Args"
|
argsName = "Args"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
once sync.Once
|
once sync.Once
|
||||||
ts *schema.TypeSystem
|
ts *schema.TypeSystem
|
||||||
errSchema error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
func argsType() schema.Type {
|
func argsType() schema.Type {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
ts, errSchema = ipld.LoadSchemaBytes([]byte(argsSchema))
|
ts, err = ipld.LoadSchemaBytes([]byte(argsSchema))
|
||||||
})
|
})
|
||||||
if errSchema != nil {
|
if err != nil {
|
||||||
panic(errSchema)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ts.TypeByName(argsName)
|
return ts.TypeByName(argsName)
|
||||||
|
|||||||
@@ -1,71 +0,0 @@
|
|||||||
package args
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Builder allows the fluid construction of an Args.
|
|
||||||
type Builder struct {
|
|
||||||
args *Args
|
|
||||||
errs error
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewBuilder returns a Builder which will assemble the Args.
|
|
||||||
func NewBuilder() *Builder {
|
|
||||||
return &Builder{
|
|
||||||
args: New(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add inserts a new key/val into the Args being assembled while collecting
|
|
||||||
// any errors caused by duplicate keys.
|
|
||||||
func (b *Builder) Add(key string, val any) *Builder {
|
|
||||||
b.errs = errors.Join(b.errs, b.args.Add(key, val))
|
|
||||||
|
|
||||||
return b
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build returns the assembled Args or an error containing a list of
|
|
||||||
// errors encountered while trying to build the Args.
|
|
||||||
func (b *Builder) Build() (*Args, error) {
|
|
||||||
if b.errs != nil {
|
|
||||||
return nil, b.errs
|
|
||||||
}
|
|
||||||
|
|
||||||
return b.args, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// BuildIPLD is the same as Build except it takes the additional step of
|
|
||||||
// converting the Args to an ipld.Node.
|
|
||||||
func (b *Builder) BuildIPLD() (ipld.Node, error) {
|
|
||||||
args, err := b.Build()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return args.ToIPLD()
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBuild is the same as Build except it panics if an error occurs.
|
|
||||||
func (b *Builder) MustBuild() *Args {
|
|
||||||
args, err := b.Build()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
panic(b.errs)
|
|
||||||
}
|
|
||||||
|
|
||||||
return args
|
|
||||||
}
|
|
||||||
|
|
||||||
// MustBuildIPLD is the same as BuildIPLD except it panics if an error
|
|
||||||
// occurs.
|
|
||||||
func (b *Builder) MustBuildIPLD() ipld.Node {
|
|
||||||
node, err := b.BuildIPLD()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package args_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/args"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestBuilder_XXX(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
const (
|
|
||||||
keyOne = "key1"
|
|
||||||
valOne = "string"
|
|
||||||
keyTwo = "key2"
|
|
||||||
valTwo = 42
|
|
||||||
)
|
|
||||||
|
|
||||||
exp := args.New()
|
|
||||||
exp.Add(keyOne, valOne)
|
|
||||||
exp.Add(keyTwo, valTwo)
|
|
||||||
|
|
||||||
expNode, err := exp.ToIPLD()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
disjointKeys := args.NewBuilder().
|
|
||||||
Add(keyOne, valOne).
|
|
||||||
Add(keyTwo, valTwo)
|
|
||||||
|
|
||||||
duplicateKeys := args.NewBuilder().
|
|
||||||
Add(keyOne, valOne).
|
|
||||||
Add(keyTwo, valTwo).
|
|
||||||
Add(keyOne, "oh no!")
|
|
||||||
|
|
||||||
t.Run("MustBuild succeeds with disjoint keys", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var act *args.Args
|
|
||||||
|
|
||||||
require.NotPanics(t, func() {
|
|
||||||
act = disjointKeys.MustBuild()
|
|
||||||
})
|
|
||||||
assert.Equal(t, exp, act)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("MustBuild fails with duplicate keys", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var act *args.Args
|
|
||||||
|
|
||||||
require.Panics(t, func() {
|
|
||||||
act = duplicateKeys.MustBuild()
|
|
||||||
})
|
|
||||||
assert.Nil(t, act)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("MustBuildIPLD succeeds with disjoint keys", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var act ipld.Node
|
|
||||||
|
|
||||||
require.NotPanics(t, func() {
|
|
||||||
act = disjointKeys.MustBuildIPLD()
|
|
||||||
})
|
|
||||||
assert.Equal(t, expNode, act)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("MustBuildIPLD fails with duplicate keys", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
var act ipld.Node
|
|
||||||
|
|
||||||
require.Panics(t, func() {
|
|
||||||
act = duplicateKeys.MustBuildIPLD()
|
|
||||||
})
|
|
||||||
assert.Nil(t, act)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,33 +1,17 @@
|
|||||||
package args
|
package args
|
||||||
|
|
||||||
import (
|
import "github.com/ipld/go-ipld-prime"
|
||||||
"iter"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ReadOnly struct {
|
type ReadOnly struct {
|
||||||
args *Args
|
args *Args
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetNode(key string) (ipld.Node, error) {
|
|
||||||
return r.args.GetNode(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) Len() int {
|
|
||||||
return r.args.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) Iter() iter.Seq2[string, ipld.Node] {
|
|
||||||
return r.args.Iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) ToIPLD() (ipld.Node, error) {
|
func (r ReadOnly) ToIPLD() (ipld.Node, error) {
|
||||||
return r.args.ToIPLD()
|
return r.args.ToIPLD()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) Equals(other ReadOnly) bool {
|
func (r ReadOnly) Equals(other *Args) bool {
|
||||||
return r.args.Equals(other.args)
|
return r.args.Equals(other)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) String() string {
|
func (r ReadOnly) String() string {
|
||||||
|
|||||||
@@ -1,265 +0,0 @@
|
|||||||
package claims
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"iter"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
"github.com/ipld/go-ipld-prime/printer"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/secretbox"
|
|
||||||
)
|
|
||||||
|
|
||||||
var ErrNotFound = errors.New("key not found in claims")
|
|
||||||
|
|
||||||
var ErrNotEncryptable = errors.New("value of this type cannot be encrypted")
|
|
||||||
|
|
||||||
// Claims is a container for claims key-value pairs in an attestation token.
|
|
||||||
// This also serves as a way to construct the underlying IPLD data with minimum allocations
|
|
||||||
// and transformations, while hiding the IPLD complexity from the caller.
|
|
||||||
type Claims struct {
|
|
||||||
// This type must be compatible with the IPLD type represented by the IPLD
|
|
||||||
// schema { String : Any }.
|
|
||||||
|
|
||||||
Keys []string
|
|
||||||
Values map[string]ipld.Node
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewClaims constructs a new Claims.
|
|
||||||
func NewClaims() *Claims {
|
|
||||||
return &Claims{Values: map[string]ipld.Node{}}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBool retrieves a value as a bool.
|
|
||||||
// Returns ErrNotFound if the given key is missing.
|
|
||||||
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
|
||||||
func (m *Claims) GetBool(key string) (bool, error) {
|
|
||||||
v, ok := m.Values[key]
|
|
||||||
if !ok {
|
|
||||||
return false, ErrNotFound
|
|
||||||
}
|
|
||||||
return v.AsBool()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetString retrieves a value as a string.
|
|
||||||
// Returns ErrNotFound if the given key is missing.
|
|
||||||
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
|
||||||
func (m *Claims) GetString(key string) (string, error) {
|
|
||||||
v, ok := m.Values[key]
|
|
||||||
if !ok {
|
|
||||||
return "", ErrNotFound
|
|
||||||
}
|
|
||||||
return v.AsString()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEncryptedString decorates GetString and decrypt its output with the given symmetric encryption key.
|
|
||||||
func (m *Claims) GetEncryptedString(key string, encryptionKey []byte) (string, error) {
|
|
||||||
v, err := m.GetBytes(key)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
decrypted, err := secretbox.DecryptStringWithKey(v, encryptionKey)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(decrypted), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInt64 retrieves a value as an int64.
|
|
||||||
// Returns ErrNotFound if the given key is missing.
|
|
||||||
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
|
||||||
func (m *Claims) GetInt64(key string) (int64, error) {
|
|
||||||
v, ok := m.Values[key]
|
|
||||||
if !ok {
|
|
||||||
return 0, ErrNotFound
|
|
||||||
}
|
|
||||||
return v.AsInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetFloat64 retrieves a value as a float64.
|
|
||||||
// Returns ErrNotFound if the given key is missing.
|
|
||||||
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
|
||||||
func (m *Claims) GetFloat64(key string) (float64, error) {
|
|
||||||
v, ok := m.Values[key]
|
|
||||||
if !ok {
|
|
||||||
return 0, ErrNotFound
|
|
||||||
}
|
|
||||||
return v.AsFloat()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetBytes retrieves a value as a []byte.
|
|
||||||
// Returns ErrNotFound if the given key is missing.
|
|
||||||
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
|
||||||
func (m *Claims) GetBytes(key string) ([]byte, error) {
|
|
||||||
v, ok := m.Values[key]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
return v.AsBytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetEncryptedBytes decorates GetBytes and decrypt its output with the given symmetric encryption key.
|
|
||||||
func (m *Claims) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, error) {
|
|
||||||
v, err := m.GetBytes(key)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
decrypted, err := secretbox.DecryptStringWithKey(v, encryptionKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return decrypted, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetNode retrieves a value as a raw IPLD node.
|
|
||||||
// Returns ErrNotFound if the given key is missing.
|
|
||||||
func (m *Claims) GetNode(key string) (ipld.Node, error) {
|
|
||||||
v, ok := m.Values[key]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add adds a key/value pair in the claims set.
|
|
||||||
// Accepted types for val are any CBOR compatible type, or directly IPLD values.
|
|
||||||
func (m *Claims) Add(key string, val any) error {
|
|
||||||
if _, ok := m.Values[key]; ok {
|
|
||||||
return fmt.Errorf("duplicate key %q", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
node, err := literal.Any(val)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
m.Keys = append(m.Keys, key)
|
|
||||||
m.Values[key] = node
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// AddEncrypted adds a key/value pair in the claims set.
|
|
||||||
// The value is encrypted with the given encryptionKey.
|
|
||||||
// Accepted types for the value are: string, []byte.
|
|
||||||
// The ciphertext will be 40 bytes larger than the plaintext due to encryption overhead.
|
|
||||||
func (m *Claims) AddEncrypted(key string, val any, encryptionKey []byte) error {
|
|
||||||
var encrypted []byte
|
|
||||||
var err error
|
|
||||||
|
|
||||||
switch val := val.(type) {
|
|
||||||
case string:
|
|
||||||
encrypted, err = secretbox.EncryptWithKey([]byte(val), encryptionKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
case []byte:
|
|
||||||
encrypted, err = secretbox.EncryptWithKey(val, encryptionKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return ErrNotEncryptable
|
|
||||||
}
|
|
||||||
|
|
||||||
return m.Add(key, encrypted)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Iterator interface {
|
|
||||||
Iter() iter.Seq2[string, ipld.Node]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include merges the provided claims into the existing one.
|
|
||||||
//
|
|
||||||
// If duplicate keys are encountered, the new value is silently dropped
|
|
||||||
// without causing an error.
|
|
||||||
func (m *Claims) Include(other Iterator) {
|
|
||||||
for key, value := range other.Iter() {
|
|
||||||
if _, ok := m.Values[key]; ok {
|
|
||||||
// don't overwrite
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m.Values[key] = value
|
|
||||||
m.Keys = append(m.Keys, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of key/values.
|
|
||||||
func (m *Claims) Len() int {
|
|
||||||
return len(m.Values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iter iterates over the claims key/values
|
|
||||||
func (m *Claims) Iter() iter.Seq2[string, ipld.Node] {
|
|
||||||
return func(yield func(string, ipld.Node) bool) {
|
|
||||||
for _, key := range m.Keys {
|
|
||||||
if !yield(key, m.Values[key]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals tells if two Claims hold the same key/values.
|
|
||||||
func (m *Claims) Equals(other *Claims) bool {
|
|
||||||
if len(m.Keys) != len(other.Keys) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
if len(m.Values) != len(other.Values) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
for _, key := range m.Keys {
|
|
||||||
if !ipld.DeepEqual(m.Values[key], other.Values[key]) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Claims) String() string {
|
|
||||||
sort.Strings(m.Keys)
|
|
||||||
|
|
||||||
buf := strings.Builder{}
|
|
||||||
buf.WriteString("{")
|
|
||||||
|
|
||||||
for key, node := range m.Values {
|
|
||||||
buf.WriteString("\n\t")
|
|
||||||
buf.WriteString(key)
|
|
||||||
buf.WriteString(": ")
|
|
||||||
buf.WriteString(strings.ReplaceAll(printer.Sprint(node), "\n", "\n\t"))
|
|
||||||
buf.WriteString(",")
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(m.Values) > 0 {
|
|
||||||
buf.WriteString("\n")
|
|
||||||
}
|
|
||||||
buf.WriteString("}")
|
|
||||||
|
|
||||||
return buf.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ReadOnly returns a read-only version of Claims.
|
|
||||||
func (m *Claims) ReadOnly() ReadOnly {
|
|
||||||
return ReadOnly{claims: m}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clone makes a deep copy.
|
|
||||||
func (m *Claims) Clone() *Claims {
|
|
||||||
res := &Claims{
|
|
||||||
Keys: make([]string, len(m.Keys)),
|
|
||||||
Values: make(map[string]ipld.Node, len(m.Values)),
|
|
||||||
}
|
|
||||||
copy(res.Keys, m.Keys)
|
|
||||||
for k, v := range m.Values {
|
|
||||||
res.Values[k] = v
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
package claims_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"maps"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/claims"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestClaims_Add(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
type Unsupported struct{}
|
|
||||||
|
|
||||||
t.Run("error if not primitive or Node", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
err := (&claims.Claims{}).Add("invalid", &Unsupported{})
|
|
||||||
require.Error(t, err)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("encrypted claims", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
key := make([]byte, 32)
|
|
||||||
_, err := rand.Read(key)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
m := claims.NewClaims()
|
|
||||||
|
|
||||||
// string encryption
|
|
||||||
err = m.AddEncrypted("secret", "hello world", key)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
_, err = m.GetString("secret")
|
|
||||||
require.Error(t, err) // the ciphertext is saved as []byte instead of string
|
|
||||||
|
|
||||||
decrypted, err := m.GetEncryptedString("secret", key)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, "hello world", decrypted)
|
|
||||||
|
|
||||||
// bytes encryption
|
|
||||||
originalBytes := make([]byte, 128)
|
|
||||||
_, err = rand.Read(originalBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
err = m.AddEncrypted("secret-bytes", originalBytes, key)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
encryptedBytes, err := m.GetBytes("secret-bytes")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.NotEqual(t, originalBytes, encryptedBytes)
|
|
||||||
|
|
||||||
decryptedBytes, err := m.GetEncryptedBytes("secret-bytes", key)
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, originalBytes, decryptedBytes)
|
|
||||||
|
|
||||||
// error cases
|
|
||||||
t.Run("error on unsupported type", func(t *testing.T) {
|
|
||||||
err := m.AddEncrypted("invalid", 123, key)
|
|
||||||
require.ErrorIs(t, err, claims.ErrNotEncryptable)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("error on invalid key size", func(t *testing.T) {
|
|
||||||
err := m.AddEncrypted("invalid", "test", []byte("short-key"))
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Contains(t, err.Error(), "invalid key size")
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("error on nil key", func(t *testing.T) {
|
|
||||||
err := m.AddEncrypted("invalid", "test", nil)
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Contains(t, err.Error(), "encryption key is required")
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestIterCloneEquals(t *testing.T) {
|
|
||||||
m := claims.NewClaims()
|
|
||||||
|
|
||||||
require.NoError(t, m.Add("foo", "bar"))
|
|
||||||
require.NoError(t, m.Add("baz", 1234))
|
|
||||||
|
|
||||||
expected := map[string]ipld.Node{
|
|
||||||
"foo": basicnode.NewString("bar"),
|
|
||||||
"baz": basicnode.NewInt(1234),
|
|
||||||
}
|
|
||||||
|
|
||||||
// claims -> iter
|
|
||||||
require.Equal(t, expected, maps.Collect(m.Iter()))
|
|
||||||
|
|
||||||
// readonly -> iter
|
|
||||||
ro := m.ReadOnly()
|
|
||||||
require.Equal(t, expected, maps.Collect(ro.Iter()))
|
|
||||||
|
|
||||||
// claims -> clone -> iter
|
|
||||||
clone := m.Clone()
|
|
||||||
require.Equal(t, expected, maps.Collect(clone.Iter()))
|
|
||||||
|
|
||||||
// readonly -> WriteableClone -> iter
|
|
||||||
wclone := ro.WriteableClone()
|
|
||||||
require.Equal(t, expected, maps.Collect(wclone.Iter()))
|
|
||||||
|
|
||||||
require.True(t, m.Equals(wclone))
|
|
||||||
require.True(t, ro.Equals(wclone.ReadOnly()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInclude(t *testing.T) {
|
|
||||||
m1 := claims.NewClaims()
|
|
||||||
|
|
||||||
require.NoError(t, m1.Add("samekey", "bar"))
|
|
||||||
require.NoError(t, m1.Add("baz", 1234))
|
|
||||||
|
|
||||||
m2 := claims.NewClaims()
|
|
||||||
|
|
||||||
require.NoError(t, m2.Add("samekey", "othervalue")) // check no overwrite
|
|
||||||
require.NoError(t, m2.Add("otherkey", 1234))
|
|
||||||
|
|
||||||
m1.Include(m2)
|
|
||||||
|
|
||||||
require.Equal(t, map[string]ipld.Node{
|
|
||||||
"samekey": basicnode.NewString("bar"),
|
|
||||||
"baz": basicnode.NewInt(1234),
|
|
||||||
"otherkey": basicnode.NewInt(1234),
|
|
||||||
}, maps.Collect(m1.Iter()))
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
package claims
|
|
||||||
|
|
||||||
import (
|
|
||||||
"iter"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ReadOnly wraps a Claims into a read-only facade.
|
|
||||||
type ReadOnly struct {
|
|
||||||
claims *Claims
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) GetBool(key string) (bool, error) {
|
|
||||||
return r.claims.GetBool(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) GetString(key string) (string, error) {
|
|
||||||
return r.claims.GetString(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) GetEncryptedString(key string, encryptionKey []byte) (string, error) {
|
|
||||||
return r.claims.GetEncryptedString(key, encryptionKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) GetInt64(key string) (int64, error) {
|
|
||||||
return r.claims.GetInt64(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) GetFloat64(key string) (float64, error) {
|
|
||||||
return r.claims.GetFloat64(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) GetBytes(key string) ([]byte, error) {
|
|
||||||
return r.claims.GetBytes(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, error) {
|
|
||||||
return r.claims.GetEncryptedBytes(key, encryptionKey)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) GetNode(key string) (ipld.Node, error) {
|
|
||||||
return r.claims.GetNode(key)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) Len() int {
|
|
||||||
return r.claims.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) Iter() iter.Seq2[string, ipld.Node] {
|
|
||||||
return r.claims.Iter()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) Equals(other ReadOnly) bool {
|
|
||||||
return r.claims.Equals(other.claims)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) String() string {
|
|
||||||
return r.claims.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) WriteableClone() *Claims {
|
|
||||||
return r.claims.Clone()
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,5 @@
|
|||||||
# Token container
|
# Token container
|
||||||
|
|
||||||
The specification has been promoted to https://github.com/ucan-wg/container.
|
|
||||||
|
|
||||||
## Why do I need that?
|
## Why do I need that?
|
||||||
|
|
||||||
Some common situation asks to package multiple tokens together:
|
Some common situation asks to package multiple tokens together:
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
OH4sIAAAAAAAA/5zXa5MU1R3HcXYjIOAVMQZQUQElCGx3n9M3wLhzn8xl3ZnpuSLB0+ecnp6enft9VBJWjSAkxEVXkBUJAgbULTUq4gULlWhQiauilKiFgiaFoIhoCVFIpXY3VXm2s2/g++RTv+7/+bOGi+nZZfauaNOGzkgrmfzUPZ9cOm50aPP2jdPmvbb81I3vbtiTWHDmwAcZZt+W78+6vCU3Irb6u23LjnR37vp07d5Iz7nHX9+96OnXlsw8e9Oaj87agHQnbDradLTpolyhhFG6hXTEW9k5zBxmdh7PYf+CUYlUJZIgc5O0NrcueJOJrDlsLkKXwBnEYlciBgsAWyqFgdnpy6oxPWU3SCpQE0L+KI9xiugtWibToqI8ptXsJL2rZRtOFAr/3yxqAQfJqG6iS6561FqQsk7ZkecUt9cac9WSpOSGJb9d9njNsIqzmY4lt2HU0YHnLFh4G/qVNqeMOkq0GRdK6vCzJEWL6CFdALzMsQIyEAZUYjlW0nRJJAwPNN5QBY3lAAC8oKuYUsLKqqFRAau8RgGl6UwaU891vofXTUn5Lu58ZDzpRzqlf63e713VHbzpqg8X3iqFfzniqfGtdlx7DrzQfPbDT2RXHLnnX9e/fLJp/eFR27974d9/3HHJyusXt7+Zcoyd9uIPSzePGAZSho/biynJrhC3JrEIFeSUkOc9rmSbwWqlMKl5Wc2d5kRUz1iGjOTLxLwatBZVv9tdkWFIoylbDvjsjjbOXSt7JFhkXVQgBS5ZagSpgewAEkcYqFHAGSojIEJEyCNdFjmMoUoNBjEyIBJhNR0JjEQIkQ1WhiJCSMBkAMl427RzzztvjD8G9jP9SONm9dxw6dFNH78+uqfpgtRV3ZEfJ3jfbfvkw0MLr/J2r205cGjrvOVvdNaa4tPO7GpfMWXU12+8N4ELOK/tu2DJzrc7rztnGEglu0VM521lPl0LmqOuhBRMZ/KC1ahYRXdSUax5bM9nqlRtJ5nKUJFyLjtvMoMyF0yaCvlE0mGLI1MkAKGlFOAEEiaKOx0qUls4YKo3gNRIdgBJpIwIJcgaUCaaJmkaILosaAQLEm8IkgiIpAmY0xECDFUhNhCUiCxzGGgDSIuv+WnPyHPyh1f3XRDuR/p2VXRu29o7XKfEVTN6F/kfv2XLyyfI7vWT+27+NvDZoZk3L7jm+Jm3Os9qPbjd0/fNHdmtTcKnnfKMpnVLf977PTjw2djhfO7CAQLNKFVS89GU325j0/ZcO0ybc+4QnyhhUfTasSoVK1aHxxgqklZIB+yYOjhUZWvRrNNVjqlK1iFUTTZTwFOLpMopW6ZYyeZ03t8AUiPZwSVJjIoAgwxNBSzlOVVWdaRJVOAwMgQKMKQSFXmdcARSEUkGFVUWS4IApAGkhyb+bPHD1yg3jHt1m9KP1D7Z81H2oxu/WEaZY5NOPtrT8fQGafFXc2/7HNw+4q5/2vnn9yJ2zIQFm8907niQ+eKbg9Gde73li/RJnv3dpmV/rY8cBpKRC1lNpmBBL/uSFVd7RkplEuFSOJRzJ4rZTCwlxnRLu9RWZKNCdqhImbBIY6Ag2CxqWrbFcrYsiXj8SibSHpDEWtRjCmeor8x6VL8oNYDUSHZwSVDGEuVYQxR5ATAaAZqu8ghSGQGDhRLAgOVkrGPK8xLPA0OgqkqoIKqDS1o/ZdHt7ldffNPyt3un9yPN+MU73vKTuw8F1z17+fe+51Yefn+s5+rz1t0Fn36m9akHz7viifvOXV1r//KVWRvbn22euGD9LPjK3d0FX2zTe4vgyMuOjRsGkibmcKCuhMPhesxQirLVAkvAEtUDvmilYI5EjIALAIGIfCwcHfKSUgVH0JZJKW2yFpFs2GKEzdlstaLkk0otmw6l7YYt68rURDGXa2RJDWQHkJj/fus4jTeoTCiUACMLOsuyLIepZlABUchCIEm6jCmLeYE3VKgyPOE1QR1Amsif2hd9xD//84nTf92PVDgynzy//f4Pbthd2fXV6dZXxl7bd1oYX7LN61o7f/TyS0bfd2Tes4ww76Y9S5ofK+y7+bONZ59/ZzI/6qLN+49dvX7mlObhHA5Wtz9ZTHJVZ8hU8lS5rN+XUarhUpuiQFeUr+QqcrBsWKyBSMQ0VKR4oEYdqbqYj8slrlyzSL6YyezWqZoTSDgcr5hqCleL1jTBldUbQGokO4gkEkGDCBuiJsssAJKGdEEVMRUhMVRKOVZlBRHoIoZAZBneIAyrchpiODyA9OBLv5tv77LPvqK+98p+pMyOH1b2jFrW+vfukx1XjlkxsnjnNPWdW3t63dHO5nMegCO37Np74uSkNZ7I0i1be0949RWt5y/v/P29k17auiZ0r/PCYS0pqoZ8ArZhD1EVn+IuakF/LOV3hi1WSYmgSDEfzJjSrhCXrg75BE9mcEGwu4uGWrRWg1az3aaLDo56QnWTJeJ3pKqRUnsorNkKXgQaQGokO4DEU5lqiFCDZ6jGMzIiSBcow/EaRw3IExYQwkFGxwKHJIGTDBkwGtIAwoMn+CfL7+77urfL/UxtwS39SH+6sGvR/M3b7j489vTU1OwxHxxnp7rWHVx98fpL9ph/M/fEq1ue+e1N/xDGNz9xCzw8q3vyZT2jp46ePuP9mZvqo/bv+6llOP+kuMfscECbC5eddZJTfHlT2M5bk4GEYvWZS3mxElTCKdkUS8XjcMjXHUtLCaermiY4S2g8zpoiRqIcssplwuUlECoqeiXutikRACONXHcNZAeXpKqCqMmcgURW1lgsAUkHkGUxo6kGhYSIEksg0mXACEjloAGhCmRVZoXBJS2LGo9+PG7/LrnvXXc/0iOu5JnKZS0/9n65Za8n+d3jdfGtx5ZEVtktu9uPT1+9Z8J2T8fGrulTS5mltvn3H+xyHljjfODJHWt3LkycGfOYDYwaBlLF7ra4goKzXg2zyJXysnwlTKS8I2su5R12iiqkXKon4gb0ytyQ30nBONtWymUrZU/cUi9DLppTvJZglavadJxyF5N2wSSBkDNiVWONvJMayP5vSURSkcAYLBYBx0CEVF0SoarxSDQIzyFGRZJMdY2DHBEk2ZA5RmU4BjBwAOlopu2+lb0heeXJP9zxnwAAAP//V8QWnXsQAAA=
|
|
||||||
File diff suppressed because one or more lines are too long
@@ -1 +0,0 @@
|
|||||||
PH4sIAAAAAAAA_5zW65MU1f3HcS7-hB8igpfIgqUxCqLC0tfTfUADMzs7M87ObubaOzMEsM-lp6eb6Zmee49GvEKwVBQRgygIUVHLCwoqoJAAikCQaJRICCqFGPCCcXVFoaBMJbukKs929h94P3nV-ZzvSg2XrEkV9u7kwFW3J6bv7bj7tflfrjm4e8_grc7Dke4jPx6c_Mm87olH_O9Of35_59-X7z2xe056dKx7156F074aOXzCcemCRe-Y4T3n3v7SNV-fdd4qVfcLA48NPDbwArtYxqo1mcxJT2ebmWZmUgE3s09jtUxqMsmQKSZ1ptRBu5lF0WK75ZbawpVO25NW2rLtfgG5XFZKtlwBr1GPVfyKU9YjZS_BOEv0yVouNxmpBUxr-TH6osmv4Uyx-L9NM5PUWqLtmqTohbQnkWp1i5JlxiOWLxAFAdaJeU13Z0gwWsN2AOdzc267A6tz5uDmGTPvUH-pNVfUOWU6CBfLqP9ZkqUl9fc6w3KqRmTVUDWGB1BiiKoDCBhJptRABAuCxrJY0zHPQBZjbLCY8hKCVBOplbMwDXbdsO9O4ZT5m20fTPywBymh3HfTBjDl5MzlT65xpm15kC4-f_2hlTtKyQUbrj78wBPfjn1e3T9i7OmuEfmt25d4z_q2K_0H36lRiw_9NbK0Szy0d3A_kPKuFk6qOYqVTSCj2uGNknxdT0BN0Etlqklxr8QaSqzF468oQl-R0oo7rztKQaK5crqzmMq7vOV4uh4jFZ2zPIrfFL0hS_ZYOFikDSA1ku1FAqJEZIRUA2gMy0OeY4EOKGVlHmCDIgZKKhYhp2NKGQR5YvBYZAWVp4j0Io1b4N905xU_e_SJj1-4pAfpbefa207fnMit_9XP39xxw7D3Fk0bH7vs09W_3uwcG3Vk5Nxv14yYP2Zj7lLPQ01Nnw0bsmXTssuZprL6-dX3zB3U9NH9q8_tB1KhgwUG8gA54HH5OcMyW1vqjmLnO11GNB0Ph6ut8WI1zAbdSMv2FclKhWK2riCS9trFvDvmd3tC0bpo8SAeC5QExZfiO3LeIOuKePkGkBrJnkEiWJYlnjEg1nhERSKouowIUgEvGUTGRBN5Ims6QiyVZVUwNIp4GUNOxr1I199x4OH_y6103Q-3DOhBuhYw1x1Y9eHYjejREfahp2Z9SNaqo83Xb0yff-mFr3_srFg7at3FV2aEy28atv2p8zbc-313YOQ3y65gm1Z9MHzdipHm0H4gFYmaLrQGWjpFLp4ICmagamWKHI560tlAsoa1uNcotoQKWj7fGuvz3Ck1v5hJgvZQJGYIKTlcFrypWocVLHPuEC_oWa2e0M3OVsen-BuZuwayZ-YOckQARDJ4TLAsChqUdVHVOBkA1pA5GVKGkaiqA04CDISiIcoUMhRjCHuRmjJraxO7f_ji-PiTE3pf0g83F4Zc-hF4-7uTV4XPG7xw986TC__2_qlnjrf9OLDrrKfeXioPGvtN0-NHRv8OHh7_5fzP1V3Lt40Zar-z-c3Vtx5O54b0AykTCrGsVS1iKmkdapQ1q6lqKWkV2gD1J3MIY09SlHw5O6yHgn1FqnoTLU66YobsbASoKq46rmoka2c8LomNkHBFYH2VVldNa-GTQgNIjWR7kTiVAIYRsYEFTcMCgpDqogw4TFXGwKqmYaACRtMJ4ClmEDAQFaHAYAbSXqRXxt9z8fIVxXOXjpjI9SDtXIkK6MJ3T6xbv-WuN7azi9K8UGl5a1t056x9G6fgxJJ5g03lSBk_O2juXV0Lnh2kVHfIwcqrKw-9dboId5X-eU5__iRPLhyxMsF03C9ZnWYKtRfZajIbUDjb68k7XFG2UL5adKesKOwrku42qkgrRaK2lI1jYJaMelnpaLf9RhW0sfmUrjk-FymaAcHBDSA1ku1FYjVeBBpGBqUCpjIUKdI5TuAwwqqhiQAAyvAQ6BoDBCgiwSCizMsSQhzfi_SP-2K_-PGE9saxTzMze5Dy-z0P7Di679olRys3fzdIXbZgbpvy0Krm5oXnnP_1w9yzj0yF8_64bCjzW2H22feEv48NLk6_8UD32fWPNvlf_CS18OUB_UDSE1WcaNUKSsqx_SjA-zNeLYI4T0cybfM4pbMg4AknnbbOetjqK1K-blcKUt4DWcNrVBzBIiJ2fGE7W8gCKZbI4WhBy4KkP9MWhA0gNZL9LxJLVMQhA_KEkTAjEkbnBZFILMQGQRLPi5gSpAOqEShqqoFFWZA4jCHoRRLeO9XcMqtwy7p35cd6kIY99snirRBtnvqcHN9benz4hCuG3LJt9bafZki7thTP_mHpi5PGXfbY6N1Nm68Z992tLD51S-q5DR98dsnhAbNSNzwZvOr_-4FUTdpy0XIjlg0nZSHkqaneUBVUCylgZEtmwQ3LhZrhR3WtkrX7PHegSpISVKtKuCYFyu11s5pK6FHbR112BNtsIZUOt1fSHVIuLzYydw1kz8ydoGqUF4nBizIHZYFyog5ZLHCU0QwEOZGROVEiusr824blDCgBSSIMgGdO8ClPP7KYPJnsQIeKG3uQrnz5teFvvD98hrrOc3nrmK3rn9s_-09PjBzaddOwxDl3Hz1xfEx810uPhvYcbL3sL9dNWjnuxamjXnCFEheAP29i5nXf3tGf6y7r6qwrJZfHlLyGLCvuWCf05Qp6JGDYBaNWqnvS5bKN_VRBVaWvSDnJBG4lHBEAl05yIccVIHZabmmLBttdobSjaKY3FRY9alvY42oAqZFsL5KKCAaEQkMlokgpyzKijhlRpaIoGZClCLCU1ahOIGQhFWSDQwLLqkhlzrwkvOH-ScEtSw68Omr87B6kpYWRa185WbvuC8u8aMC0i1pXbHeO72P9982fsHhh2xbf8WGe0y5Zq-wAk3YenZ05evG93MzVAx78aXJlxr1r2hce7A9Srqh6jIIVjNU7INENUg7Tqmz7tFBb2pfy-oq6zxuOOO0kWo27-3yC58tCwi900DxOtpk-k2txFCfuCGKllnOscqVk-W0N1dmoFcw3coI3kO1FElVWYhiiGhzgCS-xPES6zFKVAZxgyIAXJZWjAOsygppAEG_8Z-wIQZzai7Ri5eyp3V8981nL9fET_woAAP__svLMp3sQAAA
|
|
||||||
Binary file not shown.
Binary file not shown.
@@ -1,21 +0,0 @@
|
|||||||
package containertest
|
|
||||||
|
|
||||||
import _ "embed"
|
|
||||||
|
|
||||||
//go:embed Base64StdPadding
|
|
||||||
var Base64StdPadding string
|
|
||||||
|
|
||||||
//go:embed Base64StdPaddingGzipped
|
|
||||||
var Base64StdPaddingGzipped string
|
|
||||||
|
|
||||||
//go:embed Base64URL
|
|
||||||
var Base64URL string
|
|
||||||
|
|
||||||
//go:embed Base64URLGzipped
|
|
||||||
var Base64URLGzipped string
|
|
||||||
|
|
||||||
//go:embed Bytes
|
|
||||||
var Bytes []byte
|
|
||||||
|
|
||||||
//go:embed BytesGzipped
|
|
||||||
var BytesGzipped []byte
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"compress/gzip"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
)
|
|
||||||
|
|
||||||
const containerVersionTag = "ctn-v1"
|
|
||||||
|
|
||||||
type header byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
headerRawBytes = header(0x40)
|
|
||||||
headerBase64StdPadding = header(0x42)
|
|
||||||
headerBase64URL = header(0x43)
|
|
||||||
headerRawBytesGzip = header(0x4D)
|
|
||||||
headerBase64StdPaddingGzip = header(0x4F)
|
|
||||||
headerBase64URLGzip = header(0x50)
|
|
||||||
)
|
|
||||||
|
|
||||||
func (h header) encoder(w io.Writer) *payloadWriter {
|
|
||||||
res := &payloadWriter{rawWriter: w, writer: w, header: h}
|
|
||||||
|
|
||||||
switch h {
|
|
||||||
case headerBase64StdPadding, headerBase64StdPaddingGzip:
|
|
||||||
b64Writer := base64.NewEncoder(base64.StdEncoding, res.writer)
|
|
||||||
res.writer = b64Writer
|
|
||||||
res.closers = append([]io.Closer{b64Writer}, res.closers...)
|
|
||||||
case headerBase64URL, headerBase64URLGzip:
|
|
||||||
b64Writer := base64.NewEncoder(base64.RawURLEncoding, res.writer)
|
|
||||||
res.writer = b64Writer
|
|
||||||
res.closers = append([]io.Closer{b64Writer}, res.closers...)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch h {
|
|
||||||
case headerRawBytesGzip, headerBase64StdPaddingGzip, headerBase64URLGzip:
|
|
||||||
gzipWriter := gzip.NewWriter(res.writer)
|
|
||||||
res.writer = gzipWriter
|
|
||||||
res.closers = append([]io.Closer{gzipWriter}, res.closers...)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
|
|
||||||
func payloadDecoder(r io.Reader) (io.Reader, error) {
|
|
||||||
headerBuf := make([]byte, 1)
|
|
||||||
_, err := r.Read(headerBuf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
h := header(headerBuf[0])
|
|
||||||
|
|
||||||
switch h {
|
|
||||||
case headerRawBytes,
|
|
||||||
headerBase64StdPadding,
|
|
||||||
headerBase64URL,
|
|
||||||
headerRawBytesGzip,
|
|
||||||
headerBase64StdPaddingGzip,
|
|
||||||
headerBase64URLGzip:
|
|
||||||
default:
|
|
||||||
return nil, fmt.Errorf("unknown container header")
|
|
||||||
}
|
|
||||||
|
|
||||||
switch h {
|
|
||||||
case headerBase64StdPadding, headerBase64StdPaddingGzip:
|
|
||||||
r = base64.NewDecoder(base64.StdEncoding, r)
|
|
||||||
case headerBase64URL, headerBase64URLGzip:
|
|
||||||
r = base64.NewDecoder(base64.RawURLEncoding, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
switch h {
|
|
||||||
case headerRawBytesGzip, headerBase64StdPaddingGzip, headerBase64URLGzip:
|
|
||||||
gzipReader, err := gzip.NewReader(r)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
r = gzipReader
|
|
||||||
}
|
|
||||||
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ io.WriteCloser = &payloadWriter{}
|
|
||||||
|
|
||||||
// payloadWriter is tasked with two things:
|
|
||||||
// - prepend the header byte
|
|
||||||
// - call Close() on all the underlying io.Writer
|
|
||||||
type payloadWriter struct {
|
|
||||||
rawWriter io.Writer
|
|
||||||
writer io.Writer
|
|
||||||
header header
|
|
||||||
headerWrote bool
|
|
||||||
closers []io.Closer
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *payloadWriter) Write(p []byte) (n int, err error) {
|
|
||||||
if !w.headerWrote {
|
|
||||||
_, err := w.rawWriter.Write([]byte{byte(w.header)})
|
|
||||||
if err != nil {
|
|
||||||
return 0, err
|
|
||||||
}
|
|
||||||
w.headerWrote = true
|
|
||||||
}
|
|
||||||
return w.writer.Write(p)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w *payloadWriter) Close() error {
|
|
||||||
var errs error
|
|
||||||
for _, closer := range w.closers {
|
|
||||||
if err := closer.Close(); err != nil {
|
|
||||||
errs = errors.Join(errs, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
@@ -1,15 +1,15 @@
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"encoding/base64"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"iter"
|
"iter"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/codec/cbor"
|
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/token"
|
"github.com/ucan-wg/go-ucan/token"
|
||||||
@@ -18,34 +18,87 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var ErrNotFound = fmt.Errorf("not found")
|
var ErrNotFound = fmt.Errorf("not found")
|
||||||
var ErrMultipleInvocations = fmt.Errorf("multiple invocations")
|
|
||||||
|
|
||||||
// Reader is a token container reader. It exposes the tokens conveniently decoded.
|
// Reader is a token container reader. It exposes the tokens conveniently decoded.
|
||||||
type Reader map[cid.Cid]bundle
|
type Reader map[cid.Cid]token.Token
|
||||||
|
|
||||||
type bundle struct {
|
// GetToken returns an arbitrary decoded token, from its CID.
|
||||||
sealed []byte
|
// If not found, ErrNotFound is returned.
|
||||||
token token.Token
|
func (ctn Reader) GetToken(cid cid.Cid) (token.Token, error) {
|
||||||
|
tkn, ok := ctn[cid]
|
||||||
|
if !ok {
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
return tkn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromBytes decodes a container from a []byte
|
// GetDelegation is the same as GetToken but only return a delegation.Token, with the right type.
|
||||||
func FromBytes(data []byte) (Reader, error) {
|
func (ctn Reader) GetDelegation(cid cid.Cid) (*delegation.Token, error) {
|
||||||
return FromReader(bytes.NewReader(data))
|
tkn, err := ctn.GetToken(cid)
|
||||||
|
if errors.Is(err, ErrNotFound) {
|
||||||
|
return nil, delegation.ErrDelegationNotFound
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if tkn, ok := tkn.(*delegation.Token); ok {
|
||||||
|
return tkn, nil
|
||||||
|
}
|
||||||
|
return nil, delegation.ErrDelegationNotFound
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromString decodes a container from a string
|
// GetAllDelegations returns all the delegation.Token in the container.
|
||||||
func FromString(s string) (Reader, error) {
|
func (ctn Reader) GetAllDelegations() iter.Seq2[cid.Cid, *delegation.Token] {
|
||||||
return FromReader(strings.NewReader(s))
|
return func(yield func(cid.Cid, *delegation.Token) bool) {
|
||||||
|
for c, t := range ctn {
|
||||||
|
if t, ok := t.(*delegation.Token); ok {
|
||||||
|
if !yield(c, t) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FromReader decodes a container from an io.Reader.
|
// GetInvocation returns the first found invocation.Token.
|
||||||
func FromReader(r io.Reader) (Reader, error) {
|
// If none are found, ErrNotFound is returned.
|
||||||
payload, err := payloadDecoder(r)
|
func (ctn Reader) GetInvocation() (*invocation.Token, error) {
|
||||||
|
for _, t := range ctn {
|
||||||
|
if inv, ok := t.(*invocation.Token); ok {
|
||||||
|
return inv, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, ErrNotFound
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromCar(r io.Reader) (Reader, error) {
|
||||||
|
_, it, err := readCar(r)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
n, err := ipld.DecodeStreaming(payload, cbor.Decode)
|
ctn := make(Reader)
|
||||||
|
|
||||||
|
for block, err := range it {
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ctn.addToken(block.data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ctn, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromCarBase64(r io.Reader) (Reader, error) {
|
||||||
|
return FromCar(base64.NewDecoder(base64.StdEncoding, r))
|
||||||
|
}
|
||||||
|
|
||||||
|
func FromCbor(r io.Reader) (Reader, error) {
|
||||||
|
n, err := ipld.DecodeStreaming(r, dagcbor.Decode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -67,7 +120,7 @@ func FromReader(r io.Reader) (Reader, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("invalid container format: version must be string")
|
return nil, fmt.Errorf("invalid container format: version must be string")
|
||||||
}
|
}
|
||||||
if version != containerVersionTag {
|
if version != currentContainerVersion {
|
||||||
return nil, fmt.Errorf("unsupported container version: %s", version)
|
return nil, fmt.Errorf("unsupported container version: %s", version)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,122 +147,8 @@ func FromReader(r io.Reader) (Reader, error) {
|
|||||||
return ctn, nil
|
return ctn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetToken returns an arbitrary decoded token, from its CID.
|
func FromCborBase64(r io.Reader) (Reader, error) {
|
||||||
// If not found, ErrNotFound is returned.
|
return FromCbor(base64.NewDecoder(base64.StdEncoding, r))
|
||||||
func (ctn Reader) GetToken(cid cid.Cid) (token.Token, error) {
|
|
||||||
bndl, ok := ctn[cid]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
return bndl.token, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetSealed returns an arbitrary sealed token, from its CID.
|
|
||||||
// If not found, ErrNotFound is returned.
|
|
||||||
func (ctn Reader) GetSealed(cid cid.Cid) ([]byte, error) {
|
|
||||||
bndl, ok := ctn[cid]
|
|
||||||
if !ok {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
return bndl.sealed, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllTokens return all the tokens in the container.
|
|
||||||
func (ctn Reader) GetAllTokens() iter.Seq[token.Bundle] {
|
|
||||||
return func(yield func(token.Bundle) bool) {
|
|
||||||
for c, bndl := range ctn {
|
|
||||||
if !yield(token.Bundle{
|
|
||||||
Cid: c,
|
|
||||||
Decoded: bndl.token,
|
|
||||||
Sealed: bndl.sealed,
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDelegation is the same as GetToken but only return a delegation.Token, with the right type.
|
|
||||||
// If not found, delegation.ErrDelegationNotFound is returned.
|
|
||||||
func (ctn Reader) GetDelegation(cid cid.Cid) (*delegation.Token, error) {
|
|
||||||
tkn, err := ctn.GetToken(cid)
|
|
||||||
if err != nil { // only ErrNotFound expected
|
|
||||||
return nil, delegation.ErrDelegationNotFound
|
|
||||||
}
|
|
||||||
if tkn, ok := tkn.(*delegation.Token); ok {
|
|
||||||
return tkn, nil
|
|
||||||
}
|
|
||||||
return nil, delegation.ErrDelegationNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDelegationBundle is the same as GetToken but only return a delegation.Bundle, with the right type.
|
|
||||||
// If not found, delegation.ErrDelegationNotFound is returned.
|
|
||||||
func (ctn Reader) GetDelegationBundle(cid cid.Cid) (*delegation.Bundle, error) {
|
|
||||||
bndl, ok := ctn[cid]
|
|
||||||
if !ok {
|
|
||||||
return nil, delegation.ErrDelegationNotFound
|
|
||||||
}
|
|
||||||
if tkn, ok := bndl.token.(*delegation.Token); ok {
|
|
||||||
return &delegation.Bundle{
|
|
||||||
Cid: cid,
|
|
||||||
Decoded: tkn,
|
|
||||||
Sealed: bndl.sealed,
|
|
||||||
}, nil
|
|
||||||
}
|
|
||||||
return nil, delegation.ErrDelegationNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllDelegations returns all the delegation.Token in the container.
|
|
||||||
func (ctn Reader) GetAllDelegations() iter.Seq[*delegation.Bundle] {
|
|
||||||
return func(yield func(*delegation.Bundle) bool) {
|
|
||||||
for c, bndl := range ctn {
|
|
||||||
if t, ok := bndl.token.(*delegation.Token); ok {
|
|
||||||
if !yield(&delegation.Bundle{
|
|
||||||
Cid: c,
|
|
||||||
Decoded: t,
|
|
||||||
Sealed: bndl.sealed,
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetInvocation returns a single invocation.Token.
|
|
||||||
// If none are found, ErrNotFound is returned.
|
|
||||||
// If more than one invocation exists, ErrMultipleInvocations is returned.
|
|
||||||
func (ctn Reader) GetInvocation() (*invocation.Token, error) {
|
|
||||||
var res *invocation.Token
|
|
||||||
for _, bndl := range ctn {
|
|
||||||
if inv, ok := bndl.token.(*invocation.Token); ok {
|
|
||||||
if res != nil {
|
|
||||||
return nil, ErrMultipleInvocations
|
|
||||||
}
|
|
||||||
res = inv
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if res == nil {
|
|
||||||
return nil, ErrNotFound
|
|
||||||
}
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetAllInvocations returns all the invocation.Token in the container.
|
|
||||||
func (ctn Reader) GetAllInvocations() iter.Seq[invocation.Bundle] {
|
|
||||||
return func(yield func(invocation.Bundle) bool) {
|
|
||||||
for c, bndl := range ctn {
|
|
||||||
if t, ok := bndl.token.(*invocation.Token); ok {
|
|
||||||
if !yield(invocation.Bundle{
|
|
||||||
Cid: c,
|
|
||||||
Decoded: t,
|
|
||||||
Sealed: bndl.sealed,
|
|
||||||
}) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctn Reader) addToken(data []byte) error {
|
func (ctn Reader) addToken(data []byte) error {
|
||||||
@@ -217,19 +156,6 @@ func (ctn Reader) addToken(data []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
ctn[c] = bundle{
|
ctn[c] = tkn
|
||||||
sealed: data,
|
|
||||||
token: tkn,
|
|
||||||
}
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToWriter convert a container Reader into a Writer.
|
|
||||||
// Most likely, you only want to use this in tests for convenience.
|
|
||||||
func (ctn Reader) ToWriter() Writer {
|
|
||||||
writer := NewWriter()
|
|
||||||
for _, bndl := range ctn {
|
|
||||||
writer.AddSealed(bndl.sealed)
|
|
||||||
}
|
|
||||||
return writer
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -9,12 +9,11 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/MetaMask/go-did-it"
|
|
||||||
"github.com/MetaMask/go-did-it/controller/did-key"
|
|
||||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||||
@@ -23,22 +22,14 @@ import (
|
|||||||
|
|
||||||
func TestContainerRoundTrip(t *testing.T) {
|
func TestContainerRoundTrip(t *testing.T) {
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name string
|
name string
|
||||||
expectedHeader header
|
writer func(ctn Writer, w io.Writer) error
|
||||||
writer any
|
reader func(io.Reader) (Reader, error)
|
||||||
}{
|
}{
|
||||||
{"Bytes", headerRawBytes, Writer.ToBytes},
|
{"car", Writer.ToCar, FromCar},
|
||||||
{"BytesWriter", headerRawBytes, Writer.ToBytesWriter},
|
{"carBase64", Writer.ToCarBase64, FromCarBase64},
|
||||||
{"BytesGzipped", headerRawBytesGzip, Writer.ToBytesGzipped},
|
{"cbor", Writer.ToCbor, FromCbor},
|
||||||
{"BytesGzippedWriter", headerRawBytesGzip, Writer.ToBytesGzippedWriter},
|
{"cborBase64", Writer.ToCborBase64, FromCborBase64},
|
||||||
{"Base64StdPadding", headerBase64StdPadding, Writer.ToBase64StdPadding},
|
|
||||||
{"Base64StdPaddingWriter", headerBase64StdPadding, Writer.ToBase64StdPaddingWriter},
|
|
||||||
{"Base64StdPaddingGzipped", headerBase64StdPaddingGzip, Writer.ToBase64StdPaddingGzipped},
|
|
||||||
{"Base64StdPaddingGzippedWriter", headerBase64StdPaddingGzip, Writer.ToBase64StdPaddingGzippedWriter},
|
|
||||||
{"Base64URL", headerBase64URL, Writer.ToBase64URL},
|
|
||||||
{"Base64URLWriter", headerBase64URL, Writer.ToBase64URLWriter},
|
|
||||||
{"Base64URLGzipped", headerBase64URLGzip, Writer.ToBase64URLGzipped},
|
|
||||||
{"Base64URLGzipWriter", headerBase64URLGzip, Writer.ToBase64URLGzipWriter},
|
|
||||||
} {
|
} {
|
||||||
t.Run(tc.name, func(t *testing.T) {
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
tokens := make(map[cid.Cid]*delegation.Token)
|
tokens := make(map[cid.Cid]*delegation.Token)
|
||||||
@@ -48,53 +39,21 @@ func TestContainerRoundTrip(t *testing.T) {
|
|||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
dlg, c, data := randToken()
|
dlg, c, data := randToken()
|
||||||
writer.AddSealed(data)
|
writer.AddSealed(c, data)
|
||||||
tokens[c] = dlg
|
tokens[c] = dlg
|
||||||
dataSize += len(data)
|
dataSize += len(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
var reader Reader
|
buf := bytes.NewBuffer(nil)
|
||||||
var serialLen int
|
|
||||||
|
|
||||||
switch fn := tc.writer.(type) {
|
err := tc.writer(writer, buf)
|
||||||
case func(ctn Writer, w io.Writer) error:
|
require.NoError(t, err)
|
||||||
buf := bytes.NewBuffer(nil)
|
|
||||||
err := fn(writer, buf)
|
|
||||||
require.NoError(t, err)
|
|
||||||
serialLen = buf.Len()
|
|
||||||
|
|
||||||
h, err := buf.ReadByte()
|
t.Logf("data size %d", dataSize)
|
||||||
require.NoError(t, err)
|
t.Logf("container overhead: %d%%, %d bytes", int(float32(buf.Len()-dataSize)/float32(dataSize)*100.0), buf.Len()-dataSize)
|
||||||
require.Equal(t, byte(tc.expectedHeader), h)
|
|
||||||
err = buf.UnreadByte()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
reader, err = FromReader(bytes.NewReader(buf.Bytes()))
|
reader, err := tc.reader(bytes.NewReader(buf.Bytes()))
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
case func(ctn Writer) ([]byte, error):
|
|
||||||
b, err := fn(writer)
|
|
||||||
require.NoError(t, err)
|
|
||||||
serialLen = len(b)
|
|
||||||
|
|
||||||
require.Equal(t, byte(tc.expectedHeader), b[0])
|
|
||||||
|
|
||||||
reader, err = FromBytes(b)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
case func(ctn Writer) (string, error):
|
|
||||||
s, err := fn(writer)
|
|
||||||
require.NoError(t, err)
|
|
||||||
serialLen = len(s)
|
|
||||||
|
|
||||||
require.Equal(t, byte(tc.expectedHeader), s[0])
|
|
||||||
|
|
||||||
reader, err = FromString(s)
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Logf("data size %d, container size %d, overhead: %d%%, %d bytes",
|
|
||||||
dataSize, serialLen, int(float32(serialLen-dataSize)/float32(dataSize)*100.0), serialLen-dataSize)
|
|
||||||
|
|
||||||
for c, dlg := range tokens {
|
for c, dlg := range tokens {
|
||||||
tknRead, err := reader.GetToken(c)
|
tknRead, err := reader.GetToken(c)
|
||||||
@@ -139,18 +98,16 @@ func BenchmarkContainerSerialisation(b *testing.B) {
|
|||||||
writer func(ctn Writer, w io.Writer) error
|
writer func(ctn Writer, w io.Writer) error
|
||||||
reader func(io.Reader) (Reader, error)
|
reader func(io.Reader) (Reader, error)
|
||||||
}{
|
}{
|
||||||
{"Bytes", Writer.ToBytesWriter, FromReader},
|
{"car", Writer.ToCar, FromCar},
|
||||||
{"BytesGzipped", Writer.ToBytesGzippedWriter, FromReader},
|
{"carBase64", Writer.ToCarBase64, FromCarBase64},
|
||||||
{"Base64StdPadding", Writer.ToBase64StdPaddingWriter, FromReader},
|
{"cbor", Writer.ToCbor, FromCbor},
|
||||||
{"Base64StdPaddingGzipped", Writer.ToBase64StdPaddingGzippedWriter, FromReader},
|
{"cborBase64", Writer.ToCborBase64, FromCborBase64},
|
||||||
{"Base64URL", Writer.ToBase64URLWriter, FromReader},
|
|
||||||
{"Base64URLGzip", Writer.ToBase64URLGzipWriter, FromReader},
|
|
||||||
} {
|
} {
|
||||||
writer := NewWriter()
|
writer := NewWriter()
|
||||||
|
|
||||||
for i := 0; i < 10; i++ {
|
for i := 0; i < 10; i++ {
|
||||||
_, _, data := randToken()
|
_, c, data := randToken()
|
||||||
writer.AddSealed(data)
|
writer.AddSealed(c, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
buf := bytes.NewBuffer(nil)
|
buf := bytes.NewBuffer(nil)
|
||||||
@@ -173,12 +130,15 @@ func BenchmarkContainerSerialisation(b *testing.B) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func randDID() (ed25519.PrivateKey, did.DID) {
|
func randDID() (crypto.PrivKey, did.DID) {
|
||||||
_, privKey, err := ed25519.GenerateKeyPair()
|
privKey, _, err := crypto.GenerateEd25519Key(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
d, err := did.FromPrivKey(privKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
d := didkeyctl.FromPrivateKey(privKey)
|
|
||||||
return privKey, d
|
return privKey, d
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,12 +160,13 @@ func randToken() (*delegation.Token, cid.Cid, []byte) {
|
|||||||
|
|
||||||
opts := []delegation.Option{
|
opts := []delegation.Option{
|
||||||
delegation.WithExpiration(time.Now().Add(time.Hour)),
|
delegation.WithExpiration(time.Now().Add(time.Hour)),
|
||||||
|
delegation.WithSubject(iss),
|
||||||
}
|
}
|
||||||
for i := 0; i < 3; i++ {
|
for i := 0; i < 3; i++ {
|
||||||
opts = append(opts, delegation.WithMeta(randomString(8), randomString(10)))
|
opts = append(opts, delegation.WithMeta(randomString(8), randomString(10)))
|
||||||
}
|
}
|
||||||
|
|
||||||
t, err := delegation.Root(iss, aud, cmd, pol, opts...)
|
t, err := delegation.New(iss, aud, cmd, pol, opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -221,20 +182,21 @@ func FuzzContainerRead(f *testing.F) {
|
|||||||
for tokenCount := 0; tokenCount < 10; tokenCount++ {
|
for tokenCount := 0; tokenCount < 10; tokenCount++ {
|
||||||
writer := NewWriter()
|
writer := NewWriter()
|
||||||
for i := 0; i < tokenCount; i++ {
|
for i := 0; i < tokenCount; i++ {
|
||||||
_, _, data := randToken()
|
_, c, data := randToken()
|
||||||
writer.AddSealed(data)
|
writer.AddSealed(c, data)
|
||||||
}
|
}
|
||||||
data, err := writer.ToBytes()
|
buf := bytes.NewBuffer(nil)
|
||||||
|
err := writer.ToCbor(buf)
|
||||||
require.NoError(f, err)
|
require.NoError(f, err)
|
||||||
|
|
||||||
f.Add(data)
|
f.Add(buf.Bytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
f.Fuzz(func(t *testing.T, data []byte) {
|
f.Fuzz(func(t *testing.T, data []byte) {
|
||||||
start := time.Now()
|
start := time.Now()
|
||||||
|
|
||||||
// search for panics
|
// search for panics
|
||||||
_, _ = FromBytes(data)
|
_, _ = FromCbor(bytes.NewReader(data))
|
||||||
|
|
||||||
if time.Since(start) > 100*time.Millisecond {
|
if time.Since(start) > 100*time.Millisecond {
|
||||||
panic("too long")
|
panic("too long")
|
||||||
|
|||||||
@@ -1,145 +1,65 @@
|
|||||||
package container
|
package container
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"encoding/base64"
|
||||||
"io"
|
"io"
|
||||||
"slices"
|
|
||||||
|
|
||||||
|
"github.com/ipfs/go-cid"
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/codec/cbor"
|
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
"github.com/ipld/go-ipld-prime/fluent/qp"
|
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TODO: should we have a multibase to wrap the cbor? but there is no reader/write in go-multibase :-(
|
||||||
|
|
||||||
|
const currentContainerVersion = "ctn-v1"
|
||||||
|
|
||||||
// Writer is a token container writer. It provides a convenient way to aggregate and serialize tokens together.
|
// Writer is a token container writer. It provides a convenient way to aggregate and serialize tokens together.
|
||||||
type Writer map[string]struct{}
|
type Writer map[cid.Cid][]byte
|
||||||
|
|
||||||
func NewWriter() Writer {
|
func NewWriter() Writer {
|
||||||
return make(Writer)
|
return make(Writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddSealed includes a "sealed" token (serialized with a ToSealed* function) in the container.
|
// AddSealed includes a "sealed" token (serialized with a ToSealed* function) in the container.
|
||||||
func (ctn Writer) AddSealed(data []byte) {
|
func (ctn Writer) AddSealed(cid cid.Cid, data []byte) {
|
||||||
ctn[string(data)] = struct{}{}
|
ctn[cid] = data
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToBytes encode the container into raw bytes.
|
func (ctn Writer) ToCar(w io.Writer) error {
|
||||||
func (ctn Writer) ToBytes() ([]byte, error) {
|
return writeCar(w, nil, func(yield func(carBlock, error) bool) {
|
||||||
return ctn.toBytes(headerRawBytes)
|
for c, bytes := range ctn {
|
||||||
}
|
if !yield(carBlock{c: c, data: bytes}, nil) {
|
||||||
|
return
|
||||||
// ToBytesWriter is the same as ToBytes, but with an io.Writer.
|
|
||||||
func (ctn Writer) ToBytesWriter(w io.Writer) error {
|
|
||||||
return ctn.toWriter(headerRawBytes, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBytesGzipped encode the container into gzipped bytes.
|
|
||||||
func (ctn Writer) ToBytesGzipped() ([]byte, error) {
|
|
||||||
return ctn.toBytes(headerRawBytesGzip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBytesGzippedWriter is the same as ToBytesGzipped, but with an io.Writer.
|
|
||||||
func (ctn Writer) ToBytesGzippedWriter(w io.Writer) error {
|
|
||||||
return ctn.toWriter(headerRawBytesGzip, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBase64StdPadding encode the container into a base64 string, with standard encoding and padding.
|
|
||||||
func (ctn Writer) ToBase64StdPadding() (string, error) {
|
|
||||||
return ctn.toString(headerBase64StdPadding)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBase64StdPaddingWriter is the same as ToBase64StdPadding, but with an io.Writer.
|
|
||||||
func (ctn Writer) ToBase64StdPaddingWriter(w io.Writer) error {
|
|
||||||
return ctn.toWriter(headerBase64StdPadding, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBase64StdPaddingGzipped encode the container into a pre-gzipped base64 string, with standard encoding and padding.
|
|
||||||
func (ctn Writer) ToBase64StdPaddingGzipped() (string, error) {
|
|
||||||
return ctn.toString(headerBase64StdPaddingGzip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBase64StdPaddingGzippedWriter is the same as ToBase64StdPaddingGzipped, but with an io.Writer.
|
|
||||||
func (ctn Writer) ToBase64StdPaddingGzippedWriter(w io.Writer) error {
|
|
||||||
return ctn.toWriter(headerBase64StdPaddingGzip, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBase64URL encode the container into base64 string, with URL-safe encoding and no padding.
|
|
||||||
func (ctn Writer) ToBase64URL() (string, error) {
|
|
||||||
return ctn.toString(headerBase64URL)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBase64URLWriter is the same as ToBase64URL, but with an io.Writer.
|
|
||||||
func (ctn Writer) ToBase64URLWriter(w io.Writer) error {
|
|
||||||
return ctn.toWriter(headerBase64URL, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBase64URLGzipped encode the container into pre-gzipped base64 string, with URL-safe encoding and no padding.
|
|
||||||
func (ctn Writer) ToBase64URLGzipped() (string, error) {
|
|
||||||
return ctn.toString(headerBase64URLGzip)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToBase64URLGzipWriter is the same as ToBase64URL, but with an io.Writer.
|
|
||||||
func (ctn Writer) ToBase64URLGzipWriter(w io.Writer) error {
|
|
||||||
return ctn.toWriter(headerBase64URLGzip, w)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctn Writer) toBytes(header header) ([]byte, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := ctn.toWriter(header, &buf)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return buf.Bytes(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctn Writer) toString(header header) (string, error) {
|
|
||||||
var buf bytes.Buffer
|
|
||||||
err := ctn.toWriter(header, &buf)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return buf.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (ctn Writer) toWriter(header header, w io.Writer) (err error) {
|
|
||||||
encoder := header.encoder(w)
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
err = encoder.Close()
|
|
||||||
}()
|
|
||||||
node, err := qp.BuildMap(basicnode.Prototype.Any, 1, func(ma datamodel.MapAssembler) {
|
|
||||||
qp.MapEntry(ma, containerVersionTag, qp.List(int64(len(ctn)), func(la datamodel.ListAssembler) {
|
|
||||||
tokens := make([][]byte, 0, len(ctn))
|
|
||||||
for data := range ctn {
|
|
||||||
tokens = append(tokens, []byte(data))
|
|
||||||
}
|
}
|
||||||
slices.SortFunc(tokens, bytes.Compare)
|
}
|
||||||
for _, data := range tokens {
|
})
|
||||||
qp.ListEntry(la, qp.Bytes(data))
|
}
|
||||||
|
|
||||||
|
func (ctn Writer) ToCarBase64(w io.Writer) error {
|
||||||
|
w2 := base64.NewEncoder(base64.StdEncoding, w)
|
||||||
|
defer w2.Close()
|
||||||
|
return ctn.ToCar(w2)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ctn Writer) ToCbor(w io.Writer) error {
|
||||||
|
node, err := qp.BuildMap(basicnode.Prototype.Any, 1, func(ma datamodel.MapAssembler) {
|
||||||
|
qp.MapEntry(ma, currentContainerVersion, qp.List(int64(len(ctn)), func(la datamodel.ListAssembler) {
|
||||||
|
for _, bytes := range ctn {
|
||||||
|
qp.ListEntry(la, qp.Bytes(bytes))
|
||||||
}
|
}
|
||||||
}))
|
}))
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
return ipld.EncodeStreaming(w, node, dagcbor.Encode)
|
||||||
return ipld.EncodeStreaming(encoder, node, cbor.Encode)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToReader convert a container Writer into a Reader.
|
func (ctn Writer) ToCborBase64(w io.Writer) error {
|
||||||
// Most likely, you only want to use this in tests for convenience.
|
w2 := base64.NewEncoder(base64.StdEncoding, w)
|
||||||
// This is not optimized and can panic.
|
defer w2.Close()
|
||||||
func (ctn Writer) ToReader() Reader {
|
return ctn.ToCbor(w2)
|
||||||
data, err := ctn.ToBytes()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
reader, err := FromBytes(data)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return reader
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
package container
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestWriterDedup(t *testing.T) {
|
|
||||||
ctn := NewWriter()
|
|
||||||
|
|
||||||
_, _, sealed := randToken()
|
|
||||||
ctn.AddSealed(sealed)
|
|
||||||
require.Len(t, ctn, 1)
|
|
||||||
|
|
||||||
ctn.AddSealed(sealed)
|
|
||||||
require.Len(t, ctn, 1)
|
|
||||||
}
|
|
||||||
132
pkg/meta/internal/crypto/aes.go
Normal file
132
pkg/meta/internal/crypto/aes.go
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
package crypto
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
// KeySize represents valid AES key sizes
|
||||||
|
type KeySize int
|
||||||
|
|
||||||
|
const (
|
||||||
|
KeySize128 KeySize = 16 // AES-128
|
||||||
|
KeySize192 KeySize = 24 // AES-192
|
||||||
|
KeySize256 KeySize = 32 // AES-256 (recommended)
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsValid returns true if the key size is valid for AES
|
||||||
|
func (ks KeySize) IsValid() bool {
|
||||||
|
switch ks {
|
||||||
|
case KeySize128, KeySize192, KeySize256:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ErrShortCipherText = errors.New("ciphertext too short")
|
||||||
|
var ErrNoEncryptionKey = errors.New("encryption key is required")
|
||||||
|
var ErrInvalidKeySize = errors.New("invalid key size: must be 16, 24, or 32 bytes")
|
||||||
|
var ErrZeroKey = errors.New("encryption key cannot be all zeros")
|
||||||
|
|
||||||
|
// GenerateKey generates a random AES key of default size KeySize256 (32 bytes).
|
||||||
|
// Returns an error if the specified size is invalid or if key generation fails.
|
||||||
|
func GenerateKey() ([]byte, error) {
|
||||||
|
return GenerateKeyWithSize(KeySize256)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateKeyWithSize generates a random AES key of the specified size.
|
||||||
|
// Returns an error if the specified size is invalid or if key generation fails.
|
||||||
|
func GenerateKeyWithSize(size KeySize) ([]byte, error) {
|
||||||
|
if !size.IsValid() {
|
||||||
|
return nil, ErrInvalidKeySize
|
||||||
|
}
|
||||||
|
|
||||||
|
key := make([]byte, size)
|
||||||
|
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to generate AES key: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncryptWithAESKey encrypts data using AES-GCM with the provided key.
|
||||||
|
// The key must be 16, 24, or 32 bytes long (for AES-128, AES-192, or AES-256).
|
||||||
|
// Returns the encrypted data with the nonce prepended, or an error if encryption fails.
|
||||||
|
func EncryptWithAESKey(data, key []byte) ([]byte, error) {
|
||||||
|
if err := validateAESKey(key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce := make([]byte, gcm.NonceSize())
|
||||||
|
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return gcm.Seal(nonce, nonce, data, nil), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// DecryptStringWithAESKey decrypts data that was encrypted with EncryptWithAESKey.
|
||||||
|
// The key must match the one used for encryption.
|
||||||
|
// Expects the input to have a prepended nonce.
|
||||||
|
// Returns the decrypted data or an error if decryption fails.
|
||||||
|
func DecryptStringWithAESKey(data, key []byte) ([]byte, error) {
|
||||||
|
if err := validateAESKey(key); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(data) < gcm.NonceSize() {
|
||||||
|
return nil, ErrShortCipherText
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce, ciphertext := data[:gcm.NonceSize()], data[gcm.NonceSize():]
|
||||||
|
decrypted, err := gcm.Open(nil, nonce, ciphertext, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return decrypted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateAESKey(key []byte) error {
|
||||||
|
if key == nil {
|
||||||
|
return ErrNoEncryptionKey
|
||||||
|
}
|
||||||
|
|
||||||
|
if !KeySize(len(key)).IsValid() {
|
||||||
|
return ErrInvalidKeySize
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if key is all zeros
|
||||||
|
for _, b := range key {
|
||||||
|
if b != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ErrZeroKey
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
package secretbox
|
package crypto
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
@@ -8,10 +8,10 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestSecretBoxEncryption(t *testing.T) {
|
func TestAESEncryption(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
key := make([]byte, keySize) // generate random 32-byte key
|
key := make([]byte, 32) // generated random 32-byte key
|
||||||
_, errKey := rand.Read(key)
|
_, errKey := rand.Read(key)
|
||||||
require.NoError(t, errKey)
|
require.NoError(t, errKey)
|
||||||
|
|
||||||
@@ -40,13 +40,13 @@ func TestSecretBoxEncryption(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "invalid key size",
|
name: "invalid key size",
|
||||||
data: []byte("hello world"),
|
data: []byte("hello world"),
|
||||||
key: make([]byte, 16), // Only 32 bytes allowed now
|
key: make([]byte, 31),
|
||||||
wantErr: ErrInvalidKeySize,
|
wantErr: ErrInvalidKeySize,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "zero key returns error",
|
name: "zero key returns error",
|
||||||
data: []byte("hello world"),
|
data: []byte("hello world"),
|
||||||
key: make([]byte, keySize),
|
key: make([]byte, 32),
|
||||||
wantErr: ErrZeroKey,
|
wantErr: ErrZeroKey,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -56,22 +56,24 @@ func TestSecretBoxEncryption(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
encrypted, err := EncryptWithKey(tt.data, tt.key)
|
encrypted, err := EncryptWithAESKey(tt.data, tt.key)
|
||||||
if tt.wantErr != nil {
|
if tt.wantErr != nil {
|
||||||
require.ErrorIs(t, err, tt.wantErr)
|
require.ErrorIs(t, err, tt.wantErr)
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Verify encrypted data is different and includes nonce
|
decrypted, err := DecryptStringWithAESKey(encrypted, tt.key)
|
||||||
require.Greater(t, len(encrypted), 24) // At least nonce size
|
|
||||||
if len(tt.data) > 0 {
|
|
||||||
require.NotEqual(t, tt.data, encrypted[24:]) // Ignore nonce prefix
|
|
||||||
}
|
|
||||||
|
|
||||||
decrypted, err := DecryptStringWithKey(encrypted, tt.key)
|
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, bytes.Equal(tt.data, decrypted))
|
|
||||||
|
if tt.key == nil {
|
||||||
|
require.Equal(t, tt.data, encrypted)
|
||||||
|
require.Equal(t, tt.data, decrypted)
|
||||||
|
} else {
|
||||||
|
require.NotEqual(t, tt.data, encrypted)
|
||||||
|
require.True(t, bytes.Equal(tt.data, decrypted))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -79,15 +81,10 @@ func TestSecretBoxEncryption(t *testing.T) {
|
|||||||
func TestDecryptionErrors(t *testing.T) {
|
func TestDecryptionErrors(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
key := make([]byte, keySize)
|
key := make([]byte, 32)
|
||||||
_, err := rand.Read(key)
|
_, err := rand.Read(key)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
// Create valid encrypted data for tampering tests
|
|
||||||
validData := []byte("test message")
|
|
||||||
encrypted, err := EncryptWithKey(validData, key)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
data []byte
|
data []byte
|
||||||
@@ -96,25 +93,19 @@ func TestDecryptionErrors(t *testing.T) {
|
|||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "short ciphertext",
|
name: "short ciphertext",
|
||||||
data: make([]byte, 23), // Less than nonce size
|
data: []byte("short"),
|
||||||
key: key,
|
key: key,
|
||||||
errMsg: "ciphertext too short",
|
errMsg: "ciphertext too short",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "invalid ciphertext",
|
name: "invalid ciphertext",
|
||||||
data: make([]byte, 24), // Just nonce size
|
data: make([]byte, 16), // just nonce size
|
||||||
key: key,
|
key: key,
|
||||||
errMsg: "decryption failed",
|
errMsg: "message authentication failed",
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "tampered ciphertext",
|
|
||||||
data: tamperWithBytes(encrypted),
|
|
||||||
key: key,
|
|
||||||
errMsg: "decryption failed",
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "missing key",
|
name: "missing key",
|
||||||
data: encrypted,
|
data: []byte("<22>`M<><4D><EFBFBD>l\u001AIF<49>\u0012<31><32><EFBFBD>=h<>?<3F>c<EFBFBD> <20><>\u0012<31><32><EFBFBD><EFBFBD>\u001C<31>\u0018Ƽ(g"),
|
||||||
key: nil,
|
key: nil,
|
||||||
errMsg: "encryption key is required",
|
errMsg: "encryption key is required",
|
||||||
},
|
},
|
||||||
@@ -125,20 +116,9 @@ func TestDecryptionErrors(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
_, err := DecryptStringWithKey(tt.data, tt.key)
|
_, err := DecryptStringWithAESKey(tt.data, tt.key)
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Contains(t, err.Error(), tt.errMsg)
|
require.Contains(t, err.Error(), tt.errMsg)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// tamperWithBytes modifies a byte in the encrypted data to simulate tampering
|
|
||||||
func tamperWithBytes(data []byte) []byte {
|
|
||||||
if len(data) < 25 { // Need at least nonce + 1 byte
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
tampered := make([]byte, len(data))
|
|
||||||
copy(tampered, data)
|
|
||||||
tampered[24] ^= 0x01 // Modify first byte after nonce
|
|
||||||
return tampered
|
|
||||||
}
|
|
||||||
@@ -3,15 +3,13 @@ package meta
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"iter"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/printer"
|
"github.com/ipld/go-ipld-prime/printer"
|
||||||
|
|
||||||
|
"github.com/ucan-wg/go-ucan/pkg/meta/internal/crypto"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/secretbox"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var ErrNotFound = errors.New("key not found in meta")
|
var ErrNotFound = errors.New("key not found in meta")
|
||||||
@@ -63,7 +61,7 @@ func (m *Meta) GetEncryptedString(key string, encryptionKey []byte) (string, err
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
decrypted, err := secretbox.DecryptStringWithKey(v, encryptionKey)
|
decrypted, err := crypto.DecryptStringWithAESKey(v, encryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
@@ -111,7 +109,7 @@ func (m *Meta) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
decrypted, err := secretbox.DecryptStringWithKey(v, encryptionKey)
|
decrypted, err := crypto.DecryptStringWithAESKey(v, encryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -121,6 +119,7 @@ func (m *Meta) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, erro
|
|||||||
|
|
||||||
// GetNode retrieves a value as a raw IPLD node.
|
// GetNode retrieves a value as a raw IPLD node.
|
||||||
// Returns ErrNotFound if the given key is missing.
|
// Returns ErrNotFound if the given key is missing.
|
||||||
|
// Returns datamodel.ErrWrongKind if the value has the wrong type.
|
||||||
func (m *Meta) GetNode(key string) (ipld.Node, error) {
|
func (m *Meta) GetNode(key string) (ipld.Node, error) {
|
||||||
v, ok := m.Values[key]
|
v, ok := m.Values[key]
|
||||||
if !ok {
|
if !ok {
|
||||||
@@ -150,19 +149,18 @@ func (m *Meta) Add(key string, val any) error {
|
|||||||
// AddEncrypted adds a key/value pair in the meta set.
|
// AddEncrypted adds a key/value pair in the meta set.
|
||||||
// The value is encrypted with the given encryptionKey.
|
// The value is encrypted with the given encryptionKey.
|
||||||
// Accepted types for the value are: string, []byte.
|
// Accepted types for the value are: string, []byte.
|
||||||
// The ciphertext will be 40 bytes larger than the plaintext due to encryption overhead.
|
|
||||||
func (m *Meta) AddEncrypted(key string, val any, encryptionKey []byte) error {
|
func (m *Meta) AddEncrypted(key string, val any, encryptionKey []byte) error {
|
||||||
var encrypted []byte
|
var encrypted []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
switch val := val.(type) {
|
switch val := val.(type) {
|
||||||
case string:
|
case string:
|
||||||
encrypted, err = secretbox.EncryptWithKey([]byte(val), encryptionKey)
|
encrypted, err = crypto.EncryptWithAESKey([]byte(val), encryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
case []byte:
|
case []byte:
|
||||||
encrypted, err = secretbox.EncryptWithKey(val, encryptionKey)
|
encrypted, err = crypto.EncryptWithAESKey(val, encryptionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -173,41 +171,6 @@ func (m *Meta) AddEncrypted(key string, val any, encryptionKey []byte) error {
|
|||||||
return m.Add(key, encrypted)
|
return m.Add(key, encrypted)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Iterator interface {
|
|
||||||
Iter() iter.Seq2[string, ipld.Node]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Include merges the provided meta into the existing one.
|
|
||||||
//
|
|
||||||
// If duplicate keys are encountered, the new value is silently dropped
|
|
||||||
// without causing an error.
|
|
||||||
func (m *Meta) Include(other Iterator) {
|
|
||||||
for key, value := range other.Iter() {
|
|
||||||
if _, ok := m.Values[key]; ok {
|
|
||||||
// don't overwrite
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m.Values[key] = value
|
|
||||||
m.Keys = append(m.Keys, key)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Len returns the number of key/values.
|
|
||||||
func (m *Meta) Len() int {
|
|
||||||
return len(m.Values)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Iter iterates over the meta key/values
|
|
||||||
func (m *Meta) Iter() iter.Seq2[string, ipld.Node] {
|
|
||||||
return func(yield func(string, ipld.Node) bool) {
|
|
||||||
for _, key := range m.Keys {
|
|
||||||
if !yield(key, m.Values[key]) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Equals tells if two Meta hold the same key/values.
|
// Equals tells if two Meta hold the same key/values.
|
||||||
func (m *Meta) Equals(other *Meta) bool {
|
func (m *Meta) Equals(other *Meta) bool {
|
||||||
if len(m.Keys) != len(other.Keys) {
|
if len(m.Keys) != len(other.Keys) {
|
||||||
@@ -225,8 +188,6 @@ func (m *Meta) Equals(other *Meta) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Meta) String() string {
|
func (m *Meta) String() string {
|
||||||
sort.Strings(m.Keys)
|
|
||||||
|
|
||||||
buf := strings.Builder{}
|
buf := strings.Builder{}
|
||||||
buf.WriteString("{")
|
buf.WriteString("{")
|
||||||
|
|
||||||
@@ -248,18 +209,5 @@ func (m *Meta) String() string {
|
|||||||
|
|
||||||
// ReadOnly returns a read-only version of Meta.
|
// ReadOnly returns a read-only version of Meta.
|
||||||
func (m *Meta) ReadOnly() ReadOnly {
|
func (m *Meta) ReadOnly() ReadOnly {
|
||||||
return ReadOnly{meta: m}
|
return ReadOnly{m: m}
|
||||||
}
|
|
||||||
|
|
||||||
// Clone makes a deep copy.
|
|
||||||
func (m *Meta) Clone() *Meta {
|
|
||||||
res := &Meta{
|
|
||||||
Keys: make([]string, len(m.Keys)),
|
|
||||||
Values: make(map[string]ipld.Node, len(m.Values)),
|
|
||||||
}
|
|
||||||
copy(res.Keys, m.Keys)
|
|
||||||
for k, v := range m.Values {
|
|
||||||
res.Values[k] = v
|
|
||||||
}
|
|
||||||
return res
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,8 @@ package meta_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"maps"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/meta"
|
"github.com/ucan-wg/go-ucan/pkg/meta"
|
||||||
@@ -78,53 +75,3 @@ func TestMeta_Add(t *testing.T) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIterCloneEquals(t *testing.T) {
|
|
||||||
m := meta.NewMeta()
|
|
||||||
|
|
||||||
require.NoError(t, m.Add("foo", "bar"))
|
|
||||||
require.NoError(t, m.Add("baz", 1234))
|
|
||||||
|
|
||||||
expected := map[string]ipld.Node{
|
|
||||||
"foo": basicnode.NewString("bar"),
|
|
||||||
"baz": basicnode.NewInt(1234),
|
|
||||||
}
|
|
||||||
|
|
||||||
// meta -> iter
|
|
||||||
require.Equal(t, expected, maps.Collect(m.Iter()))
|
|
||||||
|
|
||||||
// readonly -> iter
|
|
||||||
ro := m.ReadOnly()
|
|
||||||
require.Equal(t, expected, maps.Collect(ro.Iter()))
|
|
||||||
|
|
||||||
// meta -> clone -> iter
|
|
||||||
clone := m.Clone()
|
|
||||||
require.Equal(t, expected, maps.Collect(clone.Iter()))
|
|
||||||
|
|
||||||
// readonly -> WriteableClone -> iter
|
|
||||||
wclone := ro.WriteableClone()
|
|
||||||
require.Equal(t, expected, maps.Collect(wclone.Iter()))
|
|
||||||
|
|
||||||
require.True(t, m.Equals(wclone))
|
|
||||||
require.True(t, ro.Equals(wclone.ReadOnly()))
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestInclude(t *testing.T) {
|
|
||||||
m1 := meta.NewMeta()
|
|
||||||
|
|
||||||
require.NoError(t, m1.Add("samekey", "bar"))
|
|
||||||
require.NoError(t, m1.Add("baz", 1234))
|
|
||||||
|
|
||||||
m2 := meta.NewMeta()
|
|
||||||
|
|
||||||
require.NoError(t, m2.Add("samekey", "othervalue")) // check no overwrite
|
|
||||||
require.NoError(t, m2.Add("otherkey", 1234))
|
|
||||||
|
|
||||||
m1.Include(m2)
|
|
||||||
|
|
||||||
require.Equal(t, map[string]ipld.Node{
|
|
||||||
"samekey": basicnode.NewString("bar"),
|
|
||||||
"baz": basicnode.NewInt(1234),
|
|
||||||
"otherkey": basicnode.NewInt(1234),
|
|
||||||
}, maps.Collect(m1.Iter()))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,64 +1,50 @@
|
|||||||
package meta
|
package meta
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"iter"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ReadOnly wraps a Meta into a read-only facade.
|
// ReadOnly wraps a Meta into a read-only facade.
|
||||||
type ReadOnly struct {
|
type ReadOnly struct {
|
||||||
meta *Meta
|
m *Meta
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetBool(key string) (bool, error) {
|
func (r ReadOnly) GetBool(key string) (bool, error) {
|
||||||
return r.meta.GetBool(key)
|
return r.m.GetBool(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetString(key string) (string, error) {
|
func (r ReadOnly) GetString(key string) (string, error) {
|
||||||
return r.meta.GetString(key)
|
return r.m.GetString(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetEncryptedString(key string, encryptionKey []byte) (string, error) {
|
func (r ReadOnly) GetEncryptedString(key string, encryptionKey []byte) (string, error) {
|
||||||
return r.meta.GetEncryptedString(key, encryptionKey)
|
return r.m.GetEncryptedString(key, encryptionKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetInt64(key string) (int64, error) {
|
func (r ReadOnly) GetInt64(key string) (int64, error) {
|
||||||
return r.meta.GetInt64(key)
|
return r.m.GetInt64(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetFloat64(key string) (float64, error) {
|
func (r ReadOnly) GetFloat64(key string) (float64, error) {
|
||||||
return r.meta.GetFloat64(key)
|
return r.m.GetFloat64(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetBytes(key string) ([]byte, error) {
|
func (r ReadOnly) GetBytes(key string) ([]byte, error) {
|
||||||
return r.meta.GetBytes(key)
|
return r.m.GetBytes(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, error) {
|
func (r ReadOnly) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, error) {
|
||||||
return r.meta.GetEncryptedBytes(key, encryptionKey)
|
return r.m.GetEncryptedBytes(key, encryptionKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) GetNode(key string) (ipld.Node, error) {
|
func (r ReadOnly) GetNode(key string) (ipld.Node, error) {
|
||||||
return r.meta.GetNode(key)
|
return r.m.GetNode(key)
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) Len() int {
|
|
||||||
return r.meta.Len()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) Iter() iter.Seq2[string, ipld.Node] {
|
|
||||||
return r.meta.Iter()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) Equals(other ReadOnly) bool {
|
func (r ReadOnly) Equals(other ReadOnly) bool {
|
||||||
return r.meta.Equals(other.meta)
|
return r.m.Equals(other.m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r ReadOnly) String() string {
|
func (r ReadOnly) String() string {
|
||||||
return r.meta.String()
|
return r.m.String()
|
||||||
}
|
|
||||||
|
|
||||||
func (r ReadOnly) WriteableClone() *Meta {
|
|
||||||
return r.meta.Clone()
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,15 +9,10 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime/must"
|
"github.com/ipld/go-ipld-prime/must"
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/selector"
|
"github.com/ucan-wg/go-ucan/pkg/policy/selector"
|
||||||
)
|
)
|
||||||
|
|
||||||
func FromIPLD(node datamodel.Node) (Policy, error) {
|
func FromIPLD(node datamodel.Node) (Policy, error) {
|
||||||
if err := limits.ValidateIntegerBoundsIPLD(node); err != nil {
|
|
||||||
return nil, fmt.Errorf("policy contains integer values outside safe bounds: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return statementsFromIPLD("/", node)
|
return statementsFromIPLD("/", node)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -81,7 +76,7 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
|
|||||||
}
|
}
|
||||||
case 3:
|
case 3:
|
||||||
switch op {
|
switch op {
|
||||||
case KindEqual, KindNotEqual, KindLessThan, KindLessThanOrEqual, KindGreaterThan, KindGreaterThanOrEqual:
|
case KindEqual, KindLessThan, KindLessThanOrEqual, KindGreaterThan, KindGreaterThanOrEqual:
|
||||||
sel, err := arg2AsSelector(op)
|
sel, err := arg2AsSelector(op)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -21,31 +21,10 @@ func TestIpldRoundTrip(t *testing.T) {
|
|||||||
]
|
]
|
||||||
]`
|
]`
|
||||||
|
|
||||||
// must contain all the operators
|
|
||||||
const allOps = `
|
|
||||||
[
|
|
||||||
["and", [
|
|
||||||
["==", ".foo1", ".bar1"],
|
|
||||||
["!=", ".foo2", ".bar2"]
|
|
||||||
]],
|
|
||||||
["or", [
|
|
||||||
[">", ".foo5", 5.2],
|
|
||||||
[">=", ".foo6", 6.2]
|
|
||||||
]],
|
|
||||||
["not", ["like", ".foo7", "*@example.com"]],
|
|
||||||
["all", ".foo8",
|
|
||||||
["<", ".foo3", 3]
|
|
||||||
],
|
|
||||||
["any", ".foo9",
|
|
||||||
["<=", ".foo4", 4]
|
|
||||||
]
|
|
||||||
]`
|
|
||||||
|
|
||||||
for _, tc := range []struct {
|
for _, tc := range []struct {
|
||||||
name, dagJsonStr string
|
name, dagJsonStr string
|
||||||
}{
|
}{
|
||||||
{"illustrativeExample", illustrativeExample},
|
{"illustrativeExample", illustrativeExample},
|
||||||
{"allOps", allOps},
|
|
||||||
} {
|
} {
|
||||||
nodes, err := ipld.Decode([]byte(tc.dagJsonStr), dagjson.Decode)
|
nodes, err := ipld.Decode([]byte(tc.dagJsonStr), dagjson.Decode)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
package limits
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
"github.com/ipld/go-ipld-prime/must"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
// MaxInt53 represents the maximum safe integer in JavaScript (2^53 - 1)
|
|
||||||
MaxInt53 int64 = 9007199254740991
|
|
||||||
// MinInt53 represents the minimum safe integer in JavaScript (-2^53 + 1)
|
|
||||||
MinInt53 int64 = -9007199254740991
|
|
||||||
)
|
|
||||||
|
|
||||||
func ValidateIntegerBoundsIPLD(node ipld.Node) error {
|
|
||||||
switch node.Kind() {
|
|
||||||
case ipld.Kind_Int:
|
|
||||||
val := must.Int(node)
|
|
||||||
if val > MaxInt53 || val < MinInt53 {
|
|
||||||
return fmt.Errorf("integer value %d exceeds safe bounds", val)
|
|
||||||
}
|
|
||||||
case ipld.Kind_List:
|
|
||||||
it := node.ListIterator()
|
|
||||||
for !it.Done() {
|
|
||||||
_, v, err := it.Next()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := ValidateIntegerBoundsIPLD(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case ipld.Kind_Map:
|
|
||||||
it := node.MapIterator()
|
|
||||||
for !it.Done() {
|
|
||||||
_, v, err := it.Next()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := ValidateIntegerBoundsIPLD(v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
package limits
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
|
||||||
"github.com/ipld/go-ipld-prime/fluent/qp"
|
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestValidateIntegerBoundsIPLD(t *testing.T) {
|
|
||||||
buildMap := func() datamodel.Node {
|
|
||||||
nb := basicnode.Prototype.Any.NewBuilder()
|
|
||||||
qp.Map(1, func(ma datamodel.MapAssembler) {
|
|
||||||
qp.MapEntry(ma, "foo", qp.Int(MaxInt53+1))
|
|
||||||
})(nb)
|
|
||||||
return nb.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
buildList := func() datamodel.Node {
|
|
||||||
nb := basicnode.Prototype.Any.NewBuilder()
|
|
||||||
qp.List(1, func(la datamodel.ListAssembler) {
|
|
||||||
qp.ListEntry(la, qp.Int(MinInt53-1))
|
|
||||||
})(nb)
|
|
||||||
return nb.Build()
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input datamodel.Node
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid int",
|
|
||||||
input: basicnode.NewInt(42),
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "max safe int",
|
|
||||||
input: basicnode.NewInt(MaxInt53),
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "min safe int",
|
|
||||||
input: basicnode.NewInt(MinInt53),
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "above MaxInt53",
|
|
||||||
input: basicnode.NewInt(MaxInt53 + 1),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "below MinInt53",
|
|
||||||
input: basicnode.NewInt(MinInt53 - 1),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nested map with invalid int",
|
|
||||||
input: buildMap(),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "nested list with invalid int",
|
|
||||||
input: buildList(),
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
err := ValidateIntegerBoundsIPLD(tt.input)
|
|
||||||
if tt.wantErr {
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Contains(t, err.Error(), "exceeds safe bounds")
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -12,8 +12,6 @@ import (
|
|||||||
"github.com/ipld/go-ipld-prime/fluent/qp"
|
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||||
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var Bool = basicnode.NewBool
|
var Bool = basicnode.NewBool
|
||||||
@@ -60,6 +58,8 @@ func List[T any](l []T) (ipld.Node, error) {
|
|||||||
// Any creates an IPLD node from any value
|
// Any creates an IPLD node from any value
|
||||||
// If possible, use another dedicated function for your type for performance.
|
// If possible, use another dedicated function for your type for performance.
|
||||||
func Any(v any) (res ipld.Node, err error) {
|
func Any(v any) (res ipld.Node, err error) {
|
||||||
|
// TODO: handle uint overflow below
|
||||||
|
|
||||||
// some fast path
|
// some fast path
|
||||||
switch val := v.(type) {
|
switch val := v.(type) {
|
||||||
case bool:
|
case bool:
|
||||||
@@ -67,11 +67,7 @@ func Any(v any) (res ipld.Node, err error) {
|
|||||||
case string:
|
case string:
|
||||||
return basicnode.NewString(val), nil
|
return basicnode.NewString(val), nil
|
||||||
case int:
|
case int:
|
||||||
i := int64(val)
|
return basicnode.NewInt(int64(val)), nil
|
||||||
if i > limits.MaxInt53 || i < limits.MinInt53 {
|
|
||||||
return nil, fmt.Errorf("integer value %d exceeds safe integer bounds", i)
|
|
||||||
}
|
|
||||||
return basicnode.NewInt(i), nil
|
|
||||||
case int8:
|
case int8:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
case int16:
|
case int16:
|
||||||
@@ -79,9 +75,6 @@ func Any(v any) (res ipld.Node, err error) {
|
|||||||
case int32:
|
case int32:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
case int64:
|
case int64:
|
||||||
if val > limits.MaxInt53 || val < limits.MinInt53 {
|
|
||||||
return nil, fmt.Errorf("integer value %d exceeds safe integer bounds", val)
|
|
||||||
}
|
|
||||||
return basicnode.NewInt(val), nil
|
return basicnode.NewInt(val), nil
|
||||||
case uint:
|
case uint:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
@@ -92,9 +85,6 @@ func Any(v any) (res ipld.Node, err error) {
|
|||||||
case uint32:
|
case uint32:
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
case uint64:
|
case uint64:
|
||||||
if val > uint64(limits.MaxInt53) {
|
|
||||||
return nil, fmt.Errorf("unsigned integer value %d exceeds safe integer bounds", val)
|
|
||||||
}
|
|
||||||
return basicnode.NewInt(int64(val)), nil
|
return basicnode.NewInt(int64(val)), nil
|
||||||
case float32:
|
case float32:
|
||||||
return basicnode.NewFloat(float64(val)), nil
|
return basicnode.NewFloat(float64(val)), nil
|
||||||
@@ -178,17 +168,9 @@ func anyAssemble(val any) qp.Assemble {
|
|||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
return qp.Bool(rv.Bool())
|
return qp.Bool(rv.Bool())
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
i := rv.Int()
|
return qp.Int(rv.Int())
|
||||||
if i > limits.MaxInt53 || i < limits.MinInt53 {
|
|
||||||
panic(fmt.Sprintf("integer %d exceeds safe bounds", i))
|
|
||||||
}
|
|
||||||
return qp.Int(i)
|
|
||||||
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
|
||||||
u := rv.Uint()
|
return qp.Int(int64(rv.Uint()))
|
||||||
if u > uint64(limits.MaxInt53) {
|
|
||||||
panic(fmt.Sprintf("unsigned integer %d exceeds safe bounds", u))
|
|
||||||
}
|
|
||||||
return qp.Int(int64(u))
|
|
||||||
case reflect.Float32, reflect.Float64:
|
case reflect.Float32, reflect.Float64:
|
||||||
return qp.Float(rv.Float())
|
return qp.Float(rv.Float())
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ import (
|
|||||||
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||||
"github.com/ipld/go-ipld-prime/printer"
|
"github.com/ipld/go-ipld-prime/printer"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestList(t *testing.T) {
|
func TestList(t *testing.T) {
|
||||||
@@ -216,7 +214,7 @@ func TestAny(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.True(t, asLink.(cidlink.Link).Equals(cid.MustParse("bafzbeigai3eoy2ccc7ybwjfz5r3rdxqrinwi4rwytly24tdbh6yk7zslrm")))
|
require.True(t, asLink.(cidlink.Link).Equals(cid.MustParse("bafzbeigai3eoy2ccc7ybwjfz5r3rdxqrinwi4rwytly24tdbh6yk7zslrm")))
|
||||||
|
|
||||||
_, err = Any(data["func"])
|
v, err = Any(data["func"])
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,56 +254,6 @@ func BenchmarkAny(b *testing.B) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAnyAssembleIntegerOverflow(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
input interface{}
|
|
||||||
shouldErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "valid int",
|
|
||||||
input: 42,
|
|
||||||
shouldErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "max safe int",
|
|
||||||
input: limits.MaxInt53,
|
|
||||||
shouldErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "min safe int",
|
|
||||||
input: limits.MinInt53,
|
|
||||||
shouldErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow int",
|
|
||||||
input: int64(limits.MaxInt53 + 1),
|
|
||||||
shouldErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "underflow int",
|
|
||||||
input: int64(limits.MinInt53 - 1),
|
|
||||||
shouldErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow uint",
|
|
||||||
input: uint64(limits.MaxInt53 + 1),
|
|
||||||
shouldErr: true,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
_, err := Any(tt.input)
|
|
||||||
if tt.shouldErr {
|
|
||||||
require.Error(t, err)
|
|
||||||
} else {
|
|
||||||
require.NoError(t, err)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func must[T any](t T, err error) T {
|
func must[T any](t T, err error) T {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package policy
|
|||||||
import (
|
import (
|
||||||
"cmp"
|
"cmp"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
@@ -82,17 +81,6 @@ func matchStatement(cur Statement, node ipld.Node) (_ matchResult, leafMost Stat
|
|||||||
}
|
}
|
||||||
return boolToRes(datamodel.DeepEqual(s.value, res))
|
return boolToRes(datamodel.DeepEqual(s.value, res))
|
||||||
}
|
}
|
||||||
case KindNotEqual:
|
|
||||||
if s, ok := cur.(equality); ok {
|
|
||||||
res, err := s.selector.Select(node)
|
|
||||||
if err != nil {
|
|
||||||
return matchResultNoData, cur
|
|
||||||
}
|
|
||||||
if res == nil { // optional selector didn't match
|
|
||||||
return matchResultOptionalNoData, nil
|
|
||||||
}
|
|
||||||
return boolToRes(!datamodel.DeepEqual(s.value, res))
|
|
||||||
}
|
|
||||||
case KindGreaterThan:
|
case KindGreaterThan:
|
||||||
if s, ok := cur.(equality); ok {
|
if s, ok := cur.(equality); ok {
|
||||||
res, err := s.selector.Select(node)
|
res, err := s.selector.Select(node)
|
||||||
@@ -261,22 +249,10 @@ func matchStatement(cur Statement, node ipld.Node) (_ matchResult, leafMost Stat
|
|||||||
panic(fmt.Errorf("unimplemented statement kind: %s", cur.Kind()))
|
panic(fmt.Errorf("unimplemented statement kind: %s", cur.Kind()))
|
||||||
}
|
}
|
||||||
|
|
||||||
// isOrdered compares two IPLD nodes and returns true if they satisfy the given ordering function.
|
|
||||||
// It supports comparison of integers and floats, returning false for:
|
|
||||||
// - Nodes of different or unsupported kinds
|
|
||||||
// - Integer values outside JavaScript's safe integer bounds (±2^53-1)
|
|
||||||
// - Non-finite floating point values (NaN or ±Inf)
|
|
||||||
//
|
|
||||||
// The satisfies parameter is a function that interprets the comparison result:
|
|
||||||
// - For ">" it returns true when order is 1
|
|
||||||
// - For ">=" it returns true when order is 0 or 1
|
|
||||||
// - For "<" it returns true when order is -1
|
|
||||||
// - For "<=" it returns true when order is -1 or 0
|
|
||||||
func isOrdered(expected ipld.Node, actual ipld.Node, satisfies func(order int) bool) bool {
|
func isOrdered(expected ipld.Node, actual ipld.Node, satisfies func(order int) bool) bool {
|
||||||
if expected.Kind() == ipld.Kind_Int && actual.Kind() == ipld.Kind_Int {
|
if expected.Kind() == ipld.Kind_Int && actual.Kind() == ipld.Kind_Int {
|
||||||
a := must.Int(actual)
|
a := must.Int(actual)
|
||||||
b := must.Int(expected)
|
b := must.Int(expected)
|
||||||
|
|
||||||
return satisfies(cmp.Compare(a, b))
|
return satisfies(cmp.Compare(a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -289,11 +265,6 @@ func isOrdered(expected ipld.Node, actual ipld.Node, satisfies func(order int) b
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(fmt.Errorf("extracting selector float: %w", err))
|
panic(fmt.Errorf("extracting selector float: %w", err))
|
||||||
}
|
}
|
||||||
|
|
||||||
if math.IsInf(a, 0) || math.IsNaN(a) || math.IsInf(b, 0) || math.IsNaN(b) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return satisfies(cmp.Compare(a, b))
|
return satisfies(cmp.Compare(a, b))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,8 +7,11 @@ import (
|
|||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
"github.com/ipld/go-ipld-prime"
|
"github.com/ipld/go-ipld-prime"
|
||||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
||||||
|
"github.com/ipld/go-ipld-prime/datamodel"
|
||||||
|
"github.com/ipld/go-ipld-prime/fluent/qp"
|
||||||
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
cidlink "github.com/ipld/go-ipld-prime/linking/cid"
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
"github.com/ipld/go-ipld-prime/node/basicnode"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
||||||
@@ -16,7 +19,7 @@ import (
|
|||||||
|
|
||||||
func TestMatch(t *testing.T) {
|
func TestMatch(t *testing.T) {
|
||||||
t.Run("equality", func(t *testing.T) {
|
t.Run("equality", func(t *testing.T) {
|
||||||
t.Run("eq string", func(t *testing.T) {
|
t.Run("string", func(t *testing.T) {
|
||||||
nd := literal.String("test")
|
nd := literal.String("test")
|
||||||
|
|
||||||
pol := MustConstruct(Equal(".", literal.String("test")))
|
pol := MustConstruct(Equal(".", literal.String("test")))
|
||||||
@@ -35,7 +38,7 @@ func TestMatch(t *testing.T) {
|
|||||||
require.Equal(t, pol[0], leaf)
|
require.Equal(t, pol[0], leaf)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("eq int", func(t *testing.T) {
|
t.Run("int", func(t *testing.T) {
|
||||||
nd := literal.Int(138)
|
nd := literal.Int(138)
|
||||||
|
|
||||||
pol := MustConstruct(Equal(".", literal.Int(138)))
|
pol := MustConstruct(Equal(".", literal.Int(138)))
|
||||||
@@ -54,7 +57,7 @@ func TestMatch(t *testing.T) {
|
|||||||
require.Equal(t, pol[0], leaf)
|
require.Equal(t, pol[0], leaf)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("eq float", func(t *testing.T) {
|
t.Run("float", func(t *testing.T) {
|
||||||
nd := literal.Float(1.138)
|
nd := literal.Float(1.138)
|
||||||
|
|
||||||
pol := MustConstruct(Equal(".", literal.Float(1.138)))
|
pol := MustConstruct(Equal(".", literal.Float(1.138)))
|
||||||
@@ -73,7 +76,7 @@ func TestMatch(t *testing.T) {
|
|||||||
require.Equal(t, pol[0], leaf)
|
require.Equal(t, pol[0], leaf)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("eq IPLD Link", func(t *testing.T) {
|
t.Run("IPLD Link", func(t *testing.T) {
|
||||||
l0 := cidlink.Link{Cid: cid.MustParse("bafybeif4owy5gno5lwnixqm52rwqfodklf76hsetxdhffuxnplvijskzqq")}
|
l0 := cidlink.Link{Cid: cid.MustParse("bafybeif4owy5gno5lwnixqm52rwqfodklf76hsetxdhffuxnplvijskzqq")}
|
||||||
l1 := cidlink.Link{Cid: cid.MustParse("bafkreifau35r7vi37tvbvfy3hdwvgb4tlflqf7zcdzeujqcjk3rsphiwte")}
|
l1 := cidlink.Link{Cid: cid.MustParse("bafkreifau35r7vi37tvbvfy3hdwvgb4tlflqf7zcdzeujqcjk3rsphiwte")}
|
||||||
|
|
||||||
@@ -95,7 +98,7 @@ func TestMatch(t *testing.T) {
|
|||||||
require.Equal(t, pol[0], leaf)
|
require.Equal(t, pol[0], leaf)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("eq string in map", func(t *testing.T) {
|
t.Run("string in map", func(t *testing.T) {
|
||||||
nd, _ := literal.Map(map[string]any{
|
nd, _ := literal.Map(map[string]any{
|
||||||
"foo": "bar",
|
"foo": "bar",
|
||||||
})
|
})
|
||||||
@@ -121,7 +124,7 @@ func TestMatch(t *testing.T) {
|
|||||||
require.Equal(t, pol[0], leaf)
|
require.Equal(t, pol[0], leaf)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("eq string in list", func(t *testing.T) {
|
t.Run("string in list", func(t *testing.T) {
|
||||||
nd, _ := literal.List([]any{"foo"})
|
nd, _ := literal.List([]any{"foo"})
|
||||||
|
|
||||||
pol := MustConstruct(Equal(".[0]", literal.String("foo")))
|
pol := MustConstruct(Equal(".[0]", literal.String("foo")))
|
||||||
@@ -134,132 +137,6 @@ func TestMatch(t *testing.T) {
|
|||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
require.Equal(t, pol[0], leaf)
|
require.Equal(t, pol[0], leaf)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("neq string", func(t *testing.T) {
|
|
||||||
nd := literal.String("test")
|
|
||||||
|
|
||||||
pol := MustConstruct(NotEqual(".", literal.String("test")))
|
|
||||||
ok, leaf := pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".", literal.String("test2")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".", literal.Int(138)))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("neq int", func(t *testing.T) {
|
|
||||||
nd := literal.Int(138)
|
|
||||||
|
|
||||||
pol := MustConstruct(NotEqual(".", literal.Int(138)))
|
|
||||||
ok, leaf := pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".", literal.Int(1138)))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".", literal.String("138")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("neq float", func(t *testing.T) {
|
|
||||||
nd := literal.Float(1.138)
|
|
||||||
|
|
||||||
pol := MustConstruct(NotEqual(".", literal.Float(1.138)))
|
|
||||||
ok, leaf := pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".", literal.Float(11.38)))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".", literal.String("138")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("neq IPLD Link", func(t *testing.T) {
|
|
||||||
l0 := cidlink.Link{Cid: cid.MustParse("bafybeif4owy5gno5lwnixqm52rwqfodklf76hsetxdhffuxnplvijskzqq")}
|
|
||||||
l1 := cidlink.Link{Cid: cid.MustParse("bafkreifau35r7vi37tvbvfy3hdwvgb4tlflqf7zcdzeujqcjk3rsphiwte")}
|
|
||||||
|
|
||||||
nd := literal.Link(l0)
|
|
||||||
|
|
||||||
pol := MustConstruct(NotEqual(".", literal.Link(l0)))
|
|
||||||
ok, leaf := pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".", literal.Link(l1)))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".", literal.String("bafybeif4owy5gno5lwnixqm52rwqfodklf76hsetxdhffuxnplvijskzqq")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("neq string in map", func(t *testing.T) {
|
|
||||||
nd, _ := literal.Map(map[string]any{
|
|
||||||
"foo": "bar",
|
|
||||||
})
|
|
||||||
|
|
||||||
pol := MustConstruct(NotEqual(".foo", literal.String("bar")))
|
|
||||||
ok, leaf := pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".[\"foo\"]", literal.String("bar")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".foo", literal.String("baz")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
|
|
||||||
// missing data will fail, as not optional
|
|
||||||
pol = MustConstruct(NotEqual(".foobar", literal.String("bar")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("neq string in list", func(t *testing.T) {
|
|
||||||
nd, _ := literal.List([]any{"foo"})
|
|
||||||
|
|
||||||
pol := MustConstruct(NotEqual(".[0]", literal.String("foo")))
|
|
||||||
ok, leaf := pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(NotEqual(".[0]", literal.String("bar")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
|
|
||||||
// missing data will fail, as not optional
|
|
||||||
pol = MustConstruct(NotEqual(".[1]", literal.String("foo")))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("inequality", func(t *testing.T) {
|
t.Run("inequality", func(t *testing.T) {
|
||||||
@@ -371,61 +248,20 @@ func TestMatch(t *testing.T) {
|
|||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
require.Equal(t, pol[0], leaf)
|
require.Equal(t, pol[0], leaf)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("lt float", func(t *testing.T) {
|
|
||||||
nd := literal.Float(1.38)
|
|
||||||
|
|
||||||
pol := MustConstruct(LessThan(".", literal.Float(1)))
|
|
||||||
ok, leaf := pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(LessThan(".", literal.Float(2)))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("lte float", func(t *testing.T) {
|
|
||||||
nd := literal.Float(1.38)
|
|
||||||
|
|
||||||
pol := MustConstruct(LessThanOrEqual(".", literal.Float(1)))
|
|
||||||
ok, leaf := pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, pol[0], leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(GreaterThanOrEqual(".", literal.Float(1.38)))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
|
|
||||||
pol = MustConstruct(LessThanOrEqual(".", literal.Float(2)))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.True(t, ok)
|
|
||||||
require.Nil(t, leaf)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("negation", func(t *testing.T) {
|
t.Run("negation", func(t *testing.T) {
|
||||||
nd, _ := literal.Map(map[string]any{
|
nd := literal.Bool(false)
|
||||||
"foo": false,
|
|
||||||
})
|
|
||||||
|
|
||||||
pol := MustConstruct(Not(Equal(".foo", literal.Bool(true))))
|
pol := MustConstruct(Not(Equal(".", literal.Bool(true))))
|
||||||
ok, leaf := pol.Match(nd)
|
ok, leaf := pol.Match(nd)
|
||||||
require.True(t, ok)
|
require.True(t, ok)
|
||||||
require.Nil(t, leaf)
|
require.Nil(t, leaf)
|
||||||
|
|
||||||
pol = MustConstruct(Not(Equal(".foo", literal.Bool(false))))
|
pol = MustConstruct(Not(Equal(".", literal.Bool(false))))
|
||||||
ok, leaf = pol.Match(nd)
|
ok, leaf = pol.Match(nd)
|
||||||
require.False(t, ok)
|
require.False(t, ok)
|
||||||
require.Equal(t, pol[0], leaf)
|
require.Equal(t, pol[0], leaf)
|
||||||
|
|
||||||
// missing data will fail, as not optional
|
|
||||||
pol = MustConstruct(Not(Equal(".foobar", literal.Bool(true))))
|
|
||||||
ok, leaf = pol.Match(nd)
|
|
||||||
require.False(t, ok)
|
|
||||||
require.Equal(t, MustConstruct(Equal(".foobar", literal.Bool(true)))[0], leaf)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("conjunction", func(t *testing.T) {
|
t.Run("conjunction", func(t *testing.T) {
|
||||||
@@ -649,7 +485,6 @@ func FuzzMatch(f *testing.F) {
|
|||||||
f.Add([]byte(`[["all", ".reviewer", ["like", ".email", "*@example.com"]]]`), []byte(`{"reviewer": [{"email": "alice@example.com"}, {"email": "bob@example.com"}]}`))
|
f.Add([]byte(`[["all", ".reviewer", ["like", ".email", "*@example.com"]]]`), []byte(`{"reviewer": [{"email": "alice@example.com"}, {"email": "bob@example.com"}]}`))
|
||||||
f.Add([]byte(`[["any", ".tags", ["or", [["==", ".", "news"], ["==", ".", "press"]]]]]`), []byte(`{"tags": ["news", "press"]}`))
|
f.Add([]byte(`[["any", ".tags", ["or", [["==", ".", "news"], ["==", ".", "press"]]]]]`), []byte(`{"tags": ["news", "press"]}`))
|
||||||
f.Add([]byte(`[["==", ".name", "Alice"]]`), []byte(`{"name": "Alice"}`))
|
f.Add([]byte(`[["==", ".name", "Alice"]]`), []byte(`{"name": "Alice"}`))
|
||||||
f.Add([]byte(`[["!=", ".name", "Alice"]]`), []byte(`{"name": "Alice"}`))
|
|
||||||
f.Add([]byte(`[[">", ".age", 30]]`), []byte(`{"age": 31}`))
|
f.Add([]byte(`[[">", ".age", 30]]`), []byte(`{"age": 31}`))
|
||||||
f.Add([]byte(`[["<=", ".height", 180]]`), []byte(`{"height": 170}`))
|
f.Add([]byte(`[["<=", ".height", 180]]`), []byte(`{"height": 170}`))
|
||||||
f.Add([]byte(`[["not", ["==", ".status", "inactive"]]]`), []byte(`{"status": "active"}`))
|
f.Add([]byte(`[["not", ["==", ".status", "inactive"]]]`), []byte(`{"status": "active"}`))
|
||||||
@@ -1069,3 +904,55 @@ func TestPartialMatch(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestInvocationValidation applies the example policy to the second
|
||||||
|
// example arguments as defined in the [Validation] section of the
|
||||||
|
// invocation specification.
|
||||||
|
//
|
||||||
|
// [Validation]: https://github.com/ucan-wg/delegation/tree/v1_ipld#validation
|
||||||
|
func TestInvocationValidationSpecExamples(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
pol := MustConstruct(
|
||||||
|
Equal(".from", literal.String("alice@example.com")),
|
||||||
|
Any(".to", Like(".", "*@example.com")),
|
||||||
|
)
|
||||||
|
|
||||||
|
t.Run("with passing args", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
argsNode, err := qp.BuildMap(basicnode.Prototype.Any, 2, func(ma datamodel.MapAssembler) {
|
||||||
|
qp.MapEntry(ma, "from", qp.String("alice@example.com"))
|
||||||
|
qp.MapEntry(ma, "to", qp.List(2, func(la datamodel.ListAssembler) {
|
||||||
|
qp.ListEntry(la, qp.String("bob@example.com"))
|
||||||
|
qp.ListEntry(la, qp.String("carol@not.example.com"))
|
||||||
|
}))
|
||||||
|
qp.MapEntry(ma, "title", qp.String("Coffee"))
|
||||||
|
qp.MapEntry(ma, "body", qp.String("Still on for coffee"))
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
exec, stmt := pol.Match(argsNode)
|
||||||
|
assert.True(t, exec)
|
||||||
|
assert.Nil(t, stmt)
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("fails on recipients (second statement)", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
|
argsNode, err := qp.BuildMap(basicnode.Prototype.Any, 2, func(ma datamodel.MapAssembler) {
|
||||||
|
qp.MapEntry(ma, "from", qp.String("alice@example.com"))
|
||||||
|
qp.MapEntry(ma, "to", qp.List(2, func(la datamodel.ListAssembler) {
|
||||||
|
qp.ListEntry(la, qp.String("bob@null.com"))
|
||||||
|
qp.ListEntry(la, qp.String("carol@elsewhere.example.com"))
|
||||||
|
}))
|
||||||
|
qp.MapEntry(ma, "title", qp.String("Coffee"))
|
||||||
|
qp.MapEntry(ma, "body", qp.String("Still on for coffee"))
|
||||||
|
})
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
exec, stmt := pol.Match(argsNode)
|
||||||
|
assert.False(t, exec)
|
||||||
|
assert.NotNil(t, stmt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
KindEqual = "==" // implemented by equality
|
KindEqual = "==" // implemented by equality
|
||||||
KindNotEqual = "!=" // implemented by equality
|
|
||||||
KindGreaterThan = ">" // implemented by equality
|
KindGreaterThan = ">" // implemented by equality
|
||||||
KindGreaterThanOrEqual = ">=" // implemented by equality
|
KindGreaterThanOrEqual = ">=" // implemented by equality
|
||||||
KindLessThan = "<" // implemented by equality
|
KindLessThan = "<" // implemented by equality
|
||||||
@@ -88,13 +87,6 @@ func Equal(selector string, value ipld.Node) Constructor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotEqual(selector string, value ipld.Node) Constructor {
|
|
||||||
return func() (Statement, error) {
|
|
||||||
sel, err := selpkg.Parse(selector)
|
|
||||||
return equality{kind: KindNotEqual, selector: sel, value: value}, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func GreaterThan(selector string, value ipld.Node) Constructor {
|
func GreaterThan(selector string, value ipld.Node) Constructor {
|
||||||
return func() (Statement, error) {
|
return func() (Statement, error) {
|
||||||
sel, err := selpkg.Parse(selector)
|
sel, err := selpkg.Parse(selector)
|
||||||
@@ -133,7 +125,7 @@ func (n negation) Kind() string {
|
|||||||
|
|
||||||
func (n negation) String() string {
|
func (n negation) String() string {
|
||||||
child := n.statement.String()
|
child := n.statement.String()
|
||||||
return fmt.Sprintf(`["%s", %s]`, n.Kind(), strings.ReplaceAll(child, "\n", "\n "))
|
return fmt.Sprintf(`["%s", "%s"]`, n.Kind(), strings.ReplaceAll(child, "\n", "\n "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func Not(cstor Constructor) Constructor {
|
func Not(cstor Constructor) Constructor {
|
||||||
@@ -157,7 +149,7 @@ func (c connective) String() string {
|
|||||||
for i, statement := range c.statements {
|
for i, statement := range c.statements {
|
||||||
childs[i] = strings.ReplaceAll(statement.String(), "\n", "\n ")
|
childs[i] = strings.ReplaceAll(statement.String(), "\n", "\n ")
|
||||||
}
|
}
|
||||||
return fmt.Sprintf("[\"%s\", [\n %s\n]]", c.kind, strings.Join(childs, ",\n "))
|
return fmt.Sprintf("[\"%s\", [\n %s]]\n", c.kind, strings.Join(childs, ",\n "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func And(cstors ...Constructor) Constructor {
|
func And(cstors ...Constructor) Constructor {
|
||||||
@@ -216,7 +208,7 @@ func (n quantifier) Kind() string {
|
|||||||
|
|
||||||
func (n quantifier) String() string {
|
func (n quantifier) String() string {
|
||||||
child := n.statement.String()
|
child := n.statement.String()
|
||||||
return fmt.Sprintf("[\"%s\", \"%s\",\n %s\n]", n.Kind(), n.selector, strings.ReplaceAll(child, "\n", "\n "))
|
return fmt.Sprintf("[\"%s\", \"%s\",\n %s]", n.Kind(), n.selector, strings.ReplaceAll(child, "\n", "\n "))
|
||||||
}
|
}
|
||||||
|
|
||||||
func All(selector string, cstor Constructor) Constructor {
|
func All(selector string, cstor Constructor) Constructor {
|
||||||
|
|||||||
@@ -28,14 +28,12 @@ func ExamplePolicy() {
|
|||||||
// [
|
// [
|
||||||
// ["==", ".status", "draft"],
|
// ["==", ".status", "draft"],
|
||||||
// ["all", ".reviewer",
|
// ["all", ".reviewer",
|
||||||
// ["like", ".email", "*@example.com"]
|
// ["like", ".email", "*@example.com"]],
|
||||||
// ],
|
|
||||||
// ["any", ".tags",
|
// ["any", ".tags",
|
||||||
// ["or", [
|
// ["or", [
|
||||||
// ["==", ".", "news"],
|
// ["==", ".", "news"],
|
||||||
// ["==", ".", "press"]
|
// ["==", ".", "press"]]]
|
||||||
// ]]
|
// ]
|
||||||
// ]
|
|
||||||
// ]
|
// ]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,14 +59,12 @@ func ExamplePolicy_accumulate() {
|
|||||||
// [
|
// [
|
||||||
// ["==", ".status", "draft"],
|
// ["==", ".status", "draft"],
|
||||||
// ["all", ".reviewer",
|
// ["all", ".reviewer",
|
||||||
// ["like", ".email", "*@example.com"]
|
// ["like", ".email", "*@example.com"]],
|
||||||
// ],
|
|
||||||
// ["any", ".tags",
|
// ["any", ".tags",
|
||||||
// ["or", [
|
// ["or", [
|
||||||
// ["==", ".", "news"],
|
// ["==", ".", "news"],
|
||||||
// ["==", ".", "press"]
|
// ["==", ".", "press"]]]
|
||||||
// ]]
|
// ]
|
||||||
// ]
|
|
||||||
// ]
|
// ]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +0,0 @@
|
|||||||
package policytest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/args"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/literal"
|
|
||||||
)
|
|
||||||
|
|
||||||
// EmptyPolicy provides a Policy with no statements.
|
|
||||||
var EmptyPolicy = policy.Policy{}
|
|
||||||
|
|
||||||
// SpecPolicy provides a valid Policy containing the statements that are included
|
|
||||||
// in the second code block of the [Validation] section of the delegation specification.
|
|
||||||
//
|
|
||||||
// [Validation]: https://github.com/ucan-wg/delegation/tree/v1_ipld#validation
|
|
||||||
var SpecPolicy = policy.MustConstruct(
|
|
||||||
policy.Equal(".from", literal.String("alice@example.com")),
|
|
||||||
policy.Any(".to", policy.Like(".", "*@example.com")),
|
|
||||||
)
|
|
||||||
|
|
||||||
// TODO: Replace the URL for [Validation] above when the delegation
|
|
||||||
// specification has been finished/merged.
|
|
||||||
|
|
||||||
// SpecValidArguments provides valid, instantiated Arguments containing
|
|
||||||
// the key/value pairs that are included in portion of the second code block
|
|
||||||
// of the [Validation] section of the delegation specification.
|
|
||||||
//
|
|
||||||
// [Validation]: https://github.com/ucan-wg/delegation/tree/v1_ipld#validation
|
|
||||||
var SpecValidArguments = args.NewBuilder().
|
|
||||||
Add("from", "alice@example.com").
|
|
||||||
Add("to", []string{
|
|
||||||
"bob@example.com",
|
|
||||||
"carol@not.example.com",
|
|
||||||
}).
|
|
||||||
Add("title", "Coffee").
|
|
||||||
Add("body", "Still on for coffee").
|
|
||||||
MustBuild()
|
|
||||||
|
|
||||||
var specValidArgumentsIPLD = mustIPLD(SpecValidArguments)
|
|
||||||
|
|
||||||
// SpecInvalidArguments provides invalid, instantiated Arguments containing
|
|
||||||
// the key/value pairs that are included in portion of the second code block
|
|
||||||
// of the [Validation] section of the delegation specification.
|
|
||||||
//
|
|
||||||
// [Validation]: https://github.com/ucan-wg/delegation/tree/v1_ipld#validation
|
|
||||||
var SpecInvalidArguments = args.NewBuilder().
|
|
||||||
Add("from", "alice@example.com").
|
|
||||||
Add("to", []string{
|
|
||||||
"bob@null.com",
|
|
||||||
"carol@elsewhere.example.com",
|
|
||||||
}).
|
|
||||||
Add("title", "Coffee").
|
|
||||||
Add("body", "Still on for coffee").
|
|
||||||
MustBuild()
|
|
||||||
|
|
||||||
var specInvalidArgumentsIPLD = mustIPLD(SpecInvalidArguments)
|
|
||||||
|
|
||||||
func mustIPLD(args *args.Args) ipld.Node {
|
|
||||||
node, err := args.ToIPLD()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
package policytest
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
// TestInvocationValidation applies the example policy to the second
|
|
||||||
// example arguments as defined in the [Validation] section of the
|
|
||||||
// invocation specification.
|
|
||||||
//
|
|
||||||
// [Validation]: https://github.com/ucan-wg/delegation/tree/v1_ipld#validation
|
|
||||||
func TestInvocationValidationSpecExamples(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
t.Run("with passing args", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
exec, stmt := SpecPolicy.Match(specValidArgumentsIPLD)
|
|
||||||
assert.True(t, exec)
|
|
||||||
assert.Nil(t, stmt)
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("fails on recipients (second statement)", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
exec, stmt := SpecPolicy.Match(specInvalidArgumentsIPLD)
|
|
||||||
assert.False(t, exec)
|
|
||||||
assert.NotNil(t, stmt)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -6,24 +6,12 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
indexRegex = regexp.MustCompile(`^-?\d+$`)
|
indexRegex = regexp.MustCompile(`^-?\d+$`)
|
||||||
sliceRegex = regexp.MustCompile(`^((\-?\d+:\-?\d*)|(\-?\d*:\-?\d+))$`)
|
sliceRegex = regexp.MustCompile(`^((\-?\d+:\-?\d*)|(\-?\d*:\-?\d+))$`)
|
||||||
|
fieldRegex = regexp.MustCompile(`^\.[a-zA-Z_]*?$`)
|
||||||
// Field name requirements:
|
|
||||||
// - Must start with ASCII letter, Unicode letter, or underscore
|
|
||||||
// - Can contain:
|
|
||||||
// - ASCII letters (a-z, A-Z)
|
|
||||||
// - ASCII digits (0-9)
|
|
||||||
// - Unicode letters (\p{L})
|
|
||||||
// - Dollar sign ($)
|
|
||||||
// - Underscore (_)
|
|
||||||
// - Hyphen (-)
|
|
||||||
fieldRegex = regexp.MustCompile(`^\.[a-zA-Z_\p{L}][a-zA-Z0-9$_\p{L}\-]*$`)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func Parse(str string) (Selector, error) {
|
func Parse(str string) (Selector, error) {
|
||||||
@@ -68,9 +56,6 @@ func Parse(str string) (Selector, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newParseError("invalid index", str, col, tok)
|
return nil, newParseError("invalid index", str, col, tok)
|
||||||
}
|
}
|
||||||
if int64(idx) > limits.MaxInt53 || int64(idx) < limits.MinInt53 {
|
|
||||||
return nil, newParseError(fmt.Sprintf("index %d exceeds safe integer bounds", idx), str, col, tok)
|
|
||||||
}
|
|
||||||
sel = append(sel, segment{str: tok, optional: opt, index: idx})
|
sel = append(sel, segment{str: tok, optional: opt, index: idx})
|
||||||
|
|
||||||
// explicit field, ["abcd"]
|
// explicit field, ["abcd"]
|
||||||
@@ -92,9 +77,6 @@ func Parse(str string) (Selector, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newParseError("invalid slice index", str, col, tok)
|
return nil, newParseError("invalid slice index", str, col, tok)
|
||||||
}
|
}
|
||||||
if i > limits.MaxInt53 || i < limits.MinInt53 {
|
|
||||||
return nil, newParseError(fmt.Sprintf("slice index %d exceeds safe integer bounds", i), str, col, tok)
|
|
||||||
}
|
|
||||||
rng[0] = i
|
rng[0] = i
|
||||||
}
|
}
|
||||||
if splt[1] == "" {
|
if splt[1] == "" {
|
||||||
@@ -104,9 +86,6 @@ func Parse(str string) (Selector, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, newParseError("invalid slice index", str, col, tok)
|
return nil, newParseError("invalid slice index", str, col, tok)
|
||||||
}
|
}
|
||||||
if i > limits.MaxInt53 || i < limits.MinInt53 {
|
|
||||||
return nil, newParseError(fmt.Sprintf("slice index %d exceeds safe integer bounds", i), str, col, tok)
|
|
||||||
}
|
|
||||||
rng[1] = i
|
rng[1] = i
|
||||||
}
|
}
|
||||||
sel = append(sel, segment{str: tok, optional: opt, slice: rng[:]})
|
sel = append(sel, segment{str: tok, optional: opt, slice: rng[:]})
|
||||||
@@ -173,37 +152,37 @@ func tokenize(str string) []string {
|
|||||||
return toks
|
return toks
|
||||||
}
|
}
|
||||||
|
|
||||||
type parseErr struct {
|
type parseerr struct {
|
||||||
msg string
|
msg string
|
||||||
src string
|
src string
|
||||||
col int
|
col int
|
||||||
tok string
|
tok string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p parseErr) Name() string {
|
func (p parseerr) Name() string {
|
||||||
return "ParseError"
|
return "ParseError"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p parseErr) Message() string {
|
func (p parseerr) Message() string {
|
||||||
return p.msg
|
return p.msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p parseErr) Column() int {
|
func (p parseerr) Column() int {
|
||||||
return p.col
|
return p.col
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p parseErr) Error() string {
|
func (p parseerr) Error() string {
|
||||||
return p.msg
|
return p.msg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p parseErr) Source() string {
|
func (p parseerr) Source() string {
|
||||||
return p.src
|
return p.src
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p parseErr) Token() string {
|
func (p parseerr) Token() string {
|
||||||
return p.tok
|
return p.tok
|
||||||
}
|
}
|
||||||
|
|
||||||
func newParseError(message string, source string, column int, token string) error {
|
func newParseError(message string, source string, column int, token string) error {
|
||||||
return parseErr{message, source, column, token}
|
return parseerr{message, source, column, token}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
package selector
|
package selector
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"math"
|
"math"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/limits"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestParse(t *testing.T) {
|
func TestParse(t *testing.T) {
|
||||||
@@ -33,29 +30,6 @@ func TestParse(t *testing.T) {
|
|||||||
require.Empty(t, sel[0].Slice())
|
require.Empty(t, sel[0].Slice())
|
||||||
require.Equal(t, sel[0].Field(), "foo")
|
require.Equal(t, sel[0].Field(), "foo")
|
||||||
require.Empty(t, sel[0].Index())
|
require.Empty(t, sel[0].Index())
|
||||||
|
|
||||||
sel, err = Parse(".foo_bar")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, len(sel))
|
|
||||||
require.False(t, sel[0].Identity())
|
|
||||||
require.False(t, sel[0].Optional())
|
|
||||||
require.False(t, sel[0].Iterator())
|
|
||||||
require.Empty(t, sel[0].Slice())
|
|
||||||
require.Equal(t, sel[0].Field(), "foo_bar")
|
|
||||||
require.Empty(t, sel[0].Index())
|
|
||||||
|
|
||||||
sel, err = Parse(".foo-bar")
|
|
||||||
require.NoError(t, err)
|
|
||||||
require.Equal(t, 1, len(sel))
|
|
||||||
require.False(t, sel[0].Identity())
|
|
||||||
require.False(t, sel[0].Optional())
|
|
||||||
require.False(t, sel[0].Iterator())
|
|
||||||
require.Empty(t, sel[0].Slice())
|
|
||||||
require.Equal(t, sel[0].Field(), "foo-bar")
|
|
||||||
require.Empty(t, sel[0].Index())
|
|
||||||
|
|
||||||
sel, err = Parse(".foo*bar")
|
|
||||||
require.ErrorContains(t, err, "invalid segment")
|
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("explicit field", func(t *testing.T) {
|
t.Run("explicit field", func(t *testing.T) {
|
||||||
@@ -575,67 +549,4 @@ func TestParse(t *testing.T) {
|
|||||||
_, err := Parse(".[foo]")
|
_, err := Parse(".[foo]")
|
||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("extended field names", func(t *testing.T) {
|
|
||||||
validFields := []string{
|
|
||||||
".basic",
|
|
||||||
".user_name",
|
|
||||||
".user-name",
|
|
||||||
".userName$special",
|
|
||||||
".αβγ", // Greek letters
|
|
||||||
".użytkownik", // Polish characters
|
|
||||||
".用户", // Chinese characters
|
|
||||||
".사용자", // Korean characters
|
|
||||||
"._private",
|
|
||||||
".number123",
|
|
||||||
".camelCase",
|
|
||||||
".snake_case",
|
|
||||||
".kebab-case",
|
|
||||||
".mixed_kebab-case",
|
|
||||||
".with$dollar",
|
|
||||||
".MIXED_Case_123",
|
|
||||||
".unicodeø",
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, field := range validFields {
|
|
||||||
sel, err := Parse(field)
|
|
||||||
require.NoError(t, err, "field: %s", field)
|
|
||||||
require.NotNil(t, sel)
|
|
||||||
}
|
|
||||||
|
|
||||||
invalidFields := []string{
|
|
||||||
".123number", // Can't start with digit
|
|
||||||
".@special", // @ not allowed
|
|
||||||
".space name", // No spaces
|
|
||||||
".#hashtag", // No #
|
|
||||||
".name!", // No !
|
|
||||||
".{brackets}", // No brackets
|
|
||||||
".name/with/slashes", // No slashes
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, field := range invalidFields {
|
|
||||||
sel, err := Parse(field)
|
|
||||||
require.Error(t, err, "field: %s", field)
|
|
||||||
require.Nil(t, sel)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("integer overflow", func(t *testing.T) {
|
|
||||||
sel, err := Parse(fmt.Sprintf(".[%d]", limits.MaxInt53+1))
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Nil(t, sel)
|
|
||||||
|
|
||||||
sel, err = Parse(fmt.Sprintf(".[%d]", limits.MinInt53-1))
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Nil(t, sel)
|
|
||||||
|
|
||||||
// Test slice overflow
|
|
||||||
sel, err = Parse(fmt.Sprintf(".[%d:42]", limits.MaxInt53+1))
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Nil(t, sel)
|
|
||||||
|
|
||||||
sel, err = Parse(fmt.Sprintf(".[1:%d]", limits.MaxInt53+1))
|
|
||||||
require.Error(t, err)
|
|
||||||
require.Nil(t, sel)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ type Selector []segment
|
|||||||
// Select perform the selection described by the selector on the input IPLD DAG.
|
// Select perform the selection described by the selector on the input IPLD DAG.
|
||||||
// Select can return:
|
// Select can return:
|
||||||
// - exactly one matched IPLD node
|
// - exactly one matched IPLD node
|
||||||
// - a resolutionErr error if not being able to resolve to a node
|
// - a resolutionerr error if not being able to resolve to a node
|
||||||
// - nil and no errors, if the selector couldn't match on an optional segment (with ?).
|
// - nil and no errors, if the selector couldn't match on an optional segment (with ?).
|
||||||
func (s Selector) Select(subject ipld.Node) (ipld.Node, error) {
|
func (s Selector) Select(subject ipld.Node) (ipld.Node, error) {
|
||||||
return resolve(s, subject, nil)
|
return resolve(s, subject, nil)
|
||||||
@@ -266,32 +266,19 @@ func resolveSliceIndices(slice []int64, length int64) (start int64, end int64) {
|
|||||||
case slice[0] == math.MinInt:
|
case slice[0] == math.MinInt:
|
||||||
start = 0
|
start = 0
|
||||||
case slice[0] < 0:
|
case slice[0] < 0:
|
||||||
// Check for potential overflow before adding
|
start = length + slice[0]
|
||||||
if -slice[0] > length {
|
|
||||||
start = 0
|
|
||||||
} else {
|
|
||||||
start = length + slice[0]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case slice[1] == math.MaxInt:
|
case slice[1] == math.MaxInt:
|
||||||
end = length
|
end = length
|
||||||
case slice[1] < 0:
|
case slice[1] < 0:
|
||||||
// Check for potential overflow before adding
|
end = length + slice[1]
|
||||||
if -slice[1] > length {
|
|
||||||
end = 0
|
|
||||||
} else {
|
|
||||||
end = length + slice[1]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// backward iteration is not allowed, shortcut to an empty result
|
// backward iteration is not allowed, shortcut to an empty result
|
||||||
if start >= end {
|
if start >= end {
|
||||||
start, end = 0, 0
|
start, end = 0, 0
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// clamp out of bound
|
// clamp out of bound
|
||||||
if start < 0 {
|
if start < 0 {
|
||||||
start = 0
|
start = 0
|
||||||
@@ -299,14 +286,11 @@ func resolveSliceIndices(slice []int64, length int64) (start int64, end int64) {
|
|||||||
if start > length {
|
if start > length {
|
||||||
start = length
|
start = length
|
||||||
}
|
}
|
||||||
if end < 0 {
|
|
||||||
end = 0
|
|
||||||
}
|
|
||||||
if end > length {
|
if end > length {
|
||||||
end = length
|
end = length
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return start, end
|
||||||
}
|
}
|
||||||
|
|
||||||
func kindString(n datamodel.Node) string {
|
func kindString(n datamodel.Node) string {
|
||||||
@@ -316,27 +300,27 @@ func kindString(n datamodel.Node) string {
|
|||||||
return n.Kind().String()
|
return n.Kind().String()
|
||||||
}
|
}
|
||||||
|
|
||||||
type resolutionErr struct {
|
type resolutionerr struct {
|
||||||
msg string
|
msg string
|
||||||
at []string
|
at []string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r resolutionErr) Name() string {
|
func (r resolutionerr) Name() string {
|
||||||
return "ResolutionError"
|
return "ResolutionError"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r resolutionErr) Message() string {
|
func (r resolutionerr) Message() string {
|
||||||
return fmt.Sprintf("can not resolve path: .%s", strings.Join(r.at, "."))
|
return fmt.Sprintf("can not resolve path: .%s", strings.Join(r.at, "."))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r resolutionErr) At() []string {
|
func (r resolutionerr) At() []string {
|
||||||
return r.at
|
return r.at
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r resolutionErr) Error() string {
|
func (r resolutionerr) Error() string {
|
||||||
return r.Message()
|
return r.Message()
|
||||||
}
|
}
|
||||||
|
|
||||||
func newResolutionError(message string, at []string) error {
|
func newResolutionError(message string, at []string) error {
|
||||||
return resolutionErr{message, at}
|
return resolutionerr{message, at}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package selector
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"math"
|
|
||||||
"strings"
|
"strings"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@@ -133,7 +132,7 @@ func TestSelect(t *testing.T) {
|
|||||||
require.Error(t, err)
|
require.Error(t, err)
|
||||||
require.Empty(t, res)
|
require.Empty(t, res)
|
||||||
|
|
||||||
require.ErrorAs(t, err, &resolutionErr{}, "error should be a resolution error")
|
require.ErrorAs(t, err, &resolutionerr{}, "error should be a resolution error")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("optional not exists", func(t *testing.T) {
|
t.Run("optional not exists", func(t *testing.T) {
|
||||||
@@ -351,63 +350,9 @@ func FuzzParseAndSelect(f *testing.F) {
|
|||||||
|
|
||||||
// look for panic()
|
// look for panic()
|
||||||
_, err = sel.Select(node)
|
_, err = sel.Select(node)
|
||||||
if err != nil && !errors.As(err, &resolutionErr{}) {
|
if err != nil && !errors.As(err, &resolutionerr{}) {
|
||||||
// not normal, we should only have resolution errors
|
// not normal, we should only have resolution errors
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestResolveSliceIndices(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
slice []int64
|
|
||||||
length int64
|
|
||||||
wantStart int64
|
|
||||||
wantEnd int64
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
name: "normal case",
|
|
||||||
slice: []int64{1, 3},
|
|
||||||
length: 5,
|
|
||||||
wantStart: 1,
|
|
||||||
wantEnd: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "negative indices",
|
|
||||||
slice: []int64{-2, -1},
|
|
||||||
length: 5,
|
|
||||||
wantStart: 3,
|
|
||||||
wantEnd: 4,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow protection negative start",
|
|
||||||
slice: []int64{math.MinInt64, 3},
|
|
||||||
length: 5,
|
|
||||||
wantStart: 0,
|
|
||||||
wantEnd: 3,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "overflow protection negative end",
|
|
||||||
slice: []int64{0, math.MinInt64},
|
|
||||||
length: 5,
|
|
||||||
wantStart: 0,
|
|
||||||
wantEnd: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "max bounds",
|
|
||||||
slice: []int64{0, math.MaxInt64},
|
|
||||||
length: 5,
|
|
||||||
wantStart: 0,
|
|
||||||
wantEnd: 5,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
start, end := resolveSliceIndices(tt.slice, tt.length)
|
|
||||||
require.Equal(t, tt.wantStart, start)
|
|
||||||
require.Equal(t, tt.wantEnd, end)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,90 +0,0 @@
|
|||||||
package secretbox
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rand"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/nacl/secretbox"
|
|
||||||
)
|
|
||||||
|
|
||||||
const keySize = 32 // secretbox allows only 32-byte keys
|
|
||||||
|
|
||||||
var ErrShortCipherText = errors.New("ciphertext too short")
|
|
||||||
var ErrNoEncryptionKey = errors.New("encryption key is required")
|
|
||||||
var ErrInvalidKeySize = errors.New("invalid key size: must be 32 bytes")
|
|
||||||
var ErrZeroKey = errors.New("encryption key cannot be all zeros")
|
|
||||||
|
|
||||||
// GenerateKey generates a random 32-byte key to be used by EncryptWithKey and DecryptWithKey
|
|
||||||
func GenerateKey() ([]byte, error) {
|
|
||||||
key := make([]byte, keySize)
|
|
||||||
if _, err := io.ReadFull(rand.Reader, key); err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to generate key: %w", err)
|
|
||||||
}
|
|
||||||
return key, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncryptWithKey encrypts data using NaCl's secretbox with the provided key.
|
|
||||||
// 40 bytes of overhead (24-byte nonce + 16-byte MAC) are added to the plaintext size.
|
|
||||||
func EncryptWithKey(data, key []byte) ([]byte, error) {
|
|
||||||
if err := validateKey(key); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var secretKey [keySize]byte
|
|
||||||
copy(secretKey[:], key)
|
|
||||||
|
|
||||||
// Generate 24 bytes of random data as nonce
|
|
||||||
var nonce [24]byte
|
|
||||||
if _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encrypt and authenticate data
|
|
||||||
encrypted := secretbox.Seal(nonce[:], data, &nonce, &secretKey)
|
|
||||||
return encrypted, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecryptStringWithKey decrypts data using secretbox with the provided key
|
|
||||||
func DecryptStringWithKey(data, key []byte) ([]byte, error) {
|
|
||||||
if err := validateKey(key); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(data) < 24 {
|
|
||||||
return nil, ErrShortCipherText
|
|
||||||
}
|
|
||||||
|
|
||||||
var secretKey [keySize]byte
|
|
||||||
copy(secretKey[:], key)
|
|
||||||
|
|
||||||
var nonce [24]byte
|
|
||||||
copy(nonce[:], data[:24])
|
|
||||||
|
|
||||||
decrypted, ok := secretbox.Open(nil, data[24:], &nonce, &secretKey)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("decryption failed")
|
|
||||||
}
|
|
||||||
|
|
||||||
return decrypted, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func validateKey(key []byte) error {
|
|
||||||
if key == nil {
|
|
||||||
return ErrNoEncryptionKey
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(key) != keySize {
|
|
||||||
return ErrInvalidKeySize
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if key is all zeros
|
|
||||||
for _, b := range key {
|
|
||||||
if b != 0 {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return ErrZeroKey
|
|
||||||
}
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
// Package attestation implements the UCAN [attestation] specification with
|
|
||||||
// an immutable Token type as well as methods to convert the Token to and
|
|
||||||
// from the [envelope]-enclosed, signed and DAG-CBOR-encoded form that
|
|
||||||
// should most commonly be used for transport and storage.
|
|
||||||
//
|
|
||||||
// [envelope]: https://github.com/ucan-wg/spec#envelope
|
|
||||||
// [attestation]: TBD
|
|
||||||
package attestation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/MetaMask/go-did-it"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/claims"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/meta"
|
|
||||||
"github.com/ucan-wg/go-ucan/token/internal/nonce"
|
|
||||||
"github.com/ucan-wg/go-ucan/token/internal/parse"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Token is an immutable type that holds the fields of a UCAN attestation.
|
|
||||||
type Token struct {
|
|
||||||
// The DID of the Invoker
|
|
||||||
issuer did.DID
|
|
||||||
// TODO: should this exist?
|
|
||||||
// audience did.DID
|
|
||||||
|
|
||||||
// Arbitrary Claims
|
|
||||||
claims *claims.Claims
|
|
||||||
|
|
||||||
// Arbitrary Metadata
|
|
||||||
meta *meta.Meta
|
|
||||||
|
|
||||||
// A unique, random nonce
|
|
||||||
nonce []byte
|
|
||||||
// The timestamp at which the Invocation becomes invalid
|
|
||||||
expiration *time.Time
|
|
||||||
// The timestamp at which the Invocation was created
|
|
||||||
issuedAt *time.Time
|
|
||||||
}
|
|
||||||
|
|
||||||
// New creates an attestation Token with the provided options.
|
|
||||||
//
|
|
||||||
// If no nonce is provided, a random 12-byte nonce is generated. Use the
|
|
||||||
// WithNonce or WithEmptyNonce options to specify provide your own nonce
|
|
||||||
// or to leave the nonce empty respectively.
|
|
||||||
//
|
|
||||||
// If no IssuedAt is provided, the current time is used. Use the
|
|
||||||
// IssuedAt or WithIssuedAtIn Options to specify a different time
|
|
||||||
// or the WithoutIssuedAt Option to clear the Token's IssuedAt field.
|
|
||||||
//
|
|
||||||
// With the exception of the WithMeta option, all others will overwrite
|
|
||||||
// the previous contents of their target field.
|
|
||||||
//
|
|
||||||
// You can read it as "(Issuer - I) attest (arbitrary claim)".
|
|
||||||
func New(iss did.DID, opts ...Option) (*Token, error) {
|
|
||||||
iat := time.Now()
|
|
||||||
|
|
||||||
tkn := Token{
|
|
||||||
issuer: iss,
|
|
||||||
claims: claims.NewClaims(),
|
|
||||||
meta: meta.NewMeta(),
|
|
||||||
nonce: nil,
|
|
||||||
issuedAt: &iat,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, opt := range opts {
|
|
||||||
if err := opt(&tkn); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
|
||||||
if len(tkn.nonce) == 0 {
|
|
||||||
tkn.nonce, err = nonce.Generate()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tkn.validate(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &tkn, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Issuer returns the did.DID representing the Token's issuer.
|
|
||||||
func (t *Token) Issuer() did.DID {
|
|
||||||
return t.issuer
|
|
||||||
}
|
|
||||||
|
|
||||||
// Claims returns the Token's claims.
|
|
||||||
func (t *Token) Claims() claims.ReadOnly {
|
|
||||||
return t.claims.ReadOnly()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Meta returns the Token's metadata.
|
|
||||||
func (t *Token) Meta() meta.ReadOnly {
|
|
||||||
return t.meta.ReadOnly()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Nonce returns the random Nonce encapsulated in this Token.
|
|
||||||
func (t *Token) Nonce() []byte {
|
|
||||||
return t.nonce
|
|
||||||
}
|
|
||||||
|
|
||||||
// Expiration returns the time at which the Token expires.
|
|
||||||
func (t *Token) Expiration() *time.Time {
|
|
||||||
return t.expiration
|
|
||||||
}
|
|
||||||
|
|
||||||
// IssuedAt returns the time.Time at which the invocation token was
|
|
||||||
// created.
|
|
||||||
func (t *Token) IssuedAt() *time.Time {
|
|
||||||
return t.issuedAt
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValidNow verifies that the token can be used at the current time, based on expiration or "not before" fields.
|
|
||||||
// This does NOT do any other kind of verifications.
|
|
||||||
func (t *Token) IsValidNow() bool {
|
|
||||||
return t.IsValidAt(time.Now())
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValidAt verifies that the token can be used at the given time, based on expiration or "not before" fields.
|
|
||||||
// This does NOT do any other kind of verifications.
|
|
||||||
func (t *Token) IsValidAt(ti time.Time) bool {
|
|
||||||
if t.expiration != nil && ti.After(*t.expiration) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Token) String() string {
|
|
||||||
var res strings.Builder
|
|
||||||
|
|
||||||
res.WriteString(fmt.Sprintf("Issuer: %s\n", t.Issuer()))
|
|
||||||
res.WriteString(fmt.Sprintf("Nonce: %s\n", base64.StdEncoding.EncodeToString(t.Nonce())))
|
|
||||||
res.WriteString(fmt.Sprintf("Meta: %s\n", t.Meta()))
|
|
||||||
res.WriteString(fmt.Sprintf("Expiration: %v\n", t.Expiration()))
|
|
||||||
res.WriteString(fmt.Sprintf("Issued At: %v\n", t.IssuedAt()))
|
|
||||||
|
|
||||||
return res.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Token) validate() error {
|
|
||||||
var errs error
|
|
||||||
|
|
||||||
requiredDID := func(id did.DID, fieldname string) {
|
|
||||||
if id == nil {
|
|
||||||
errs = errors.Join(errs, fmt.Errorf(`a valid did is required for %s: %s`, fieldname, id.String()))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
requiredDID(t.issuer, "Issuer")
|
|
||||||
|
|
||||||
if len(t.nonce) < 12 {
|
|
||||||
errs = errors.Join(errs, fmt.Errorf("token nonce too small"))
|
|
||||||
}
|
|
||||||
|
|
||||||
return errs
|
|
||||||
}
|
|
||||||
|
|
||||||
// tokenFromModel build a decoded view of the raw IPLD data.
|
|
||||||
// This function also serves as validation.
|
|
||||||
func tokenFromModel(m tokenPayloadModel) (*Token, error) {
|
|
||||||
var (
|
|
||||||
tkn Token
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
|
|
||||||
if tkn.issuer, err = did.Parse(m.Iss); err != nil {
|
|
||||||
return nil, fmt.Errorf("parse iss: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tkn.claims = m.Claims
|
|
||||||
if tkn.claims == nil {
|
|
||||||
tkn.claims = claims.NewClaims()
|
|
||||||
}
|
|
||||||
|
|
||||||
tkn.meta = m.Meta
|
|
||||||
if tkn.meta == nil {
|
|
||||||
tkn.meta = meta.NewMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(m.Nonce) == 0 {
|
|
||||||
return nil, fmt.Errorf("nonce is required")
|
|
||||||
}
|
|
||||||
tkn.nonce = m.Nonce
|
|
||||||
|
|
||||||
tkn.expiration, err = parse.OptionalTimestamp(m.Exp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parse expiration: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tkn.issuedAt, err = parse.OptionalTimestamp(m.Iat)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parse IssuedAt: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tkn.validate(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return &tkn, nil
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
type DID string
|
|
||||||
|
|
||||||
# The Attestation payload
|
|
||||||
type Payload struct {
|
|
||||||
# Issuer DID (sender)
|
|
||||||
iss DID
|
|
||||||
# Audience DID (receiver) TODO: should that exist?
|
|
||||||
# aud DID
|
|
||||||
|
|
||||||
# Arbitrary claims
|
|
||||||
claims optional {String: Any}
|
|
||||||
|
|
||||||
# Arbitrary Metadata
|
|
||||||
meta optional {String : Any}
|
|
||||||
|
|
||||||
# A unique, random nonce
|
|
||||||
nonce Bytes
|
|
||||||
# The timestamp at which the Invocation becomes invalid
|
|
||||||
exp nullable Int
|
|
||||||
# The Timestamp at which the Invocation was created
|
|
||||||
iat optional Int
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
package attestation_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"encoding/json"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/MetaMask/go-did-it"
|
|
||||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
|
||||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
|
||||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
|
||||||
"github.com/ipld/go-ipld-prime/node/basicnode"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/token/attestation"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ExampleNew() {
|
|
||||||
privKey, iss, _, _, err := setupExampleNew()
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("failed to create setup:", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
att, err := attestation.New(iss,
|
|
||||||
attestation.WithClaim("claim1", "UCAN is great"),
|
|
||||||
attestation.WithMeta("env", "development"),
|
|
||||||
attestation.WithExpirationIn(time.Minute),
|
|
||||||
attestation.WithoutIssuedAt())
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("failed to create attestation:", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// foo, _ := att.ToDagJson(privKey)
|
|
||||||
// os.WriteFile("testdata/new.dagjson", foo, 0666)
|
|
||||||
// fmt.Println(base64.StdEncoding.EncodeToString(privKey.ToBytes()))
|
|
||||||
|
|
||||||
data, cid, err := att.ToSealed(privKey)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("failed to seal attestation:", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
json, err := prettyDAGJSON(data)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("failed to pretty DAG-JSON:", err.Error())
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("CID:", cid)
|
|
||||||
fmt.Println("Token (pretty DAG-JSON):")
|
|
||||||
fmt.Println(json)
|
|
||||||
|
|
||||||
// Expected CID and DAG-JSON output:
|
|
||||||
// CID: bafyreibm5vo6gk75oreefkg6xkrrfb4d5dgkccgmutirjgtzi5j45svjm4
|
|
||||||
// Token (pretty DAG-JSON):
|
|
||||||
// [
|
|
||||||
// {
|
|
||||||
// "/": {
|
|
||||||
// "bytes": "ApuXUsUYhqostO2zfKZK50GW0gXYPtrlpoVA8EwGFdyYahQecOizVpl+9wy64aqk2rMP4Q0UEUKCTV0ONMdPAw"
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// "h": {
|
|
||||||
// "/": {
|
|
||||||
// "bytes": "NAHtAe0BE3E"
|
|
||||||
// }
|
|
||||||
// },
|
|
||||||
// "ucan/att@tbd": {
|
|
||||||
// "claims": {
|
|
||||||
// "claim1": "UCAN is great"
|
|
||||||
// },
|
|
||||||
// "exp": 1767790971,
|
|
||||||
// "iss": "did:key:z6Mkm4RzzBDfSHqmwV9dp5jFsLkVgKRYp1PhSj7VybCcLHC4",
|
|
||||||
// "meta": {
|
|
||||||
// "env": "development"
|
|
||||||
// },
|
|
||||||
// "nonce": {
|
|
||||||
// "/": {
|
|
||||||
// "bytes": "NjS8QPvft97jbtUG"
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// ]
|
|
||||||
}
|
|
||||||
|
|
||||||
func prettyDAGJSON(data []byte) (string, error) {
|
|
||||||
var node ipld.Node
|
|
||||||
|
|
||||||
node, err := ipld.Decode(data, dagcbor.Decode)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
jsonData, err := ipld.Encode(node, dagjson.Encode)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
var out bytes.Buffer
|
|
||||||
if err := json.Indent(&out, jsonData, "", " "); err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return out.String(), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func setupExampleNew() (privKey ed25519.PrivateKey, iss did.DID, claims map[string]any, meta map[string]any, errs error) {
|
|
||||||
var err error
|
|
||||||
|
|
||||||
_, privKey, err = ed25519.GenerateKeyPair()
|
|
||||||
if err != nil {
|
|
||||||
errs = errors.Join(errs, fmt.Errorf("failed to generate Ed25519 keypair: %w", err))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
iss = didkeyctl.FromPrivateKey(privKey)
|
|
||||||
|
|
||||||
claims = map[string]any{
|
|
||||||
"claim1": "UCAN is great",
|
|
||||||
}
|
|
||||||
|
|
||||||
meta = map[string]any{
|
|
||||||
"env": basicnode.NewString("development"),
|
|
||||||
}
|
|
||||||
|
|
||||||
return // WARNING: named return values
|
|
||||||
}
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
package attestation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"io"
|
|
||||||
|
|
||||||
"github.com/MetaMask/go-did-it"
|
|
||||||
"github.com/MetaMask/go-did-it/crypto"
|
|
||||||
"github.com/ipfs/go-cid"
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
"github.com/ipld/go-ipld-prime/codec"
|
|
||||||
"github.com/ipld/go-ipld-prime/codec/dagcbor"
|
|
||||||
"github.com/ipld/go-ipld-prime/codec/dagjson"
|
|
||||||
"github.com/ipld/go-ipld-prime/datamodel"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
|
||||||
)
|
|
||||||
|
|
||||||
// ToSealed wraps the attestation token in an envelope, generates the
|
|
||||||
// signature, encodes the result to DAG-CBOR and calculates the CID of
|
|
||||||
// the resulting binary data.
|
|
||||||
func (t *Token) ToSealed(privKey crypto.PrivateKeySigningBytes) ([]byte, cid.Cid, error) {
|
|
||||||
data, err := t.ToDagCbor(privKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := envelope.CIDFromBytes(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return data, id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToSealedWriter is the same as ToSealed but accepts an io.Writer.
|
|
||||||
func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) (cid.Cid, error) {
|
|
||||||
cidWriter := envelope.NewCIDWriter(w)
|
|
||||||
|
|
||||||
if err := t.ToDagCborWriter(cidWriter, privKey); err != nil {
|
|
||||||
return cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return cidWriter.CID()
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromSealed decodes the provided binary data from the DAG-CBOR format,
|
|
||||||
// verifies that the envelope's signature is correct based on the public
|
|
||||||
// key taken from the issuer (iss) field and calculates the CID of the
|
|
||||||
// incoming data.
|
|
||||||
func FromSealed(data []byte, resolvOpts ...did.ResolutionOption) (*Token, cid.Cid, error) {
|
|
||||||
tkn, err := FromDagCbor(data, resolvOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := envelope.CIDFromBytes(data)
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tkn, id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromSealedReader is the same as Unseal but accepts an io.Reader.
|
|
||||||
func FromSealedReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, cid.Cid, error) {
|
|
||||||
cidReader := envelope.NewCIDReader(r)
|
|
||||||
|
|
||||||
tkn, err := FromDagCborReader(cidReader, resolvOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := cidReader.CID()
|
|
||||||
if err != nil {
|
|
||||||
return nil, cid.Undef, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tkn, id, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Encode marshals a Token to the format specified by the provided
|
|
||||||
// codec.Encoder.
|
|
||||||
func (t *Token) Encode(privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) ([]byte, error) {
|
|
||||||
node, err := t.toIPLD(privKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ipld.Encode(node, encFn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// EncodeWriter is the same as Encode, but accepts an io.Writer.
|
|
||||||
func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) error {
|
|
||||||
node, err := t.toIPLD(privKey)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return ipld.EncodeStreaming(w, node, encFn)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDagCbor marshals the Token to the DAG-CBOR format.
|
|
||||||
func (t *Token) ToDagCbor(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
|
|
||||||
return t.Encode(privKey, dagcbor.Encode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDagCborWriter is the same as ToDagCbor, but it accepts an io.Writer.
|
|
||||||
func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
|
|
||||||
return t.EncodeWriter(w, privKey, dagcbor.Encode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDagJson marshals the Token to the DAG-JSON format.
|
|
||||||
func (t *Token) ToDagJson(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
|
|
||||||
return t.Encode(privKey, dagjson.Encode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDagJsonWriter is the same as ToDagJson, but it accepts an io.Writer.
|
|
||||||
func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
|
|
||||||
return t.EncodeWriter(w, privKey, dagjson.Encode)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode unmarshals the input data using the format specified by the
|
|
||||||
// provided codec.Decoder into a Token.
|
|
||||||
//
|
|
||||||
// An error is returned if the conversion fails or if the resulting
|
|
||||||
// Token is invalid.
|
|
||||||
func Decode(b []byte, decFn codec.Decoder, resolvOpts ...did.ResolutionOption) (*Token, error) {
|
|
||||||
node, err := ipld.Decode(b, decFn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return FromIPLD(node, resolvOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DecodeReader is the same as Decode, but accept an io.Reader.
|
|
||||||
func DecodeReader(r io.Reader, decFn codec.Decoder, resolvOpts ...did.ResolutionOption) (*Token, error) {
|
|
||||||
node, err := ipld.DecodeStreaming(r, decFn)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return FromIPLD(node, resolvOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromDagCbor unmarshals the input data into a Token.
|
|
||||||
//
|
|
||||||
// An error is returned if the conversion fails or if the resulting
|
|
||||||
// Token is invalid.
|
|
||||||
func FromDagCbor(data []byte, resolvOpts ...did.ResolutionOption) (*Token, error) {
|
|
||||||
pay, err := envelope.FromDagCbor[*tokenPayloadModel](data, resolvOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tkn, err := tokenFromModel(*pay)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tkn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
|
|
||||||
func FromDagCborReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, error) {
|
|
||||||
return DecodeReader(r, dagcbor.Decode, resolvOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromDagJson unmarshals the input data into a Token.
|
|
||||||
//
|
|
||||||
// An error is returned if the conversion fails or if the resulting
|
|
||||||
// Token is invalid.
|
|
||||||
func FromDagJson(data []byte, resolvOpts ...did.ResolutionOption) (*Token, error) {
|
|
||||||
return Decode(data, dagjson.Decode, resolvOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
|
|
||||||
func FromDagJsonReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, error) {
|
|
||||||
return DecodeReader(r, dagjson.Decode, resolvOpts...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// FromIPLD decode the given IPLD representation into a Token.
|
|
||||||
func FromIPLD(node datamodel.Node, resolvOpts ...did.ResolutionOption) (*Token, error) {
|
|
||||||
pay, err := envelope.FromIPLD[*tokenPayloadModel](node, resolvOpts...)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
tkn, err := tokenFromModel(*pay)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return tkn, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Token) toIPLD(privKey crypto.PrivateKeySigningBytes) (datamodel.Node, error) {
|
|
||||||
var exp *int64
|
|
||||||
if t.expiration != nil {
|
|
||||||
u := t.expiration.Unix()
|
|
||||||
exp = &u
|
|
||||||
}
|
|
||||||
|
|
||||||
var iat *int64
|
|
||||||
if t.issuedAt != nil {
|
|
||||||
i := t.issuedAt.Unix()
|
|
||||||
iat = &i
|
|
||||||
}
|
|
||||||
|
|
||||||
model := &tokenPayloadModel{
|
|
||||||
Iss: t.issuer.String(),
|
|
||||||
Claims: t.claims,
|
|
||||||
Meta: t.meta,
|
|
||||||
Nonce: t.nonce,
|
|
||||||
Exp: exp,
|
|
||||||
Iat: iat,
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(model.Claims.Keys) == 0 {
|
|
||||||
model.Claims = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// seems like it's a requirement to have a null meta if there are no values?
|
|
||||||
if len(model.Meta.Keys) == 0 {
|
|
||||||
model.Meta = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return envelope.ToIPLD(privKey, model)
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
package attestation_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/token/attestation"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSealUnsealRoundtrip(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
privKey, iss, claims, meta, err := setupExampleNew()
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tkn1, err := attestation.New(iss,
|
|
||||||
attestation.WithClaimMap(claims),
|
|
||||||
attestation.WithMetaMap(meta),
|
|
||||||
attestation.WithExpirationIn(time.Minute),
|
|
||||||
attestation.WithoutIssuedAt(),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
data, cid1, err := tkn1.ToSealed(privKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
tkn2, cid2, err := attestation.FromSealed(data)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, cid1, cid2)
|
|
||||||
assert.Equal(t, tkn1, tkn2)
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
package attestation
|
|
||||||
|
|
||||||
import (
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Option is a type that allows optional fields to be set during the
|
|
||||||
// creation of a Token.
|
|
||||||
type Option func(*Token) error
|
|
||||||
|
|
||||||
// WithClaim adds a key/value pair in the "claims" field.
|
|
||||||
//
|
|
||||||
// WithClaims can be used multiple times in the same call.
|
|
||||||
// Accepted types for the value are: bool, string, int, int32, int64, []byte,
|
|
||||||
// and ipld.Node.
|
|
||||||
func WithClaim(key string, val any) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
return t.claims.Add(key, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithClaimsMap adds all key/value pairs in the provided map to the
|
|
||||||
// Token's "claims" field.
|
|
||||||
//
|
|
||||||
// WithClaimsMap can be used multiple times in the same call.
|
|
||||||
// Accepted types for the value are: bool, string, int, int32, int64, []byte,
|
|
||||||
// and ipld.Node.
|
|
||||||
func WithClaimMap(m map[string]any) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
for k, v := range m {
|
|
||||||
if err := t.claims.Add(k, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEncryptedClaimsString adds a key/value pair in the "claims" field.
|
|
||||||
// The string value is encrypted with the given aesKey.
|
|
||||||
func WithEncryptedClaimsString(key, val string, encryptionKey []byte) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
return t.claims.AddEncrypted(key, val, encryptionKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEncryptedClaimsBytes adds a key/value pair in the "claims" field.
|
|
||||||
// The []byte value is encrypted with the given aesKey.
|
|
||||||
func WithEncryptedClaimsBytes(key string, val, encryptionKey []byte) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
return t.claims.AddEncrypted(key, val, encryptionKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMeta adds a key/value pair in the "meta" field.
|
|
||||||
//
|
|
||||||
// WithMeta can be used multiple times in the same call.
|
|
||||||
// Accepted types for the value are: bool, string, int, int32, int64, []byte,
|
|
||||||
// and ipld.Node.
|
|
||||||
func WithMeta(key string, val any) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
return t.meta.Add(key, val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithMetaMap adds all key/value pairs in the provided map to the
|
|
||||||
// Token's "meta" field.
|
|
||||||
//
|
|
||||||
// WithMetaMap can be used multiple times in the same call.
|
|
||||||
// Accepted types for the value are: bool, string, int, int32, int64, []byte,
|
|
||||||
// and ipld.Node.
|
|
||||||
func WithMetaMap(m map[string]any) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
for k, v := range m {
|
|
||||||
if err := t.meta.Add(k, v); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEncryptedMetaString adds a key/value pair in the "meta" field.
|
|
||||||
// The string value is encrypted with the given aesKey.
|
|
||||||
func WithEncryptedMetaString(key, val string, encryptionKey []byte) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
return t.meta.AddEncrypted(key, val, encryptionKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEncryptedMetaBytes adds a key/value pair in the "meta" field.
|
|
||||||
// The []byte value is encrypted with the given aesKey.
|
|
||||||
func WithEncryptedMetaBytes(key string, val, encryptionKey []byte) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
return t.meta.AddEncrypted(key, val, encryptionKey)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithNonce sets the Token's nonce with the given value.
|
|
||||||
//
|
|
||||||
// If this option is not used, a random 12-byte nonce is generated for
|
|
||||||
// this required field. If you truly want to create an invocation Token
|
|
||||||
// without a nonce, use the WithEmptyNonce Option which will set the
|
|
||||||
// nonce to an empty byte array.
|
|
||||||
func WithNonce(nonce []byte) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
t.nonce = nonce
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithEmptyNonce sets the Token's nonce to an empty byte slice as
|
|
||||||
// suggested by the UCAN spec for invocation tokens that represent
|
|
||||||
// idempotent operations.
|
|
||||||
func WithEmptyNonce() Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
t.nonce = []byte{}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithExpiration set's the Token's optional "expiration" field to the
|
|
||||||
// value of the provided time.Time.
|
|
||||||
func WithExpiration(exp time.Time) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
exp = exp.Round(time.Second)
|
|
||||||
t.expiration = &exp
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithExpirationIn set's the Token's optional "expiration" field to
|
|
||||||
// Now() plus the given duration.
|
|
||||||
func WithExpirationIn(after time.Duration) Option {
|
|
||||||
return WithExpiration(time.Now().Add(after))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithIssuedAt sets the Token's IssuedAt field to the provided
|
|
||||||
// time.Time.
|
|
||||||
//
|
|
||||||
// If this Option is not provided, the invocation Token's iat field will
|
|
||||||
// be set to the value of time.Now(). If you want to create an invocation
|
|
||||||
// Token without this field being set, use the WithoutIssuedAt Option.
|
|
||||||
func WithIssuedAt(iat time.Time) Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
t.issuedAt = &iat
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithIssuedAtIn sets the Token's IssuedAt field to Now() plus the
|
|
||||||
// given duration.
|
|
||||||
func WithIssuedAtIn(after time.Duration) Option {
|
|
||||||
return WithIssuedAt(time.Now().Add(after))
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithoutIssuedAt clears the Token's IssuedAt field.
|
|
||||||
func WithoutIssuedAt() Option {
|
|
||||||
return func(t *Token) error {
|
|
||||||
t.issuedAt = nil
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
package attestation
|
|
||||||
|
|
||||||
import (
|
|
||||||
_ "embed"
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/ipld/go-ipld-prime"
|
|
||||||
"github.com/ipld/go-ipld-prime/node/bindnode"
|
|
||||||
"github.com/ipld/go-ipld-prime/schema"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/claims"
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/meta"
|
|
||||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
|
||||||
)
|
|
||||||
|
|
||||||
// [Tag] is the string used as a key within the SigPayload that identifies
|
|
||||||
// that the TokenPayload is an attestation.
|
|
||||||
//
|
|
||||||
// [Tag]: TODO: TBD
|
|
||||||
const Tag = "ucan/att@tbd" // TODO: TBD
|
|
||||||
|
|
||||||
//go:embed attestation.ipldsch
|
|
||||||
var schemaBytes []byte
|
|
||||||
|
|
||||||
var (
|
|
||||||
once sync.Once
|
|
||||||
ts *schema.TypeSystem
|
|
||||||
errSchema error
|
|
||||||
)
|
|
||||||
|
|
||||||
func mustLoadSchema() *schema.TypeSystem {
|
|
||||||
once.Do(func() {
|
|
||||||
ts, errSchema = ipld.LoadSchemaBytes(schemaBytes)
|
|
||||||
})
|
|
||||||
if errSchema != nil {
|
|
||||||
panic(fmt.Errorf("failed to load IPLD schema: %s", errSchema))
|
|
||||||
}
|
|
||||||
return ts
|
|
||||||
}
|
|
||||||
|
|
||||||
func payloadType() schema.Type {
|
|
||||||
return mustLoadSchema().TypeByName("Payload")
|
|
||||||
}
|
|
||||||
|
|
||||||
var _ envelope.Tokener = (*tokenPayloadModel)(nil)
|
|
||||||
|
|
||||||
type tokenPayloadModel struct {
|
|
||||||
// The DID of the Invoker
|
|
||||||
Iss string
|
|
||||||
|
|
||||||
// Arbitrary claims
|
|
||||||
Claims *claims.Claims
|
|
||||||
|
|
||||||
// Arbitrary Metadata
|
|
||||||
Meta *meta.Meta
|
|
||||||
|
|
||||||
// A unique, random nonce
|
|
||||||
Nonce []byte
|
|
||||||
// The timestamp at which the Invocation becomes invalid
|
|
||||||
// optional: can be nil
|
|
||||||
Exp *int64
|
|
||||||
// The timestamp at which the Invocation was created
|
|
||||||
Iat *int64
|
|
||||||
}
|
|
||||||
|
|
||||||
func (e *tokenPayloadModel) Prototype() schema.TypedPrototype {
|
|
||||||
return bindnode.Prototype((*tokenPayloadModel)(nil), payloadType())
|
|
||||||
}
|
|
||||||
|
|
||||||
func (*tokenPayloadModel) Tag() string {
|
|
||||||
return Tag
|
|
||||||
}
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
package attestation_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
_ "embed"
|
|
||||||
"encoding/base64"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/MetaMask/go-did-it/crypto"
|
|
||||||
"github.com/MetaMask/go-did-it/crypto/ed25519"
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/token/attestation"
|
|
||||||
"github.com/ucan-wg/go-ucan/token/internal/envelope"
|
|
||||||
)
|
|
||||||
|
|
||||||
//go:embed testdata/new.dagjson
|
|
||||||
var newDagJson []byte
|
|
||||||
|
|
||||||
const (
|
|
||||||
issuerPrivKeyCfg = "8bX3+HJxxlIGgNZ8yFG+t48oMGygEGyWD5Cy8ugeCIRksEIVyCabkuLVXbMZYj1lpXgL22Fok8nv52clGfEMXA=="
|
|
||||||
newCID = "zdpuAyWCG3GWfebFME3e4oG926tzpJodw4WTa9VjBwqPNiVWF"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestSchemaRoundTrip(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
privKey := privKey(t, issuerPrivKeyCfg)
|
|
||||||
|
|
||||||
t.Run("via buffers", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
|
|
||||||
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
|
|
||||||
|
|
||||||
p1, err := attestation.FromDagJson(newDagJson)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cborBytes, id, err := p1.ToSealed(privKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, newCID, envelope.CIDToBase58BTC(id))
|
|
||||||
|
|
||||||
p2, c2, err := attestation.FromSealed(cborBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, id, c2)
|
|
||||||
|
|
||||||
readJson, err := p2.ToDagJson(privKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.JSONEq(t, string(newDagJson), string(readJson))
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("via streaming", func(t *testing.T) {
|
|
||||||
t.Parallel()
|
|
||||||
|
|
||||||
buf := bytes.NewBuffer(newDagJson)
|
|
||||||
|
|
||||||
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
|
|
||||||
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
|
|
||||||
|
|
||||||
p1, err := attestation.FromDagJsonReader(buf)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
cborBytes := &bytes.Buffer{}
|
|
||||||
id, err := p1.ToSealedWriter(cborBytes, privKey)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, newCID, envelope.CIDToBase58BTC(id))
|
|
||||||
|
|
||||||
p2, c2, err := attestation.FromSealedReader(cborBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
assert.Equal(t, envelope.CIDToBase58BTC(id), envelope.CIDToBase58BTC(c2))
|
|
||||||
|
|
||||||
readJson := &bytes.Buffer{}
|
|
||||||
require.NoError(t, p2.ToDagJsonWriter(readJson, privKey))
|
|
||||||
|
|
||||||
assert.JSONEq(t, string(newDagJson), readJson.String())
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func privKey(t require.TestingT, privKeyCfg string) crypto.PrivateKeySigningBytes {
|
|
||||||
privBytes, err := base64.StdEncoding.DecodeString(privKeyCfg)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
privKey, err := ed25519.PrivateKeyFromBytes(privBytes)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
return privKey
|
|
||||||
}
|
|
||||||
1
token/attestation/testdata/new.dagjson
vendored
1
token/attestation/testdata/new.dagjson
vendored
@@ -1 +0,0 @@
|
|||||||
[{"/":{"bytes":"9lfCwLn+HqFGPNMbD9mIuMjhZarhZk1mOSq2eGLIBfRM6B5dtIftDh25TOG3qJrWRvZtvupd0az/PiVv/8zMCg"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/att@tbd":{"claims":{"claim1":"UCAN is great"},"exp":1767790946,"iss":"did:key:z6MkmEJhVC9xHMREKTw1HpPrwVh6fcUbJ8hoVEa3UQdP9sNs","meta":{"env":"development"},"nonce":{"/":{"bytes":"jPnfQhL20Eoq/8fu"}}}}]
|
|
||||||
@@ -10,14 +10,11 @@ package delegation
|
|||||||
// TODO: change the "delegation" link above when the specification is merged
|
// TODO: change the "delegation" link above when the specification is merged
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/MetaMask/go-did-it"
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/meta"
|
"github.com/ucan-wg/go-ucan/pkg/meta"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||||
@@ -47,15 +44,16 @@ type Token struct {
|
|||||||
expiration *time.Time
|
expiration *time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// New creates a validated delegation Token from the provided parameters and options.
|
// New creates a validated Token from the provided parameters and options.
|
||||||
// This is typically used to delegate a given power to another agent.
|
|
||||||
//
|
//
|
||||||
// You can read it as "(issuer) allows (audience) to perform (cmd+pol) on (subject)".
|
// When creating a delegated token, the Issuer's (iss) DID is assembled
|
||||||
func New(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, sub did.DID, opts ...Option) (*Token, error) {
|
// using the public key associated with the private key sent as the first
|
||||||
|
// parameter.
|
||||||
|
func New(iss, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) {
|
||||||
tkn := &Token{
|
tkn := &Token{
|
||||||
issuer: iss,
|
issuer: iss,
|
||||||
audience: aud,
|
audience: aud,
|
||||||
subject: sub,
|
subject: did.Undef,
|
||||||
command: cmd,
|
command: cmd,
|
||||||
policy: pol,
|
policy: pol,
|
||||||
meta: meta.NewMeta(),
|
meta: meta.NewMeta(),
|
||||||
@@ -83,27 +81,16 @@ func New(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, sub d
|
|||||||
return tkn, nil
|
return tkn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Root creates a validated UCAN delegation Token from the provided parameters and options.
|
// Root creates a validated UCAN delegation Token from the provided
|
||||||
// This is typically used to create and give power to an agent.
|
// parameters and options.
|
||||||
//
|
//
|
||||||
// You can read it as "(issuer) allows (audience) to perform (cmd+pol) on itself".
|
// When creating a root token, both the Issuer's (iss) and Subject's
|
||||||
func Root(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) {
|
// (sub) DIDs are assembled from the public key associated with the
|
||||||
return New(iss, aud, cmd, pol, iss, opts...)
|
// private key passed as the first argument.
|
||||||
}
|
func Root(iss, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) {
|
||||||
|
opts = append(opts, WithSubject(iss))
|
||||||
|
|
||||||
// Powerline creates a validated UCAN delegation Token from the provided parameters and options.
|
return New(iss, aud, cmd, pol, opts...)
|
||||||
//
|
|
||||||
// Powerline is a pattern for automatically delegating all future delegations to another agent regardless of Subject.
|
|
||||||
// This is a very powerful pattern, use it only if you understand it.
|
|
||||||
// Powerline delegations MUST NOT be used as the root delegation to a resource
|
|
||||||
//
|
|
||||||
// A very common use case for Powerline is providing a stable DID across multiple agents (e.g. representing a user with
|
|
||||||
// multiple devices). This enables the automatic sharing of authority across their devices without needing to share keys
|
|
||||||
// or set up a threshold scheme. It is also flexible, since a Powerline delegation MAY be revoked.
|
|
||||||
//
|
|
||||||
// You can read it as "(issuer) allows (audience) to perform (cmd+pol) on anything".
|
|
||||||
func Powerline(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) {
|
|
||||||
return New(iss, aud, cmd, pol, nil, opts...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Issuer returns the did.DID representing the Token's issuer.
|
// Issuer returns the did.DID representing the Token's issuer.
|
||||||
@@ -155,16 +142,6 @@ func (t *Token) Expiration() *time.Time {
|
|||||||
return t.expiration
|
return t.expiration
|
||||||
}
|
}
|
||||||
|
|
||||||
// IsRoot tells if the token is a root delegation.
|
|
||||||
func (t *Token) IsRoot() bool {
|
|
||||||
return t.issuer.Equal(t.subject)
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsPowerline tells if the token is a powerline delegation.
|
|
||||||
func (t *Token) IsPowerline() bool {
|
|
||||||
return t.subject == nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// IsValidNow verifies that the token can be used at the current time, based on expiration or "not before" fields.
|
// IsValidNow verifies that the token can be used at the current time, based on expiration or "not before" fields.
|
||||||
// This does NOT do any other kind of verifications.
|
// This does NOT do any other kind of verifications.
|
||||||
func (t *Token) IsValidNow() bool {
|
func (t *Token) IsValidNow() bool {
|
||||||
@@ -183,37 +160,11 @@ func (t *Token) IsValidAt(ti time.Time) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Token) String() string {
|
|
||||||
var res strings.Builder
|
|
||||||
|
|
||||||
var kind string
|
|
||||||
switch {
|
|
||||||
case t.issuer == t.subject:
|
|
||||||
kind = " (root delegation)"
|
|
||||||
case t.subject == nil:
|
|
||||||
kind = " (powerline delegation)"
|
|
||||||
default:
|
|
||||||
kind = " (normal delegation)"
|
|
||||||
}
|
|
||||||
|
|
||||||
res.WriteString(fmt.Sprintf("Issuer: %s\n", t.Issuer()))
|
|
||||||
res.WriteString(fmt.Sprintf("Audience: %s\n", t.Audience()))
|
|
||||||
res.WriteString(fmt.Sprintf("Subject: %s%s\n", t.Subject(), kind))
|
|
||||||
res.WriteString(fmt.Sprintf("Command: %s\n", t.Command()))
|
|
||||||
res.WriteString(fmt.Sprintf("Policy: %s\n", t.Policy()))
|
|
||||||
res.WriteString(fmt.Sprintf("Nonce: %s\n", base64.StdEncoding.EncodeToString(t.Nonce())))
|
|
||||||
res.WriteString(fmt.Sprintf("Meta: %s\n", t.Meta()))
|
|
||||||
res.WriteString(fmt.Sprintf("NotBefore: %v\n", t.NotBefore()))
|
|
||||||
res.WriteString(fmt.Sprintf("Expiration: %v", t.Expiration()))
|
|
||||||
|
|
||||||
return res.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (t *Token) validate() error {
|
func (t *Token) validate() error {
|
||||||
var errs error
|
var errs error
|
||||||
|
|
||||||
requiredDID := func(id did.DID, fieldname string) {
|
requiredDID := func(id did.DID, fieldname string) {
|
||||||
if id == nil {
|
if !id.Defined() {
|
||||||
errs = errors.Join(errs, fmt.Errorf(`a valid did is required for %s: %s`, fieldname, id.String()))
|
errs = errors.Join(errs, fmt.Errorf(`a valid did is required for %s: %s`, fieldname, id.String()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -263,19 +214,9 @@ func tokenFromModel(m tokenPayloadModel) (*Token, error) {
|
|||||||
tkn.nonce = m.Nonce
|
tkn.nonce = m.Nonce
|
||||||
|
|
||||||
tkn.meta = m.Meta
|
tkn.meta = m.Meta
|
||||||
if tkn.meta == nil {
|
|
||||||
tkn.meta = meta.NewMeta()
|
|
||||||
}
|
|
||||||
|
|
||||||
tkn.notBefore, err = parse.OptionalTimestamp(m.Nbf)
|
tkn.notBefore = parse.OptionalTimestamp(m.Nbf)
|
||||||
if err != nil {
|
tkn.expiration = parse.OptionalTimestamp(m.Exp)
|
||||||
return nil, fmt.Errorf("parse notBefore: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
tkn.expiration, err = parse.OptionalTimestamp(m.Exp)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("parse expiration: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := tkn.validate(); err != nil {
|
if err := tkn.validate(); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -1,47 +1,64 @@
|
|||||||
package delegation_test
|
package delegation_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
_ "embed"
|
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/MetaMask/go-did-it/didtest"
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
"gotest.tools/v3/golden"
|
||||||
|
|
||||||
|
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||||
)
|
)
|
||||||
|
|
||||||
//go:embed testdata/new.dagjson
|
|
||||||
var newDagJson []byte
|
|
||||||
|
|
||||||
//go:embed testdata/powerline.dagjson
|
|
||||||
var powerlineDagJson []byte
|
|
||||||
|
|
||||||
//go:embed testdata/root.dagjson
|
|
||||||
var rootDagJson []byte
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
nonce = "6roDhGi0kiNriQAz7J3d+bOeoI/tj8ENikmQNbtjnD0"
|
nonce = "6roDhGi0kiNriQAz7J3d+bOeoI/tj8ENikmQNbtjnD0"
|
||||||
|
|
||||||
subJectCmd = "/foo/bar"
|
subJectCmd = "/foo/bar"
|
||||||
subjectPol = `
|
subjectPol = `
|
||||||
[
|
[
|
||||||
["==", ".status", "draft"],
|
[
|
||||||
["all", ".reviewer",
|
"==",
|
||||||
["like", ".email", "*@example.com"]
|
".status",
|
||||||
],
|
"draft"
|
||||||
["any", ".tags",
|
],
|
||||||
["or", [
|
[
|
||||||
["==", ".", "news"],
|
"all",
|
||||||
["==", ".", "press"]
|
".reviewer",
|
||||||
]]
|
[
|
||||||
]
|
"like",
|
||||||
|
".email",
|
||||||
|
"*@example.com"
|
||||||
|
]
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"any",
|
||||||
|
".tags",
|
||||||
|
[
|
||||||
|
"or",
|
||||||
|
[
|
||||||
|
[
|
||||||
|
"==",
|
||||||
|
".",
|
||||||
|
"news"
|
||||||
|
],
|
||||||
|
[
|
||||||
|
"==",
|
||||||
|
".",
|
||||||
|
"press"
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
`
|
`
|
||||||
|
|
||||||
|
newCID = "zdpuAwa4qv3ncMDPeDoqVxjZy3JoyWsbqUzm94rdA1AvRFkkw"
|
||||||
|
rootCID = "zdpuAkgGmUp5JrXvehGuuw9JA8DLQKDaxtK3R8brDQQVC2i5X"
|
||||||
|
|
||||||
aesKey = "xQklMmNTnVrmaPBq/0pwV5fEwuv/iClF5HWak9MsgI8="
|
aesKey = "xQklMmNTnVrmaPBq/0pwV5fEwuv/iClF5HWak9MsgI8="
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -58,24 +75,24 @@ func TestConstructors(t *testing.T) {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
t.Run("New", func(t *testing.T) {
|
t.Run("New", func(t *testing.T) {
|
||||||
tkn, err := delegation.New(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol, didtest.PersonaCarol.DID(),
|
tkn, err := delegation.New(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
|
||||||
delegation.WithNonce([]byte(nonce)),
|
delegation.WithNonce([]byte(nonce)),
|
||||||
|
delegation.WithSubject(didtest.PersonaAlice.DID()),
|
||||||
delegation.WithExpiration(exp),
|
delegation.WithExpiration(exp),
|
||||||
delegation.WithMeta("foo", "fooo"),
|
delegation.WithMeta("foo", "fooo"),
|
||||||
delegation.WithMeta("bar", "barr"),
|
delegation.WithMeta("bar", "barr"),
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.False(t, tkn.IsRoot())
|
|
||||||
require.False(t, tkn.IsPowerline())
|
|
||||||
|
|
||||||
data, err := tkn.ToDagJson(didtest.PersonaAlice.PrivKey())
|
data, err := tkn.ToDagJson(didtest.PersonaAlice.PrivKey())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, newDagJson, data)
|
golden.Assert(t, string(data), "new.dagjson")
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Root", func(t *testing.T) {
|
t.Run("Root", func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
tkn, err := delegation.Root(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
|
tkn, err := delegation.Root(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
|
||||||
delegation.WithNonce([]byte(nonce)),
|
delegation.WithNonce([]byte(nonce)),
|
||||||
delegation.WithExpiration(exp),
|
delegation.WithExpiration(exp),
|
||||||
@@ -84,31 +101,10 @@ func TestConstructors(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.True(t, tkn.IsRoot())
|
|
||||||
require.False(t, tkn.IsPowerline())
|
|
||||||
|
|
||||||
data, err := tkn.ToDagJson(didtest.PersonaAlice.PrivKey())
|
data, err := tkn.ToDagJson(didtest.PersonaAlice.PrivKey())
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, rootDagJson, data)
|
golden.Assert(t, string(data), "root.dagjson")
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Powerline", func(t *testing.T) {
|
|
||||||
tkn, err := delegation.Powerline(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
|
|
||||||
delegation.WithNonce([]byte(nonce)),
|
|
||||||
delegation.WithExpiration(exp),
|
|
||||||
delegation.WithMeta("foo", "fooo"),
|
|
||||||
delegation.WithMeta("bar", "barr"),
|
|
||||||
)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.False(t, tkn.IsRoot())
|
|
||||||
require.True(t, tkn.IsPowerline())
|
|
||||||
|
|
||||||
data, err := tkn.ToDagJson(didtest.PersonaAlice.PrivKey())
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
require.Equal(t, powerlineDagJson, data)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +153,7 @@ func TestEncryptedMeta(t *testing.T) {
|
|||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
tkn, err := delegation.Root(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
|
tkn, err := delegation.New(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol,
|
||||||
delegation.WithEncryptedMetaString(tt.key, tt.value, encryptionKey),
|
delegation.WithEncryptedMetaString(tt.key, tt.value, encryptionKey),
|
||||||
)
|
)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -195,7 +191,7 @@ func TestEncryptedMeta(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create token with multiple encrypted values
|
// Create token with multiple encrypted values
|
||||||
tkn, err := delegation.Root(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol, opts...)
|
tkn, err := delegation.New(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol, opts...)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
data, err := tkn.ToDagCbor(didtest.PersonaAlice.PrivKey())
|
data, err := tkn.ToDagCbor(didtest.PersonaAlice.PrivKey())
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,23 +1,19 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
"go/format"
|
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"slices"
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/MetaMask/go-did-it"
|
"github.com/dave/jennifer/jen"
|
||||||
didkeyctl "github.com/MetaMask/go-did-it/controller/did-key"
|
|
||||||
"github.com/MetaMask/go-did-it/crypto"
|
|
||||||
"github.com/MetaMask/go-did-it/didtest"
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
|
"github.com/libp2p/go-libp2p/core/crypto"
|
||||||
|
|
||||||
|
"github.com/ucan-wg/go-ucan/did"
|
||||||
|
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy"
|
"github.com/ucan-wg/go-ucan/pkg/policy"
|
||||||
"github.com/ucan-wg/go-ucan/pkg/policy/policytest"
|
|
||||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
"github.com/ucan-wg/go-ucan/token/delegation"
|
||||||
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
|
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
|
||||||
)
|
)
|
||||||
@@ -31,11 +27,11 @@ const (
|
|||||||
var constantNonce = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}
|
var constantNonce = []byte{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b}
|
||||||
|
|
||||||
type newDelegationParams struct {
|
type newDelegationParams struct {
|
||||||
privKey crypto.PrivateKeySigningBytes // iss
|
privKey crypto.PrivKey
|
||||||
aud did.DID
|
aud did.DID
|
||||||
|
sub did.DID
|
||||||
cmd command.Command
|
cmd command.Command
|
||||||
pol policy.Policy
|
pol policy.Policy
|
||||||
sub did.DID
|
|
||||||
opts []delegation.Option
|
opts []delegation.Option
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,9 +86,9 @@ func (g *generator) chainPersonas(personas []didtest.Persona, acc acc, vari vari
|
|||||||
privKey: personas[0].PrivKey(),
|
privKey: personas[0].PrivKey(),
|
||||||
aud: personas[1].DID(),
|
aud: personas[1].DID(),
|
||||||
cmd: delegationtest.NominalCommand,
|
cmd: delegationtest.NominalCommand,
|
||||||
pol: policytest.EmptyPolicy,
|
pol: policy.Policy{},
|
||||||
sub: didtest.PersonaAlice.DID(),
|
|
||||||
opts: []delegation.Option{
|
opts: []delegation.Option{
|
||||||
|
delegation.WithSubject(didtest.PersonaAlice.DID()),
|
||||||
delegation.WithNonce(constantNonce),
|
delegation.WithNonce(constantNonce),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -119,7 +115,7 @@ func (g *generator) chainPersonas(personas []didtest.Persona, acc acc, vari vari
|
|||||||
p.cmd = delegationtest.AttenuatedCommand
|
p.cmd = delegationtest.AttenuatedCommand
|
||||||
}},
|
}},
|
||||||
{name: "InvalidSubject", variant: func(p *newDelegationParams) {
|
{name: "InvalidSubject", variant: func(p *newDelegationParams) {
|
||||||
p.sub = didtest.PersonaBob.DID()
|
p.opts = append(p.opts, delegation.WithSubject(didtest.PersonaBob.DID()))
|
||||||
}},
|
}},
|
||||||
{name: "InvalidExpired", variant: func(p *newDelegationParams) {
|
{name: "InvalidExpired", variant: func(p *newDelegationParams) {
|
||||||
// Note: this makes the generator not deterministic
|
// Note: this makes the generator not deterministic
|
||||||
@@ -132,9 +128,6 @@ func (g *generator) chainPersonas(personas []didtest.Persona, acc acc, vari vari
|
|||||||
}
|
}
|
||||||
p.opts = append(p.opts, delegation.WithNotBefore(nbf))
|
p.opts = append(p.opts, delegation.WithNotBefore(nbf))
|
||||||
}},
|
}},
|
||||||
{name: "ValidExamplePolicy", variant: func(p *newDelegationParams) {
|
|
||||||
p.pol = policytest.SpecPolicy
|
|
||||||
}},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start a branch in the recursion for each of the variants
|
// Start a branch in the recursion for each of the variants
|
||||||
@@ -158,9 +151,12 @@ func (g *generator) chainPersonas(personas []didtest.Persona, acc acc, vari vari
|
|||||||
func (g *generator) createDelegation(params newDelegationParams, name string, vari variant) (cid.Cid, error) {
|
func (g *generator) createDelegation(params newDelegationParams, name string, vari variant) (cid.Cid, error) {
|
||||||
vari.variant(¶ms)
|
vari.variant(¶ms)
|
||||||
|
|
||||||
issDID := didkeyctl.FromPrivateKey(params.privKey)
|
issDID, err := did.FromPrivKey(params.privKey)
|
||||||
|
if err != nil {
|
||||||
|
return cid.Undef, err
|
||||||
|
}
|
||||||
|
|
||||||
tkn, err := delegation.New(issDID, params.aud, params.cmd, params.pol, params.sub, params.opts...)
|
tkn, err := delegation.New(issDID, params.aud, params.cmd, params.pol, params.opts...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return cid.Undef, err
|
return cid.Undef, err
|
||||||
}
|
}
|
||||||
@@ -203,71 +199,36 @@ func (g *generator) createProofChain(name string, prf []cid.Cid) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *generator) writeGoFile() error {
|
func (g *generator) writeGoFile() error {
|
||||||
buf := bytes.NewBuffer(nil)
|
file := jen.NewFile("delegationtest")
|
||||||
|
file.HeaderComment("Code generated by delegationtest - DO NOT EDIT.")
|
||||||
|
|
||||||
Println := func(a ...any) { _, _ = fmt.Fprintln(buf, a...) }
|
refs := map[cid.Cid]string{}
|
||||||
Printf := func(format string, a ...any) { _, _ = fmt.Fprintf(buf, format, a...) }
|
|
||||||
|
|
||||||
Println("// Code generated by delegationtest - DO NOT EDIT.")
|
|
||||||
Println()
|
|
||||||
Println("package delegationtest")
|
|
||||||
Println()
|
|
||||||
Println("import (")
|
|
||||||
Println("\t\"github.com/ipfs/go-cid\"")
|
|
||||||
Println()
|
|
||||||
Println("\t\"github.com/ucan-wg/go-ucan/token/delegation\"")
|
|
||||||
Println(")")
|
|
||||||
|
|
||||||
refs := make(map[cid.Cid]string, len(g.dlgs))
|
|
||||||
|
|
||||||
for _, d := range g.dlgs {
|
for _, d := range g.dlgs {
|
||||||
refs[d.id] = d.name + "CID"
|
refs[d.id] = d.name + "CID"
|
||||||
|
|
||||||
Println()
|
file.Var().Defs(
|
||||||
Println("var (")
|
jen.Id(d.name+"CID").Op("=").Qual("github.com/ipfs/go-cid", "MustParse").Call(jen.Lit(d.id.String())),
|
||||||
Printf("\t%sCID = cid.MustParse(\"%s\")\n", d.name, d.id.String())
|
jen.Id(d.name).Op("=").Id("mustGetDelegation").Call(jen.Id(d.name+"CID")),
|
||||||
Printf("\t%sSealed = mustGetBundle(%s).Sealed\n", d.name, d.name+"CID")
|
)
|
||||||
Printf("\t%sBundle = mustGetBundle(%s)\n", d.name, d.name+"CID")
|
file.Line()
|
||||||
Printf("\t%s = mustGetBundle(%s).Decoded\n", d.name, d.name+"CID")
|
|
||||||
Println(")")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Println()
|
|
||||||
Println("var AllTokens = []*delegation.Token{")
|
|
||||||
for _, d := range g.dlgs {
|
|
||||||
Printf("\t%s,\n", d.name)
|
|
||||||
}
|
|
||||||
Println("}")
|
|
||||||
|
|
||||||
Println()
|
|
||||||
Println("var AllBundles = []delegation.Bundle{")
|
|
||||||
for _, d := range g.dlgs {
|
|
||||||
Printf("\t%sBundle,\n", d.name)
|
|
||||||
}
|
|
||||||
Println("}")
|
|
||||||
|
|
||||||
Println()
|
|
||||||
Println("var cidToName = map[cid.Cid]string{")
|
|
||||||
for _, d := range g.dlgs {
|
|
||||||
Printf("\t%sCID: \"%s\",\n", d.name, d.name)
|
|
||||||
}
|
|
||||||
Println("}")
|
|
||||||
|
|
||||||
for _, c := range g.chains {
|
for _, c := range g.chains {
|
||||||
Println()
|
g := jen.CustomFunc(jen.Options{
|
||||||
Printf("var %s = []cid.Cid{\n", c.name)
|
Multi: true,
|
||||||
|
Separator: ",",
|
||||||
|
Close: "\n",
|
||||||
|
}, func(g *jen.Group) {
|
||||||
|
slices.Reverse(c.prf)
|
||||||
|
for _, p := range c.prf {
|
||||||
|
g.Id(refs[p])
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
for _, d := range slices.Backward(c.prf) {
|
file.Var().Id(c.name).Op("=").Index().Qual("github.com/ipfs/go-cid", "Cid").Values(g)
|
||||||
Printf("\t%s,\n", refs[d])
|
file.Line()
|
||||||
}
|
|
||||||
|
|
||||||
Println("}")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out, err := format.Source(buf.Bytes())
|
return file.Save("../token_gen.go")
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return os.WriteFile("../token_gen.go", out, 0666)
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"github.com/MetaMask/go-did-it/didtest"
|
"github.com/ucan-wg/go-ucan/did/didtest"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"path/filepath"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
_ "github.com/MetaMask/go-did-it/verifiers/did-key"
|
|
||||||
"github.com/ipfs/go-cid"
|
"github.com/ipfs/go-cid"
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/pkg/command"
|
"github.com/ucan-wg/go-ucan/pkg/command"
|
||||||
@@ -19,12 +18,12 @@ var (
|
|||||||
// Execution of this command is generally prohibited in tests.
|
// Execution of this command is generally prohibited in tests.
|
||||||
ExpandedCommand = command.MustParse("/expanded")
|
ExpandedCommand = command.MustParse("/expanded")
|
||||||
|
|
||||||
// NominalCommand is the command used for most test tokens and proof-chains.
|
// NominalCommand is the command used for most test tokens and proof-
|
||||||
// Execution of this command is generally allowed in tests.
|
// chains. Execution of this command is generally allowed in tests.
|
||||||
NominalCommand = ExpandedCommand.Join("nominal")
|
NominalCommand = ExpandedCommand.Join("nominal")
|
||||||
|
|
||||||
// AttenuatedCommand is a sub-command of the NominalCommand.
|
// AttenuatedCommand is a sub-command of the NominalCommand. Execution
|
||||||
// Execution of this command is generally allowed in tests.
|
// of this command is generally allowed in tests.
|
||||||
AttenuatedCommand = NominalCommand.Join("attenuated")
|
AttenuatedCommand = NominalCommand.Join("attenuated")
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -36,21 +35,21 @@ const TokenDir = "data"
|
|||||||
//go:embed data
|
//go:embed data
|
||||||
var fs embed.FS
|
var fs embed.FS
|
||||||
|
|
||||||
var _ delegation.Loader = (*DelegationLoader)(nil)
|
var _ delegation.Loader = (*delegationLoader)(nil)
|
||||||
|
|
||||||
type DelegationLoader struct {
|
type delegationLoader struct {
|
||||||
bundles map[cid.Cid]delegation.Bundle
|
tokens map[cid.Cid]*delegation.Token
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
once sync.Once
|
once sync.Once
|
||||||
ldr *DelegationLoader
|
ldr delegation.Loader
|
||||||
)
|
)
|
||||||
|
|
||||||
// GetDelegationLoader returns a singleton instance of a test
|
// GetDelegationLoader returns a singleton instance of a test
|
||||||
// DelegationLoader containing all the tokens present in the data/
|
// DelegationLoader containing all the tokens present in the data/
|
||||||
// directory.
|
// directory.
|
||||||
func GetDelegationLoader() *DelegationLoader {
|
func GetDelegationLoader() delegation.Loader {
|
||||||
once.Do(func() {
|
once.Do(func() {
|
||||||
var err error
|
var err error
|
||||||
ldr, err = loadDelegations()
|
ldr, err = loadDelegations()
|
||||||
@@ -62,21 +61,22 @@ func GetDelegationLoader() *DelegationLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetDelegation implements invocation.DelegationLoader.
|
// GetDelegation implements invocation.DelegationLoader.
|
||||||
func (l *DelegationLoader) GetDelegation(id cid.Cid) (*delegation.Token, error) {
|
func (l *delegationLoader) GetDelegation(id cid.Cid) (*delegation.Token, error) {
|
||||||
bundle, ok := l.bundles[id]
|
tkn, ok := l.tokens[id]
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, delegation.ErrDelegationNotFound
|
return nil, delegation.ErrDelegationNotFound
|
||||||
}
|
}
|
||||||
return bundle.Decoded, nil
|
|
||||||
|
return tkn, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadDelegations() (*DelegationLoader, error) {
|
func loadDelegations() (delegation.Loader, error) {
|
||||||
dirEntries, err := fs.ReadDir(TokenDir)
|
dirEntries, err := fs.ReadDir(TokenDir)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bundles := make(map[cid.Cid]delegation.Bundle, len(dirEntries))
|
tkns := make(map[cid.Cid]*delegation.Token, len(dirEntries))
|
||||||
|
|
||||||
for _, dirEntry := range dirEntries {
|
for _, dirEntry := range dirEntries {
|
||||||
data, err := fs.ReadFile(filepath.Join(TokenDir, dirEntry.Name()))
|
data, err := fs.ReadFile(filepath.Join(TokenDir, dirEntry.Name()))
|
||||||
@@ -89,11 +89,11 @@ func loadDelegations() (*DelegationLoader, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bundles[id] = delegation.Bundle{Cid: id, Decoded: tkn, Sealed: data}
|
tkns[id] = tkn
|
||||||
}
|
}
|
||||||
|
|
||||||
return &DelegationLoader{
|
return &delegationLoader{
|
||||||
bundles: bundles,
|
tokens: tkns,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -103,14 +103,10 @@ func GetDelegation(id cid.Cid) (*delegation.Token, error) {
|
|||||||
return GetDelegationLoader().GetDelegation(id)
|
return GetDelegationLoader().GetDelegation(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CidToName(id cid.Cid) string {
|
func mustGetDelegation(id cid.Cid) *delegation.Token {
|
||||||
return cidToName[id]
|
tkn, err := GetDelegation(id)
|
||||||
}
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
func mustGetBundle(id cid.Cid) delegation.Bundle {
|
|
||||||
bundle, ok := GetDelegationLoader().bundles[id]
|
|
||||||
if !ok {
|
|
||||||
panic(delegation.ErrDelegationNotFound)
|
|
||||||
}
|
}
|
||||||
return bundle
|
return tkn
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,274 +2,131 @@
|
|||||||
|
|
||||||
package delegationtest
|
package delegationtest
|
||||||
|
|
||||||
import (
|
import gocid "github.com/ipfs/go-cid"
|
||||||
"github.com/ipfs/go-cid"
|
|
||||||
|
|
||||||
"github.com/ucan-wg/go-ucan/token/delegation"
|
var (
|
||||||
|
TokenAliceBobCID = gocid.MustParse("bafyreicidrwvmac5lvjypucgityrtjsknojraio7ujjli4r5eyby66wjzm")
|
||||||
|
TokenAliceBob = mustGetDelegation(TokenAliceBobCID)
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenAliceBobCID = cid.MustParse("bafyreifa35rjstdm37cjudzs72ab22rnh5blny725khtapox63fnsj6pbe")
|
TokenBobCarolCID = gocid.MustParse("bafyreihxv2uhq43oxllzs2xfvxst7wtvvvl7pohb2chcz6hjvfv2ntea5u")
|
||||||
TokenAliceBobSealed = mustGetBundle(TokenAliceBobCID).Sealed
|
TokenBobCarol = mustGetDelegation(TokenBobCarolCID)
|
||||||
TokenAliceBobBundle = mustGetBundle(TokenAliceBobCID)
|
|
||||||
TokenAliceBob = mustGetBundle(TokenAliceBobCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenBobCarolCID = cid.MustParse("bafyreiaysaafvfplhjsjywaqfletlr2sziui3sekkczwp2srszdoezwc7u")
|
TokenCarolDanCID = gocid.MustParse("bafyreihclsgiroazq3heqdswvj2cafwqbpboicq7immo65scl7ahktpsdq")
|
||||||
TokenBobCarolSealed = mustGetBundle(TokenBobCarolCID).Sealed
|
TokenCarolDan = mustGetDelegation(TokenCarolDanCID)
|
||||||
TokenBobCarolBundle = mustGetBundle(TokenBobCarolCID)
|
|
||||||
TokenBobCarol = mustGetBundle(TokenBobCarolCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenCarolDanCID = cid.MustParse("bafyreibwpzxcvrere7g5riv3dhza4xibrvulxcvj2nwxu6ozp572o2sbfa")
|
TokenDanErinCID = gocid.MustParse("bafyreicja6ihewy64p3ake56xukotafjlkh4uqep2qhj52en46zzfwby3e")
|
||||||
TokenCarolDanSealed = mustGetBundle(TokenCarolDanCID).Sealed
|
TokenDanErin = mustGetDelegation(TokenDanErinCID)
|
||||||
TokenCarolDanBundle = mustGetBundle(TokenCarolDanCID)
|
|
||||||
TokenCarolDan = mustGetBundle(TokenCarolDanCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenDanErinCID = cid.MustParse("bafyreichxodkcokcbvomxhmlf3g3j3zokruxvca63itynf7ib3hsmi4hla")
|
TokenErinFrankCID = gocid.MustParse("bafyreicjlx3lobxm6hl5s4htd4ydwkkqeiou6rft4rnvulfdyoew565vka")
|
||||||
TokenDanErinSealed = mustGetBundle(TokenDanErinCID).Sealed
|
TokenErinFrank = mustGetDelegation(TokenErinFrankCID)
|
||||||
TokenDanErinBundle = mustGetBundle(TokenDanErinCID)
|
|
||||||
TokenDanErin = mustGetBundle(TokenDanErinCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenErinFrankCID = cid.MustParse("bafyreigg434khwxqyfasnk63pi542uwtbrlpfd5sgjfrddqqcba3tardlu")
|
TokenCarolDan_InvalidExpandedCommandCID = gocid.MustParse("bafyreid3m3pk53gqgp5rlzqhvpedbwsqbidqlp4yz64vknwbzj7bxrmsr4")
|
||||||
TokenErinFrankSealed = mustGetBundle(TokenErinFrankCID).Sealed
|
TokenCarolDan_InvalidExpandedCommand = mustGetDelegation(TokenCarolDan_InvalidExpandedCommandCID)
|
||||||
TokenErinFrankBundle = mustGetBundle(TokenErinFrankCID)
|
|
||||||
TokenErinFrank = mustGetBundle(TokenErinFrankCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenCarolDan_InvalidExpandedCommandCID = cid.MustParse("bafyreifxdve23izhlg7fhzqh32i6wxtw33rqidm3cb72pghxlovnensjwe")
|
TokenDanErin_InvalidExpandedCommandCID = gocid.MustParse("bafyreifn4sy5onwajx3kqvot5mib6m6xarzrqjozqbzgmzpmc5ox3g2uzm")
|
||||||
TokenCarolDan_InvalidExpandedCommandSealed = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID).Sealed
|
TokenDanErin_InvalidExpandedCommand = mustGetDelegation(TokenDanErin_InvalidExpandedCommandCID)
|
||||||
TokenCarolDan_InvalidExpandedCommandBundle = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID)
|
|
||||||
TokenCarolDan_InvalidExpandedCommand = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenDanErin_InvalidExpandedCommandCID = cid.MustParse("bafyreieblevkqh2xnbr4x5mosvwv7rboat7ip6ucqvefb2pomrb4qjg4wu")
|
TokenErinFrank_InvalidExpandedCommandCID = gocid.MustParse("bafyreidmpgd36jznmq42bs34o4qi3fcbrsh4idkg6ejahudejzwb76fwxe")
|
||||||
TokenDanErin_InvalidExpandedCommandSealed = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID).Sealed
|
TokenErinFrank_InvalidExpandedCommand = mustGetDelegation(TokenErinFrank_InvalidExpandedCommandCID)
|
||||||
TokenDanErin_InvalidExpandedCommandBundle = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID)
|
|
||||||
TokenDanErin_InvalidExpandedCommand = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenErinFrank_InvalidExpandedCommandCID = cid.MustParse("bafyreia2ckukamhpeqpcdhevr75ym66vqem432dx4dwbdrdkh63pke3r3i")
|
TokenCarolDan_ValidAttenuatedCommandCID = gocid.MustParse("bafyreiekhtm237vyapk3c6voeb5lnz54crebqdqi3x4wn4u4cbrrhzsqfe")
|
||||||
TokenErinFrank_InvalidExpandedCommandSealed = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID).Sealed
|
TokenCarolDan_ValidAttenuatedCommand = mustGetDelegation(TokenCarolDan_ValidAttenuatedCommandCID)
|
||||||
TokenErinFrank_InvalidExpandedCommandBundle = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID)
|
|
||||||
TokenErinFrank_InvalidExpandedCommand = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenCarolDan_ValidAttenuatedCommandCID = cid.MustParse("bafyreibftsq3mhjd5itg6jbos4doccwspvmnilpuduniv2el7mnl45z33y")
|
TokenDanErin_ValidAttenuatedCommandCID = gocid.MustParse("bafyreicrvzqferyy7rgo75l5rn6r2nl7zyeexxjmu3dm4ff7rn2coblj4y")
|
||||||
TokenCarolDan_ValidAttenuatedCommandSealed = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID).Sealed
|
TokenDanErin_ValidAttenuatedCommand = mustGetDelegation(TokenDanErin_ValidAttenuatedCommandCID)
|
||||||
TokenCarolDan_ValidAttenuatedCommandBundle = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID)
|
|
||||||
TokenCarolDan_ValidAttenuatedCommand = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenDanErin_ValidAttenuatedCommandCID = cid.MustParse("bafyreig5gsifqn3afnyaoqtjhnud2w7tnrda57eiel3d45vbp47hjyayey")
|
TokenErinFrank_ValidAttenuatedCommandCID = gocid.MustParse("bafyreie6fhspk53kplcc2phla3e7z7fzldlbmmpuwk6nbow5q6s2zjmw2q")
|
||||||
TokenDanErin_ValidAttenuatedCommandSealed = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID).Sealed
|
TokenErinFrank_ValidAttenuatedCommand = mustGetDelegation(TokenErinFrank_ValidAttenuatedCommandCID)
|
||||||
TokenDanErin_ValidAttenuatedCommandBundle = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID)
|
|
||||||
TokenDanErin_ValidAttenuatedCommand = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenErinFrank_ValidAttenuatedCommandCID = cid.MustParse("bafyreiex2qyrw3xmye4fk5wdf6ukea3ojbokq77k6y566hupj6cicszi3e")
|
TokenCarolDan_InvalidSubjectCID = gocid.MustParse("bafyreifgksz6756if42tnc6rqsnbaa2u3fdrveo7ek44lnj2d64d5sw26u")
|
||||||
TokenErinFrank_ValidAttenuatedCommandSealed = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID).Sealed
|
TokenCarolDan_InvalidSubject = mustGetDelegation(TokenCarolDan_InvalidSubjectCID)
|
||||||
TokenErinFrank_ValidAttenuatedCommandBundle = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID)
|
|
||||||
TokenErinFrank_ValidAttenuatedCommand = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenCarolDan_InvalidSubjectCID = cid.MustParse("bafyreie7snyknubaeh4ruig7wxvos54b7skmwsnegf6k23bsffs5btbiiq")
|
TokenDanErin_InvalidSubjectCID = gocid.MustParse("bafyreibdwew5nypsxrm4fq73wu6hw3lgwwiolj3bi33xdrbgcf3ogm6fty")
|
||||||
TokenCarolDan_InvalidSubjectSealed = mustGetBundle(TokenCarolDan_InvalidSubjectCID).Sealed
|
TokenDanErin_InvalidSubject = mustGetDelegation(TokenDanErin_InvalidSubjectCID)
|
||||||
TokenCarolDan_InvalidSubjectBundle = mustGetBundle(TokenCarolDan_InvalidSubjectCID)
|
|
||||||
TokenCarolDan_InvalidSubject = mustGetBundle(TokenCarolDan_InvalidSubjectCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenDanErin_InvalidSubjectCID = cid.MustParse("bafyreicrfmlw4nqqk5t7j6r7ct3c7jlxeasyetb6fq3556vmtihcnbi54i")
|
TokenErinFrank_InvalidSubjectCID = gocid.MustParse("bafyreicr364mj3n7x4iyhcksxypelktcqkkw3ptg7ggxtqegw3p3mr6zc4")
|
||||||
TokenDanErin_InvalidSubjectSealed = mustGetBundle(TokenDanErin_InvalidSubjectCID).Sealed
|
TokenErinFrank_InvalidSubject = mustGetDelegation(TokenErinFrank_InvalidSubjectCID)
|
||||||
TokenDanErin_InvalidSubjectBundle = mustGetBundle(TokenDanErin_InvalidSubjectCID)
|
|
||||||
TokenDanErin_InvalidSubject = mustGetBundle(TokenDanErin_InvalidSubjectCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenErinFrank_InvalidSubjectCID = cid.MustParse("bafyreietx2sjsm72sbgjnosdnejwlqc5dadlolh2bjl6ifxdllslup7ecm")
|
TokenCarolDan_InvalidExpiredCID = gocid.MustParse("bafyreigenypixaxvhzlry5rjnywvjyl4xvzlzxz2ui74uzys7qdhos4bbu")
|
||||||
TokenErinFrank_InvalidSubjectSealed = mustGetBundle(TokenErinFrank_InvalidSubjectCID).Sealed
|
TokenCarolDan_InvalidExpired = mustGetDelegation(TokenCarolDan_InvalidExpiredCID)
|
||||||
TokenErinFrank_InvalidSubjectBundle = mustGetBundle(TokenErinFrank_InvalidSubjectCID)
|
|
||||||
TokenErinFrank_InvalidSubject = mustGetBundle(TokenErinFrank_InvalidSubjectCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenCarolDan_InvalidExpiredCID = cid.MustParse("bafyreifhdiem6r5fh3wwaa6uszqe5uashfwaastqjyvzssbiju4mravx3y")
|
TokenDanErin_InvalidExpiredCID = gocid.MustParse("bafyreifvnfb7zqocpdysedcvjkb4y7tqfuziuqjhbbdoay4zg33pwpbzqi")
|
||||||
TokenCarolDan_InvalidExpiredSealed = mustGetBundle(TokenCarolDan_InvalidExpiredCID).Sealed
|
TokenDanErin_InvalidExpired = mustGetDelegation(TokenDanErin_InvalidExpiredCID)
|
||||||
TokenCarolDan_InvalidExpiredBundle = mustGetBundle(TokenCarolDan_InvalidExpiredCID)
|
|
||||||
TokenCarolDan_InvalidExpired = mustGetBundle(TokenCarolDan_InvalidExpiredCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenDanErin_InvalidExpiredCID = cid.MustParse("bafyreiff7fqutstzz2ep5gzs2mqnt4rka7ezscrzpijyc4jjeopqm7oxxq")
|
TokenErinFrank_InvalidExpiredCID = gocid.MustParse("bafyreicvydzt3obkqx7krmoi3zu4tlirlksibxfks5jc7vlvjxjamv2764")
|
||||||
TokenDanErin_InvalidExpiredSealed = mustGetBundle(TokenDanErin_InvalidExpiredCID).Sealed
|
TokenErinFrank_InvalidExpired = mustGetDelegation(TokenErinFrank_InvalidExpiredCID)
|
||||||
TokenDanErin_InvalidExpiredBundle = mustGetBundle(TokenDanErin_InvalidExpiredCID)
|
|
||||||
TokenDanErin_InvalidExpired = mustGetBundle(TokenDanErin_InvalidExpiredCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenErinFrank_InvalidExpiredCID = cid.MustParse("bafyreigxfwcynzsy4v4po36s3mz5nf6mg4kpptw6rpld3sc3c3mthq4noa")
|
TokenCarolDan_InvalidInactiveCID = gocid.MustParse("bafyreicea5y2nvlitvxijkupeavtg23i7ktjk3uejnaquguurzptiabk4u")
|
||||||
TokenErinFrank_InvalidExpiredSealed = mustGetBundle(TokenErinFrank_InvalidExpiredCID).Sealed
|
TokenCarolDan_InvalidInactive = mustGetDelegation(TokenCarolDan_InvalidInactiveCID)
|
||||||
TokenErinFrank_InvalidExpiredBundle = mustGetBundle(TokenErinFrank_InvalidExpiredCID)
|
|
||||||
TokenErinFrank_InvalidExpired = mustGetBundle(TokenErinFrank_InvalidExpiredCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenCarolDan_InvalidInactiveCID = cid.MustParse("bafyreifw4j2mxg4bovuhihvnhqgln7m57lvajt6fza4x4jc7sncyh66wxm")
|
TokenDanErin_InvalidInactiveCID = gocid.MustParse("bafyreifsgqzkmxj2vexuts3z766mwcjreiisjg2jykyzf7tbj5sclutpvq")
|
||||||
TokenCarolDan_InvalidInactiveSealed = mustGetBundle(TokenCarolDan_InvalidInactiveCID).Sealed
|
TokenDanErin_InvalidInactive = mustGetDelegation(TokenDanErin_InvalidInactiveCID)
|
||||||
TokenCarolDan_InvalidInactiveBundle = mustGetBundle(TokenCarolDan_InvalidInactiveCID)
|
|
||||||
TokenCarolDan_InvalidInactive = mustGetBundle(TokenCarolDan_InvalidInactiveCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
TokenDanErin_InvalidInactiveCID = cid.MustParse("bafyreigrrfefw4wvjitiheonigeldnem2loo4yp5cghdskteunuxyozkiq")
|
TokenErinFrank_InvalidInactiveCID = gocid.MustParse("bafyreifbfegon24c6dndiqyktahzs65vhyasrygbw7nhsvojn6distsdre")
|
||||||
TokenDanErin_InvalidInactiveSealed = mustGetBundle(TokenDanErin_InvalidInactiveCID).Sealed
|
TokenErinFrank_InvalidInactive = mustGetDelegation(TokenErinFrank_InvalidInactiveCID)
|
||||||
TokenDanErin_InvalidInactiveBundle = mustGetBundle(TokenDanErin_InvalidInactiveCID)
|
|
||||||
TokenDanErin_InvalidInactive = mustGetBundle(TokenDanErin_InvalidInactiveCID).Decoded
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var ProofAliceBob = []gocid.Cid{
|
||||||
TokenErinFrank_InvalidInactiveCID = cid.MustParse("bafyreicdlvnc7iinl2eay3xc3yvtmwezifvfxywjud3defssstusngt6du")
|
|
||||||
TokenErinFrank_InvalidInactiveSealed = mustGetBundle(TokenErinFrank_InvalidInactiveCID).Sealed
|
|
||||||
TokenErinFrank_InvalidInactiveBundle = mustGetBundle(TokenErinFrank_InvalidInactiveCID)
|
|
||||||
TokenErinFrank_InvalidInactive = mustGetBundle(TokenErinFrank_InvalidInactiveCID).Decoded
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
TokenCarolDan_ValidExamplePolicyCID = cid.MustParse("bafyreidvi5y42befcnb2qud23yt5memf4itn2v6xmw7gvdpihbnanvgqly")
|
|
||||||
TokenCarolDan_ValidExamplePolicySealed = mustGetBundle(TokenCarolDan_ValidExamplePolicyCID).Sealed
|
|
||||||
TokenCarolDan_ValidExamplePolicyBundle = mustGetBundle(TokenCarolDan_ValidExamplePolicyCID)
|
|
||||||
TokenCarolDan_ValidExamplePolicy = mustGetBundle(TokenCarolDan_ValidExamplePolicyCID).Decoded
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
TokenDanErin_ValidExamplePolicyCID = cid.MustParse("bafyreid3mr53fjsntyyfbbzjx2n32d7atj3v4ztfufdkamrodm6kcjww64")
|
|
||||||
TokenDanErin_ValidExamplePolicySealed = mustGetBundle(TokenDanErin_ValidExamplePolicyCID).Sealed
|
|
||||||
TokenDanErin_ValidExamplePolicyBundle = mustGetBundle(TokenDanErin_ValidExamplePolicyCID)
|
|
||||||
TokenDanErin_ValidExamplePolicy = mustGetBundle(TokenDanErin_ValidExamplePolicyCID).Decoded
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
TokenErinFrank_ValidExamplePolicyCID = cid.MustParse("bafyreiegytubdwqnl3gd7dddfmqhr4kyo4sb5446kjyqvvolk2wmwum6ae")
|
|
||||||
TokenErinFrank_ValidExamplePolicySealed = mustGetBundle(TokenErinFrank_ValidExamplePolicyCID).Sealed
|
|
||||||
TokenErinFrank_ValidExamplePolicyBundle = mustGetBundle(TokenErinFrank_ValidExamplePolicyCID)
|
|
||||||
TokenErinFrank_ValidExamplePolicy = mustGetBundle(TokenErinFrank_ValidExamplePolicyCID).Decoded
|
|
||||||
)
|
|
||||||
|
|
||||||
var AllTokens = []*delegation.Token{
|
|
||||||
TokenAliceBob,
|
|
||||||
TokenBobCarol,
|
|
||||||
TokenCarolDan,
|
|
||||||
TokenDanErin,
|
|
||||||
TokenErinFrank,
|
|
||||||
TokenCarolDan_InvalidExpandedCommand,
|
|
||||||
TokenDanErin_InvalidExpandedCommand,
|
|
||||||
TokenErinFrank_InvalidExpandedCommand,
|
|
||||||
TokenCarolDan_ValidAttenuatedCommand,
|
|
||||||
TokenDanErin_ValidAttenuatedCommand,
|
|
||||||
TokenErinFrank_ValidAttenuatedCommand,
|
|
||||||
TokenCarolDan_InvalidSubject,
|
|
||||||
TokenDanErin_InvalidSubject,
|
|
||||||
TokenErinFrank_InvalidSubject,
|
|
||||||
TokenCarolDan_InvalidExpired,
|
|
||||||
TokenDanErin_InvalidExpired,
|
|
||||||
TokenErinFrank_InvalidExpired,
|
|
||||||
TokenCarolDan_InvalidInactive,
|
|
||||||
TokenDanErin_InvalidInactive,
|
|
||||||
TokenErinFrank_InvalidInactive,
|
|
||||||
TokenCarolDan_ValidExamplePolicy,
|
|
||||||
TokenDanErin_ValidExamplePolicy,
|
|
||||||
TokenErinFrank_ValidExamplePolicy,
|
|
||||||
}
|
|
||||||
|
|
||||||
var AllBundles = []delegation.Bundle{
|
|
||||||
TokenAliceBobBundle,
|
|
||||||
TokenBobCarolBundle,
|
|
||||||
TokenCarolDanBundle,
|
|
||||||
TokenDanErinBundle,
|
|
||||||
TokenErinFrankBundle,
|
|
||||||
TokenCarolDan_InvalidExpandedCommandBundle,
|
|
||||||
TokenDanErin_InvalidExpandedCommandBundle,
|
|
||||||
TokenErinFrank_InvalidExpandedCommandBundle,
|
|
||||||
TokenCarolDan_ValidAttenuatedCommandBundle,
|
|
||||||
TokenDanErin_ValidAttenuatedCommandBundle,
|
|
||||||
TokenErinFrank_ValidAttenuatedCommandBundle,
|
|
||||||
TokenCarolDan_InvalidSubjectBundle,
|
|
||||||
TokenDanErin_InvalidSubjectBundle,
|
|
||||||
TokenErinFrank_InvalidSubjectBundle,
|
|
||||||
TokenCarolDan_InvalidExpiredBundle,
|
|
||||||
TokenDanErin_InvalidExpiredBundle,
|
|
||||||
TokenErinFrank_InvalidExpiredBundle,
|
|
||||||
TokenCarolDan_InvalidInactiveBundle,
|
|
||||||
TokenDanErin_InvalidInactiveBundle,
|
|
||||||
TokenErinFrank_InvalidInactiveBundle,
|
|
||||||
TokenCarolDan_ValidExamplePolicyBundle,
|
|
||||||
TokenDanErin_ValidExamplePolicyBundle,
|
|
||||||
TokenErinFrank_ValidExamplePolicyBundle,
|
|
||||||
}
|
|
||||||
|
|
||||||
var cidToName = map[cid.Cid]string{
|
|
||||||
TokenAliceBobCID: "TokenAliceBob",
|
|
||||||
TokenBobCarolCID: "TokenBobCarol",
|
|
||||||
TokenCarolDanCID: "TokenCarolDan",
|
|
||||||
TokenDanErinCID: "TokenDanErin",
|
|
||||||
TokenErinFrankCID: "TokenErinFrank",
|
|
||||||
TokenCarolDan_InvalidExpandedCommandCID: "TokenCarolDan_InvalidExpandedCommand",
|
|
||||||
TokenDanErin_InvalidExpandedCommandCID: "TokenDanErin_InvalidExpandedCommand",
|
|
||||||
TokenErinFrank_InvalidExpandedCommandCID: "TokenErinFrank_InvalidExpandedCommand",
|
|
||||||
TokenCarolDan_ValidAttenuatedCommandCID: "TokenCarolDan_ValidAttenuatedCommand",
|
|
||||||
TokenDanErin_ValidAttenuatedCommandCID: "TokenDanErin_ValidAttenuatedCommand",
|
|
||||||
TokenErinFrank_ValidAttenuatedCommandCID: "TokenErinFrank_ValidAttenuatedCommand",
|
|
||||||
TokenCarolDan_InvalidSubjectCID: "TokenCarolDan_InvalidSubject",
|
|
||||||
TokenDanErin_InvalidSubjectCID: "TokenDanErin_InvalidSubject",
|
|
||||||
TokenErinFrank_InvalidSubjectCID: "TokenErinFrank_InvalidSubject",
|
|
||||||
TokenCarolDan_InvalidExpiredCID: "TokenCarolDan_InvalidExpired",
|
|
||||||
TokenDanErin_InvalidExpiredCID: "TokenDanErin_InvalidExpired",
|
|
||||||
TokenErinFrank_InvalidExpiredCID: "TokenErinFrank_InvalidExpired",
|
|
||||||
TokenCarolDan_InvalidInactiveCID: "TokenCarolDan_InvalidInactive",
|
|
||||||
TokenDanErin_InvalidInactiveCID: "TokenDanErin_InvalidInactive",
|
|
||||||
TokenErinFrank_InvalidInactiveCID: "TokenErinFrank_InvalidInactive",
|
|
||||||
TokenCarolDan_ValidExamplePolicyCID: "TokenCarolDan_ValidExamplePolicy",
|
|
||||||
TokenDanErin_ValidExamplePolicyCID: "TokenDanErin_ValidExamplePolicy",
|
|
||||||
TokenErinFrank_ValidExamplePolicyCID: "TokenErinFrank_ValidExamplePolicy",
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProofAliceBob = []cid.Cid{
|
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarol = []cid.Cid{
|
var ProofAliceBobCarol = []gocid.Cid{
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDan = []cid.Cid{
|
var ProofAliceBobCarolDan = []gocid.Cid{
|
||||||
TokenCarolDanCID,
|
TokenCarolDanCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErin = []cid.Cid{
|
var ProofAliceBobCarolDanErin = []gocid.Cid{
|
||||||
TokenDanErinCID,
|
TokenDanErinCID,
|
||||||
TokenCarolDanCID,
|
TokenCarolDanCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErinFrank = []cid.Cid{
|
var ProofAliceBobCarolDanErinFrank = []gocid.Cid{
|
||||||
TokenErinFrankCID,
|
TokenErinFrankCID,
|
||||||
TokenDanErinCID,
|
TokenDanErinCID,
|
||||||
TokenCarolDanCID,
|
TokenCarolDanCID,
|
||||||
@@ -277,20 +134,20 @@ var ProofAliceBobCarolDanErinFrank = []cid.Cid{
|
|||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDan_InvalidExpandedCommand = []cid.Cid{
|
var ProofAliceBobCarolDan_InvalidExpandedCommand = []gocid.Cid{
|
||||||
TokenCarolDan_InvalidExpandedCommandCID,
|
TokenCarolDan_InvalidExpandedCommandCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErin_InvalidExpandedCommand = []cid.Cid{
|
var ProofAliceBobCarolDanErin_InvalidExpandedCommand = []gocid.Cid{
|
||||||
TokenDanErin_InvalidExpandedCommandCID,
|
TokenDanErin_InvalidExpandedCommandCID,
|
||||||
TokenCarolDan_InvalidExpandedCommandCID,
|
TokenCarolDan_InvalidExpandedCommandCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErinFrank_InvalidExpandedCommand = []cid.Cid{
|
var ProofAliceBobCarolDanErinFrank_InvalidExpandedCommand = []gocid.Cid{
|
||||||
TokenErinFrank_InvalidExpandedCommandCID,
|
TokenErinFrank_InvalidExpandedCommandCID,
|
||||||
TokenDanErin_InvalidExpandedCommandCID,
|
TokenDanErin_InvalidExpandedCommandCID,
|
||||||
TokenCarolDan_InvalidExpandedCommandCID,
|
TokenCarolDan_InvalidExpandedCommandCID,
|
||||||
@@ -298,20 +155,20 @@ var ProofAliceBobCarolDanErinFrank_InvalidExpandedCommand = []cid.Cid{
|
|||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDan_ValidAttenuatedCommand = []cid.Cid{
|
var ProofAliceBobCarolDan_ValidAttenuatedCommand = []gocid.Cid{
|
||||||
TokenCarolDan_ValidAttenuatedCommandCID,
|
TokenCarolDan_ValidAttenuatedCommandCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErin_ValidAttenuatedCommand = []cid.Cid{
|
var ProofAliceBobCarolDanErin_ValidAttenuatedCommand = []gocid.Cid{
|
||||||
TokenDanErin_ValidAttenuatedCommandCID,
|
TokenDanErin_ValidAttenuatedCommandCID,
|
||||||
TokenCarolDan_ValidAttenuatedCommandCID,
|
TokenCarolDan_ValidAttenuatedCommandCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErinFrank_ValidAttenuatedCommand = []cid.Cid{
|
var ProofAliceBobCarolDanErinFrank_ValidAttenuatedCommand = []gocid.Cid{
|
||||||
TokenErinFrank_ValidAttenuatedCommandCID,
|
TokenErinFrank_ValidAttenuatedCommandCID,
|
||||||
TokenDanErin_ValidAttenuatedCommandCID,
|
TokenDanErin_ValidAttenuatedCommandCID,
|
||||||
TokenCarolDan_ValidAttenuatedCommandCID,
|
TokenCarolDan_ValidAttenuatedCommandCID,
|
||||||
@@ -319,20 +176,20 @@ var ProofAliceBobCarolDanErinFrank_ValidAttenuatedCommand = []cid.Cid{
|
|||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDan_InvalidSubject = []cid.Cid{
|
var ProofAliceBobCarolDan_InvalidSubject = []gocid.Cid{
|
||||||
TokenCarolDan_InvalidSubjectCID,
|
TokenCarolDan_InvalidSubjectCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErin_InvalidSubject = []cid.Cid{
|
var ProofAliceBobCarolDanErin_InvalidSubject = []gocid.Cid{
|
||||||
TokenDanErin_InvalidSubjectCID,
|
TokenDanErin_InvalidSubjectCID,
|
||||||
TokenCarolDan_InvalidSubjectCID,
|
TokenCarolDan_InvalidSubjectCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErinFrank_InvalidSubject = []cid.Cid{
|
var ProofAliceBobCarolDanErinFrank_InvalidSubject = []gocid.Cid{
|
||||||
TokenErinFrank_InvalidSubjectCID,
|
TokenErinFrank_InvalidSubjectCID,
|
||||||
TokenDanErin_InvalidSubjectCID,
|
TokenDanErin_InvalidSubjectCID,
|
||||||
TokenCarolDan_InvalidSubjectCID,
|
TokenCarolDan_InvalidSubjectCID,
|
||||||
@@ -340,20 +197,20 @@ var ProofAliceBobCarolDanErinFrank_InvalidSubject = []cid.Cid{
|
|||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDan_InvalidExpired = []cid.Cid{
|
var ProofAliceBobCarolDan_InvalidExpired = []gocid.Cid{
|
||||||
TokenCarolDan_InvalidExpiredCID,
|
TokenCarolDan_InvalidExpiredCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErin_InvalidExpired = []cid.Cid{
|
var ProofAliceBobCarolDanErin_InvalidExpired = []gocid.Cid{
|
||||||
TokenDanErin_InvalidExpiredCID,
|
TokenDanErin_InvalidExpiredCID,
|
||||||
TokenCarolDan_InvalidExpiredCID,
|
TokenCarolDan_InvalidExpiredCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErinFrank_InvalidExpired = []cid.Cid{
|
var ProofAliceBobCarolDanErinFrank_InvalidExpired = []gocid.Cid{
|
||||||
TokenErinFrank_InvalidExpiredCID,
|
TokenErinFrank_InvalidExpiredCID,
|
||||||
TokenDanErin_InvalidExpiredCID,
|
TokenDanErin_InvalidExpiredCID,
|
||||||
TokenCarolDan_InvalidExpiredCID,
|
TokenCarolDan_InvalidExpiredCID,
|
||||||
@@ -361,44 +218,23 @@ var ProofAliceBobCarolDanErinFrank_InvalidExpired = []cid.Cid{
|
|||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDan_InvalidInactive = []cid.Cid{
|
var ProofAliceBobCarolDan_InvalidInactive = []gocid.Cid{
|
||||||
TokenCarolDan_InvalidInactiveCID,
|
TokenCarolDan_InvalidInactiveCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErin_InvalidInactive = []cid.Cid{
|
var ProofAliceBobCarolDanErin_InvalidInactive = []gocid.Cid{
|
||||||
TokenDanErin_InvalidInactiveCID,
|
TokenDanErin_InvalidInactiveCID,
|
||||||
TokenCarolDan_InvalidInactiveCID,
|
TokenCarolDan_InvalidInactiveCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErinFrank_InvalidInactive = []cid.Cid{
|
var ProofAliceBobCarolDanErinFrank_InvalidInactive = []gocid.Cid{
|
||||||
TokenErinFrank_InvalidInactiveCID,
|
TokenErinFrank_InvalidInactiveCID,
|
||||||
TokenDanErin_InvalidInactiveCID,
|
TokenDanErin_InvalidInactiveCID,
|
||||||
TokenCarolDan_InvalidInactiveCID,
|
TokenCarolDan_InvalidInactiveCID,
|
||||||
TokenBobCarolCID,
|
TokenBobCarolCID,
|
||||||
TokenAliceBobCID,
|
TokenAliceBobCID,
|
||||||
}
|
}
|
||||||
|
|
||||||
var ProofAliceBobCarolDan_ValidExamplePolicy = []cid.Cid{
|
|
||||||
TokenCarolDan_ValidExamplePolicyCID,
|
|
||||||
TokenBobCarolCID,
|
|
||||||
TokenAliceBobCID,
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErin_ValidExamplePolicy = []cid.Cid{
|
|
||||||
TokenDanErin_ValidExamplePolicyCID,
|
|
||||||
TokenCarolDan_ValidExamplePolicyCID,
|
|
||||||
TokenBobCarolCID,
|
|
||||||
TokenAliceBobCID,
|
|
||||||
}
|
|
||||||
|
|
||||||
var ProofAliceBobCarolDanErinFrank_ValidExamplePolicy = []cid.Cid{
|
|
||||||
TokenErinFrank_ValidExamplePolicyCID,
|
|
||||||
TokenDanErin_ValidExamplePolicyCID,
|
|
||||||
TokenCarolDan_ValidExamplePolicyCID,
|
|
||||||
TokenBobCarolCID,
|
|
||||||
TokenAliceBobCID,
|
|
||||||
}
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user