172 Commits
v1 ... v1.1.0

Author SHA1 Message Date
1fbb8568c0 build(config): bump go-varint dependency to v0.1.0
Some checks failed
go continuous benchmark / Run Go continuous benchmark (push) Has been cancelled
Go Test / ubuntu (go 1.22.x) (push) Has been cancelled
Go Test / ubuntu (go 1.23.x) (push) Has been cancelled
2026-01-08 15:48:44 -05:00
36717a0826 refactor(token): migrate from ucan-wg to code.sonr.org/go
Some checks failed
go continuous benchmark / Run Go continuous benchmark (push) Has been cancelled
Go Test / ubuntu (go 1.22.x) (push) Has been cancelled
Go Test / ubuntu (go 1.23.x) (push) Has been cancelled
2026-01-08 15:46:02 -05:00
fe8b5a6e4c refactor(server): migrate from ucan-wg to code.sonr.org/go/ucan 2026-01-08 15:46:01 -05:00
4c1ae3a0be refactor(pkg): Migrate package imports to use new code.sonr.org/go/ucan module path 2026-01-08 15:46:00 -05:00
1f69e9c2c4 refactor(meta): update import paths to use new code.sonr.org/go-ucan package 2026-01-08 15:45:59 -05:00
4895a069dd refactor(issuer): migrate from github.com/MetaMask/go-did-it to code.sonr.org/go/did-it 2026-01-08 15:45:58 -05:00
ec4cc024d4 refactor(invocation): migrate from MetaMask/go-did-it to code.sonr.org/go/did-it 2026-01-08 15:45:57 -05:00
0c35addf65 refactor(examples): migrate from MetaMask/go-did-it to code.sonr.org/go/did-it 2026-01-08 15:45:56 -05:00
c5b594dd69 docs(readme): update links and references to point to new repository location 2026-01-08 15:45:55 -05:00
b23a6de775 refactor(delegation): migrate from github.com/ucan-wg to code.sonr.org/go 2026-01-08 15:45:53 -05:00
499f83fd6f refactor(container): migrate to new ucan and did-it package paths 2026-01-08 15:45:52 -05:00
d660445190 chore(config): update go module dependencies and repo config 2026-01-08 15:45:51 -05:00
869e449383 test(command): update command_test imports to use code.sonr.org/go/ucan 2026-01-08 15:45:50 -05:00
6b9698c1e8 refactor(client): migrate from MetaMask/go-did-it to code.sonr.org/go/did-it 2026-01-08 15:45:49 -05:00
33841ea261 refactor(args): update import paths to use new repository location 2026-01-08 15:45:48 -05:00
Michael Muré
4b99c9f1df Merge pull request #118 from alanshaw/ash/fix/sort-tokens
fix: sort tokens
2025-12-03 17:04:04 +01:00
Michael Muré
39694340cd Merge pull request #117 from ucan-wg/ash/fix/licence-name
fix: license name
2025-12-03 13:32:54 +01:00
Alan Shaw
e93e464977 fix: sort tokens 2025-12-03 12:19:27 +00:00
ash
823af27272 fix: license name
License is Apache-2.0 **OR** MIT i.e. not "and"/"+"

Signed-off-by: ash <alan138@gmail.com>
2025-11-02 22:02:28 +00:00
Michael Muré
70f039a654 update Readme 2025-10-29 15:48:15 +01:00
Michael Muré
d56db6722c Merge pull request #115 from ucan-wg/tooling
Add a somewhat complete toolkit
2025-08-07 14:39:42 +02:00
Michael Muré
0647e4ff8a adjust the toolkit to the new location 2025-08-07 14:28:16 +02:00
Steve Moyer
06f478b9c3 feat(client): add delegation bundle(s) to client.pool 2025-08-05 16:57:19 +02:00
Michael Muré
4aedc4de39 client: add a convenient PrepareDelegation function 2025-08-05 16:57:19 +02:00
Michael Muré
0fd71612d3 exectx: DX improvement: catch proof chain issue early 2025-08-05 16:57:19 +02:00
Michael Muré
29ccdb700e exectx: more tests 2025-08-05 16:57:19 +02:00
Michael Muré
2d031fdbdb exectx: easy access to the root delegation 2025-08-05 16:57:19 +02:00
Michael Muré
1c4a0a9c81 exectx: add test for the main middleware, adjust some HTTP status 2025-08-05 16:57:19 +02:00
Michael Muré
df6dfee210 example: finish readme, small tuning 2025-08-05 16:57:19 +02:00
Michael Muré
c670433335 example: add sub-delegation 2025-08-05 16:57:19 +02:00
Michael Muré
55f38fef4a add server/issuer/client examples, and lots of sanding 2025-08-05 16:57:19 +02:00
Michael Muré
1098a834fb bearer: add some tests 2025-08-05 16:57:19 +02:00
Michael Muré
b95e525cfb exectx: add a minimal middleware to extract the HTTP bearer, and make the UCAN ctx 2025-08-05 16:57:19 +02:00
Michael Muré
cc207aa202 bearer: add a package to add/extract a HTTP Authorisation bearer header 2025-08-05 16:57:19 +02:00
Michael Muré
41d679dfab extargs: make the hash more convenient to use by directly returning the invocation.Option 2025-08-05 16:57:19 +02:00
Michael Muré
cc661f3936 infura requester: properly handle failure 2025-08-05 16:57:19 +02:00
Michael Muré
07d2745966 client,step1: add a KS compatible client, and a script to package tokens for PC step1 2025-08-05 16:57:19 +02:00
rarquevaux
cd9ee535ad fix(ucanCtx): update test 2025-08-05 16:57:19 +02:00
rarquevaux
9e062b0cc7 fix(ucanCtx): pointer receiver and fix to verifyHttp 2025-08-05 16:57:19 +02:00
Michael Muré
cf3eb1b3f7 bump go-ucan 2025-08-05 16:57:19 +02:00
Michael Muré
3b6d70f47a extargs: add ".inf" external args for arbitrary Infura args 2025-08-05 16:57:19 +02:00
Michael Muré
09c8815755 update go-ucan, with the now spec defined container
https://github.com/ucan-wg/go-ucan/blob/main/pkg/container/SPEC.md
2025-08-05 16:57:19 +02:00
Michael Muré
f18ae547ab Update server/extargs/http.go
Co-authored-by: Steve Moyer <smoyer64@gmail.com>
2025-08-05 16:57:19 +02:00
Michael Muré
ad02aa8d4f server: rename "bearer" package to "extargs" 2025-08-05 16:57:19 +02:00
Michael Muré
9c8e9f17fa cli: update following go-ucan changes 2025-08-05 16:57:19 +02:00
Michael Muré
11b4352063 client: fix FindProof() 2025-08-05 16:57:19 +02:00
Michael Muré
b0783bf4a4 add a "core" implementation of an issuer 2025-08-05 16:57:19 +02:00
Michael Muré
2eeaaccc6d client: follow go-ucan changes, improvements, example 2025-08-05 16:57:19 +02:00
Michael Muré
1187674a24 server: minor code fixes 2025-08-05 16:57:19 +02:00
Michael Muré
4c08b22c61 add an early version of a UCAN client 2025-08-05 16:57:19 +02:00
Michael Muré
174bf01c64 client: simpler FindProof() 2025-08-05 16:57:19 +02:00
Michael Muré
4167bf44bd reorg packages, remove outdated server example 2025-08-05 16:57:19 +02:00
Michael Muré
4f4331b677 client: move FindProof testing directly where the code is 2025-08-05 16:57:19 +02:00
Michael Muré
547416e60d client: improve/modularize 2025-08-05 16:57:19 +02:00
Michael Muré
6c1602507b client: add a pool that can find a prood for an invocation 2025-08-05 16:57:19 +02:00
Michael Muré
6f4853cd2f bearer,context: support hash verification of the computed args 2025-08-05 16:57:19 +02:00
Michael Muré
4c25456583 server: update deps 2025-08-05 16:57:19 +02:00
Michael Muré
83f3e4c3b0 server: add a readme 2025-08-05 16:57:19 +02:00
Michael Muré
1178e51b18 server: add a WIP middleware example 2025-08-05 16:57:19 +02:00
Michael Muré
3ec8f56412 Merge pull request #114 from ucan-wg/varsig-did
Integrate go-varsig and go-did-it
2025-08-05 16:56:51 +02:00
Michael Muré
5891bdcd5d update go-varsig, go-did-it, add missing resolution options 2025-08-05 16:54:18 +02:00
Michael Muré
6fb25481ce delegation: add interop testing 2025-08-04 16:45:49 +02:00
Michael Muré
e7edccdd71 wire the DID resolution options 2025-07-31 17:49:01 +02:00
Michael Muré
33e8a8a821 Integrate go-varsig and go-did-it
- go-varsig provides a varsig V1 implementation
- go-did-it provides a complete and extensible DID implementation
2025-07-31 15:00:31 +02:00
Steve Moyer
947add66c5 Merge pull request #112 from ucan-wg/fix/invokedat-to-issued-at
fix(invocation): iat incorrectly named InvokedAt instead of IssuedAt
2025-03-11 13:20:25 -04:00
Steve Moyer
3faf9d598c fix(invocation): also update docs and examples 2025-03-11 13:14:53 -04:00
Steve Moyer
fbf55e98ba Update token/invocation/options.go
Co-authored-by: Michael Muré <batolettre@gmail.com>
Signed-off-by: Steve Moyer <smoyer64@gmail.com>
2025-03-11 12:57:08 -04:00
Steve Moyer
05c2573d95 Update token/invocation/options.go
Co-authored-by: Michael Muré <batolettre@gmail.com>
Signed-off-by: Steve Moyer <smoyer64@gmail.com>
2025-03-11 12:56:49 -04:00
Steve Moyer
d7472621ce Update token/invocation/invocation.go
Co-authored-by: Michael Muré <batolettre@gmail.com>
Signed-off-by: Steve Moyer <smoyer64@gmail.com>
2025-03-11 12:56:34 -04:00
Steve Moyer
7b44f480ee fix(invocation): iat incorrectly named InvokedAt instead of IssuedAt 2025-03-11 12:41:24 -04:00
Michael Muré
5eb7b1a8e4 Merge pull request #111 from ucan-wg/MichaelMure-patch-1
container: typo fix in SPEC.md
2025-03-11 17:34:08 +01:00
Michael Muré
e91afe29d8 Update SPEC.md
Signed-off-by: Michael Muré <batolettre@gmail.com>
2025-03-11 17:32:17 +01:00
Michael Muré
a82bce556f Merge pull request #108 from ucan-wg/cont-test
container: add a helper for test only
2025-02-27 15:00:47 +01:00
Michael Muré
a54d66afe5 Merge pull request #110 from ucan-wg/cleanups
minor cleanups
2025-02-27 14:58:24 +01:00
Michael Muré
14a57d7391 Merge pull request #109 from ucan-wg/downgrade-libp2p
deps: downgrade libp2p
2025-02-27 14:58:17 +01:00
Michael Muré
68469db91a minor cleanups 2025-02-27 14:35:53 +01:00
Michael Muré
cc1d68be0c deps: downgrade libp2p
We only use go-libp2p for the crypto wrappers (github.com/libp2p/go-libp2p/core/crypto), and those have not changed in a while.

We don't need the most recent version of go-libp2p. On the other hand, a too recent version can easily mess with project that aim for an older version of libp2p (often transitively). This PR gives some leeway for version resolution.
2025-02-27 14:28:23 +01:00
Michael Muré
6d3846ac62 container: add a helper for test only 2025-02-27 14:27:02 +01:00
Steve Moyer
6aa33b1547 Merge pull request #107 from ucan-wg/tinygo
fix(policy): make 53-bit limits portable across platforms
2025-02-24 06:25:06 -05:00
Steve Moyer
9589cc8b44 feat(tinygo): make 53-bit limits portable across platforms 2025-02-21 11:03:33 -05:00
Michael Muré
879c0ab03b Merge pull request #106 from ucan-wg/cont-type
container: use the correct type in GetAllDelegations
2025-02-03 15:13:05 +01:00
Michael Muré
fe14765c8d container: use the correct type in GetAllDelegations 2025-02-03 15:02:31 +01:00
Michael Muré
1b28cb49bf Merge pull request #105 from ucan-wg/containertest
rename testvectors into containertest to match previous namings
2025-01-29 15:04:33 +01:00
Michael Muré
e1fc838caf rename testvectors into containertest to match previous namings 2025-01-29 14:46:01 +01:00
Michael Muré
f29b9e94fc Merge pull request #104 from ucan-wg/bundle
token: ditch the generic bundle in favor of specialized struct
2025-01-29 14:37:29 +01:00
Michael Muré
506ed21b94 token: ditch the generic bundle in favor of specialized struct
It's kust cleaner that way, the generic has no upside.
2025-01-29 14:28:13 +01:00
Michael Muré
126177b9e5 Merge pull request #103 from ucan-wg/dlg-is
delegation: add predicates to check if a delegation is a root or powe…
2025-01-29 14:11:29 +01:00
Michael Muré
2bddab8b0c delegation: add predicates to check if a delegation is a root or powerline 2025-01-29 14:07:49 +01:00
Michael Muré
45ead12131 Merge pull request #102 from ucan-wg/cont-keep-sealed
container: Reader should keep around and expose the sealed bytes
2025-01-23 18:06:41 +01:00
Michael Muré
9d5e170409 Merge pull request #101 from ucan-wg/cont-testing
container: expose test vectors for easier testing
2025-01-23 18:06:27 +01:00
Michael Muré
9d047f038d container: Reader should keep around and expose the sealed bytes 2025-01-23 17:13:10 +01:00
Michael Muré
4c5afcb084 container: expose test vectors for easier testing 2025-01-23 17:10:34 +01:00
Michael Muré
e218b49577 Merge pull request #100 from ucan-wg/pol-neq
policy: implement the missing '!='
2025-01-23 12:38:17 +01:00
Michael Muré
4c81ac778e policy: fix tests now that selector evaluation is clearer
https://github.com/ucan-wg/delegation/pull/15
https://github.com/ucan-wg/delegation/pull/16
2025-01-23 12:28:02 +01:00
Michael Muré
7ae65e7c8e policy: fix some code style 2025-01-22 17:30:28 +01:00
Michael Muré
7f9cb6426c policy: implement the missing '!='
https://github.com/ucan-wg/delegation/pull/15
2025-01-22 14:12:41 +01:00
Michael Muré
9c141029c3 container: better naming 2025-01-20 12:14:45 +01:00
Michael Muré
0c6717cfbc Merge pull request #98 from ucan-wg/container-err
container: clarify returned error
2025-01-13 13:54:06 +01:00
Michael Muré
6d7fd28324 container: clarify returned error 2025-01-13 13:50:05 +01:00
Michael Muré
ccc0120e78 Merge pull request #99 from ucan-wg/persona-fix
didtest: offset persona constants
2025-01-13 13:44:18 +01:00
Michael Muré
bf27b97a57 didtest: offset persona constants
It seems like having a persona with a zero value confuses some static checker into warning against a possible nil pointer deref.

An easy fix is to just not have zero.
2025-01-13 13:30:03 +01:00
Michael Muré
10dd4fa6d1 Merge pull request #97 from ucan-wg/ctn-writer-no-cid
container: the writer should not ask for a CID, as its discarded
2025-01-13 12:41:10 +01:00
Michael Muré
5695609f8b container: the writer should not ask for a CID, as its discarded 2025-01-13 12:24:04 +01:00
Michael Muré
b7ddead7c8 Merge pull request #96 from ucan-wg/container-spec
container: add a specification
2025-01-09 13:30:48 +01:00
Michael Muré
9cbec37685 container: no extra keys in the packaging allowed 2025-01-08 21:14:30 +01:00
Michael Muré
00dbd975b1 container: add test vectors 2025-01-08 18:59:30 +01:00
Michael Muré
c792a4cce5 container: typo and clarity in the spec 2025-01-08 18:55:08 +01:00
Michael Muré
8f3f1c775e container: implement header based serialization, also add SPEC test vectors 2025-01-08 17:31:04 +01:00
Michael Muré
704ed25768 container: add a specification 2025-01-07 18:16:27 +01:00
Michael Muré
95bdbc4fc5 Merge pull request #95 from ucan-wg/minor-impro
Minor impro
2024-12-12 16:36:17 +01:00
Michael Muré
416345dba9 args,meta: add a Len() 2024-12-12 16:06:01 +01:00
Michael Muré
042d6dc52f didtest: complete the set of function, finish removing the dependency on testing.T 2024-12-12 16:05:13 +01:00
Michael Muré
8bb3a4f4d0 expose secretbox, notably for the GenerateKey() function that should be public 2024-12-12 16:04:31 +01:00
Michael Muré
47156a8ad6 Merge pull request #94 from ucan-wg/base64-string
container: I/O as strings for base64
2024-12-11 16:12:48 +01:00
Michael Muré
ce6d163627 container: I/O as strings for base64 2024-12-11 16:05:16 +01:00
Michael Muré
c3c2c96008 Merge pull request #93 from ucan-wg/proof-rephrase
invocation: rephrase slightly the proof rules to be less confusing down the line
2024-12-10 13:27:12 +01:00
Steve Moyer
903632695f test(delegation): calculate newCID from new.dagjson so tests are not brittle 2024-12-10 07:14:36 -05:00
Steve Moyer
f2d75b7815 test(delegation): fix newCID after new.dagjson update (brittle) 2024-12-10 07:03:06 -05:00
Steve Moyer
4f09829abe feat(delegationtest): update generator to changed delegation.New constructor 2024-12-10 06:53:01 -05:00
Michael Muré
5660df32b5 delegation,invocation: proper String() function 2024-12-10 12:20:18 +01:00
Michael Muré
2f2a74c7ec delegation: fix following constructor updates 2024-12-10 12:19:47 +01:00
Michael Muré
0592717637 (WIP) refine the token constructors:
- for invocation, reorder the parameters for a more "natural language" mental model
- for delegation, make "subject" a required parameter to avoid make powerline by mistake
- for delegation, implement powerline
2024-12-09 20:39:47 +01:00
Michael Muré
80c2d60ab3 invocation: rephrase slightly the proof rules to be less confusing down the line 2024-12-09 18:53:38 +01:00
Michael Muré
c518c6657a Merge pull request #92 from ucan-wg/generator-cleanup
delegationtest: run go fmt, less noisy code
2024-12-05 15:36:23 +01:00
Michael Muré
78825f4f55 delegationtest: run go fmt, less noisy code 2024-12-05 15:30:23 +01:00
Michael Muré
7f1adbd945 Merge pull request #91 from ucan-wg/fix-everything
Fix everything
2024-12-04 20:09:28 +01:00
Michael Muré
0f59088d0b test: improvement on the generator, small fixes 2024-12-04 19:55:29 +01:00
Michael Muré
72e0f353e7 delegation: add a Bundle to carry around decoded, sealed and Cid 2024-12-04 19:54:46 +01:00
Michael Muré
d0d4ec3abe delegation,invocation: meta can be nil in the model, but not in the view 2024-12-04 19:53:05 +01:00
Michael Muré
bb24081b28 Merge pull request #90 from ucan-wg/fix/extended-field-names
Add support for extended field names in selectors
2024-12-03 11:43:38 +01:00
Fabio Bozzo
3688ccea01 fieldRegex to be more restrictive and consistent 2024-12-02 19:18:01 +01:00
Fabio Bozzo
e9105896d7 Merge branch 'main' into fix/extended-field-names
# Conflicts:
#	pkg/policy/selector/parsing_test.go
2024-12-02 19:05:19 +01:00
Fabio Bozzo
15751c7362 regex to be more restrictive and consistent 2024-12-02 18:30:41 +01:00
Michael Muré
d52218fa5a Merge pull request #88 from ucan-wg/feat/secretbox-meta-encryption
feat(meta): secretbox encryption in place of aes-gcm
2024-12-02 17:34:29 +01:00
Fabio Bozzo
64d3024dec remove aes comparison and add ciphertext overhead comments 2024-12-02 17:31:19 +01:00
Michael Muré
78d37d92ef Merge pull request #89 from ucan-wg/fix/prevent-int-overflow
fix: prevent overflow of int values
2024-12-02 14:41:12 +01:00
Fabio Bozzo
da806b1bc5 remove TODO comment 2024-12-02 14:32:15 +01:00
Fabio Bozzo
311b942a6d validate invocation token args 2024-12-02 14:22:42 +01:00
Fabio Bozzo
56eab758ed move args int validation to their creation 2024-12-02 12:24:06 +01:00
Fabio Bozzo
105323b989 moved unit test 2024-12-02 12:13:56 +01:00
Fabio Bozzo
5b816ccc62 streamline int overflow check for token timestamps 2024-12-02 12:06:06 +01:00
Fabio Bozzo
28272e6900 move int validation to where a error can be returned 2024-12-02 11:59:16 +01:00
Fabio Bozzo
a854389e32 validate token timestamps integer limits 2024-12-02 11:37:03 +01:00
Fabio Bozzo
117a75e2c4 cleanup comments 2024-11-29 19:36:00 +01:00
Fabio Bozzo
a25bfbaf45 fix: extended field names 2024-11-29 19:32:31 +01:00
Fabio Bozzo
bff482f73b add constants.go 2024-11-29 13:04:14 +01:00
Fabio Bozzo
ff79bbb443 go fmt 2024-11-29 13:03:48 +01:00
Fabio Bozzo
3997a86184 fix: prevent overflow of int values 2024-11-29 13:00:00 +01:00
Fabio Bozzo
200d6a8ae2 benchmarks vs aes-gcm 2024-11-28 17:17:10 +01:00
Fabio Bozzo
0349e7e463 feat(meta): secretbox encryption in place of aes-gcm 2024-11-28 16:16:04 +01:00
Steve Moyer
dff52f80c4 Merge pull request #87 from ucan-wg/chore-eliminate-jen-dependency
chore(delegationtest): eliminate dependency on dave/jennifer/jen package
2024-11-28 06:06:39 -05:00
Steve Moyer
5b7a63a2c6 style(delegationtest/generator): adopt suggestions from PR 2024-11-28 05:37:05 -05:00
Steve Moyer
66675f7030 build: tidy Go module files 2024-11-27 15:06:32 -05:00
Steve Moyer
7e54be49e1 chore(delegationtest): eliminate dependency on dave/jennifer/jen package
Resolves #68
2024-11-27 15:01:16 -05:00
Steve Moyer
15535d3474 Merge pull request #86 from ucan-wg/test/invocation-verifies-args-vs-pols
test(invocation): verify arguments versus aggregated policies
2024-11-27 14:56:05 -05:00
Steve Moyer
170e597e71 feat(args): export fluent builder 2024-11-27 12:05:00 -05:00
Steve Moyer
ce1a4b6e32 test(invocation): verify arguments versus aggregated policies 2024-11-27 10:20:40 -05:00
Michael Muré
d1d047cd9e Merge pull request #85 from ucan-wg/container-accessor
container: add a way to check for a single invocation, also iterator
2024-11-27 16:13:29 +01:00
Michael Muré
3680637090 container: add a way to check for a single invocation, also iterator 2024-11-27 16:13:15 +01:00
Michael Muré
1166a68e5c Merge pull request #84 from ucan-wg/args-minor
args: add simple value accessor
2024-11-27 12:26:19 +01:00
Michael Muré
ba4db9bce8 args: add simple value accessor 2024-11-27 12:23:10 +01:00
Michael Muré
20369dba49 Merge pull request #83 from ucan-wg/feat/invocation-witharguments
feat(invocation): add WithArguments Option to set all arguments at once
2024-11-27 10:10:19 +01:00
Steve Moyer
ade2c7f858 feat(invocation): add WithArguments Option to set all arguments at once 2024-11-26 14:39:10 -05:00
Michael Muré
943a318b26 Merge commit '60bdc8873b3d259ff297be4873a9623fcebb7f75' 2024-11-26 11:44:27 +01:00
Michael Muré
2d79cdc54e Merge pull request #82 from ucan-wg/cont-io
container: streamed and non-streamed IO, documentation
2024-11-26 11:37:46 +01:00
Michael Muré
820057e41e container: streamed and non-streamed IO, documentation 2024-11-21 15:49:29 +01:00
Michael Muré
ba0038b0ae Merge pull request #79 from ucan-wg/meta-args
meta,args: add missing Include, add iterator to use normal or Readonly the same way
2024-11-21 11:27:37 +01:00
Michael Muré
4a4b200312 Merge pull request #81 from ucan-wg/fix/66/allow-dash-in-policy-segment
fix(selector): allow dashes in selector names
2024-11-21 11:27:14 +01:00
Michael Muré
caae2f58bf meta,args: add missing Include, add iterator to use normal or Readonly the same way 2024-11-21 11:25:15 +01:00
Steve Moyer
ec627138cb fix(selector): allow dashes in selector names
This functionality will be further enhanced in #80.

Resolves #66
2024-11-20 12:37:29 -05:00
Michael Muré
4ec409edc6 Merge pull request #74 from ucan-wg/ci
ci: fix after merging to main
2024-11-20 16:11:07 +01:00
Michael Muré
c61fc8d8b3 ci: fix after merging to main 2024-11-20 16:07:49 +01:00
160 changed files with 5980 additions and 3643 deletions

20
.github/Repo.toml vendored Normal file
View File

@@ -0,0 +1,20 @@
[scopes]
ci = [".github/workflows"]
config = [".github", "go.mod", "go.sum"]
docs = ["Readme.md", "LICENSE.md"]
assets = ["assets"]
pkg = ["pkg"]
args = ["pkg/args"]
command = ["pkg/command"]
container = ["pkg/container"]
meta = ["pkg/meta"]
policy = ["pkg/policy"]
secretbox = ["pkg/secretbox"]
token = ["token"]
delegation = ["token/delegation"]
invocation = ["token/invocation"]
toolkit = ["toolkit"]
examples = ["toolkit/_example"]
client = ["toolkit/client"]
issuer = ["toolkit/issuer"]
server = ["toolkit/server"]

View File

@@ -2,7 +2,7 @@ name: go continuous benchmark
on: on:
push: push:
branches: branches:
- v1 - main
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@v4 - uses: actions/setup-go@v5
with: with:
go-version: "stable" go-version: "stable"
- name: Run benchmark - name: Run benchmark

View File

@@ -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@v2 - uses: actions/checkout@v4
- uses: actions/setup-go@v2 - uses: actions/setup-go@v5
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- name: Go information - name: Go information

View File

@@ -1,5 +1,5 @@
<div align="center"> <div align="center">
<a href="https://github.com/ucan-wg/go-ucan" target="_blank"> <a href="https://code.sonr.org/go/ucan" target="_blank">
<img src="https://raw.githubusercontent.com/ucan-wg/go-ucan/v1/assets/logo.png" alt="go-ucan Logo" height="250"></img> <img src="https://raw.githubusercontent.com/ucan-wg/go-ucan/v1/assets/logo.png" alt="go-ucan Logo" height="250"></img>
</a> </a>
@@ -7,19 +7,19 @@
<p> <p>
<img src="https://img.shields.io/badge/UCAN-v1.0.0--rc.1-blue" alt="UCAN v1.0.0-rc.1"> <img src="https://img.shields.io/badge/UCAN-v1.0.0--rc.1-blue" alt="UCAN v1.0.0-rc.1">
<a href="https://github.com/ucan-wg/go-ucan/tags"> <a href="https://code.sonr.org/go/ucan/tags">
<img alt="GitHub Tag" src="https://img.shields.io/github/v/tag/ucan-wg/go-ucan"> <img alt="GitHub Tag" src="https://img.shields.io/github/v/tag/ucan-wg/go-ucan">
</a> </a>
<a href="https://github.com/ucan-wg/go-ucan/actions?query="> <a href="https://code.sonr.org/go/ucan/actions?query=">
<img src="https://github.com/ucan-wg/go-ucan/actions/workflows/gotest.yml/badge.svg" alt="Build Status"> <img src="https://code.sonr.org/go/ucan/actions/workflows/gotest.yml/badge.svg" alt="Build Status">
</a> </a>
<a href="https://ucan-wg.github.io/go-ucan/dev/bench/"> <a href="https://ucan-wg.github.io/go-ucan/dev/bench/">
<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://code.sonr.org/go/ucan/blob/v1/LICENSE.md">
<img alt="Apache 2.0 + MIT License" src="https://img.shields.io/badge/License-Apache--2.0+MIT-green"> <img alt="Apache 2.0 OR MIT License" src="https://img.shields.io/badge/License-Apache--2.0_OR_MIT-green">
</a> </a>
<a href="https://pkg.go.dev/github.com/ucan-wg/go-ucan"> <a href="https://pkg.go.dev/code.sonr.org/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">
</a> </a>
<a href="https://discord.gg/JSyFG6XgVM"> <a href="https://discord.gg/JSyFG6XgVM">
@@ -41,6 +41,7 @@ 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)
@@ -53,11 +54,9 @@ Not implemented yet:
## Status ## Status
`go-ucan` currently support the required parts of the UCAN specification: the main specification, delegation and invocation. `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://code.sonr.org/go/did-it) for easy and extensible DID support.
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
@@ -74,4 +73,4 @@ Artwork by [Bruno Monts](https://www.instagram.com/bruno_monts). Thank you [Rene
## License ## License
This project is licensed under the double license [Apache 2.0 + MIT](https://github.com/ucan-wg/go-ucan/blob/v1/LICENSE.md). This project is licensed under the dual license [Apache 2.0 OR MIT](https://code.sonr.org/go/ucan/blob/v1/LICENSE.md).

View File

@@ -1,31 +0,0 @@
## 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",
```

View File

@@ -1,231 +0,0 @@
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
}

View File

@@ -1,108 +0,0 @@
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
}

View File

@@ -1,140 +0,0 @@
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)
}

View File

@@ -1,41 +0,0 @@
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)
}

View File

@@ -1,127 +0,0 @@
// 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,
}
}

View File

@@ -1,82 +0,0 @@
//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
}

View File

@@ -1,231 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,293 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,371 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,106 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,257 +0,0 @@
{
"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"
]
}
}
}

View File

@@ -1,163 +0,0 @@
//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)
}

View File

@@ -1,80 +0,0 @@
{
"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"
]
}
}
}

38
go.mod
View File

@@ -1,43 +1,35 @@
module github.com/ucan-wg/go-ucan module code.sonr.org/go/ucan
go 1.23 go 1.24.4
toolchain go1.24.5
require ( require (
github.com/dave/jennifer v1.7.1 code.sonr.org/go/did-it v1.0.0
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 github.com/avast/retry-go/v4 v4.6.1
github.com/ipfs/go-cid v0.4.1 github.com/ipfs/go-cid v0.5.0
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.1.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.10.0
gotest.tools/v3 v3.5.1 github.com/ucan-wg/go-varsig v1.0.0
golang.org/x/crypto v0.45.0
) )
require ( require (
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect
github.com/google/go-cmp v0.5.9 // indirect github.com/klauspost/cpuid/v2 v2.2.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/crypto v0.25.0 // indirect golang.org/x/sys v0.38.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

74
go.sum
View File

@@ -1,51 +1,34 @@
code.sonr.org/go/did-it v1.0.0 h1:Wh8igUkD6cuf0Ul3gawi27z2/M1YfdnQ/mD9gBq/2EU=
code.sonr.org/go/did-it v1.0.0/go.mod h1:PFK6ItvNyB2xbnVqipBbkN9BK1Sq+E2lf1YfOyCA0Og=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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.0.1 h1:7PltbUIQB7u/FfZ39+DGa/ShuMyJ5ilcvdfma9wOH6Y= github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8=
github.com/decred/dcrd/crypto/blake256 v1.0.1/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 h1:rpfIENRNNilwHwZeG5+P150SMrnNEcHYvcCuK6dPZSg= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0/go.mod h1:v57UDF4pDQJcEfFUCRop3lJL149eHGSe9Jvczhzjo/0= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40=
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.4.1 h1:A/T3qGvxi4kpKWWcPC/PgbvDA2bjVLO7n4UeVwnbs/s= github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg=
github.com/ipfs/go-cid v0.4.1/go.mod h1:uQHwDeX4c6CtyrFwdqyhpNcxVewur1M7l7fNU7LKwZk= github.com/ipfs/go-cid v0.5.0/go.mod h1:0L7vmeNXpQpUS9vt+yEARkJ8rOg43DF3iPgn4GIN0mk=
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.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=
github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=
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=
@@ -54,16 +37,14 @@ 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=
github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k= github.com/multiformats/go-multicodec v0.9.0/go.mod h1:L3QTQvMIaVBkXOXXtVmYE+LI16i14xuaojr/H7Ai54k=
github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U= github.com/multiformats/go-multihash v0.2.3 h1:7Lyc8XfX/IY2jWb/gI7JP+o7JEq9hOa7BFvVU9RSh+U=
github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM= github.com/multiformats/go-multihash v0.2.3/go.mod h1:dXgKXCXjBzdscBLk9JkjINiEsCKRVch90MdaGiKsvSM=
github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= github.com/multiformats/go-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI=
github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/multiformats/go-varint v0.1.0/go.mod h1:5KVAVXegtfmNQQm/lCY+ATvDzvJJhSkUlGQV9wgObdI=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4=
@@ -71,8 +52,6 @@ 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=
@@ -80,38 +59,29 @@ 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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.7.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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/ucan-wg/go-varsig v1.0.0/go.mod h1:Sakln6IPooDPH+ClQ0VvR09TuwUhHcfLqcPiPkMZGh0=
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.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
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.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
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=

View File

@@ -4,7 +4,9 @@
package args package args
import ( import (
"errors"
"fmt" "fmt"
"iter"
"sort" "sort"
"strings" "strings"
@@ -14,9 +16,12 @@ 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/literal" "code.sonr.org/go/ucan/pkg/policy/limits"
"code.sonr.org/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.
@@ -35,6 +40,16 @@ 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.
@@ -48,27 +63,51 @@ 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 *Args) { func (a *Args) Include(other Iterator) {
for _, key := range other.Keys { for key, value := range other.Iter() {
if _, ok := a.Values[key]; ok { if _, ok := a.Values[key]; ok {
// don't overwrite // don't overwrite
continue continue
} }
a.Values[key] = other.Values[key] a.Values[key] = value
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)
@@ -135,3 +174,14 @@ 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
}

View File

@@ -1,6 +1,7 @@
package args_test package args_test
import ( import (
"maps"
"sync" "sync"
"testing" "testing"
@@ -8,12 +9,14 @@ 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" "code.sonr.org/go/ucan/pkg/args"
"github.com/ucan-wg/go-ucan/pkg/policy/literal" "code.sonr.org/go/ucan/pkg/policy/limits"
"code.sonr.org/go/ucan/pkg/policy/literal"
) )
func TestArgs(t *testing.T) { func TestArgs(t *testing.T) {
@@ -133,23 +136,138 @@ 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
err error errSchema error
) )
func argsType() schema.Type { func argsType() schema.Type {
once.Do(func() { once.Do(func() {
ts, err = ipld.LoadSchemaBytes([]byte(argsSchema)) ts, errSchema = ipld.LoadSchemaBytes([]byte(argsSchema))
}) })
if err != nil { if errSchema != nil {
panic(err) panic(errSchema)
} }
return ts.TypeByName(argsName) return ts.TypeByName(argsName)

71
pkg/args/builder.go Normal file
View File

@@ -0,0 +1,71 @@
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
}

82
pkg/args/builder_test.go Normal file
View File

@@ -0,0 +1,82 @@
package args_test
import (
"testing"
"github.com/ipld/go-ipld-prime"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"code.sonr.org/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)
})
}

View File

@@ -1,17 +1,33 @@
package args package args
import "github.com/ipld/go-ipld-prime" import (
"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 *Args) bool { func (r ReadOnly) Equals(other ReadOnly) bool {
return r.args.Equals(other) return r.args.Equals(other.args)
} }
func (r ReadOnly) String() string { func (r ReadOnly) String() string {

View File

@@ -5,7 +5,7 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/pkg/command" "code.sonr.org/go/ucan/pkg/command"
) )
func TestTop(t *testing.T) { func TestTop(t *testing.T) {

View File

@@ -1,5 +1,7 @@
# 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

View File

@@ -0,0 +1 @@
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

View File

@@ -0,0 +1 @@
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.

View File

@@ -0,0 +1,21 @@
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

118
pkg/container/packaging.go Normal file
View File

@@ -0,0 +1,118 @@
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
}

View File

@@ -1,104 +1,51 @@
package container package container
import ( import (
"encoding/base64" "bytes"
"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/dagcbor" "github.com/ipld/go-ipld-prime/codec/cbor"
"github.com/ipld/go-ipld-prime/datamodel" "github.com/ipld/go-ipld-prime/datamodel"
"github.com/ucan-wg/go-ucan/token" "code.sonr.org/go/ucan/token"
"github.com/ucan-wg/go-ucan/token/delegation" "code.sonr.org/go/ucan/token/delegation"
"github.com/ucan-wg/go-ucan/token/invocation" "code.sonr.org/go/ucan/token/invocation"
) )
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]token.Token type Reader map[cid.Cid]bundle
// GetToken returns an arbitrary decoded token, from its CID. type bundle struct {
// If not found, ErrNotFound is returned. sealed []byte
func (ctn Reader) GetToken(cid cid.Cid) (token.Token, error) { token token.Token
tkn, ok := ctn[cid]
if !ok {
return nil, ErrNotFound
}
return tkn, nil
} }
// GetDelegation is the same as GetToken but only return a delegation.Token, with the right type. // FromBytes decodes a container from a []byte
func (ctn Reader) GetDelegation(cid cid.Cid) (*delegation.Token, error) { func FromBytes(data []byte) (Reader, error) {
tkn, err := ctn.GetToken(cid) return FromReader(bytes.NewReader(data))
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
} }
// GetAllDelegations returns all the delegation.Token in the container. // FromString decodes a container from a string
func (ctn Reader) GetAllDelegations() iter.Seq2[cid.Cid, *delegation.Token] { func FromString(s string) (Reader, error) {
return func(yield func(cid.Cid, *delegation.Token) bool) { return FromReader(strings.NewReader(s))
for c, t := range ctn {
if t, ok := t.(*delegation.Token); ok {
if !yield(c, t) {
return
}
}
}
}
} }
// GetInvocation returns the first found invocation.Token. // FromReader decodes a container from an io.Reader.
// If none are found, ErrNotFound is returned. func FromReader(r io.Reader) (Reader, error) {
func (ctn Reader) GetInvocation() (*invocation.Token, error) { payload, err := payloadDecoder(r)
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
} }
ctn := make(Reader) n, err := ipld.DecodeStreaming(payload, cbor.Decode)
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
} }
@@ -120,7 +67,7 @@ func FromCbor(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 != currentContainerVersion { if version != containerVersionTag {
return nil, fmt.Errorf("unsupported container version: %s", version) return nil, fmt.Errorf("unsupported container version: %s", version)
} }
@@ -147,8 +94,122 @@ func FromCbor(r io.Reader) (Reader, error) {
return ctn, nil return ctn, nil
} }
func FromCborBase64(r io.Reader) (Reader, error) { // GetToken returns an arbitrary decoded token, from its CID.
return FromCbor(base64.NewDecoder(base64.StdEncoding, r)) // If not found, ErrNotFound is returned.
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 {
@@ -156,6 +217,19 @@ func (ctn Reader) addToken(data []byte) error {
if err != nil { if err != nil {
return err return err
} }
ctn[c] = tkn ctn[c] = bundle{
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
}

View File

@@ -9,27 +9,36 @@ import (
"testing" "testing"
"time" "time"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/controller/did-key"
"code.sonr.org/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" "code.sonr.org/go/ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/command" "code.sonr.org/go/ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/policy" "code.sonr.org/go/ucan/pkg/policy/literal"
"github.com/ucan-wg/go-ucan/pkg/policy/literal" "code.sonr.org/go/ucan/token/delegation"
"github.com/ucan-wg/go-ucan/token/delegation"
) )
func TestContainerRoundTrip(t *testing.T) { func TestContainerRoundTrip(t *testing.T) {
for _, tc := range []struct { for _, tc := range []struct {
name string name string
writer func(ctn Writer, w io.Writer) error expectedHeader header
reader func(io.Reader) (Reader, error) writer any
}{ }{
{"car", Writer.ToCar, FromCar}, {"Bytes", headerRawBytes, Writer.ToBytes},
{"carBase64", Writer.ToCarBase64, FromCarBase64}, {"BytesWriter", headerRawBytes, Writer.ToBytesWriter},
{"cbor", Writer.ToCbor, FromCbor}, {"BytesGzipped", headerRawBytesGzip, Writer.ToBytesGzipped},
{"cborBase64", Writer.ToCborBase64, FromCborBase64}, {"BytesGzippedWriter", headerRawBytesGzip, Writer.ToBytesGzippedWriter},
{"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)
@@ -39,21 +48,53 @@ 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(c, data) writer.AddSealed(data)
tokens[c] = dlg tokens[c] = dlg
dataSize += len(data) dataSize += len(data)
} }
buf := bytes.NewBuffer(nil) var reader Reader
var serialLen int
err := tc.writer(writer, buf) switch fn := tc.writer.(type) {
require.NoError(t, err) case func(ctn Writer, w io.Writer) error:
buf := bytes.NewBuffer(nil)
err := fn(writer, buf)
require.NoError(t, err)
serialLen = buf.Len()
t.Logf("data size %d", dataSize) h, err := buf.ReadByte()
t.Logf("container overhead: %d%%, %d bytes", int(float32(buf.Len()-dataSize)/float32(dataSize)*100.0), buf.Len()-dataSize) require.NoError(t, err)
require.Equal(t, byte(tc.expectedHeader), h)
err = buf.UnreadByte()
require.NoError(t, err)
reader, err := tc.reader(bytes.NewReader(buf.Bytes())) reader, err = FromReader(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)
@@ -98,16 +139,18 @@ 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)
}{ }{
{"car", Writer.ToCar, FromCar}, {"Bytes", Writer.ToBytesWriter, FromReader},
{"carBase64", Writer.ToCarBase64, FromCarBase64}, {"BytesGzipped", Writer.ToBytesGzippedWriter, FromReader},
{"cbor", Writer.ToCbor, FromCbor}, {"Base64StdPadding", Writer.ToBase64StdPaddingWriter, FromReader},
{"cborBase64", Writer.ToCborBase64, FromCborBase64}, {"Base64StdPaddingGzipped", Writer.ToBase64StdPaddingGzippedWriter, FromReader},
{"Base64URL", Writer.ToBase64URLWriter, FromReader},
{"Base64URLGzip", Writer.ToBase64URLGzipWriter, FromReader},
} { } {
writer := NewWriter() writer := NewWriter()
for i := 0; i < 10; i++ { for i := 0; i < 10; i++ {
_, c, data := randToken() _, _, data := randToken()
writer.AddSealed(c, data) writer.AddSealed(data)
} }
buf := bytes.NewBuffer(nil) buf := bytes.NewBuffer(nil)
@@ -130,15 +173,12 @@ func BenchmarkContainerSerialisation(b *testing.B) {
} }
} }
func randDID() (crypto.PrivKey, did.DID) { func randDID() (ed25519.PrivateKey, did.DID) {
privKey, _, err := crypto.GenerateEd25519Key(rand.Reader) _, privKey, err := ed25519.GenerateKeyPair()
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
} }
@@ -160,13 +200,12 @@ 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.New(iss, aud, cmd, pol, opts...) t, err := delegation.Root(iss, aud, cmd, pol, opts...)
if err != nil { if err != nil {
panic(err) panic(err)
} }
@@ -182,21 +221,20 @@ 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++ {
_, c, data := randToken() _, _, data := randToken()
writer.AddSealed(c, data) writer.AddSealed(data)
} }
buf := bytes.NewBuffer(nil) data, err := writer.ToBytes()
err := writer.ToCbor(buf)
require.NoError(f, err) require.NoError(f, err)
f.Add(buf.Bytes()) f.Add(data)
} }
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
_, _ = FromCbor(bytes.NewReader(data)) _, _ = FromBytes(data)
if time.Since(start) > 100*time.Millisecond { if time.Since(start) > 100*time.Millisecond {
panic("too long") panic("too long")

View File

@@ -1,65 +1,145 @@
package container package container
import ( import (
"encoding/base64" "bytes"
"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/dagcbor" "github.com/ipld/go-ipld-prime/codec/cbor"
"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[cid.Cid][]byte type Writer map[string]struct{}
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(cid cid.Cid, data []byte) { func (ctn Writer) AddSealed(data []byte) {
ctn[cid] = data ctn[string(data)] = struct{}{}
} }
func (ctn Writer) ToCar(w io.Writer) error { // ToBytes encode the container into raw bytes.
return writeCar(w, nil, func(yield func(carBlock, error) bool) { func (ctn Writer) ToBytes() ([]byte, error) {
for c, bytes := range ctn { return ctn.toBytes(headerRawBytes)
if !yield(carBlock{c: c, data: bytes}, nil) {
return
}
}
})
} }
func (ctn Writer) ToCarBase64(w io.Writer) error { // ToBytesWriter is the same as ToBytes, but with an io.Writer.
w2 := base64.NewEncoder(base64.StdEncoding, w) func (ctn Writer) ToBytesWriter(w io.Writer) error {
defer w2.Close() return ctn.toWriter(headerRawBytes, w)
return ctn.ToCar(w2)
} }
func (ctn Writer) ToCbor(w io.Writer) error { // 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) { 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) { qp.MapEntry(ma, containerVersionTag, qp.List(int64(len(ctn)), func(la datamodel.ListAssembler) {
for _, bytes := range ctn { tokens := make([][]byte, 0, len(ctn))
qp.ListEntry(la, qp.Bytes(bytes)) for data := range ctn {
tokens = append(tokens, []byte(data))
}
slices.SortFunc(tokens, bytes.Compare)
for _, data := range tokens {
qp.ListEntry(la, qp.Bytes(data))
} }
})) }))
}) })
if err != nil { if err != nil {
return err return err
} }
return ipld.EncodeStreaming(w, node, dagcbor.Encode)
return ipld.EncodeStreaming(encoder, node, cbor.Encode)
} }
func (ctn Writer) ToCborBase64(w io.Writer) error { // ToReader convert a container Writer into a Reader.
w2 := base64.NewEncoder(base64.StdEncoding, w) // Most likely, you only want to use this in tests for convenience.
defer w2.Close() // This is not optimized and can panic.
return ctn.ToCbor(w2) func (ctn Writer) ToReader() Reader {
data, err := ctn.ToBytes()
if err != nil {
panic(err)
}
reader, err := FromBytes(data)
if err != nil {
panic(err)
}
return reader
} }

View File

@@ -0,0 +1,18 @@
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)
}

View File

@@ -1,132 +0,0 @@
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
}

View File

@@ -3,13 +3,15 @@ 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" "code.sonr.org/go/ucan/pkg/policy/literal"
"github.com/ucan-wg/go-ucan/pkg/policy/literal" "code.sonr.org/go/ucan/pkg/secretbox"
) )
var ErrNotFound = errors.New("key not found in meta") var ErrNotFound = errors.New("key not found in meta")
@@ -61,7 +63,7 @@ func (m *Meta) GetEncryptedString(key string, encryptionKey []byte) (string, err
return "", err return "", err
} }
decrypted, err := crypto.DecryptStringWithAESKey(v, encryptionKey) decrypted, err := secretbox.DecryptStringWithKey(v, encryptionKey)
if err != nil { if err != nil {
return "", err return "", err
} }
@@ -109,7 +111,7 @@ func (m *Meta) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, erro
return nil, err return nil, err
} }
decrypted, err := crypto.DecryptStringWithAESKey(v, encryptionKey) decrypted, err := secretbox.DecryptStringWithKey(v, encryptionKey)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -119,7 +121,6 @@ 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 {
@@ -149,18 +150,19 @@ 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 = crypto.EncryptWithAESKey([]byte(val), encryptionKey) encrypted, err = secretbox.EncryptWithKey([]byte(val), encryptionKey)
if err != nil { if err != nil {
return err return err
} }
case []byte: case []byte:
encrypted, err = crypto.EncryptWithAESKey(val, encryptionKey) encrypted, err = secretbox.EncryptWithKey(val, encryptionKey)
if err != nil { if err != nil {
return err return err
} }
@@ -171,6 +173,41 @@ 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) {
@@ -188,6 +225,8 @@ 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("{")
@@ -209,5 +248,18 @@ 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{m: m} return ReadOnly{meta: 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
} }

View File

@@ -2,11 +2,14 @@ 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" "code.sonr.org/go/ucan/pkg/meta"
) )
func TestMeta_Add(t *testing.T) { func TestMeta_Add(t *testing.T) {
@@ -75,3 +78,53 @@ 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()))
}

View File

@@ -1,50 +1,64 @@
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 {
m *Meta meta *Meta
} }
func (r ReadOnly) GetBool(key string) (bool, error) { func (r ReadOnly) GetBool(key string) (bool, error) {
return r.m.GetBool(key) return r.meta.GetBool(key)
} }
func (r ReadOnly) GetString(key string) (string, error) { func (r ReadOnly) GetString(key string) (string, error) {
return r.m.GetString(key) return r.meta.GetString(key)
} }
func (r ReadOnly) GetEncryptedString(key string, encryptionKey []byte) (string, error) { func (r ReadOnly) GetEncryptedString(key string, encryptionKey []byte) (string, error) {
return r.m.GetEncryptedString(key, encryptionKey) return r.meta.GetEncryptedString(key, encryptionKey)
} }
func (r ReadOnly) GetInt64(key string) (int64, error) { func (r ReadOnly) GetInt64(key string) (int64, error) {
return r.m.GetInt64(key) return r.meta.GetInt64(key)
} }
func (r ReadOnly) GetFloat64(key string) (float64, error) { func (r ReadOnly) GetFloat64(key string) (float64, error) {
return r.m.GetFloat64(key) return r.meta.GetFloat64(key)
} }
func (r ReadOnly) GetBytes(key string) ([]byte, error) { func (r ReadOnly) GetBytes(key string) ([]byte, error) {
return r.m.GetBytes(key) return r.meta.GetBytes(key)
} }
func (r ReadOnly) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, error) { func (r ReadOnly) GetEncryptedBytes(key string, encryptionKey []byte) ([]byte, error) {
return r.m.GetEncryptedBytes(key, encryptionKey) return r.meta.GetEncryptedBytes(key, encryptionKey)
} }
func (r ReadOnly) GetNode(key string) (ipld.Node, error) { func (r ReadOnly) GetNode(key string) (ipld.Node, error) {
return r.m.GetNode(key) return r.meta.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.m.Equals(other.m) return r.meta.Equals(other.meta)
} }
func (r ReadOnly) String() string { func (r ReadOnly) String() string {
return r.m.String() return r.meta.String()
}
func (r ReadOnly) WriteableClone() *Meta {
return r.meta.Clone()
} }

View File

@@ -9,10 +9,15 @@ 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/selector" "code.sonr.org/go/ucan/pkg/policy/limits"
"code.sonr.org/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)
} }
@@ -76,7 +81,7 @@ func statementFromIPLD(path string, node datamodel.Node) (Statement, error) {
} }
case 3: case 3:
switch op { switch op {
case KindEqual, KindLessThan, KindLessThanOrEqual, KindGreaterThan, KindGreaterThanOrEqual: case KindEqual, KindNotEqual, KindLessThan, KindLessThanOrEqual, KindGreaterThan, KindGreaterThanOrEqual:
sel, err := arg2AsSelector(op) sel, err := arg2AsSelector(op)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -21,10 +21,31 @@ 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)

49
pkg/policy/limits/int.go Normal file
View File

@@ -0,0 +1,49 @@
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
}

View File

@@ -0,0 +1,82 @@
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)
}
})
}
}

View File

@@ -12,6 +12,8 @@ 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"
"code.sonr.org/go/ucan/pkg/policy/limits"
) )
var Bool = basicnode.NewBool var Bool = basicnode.NewBool
@@ -58,8 +60,6 @@ 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,7 +67,11 @@ 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:
return basicnode.NewInt(int64(val)), nil i := int64(val)
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:
@@ -75,6 +79,9 @@ 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
@@ -85,6 +92,9 @@ 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
@@ -168,9 +178,17 @@ 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:
return qp.Int(rv.Int()) i := 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:
return qp.Int(int64(rv.Uint())) u := 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:

View File

@@ -8,6 +8,8 @@ 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"
"code.sonr.org/go/ucan/pkg/policy/limits"
) )
func TestList(t *testing.T) { func TestList(t *testing.T) {
@@ -214,7 +216,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")))
v, err = Any(data["func"]) _, err = Any(data["func"])
require.Error(t, err) require.Error(t, err)
} }
@@ -254,6 +256,56 @@ 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)

View File

@@ -3,6 +3,7 @@ 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"
@@ -81,6 +82,17 @@ 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)
@@ -249,10 +261,22 @@ 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))
} }
@@ -265,6 +289,11 @@ 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))
} }

View File

@@ -7,19 +7,16 @@ 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" "code.sonr.org/go/ucan/pkg/policy/literal"
) )
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("string", func(t *testing.T) { t.Run("eq 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")))
@@ -38,7 +35,7 @@ func TestMatch(t *testing.T) {
require.Equal(t, pol[0], leaf) require.Equal(t, pol[0], leaf)
}) })
t.Run("int", func(t *testing.T) { t.Run("eq 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)))
@@ -57,7 +54,7 @@ func TestMatch(t *testing.T) {
require.Equal(t, pol[0], leaf) require.Equal(t, pol[0], leaf)
}) })
t.Run("float", func(t *testing.T) { t.Run("eq 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)))
@@ -76,7 +73,7 @@ func TestMatch(t *testing.T) {
require.Equal(t, pol[0], leaf) require.Equal(t, pol[0], leaf)
}) })
t.Run("IPLD Link", func(t *testing.T) { t.Run("eq 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")}
@@ -98,7 +95,7 @@ func TestMatch(t *testing.T) {
require.Equal(t, pol[0], leaf) require.Equal(t, pol[0], leaf)
}) })
t.Run("string in map", func(t *testing.T) { t.Run("eq string in map", func(t *testing.T) {
nd, _ := literal.Map(map[string]any{ nd, _ := literal.Map(map[string]any{
"foo": "bar", "foo": "bar",
}) })
@@ -124,7 +121,7 @@ func TestMatch(t *testing.T) {
require.Equal(t, pol[0], leaf) require.Equal(t, pol[0], leaf)
}) })
t.Run("string in list", func(t *testing.T) { t.Run("eq 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")))
@@ -137,6 +134,132 @@ 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) {
@@ -248,20 +371,61 @@ 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.Bool(false) nd, _ := literal.Map(map[string]any{
"foo": false,
})
pol := MustConstruct(Not(Equal(".", literal.Bool(true)))) pol := MustConstruct(Not(Equal(".foo", 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(".", literal.Bool(false)))) pol = MustConstruct(Not(Equal(".foo", 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) {
@@ -485,6 +649,7 @@ 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"}`))
@@ -904,55 +1069,3 @@ 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)
})
}

View File

@@ -9,11 +9,12 @@ import (
"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"
selpkg "github.com/ucan-wg/go-ucan/pkg/policy/selector" selpkg "code.sonr.org/go/ucan/pkg/policy/selector"
) )
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
@@ -87,6 +88,13 @@ 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)
@@ -125,7 +133,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 {
@@ -149,7 +157,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 {
@@ -208,7 +216,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.Kind(), n.selector, strings.ReplaceAll(child, "\n", "\n ")) return fmt.Sprintf("[\"%s\", \"%s\",\n %s\n]", n.Kind(), n.selector, strings.ReplaceAll(child, "\n", "\n "))
} }
func All(selector string, cstor Constructor) Constructor { func All(selector string, cstor Constructor) Constructor {

View File

@@ -6,8 +6,8 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/pkg/policy" "code.sonr.org/go/ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/policy/literal" "code.sonr.org/go/ucan/pkg/policy/literal"
) )
func ExamplePolicy() { func ExamplePolicy() {
@@ -28,12 +28,14 @@ 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"]
// ] // ]]
// ]
// ] // ]
} }
@@ -59,12 +61,14 @@ 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"]
// ] // ]]
// ]
// ] // ]
} }

View File

@@ -0,0 +1,67 @@
package policytest
import (
"github.com/ipld/go-ipld-prime"
"code.sonr.org/go/ucan/pkg/args"
"code.sonr.org/go/ucan/pkg/policy"
"code.sonr.org/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
}

View File

@@ -0,0 +1,32 @@
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)
})
}

View File

@@ -6,12 +6,24 @@ import (
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
"code.sonr.org/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) {
@@ -56,6 +68,9 @@ 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"]
@@ -77,6 +92,9 @@ 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] == "" {
@@ -86,6 +104,9 @@ 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[:]})
@@ -152,37 +173,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}
} }

View File

@@ -1,10 +1,13 @@
package selector package selector
import ( import (
"fmt"
"math" "math"
"testing" "testing"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"code.sonr.org/go/ucan/pkg/policy/limits"
) )
func TestParse(t *testing.T) { func TestParse(t *testing.T) {
@@ -30,6 +33,29 @@ 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) {
@@ -549,4 +575,67 @@ 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)
})
} }

View File

@@ -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,19 +266,32 @@ 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:
start = length + slice[0] // Check for potential overflow before adding
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:
end = length + slice[1] // Check for potential overflow before adding
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
@@ -286,11 +299,14 @@ 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 start, end return
} }
func kindString(n datamodel.Node) string { func kindString(n datamodel.Node) string {
@@ -300,27 +316,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}
} }

View File

@@ -2,6 +2,7 @@ package selector
import ( import (
"errors" "errors"
"math"
"strings" "strings"
"testing" "testing"
@@ -132,7 +133,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) {
@@ -350,9 +351,63 @@ 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)
})
}
}

View File

@@ -9,7 +9,7 @@ import (
basicnode "github.com/ipld/go-ipld-prime/node/basic" basicnode "github.com/ipld/go-ipld-prime/node/basic"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"github.com/ucan-wg/go-ucan/pkg/policy/selector" "code.sonr.org/go/ucan/pkg/policy/selector"
) )
// TestSupported Forms runs tests against the Selector according to the // TestSupported Forms runs tests against the Selector according to the

View File

@@ -0,0 +1,90 @@
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
}

View File

@@ -1,4 +1,4 @@
package crypto package secretbox
import ( import (
"bytes" "bytes"
@@ -8,10 +8,10 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestAESEncryption(t *testing.T) { func TestSecretBoxEncryption(t *testing.T) {
t.Parallel() t.Parallel()
key := make([]byte, 32) // generated random 32-byte key key := make([]byte, keySize) // generate 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 TestAESEncryption(t *testing.T) {
{ {
name: "invalid key size", name: "invalid key size",
data: []byte("hello world"), data: []byte("hello world"),
key: make([]byte, 31), key: make([]byte, 16), // Only 32 bytes allowed now
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, 32), key: make([]byte, keySize),
wantErr: ErrZeroKey, wantErr: ErrZeroKey,
}, },
} }
@@ -56,24 +56,22 @@ func TestAESEncryption(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 := EncryptWithAESKey(tt.data, tt.key) encrypted, err := EncryptWithKey(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)
decrypted, err := DecryptStringWithAESKey(encrypted, tt.key) // Verify encrypted data is different and includes nonce
require.NoError(t, err) require.Greater(t, len(encrypted), 24) // At least nonce size
if len(tt.data) > 0 {
if tt.key == nil { require.NotEqual(t, tt.data, encrypted[24:]) // Ignore nonce prefix
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))
} }
decrypted, err := DecryptStringWithKey(encrypted, tt.key)
require.NoError(t, err)
require.True(t, bytes.Equal(tt.data, decrypted))
}) })
} }
} }
@@ -81,10 +79,15 @@ func TestAESEncryption(t *testing.T) {
func TestDecryptionErrors(t *testing.T) { func TestDecryptionErrors(t *testing.T) {
t.Parallel() t.Parallel()
key := make([]byte, 32) key := make([]byte, keySize)
_, 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
@@ -93,19 +96,25 @@ func TestDecryptionErrors(t *testing.T) {
}{ }{
{ {
name: "short ciphertext", name: "short ciphertext",
data: []byte("short"), data: make([]byte, 23), // Less than nonce size
key: key, key: key,
errMsg: "ciphertext too short", errMsg: "ciphertext too short",
}, },
{ {
name: "invalid ciphertext", name: "invalid ciphertext",
data: make([]byte, 16), // just nonce size data: make([]byte, 24), // Just nonce size
key: key, key: key,
errMsg: "message authentication failed", errMsg: "decryption failed",
},
{
name: "tampered ciphertext",
data: tamperWithBytes(encrypted),
key: key,
errMsg: "decryption failed",
}, },
{ {
name: "missing key", name: "missing key",
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"), data: encrypted,
key: nil, key: nil,
errMsg: "encryption key is required", errMsg: "encryption key is required",
}, },
@@ -116,9 +125,20 @@ 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 := DecryptStringWithAESKey(tt.data, tt.key) _, err := DecryptStringWithKey(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
}

View File

@@ -10,16 +10,19 @@ 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/ucan-wg/go-ucan/did" "code.sonr.org/go/did-it"
"github.com/ucan-wg/go-ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/meta" "code.sonr.org/go/ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/policy" "code.sonr.org/go/ucan/pkg/meta"
"github.com/ucan-wg/go-ucan/token/internal/nonce" "code.sonr.org/go/ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/token/internal/parse" "code.sonr.org/go/ucan/token/internal/nonce"
"code.sonr.org/go/ucan/token/internal/parse"
) )
// Token is an immutable type that holds the fields of a UCAN delegation. // Token is an immutable type that holds the fields of a UCAN delegation.
@@ -44,16 +47,15 @@ type Token struct {
expiration *time.Time expiration *time.Time
} }
// New creates a validated Token from the provided parameters and options. // New creates a validated delegation Token from the provided parameters and options.
// This is typically used to delegate a given power to another agent.
// //
// When creating a delegated token, the Issuer's (iss) DID is assembled // You can read it as "(issuer) allows (audience) to perform (cmd+pol) on (subject)".
// using the public key associated with the private key sent as the first func New(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, sub did.DID, opts ...Option) (*Token, error) {
// 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: did.Undef, subject: sub,
command: cmd, command: cmd,
policy: pol, policy: pol,
meta: meta.NewMeta(), meta: meta.NewMeta(),
@@ -81,16 +83,27 @@ func New(iss, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Optio
return tkn, nil return tkn, nil
} }
// Root creates a validated UCAN delegation Token from the provided // Root creates a validated UCAN delegation Token from the provided parameters and options.
// parameters and options. // This is typically used to create and give power to an agent.
// //
// When creating a root token, both the Issuer's (iss) and Subject's // You can read it as "(issuer) allows (audience) to perform (cmd+pol) on itself".
// (sub) DIDs are assembled from the public key associated with the func Root(iss did.DID, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) {
// private key passed as the first argument. return New(iss, aud, cmd, pol, iss, opts...)
func Root(iss, aud did.DID, cmd command.Command, pol policy.Policy, opts ...Option) (*Token, error) { }
opts = append(opts, WithSubject(iss))
return New(iss, aud, cmd, pol, opts...) // Powerline creates a validated UCAN delegation Token from the provided parameters and options.
//
// 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.
@@ -142,6 +155,16 @@ 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 {
@@ -160,11 +183,37 @@ 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.Defined() { if id == nil {
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()))
} }
} }
@@ -214,9 +263,19 @@ 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 = parse.OptionalTimestamp(m.Nbf) tkn.notBefore, err = parse.OptionalTimestamp(m.Nbf)
tkn.expiration = parse.OptionalTimestamp(m.Exp) if err != nil {
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

View File

@@ -1,64 +1,47 @@
package delegation_test package delegation_test
import ( import (
_ "embed"
"encoding/base64" "encoding/base64"
"testing" "testing"
"time" "time"
"code.sonr.org/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" "code.sonr.org/go/ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/command" "code.sonr.org/go/ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/policy" "code.sonr.org/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",
".status", ["like", ".email", "*@example.com"]
"draft" ],
], ["any", ".tags",
[ ["or", [
"all", ["==", ".", "news"],
".reviewer", ["==", ".", "press"]
[ ]]
"like", ]
".email",
"*@example.com"
]
],
[
"any",
".tags",
[
"or",
[
[
"==",
".",
"news"
],
[
"==",
".",
"press"
]
]
]
]
] ]
` `
newCID = "zdpuAwa4qv3ncMDPeDoqVxjZy3JoyWsbqUzm94rdA1AvRFkkw"
rootCID = "zdpuAkgGmUp5JrXvehGuuw9JA8DLQKDaxtK3R8brDQQVC2i5X"
aesKey = "xQklMmNTnVrmaPBq/0pwV5fEwuv/iClF5HWak9MsgI8=" aesKey = "xQklMmNTnVrmaPBq/0pwV5fEwuv/iClF5HWak9MsgI8="
) )
@@ -75,24 +58,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, tkn, err := delegation.New(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol, didtest.PersonaCarol.DID(),
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)
golden.Assert(t, string(data), "new.dagjson") require.Equal(t, newDagJson, data)
}) })
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),
@@ -101,10 +84,31 @@ 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)
golden.Assert(t, string(data), "root.dagjson") require.Equal(t, rootDagJson, data)
})
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)
}) })
} }
@@ -153,7 +157,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.New(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol, tkn, err := delegation.Root(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)
@@ -191,7 +195,7 @@ func TestEncryptedMeta(t *testing.T) {
} }
// Create token with multiple encrypted values // Create token with multiple encrypted values
tkn, err := delegation.New(didtest.PersonaAlice.DID(), didtest.PersonaBob.DID(), cmd, pol, opts...) tkn, err := delegation.Root(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())

View File

@@ -1,21 +1,25 @@
package main package main
import ( import (
"bytes"
"fmt"
"go/format"
"os" "os"
"path/filepath" "path/filepath"
"slices" "slices"
"time" "time"
"github.com/dave/jennifer/jen" "code.sonr.org/go/did-it"
didkeyctl "code.sonr.org/go/did-it/controller/did-key"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/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" "code.sonr.org/go/ucan/pkg/command"
"github.com/ucan-wg/go-ucan/did/didtest" "code.sonr.org/go/ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/command" "code.sonr.org/go/ucan/pkg/policy/policytest"
"github.com/ucan-wg/go-ucan/pkg/policy" "code.sonr.org/go/ucan/token/delegation"
"github.com/ucan-wg/go-ucan/token/delegation" "code.sonr.org/go/ucan/token/delegation/delegationtest"
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest"
) )
const ( const (
@@ -27,11 +31,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.PrivKey privKey crypto.PrivateKeySigningBytes // iss
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
} }
@@ -86,9 +90,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: policy.Policy{}, pol: policytest.EmptyPolicy,
sub: didtest.PersonaAlice.DID(),
opts: []delegation.Option{ opts: []delegation.Option{
delegation.WithSubject(didtest.PersonaAlice.DID()),
delegation.WithNonce(constantNonce), delegation.WithNonce(constantNonce),
}, },
} }
@@ -115,7 +119,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.opts = append(p.opts, delegation.WithSubject(didtest.PersonaBob.DID())) p.sub = 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
@@ -128,6 +132,9 @@ 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
@@ -151,12 +158,9 @@ 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(&params) vari.variant(&params)
issDID, err := did.FromPrivKey(params.privKey) issDID := didkeyctl.FromPrivateKey(params.privKey)
if err != nil {
return cid.Undef, err
}
tkn, err := delegation.New(issDID, params.aud, params.cmd, params.pol, params.opts...) tkn, err := delegation.New(issDID, params.aud, params.cmd, params.pol, params.sub, params.opts...)
if err != nil { if err != nil {
return cid.Undef, err return cid.Undef, err
} }
@@ -199,36 +203,71 @@ func (g *generator) createProofChain(name string, prf []cid.Cid) {
} }
func (g *generator) writeGoFile() error { func (g *generator) writeGoFile() error {
file := jen.NewFile("delegationtest") buf := bytes.NewBuffer(nil)
file.HeaderComment("Code generated by delegationtest - DO NOT EDIT.")
refs := map[cid.Cid]string{} Println := func(a ...any) { _, _ = fmt.Fprintln(buf, a...) }
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\"code.sonr.org/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"
file.Var().Defs( Println()
jen.Id(d.name+"CID").Op("=").Qual("github.com/ipfs/go-cid", "MustParse").Call(jen.Lit(d.id.String())), Println("var (")
jen.Id(d.name).Op("=").Id("mustGetDelegation").Call(jen.Id(d.name+"CID")), Printf("\t%sCID = cid.MustParse(\"%s\")\n", d.name, d.id.String())
) Printf("\t%sSealed = mustGetBundle(%s).Sealed\n", d.name, d.name+"CID")
file.Line() Printf("\t%sBundle = mustGetBundle(%s)\n", d.name, d.name+"CID")
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 {
g := jen.CustomFunc(jen.Options{ Println()
Multi: true, Printf("var %s = []cid.Cid{\n", c.name)
Separator: ",",
Close: "\n",
}, func(g *jen.Group) {
slices.Reverse(c.prf)
for _, p := range c.prf {
g.Id(refs[p])
}
})
file.Var().Id(c.name).Op("=").Index().Qual("github.com/ipfs/go-cid", "Cid").Values(g) for _, d := range slices.Backward(c.prf) {
file.Line() Printf("\t%s,\n", refs[d])
}
Println("}")
} }
return file.Save("../token_gen.go") out, err := format.Source(buf.Bytes())
if err != nil {
return err
}
return os.WriteFile("../token_gen.go", out, 0666)
} }

View File

@@ -1,7 +1,7 @@
package main package main
import ( import (
"github.com/ucan-wg/go-ucan/did/didtest" "code.sonr.org/go/did-it/didtest"
) )
func main() { func main() {

View File

@@ -5,10 +5,11 @@ import (
"path/filepath" "path/filepath"
"sync" "sync"
_ "code.sonr.org/go/did-it/verifiers/did-key"
"github.com/ipfs/go-cid" "github.com/ipfs/go-cid"
"github.com/ucan-wg/go-ucan/pkg/command" "code.sonr.org/go/ucan/pkg/command"
"github.com/ucan-wg/go-ucan/token/delegation" "code.sonr.org/go/ucan/token/delegation"
) )
var ( var (
@@ -18,12 +19,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- // NominalCommand is the command used for most test tokens and proof-chains.
// chains. Execution of this command is generally allowed in tests. // 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. Execution // AttenuatedCommand is a sub-command of the NominalCommand.
// of this command is generally allowed in tests. // Execution of this command is generally allowed in tests.
AttenuatedCommand = NominalCommand.Join("attenuated") AttenuatedCommand = NominalCommand.Join("attenuated")
) )
@@ -35,21 +36,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 {
tokens map[cid.Cid]*delegation.Token bundles map[cid.Cid]delegation.Bundle
} }
var ( var (
once sync.Once once sync.Once
ldr delegation.Loader ldr *DelegationLoader
) )
// 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() delegation.Loader { func GetDelegationLoader() *DelegationLoader {
once.Do(func() { once.Do(func() {
var err error var err error
ldr, err = loadDelegations() ldr, err = loadDelegations()
@@ -61,22 +62,21 @@ func GetDelegationLoader() delegation.Loader {
} }
// 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) {
tkn, ok := l.tokens[id] bundle, ok := l.bundles[id]
if !ok { if !ok {
return nil, delegation.ErrDelegationNotFound return nil, delegation.ErrDelegationNotFound
} }
return bundle.Decoded, nil
return tkn, nil
} }
func loadDelegations() (delegation.Loader, error) { func loadDelegations() (*DelegationLoader, error) {
dirEntries, err := fs.ReadDir(TokenDir) dirEntries, err := fs.ReadDir(TokenDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
tkns := make(map[cid.Cid]*delegation.Token, len(dirEntries)) bundles := make(map[cid.Cid]delegation.Bundle, 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() (delegation.Loader, error) {
return nil, err return nil, err
} }
tkns[id] = tkn bundles[id] = delegation.Bundle{Cid: id, Decoded: tkn, Sealed: data}
} }
return &delegationLoader{ return &DelegationLoader{
tokens: tkns, bundles: bundles,
}, nil }, nil
} }
@@ -103,10 +103,14 @@ func GetDelegation(id cid.Cid) (*delegation.Token, error) {
return GetDelegationLoader().GetDelegation(id) return GetDelegationLoader().GetDelegation(id)
} }
func mustGetDelegation(id cid.Cid) *delegation.Token { func CidToName(id cid.Cid) string {
tkn, err := GetDelegation(id) return cidToName[id]
if err != nil { }
panic(err)
} func mustGetBundle(id cid.Cid) delegation.Bundle {
return tkn bundle, ok := GetDelegationLoader().bundles[id]
if !ok {
panic(delegation.ErrDelegationNotFound)
}
return bundle
} }

View File

@@ -2,131 +2,274 @@
package delegationtest package delegationtest
import gocid "github.com/ipfs/go-cid" import (
"github.com/ipfs/go-cid"
var ( "code.sonr.org/go/ucan/token/delegation"
TokenAliceBobCID = gocid.MustParse("bafyreicidrwvmac5lvjypucgityrtjsknojraio7ujjli4r5eyby66wjzm")
TokenAliceBob = mustGetDelegation(TokenAliceBobCID)
) )
var ( var (
TokenBobCarolCID = gocid.MustParse("bafyreihxv2uhq43oxllzs2xfvxst7wtvvvl7pohb2chcz6hjvfv2ntea5u") TokenAliceBobCID = cid.MustParse("bafyreifa35rjstdm37cjudzs72ab22rnh5blny725khtapox63fnsj6pbe")
TokenBobCarol = mustGetDelegation(TokenBobCarolCID) TokenAliceBobSealed = mustGetBundle(TokenAliceBobCID).Sealed
TokenAliceBobBundle = mustGetBundle(TokenAliceBobCID)
TokenAliceBob = mustGetBundle(TokenAliceBobCID).Decoded
) )
var ( var (
TokenCarolDanCID = gocid.MustParse("bafyreihclsgiroazq3heqdswvj2cafwqbpboicq7immo65scl7ahktpsdq") TokenBobCarolCID = cid.MustParse("bafyreiaysaafvfplhjsjywaqfletlr2sziui3sekkczwp2srszdoezwc7u")
TokenCarolDan = mustGetDelegation(TokenCarolDanCID) TokenBobCarolSealed = mustGetBundle(TokenBobCarolCID).Sealed
TokenBobCarolBundle = mustGetBundle(TokenBobCarolCID)
TokenBobCarol = mustGetBundle(TokenBobCarolCID).Decoded
) )
var ( var (
TokenDanErinCID = gocid.MustParse("bafyreicja6ihewy64p3ake56xukotafjlkh4uqep2qhj52en46zzfwby3e") TokenCarolDanCID = cid.MustParse("bafyreibwpzxcvrere7g5riv3dhza4xibrvulxcvj2nwxu6ozp572o2sbfa")
TokenDanErin = mustGetDelegation(TokenDanErinCID) TokenCarolDanSealed = mustGetBundle(TokenCarolDanCID).Sealed
TokenCarolDanBundle = mustGetBundle(TokenCarolDanCID)
TokenCarolDan = mustGetBundle(TokenCarolDanCID).Decoded
) )
var ( var (
TokenErinFrankCID = gocid.MustParse("bafyreicjlx3lobxm6hl5s4htd4ydwkkqeiou6rft4rnvulfdyoew565vka") TokenDanErinCID = cid.MustParse("bafyreichxodkcokcbvomxhmlf3g3j3zokruxvca63itynf7ib3hsmi4hla")
TokenErinFrank = mustGetDelegation(TokenErinFrankCID) TokenDanErinSealed = mustGetBundle(TokenDanErinCID).Sealed
TokenDanErinBundle = mustGetBundle(TokenDanErinCID)
TokenDanErin = mustGetBundle(TokenDanErinCID).Decoded
) )
var ( var (
TokenCarolDan_InvalidExpandedCommandCID = gocid.MustParse("bafyreid3m3pk53gqgp5rlzqhvpedbwsqbidqlp4yz64vknwbzj7bxrmsr4") TokenErinFrankCID = cid.MustParse("bafyreigg434khwxqyfasnk63pi542uwtbrlpfd5sgjfrddqqcba3tardlu")
TokenCarolDan_InvalidExpandedCommand = mustGetDelegation(TokenCarolDan_InvalidExpandedCommandCID) TokenErinFrankSealed = mustGetBundle(TokenErinFrankCID).Sealed
TokenErinFrankBundle = mustGetBundle(TokenErinFrankCID)
TokenErinFrank = mustGetBundle(TokenErinFrankCID).Decoded
) )
var ( var (
TokenDanErin_InvalidExpandedCommandCID = gocid.MustParse("bafyreifn4sy5onwajx3kqvot5mib6m6xarzrqjozqbzgmzpmc5ox3g2uzm") TokenCarolDan_InvalidExpandedCommandCID = cid.MustParse("bafyreifxdve23izhlg7fhzqh32i6wxtw33rqidm3cb72pghxlovnensjwe")
TokenDanErin_InvalidExpandedCommand = mustGetDelegation(TokenDanErin_InvalidExpandedCommandCID) TokenCarolDan_InvalidExpandedCommandSealed = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID).Sealed
TokenCarolDan_InvalidExpandedCommandBundle = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID)
TokenCarolDan_InvalidExpandedCommand = mustGetBundle(TokenCarolDan_InvalidExpandedCommandCID).Decoded
) )
var ( var (
TokenErinFrank_InvalidExpandedCommandCID = gocid.MustParse("bafyreidmpgd36jznmq42bs34o4qi3fcbrsh4idkg6ejahudejzwb76fwxe") TokenDanErin_InvalidExpandedCommandCID = cid.MustParse("bafyreieblevkqh2xnbr4x5mosvwv7rboat7ip6ucqvefb2pomrb4qjg4wu")
TokenErinFrank_InvalidExpandedCommand = mustGetDelegation(TokenErinFrank_InvalidExpandedCommandCID) TokenDanErin_InvalidExpandedCommandSealed = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID).Sealed
TokenDanErin_InvalidExpandedCommandBundle = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID)
TokenDanErin_InvalidExpandedCommand = mustGetBundle(TokenDanErin_InvalidExpandedCommandCID).Decoded
) )
var ( var (
TokenCarolDan_ValidAttenuatedCommandCID = gocid.MustParse("bafyreiekhtm237vyapk3c6voeb5lnz54crebqdqi3x4wn4u4cbrrhzsqfe") TokenErinFrank_InvalidExpandedCommandCID = cid.MustParse("bafyreia2ckukamhpeqpcdhevr75ym66vqem432dx4dwbdrdkh63pke3r3i")
TokenCarolDan_ValidAttenuatedCommand = mustGetDelegation(TokenCarolDan_ValidAttenuatedCommandCID) TokenErinFrank_InvalidExpandedCommandSealed = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID).Sealed
TokenErinFrank_InvalidExpandedCommandBundle = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID)
TokenErinFrank_InvalidExpandedCommand = mustGetBundle(TokenErinFrank_InvalidExpandedCommandCID).Decoded
) )
var ( var (
TokenDanErin_ValidAttenuatedCommandCID = gocid.MustParse("bafyreicrvzqferyy7rgo75l5rn6r2nl7zyeexxjmu3dm4ff7rn2coblj4y") TokenCarolDan_ValidAttenuatedCommandCID = cid.MustParse("bafyreibftsq3mhjd5itg6jbos4doccwspvmnilpuduniv2el7mnl45z33y")
TokenDanErin_ValidAttenuatedCommand = mustGetDelegation(TokenDanErin_ValidAttenuatedCommandCID) TokenCarolDan_ValidAttenuatedCommandSealed = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID).Sealed
TokenCarolDan_ValidAttenuatedCommandBundle = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID)
TokenCarolDan_ValidAttenuatedCommand = mustGetBundle(TokenCarolDan_ValidAttenuatedCommandCID).Decoded
) )
var ( var (
TokenErinFrank_ValidAttenuatedCommandCID = gocid.MustParse("bafyreie6fhspk53kplcc2phla3e7z7fzldlbmmpuwk6nbow5q6s2zjmw2q") TokenDanErin_ValidAttenuatedCommandCID = cid.MustParse("bafyreig5gsifqn3afnyaoqtjhnud2w7tnrda57eiel3d45vbp47hjyayey")
TokenErinFrank_ValidAttenuatedCommand = mustGetDelegation(TokenErinFrank_ValidAttenuatedCommandCID) TokenDanErin_ValidAttenuatedCommandSealed = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID).Sealed
TokenDanErin_ValidAttenuatedCommandBundle = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID)
TokenDanErin_ValidAttenuatedCommand = mustGetBundle(TokenDanErin_ValidAttenuatedCommandCID).Decoded
) )
var ( var (
TokenCarolDan_InvalidSubjectCID = gocid.MustParse("bafyreifgksz6756if42tnc6rqsnbaa2u3fdrveo7ek44lnj2d64d5sw26u") TokenErinFrank_ValidAttenuatedCommandCID = cid.MustParse("bafyreiex2qyrw3xmye4fk5wdf6ukea3ojbokq77k6y566hupj6cicszi3e")
TokenCarolDan_InvalidSubject = mustGetDelegation(TokenCarolDan_InvalidSubjectCID) TokenErinFrank_ValidAttenuatedCommandSealed = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID).Sealed
TokenErinFrank_ValidAttenuatedCommandBundle = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID)
TokenErinFrank_ValidAttenuatedCommand = mustGetBundle(TokenErinFrank_ValidAttenuatedCommandCID).Decoded
) )
var ( var (
TokenDanErin_InvalidSubjectCID = gocid.MustParse("bafyreibdwew5nypsxrm4fq73wu6hw3lgwwiolj3bi33xdrbgcf3ogm6fty") TokenCarolDan_InvalidSubjectCID = cid.MustParse("bafyreie7snyknubaeh4ruig7wxvos54b7skmwsnegf6k23bsffs5btbiiq")
TokenDanErin_InvalidSubject = mustGetDelegation(TokenDanErin_InvalidSubjectCID) TokenCarolDan_InvalidSubjectSealed = mustGetBundle(TokenCarolDan_InvalidSubjectCID).Sealed
TokenCarolDan_InvalidSubjectBundle = mustGetBundle(TokenCarolDan_InvalidSubjectCID)
TokenCarolDan_InvalidSubject = mustGetBundle(TokenCarolDan_InvalidSubjectCID).Decoded
) )
var ( var (
TokenErinFrank_InvalidSubjectCID = gocid.MustParse("bafyreicr364mj3n7x4iyhcksxypelktcqkkw3ptg7ggxtqegw3p3mr6zc4") TokenDanErin_InvalidSubjectCID = cid.MustParse("bafyreicrfmlw4nqqk5t7j6r7ct3c7jlxeasyetb6fq3556vmtihcnbi54i")
TokenErinFrank_InvalidSubject = mustGetDelegation(TokenErinFrank_InvalidSubjectCID) TokenDanErin_InvalidSubjectSealed = mustGetBundle(TokenDanErin_InvalidSubjectCID).Sealed
TokenDanErin_InvalidSubjectBundle = mustGetBundle(TokenDanErin_InvalidSubjectCID)
TokenDanErin_InvalidSubject = mustGetBundle(TokenDanErin_InvalidSubjectCID).Decoded
) )
var ( var (
TokenCarolDan_InvalidExpiredCID = gocid.MustParse("bafyreigenypixaxvhzlry5rjnywvjyl4xvzlzxz2ui74uzys7qdhos4bbu") TokenErinFrank_InvalidSubjectCID = cid.MustParse("bafyreietx2sjsm72sbgjnosdnejwlqc5dadlolh2bjl6ifxdllslup7ecm")
TokenCarolDan_InvalidExpired = mustGetDelegation(TokenCarolDan_InvalidExpiredCID) TokenErinFrank_InvalidSubjectSealed = mustGetBundle(TokenErinFrank_InvalidSubjectCID).Sealed
TokenErinFrank_InvalidSubjectBundle = mustGetBundle(TokenErinFrank_InvalidSubjectCID)
TokenErinFrank_InvalidSubject = mustGetBundle(TokenErinFrank_InvalidSubjectCID).Decoded
) )
var ( var (
TokenDanErin_InvalidExpiredCID = gocid.MustParse("bafyreifvnfb7zqocpdysedcvjkb4y7tqfuziuqjhbbdoay4zg33pwpbzqi") TokenCarolDan_InvalidExpiredCID = cid.MustParse("bafyreifhdiem6r5fh3wwaa6uszqe5uashfwaastqjyvzssbiju4mravx3y")
TokenDanErin_InvalidExpired = mustGetDelegation(TokenDanErin_InvalidExpiredCID) TokenCarolDan_InvalidExpiredSealed = mustGetBundle(TokenCarolDan_InvalidExpiredCID).Sealed
TokenCarolDan_InvalidExpiredBundle = mustGetBundle(TokenCarolDan_InvalidExpiredCID)
TokenCarolDan_InvalidExpired = mustGetBundle(TokenCarolDan_InvalidExpiredCID).Decoded
) )
var ( var (
TokenErinFrank_InvalidExpiredCID = gocid.MustParse("bafyreicvydzt3obkqx7krmoi3zu4tlirlksibxfks5jc7vlvjxjamv2764") TokenDanErin_InvalidExpiredCID = cid.MustParse("bafyreiff7fqutstzz2ep5gzs2mqnt4rka7ezscrzpijyc4jjeopqm7oxxq")
TokenErinFrank_InvalidExpired = mustGetDelegation(TokenErinFrank_InvalidExpiredCID) TokenDanErin_InvalidExpiredSealed = mustGetBundle(TokenDanErin_InvalidExpiredCID).Sealed
TokenDanErin_InvalidExpiredBundle = mustGetBundle(TokenDanErin_InvalidExpiredCID)
TokenDanErin_InvalidExpired = mustGetBundle(TokenDanErin_InvalidExpiredCID).Decoded
) )
var ( var (
TokenCarolDan_InvalidInactiveCID = gocid.MustParse("bafyreicea5y2nvlitvxijkupeavtg23i7ktjk3uejnaquguurzptiabk4u") TokenErinFrank_InvalidExpiredCID = cid.MustParse("bafyreigxfwcynzsy4v4po36s3mz5nf6mg4kpptw6rpld3sc3c3mthq4noa")
TokenCarolDan_InvalidInactive = mustGetDelegation(TokenCarolDan_InvalidInactiveCID) TokenErinFrank_InvalidExpiredSealed = mustGetBundle(TokenErinFrank_InvalidExpiredCID).Sealed
TokenErinFrank_InvalidExpiredBundle = mustGetBundle(TokenErinFrank_InvalidExpiredCID)
TokenErinFrank_InvalidExpired = mustGetBundle(TokenErinFrank_InvalidExpiredCID).Decoded
) )
var ( var (
TokenDanErin_InvalidInactiveCID = gocid.MustParse("bafyreifsgqzkmxj2vexuts3z766mwcjreiisjg2jykyzf7tbj5sclutpvq") TokenCarolDan_InvalidInactiveCID = cid.MustParse("bafyreifw4j2mxg4bovuhihvnhqgln7m57lvajt6fza4x4jc7sncyh66wxm")
TokenDanErin_InvalidInactive = mustGetDelegation(TokenDanErin_InvalidInactiveCID) TokenCarolDan_InvalidInactiveSealed = mustGetBundle(TokenCarolDan_InvalidInactiveCID).Sealed
TokenCarolDan_InvalidInactiveBundle = mustGetBundle(TokenCarolDan_InvalidInactiveCID)
TokenCarolDan_InvalidInactive = mustGetBundle(TokenCarolDan_InvalidInactiveCID).Decoded
) )
var ( var (
TokenErinFrank_InvalidInactiveCID = gocid.MustParse("bafyreifbfegon24c6dndiqyktahzs65vhyasrygbw7nhsvojn6distsdre") TokenDanErin_InvalidInactiveCID = cid.MustParse("bafyreigrrfefw4wvjitiheonigeldnem2loo4yp5cghdskteunuxyozkiq")
TokenErinFrank_InvalidInactive = mustGetDelegation(TokenErinFrank_InvalidInactiveCID) TokenDanErin_InvalidInactiveSealed = mustGetBundle(TokenDanErin_InvalidInactiveCID).Sealed
TokenDanErin_InvalidInactiveBundle = mustGetBundle(TokenDanErin_InvalidInactiveCID)
TokenDanErin_InvalidInactive = mustGetBundle(TokenDanErin_InvalidInactiveCID).Decoded
) )
var ProofAliceBob = []gocid.Cid{ var (
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 = []gocid.Cid{ var ProofAliceBobCarol = []cid.Cid{
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDan = []gocid.Cid{ var ProofAliceBobCarolDan = []cid.Cid{
TokenCarolDanCID, TokenCarolDanCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErin = []gocid.Cid{ var ProofAliceBobCarolDanErin = []cid.Cid{
TokenDanErinCID, TokenDanErinCID,
TokenCarolDanCID, TokenCarolDanCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErinFrank = []gocid.Cid{ var ProofAliceBobCarolDanErinFrank = []cid.Cid{
TokenErinFrankCID, TokenErinFrankCID,
TokenDanErinCID, TokenDanErinCID,
TokenCarolDanCID, TokenCarolDanCID,
@@ -134,20 +277,20 @@ var ProofAliceBobCarolDanErinFrank = []gocid.Cid{
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDan_InvalidExpandedCommand = []gocid.Cid{ var ProofAliceBobCarolDan_InvalidExpandedCommand = []cid.Cid{
TokenCarolDan_InvalidExpandedCommandCID, TokenCarolDan_InvalidExpandedCommandCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErin_InvalidExpandedCommand = []gocid.Cid{ var ProofAliceBobCarolDanErin_InvalidExpandedCommand = []cid.Cid{
TokenDanErin_InvalidExpandedCommandCID, TokenDanErin_InvalidExpandedCommandCID,
TokenCarolDan_InvalidExpandedCommandCID, TokenCarolDan_InvalidExpandedCommandCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErinFrank_InvalidExpandedCommand = []gocid.Cid{ var ProofAliceBobCarolDanErinFrank_InvalidExpandedCommand = []cid.Cid{
TokenErinFrank_InvalidExpandedCommandCID, TokenErinFrank_InvalidExpandedCommandCID,
TokenDanErin_InvalidExpandedCommandCID, TokenDanErin_InvalidExpandedCommandCID,
TokenCarolDan_InvalidExpandedCommandCID, TokenCarolDan_InvalidExpandedCommandCID,
@@ -155,20 +298,20 @@ var ProofAliceBobCarolDanErinFrank_InvalidExpandedCommand = []gocid.Cid{
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDan_ValidAttenuatedCommand = []gocid.Cid{ var ProofAliceBobCarolDan_ValidAttenuatedCommand = []cid.Cid{
TokenCarolDan_ValidAttenuatedCommandCID, TokenCarolDan_ValidAttenuatedCommandCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErin_ValidAttenuatedCommand = []gocid.Cid{ var ProofAliceBobCarolDanErin_ValidAttenuatedCommand = []cid.Cid{
TokenDanErin_ValidAttenuatedCommandCID, TokenDanErin_ValidAttenuatedCommandCID,
TokenCarolDan_ValidAttenuatedCommandCID, TokenCarolDan_ValidAttenuatedCommandCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErinFrank_ValidAttenuatedCommand = []gocid.Cid{ var ProofAliceBobCarolDanErinFrank_ValidAttenuatedCommand = []cid.Cid{
TokenErinFrank_ValidAttenuatedCommandCID, TokenErinFrank_ValidAttenuatedCommandCID,
TokenDanErin_ValidAttenuatedCommandCID, TokenDanErin_ValidAttenuatedCommandCID,
TokenCarolDan_ValidAttenuatedCommandCID, TokenCarolDan_ValidAttenuatedCommandCID,
@@ -176,20 +319,20 @@ var ProofAliceBobCarolDanErinFrank_ValidAttenuatedCommand = []gocid.Cid{
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDan_InvalidSubject = []gocid.Cid{ var ProofAliceBobCarolDan_InvalidSubject = []cid.Cid{
TokenCarolDan_InvalidSubjectCID, TokenCarolDan_InvalidSubjectCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErin_InvalidSubject = []gocid.Cid{ var ProofAliceBobCarolDanErin_InvalidSubject = []cid.Cid{
TokenDanErin_InvalidSubjectCID, TokenDanErin_InvalidSubjectCID,
TokenCarolDan_InvalidSubjectCID, TokenCarolDan_InvalidSubjectCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErinFrank_InvalidSubject = []gocid.Cid{ var ProofAliceBobCarolDanErinFrank_InvalidSubject = []cid.Cid{
TokenErinFrank_InvalidSubjectCID, TokenErinFrank_InvalidSubjectCID,
TokenDanErin_InvalidSubjectCID, TokenDanErin_InvalidSubjectCID,
TokenCarolDan_InvalidSubjectCID, TokenCarolDan_InvalidSubjectCID,
@@ -197,20 +340,20 @@ var ProofAliceBobCarolDanErinFrank_InvalidSubject = []gocid.Cid{
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDan_InvalidExpired = []gocid.Cid{ var ProofAliceBobCarolDan_InvalidExpired = []cid.Cid{
TokenCarolDan_InvalidExpiredCID, TokenCarolDan_InvalidExpiredCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErin_InvalidExpired = []gocid.Cid{ var ProofAliceBobCarolDanErin_InvalidExpired = []cid.Cid{
TokenDanErin_InvalidExpiredCID, TokenDanErin_InvalidExpiredCID,
TokenCarolDan_InvalidExpiredCID, TokenCarolDan_InvalidExpiredCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErinFrank_InvalidExpired = []gocid.Cid{ var ProofAliceBobCarolDanErinFrank_InvalidExpired = []cid.Cid{
TokenErinFrank_InvalidExpiredCID, TokenErinFrank_InvalidExpiredCID,
TokenDanErin_InvalidExpiredCID, TokenDanErin_InvalidExpiredCID,
TokenCarolDan_InvalidExpiredCID, TokenCarolDan_InvalidExpiredCID,
@@ -218,23 +361,44 @@ var ProofAliceBobCarolDanErinFrank_InvalidExpired = []gocid.Cid{
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDan_InvalidInactive = []gocid.Cid{ var ProofAliceBobCarolDan_InvalidInactive = []cid.Cid{
TokenCarolDan_InvalidInactiveCID, TokenCarolDan_InvalidInactiveCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErin_InvalidInactive = []gocid.Cid{ var ProofAliceBobCarolDanErin_InvalidInactive = []cid.Cid{
TokenDanErin_InvalidInactiveCID, TokenDanErin_InvalidInactiveCID,
TokenCarolDan_InvalidInactiveCID, TokenCarolDan_InvalidInactiveCID,
TokenBobCarolCID, TokenBobCarolCID,
TokenAliceBobCID, TokenAliceBobCID,
} }
var ProofAliceBobCarolDanErinFrank_InvalidInactive = []gocid.Cid{ var ProofAliceBobCarolDanErinFrank_InvalidInactive = []cid.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,
}

View File

@@ -7,8 +7,8 @@ import (
"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/token/delegation" "code.sonr.org/go/ucan/token/delegation"
"github.com/ucan-wg/go-ucan/token/delegation/delegationtest" "code.sonr.org/go/ucan/token/delegation/delegationtest"
) )
func TestGetDelegation(t *testing.T) { func TestGetDelegation(t *testing.T) {

View File

@@ -8,17 +8,17 @@ import (
"fmt" "fmt"
"time" "time"
"code.sonr.org/go/did-it/didtest"
"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/dagcbor" "github.com/ipld/go-ipld-prime/codec/dagcbor"
"github.com/ipld/go-ipld-prime/codec/dagjson" "github.com/ipld/go-ipld-prime/codec/dagjson"
"github.com/ucan-wg/go-ucan/did/didtest" "code.sonr.org/go/ucan/pkg/command"
"github.com/ucan-wg/go-ucan/pkg/command" "code.sonr.org/go/ucan/pkg/policy"
"github.com/ucan-wg/go-ucan/pkg/policy" "code.sonr.org/go/ucan/pkg/policy/literal"
"github.com/ucan-wg/go-ucan/pkg/policy/literal" "code.sonr.org/go/ucan/token/delegation"
"github.com/ucan-wg/go-ucan/token/delegation" "code.sonr.org/go/ucan/token/internal/envelope"
"github.com/ucan-wg/go-ucan/token/internal/envelope"
) )
// The following example shows how to create a delegation.Token with // The following example shows how to create a delegation.Token with
@@ -41,8 +41,7 @@ func ExampleNew() {
)), )),
) )
tkn, err := delegation.New(didtest.PersonaBob.DID(), didtest.PersonaCarol.DID(), cmd, pol, tkn, err := delegation.New(didtest.PersonaBob.DID(), didtest.PersonaCarol.DID(), cmd, pol, didtest.PersonaAlice.DID(),
delegation.WithSubject(didtest.PersonaAlice.DID()),
delegation.WithExpirationIn(time.Hour), delegation.WithExpirationIn(time.Hour),
delegation.WithNotBeforeIn(time.Minute), delegation.WithNotBeforeIn(time.Minute),
delegation.WithMeta("foo", "bar"), delegation.WithMeta("foo", "bar"),
@@ -58,37 +57,37 @@ func ExampleNew() {
// Example output: // Example output:
// //
// issDid: did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU // issDid: did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1
// //
// CID (base58BTC): zdpuAsqfZkgg2jgZyob23sq1J9xwtf9PHgt1PsskVCMq7Vvxk // CID (base58BTC): zdpuB1bzMdGAtzBej9ZBJbW3ppsjP2Cf9KZ8xMjdhUjaf3vz1
// //
// DAG-CBOR (base64) out: lhAOnjc0bPptlI5MxRBrIK3YmAP1CxKfXOPkz6MHt/UJCx2gCN+6gXZX2N+BIJvmy8XmAO5sT2GYimiV7HlJH1AA6JhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGpY2F1ZHg4ZGlkOmtleTp6Nk1rZ3VwY2hoNUh3dUhhaFM3WXN5RThiTHVhMU1yOHAyaUtOUmh5dlN2UkFzOW5jY21kaC9mb28vYmFyY2V4cBpnROP/Y2lzc3g4ZGlkOmtleTp6Nk1rdkpQbUVaWlliZ2l3MW91VDFvb3VUc1RGQkhKU3RzOW9waFZzTmdjUm1ZeFVjbmJmGmdE1itjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa3V1a2syc2tEWExRbjdOSzNFaDlqTW5kWWZ2REJ4eGt0Z3BpZEpBcWI3TTNwZG1ldGGiY2Jhehh7Y2Zvb2NiYXJlbm9uY2VMv+Diy6GExIuM1eX4 // DAG-CBOR (base64) out: glhAQhZTTb4U1rin/oLFre618Ol5/leaP758T6EkHYfQaNmFYad7yVTer8bbM1Zp2LxrV3eEYeWkHeL3F0V3zWC/CaJhaEg0Ae0B7QETcXN1Y2FuL2RsZ0AxLjAuMC1yYy4xqWNhdWR4OGRpZDprZXk6ejZNa2pXRlU4SHRmTXF1V241TUVROXIyMnpXcXlDTjF2YXpHdzZnNnB6Y2l6aU5WY2NtZGgvZm9vL2JhcmNleHAaaItoW2Npc3N4OGRpZDprZXk6ejZNa2Y0V3RDd1BEdGFtc1p2QkpBNGVTVmNFN3ZadVJQeTVTa200SGFvUXY4MWkxY25iZhpoi1qHY3BvbIODYj09Zy5zdGF0dXNlZHJhZnSDY2FsbGkucmV2aWV3ZXKDZGxpa2VmLmVtYWlsbSpAZXhhbXBsZS5jb22DY2FueWUudGFnc4Jib3KCg2I9PWEuZG5ld3ODYj09YS5lcHJlc3Njc3VieDhkaWQ6a2V5Ono2TWtuVXoxbVNqNHB2UzZhVVVIZWtDSGRVUHY3SEJoRHlEQlpRMlczVnVqYzVxQ2RtZXRhomNiYXoYe2Nmb29jYmFyZW5vbmNlTHa1D4DJ78LvscA/hg==
// Converted to DAG-JSON out: // Converted to DAG-JSON out:
// [ // [
// { // {
// "/": { // "/": {
// "bytes": "5rvl8uKmDVGvAVSt4m/0MGiXl9dZwljJJ9m2qHCoIB617l26UvMxyH5uvN9hM7ozfVATiq4mLhoGgm9IGnEEAg" // "bytes": "QhZTTb4U1rin/oLFre618Ol5/leaP758T6EkHYfQaNmFYad7yVTer8bbM1Zp2LxrV3eEYeWkHeL3F0V3zWC/CQ"
// } // }
// }, // },
// { // {
// "h": { // "h": {
// "/": { // "/": {
// "bytes": "NO0BcQ" // "bytes": "NAHtAe0BE3E"
// } // }
// }, // },
// "ucan/dlg@1.0.0-rc.1": { // "ucan/dlg@1.0.0-rc.1": {
// "aud": "did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv", // "aud": "did:key:z6MkjWFU8HtfMquWn5MEQ9r22zWqyCN1vazGw6g6pzciziNV",
// "cmd": "/foo/bar", // "cmd": "/foo/bar",
// "exp": 1728933098, // "exp": 1753966683,
// "iss": "did:key:z6MkhVFznPeR572rTK51UjoTNpnF8cxuWfPm9oBMPr7y8ABe", // "iss": "did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1",
// "meta": { // "meta": {
// "baz": 123, // "baz": 123,
// "foo": "bar" // "foo": "bar"
// }, // },
// "nbf": 1728929558, // "nbf": 1753963143,
// "nonce": { // "nonce": {
// "/": { // "/": {
// "bytes": "u0HMgJ5Y+M84I/66" // "bytes": "drUPgMnvwu+xwD+G"
// } // }
// }, // },
// "pol": [ // "pol": [
@@ -126,7 +125,7 @@ func ExampleNew() {
// ] // ]
// ] // ]
// ], // ],
// "sub": "did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2" // "sub": "did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC"
// } // }
// } // }
// ] // ]
@@ -167,38 +166,36 @@ func ExampleRoot() {
// Example output: // Example output:
// //
// issDid: did:key:z6MknWJqz17Y4AfsXSJUFKomuBR4GTkViM7kJYutzTMkCyFF
//
// CID (base58BTC): zdpuAkwYz8nY7uU8j3F6wVTfFY1VEoExwvUAYBEwRWfTozddE // CID (base58BTC): zdpuAkwYz8nY7uU8j3F6wVTfFY1VEoExwvUAYBEwRWfTozddE
// //
// DAG-CBOR (base64) out: glhAVpW67FJ+myNi+azvnw2jivuiqXTuMrDZI2Qdaa8jE1Oi3mkjnm7DyqSQGADcomcuDslMWKmJ+OIyvbPG5PtSA6JhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGpY2F1ZHg4ZGlkOmtleTp6Nk1rdkpQbUVaWlliZ2l3MW91VDFvb3VUc1RGQkhKU3RzOW9waFZzTmdjUm1ZeFVjY21kaC9mb28vYmFyY2V4cBpnROVoY2lzc3g4ZGlkOmtleTp6Nk1rdXVrazJza0RYTFFuN05LM0VoOWpNbmRZZnZEQnh4a3RncGlkSkFxYjdNM3BjbmJmGmdE15RjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa3V1a2syc2tEWExRbjdOSzNFaDlqTW5kWWZ2REJ4eGt0Z3BpZEpBcWI3TTNwZG1ldGGiY2Jhehh7Y2Zvb2NiYXJlbm9uY2VMwzDc03WBciJIGPWG // DAG-CBOR (base64) out: glhATIXb2wnBByq/llaQ4RLoWTxheAwamCNo2sKL8SQJbq0EVRvdfUQDKNpuMVkvtyUR6tUdZlKv1BcXjfGEaF2XAKJhaEg0Ae0B7QETcXN1Y2FuL2RsZ0AxLjAuMC1yYy4xqWNhdWR4OGRpZDprZXk6ejZNa2Y0V3RDd1BEdGFtc1p2QkpBNGVTVmNFN3ZadVJQeTVTa200SGFvUXY4MWkxY2NtZGgvZm9vL2JhcmNleHAaaItnfWNpc3N4OGRpZDprZXk6ejZNa25VejFtU2o0cHZTNmFVVUhla0NIZFVQdjdIQmhEeURCWlEyVzNWdWpjNXFDY25iZhpoi1mpY3BvbIODYj09Zy5zdGF0dXNlZHJhZnSDY2FsbGkucmV2aWV3ZXKDZGxpa2VmLmVtYWlsbSpAZXhhbXBsZS5jb22DY2FueWUudGFnc4Jib3KCg2I9PWEuZG5ld3ODYj09YS5lcHJlc3Njc3VieDhkaWQ6a2V5Ono2TWtuVXoxbVNqNHB2UzZhVVVIZWtDSGRVUHY3SEJoRHlEQlpRMlczVnVqYzVxQ2RtZXRhomNiYXoYe2Nmb29jYmFyZW5vbmNlTGxwbjHKcevt5dZ0Xg==
// //
// Converted to DAG-JSON out: // Converted to DAG-JSON out:
// [ // [
// { // {
// "/": { // "/": {
// "bytes": "VpW67FJ+myNi+azvnw2jivuiqXTuMrDZI2Qdaa8jE1Oi3mkjnm7DyqSQGADcomcuDslMWKmJ+OIyvbPG5PtSAw" // "bytes": "TIXb2wnBByq/llaQ4RLoWTxheAwamCNo2sKL8SQJbq0EVRvdfUQDKNpuMVkvtyUR6tUdZlKv1BcXjfGEaF2XAA"
// } // }
// }, // },
// { // {
// "h": { // "h": {
// "/": { // "/": {
// "bytes": "NO0BcQ" // "bytes": "NAHtAe0BE3E"
// } // }
// }, // },
// "ucan/dlg@1.0.0-rc.1": { // "ucan/dlg@1.0.0-rc.1": {
// "aud": "did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU", // "aud": "did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1",
// "cmd": "/foo/bar", // "cmd": "/foo/bar",
// "exp": 1732568424, // "exp": 1753966461,
// "iss": "did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p", // "iss": "did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC",
// "meta": { // "meta": {
// "baz": 123, // "baz": 123,
// "foo": "bar" // "foo": "bar"
// }, // },
// "nbf": 1732564884, // "nbf": 1753962921,
// "nonce": { // "nonce": {
// "/": { // "/": {
// "bytes": "wzDc03WBciJIGPWG" // "bytes": "bHBuMcpx6+3l1nRe"
// } // }
// }, // },
// "pol": [ // "pol": [
@@ -236,7 +233,7 @@ func ExampleRoot() {
// ] // ]
// ] // ]
// ], // ],
// "sub": "did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p" // "sub": "did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC"
// } // }
// } // }
// ] // ]
@@ -245,7 +242,7 @@ func ExampleRoot() {
// The following example demonstrates how to get a delegation.Token from // The following example demonstrates how to get a delegation.Token from
// a DAG-CBOR []byte. // a DAG-CBOR []byte.
func ExampleFromSealed() { func ExampleFromSealed() {
const cborBase64 = "glhAmnAkgfjAx4SA5pzJmtaHRJtTGNpF1y6oqb4yhGoM2H2EUGbBYT4rVDjMKBgCjhdGHjipm00L8iR5SsQh3sIEBaJhaEQ07QFxc3VjYW4vZGxnQDEuMC4wLXJjLjGoY2F1ZHg4ZGlkOmtleTp6Nk1rcTVZbWJKY1RyUEV4TkRpMjZpbXJUQ3BLaGVwakJGQlNIcXJCRE4yQXJQa3ZjY21kaC9mb28vYmFyY2V4cPZjaXNzeDhkaWQ6a2V5Ono2TWtwem4ybjNaR1QyVmFxTUdTUUMzdHptelY0VFM5UzcxaUZzRFhFMVdub05IMmNwb2yDg2I9PWcuc3RhdHVzZWRyYWZ0g2NhbGxpLnJldmlld2Vyg2RsaWtlZi5lbWFpbG0qQGV4YW1wbGUuY29tg2NhbnllLnRhZ3OCYm9ygoNiPT1hLmRuZXdzg2I9PWEuZXByZXNzY3N1Yng4ZGlkOmtleTp6Nk1rdEExdUJkQ3BxNHVKQnFFOWpqTWlMeXhaQmc5YTZ4Z1BQS0pqTXFzczZaYzJkbWV0YaBlbm9uY2VMAAECAwQFBgcICQoL" const cborBase64 = "glhACBuW/rjVKyBPUVPxexsafwBe7y84k0yzywq3hQW2rs2TNmWA5wexAQ+jTkSQ07zhmQRA/wytBfqWkx24+sjlD6JhaEg0Ae0B7QETcXN1Y2FuL2RsZ0AxLjAuMC1yYy4xp2NhdWR4OGRpZDprZXk6ejZNa2pXRlU4SHRmTXF1V241TUVROXIyMnpXcXlDTjF2YXpHdzZnNnB6Y2l6aU5WY2NtZGgvZm9vL2JhcmNleHD2Y2lzc3g4ZGlkOmtleTp6Nk1rZjRXdEN3UER0YW1zWnZCSkE0ZVNWY0U3dlp1UlB5NVNrbTRIYW9RdjgxaTFjcG9sg4NiPT1nLnN0YXR1c2VkcmFmdINjYWxsaS5yZXZpZXdlcoNkbGlrZWYuZW1haWxtKkBleGFtcGxlLmNvbYNjYW55ZS50YWdzgmJvcoKDYj09YS5kbmV3c4NiPT1hLmVwcmVzc2NzdWJ4OGRpZDprZXk6ejZNa25VejFtU2o0cHZTNmFVVUhla0NIZFVQdjdIQmhEeURCWlEyVzNWdWpjNXFDZW5vbmNlTDVJDkO3LVTKnhxKKw=="
cborBytes, err := base64.StdEncoding.DecodeString(cborBase64) cborBytes, err := base64.StdEncoding.DecodeString(cborBase64)
printThenPanicOnErr(err) printThenPanicOnErr(err)
@@ -265,22 +262,24 @@ func ExampleFromSealed() {
fmt.Println("Expiration (exp):", tkn.Expiration()) fmt.Println("Expiration (exp):", tkn.Expiration())
// Output: // Output:
// CID (base58BTC): zdpuAw26pFuvZa2Z9YAtpZZnWN6VmnRFr7Z8LVY5c7RVWoxGY // CID (base58BTC): zdpuAsgmtmC849BEApGCJm2fTSzwtHiAqQnuJCMBBBCbFiadd
// Issuer (iss): did:key:z6Mkpzn2n3ZGT2VaqMGSQC3tzmzV4TS9S71iFsDXE1WnoNH2 // Issuer (iss): did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1
// Audience (aud): did:key:z6Mkq5YmbJcTrPExNDi26imrTCpKhepjBFBSHqrBDN2ArPkv // Audience (aud): did:key:z6MkjWFU8HtfMquWn5MEQ9r22zWqyCN1vazGw6g6pzciziNV
// Subject (sub): did:key:z6MktA1uBdCpq4uJBqE9jjMiLyxZBg9a6xgPPKJjMqss6Zc2 // Subject (sub): did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC
// Command (cmd): /foo/bar // Command (cmd): /foo/bar
// Policy (pol): [ // Policy (pol): [
// ["==", ".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"]
// ] // ]]
// ]
// ] // ]
// Nonce (nonce): 000102030405060708090a0b // Nonce (nonce): 35490e43b72d54ca9e1c4a2b
// Meta (meta): {} // Meta (meta): {}
// NotBefore (nbf): <nil> // NotBefore (nbf): <nil>
// Expiration (exp): <nil> // Expiration (exp): <nil>

View File

@@ -0,0 +1,108 @@
package delegation
import (
_ "embed"
"encoding/base64"
"encoding/json"
"fmt"
"testing"
"code.sonr.org/go/did-it/crypto"
"code.sonr.org/go/did-it/crypto/ed25519"
"github.com/multiformats/go-varint"
"github.com/stretchr/testify/require"
)
// This comes from https://github.com/ucan-wg/spec/blob/main/fixtures/1.0.0/delegation.json
//
//go:embed testdata/interop_delegation.json
var interopDelegation []byte
type interop struct {
Version string `json:"version"`
Comments string `json:"comments"`
Principals map[string]string `json:"principals"`
Valid []validTestCase `json:"valid"`
}
type validTestCase struct {
Name string `json:"name"`
Token string `json:"token"`
CID string `json:"cid"`
Envelope envelopeData `json:"envelope"`
}
type envelopeData struct {
Payload payloadData `json:"payload"`
Signature string `json:"signature"`
Algorithm string `json:"alg"`
Encoding string `json:"enc"`
Spec string `json:"spec"`
Version string `json:"version"`
}
type payloadData struct {
Issuer string `json:"iss"`
Audience string `json:"aud"`
Subject string `json:"sub"`
Command string `json:"cmd"`
Policies json.RawMessage `json:"pol"`
ExpiresAt int64 `json:"exp"`
Nonce string `json:"nonce"`
}
func TestInterop(t *testing.T) {
var testData interop
err := json.Unmarshal(interopDelegation, &testData)
require.NoError(t, err)
require.Equal(t, "1.0.0-rc.1", testData.Version)
// alice, err := decodeKey(testData.Principals["alice"])
// require.NoError(t, err)
// bob, err := decodeKey(testData.Principals["bob"])
// require.NoError(t, err)
// carol, err := decodeKey(testData.Principals["carol"])
// require.NoError(t, err)
t.Run("valid", func(t *testing.T) {
for _, tc := range testData.Valid {
t.Run(tc.Name, func(t *testing.T) {
dlgBytes, err := base64.StdEncoding.DecodeString(tc.Token)
require.NoError(t, err)
dlg, c, err := FromSealed(dlgBytes)
require.NoError(t, err)
require.Equal(t, tc.CID, c.String())
require.Equal(t, tc.Envelope.Payload.Issuer, dlg.Issuer().String())
require.Equal(t, tc.Envelope.Payload.Audience, dlg.Audience().String())
require.Equal(t, tc.Envelope.Payload.Subject, dlg.Subject().String())
require.Equal(t, tc.Envelope.Payload.Command, dlg.Command().String())
require.Equal(t, tc.Envelope.Payload.Command, dlg.Command().String())
require.JSONEq(t, string(tc.Envelope.Payload.Policies), dlg.Policy().String())
require.Equal(t, tc.Envelope.Payload.ExpiresAt, dlg.expiration.Unix())
nonceBytes, err := base64.StdEncoding.DecodeString(tc.Envelope.Payload.Nonce)
require.NoError(t, err)
require.Equal(t, nonceBytes, dlg.Nonce())
})
}
})
}
func decodeKey(key string) (crypto.PrivateKeySigningBytes, error) {
bytes, err := base64.StdEncoding.DecodeString(key)
if err != nil {
return nil, err
}
code, read, err := varint.FromUvarint(bytes)
if err != nil {
return nil, err
}
if code != 0x1300 {
return nil, fmt.Errorf("invalid varint code: %d", code)
}
return ed25519.PrivateKeyFromSeed(bytes[read:])
}

View File

@@ -1,25 +1,24 @@
package delegation package delegation
import ( import (
"fmt"
"io" "io"
"code.sonr.org/go/did-it"
"code.sonr.org/go/did-it/crypto"
"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" "github.com/ipld/go-ipld-prime/codec"
"github.com/ipld/go-ipld-prime/codec/dagcbor" "github.com/ipld/go-ipld-prime/codec/dagcbor"
"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/datamodel"
"github.com/libp2p/go-libp2p/core/crypto"
"github.com/ucan-wg/go-ucan/did" "code.sonr.org/go/ucan/token/internal/envelope"
"github.com/ucan-wg/go-ucan/token/internal/envelope"
) )
// ToSealed wraps the delegation token in an envelope, generates the // ToSealed wraps the delegation token in an envelope, generates the
// signature, encodes the result to DAG-CBOR and calculates the CID of // signature, encodes the result to DAG-CBOR and calculates the CID of
// the resulting binary data. // the resulting binary data.
func (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error) { func (t *Token) ToSealed(privKey crypto.PrivateKeySigningBytes) ([]byte, cid.Cid, error) {
data, err := t.ToDagCbor(privKey) data, err := t.ToDagCbor(privKey)
if err != nil { if err != nil {
return nil, cid.Undef, err return nil, cid.Undef, err
@@ -34,7 +33,7 @@ func (t *Token) ToSealed(privKey crypto.PrivKey) ([]byte, cid.Cid, error) {
} }
// ToSealedWriter is the same as ToSealed but accepts an io.Writer. // ToSealedWriter is the same as ToSealed but accepts an io.Writer.
func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, error) { func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) (cid.Cid, error) {
cidWriter := envelope.NewCIDWriter(w) cidWriter := envelope.NewCIDWriter(w)
if err := t.ToDagCborWriter(cidWriter, privKey); err != nil { if err := t.ToDagCborWriter(cidWriter, privKey); err != nil {
@@ -48,8 +47,8 @@ func (t *Token) ToSealedWriter(w io.Writer, privKey crypto.PrivKey) (cid.Cid, er
// verifies that the envelope's signature is correct based on the public // 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 // key taken from the issuer (iss) field and calculates the CID of the
// incoming data. // incoming data.
func FromSealed(data []byte) (*Token, cid.Cid, error) { func FromSealed(data []byte, resolvOpts ...did.ResolutionOption) (*Token, cid.Cid, error) {
tkn, err := FromDagCbor(data) tkn, err := FromDagCbor(data, resolvOpts...)
if err != nil { if err != nil {
return nil, cid.Undef, err return nil, cid.Undef, err
} }
@@ -63,10 +62,10 @@ func FromSealed(data []byte) (*Token, cid.Cid, error) {
} }
// FromSealedReader is the same as Unseal but accepts an io.Reader. // FromSealedReader is the same as Unseal but accepts an io.Reader.
func FromSealedReader(r io.Reader) (*Token, cid.Cid, error) { func FromSealedReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, cid.Cid, error) {
cidReader := envelope.NewCIDReader(r) cidReader := envelope.NewCIDReader(r)
tkn, err := FromDagCborReader(cidReader) tkn, err := FromDagCborReader(cidReader, resolvOpts...)
if err != nil { if err != nil {
return nil, cid.Undef, err return nil, cid.Undef, err
} }
@@ -81,7 +80,7 @@ func FromSealedReader(r io.Reader) (*Token, cid.Cid, error) {
// Encode marshals a Token to the format specified by the provided // Encode marshals a Token to the format specified by the provided
// codec.Encoder. // codec.Encoder.
func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, error) { func (t *Token) Encode(privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) ([]byte, error) {
node, err := t.toIPLD(privKey) node, err := t.toIPLD(privKey)
if err != nil { if err != nil {
return nil, err return nil, err
@@ -91,7 +90,7 @@ func (t *Token) Encode(privKey crypto.PrivKey, encFn codec.Encoder) ([]byte, err
} }
// EncodeWriter is the same as Encode, but accepts an io.Writer. // EncodeWriter is the same as Encode, but accepts an io.Writer.
func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.Encoder) error { func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes, encFn codec.Encoder) error {
node, err := t.toIPLD(privKey) node, err := t.toIPLD(privKey)
if err != nil { if err != nil {
return err return err
@@ -101,22 +100,22 @@ func (t *Token) EncodeWriter(w io.Writer, privKey crypto.PrivKey, encFn codec.En
} }
// ToDagCbor marshals the Token to the DAG-CBOR format. // ToDagCbor marshals the Token to the DAG-CBOR format.
func (t *Token) ToDagCbor(privKey crypto.PrivKey) ([]byte, error) { func (t *Token) ToDagCbor(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
return t.Encode(privKey, dagcbor.Encode) return t.Encode(privKey, dagcbor.Encode)
} }
// ToDagCborWriter is the same as ToDagCbor, but it accepts an io.Writer. // ToDagCborWriter is the same as ToDagCbor, but it accepts an io.Writer.
func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivKey) error { func (t *Token) ToDagCborWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
return t.EncodeWriter(w, privKey, dagcbor.Encode) return t.EncodeWriter(w, privKey, dagcbor.Encode)
} }
// ToDagJson marshals the Token to the DAG-JSON format. // ToDagJson marshals the Token to the DAG-JSON format.
func (t *Token) ToDagJson(privKey crypto.PrivKey) ([]byte, error) { func (t *Token) ToDagJson(privKey crypto.PrivateKeySigningBytes) ([]byte, error) {
return t.Encode(privKey, dagjson.Encode) return t.Encode(privKey, dagjson.Encode)
} }
// ToDagJsonWriter is the same as ToDagJson, but it accepts an io.Writer. // ToDagJsonWriter is the same as ToDagJson, but it accepts an io.Writer.
func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error { func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivateKeySigningBytes) error {
return t.EncodeWriter(w, privKey, dagjson.Encode) return t.EncodeWriter(w, privKey, dagjson.Encode)
} }
@@ -125,29 +124,29 @@ func (t *Token) ToDagJsonWriter(w io.Writer, privKey crypto.PrivKey) error {
// //
// An error is returned if the conversion fails, or if the resulting // An error is returned if the conversion fails, or if the resulting
// Token is invalid. // Token is invalid.
func Decode(b []byte, decFn codec.Decoder) (*Token, error) { func Decode(b []byte, decFn codec.Decoder, resolvOpts ...did.ResolutionOption) (*Token, error) {
node, err := ipld.Decode(b, decFn) node, err := ipld.Decode(b, decFn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return FromIPLD(node) return FromIPLD(node, resolvOpts...)
} }
// DecodeReader is the same as Decode, but accept an io.Reader. // DecodeReader is the same as Decode, but accept an io.Reader.
func DecodeReader(r io.Reader, decFn codec.Decoder) (*Token, error) { func DecodeReader(r io.Reader, decFn codec.Decoder, resolvOpts ...did.ResolutionOption) (*Token, error) {
node, err := ipld.DecodeStreaming(r, decFn) node, err := ipld.DecodeStreaming(r, decFn)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return FromIPLD(node) return FromIPLD(node, resolvOpts...)
} }
// FromDagCbor unmarshals the input data into a Token. // FromDagCbor unmarshals the input data into a Token.
// //
// An error is returned if the conversion fails, or if the resulting // An error is returned if the conversion fails, or if the resulting
// Token is invalid. // Token is invalid.
func FromDagCbor(data []byte) (*Token, error) { func FromDagCbor(data []byte, resolvOpts ...did.ResolutionOption) (*Token, error) {
pay, err := envelope.FromDagCbor[*tokenPayloadModel](data) pay, err := envelope.FromDagCbor[*tokenPayloadModel](data, resolvOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -161,26 +160,26 @@ func FromDagCbor(data []byte) (*Token, error) {
} }
// FromDagCborReader is the same as FromDagCbor, but accept an io.Reader. // FromDagCborReader is the same as FromDagCbor, but accept an io.Reader.
func FromDagCborReader(r io.Reader) (*Token, error) { func FromDagCborReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, error) {
return DecodeReader(r, dagcbor.Decode) return DecodeReader(r, dagcbor.Decode, resolvOpts...)
} }
// FromDagJson unmarshals the input data into a Token. // FromDagJson unmarshals the input data into a Token.
// //
// An error is returned if the conversion fails, or if the resulting // An error is returned if the conversion fails, or if the resulting
// Token is invalid. // Token is invalid.
func FromDagJson(data []byte) (*Token, error) { func FromDagJson(data []byte, resolvOpts ...did.ResolutionOption) (*Token, error) {
return Decode(data, dagjson.Decode) return Decode(data, dagjson.Decode, resolvOpts...)
} }
// FromDagJsonReader is the same as FromDagJson, but accept an io.Reader. // FromDagJsonReader is the same as FromDagJson, but accept an io.Reader.
func FromDagJsonReader(r io.Reader) (*Token, error) { func FromDagJsonReader(r io.Reader, resolvOpts ...did.ResolutionOption) (*Token, error) {
return DecodeReader(r, dagjson.Decode) return DecodeReader(r, dagjson.Decode, resolvOpts...)
} }
// FromIPLD decode the given IPLD representation into a Token. // FromIPLD decode the given IPLD representation into a Token.
func FromIPLD(node datamodel.Node) (*Token, error) { func FromIPLD(node datamodel.Node, resolvOpts ...did.ResolutionOption) (*Token, error) {
pay, err := envelope.FromIPLD[*tokenPayloadModel](node) pay, err := envelope.FromIPLD[*tokenPayloadModel](node, resolvOpts...)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -193,18 +192,9 @@ func FromIPLD(node datamodel.Node) (*Token, error) {
return tkn, err return tkn, err
} }
func (t *Token) toIPLD(privKey crypto.PrivKey) (datamodel.Node, error) { func (t *Token) toIPLD(privKey crypto.PrivateKeySigningBytes) (datamodel.Node, error) {
// sanity check that privKey and issuer are matching
issPub, err := t.issuer.PubKey()
if err != nil {
return nil, err
}
if !issPub.Equals(privKey.GetPublic()) {
return nil, fmt.Errorf("private key doesn't match the issuer")
}
var sub *string var sub *string
if t.subject != did.Undef { if t.subject != nil {
s := t.subject.String() s := t.subject.String()
sub = &s sub = &s
} }

View File

@@ -3,8 +3,6 @@ package delegation
import ( import (
"fmt" "fmt"
"time" "time"
"github.com/ucan-wg/go-ucan/did"
) )
// Option is a type that allows optional fields to be set during the // Option is a type that allows optional fields to be set during the
@@ -45,7 +43,8 @@ func WithMeta(key string, val any) Option {
} }
// WithEncryptedMetaString adds a key/value pair in the "meta" field. // WithEncryptedMetaString adds a key/value pair in the "meta" field.
// The string value is encrypted with the given aesKey. // The string value is encrypted with the given key.
// The ciphertext will be 40 bytes larger than the plaintext due to encryption overhead.
func WithEncryptedMetaString(key, val string, encryptionKey []byte) Option { func WithEncryptedMetaString(key, val string, encryptionKey []byte) Option {
return func(t *Token) error { return func(t *Token) error {
return t.meta.AddEncrypted(key, val, encryptionKey) return t.meta.AddEncrypted(key, val, encryptionKey)
@@ -53,7 +52,8 @@ func WithEncryptedMetaString(key, val string, encryptionKey []byte) Option {
} }
// WithEncryptedMetaBytes adds a key/value pair in the "meta" field. // WithEncryptedMetaBytes adds a key/value pair in the "meta" field.
// The []byte value is encrypted with the given aesKey. // The []byte value is encrypted with the given key.
// The ciphertext will be 40 bytes larger than the plaintext due to encryption overhead.
func WithEncryptedMetaBytes(key string, val, encryptionKey []byte) Option { func WithEncryptedMetaBytes(key string, val, encryptionKey []byte) Option {
return func(t *Token) error { return func(t *Token) error {
return t.meta.AddEncrypted(key, val, encryptionKey) return t.meta.AddEncrypted(key, val, encryptionKey)
@@ -83,20 +83,6 @@ func WithNotBeforeIn(nbf time.Duration) Option {
} }
} }
// WithSubject sets the Tokens's optional "subject" field to the value of
// provided did.DID.
//
// This Option should only be used with the New constructor - since
// Subject is a required parameter when creating a Token via the Root
// constructor, any value provided via this Option will be silently
// overwritten.
func WithSubject(sub did.DID) Option {
return func(t *Token) error {
t.subject = sub
return nil
}
}
// WithNonce sets the Token's nonce with the given value. // 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 this option is not used, a random 12-byte nonce is generated for this required field.
func WithNonce(nonce []byte) Option { func WithNonce(nonce []byte) Option {

View File

@@ -10,14 +10,14 @@ import (
"github.com/ipld/go-ipld-prime/node/bindnode" "github.com/ipld/go-ipld-prime/node/bindnode"
"github.com/ipld/go-ipld-prime/schema" "github.com/ipld/go-ipld-prime/schema"
"github.com/ucan-wg/go-ucan/pkg/meta" "code.sonr.org/go/ucan/pkg/meta"
"github.com/ucan-wg/go-ucan/token/internal/envelope" "code.sonr.org/go/ucan/token/internal/envelope"
) )
// [Tag] is the string used as a key within the SigPayload that identifies // Tag is the string used as a key within the SigPayload that identifies
// that the TokenPayload is a delegation. // that the TokenPayload is a delegation.
// //
// [Tag]: https://github.com/ucan-wg/delegation/tree/v1_ipld#type-tag // See: https://github.com/ucan-wg/delegation/tree/v1_ipld#type-tag
const Tag = "ucan/dlg@1.0.0-rc.1" const Tag = "ucan/dlg@1.0.0-rc.1"
// TODO: update the above Tag URL once the delegation specification is merged. // TODO: update the above Tag URL once the delegation specification is merged.

View File

@@ -5,14 +5,13 @@ import (
_ "embed" _ "embed"
"testing" "testing"
"code.sonr.org/go/did-it/didtest"
"github.com/ipld/go-ipld-prime" "github.com/ipld/go-ipld-prime"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"gotest.tools/v3/golden"
"github.com/ucan-wg/go-ucan/did/didtest" "code.sonr.org/go/ucan/token/delegation"
"github.com/ucan-wg/go-ucan/token/delegation" "code.sonr.org/go/ucan/token/internal/envelope"
"github.com/ucan-wg/go-ucan/token/internal/envelope"
) )
//go:embed delegation.ipldsch //go:embed delegation.ipldsch
@@ -21,7 +20,6 @@ var schemaBytes []byte
func TestSchemaRoundTrip(t *testing.T) { func TestSchemaRoundTrip(t *testing.T) {
t.Parallel() t.Parallel()
delegationJson := golden.Get(t, "new.dagjson")
privKey := didtest.PersonaAlice.PrivKey() privKey := didtest.PersonaAlice.PrivKey()
t.Run("via buffers", func(t *testing.T) { t.Run("via buffers", func(t *testing.T) {
@@ -30,12 +28,15 @@ func TestSchemaRoundTrip(t *testing.T) {
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson // format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson() // function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
p1, err := delegation.FromDagJson(delegationJson) p1, err := delegation.FromDagJson(newDagJson)
require.NoError(t, err)
_, newCID, err := p1.ToSealed(privKey)
require.NoError(t, err) require.NoError(t, err)
cborBytes, id, err := p1.ToSealed(privKey) cborBytes, id, err := p1.ToSealed(privKey)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, newCID, envelope.CIDToBase58BTC(id)) assert.Equal(t, envelope.CIDToBase58BTC(newCID), envelope.CIDToBase58BTC(id))
p2, c2, err := delegation.FromSealed(cborBytes) p2, c2, err := delegation.FromSealed(cborBytes)
require.NoError(t, err) require.NoError(t, err)
@@ -44,13 +45,13 @@ func TestSchemaRoundTrip(t *testing.T) {
readJson, err := p2.ToDagJson(privKey) readJson, err := p2.ToDagJson(privKey)
require.NoError(t, err) require.NoError(t, err)
assert.JSONEq(t, string(delegationJson), string(readJson)) assert.JSONEq(t, string(newDagJson), string(readJson))
}) })
t.Run("via streaming", func(t *testing.T) { t.Run("via streaming", func(t *testing.T) {
t.Parallel() t.Parallel()
buf := bytes.NewBuffer(delegationJson) buf := bytes.NewBuffer(newDagJson)
// format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson // format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson
// function: DecodeDagJson() Seal() Unseal() EncodeDagJson() // function: DecodeDagJson() Seal() Unseal() EncodeDagJson()
@@ -58,10 +59,13 @@ func TestSchemaRoundTrip(t *testing.T) {
p1, err := delegation.FromDagJsonReader(buf) p1, err := delegation.FromDagJsonReader(buf)
require.NoError(t, err) require.NoError(t, err)
_, newCID, err := p1.ToSealed(privKey)
require.NoError(t, err)
cborBytes := &bytes.Buffer{} cborBytes := &bytes.Buffer{}
id, err := p1.ToSealedWriter(cborBytes, privKey) id, err := p1.ToSealedWriter(cborBytes, privKey)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, newCID, envelope.CIDToBase58BTC(id)) assert.Equal(t, envelope.CIDToBase58BTC(newCID), envelope.CIDToBase58BTC(id))
// buf = bytes.NewBuffer(cborBytes.Bytes()) // buf = bytes.NewBuffer(cborBytes.Bytes())
p2, c2, err := delegation.FromSealedReader(cborBytes) p2, c2, err := delegation.FromSealedReader(cborBytes)
@@ -71,17 +75,7 @@ func TestSchemaRoundTrip(t *testing.T) {
readJson := &bytes.Buffer{} readJson := &bytes.Buffer{}
require.NoError(t, p2.ToDagJsonWriter(readJson, privKey)) require.NoError(t, p2.ToDagJsonWriter(readJson, privKey))
assert.JSONEq(t, string(delegationJson), readJson.String()) assert.JSONEq(t, string(newDagJson), readJson.String())
})
t.Run("fails with wrong PrivKey", func(t *testing.T) {
t.Parallel()
p1, err := delegation.FromDagJson(delegationJson)
require.NoError(t, err)
_, _, err = p1.ToSealed(didtest.PersonaBob.PrivKey())
require.EqualError(t, err, "private key doesn't match the issuer")
}) })
} }
@@ -93,11 +87,10 @@ func BenchmarkSchemaLoad(b *testing.B) {
} }
func BenchmarkRoundTrip(b *testing.B) { func BenchmarkRoundTrip(b *testing.B) {
delegationJson := golden.Get(b, "new.dagjson")
privKey := didtest.PersonaAlice.PrivKey() privKey := didtest.PersonaAlice.PrivKey()
b.Run("via buffers", func(b *testing.B) { b.Run("via buffers", func(b *testing.B) {
p1, _ := delegation.FromDagJson(delegationJson) p1, _ := delegation.FromDagJson(newDagJson)
cborBytes, _, _ := p1.ToSealed(privKey) cborBytes, _, _ := p1.ToSealed(privKey)
p2, _, _ := delegation.FromSealed(cborBytes) p2, _, _ := delegation.FromSealed(cborBytes)
@@ -106,7 +99,7 @@ func BenchmarkRoundTrip(b *testing.B) {
b.Run("FromDagJson", func(b *testing.B) { b.Run("FromDagJson", func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, _ = delegation.FromDagJson(delegationJson) _, _ = delegation.FromDagJson(newDagJson)
} }
}) })
@@ -133,7 +126,7 @@ func BenchmarkRoundTrip(b *testing.B) {
}) })
b.Run("via streaming", func(b *testing.B) { b.Run("via streaming", func(b *testing.B) {
p1, _ := delegation.FromDagJsonReader(bytes.NewReader(delegationJson)) p1, _ := delegation.FromDagJsonReader(bytes.NewReader(newDagJson))
cborBuf := &bytes.Buffer{} cborBuf := &bytes.Buffer{}
_, _ = p1.ToSealedWriter(cborBuf, privKey) _, _ = p1.ToSealedWriter(cborBuf, privKey)
cborBytes := cborBuf.Bytes() cborBytes := cborBuf.Bytes()
@@ -143,7 +136,7 @@ func BenchmarkRoundTrip(b *testing.B) {
b.Run("FromDagJsonReader", func(b *testing.B) { b.Run("FromDagJsonReader", func(b *testing.B) {
b.ReportAllocs() b.ReportAllocs()
reader := bytes.NewReader(delegationJson) reader := bytes.NewReader(newDagJson)
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
_, _ = reader.Seek(0, 0) _, _ = reader.Seek(0, 0)
_, _ = delegation.FromDagJsonReader(reader) _, _ = delegation.FromDagJsonReader(reader)

View File

@@ -0,0 +1,32 @@
{
"version": "1.0.0-rc.1",
"comments": "Principals private keys encoded as base64pad(varint(0x1300) + privateKey) and all other binary fields are encoded as base64pad.",
"principals": {
"carol": "gCZC43QGw7ZvYQuKTtBwBy+tdjYrKf0hXU3dd+J0HON5dw==",
"bob": "gCZfj9+RzU2U518TMBNK/fjdGQz34sB4iKE6z+9lQDpCIQ==",
"alice": "gCa9UfZv+yI5/rvUIt21DaGI7EZJlzFO1uDc5AyJ30c6/w=="
},
"valid": [
{
"name": "basic delegation bob > carol",
"token": "glhAd7jvZs44lTWmjSG/PWBRXvAdJA6Pq0fj86WQOVBYSw3fLrpjF7OMvjUlTynZZblPHzFsiBeBlUqtbCAHvhppCaJhaEg0Ae0B7QETcXN1Y2FuL2RsZ0AxLjAuMC1yYy4xp2NhdWR4OGRpZDprZXk6ejZNa21KY2VWb1FTSHM0NWNSZUVYb0x0V20xd29zQ0c4Ukx4Zkt3aHhvcXpvVGtDY2NtZGgvYWNjb3VudGNleHAaaIIMsWNpc3N4OGRpZDprZXk6ejZNa21UOWo2ZlZacXpYVjh1MndWVlN1NDlnWVNSWUdTUW5kdVdYRjZmb0FKcnF6Y3BvbIBjc3VieDhkaWQ6a2V5Ono2TWttVDlqNmZWWnF6WFY4dTJ3VlZTdTQ5Z1lTUllHU1FuZHVXWEY2Zm9BSnJxemVub25jZUwnbSv2keQn/Kg2KsM=",
"cid": "bafyreifqsojs54lpxxyx5xfqxiwkc4paglcyqd7vjzrcyapxi557extz6m",
"envelope": {
"payload": {
"iss": "did:key:z6MkmT9j6fVZqzXV8u2wVVSu49gYSRYGSQnduWXF6foAJrqz",
"aud": "did:key:z6MkmJceVoQSHs45cReEXoLtWm1wosCG8RLxfKwhxoqzoTkC",
"sub": "did:key:z6MkmT9j6fVZqzXV8u2wVVSu49gYSRYGSQnduWXF6foAJrqz",
"cmd": "/account",
"pol": [],
"exp": 1753353393,
"nonce": "J20r9pHkJ/yoNirD"
},
"signature": "d7jvZs44lTWmjSG/PWBRXvAdJA6Pq0fj86WQOVBYSw3fLrpjF7OMvjUlTynZZblPHzFsiBeBlUqtbCAHvhppCQ==",
"alg": "Ed25519",
"enc": "DAG-CBOR",
"spec": "dlg",
"version": "1.0.0-rc.1"
}
}
]
}

View File

@@ -1 +1 @@
[{"/":{"bytes":"BBabgnWqd+cjwG1td0w9BudNocmUwoR89RMZTqZHk3osCXEI/bOkko0zTvlusaE4EMBBeSzZDKzjvunLBfdiBg"}},{"h":{"/":{"bytes":"NO0BcQ"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6MkvJPmEZZYbgiw1ouT1oouTsTFBHJSts9ophVsNgcRmYxU","cmd":"/foo/bar","exp":7258118400,"iss":"did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p","meta":{"bar":"barr","foo":"fooo"},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"pol":[["==",".status","draft"],["all",".reviewer",["like",".email","*@example.com"]],["any",".tags",["or",[["==",".","news"],["==",".","press"]]]]],"sub":"did:key:z6Mkuukk2skDXLQn7NK3Eh9jMndYfvDBxxktgpidJAqb7M3p"}}] [{"/":{"bytes":"7Df/QlkgvQ/j1sr2NUFryQbTxo16rsOPfDPX8evBmA0hV6Omv+sScM8HC30KHr/kXCl+DzxDg+/EOwiZ9ApNBQ"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/dlg@1.0.0-rc.1":{"aud":"did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1","cmd":"/foo/bar","exp":7258118400,"iss":"did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC","meta":{"bar":"barr","foo":"fooo"},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"pol":[["==",".status","draft"],["all",".reviewer",["like",".email","*@example.com"]],["any",".tags",["or",[["==",".","news"],["==",".","press"]]]]],"sub":"did:key:z6MkjWFU8HtfMquWn5MEQ9r22zWqyCN1vazGw6g6pzciziNV"}}]

Some files were not shown because too many files have changed in this diff Show More