From 4b3a0c590a8f8b24780b4b0985f355e091c6926b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Mur=C3=A9?= Date: Thu, 4 Dec 2025 15:01:15 +0100 Subject: [PATCH] invocation: add support for self-signed invocations (issuer=subject) --- token/delegation/delegation.go | 2 +- .../data/TokenAliceAlice.dagcbor | Bin 0 -> 337 bytes .../delegationtest/data/TokenBobBob.dagcbor | Bin 0 -> 337 bytes .../data/TokenCarolCarol.dagcbor | Bin 0 -> 337 bytes .../delegationtest/data/TokenDanDan.dagcbor | Bin 0 -> 337 bytes .../delegationtest/data/TokenErinErin.dagcbor | Bin 0 -> 337 bytes .../data/TokenFrankFrank.dagcbor | Bin 0 -> 337 bytes .../delegationtest/generator/generator.go | 19 + .../delegationtest/generator/main.go | 6 +- token/delegation/delegationtest/token_gen.go | 60 +++ token/invocation/invocation.go | 20 +- token/invocation/invocation_test.go | 352 ++++++++++++------ token/invocation/proof.go | 17 +- token/invocation/schema_test.go | 18 +- .../invocation/testdata/full_example.dagjson | 1 + token/invocation/testdata/new.dagjson | 2 +- token/invocation/testdata/selfsigned.dagjson | 1 + 17 files changed, 360 insertions(+), 138 deletions(-) create mode 100644 token/delegation/delegationtest/data/TokenAliceAlice.dagcbor create mode 100644 token/delegation/delegationtest/data/TokenBobBob.dagcbor create mode 100644 token/delegation/delegationtest/data/TokenCarolCarol.dagcbor create mode 100644 token/delegation/delegationtest/data/TokenDanDan.dagcbor create mode 100644 token/delegation/delegationtest/data/TokenErinErin.dagcbor create mode 100644 token/delegation/delegationtest/data/TokenFrankFrank.dagcbor create mode 100644 token/invocation/testdata/full_example.dagjson create mode 100644 token/invocation/testdata/selfsigned.dagjson diff --git a/token/delegation/delegation.go b/token/delegation/delegation.go index 565cba0..f096e51 100644 --- a/token/delegation/delegation.go +++ b/token/delegation/delegation.go @@ -238,7 +238,7 @@ func tokenFromModel(m tokenPayloadModel) (*Token, error) { tkn.issuer, err = did.Parse(m.Iss) if err != nil { - return nil, fmt.Errorf("parse iss: %w", err) + return nil, fmt.Errorf("parse issuer: %w", err) } if tkn.audience, err = did.Parse(m.Aud); err != nil { diff --git a/token/delegation/delegationtest/data/TokenAliceAlice.dagcbor b/token/delegation/delegationtest/data/TokenAliceAlice.dagcbor new file mode 100644 index 0000000000000000000000000000000000000000..172f12e94fdf9170e98bd71e66bce6d72843b6d4 GIT binary patch literal 337 zcmZpQaQN7u)RL)Sm;c&vdTrp(FaW6{D@GxO~3xvXj#ihxKdHN|i=?;c^26_g%Mag=G z%aapJQz|S{GE=OwQ!A~i%zU%+LaPjOgR@Ku%7V=jLqk1MvzNKea!#> literal 0 HcmV?d00001 diff --git a/token/delegation/delegationtest/data/TokenBobBob.dagcbor b/token/delegation/delegationtest/data/TokenBobBob.dagcbor new file mode 100644 index 0000000000000000000000000000000000000000..417afdbc338ccd266763519b1e31890296fecf46 GIT binary patch literal 337 zcmZpQaM=ICFZZ3J{ zR+S&cqf9f`1V&DrdW3Is&DqoI`4%N+c$hH01w!G%;?m^AJpGiMbO%E{13d%XqGUb8 z<;jVqDHRqenJHG;sg+h$X1>{JCgCN{CaJ+;$*$&QQKdlvm8QYj zxh5Wo`GI8?hM9)R$+;>Qk2+yL*{eKr69 literal 0 HcmV?d00001 diff --git a/token/delegation/delegationtest/data/TokenCarolCarol.dagcbor b/token/delegation/delegationtest/data/TokenCarolCarol.dagcbor new file mode 100644 index 0000000000000000000000000000000000000000..5e9c7e17b066b94b5b5293ef7bf711e43e30cbff GIT binary patch literal 337 zcmZpQaNucPWa&|CxFz^P$OLwy8Ly@G6>VSns>4>W^v;bf7q|>Kqy>TT$-Gir=OCO?qH~Apl6_4l&ojC zJUOv6rNSa5GsP-9wbH7}%r`qL+%43?qa@9@urxf+)Ymo8vdGA&D!j1L+0U>nvC6&N zEZwZ2Dmk+%(=RMJIX9(HKeeJDF)t-GML#b;H#09WCmAU4EjhEem;!?f@^c!Ji%XNp ZHzGAJKQB4ehk=oanT3^&or9B$8vq68eTo18 literal 0 HcmV?d00001 diff --git a/token/delegation/delegationtest/data/TokenDanDan.dagcbor b/token/delegation/delegationtest/data/TokenDanDan.dagcbor new file mode 100644 index 0000000000000000000000000000000000000000..d9a2470e1d657047ed2db771edffa5324c608b98 GIT binary patch literal 337 zcmZpQa1g)y#IxbRZts7O?r!TpbL?&M1ymsYa!NeRo9>-Tp& zo5uB@;YcCpQGud@*Hh9AE8nfDVqKJ&;bFq~76^q4i%XLe^Yl}4(j5%-4D<|ii<0#W zmnSEdrc_v@WTseUr&d~3nfYcH`naZtl!cjEl$xd_8HE;SWEU8PZeu|B<7{0rs(J8=Vs<5<|G3Jz9nZC7gJzxL4Hm{a&c)A a`9`GX<>w`*`Y8aRUH+<$zuQ literal 0 HcmV?d00001 diff --git a/token/delegation/delegationtest/data/TokenErinErin.dagcbor b/token/delegation/delegationtest/data/TokenErinErin.dagcbor new file mode 100644 index 0000000000000000000000000000000000000000..72936bf3873c267b3db89c1275a5219549890230 GIT binary patch literal 337 zcmZpQaG1yVydg>?!)2-YX|vVOUpCzcDp%*v`y8~$ZS#)9$1X>&<^H_$W#5&A!xb&B zrn=Ak8@~LjPx#uzZAQ0SWnx8Fb1zEF@GxO~3xvXj#ihxKdHN|i=?;c^26_g%Mag=G z%aapJQz|S{GE=OwQ!A~i%zU#8OhW^G!+lLl3L+vR3XKA@%@ciHgMC6xGM&vWyaRI0 zgWXe-jU2s;{IZ;rlXFuF^;0Vf67y10Q}pxlb2IZ2bCQ7q-;y(nizzU;AU~%exwtfm Zd?Qlx^7E2YeHa*-m|0la*f}`4xB>I?d|?0p literal 0 HcmV?d00001 diff --git a/token/delegation/delegationtest/data/TokenFrankFrank.dagcbor b/token/delegation/delegationtest/data/TokenFrankFrank.dagcbor new file mode 100644 index 0000000000000000000000000000000000000000..d669f93a965d7ab4dc8c23a2352fd167770ff841 GIT binary patch literal 337 zcmZpQaER*Wia6pp!C1C4)#39c^T`pOQEhH+JLeo+z@)RI^SPuZPf4@7X0p>X_nACP zAC#!1xc2sY?s@%ivz9}rI`4U=MTr?6CX8=^P`I$TG&wO(KP4yK!BEdY&p@{*S PayloadModel --> dagCbor --> PayloadModel --> dagJson // function: DecodeDagJson() Seal() Unseal() EncodeDagJson() - p1, err := invocation.FromDagJson(newDagJson) + p1, err := invocation.FromDagJson(fullExampleDagJson) require.NoError(t, err) cborBytes, id, err := p1.ToSealed(privKey) require.NoError(t, err) - assert.Equal(t, newCID, envelope.CIDToBase58BTC(id)) + assert.Equal(t, fullExampleCID, envelope.CIDToBase58BTC(id)) p2, c2, err := invocation.FromSealed(cborBytes) require.NoError(t, err) @@ -44,13 +48,13 @@ func TestSchemaRoundTrip(t *testing.T) { readJson, err := p2.ToDagJson(privKey) require.NoError(t, err) - assert.JSONEq(t, string(newDagJson), string(readJson)) + assert.JSONEq(t, string(fullExampleDagJson), string(readJson)) }) t.Run("via streaming", func(t *testing.T) { t.Parallel() - buf := bytes.NewBuffer(newDagJson) + buf := bytes.NewBuffer(fullExampleDagJson) // format: dagJson --> PayloadModel --> dagCbor --> PayloadModel --> dagJson // function: DecodeDagJson() Seal() Unseal() EncodeDagJson() @@ -61,7 +65,7 @@ func TestSchemaRoundTrip(t *testing.T) { cborBytes := &bytes.Buffer{} id, err := p1.ToSealedWriter(cborBytes, privKey) require.NoError(t, err) - assert.Equal(t, newCID, envelope.CIDToBase58BTC(id)) + assert.Equal(t, fullExampleCID, envelope.CIDToBase58BTC(id)) p2, c2, err := invocation.FromSealedReader(cborBytes) require.NoError(t, err) @@ -70,7 +74,7 @@ func TestSchemaRoundTrip(t *testing.T) { readJson := &bytes.Buffer{} require.NoError(t, p2.ToDagJsonWriter(readJson, privKey)) - assert.JSONEq(t, string(newDagJson), readJson.String()) + assert.JSONEq(t, string(fullExampleDagJson), readJson.String()) }) } diff --git a/token/invocation/testdata/full_example.dagjson b/token/invocation/testdata/full_example.dagjson new file mode 100644 index 0000000..00b1b1f --- /dev/null +++ b/token/invocation/testdata/full_example.dagjson @@ -0,0 +1 @@ +[{"/":{"bytes":"tRKNRahqwdyR6OpytuGIdcYI7HxXvKI5I594zznCLbN2C6WP5f8FIfIQlo0Nnqg4xFgKjJGAbIEVqeCZdib1Dw"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/inv@1.0.0-rc.1":{"args":{"headers":{"Content-Type":"application/json"},"payload":{"body":"UCAN is great","draft":true,"title":"UCAN for Fun and Profit","topics":["authz","journal"]},"uri":"https://example.com/blog/posts"},"cmd":"/crud/create","exp":1753965668,"iss":"did:key:z6MkuScdGeTmbWubyoWWpPmX9wkwdZAshkTcLKb1bf4Cyj8N","meta":{"env":"development","tags":["blog","post","pr#123"]},"nonce":{"/":{"bytes":"BBR5znl7VpRof4ac"}},"prf":[{"/":"bafyreigx3qxd2cndpe66j2mdssj773ecv7tqd7wovcnz5raguw6lj7sjoe"},{"/":"bafyreib34ira254zdqgehz6f2bhwme2ja2re3ltcalejv4x4tkcveujvpa"},{"/":"bafyreibkb66tpo2ixqx3fe5hmekkbuasrod6olt5bwm5u5pi726mduuwlq"}],"sub":"did:key:z6MkuQU8kqxCAUeurotHyrnMgkMUBtJN8ozYxkwctnop4zzB"}}] \ No newline at end of file diff --git a/token/invocation/testdata/new.dagjson b/token/invocation/testdata/new.dagjson index 00b1b1f..04615ff 100644 --- a/token/invocation/testdata/new.dagjson +++ b/token/invocation/testdata/new.dagjson @@ -1 +1 @@ -[{"/":{"bytes":"tRKNRahqwdyR6OpytuGIdcYI7HxXvKI5I594zznCLbN2C6WP5f8FIfIQlo0Nnqg4xFgKjJGAbIEVqeCZdib1Dw"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/inv@1.0.0-rc.1":{"args":{"headers":{"Content-Type":"application/json"},"payload":{"body":"UCAN is great","draft":true,"title":"UCAN for Fun and Profit","topics":["authz","journal"]},"uri":"https://example.com/blog/posts"},"cmd":"/crud/create","exp":1753965668,"iss":"did:key:z6MkuScdGeTmbWubyoWWpPmX9wkwdZAshkTcLKb1bf4Cyj8N","meta":{"env":"development","tags":["blog","post","pr#123"]},"nonce":{"/":{"bytes":"BBR5znl7VpRof4ac"}},"prf":[{"/":"bafyreigx3qxd2cndpe66j2mdssj773ecv7tqd7wovcnz5raguw6lj7sjoe"},{"/":"bafyreib34ira254zdqgehz6f2bhwme2ja2re3ltcalejv4x4tkcveujvpa"},{"/":"bafyreibkb66tpo2ixqx3fe5hmekkbuasrod6olt5bwm5u5pi726mduuwlq"}],"sub":"did:key:z6MkuQU8kqxCAUeurotHyrnMgkMUBtJN8ozYxkwctnop4zzB"}}] \ No newline at end of file +[{"/":{"bytes":"8BxXBbXtPVoqn/z804w2w2gZH9m6kT55ivv7u2kxqptAfDcFzlRWBu3YKE9ijfIezpa79Btq5ja0PpqwjfSLAw"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/inv@1.0.0-rc.1":{"args":{"foo":"bar"},"cmd":"/foo/bar","exp":7258118400,"iat":4102444800,"iss":"did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC","meta":{"baz":123},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"prf":[{"/":"bafyreifa35rjstdm37cjudzs72ab22rnh5blny725khtapox63fnsj6pbe"}],"sub":"did:key:z6Mkf4WtCwPDtamsZvBJA4eSVcE7vZuRPy5Skm4HaoQv81i1"}}] \ No newline at end of file diff --git a/token/invocation/testdata/selfsigned.dagjson b/token/invocation/testdata/selfsigned.dagjson new file mode 100644 index 0000000..a220d21 --- /dev/null +++ b/token/invocation/testdata/selfsigned.dagjson @@ -0,0 +1 @@ +[{"/":{"bytes":"ejXoQIdp3OGXewEkfQF4Z4Vd8c3H0XF319dsNh5DEP/2l9Nt9H1IhMpks1+HXoYFOKN3QmtxpPMoYmf/rhKaAQ"}},{"h":{"/":{"bytes":"NAHtAe0BE3E"}},"ucan/inv@1.0.0-rc.1":{"args":{"foo":"bar"},"cmd":"/foo/bar","exp":7258118400,"iat":4102444800,"iss":"did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC","meta":{"baz":123},"nonce":{"/":{"bytes":"NnJvRGhHaTBraU5yaVFBejdKM2QrYk9lb0kvdGo4RU5pa21RTmJ0am5EMA"}},"prf":[],"sub":"did:key:z6MknUz1mSj4pvS6aUUHekCHdUPv7HBhDyDBZQ2W3Vujc5qC"}}] \ No newline at end of file