diff --git a/go.mod b/go.mod index b029fcd..febadd3 100644 --- a/go.mod +++ b/go.mod @@ -3,31 +3,27 @@ module enclave go 1.25.5 require ( - github.com/Oudwins/zog v0.22.0 - github.com/cosmos/cosmos-sdk v0.53.5 github.com/extism/go-pdk v1.1.3 - github.com/golang-jwt/jwt/v5 v5.3.0 - github.com/ipfs/go-cid v0.6.0 - github.com/libp2p/go-libp2p/core v0.43.0-rc2 - github.com/multiformats/go-multihash v0.2.3 + github.com/ipld/go-ipld-prime v0.21.0 github.com/ncruces/go-sqlite3 v0.30.4 github.com/sonr-io/crypto v1.0.1 github.com/stretchr/testify v1.11.1 + github.com/ucan-wg/go-ucan v1.1.0 golang.org/x/crypto v0.46.0 - lukechampine.com/blake3 v1.4.1 ) require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/MetaMask/go-did-it v1.0.0-pre1 // indirect github.com/bits-and-blooms/bitset v1.24.3 // indirect github.com/btcsuite/btcd/btcec/v2 v2.3.4 // indirect github.com/bwesterb/go-ristretto v1.2.3 // indirect github.com/consensys/gnark-crypto v0.19.0 // indirect - github.com/cosmos/btcutil v1.0.5 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 // indirect github.com/gtank/merlin v0.1.1 // indirect + github.com/ipfs/go-cid v0.5.0 // indirect github.com/klauspost/cpuid/v2 v2.2.10 // indirect github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 // indirect github.com/minio/sha256-simd v1.0.1 // indirect @@ -35,14 +31,18 @@ require ( github.com/multiformats/go-base32 v0.1.0 // indirect github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multibase v0.2.0 // indirect + github.com/multiformats/go-multicodec v0.9.0 // indirect + github.com/multiformats/go-multihash v0.2.3 // indirect github.com/multiformats/go-varint v0.1.0 // indirect github.com/ncruces/julianday v1.0.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/polydawn/refmt v0.89.0 // indirect + github.com/rogpeppe/go-internal v1.14.1 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect github.com/tetratelabs/wazero v1.11.0 // indirect - golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 // indirect + github.com/ucan-wg/go-varsig v1.0.0 // indirect golang.org/x/sys v0.39.0 // indirect - google.golang.org/protobuf v1.36.10 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/blake3 v1.4.1 // indirect ) diff --git a/go.sum b/go.sum index 541838d..2dd919f 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,8 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= -github.com/Oudwins/zog v0.22.0 h1:HUJddjSQPyAp70m5toDDgaAVOMlJMQcjCTrjiO79bmA= -github.com/Oudwins/zog v0.22.0/go.mod h1:c4ADJ2zNkJp37ZViNy1o3ZZoeMvO7UQVO7BaPtRoocg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/MetaMask/go-did-it v1.0.0-pre1 h1:NTGAC7z52TwFegEF7c+csUr/6Al1nAo6ValAAxOsjto= +github.com/MetaMask/go-did-it v1.0.0-pre1/go.mod h1:7m9syDnXFTg5GmUEcydpO4Rs3eYT4McFH7vCw5fp3A4= github.com/bits-and-blooms/bitset v1.24.3 h1:Bte86SlO3lwPQqww+7BE9ZuUCKIjfqnG5jtEyqA9y9Y= github.com/bits-and-blooms/bitset v1.24.3/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8= github.com/btcsuite/btcd/btcec/v2 v2.3.4 h1:3EJjcN70HCu/mwqlUsGK8GcNVyLVxFDlWurTXGPFfiQ= @@ -10,28 +11,30 @@ github.com/bwesterb/go-ristretto v1.2.3 h1:1w53tCkGhCQ5djbat3+MH0BAQ5Kfgbt56UZQ/ github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/consensys/gnark-crypto v0.19.0 h1:zXCqeY2txSaMl6G5wFpZzMWJU9HPNh8qxPnYJ1BL9vA= github.com/consensys/gnark-crypto v0.19.0/go.mod h1:rT23F0XSZqE0mUA0+pRtnL56IbPxs6gp4CeRsBk4XS0= -github.com/cosmos/btcutil v1.0.5 h1:t+ZFcX77LpKtDBhjucvnOH8C2l2ioGsBNEQ3jef8xFk= -github.com/cosmos/btcutil v1.0.5/go.mod h1:IyB7iuqZMJlthe2tkIFL33xPyzbFYP0XVdS8P5lUPis= -github.com/cosmos/cosmos-sdk v0.53.5 h1:JPue+SFn2gyDzTV9TYb8mGpuIH3kGt7WbGadulkpTcU= -github.com/cosmos/cosmos-sdk v0.53.5/go.mod h1:AQJx0jpon70WAD4oOs/y+SlST4u7VIwEPR6F8S7JMdo= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/decred/dcrd/crypto/blake256 v1.1.0 h1:zPMNGQCm0g4QTY27fOCorQW7EryeQ/U0x++OzVrdms8= -github.com/decred/dcrd/crypto/blake256 v1.1.0/go.mod h1:2OfgNZ5wDpcsFmHmCK5gZTPcCXqlm2ArzUIkw9czNJo= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564 h1:I6KUy4CI6hHjqnyJLNCEi7YHVMkwwtfSr2k9splgdSM= github.com/dustinxie/ecc v0.0.0-20210511000915-959544187564/go.mod h1:yekO+3ZShy19S+bsmnERmznGy9Rfg6dWWWpiGJjNAz8= github.com/extism/go-pdk v1.1.3 h1:hfViMPWrqjN6u67cIYRALZTZLk/enSPpNKa+rZ9X2SQ= github.com/extism/go-pdk v1.1.3/go.mod h1:Gz+LIU/YCKnKXhgge8yo5Yu1F/lbv7KtKFkiCSzW/P4= -github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= -github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= -github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +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/go-yaml/yaml v2.1.0+incompatible/go.mod h1:w2MrLa16VYP0jy6N7M5kHaCkaLENm+P+Tv+MfurjSw0= +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/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/gtank/merlin v0.1.1 h1:eQ90iG7K9pOhtereWsmyRJ6RAwcP4tHTDBHXNg+u5is= github.com/gtank/merlin v0.1.1/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= -github.com/ipfs/go-cid v0.6.0 h1:DlOReBV1xhHBhhfy/gBNNTSyfOM6rLiIx9J7A4DGf30= -github.com/ipfs/go-cid v0.6.0/go.mod h1:NC4kS1LZjzfhK40UGmpXv5/qD2kcMzACYJNntCUiDhQ= +github.com/ipfs/go-cid v0.5.0 h1:goEKKhaGm0ul11IHA7I6p1GmKz8kEYniqFopaB5Otwg= +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/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/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE= github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -40,10 +43,6 @@ github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/leanovate/gopter v0.2.11 h1:vRjThO1EKPb/1NsDXuDrzldR28RLkBflWYcU9CvzWu4= github.com/leanovate/gopter v0.2.11/go.mod h1:aK3tzZP/C+p1m3SPRE4SYZFGP7jjkuSI4f7Xvpt0S9c= -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/core v0.43.0-rc2 h1:1X1aDJNWhMfodJ/ynbaGLkgnC8f+hfBIqQDrzxFZOqI= -github.com/libp2p/go-libp2p/core v0.43.0-rc2/go.mod h1:NYeJ9lvyBv9nbDk2IuGb8gFKEOkIv/W5YRIy1pAJB2Q= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643 h1:hLDRPB66XQT/8+wG9WsDpiCvZf1yKO7sz7scAjSlBa0= github.com/mimoo/StrobeGo v0.0.0-20181016162300-f8f6d4d2b643/go.mod h1:43+3pMjjKimDBf5Kr4ZFNGbLql1zKkbImw+fZbw3geM= github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= @@ -54,12 +53,10 @@ 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-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-multiaddr v0.16.0 h1:oGWEVKioVQcdIOBlYM8BH1rZDWOGJSqr9/BKl6zQ4qc= -github.com/multiformats/go-multiaddr v0.16.0/go.mod h1:JSVUmXDjsVFiW7RjIFMP7+Ev+h1DTbiJgVeTV/tcmP0= 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-multicodec v0.9.1 h1:x/Fuxr7ZuR4jJV4Os5g444F7xC4XmyUaT/FWtE+9Zjo= -github.com/multiformats/go-multicodec v0.9.1/go.mod h1:LLWNMtyV5ithSBUo3vFIMaeDy+h3EbkMTek1m+Fybbo= +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-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-varint v0.1.0 h1:i2wqFp4sdl3IcIxfAonHQV9qU5OsZ4Ts9IOoETFs5dI= @@ -70,10 +67,19 @@ github.com/ncruces/julianday v1.0.0 h1:fH0OKwa7NWvniGQtxdJRxAgkBMolni2BjDHaWTxqt github.com/ncruces/julianday v1.0.0/go.mod h1:Dusn2KvZrrovOMJuOt0TNXL6tB7U2E8kvza5fFc9G7g= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= +github.com/polydawn/refmt v0.89.0/go.mod h1:/zvteZs/GwLtCgZ4BL6CBsk9IKIlexP43ObX9AxTqTw= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +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/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= +github.com/smartystreets/goconvey v1.7.2 h1:9RBaZCeXEQ3UselpuwUQHltGVXvdwm6cv1hgR6gDIPg= +github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM= github.com/sonr-io/crypto v1.0.1 h1:pTsWbdvs8I8zTMalfCK7/ecCvFkBw9VIb/bKKGwMWGw= github.com/sonr-io/crypto v1.0.1/go.mod h1:f6YZo/FfbUQEEN8TMPAeFI8BOljbDNrui3IXuIzCa/E= github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= @@ -82,19 +88,30 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/tetratelabs/wazero v1.11.0 h1:+gKemEuKCTevU4d7ZTzlsvgd1uaToIDtlQlmNbwqYhA= github.com/tetratelabs/wazero v1.11.0/go.mod h1:eV28rsN8Q+xwjogd7f4/Pp4xFxO7uOGbLcD/LzB1wiU= +github.com/ucan-wg/go-ucan v1.1.0 h1:Z4RGSjJrpLN7S9u93Md036XbyYprloe1LUyDZe9rnWg= +github.com/ucan-wg/go-ucan v1.1.0/go.mod h1:9Gnfx2XO5OCjL0PGipfDDgK423OAzyNEY+kJJQ5D4Qo= +github.com/ucan-wg/go-varsig v1.0.0 h1:Hrc437Zg+B5Eoajg+qZQZI3Q3ocPyjlnp3/Bz9ZnlWw= +github.com/ucan-wg/go-varsig v1.0.0/go.mod h1:Sakln6IPooDPH+ClQ0VvR09TuwUhHcfLqcPiPkMZGh0= +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/go.mod h1:x6AKhvSSexNrVSrViXSHUEbICjmGXhtgABaHIySUSGw= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU= golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476 h1:bsqhLWFR6G6xiQcb+JoGqdKdRU6WzPWmK8E0jxTjzo4= -golang.org/x/exp v0.0.0-20250606033433-dcc06ee1d476/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8= +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.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +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/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg= diff --git a/internal/crypto/mpc/spec/jwt.go b/internal/crypto/mpc/spec/jwt.go deleted file mode 100644 index 7b82bb8..0000000 --- a/internal/crypto/mpc/spec/jwt.go +++ /dev/null @@ -1,116 +0,0 @@ -package spec - -import ( - "crypto/sha256" - "encoding/base64" - "fmt" - - "github.com/golang-jwt/jwt/v5" - "github.com/sonr-io/crypto/mpc" -) - -// MPCSigningMethod implements the SigningMethod interface for MPC-based signing -type MPCSigningMethod struct { - Name string - enclave mpc.Enclave -} - -// NewJWTSigningMethod creates a new MPC signing method with the given enclave -func NewJWTSigningMethod(name string, enclave mpc.Enclave) *MPCSigningMethod { - return &MPCSigningMethod{ - Name: name, - enclave: enclave, - } -} - -// WithEnclave sets the enclave for an existing signing method -func (m *MPCSigningMethod) WithEnclave(enclave mpc.Enclave) *MPCSigningMethod { - return &MPCSigningMethod{ - Name: m.Name, - enclave: enclave, - } -} - -// NewMPCSigningMethod is an alias for NewJWTSigningMethod for compatibility -func NewMPCSigningMethod(name string, enclave mpc.Enclave) *MPCSigningMethod { - return NewJWTSigningMethod(name, enclave) -} - -// Alg returns the signing method's name -func (m *MPCSigningMethod) Alg() string { - return m.Name -} - -// Verify verifies the signature using the MPC public key -func (m *MPCSigningMethod) Verify(signingString string, signature []byte, key any) error { - // Check if enclave is available - if m.enclave == nil { - return fmt.Errorf("MPC enclave not available for signature verification") - } - - // Decode the signature - sig, err := base64.RawURLEncoding.DecodeString(string(signature)) - if err != nil { - return fmt.Errorf("failed to decode signature: %w", err) - } - - // Hash the signing string using SHA-256 - hasher := sha256.New() - hasher.Write([]byte(signingString)) - digest := hasher.Sum(nil) - - // Use MPC enclave to verify signature - valid, err := m.enclave.Verify(digest, sig) - if err != nil { - return fmt.Errorf("failed to verify signature: %w", err) - } - - if !valid { - return fmt.Errorf("signature verification failed") - } - - return nil -} - -// Sign signs the data using MPC -func (m *MPCSigningMethod) Sign(signingString string, key any) ([]byte, error) { - // Check if enclave is available - if m.enclave == nil { - return nil, fmt.Errorf("MPC enclave not available for signing") - } - - // Hash the signing string using SHA-256 - hasher := sha256.New() - hasher.Write([]byte(signingString)) - digest := hasher.Sum(nil) - - // Use MPC enclave to sign the digest - sig, err := m.enclave.Sign(digest) - if err != nil { - return nil, fmt.Errorf("failed to sign with MPC: %w", err) - } - - // Encode the signature as base64url - encoded := base64.RawURLEncoding.EncodeToString(sig) - return []byte(encoded), nil -} - -func init() { - // Register the MPC signing method factory - jwt.RegisterSigningMethod("MPC256", func() jwt.SigningMethod { - // This factory creates a new instance without enclave - // The enclave will be provided when creating tokens - return &MPCSigningMethod{ - Name: "MPC256", - } - }) -} - -// RegisterMPCMethod registers an MPC signing method for the given algorithm name -func RegisterMPCMethod(alg string) { - jwt.RegisterSigningMethod(alg, func() jwt.SigningMethod { - return &MPCSigningMethod{ - Name: alg, - } - }) -} diff --git a/internal/crypto/mpc/spec/source.go b/internal/crypto/mpc/spec/source.go deleted file mode 100644 index 647e991..0000000 --- a/internal/crypto/mpc/spec/source.go +++ /dev/null @@ -1,305 +0,0 @@ -package spec - -import ( - "fmt" - "strings" - "time" - - "github.com/golang-jwt/jwt/v5" - "github.com/libp2p/go-libp2p/core/crypto" - "github.com/sonr-io/crypto/keys" - "github.com/sonr-io/crypto/mpc" - "lukechampine.com/blake3" -) - -// KeyshareSource provides MPC-based UCAN token creation and validation -type KeyshareSource interface { - Address() string - Issuer() string - ChainCode() ([]byte, error) - OriginToken() (*Token, error) - SignData(data []byte) ([]byte, error) - VerifyData(data []byte, sig []byte) (bool, error) - Enclave() mpc.Enclave - - // UCAN token creation methods - NewOriginToken( - audienceDID string, - att []Attenuation, - fct []Fact, - notBefore, expires time.Time, - ) (*Token, error) - NewAttenuatedToken( - parent *Token, - audienceDID string, - att []Attenuation, - fct []Fact, - nbf, exp time.Time, - ) (*Token, error) -} - -// NewSource creates a new MPC-based keyshare source from an enclave -func NewSource(enclave mpc.Enclave) (KeyshareSource, error) { - if !enclave.IsValid() { - return nil, fmt.Errorf("invalid MPC enclave provided") - } - - pubKeyBytes := enclave.PubKeyBytes() - issuerDID, addr, err := getIssuerDIDFromBytes(pubKeyBytes) - if err != nil { - return nil, fmt.Errorf("failed to derive issuer DID: %w", err) - } - - return &mpcKeyshareSource{ - enclave: enclave, - issuerDID: issuerDID, - addr: addr, - }, nil -} - -// mpcKeyshareSource implements KeyshareSource using MPC enclave -type mpcKeyshareSource struct { - enclave mpc.Enclave - issuerDID string - addr string -} - -// Address returns the address derived from the enclave public key -func (k *mpcKeyshareSource) Address() string { - return k.addr -} - -// Issuer returns the DID of the issuer derived from the enclave public key -func (k *mpcKeyshareSource) Issuer() string { - return k.issuerDID -} - -// Enclave returns the underlying MPC enclave -func (k *mpcKeyshareSource) Enclave() mpc.Enclave { - return k.enclave -} - -// ChainCode derives a deterministic chain code from the enclave -func (k *mpcKeyshareSource) ChainCode() ([]byte, error) { - // Sign the address to create a deterministic chain code - sig, err := k.SignData([]byte(k.addr)) - if err != nil { - return nil, fmt.Errorf("failed to sign address for chain code: %w", err) - } - - // Hash the signature to create a 32-byte chain code - hash := blake3.Sum256(sig) - return hash[:32], nil -} - -// OriginToken creates a default origin token with basic capabilities -func (k *mpcKeyshareSource) OriginToken() (*Token, error) { - // Create basic capability for the MPC keyshare - resource := &SimpleResource{ - Scheme: "mpc", - Value: k.addr, - URI: fmt.Sprintf("mpc://%s", k.addr), - } - - capability := &SimpleCapability{Action: "sign"} - - attenuation := Attenuation{ - Capability: capability, - Resource: resource, - } - - // Create token with no expiration for origin token - zero := time.Time{} - return k.NewOriginToken(k.issuerDID, []Attenuation{attenuation}, nil, zero, zero) -} - -// SignData signs data using the MPC enclave -func (k *mpcKeyshareSource) SignData(data []byte) ([]byte, error) { - if !k.enclave.IsValid() { - return nil, fmt.Errorf("enclave is not valid") - } - - return k.enclave.Sign(data) -} - -// VerifyData verifies a signature using the MPC enclave -func (k *mpcKeyshareSource) VerifyData(data []byte, sig []byte) (bool, error) { - if !k.enclave.IsValid() { - return false, fmt.Errorf("enclave is not valid") - } - - return k.enclave.Verify(data, sig) -} - -// NewOriginToken creates a new UCAN origin token using MPC signing -func (k *mpcKeyshareSource) NewOriginToken( - audienceDID string, - att []Attenuation, - fct []Fact, - notBefore, expires time.Time, -) (*Token, error) { - return k.newToken(audienceDID, nil, att, fct, notBefore, expires) -} - -// NewAttenuatedToken creates a new attenuated UCAN token using MPC signing -func (k *mpcKeyshareSource) NewAttenuatedToken( - parent *Token, - audienceDID string, - att []Attenuation, - fct []Fact, - nbf, exp time.Time, -) (*Token, error) { - // Validate that new attenuations are more restrictive than parent - if !isAttenuationSubset(att, parent.Attenuations) { - return nil, fmt.Errorf("scope of ucan attenuations must be less than its parent") - } - - // Add parent as proof - proofs := []Proof{} - if parent.Raw != "" { - proofs = append(proofs, Proof(parent.Raw)) - } - proofs = append(proofs, parent.Proofs...) - - return k.newToken(audienceDID, proofs, att, fct, nbf, exp) -} - -// newToken creates a new UCAN token with MPC signing -func (k *mpcKeyshareSource) newToken( - audienceDID string, - proofs []Proof, - att []Attenuation, - fct []Fact, - nbf, exp time.Time, -) (*Token, error) { - // Validate audience DID - if !isValidDID(audienceDID) { - return nil, fmt.Errorf("invalid audience DID: %s", audienceDID) - } - - // Create JWT with MPC signing method - t := jwt.New(NewJWTSigningMethod("MPC256", k.enclave)) - - // Set UCAN version header - t.Header[UCANVersionKey] = UCANVersion - - var ( - nbfUnix int64 - expUnix int64 - ) - - if !nbf.IsZero() { - nbfUnix = nbf.Unix() - } - if !exp.IsZero() { - expUnix = exp.Unix() - } - - // Convert attenuations to claim format - attClaims := make([]map[string]any, len(att)) - for i, a := range att { - attClaims[i] = map[string]any{ - "can": a.Capability.GetActions(), - "with": a.Resource.GetURI(), - } - } - - // Convert proofs to strings - proofStrings := make([]string, len(proofs)) - for i, proof := range proofs { - proofStrings[i] = string(proof) - } - - // Convert facts to any slice - factData := make([]any, len(fct)) - for i, fact := range fct { - factData[i] = string(fact.Data) - } - - // Set claims - claims := jwt.MapClaims{ - "iss": k.issuerDID, - "aud": audienceDID, - "att": attClaims, - } - - if nbfUnix > 0 { - claims["nbf"] = nbfUnix - } - if expUnix > 0 { - claims["exp"] = expUnix - } - if len(proofStrings) > 0 { - claims["prf"] = proofStrings - } - if len(factData) > 0 { - claims["fct"] = factData - } - - t.Claims = claims - - // Sign the token using MPC enclave - tokenString, err := t.SignedString(nil) - if err != nil { - return nil, fmt.Errorf("failed to sign token: %w", err) - } - - return &Token{ - Raw: tokenString, - Issuer: k.issuerDID, - Audience: audienceDID, - ExpiresAt: expUnix, - NotBefore: nbfUnix, - Attenuations: att, - Proofs: proofs, - Facts: fct, - }, nil -} - -// isAttenuationSubset checks if child attenuations are a subset of parent attenuations -func isAttenuationSubset(child, parent []Attenuation) bool { - for _, childAtt := range child { - if !containsAttenuation(parent, childAtt) { - return false - } - } - return true -} - -// containsAttenuation checks if the parent list contains an equivalent attenuation -func containsAttenuation(parent []Attenuation, att Attenuation) bool { - for _, parentAtt := range parent { - if parentAtt.Resource.Matches(att.Resource) && - parentAtt.Capability.Contains(att.Capability) { - return true - } - } - return false -} - -// isValidDID validates DID format -func isValidDID(did string) bool { - return did != "" && len(did) > 5 && strings.HasPrefix(did, "did:") -} - -// getIssuerDIDFromBytes creates an issuer DID and address from public key bytes -func getIssuerDIDFromBytes(pubKeyBytes []byte) (string, string, error) { - // Convert MPC public key bytes to libp2p crypto.PubKey - pubKey, err := crypto.UnmarshalSecp256k1PublicKey(pubKeyBytes) - if err != nil { - return "", "", fmt.Errorf("failed to unmarshal secp256k1 key: %w", err) - } - - // Create DID using the crypto/keys package - did, err := keys.NewDID(pubKey) - if err != nil { - return "", "", fmt.Errorf("failed to create DID: %w", err) - } - - didStr := did.String() - - // Generate address from DID (simplified implementation) - address := fmt.Sprintf("addr_%x", pubKeyBytes[:8]) - - return didStr, address, nil -} diff --git a/internal/crypto/mpc/spec/ucan.go b/internal/crypto/mpc/spec/ucan.go deleted file mode 100644 index 01d9463..0000000 --- a/internal/crypto/mpc/spec/ucan.go +++ /dev/null @@ -1,125 +0,0 @@ -package spec - -import ( - "encoding/json" - "fmt" - "strings" - - "github.com/cosmos/cosmos-sdk/types/bech32" -) - -// Token represents a UCAN JWT token with parsed claims -type Token struct { - Raw string `json:"raw"` - Issuer string `json:"iss"` - Audience string `json:"aud"` - ExpiresAt int64 `json:"exp,omitempty"` - NotBefore int64 `json:"nbf,omitempty"` - Attenuations []Attenuation `json:"att"` - Proofs []Proof `json:"prf,omitempty"` - Facts []Fact `json:"fct,omitempty"` -} - -// Attenuation represents a UCAN capability attenuation -type Attenuation struct { - Capability Capability `json:"can"` - Resource Resource `json:"with"` -} - -// Proof represents a UCAN delegation proof (either JWT or CID) -type Proof string - -// Fact represents arbitrary facts in UCAN tokens -type Fact struct { - Data json.RawMessage `json:"data"` -} - -// Capability defines what actions can be performed -type Capability interface { - GetActions() []string - Grants(abilities []string) bool - Contains(other Capability) bool - String() string -} - -// Resource defines what resource the capability applies to -type Resource interface { - GetScheme() string - GetValue() string - GetURI() string - Matches(other Resource) bool -} - -// SimpleCapability implements Capability for single actions -type SimpleCapability struct { - Action string `json:"action"` -} - -func (c *SimpleCapability) GetActions() []string { return []string{c.Action} } -func (c *SimpleCapability) Grants(abilities []string) bool { - return len(abilities) == 1 && c.Action == abilities[0] -} - -func (c *SimpleCapability) Contains( - other Capability, -) bool { - return c.Action == other.GetActions()[0] -} -func (c *SimpleCapability) String() string { return c.Action } - -// SimpleResource implements Resource for basic URI resources -type SimpleResource struct { - Scheme string `json:"scheme"` - Value string `json:"value"` - URI string `json:"uri"` -} - -func (r *SimpleResource) GetScheme() string { return r.Scheme } -func (r *SimpleResource) GetValue() string { return r.Value } -func (r *SimpleResource) GetURI() string { return r.URI } -func (r *SimpleResource) Matches(other Resource) bool { return r.URI == other.GetURI() } - -// UCAN constants -const ( - UCANVersion = "0.9.0" - UCANVersionKey = "ucv" - PrfKey = "prf" - FctKey = "fct" - AttKey = "att" - CapKey = "cap" -) - -// CreateSimpleAttenuation creates a basic attenuation -func CreateSimpleAttenuation(action, resourceURI string) Attenuation { - return Attenuation{ - Capability: &SimpleCapability{Action: action}, - Resource: parseResourceURI(resourceURI), - } -} - -// parseResourceURI creates a Resource from URI string -func parseResourceURI(uri string) Resource { - parts := strings.SplitN(uri, "://", 2) - if len(parts) != 2 { - return &SimpleResource{ - Scheme: "unknown", - Value: uri, - URI: uri, - } - } - - return &SimpleResource{ - Scheme: parts[0], - Value: parts[1], - URI: uri, - } -} - -// getIssuerDIDFromBytes creates an issuer DID and address from public key bytes (alternative implementation) -func getIssuerDIDFromBytesAlt(pubKeyBytes []byte) (string, string, error) { - addr, err := bech32.ConvertAndEncode("idx", pubKeyBytes) - if err != nil { - return "", "", fmt.Errorf("failed to encode address: %w", err) - } - return fmt.Sprintf("did:sonr:%s", addr), addr, nil -} diff --git a/internal/crypto/ucan/capability.go b/internal/crypto/ucan/capability.go deleted file mode 100644 index 3833e04..0000000 --- a/internal/crypto/ucan/capability.go +++ /dev/null @@ -1,860 +0,0 @@ -// Package ucan provides User-Controlled Authorization Networks (UCAN) implementation -// for decentralized authorization and capability delegation in the Sonr network. -// This package handles JWT-based tokens, cryptographic verification, and resource capabilities. -package ucan - -import ( - "encoding/json" - "fmt" - "strings" - "time" -) - -// Token represents a UCAN JWT token with parsed claims -type Token struct { - Raw string `json:"raw"` - Issuer string `json:"iss"` - Audience string `json:"aud"` - ExpiresAt int64 `json:"exp,omitempty"` - NotBefore int64 `json:"nbf,omitempty"` - Attenuations []Attenuation `json:"att"` - Proofs []Proof `json:"prf,omitempty"` - Facts []Fact `json:"fct,omitempty"` -} - -// Attenuation represents a UCAN capability attenuation -type Attenuation struct { - Capability Capability `json:"can"` - Resource Resource `json:"with"` -} - -// Proof represents a UCAN delegation proof (either JWT or CID) -type Proof string - -// Fact represents arbitrary facts in UCAN tokens -type Fact struct { - Data json.RawMessage `json:"data"` -} - -// Capability defines what actions can be performed -type Capability interface { - // GetActions returns the list of actions this capability grants - GetActions() []string - // Grants checks if this capability grants the required abilities - Grants(abilities []string) bool - // Contains checks if this capability contains another capability - Contains(other Capability) bool - // String returns a string representation - String() string -} - -// Resource defines what resource the capability applies to -type Resource interface { - // GetScheme returns the resource scheme (e.g., "https", "ipfs") - GetScheme() string - // GetValue returns the resource value/path - GetValue() string - // GetURI returns the full URI string - GetURI() string - // Matches checks if this resource matches another resource - Matches(other Resource) bool -} - -// SimpleCapability implements Capability for single actions -type SimpleCapability struct { - Action string `json:"action"` -} - -// GetActions returns the single action -func (c *SimpleCapability) GetActions() []string { - return []string{c.Action} -} - -// Grants checks if the capability grants all required abilities -func (c *SimpleCapability) Grants(abilities []string) bool { - if len(abilities) != 1 { - return false - } - return c.Action == abilities[0] || c.Action == "*" -} - -// Contains checks if this capability contains another capability -func (c *SimpleCapability) Contains(other Capability) bool { - if c.Action == "*" { - return true - } - - otherActions := other.GetActions() - if len(otherActions) != 1 { - return false - } - - return c.Action == otherActions[0] -} - -// String returns string representation -func (c *SimpleCapability) String() string { - return c.Action -} - -// MultiCapability implements Capability for multiple actions -type MultiCapability struct { - Actions []string `json:"actions"` -} - -// GetActions returns all actions -func (c *MultiCapability) GetActions() []string { - return c.Actions -} - -// Grants checks if the capability grants all required abilities -func (c *MultiCapability) Grants(abilities []string) bool { - actionSet := make(map[string]bool) - for _, action := range c.Actions { - actionSet[action] = true - } - - // Check if we have wildcard permission - if actionSet["*"] { - return true - } - - // Check each required ability - for _, ability := range abilities { - if !actionSet[ability] { - return false - } - } - - return true -} - -// Contains checks if this capability contains another capability -func (c *MultiCapability) Contains(other Capability) bool { - actionSet := make(map[string]bool) - for _, action := range c.Actions { - actionSet[action] = true - } - - // Wildcard contains everything - if actionSet["*"] { - return true - } - - // Check if all other actions are contained - for _, otherAction := range other.GetActions() { - if !actionSet[otherAction] { - return false - } - } - - return true -} - -// String returns string representation -func (c *MultiCapability) String() string { - return strings.Join(c.Actions, ",") -} - -// SimpleResource implements Resource for basic URI resources -type SimpleResource struct { - Scheme string `json:"scheme"` - Value string `json:"value"` - URI string `json:"uri"` -} - -// GetScheme returns the resource scheme -func (r *SimpleResource) GetScheme() string { - return r.Scheme -} - -// GetValue returns the resource value -func (r *SimpleResource) GetValue() string { - return r.Value -} - -// GetURI returns the full URI -func (r *SimpleResource) GetURI() string { - return r.URI -} - -// Matches checks if resources are equivalent -func (r *SimpleResource) Matches(other Resource) bool { - return r.URI == other.GetURI() -} - -// VaultResource represents vault-specific resources with metadata -type VaultResource struct { - SimpleResource - VaultAddress string `json:"vault_address,omitempty"` - EnclaveDataCID string `json:"enclave_data_cid,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// ServiceResource represents service-specific resources -type ServiceResource struct { - SimpleResource - ServiceID string `json:"service_id"` - Domain string `json:"domain"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// CreateSimpleAttenuation creates a basic attenuation -func CreateSimpleAttenuation(action, resourceURI string) Attenuation { - return Attenuation{ - Capability: &SimpleCapability{Action: action}, - Resource: parseResourceURI(resourceURI), - } -} - -// CreateMultiAttenuation creates an attenuation with multiple actions -func CreateMultiAttenuation(actions []string, resourceURI string) Attenuation { - return Attenuation{ - Capability: &MultiCapability{Actions: actions}, - Resource: parseResourceURI(resourceURI), - } -} - -// CreateVaultAttenuation creates a vault-specific attenuation -func CreateVaultAttenuation(actions []string, enclaveDataCID, vaultAddress string) Attenuation { - resource := &VaultResource{ - SimpleResource: SimpleResource{ - Scheme: "ipfs", - Value: enclaveDataCID, - URI: fmt.Sprintf("ipfs://%s", enclaveDataCID), - }, - VaultAddress: vaultAddress, - EnclaveDataCID: enclaveDataCID, - } - - return Attenuation{ - Capability: &MultiCapability{Actions: actions}, - Resource: resource, - } -} - -// CreateServiceAttenuation creates a service-specific attenuation -func CreateServiceAttenuation(actions []string, serviceID, domain string) Attenuation { - resourceURI := fmt.Sprintf("service://%s", serviceID) - resource := &ServiceResource{ - SimpleResource: SimpleResource{ - Scheme: "service", - Value: serviceID, - URI: resourceURI, - }, - ServiceID: serviceID, - Domain: domain, - } - - return Attenuation{ - Capability: &MultiCapability{Actions: actions}, - Resource: resource, - } -} - -// parseResourceURI creates a Resource from URI string -func parseResourceURI(uri string) Resource { - parts := strings.SplitN(uri, "://", 2) - if len(parts) != 2 { - return &SimpleResource{ - Scheme: "unknown", - Value: uri, - URI: uri, - } - } - - return &SimpleResource{ - Scheme: parts[0], - Value: parts[1], - URI: uri, - } -} - -// CapabilityTemplate provides validation and construction utilities -type CapabilityTemplate struct { - AllowedActions map[string][]string `json:"allowed_actions"` // resource_type -> []actions - DefaultExpiration time.Duration `json:"default_expiration"` // default token lifetime - MaxExpiration time.Duration `json:"max_expiration"` // maximum allowed lifetime -} - -// NewCapabilityTemplate creates a new capability template -func NewCapabilityTemplate() *CapabilityTemplate { - return &CapabilityTemplate{ - AllowedActions: make(map[string][]string), - DefaultExpiration: 24 * time.Hour, - MaxExpiration: 30 * 24 * time.Hour, // 30 days - } -} - -// AddAllowedActions adds allowed actions for a resource type -func (ct *CapabilityTemplate) AddAllowedActions(resourceType string, actions []string) { - ct.AllowedActions[resourceType] = actions -} - -// ValidateAttenuation validates an attenuation against the template -func (ct *CapabilityTemplate) ValidateAttenuation(att Attenuation) error { - resourceType := att.Resource.GetScheme() - allowedActions, exists := ct.AllowedActions[resourceType] - - if !exists { - // Allow unknown resource types for backward compatibility - return nil - } - - // Create action set for efficient lookup - actionSet := make(map[string]bool) - for _, action := range allowedActions { - actionSet[action] = true - } - - // Check if all capability actions are allowed - for _, action := range att.Capability.GetActions() { - if action == "*" { - // Wildcard requires explicit permission - if !actionSet["*"] { - return fmt.Errorf("wildcard action not allowed for resource type %s", resourceType) - } - continue - } - - if !actionSet[action] { - return fmt.Errorf("action %s not allowed for resource type %s", action, resourceType) - } - } - - return nil -} - -// ValidateExpiration validates token expiration time -func (ct *CapabilityTemplate) ValidateExpiration(expiresAt int64) error { - if expiresAt == 0 { - return nil // No expiration is allowed - } - - now := time.Now() - expiry := time.Unix(expiresAt, 0) - - if expiry.Before(now) { - return fmt.Errorf("token expiration is in the past") - } - - if expiry.Sub(now) > ct.MaxExpiration { - return fmt.Errorf("token expiration exceeds maximum allowed duration") - } - - return nil -} - -// GetDefaultExpirationTime returns the default expiration timestamp -func (ct *CapabilityTemplate) GetDefaultExpirationTime() int64 { - return time.Now().Add(ct.DefaultExpiration).Unix() -} - -// StandardVaultTemplate returns a standard template for vault operations -func StandardVaultTemplate() *CapabilityTemplate { - template := NewCapabilityTemplate() - template.AddAllowedActions( - "ipfs", - []string{"read", "write", "sign", "export", "import", "delete", VaultAdminAction}, - ) - template.AddAllowedActions( - "vault", - []string{"read", "write", "sign", "export", "import", "delete", "admin", "*"}, - ) - return template -} - -// StandardServiceTemplate returns a standard template for service operations -func StandardServiceTemplate() *CapabilityTemplate { - template := NewCapabilityTemplate() - template.AddAllowedActions( - "service", - []string{"read", "write", "admin", "register", "update", "delete"}, - ) - template.AddAllowedActions("https", []string{"read", "write"}) - template.AddAllowedActions("http", []string{"read", "write"}) - return template -} - -// AttenuationList provides utilities for working with multiple attenuations -type AttenuationList []Attenuation - -// Contains checks if the list contains attenuations for a specific resource -func (al AttenuationList) Contains(resourceURI string) bool { - for _, att := range al { - if att.Resource.GetURI() == resourceURI { - return true - } - } - return false -} - -// GetCapabilitiesForResource returns all capabilities for a specific resource -func (al AttenuationList) GetCapabilitiesForResource(resourceURI string) []Capability { - var capabilities []Capability - for _, att := range al { - if att.Resource.GetURI() == resourceURI { - capabilities = append(capabilities, att.Capability) - } - } - return capabilities -} - -// CanPerform checks if the attenuations allow specific actions on a resource -func (al AttenuationList) CanPerform(resourceURI string, actions []string) bool { - capabilities := al.GetCapabilitiesForResource(resourceURI) - for _, cap := range capabilities { - if cap.Grants(actions) { - return true - } - } - return false -} - -// IsSubsetOf checks if this list is a subset of another list -func (al AttenuationList) IsSubsetOf(parent AttenuationList) bool { - for _, childAtt := range al { - if !parent.containsAttenuation(childAtt) { - return false - } - } - return true -} - -// containsAttenuation checks if the list contains an equivalent attenuation -func (al AttenuationList) containsAttenuation(att Attenuation) bool { - for _, parentAtt := range al { - if parentAtt.Resource.Matches(att.Resource) { - if parentAtt.Capability.Contains(att.Capability) { - return true - } - } - } - return false -} - -// Module-Specific Capability Types - -// DIDCapability implements Capability for DID module operations -type DIDCapability struct { - Action string `json:"action"` - Actions []string `json:"actions,omitempty"` - Caveats []string `json:"caveats,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// GetActions returns the actions this DID capability grants -func (c *DIDCapability) GetActions() []string { - if len(c.Actions) > 0 { - return c.Actions - } - return []string{c.Action} -} - -// Grants checks if this capability grants the required abilities -func (c *DIDCapability) Grants(abilities []string) bool { - if c.Action == "*" { - return true - } - - grantedActions := make(map[string]bool) - for _, action := range c.GetActions() { - grantedActions[action] = true - } - - for _, ability := range abilities { - if !grantedActions[ability] { - return false - } - } - return true -} - -// Contains checks if this capability contains another capability -func (c *DIDCapability) Contains(other Capability) bool { - if c.Action == "*" { - return true - } - - ourActions := make(map[string]bool) - for _, action := range c.GetActions() { - ourActions[action] = true - } - - for _, otherAction := range other.GetActions() { - if !ourActions[otherAction] { - return false - } - } - return true -} - -// String returns string representation -func (c *DIDCapability) String() string { - if len(c.Actions) > 1 { - return strings.Join(c.Actions, ",") - } - return c.Action -} - -// DWNCapability implements Capability for DWN module operations -type DWNCapability struct { - Action string `json:"action"` - Actions []string `json:"actions,omitempty"` - Caveats []string `json:"caveats,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// GetActions returns the actions this DWN capability grants -func (c *DWNCapability) GetActions() []string { - if len(c.Actions) > 0 { - return c.Actions - } - return []string{c.Action} -} - -// Grants checks if this capability grants the required abilities -func (c *DWNCapability) Grants(abilities []string) bool { - if c.Action == "*" { - return true - } - - grantedActions := make(map[string]bool) - for _, action := range c.GetActions() { - grantedActions[action] = true - } - - for _, ability := range abilities { - if !grantedActions[ability] { - return false - } - } - return true -} - -// Contains checks if this capability contains another capability -func (c *DWNCapability) Contains(other Capability) bool { - if c.Action == "*" { - return true - } - - ourActions := make(map[string]bool) - for _, action := range c.GetActions() { - ourActions[action] = true - } - - for _, otherAction := range other.GetActions() { - if !ourActions[otherAction] { - return false - } - } - return true -} - -// String returns string representation -func (c *DWNCapability) String() string { - if len(c.Actions) > 1 { - return strings.Join(c.Actions, ",") - } - return c.Action -} - -// DEXCapability implements Capability for DEX module operations -type DEXCapability struct { - Action string `json:"action"` - Actions []string `json:"actions,omitempty"` - Caveats []string `json:"caveats,omitempty"` - MaxAmount string `json:"max_amount,omitempty"` // For swap limits - Metadata map[string]string `json:"metadata,omitempty"` -} - -// GetActions returns the actions this DEX capability grants -func (c *DEXCapability) GetActions() []string { - if len(c.Actions) > 0 { - return c.Actions - } - return []string{c.Action} -} - -// Grants checks if this capability grants the required abilities -func (c *DEXCapability) Grants(abilities []string) bool { - if c.Action == "*" { - return true - } - - grantedActions := make(map[string]bool) - for _, action := range c.GetActions() { - grantedActions[action] = true - } - - for _, ability := range abilities { - if !grantedActions[ability] { - return false - } - } - return true -} - -// Contains checks if this capability contains another capability -func (c *DEXCapability) Contains(other Capability) bool { - if c.Action == "*" { - return true - } - - ourActions := make(map[string]bool) - for _, action := range c.GetActions() { - ourActions[action] = true - } - - for _, otherAction := range other.GetActions() { - if !ourActions[otherAction] { - return false - } - } - return true -} - -// String returns string representation -func (c *DEXCapability) String() string { - if len(c.Actions) > 1 { - return strings.Join(c.Actions, ",") - } - return c.Action -} - -// Module-Specific Resource Types - -// DIDResource represents DID-specific resources -type DIDResource struct { - SimpleResource - DIDMethod string `json:"did_method,omitempty"` - DIDSubject string `json:"did_subject,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// DWNResource represents DWN-specific resources -type DWNResource struct { - SimpleResource - RecordType string `json:"record_type,omitempty"` - Protocol string `json:"protocol,omitempty"` - Owner string `json:"owner,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// DEXResource represents DEX-specific resources -type DEXResource struct { - SimpleResource - PoolID string `json:"pool_id,omitempty"` - AssetPair string `json:"asset_pair,omitempty"` - OrderID string `json:"order_id,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// SupportsDelegate Enhanced ServiceResource adds delegation capabilities -func (r *ServiceResource) SupportsDelegate() bool { - return r.Metadata != nil && r.Metadata["supports_delegation"] == "true" -} - -// Module-Specific Capability Templates - -// StandardDIDTemplate returns a standard template for DID operations -func StandardDIDTemplate() *CapabilityTemplate { - template := NewCapabilityTemplate() - template.AddAllowedActions("did", []string{ - "create", "register", "update", "deactivate", "revoke", - "add-verification-method", "remove-verification-method", - "add-service", "remove-service", "issue-credential", - "revoke-credential", "link-wallet", "register-webauthn", "*", - }) - return template -} - -// StandardDWNTemplate returns a standard template for DWN operations -func StandardDWNTemplate() *CapabilityTemplate { - template := NewCapabilityTemplate() - template.AddAllowedActions("dwn", []string{ - "records-write", "records-delete", "protocols-configure", - "permissions-grant", "permissions-revoke", "create", "read", - "update", "delete", "*", - }) - return template -} - -// EnhancedServiceTemplate returns enhanced service template with delegation support -func EnhancedServiceTemplate() *CapabilityTemplate { - template := NewCapabilityTemplate() - template.AddAllowedActions("service", []string{ - "register", "update", "delete", "verify-domain", - "initiate-domain-verification", "delegate", "*", - }) - template.AddAllowedActions("svc", []string{ - "register", "verify-domain", "delegate", "*", - }) - template.AddAllowedActions("https", []string{"read", "write"}) - template.AddAllowedActions("http", []string{"read", "write"}) - return template -} - -// StandardDEXTemplate returns a standard template for DEX operations -func StandardDEXTemplate() *CapabilityTemplate { - template := NewCapabilityTemplate() - template.AddAllowedActions("dex", []string{ - "register-account", "swap", "provide-liquidity", "remove-liquidity", - "create-limit-order", "cancel-order", "*", - }) - template.AddAllowedActions("pool", []string{ - "swap", "provide-liquidity", "remove-liquidity", "*", - }) - return template -} - -// Module-Specific Attenuation Constructors - -// CreateDIDAttenuation creates a DID-specific attenuation -func CreateDIDAttenuation(actions []string, didPattern string, caveats []string) Attenuation { - resourceURI := fmt.Sprintf("did:%s", didPattern) - resource := &DIDResource{ - SimpleResource: SimpleResource{ - Scheme: "did", - Value: didPattern, - URI: resourceURI, - }, - } - - return Attenuation{ - Capability: &DIDCapability{ - Actions: actions, - Caveats: caveats, - }, - Resource: resource, - } -} - -// CreateDWNAttenuation creates a DWN-specific attenuation -func CreateDWNAttenuation(actions []string, recordPattern string, caveats []string) Attenuation { - resourceURI := fmt.Sprintf("dwn:records/%s", recordPattern) - resource := &DWNResource{ - SimpleResource: SimpleResource{ - Scheme: "dwn", - Value: fmt.Sprintf("records/%s", recordPattern), - URI: resourceURI, - }, - RecordType: recordPattern, - } - - return Attenuation{ - Capability: &DWNCapability{ - Actions: actions, - Caveats: caveats, - }, - Resource: resource, - } -} - -// CreateDEXAttenuation creates a DEX-specific attenuation -func CreateDEXAttenuation(actions []string, poolPattern string, caveats []string, maxAmount string) Attenuation { - resourceURI := fmt.Sprintf("dex:pool/%s", poolPattern) - resource := &DEXResource{ - SimpleResource: SimpleResource{ - Scheme: "dex", - Value: fmt.Sprintf("pool/%s", poolPattern), - URI: resourceURI, - }, - PoolID: poolPattern, - } - - return Attenuation{ - Capability: &DEXCapability{ - Actions: actions, - Caveats: caveats, - MaxAmount: maxAmount, - }, - Resource: resource, - } -} - -// Cross-Module Capability Composition - -// CrossModuleCapability allows composing capabilities across modules -type CrossModuleCapability struct { - Modules map[string]Capability `json:"modules"` -} - -// GetActions returns all actions across all modules -func (c *CrossModuleCapability) GetActions() []string { - var actions []string - for _, cap := range c.Modules { - actions = append(actions, cap.GetActions()...) - } - return actions -} - -// Grants checks if required abilities are granted across modules -func (c *CrossModuleCapability) Grants(abilities []string) bool { - allActions := make(map[string]bool) - for _, cap := range c.Modules { - for _, action := range cap.GetActions() { - allActions[action] = true - } - } - - for _, ability := range abilities { - if !allActions[ability] { - return false - } - } - return true -} - -// Contains checks if this cross-module capability contains another -func (c *CrossModuleCapability) Contains(other Capability) bool { - // For cross-module capabilities, check each module - if otherCross, ok := other.(*CrossModuleCapability); ok { - for module, otherCap := range otherCross.Modules { - if ourCap, exists := c.Modules[module]; exists { - if !ourCap.Contains(otherCap) { - return false - } - } else { - return false - } - } - return true - } - - // For single capabilities, check if any module contains it - for _, cap := range c.Modules { - if cap.Contains(other) { - return true - } - } - return false -} - -// String returns string representation -func (c *CrossModuleCapability) String() string { - var moduleStrs []string - for module, cap := range c.Modules { - moduleStrs = append(moduleStrs, fmt.Sprintf("%s:%s", module, cap.String())) - } - return strings.Join(moduleStrs, ";") -} - -// Gasless Transaction Support - -// GaslessCapability wraps other capabilities with gasless transaction support -type GaslessCapability struct { - Capability - AllowGasless bool `json:"allow_gasless"` - GasLimit uint64 `json:"gas_limit,omitempty"` -} - -// SupportsGasless returns whether this capability supports gasless transactions -func (c *GaslessCapability) SupportsGasless() bool { - return c.AllowGasless -} - -// GetGasLimit returns the gas limit for gasless transactions -func (c *GaslessCapability) GetGasLimit() uint64 { - return c.GasLimit -} diff --git a/internal/crypto/ucan/crypto.go b/internal/crypto/ucan/crypto.go deleted file mode 100644 index f69b3b3..0000000 --- a/internal/crypto/ucan/crypto.go +++ /dev/null @@ -1,352 +0,0 @@ -// Package ucan provides User-Controlled Authorization Networks (UCAN) implementation -// for decentralized authorization and capability delegation in the Sonr network. -// This package handles JWT-based tokens, cryptographic verification, and resource capabilities. -package ucan - -import ( - "crypto" - "crypto/ed25519" - "crypto/rsa" - "crypto/sha256" - "crypto/sha512" - "encoding/base64" - "fmt" - "hash" - "strings" - - "github.com/golang-jwt/jwt/v5" -) - -// SupportedSigningMethods returns the list of supported JWT signing methods for UCAN -func SupportedSigningMethods() []jwt.SigningMethod { - return []jwt.SigningMethod{ - jwt.SigningMethodRS256, - jwt.SigningMethodRS384, - jwt.SigningMethodRS512, - jwt.SigningMethodEdDSA, - } -} - -// ValidateSignature validates the cryptographic signature of a UCAN token -func ValidateSignature(tokenString string, verifyKey any) error { - // Parse token without verification first to get signing method - token, err := jwt.ParseWithClaims( - tokenString, - jwt.MapClaims{}, - func(token *jwt.Token) (any, error) { - return verifyKey, nil - }, - ) - if err != nil { - return fmt.Errorf("signature validation failed: %w", err) - } - - if !token.Valid { - return fmt.Errorf("token signature is invalid") - } - - return nil -} - -// ExtractUnsignedToken extracts the unsigned portion of a JWT token (header + payload) -func ExtractUnsignedToken(tokenString string) (string, error) { - parts := strings.Split(tokenString, ".") - if len(parts) != 3 { - return "", fmt.Errorf("invalid JWT format: expected 3 parts, got %d", len(parts)) - } - - return strings.Join(parts[:2], "."), nil -} - -// ExtractSignature extracts the signature portion of a JWT token -func ExtractSignature(tokenString string) ([]byte, error) { - parts := strings.Split(tokenString, ".") - if len(parts) != 3 { - return nil, fmt.Errorf("invalid JWT format: expected 3 parts, got %d", len(parts)) - } - - signatureBytes, err := base64.RawURLEncoding.DecodeString(parts[2]) - if err != nil { - return nil, fmt.Errorf("failed to decode signature: %w", err) - } - - return signatureBytes, nil -} - -// VerifyRSASignature verifies an RSA signature using the specified hash algorithm -func VerifyRSASignature( - signingString string, - signature []byte, - publicKey *rsa.PublicKey, - hashAlg crypto.Hash, -) error { - // Create hash of signing string - hasher := hashAlg.New() - hasher.Write([]byte(signingString)) - hashed := hasher.Sum(nil) - - // Verify signature - err := rsa.VerifyPKCS1v15(publicKey, hashAlg, hashed, signature) - if err != nil { - return fmt.Errorf("RSA signature verification failed: %w", err) - } - - return nil -} - -// VerifyEd25519Signature verifies an Ed25519 signature -func VerifyEd25519Signature( - signingString string, - signature []byte, - publicKey ed25519.PublicKey, -) error { - valid := ed25519.Verify(publicKey, []byte(signingString), signature) - if !valid { - return fmt.Errorf("Ed25519 signature verification failed") - } - - return nil -} - -// GetHashAlgorithmForMethod returns the appropriate hash algorithm for a JWT signing method -func GetHashAlgorithmForMethod(method jwt.SigningMethod) (crypto.Hash, error) { - switch method { - case jwt.SigningMethodRS256: - return crypto.SHA256, nil - case jwt.SigningMethodRS384: - return crypto.SHA384, nil - case jwt.SigningMethodRS512: - return crypto.SHA512, nil - case jwt.SigningMethodEdDSA: - // Ed25519 doesn't use a separate hash algorithm - return crypto.Hash(0), nil - default: - return crypto.Hash(0), fmt.Errorf("unsupported signing method: %v", method) - } -} - -// CreateHasher creates a hasher for the given crypto.Hash algorithm -func CreateHasher(hashAlg crypto.Hash) (hash.Hash, error) { - switch hashAlg { - case crypto.SHA256: - return sha256.New(), nil - case crypto.SHA384: - return sha512.New384(), nil - case crypto.SHA512: - return sha512.New(), nil - default: - return nil, fmt.Errorf("unsupported hash algorithm: %v", hashAlg) - } -} - -// SigningValidator provides cryptographic validation for UCAN tokens -type SigningValidator struct { - allowedMethods map[string]jwt.SigningMethod -} - -// NewSigningValidator creates a new signing validator with default allowed methods -func NewSigningValidator() *SigningValidator { - allowed := make(map[string]jwt.SigningMethod) - for _, method := range SupportedSigningMethods() { - allowed[method.Alg()] = method - } - - return &SigningValidator{ - allowedMethods: allowed, - } -} - -// NewSigningValidatorWithMethods creates a validator with specific allowed methods -func NewSigningValidatorWithMethods(methods []jwt.SigningMethod) *SigningValidator { - allowed := make(map[string]jwt.SigningMethod) - for _, method := range methods { - allowed[method.Alg()] = method - } - - return &SigningValidator{ - allowedMethods: allowed, - } -} - -// ValidateSigningMethod checks if a signing method is allowed -func (sv *SigningValidator) ValidateSigningMethod(method jwt.SigningMethod) error { - if _, ok := sv.allowedMethods[method.Alg()]; !ok { - return fmt.Errorf("signing method %s is not allowed", method.Alg()) - } - return nil -} - -// ValidateTokenSignature validates the cryptographic signature of a token -func (sv *SigningValidator) ValidateTokenSignature( - tokenString string, - keyFunc jwt.Keyfunc, -) (*jwt.Token, error) { - // Parse with validation - token, err := jwt.Parse(tokenString, keyFunc, jwt.WithValidMethods(sv.getAllowedMethodNames())) - if err != nil { - return nil, fmt.Errorf("token signature validation failed: %w", err) - } - - // Additional signing method validation - if err := sv.ValidateSigningMethod(token.Method); err != nil { - return nil, err - } - - return token, nil -} - -// getAllowedMethodNames returns the names of allowed signing methods -func (sv *SigningValidator) getAllowedMethodNames() []string { - methods := make([]string, 0, len(sv.allowedMethods)) - for name := range sv.allowedMethods { - methods = append(methods, name) - } - return methods -} - -// KeyValidator provides validation for cryptographic keys -type KeyValidator struct{} - -// NewKeyValidator creates a new key validator -func NewKeyValidator() *KeyValidator { - return &KeyValidator{} -} - -// ValidateRSAPublicKey validates an RSA public key for UCAN usage -func (kv *KeyValidator) ValidateRSAPublicKey(key *rsa.PublicKey) error { - if key == nil { - return fmt.Errorf("RSA public key is nil") - } - - // Check minimum key size (2048 bits recommended for security) - keySize := key.N.BitLen() - if keySize < 2048 { - return fmt.Errorf("RSA key size too small: %d bits (minimum 2048 bits required)", keySize) - } - - // Check maximum reasonable key size to prevent DoS - if keySize > 8192 { - return fmt.Errorf("RSA key size too large: %d bits (maximum 8192 bits allowed)", keySize) - } - - return nil -} - -// ValidateEd25519PublicKey validates an Ed25519 public key for UCAN usage -func (kv *KeyValidator) ValidateEd25519PublicKey(key ed25519.PublicKey) error { - if key == nil { - return fmt.Errorf("Ed25519 public key is nil") - } - - if len(key) != ed25519.PublicKeySize { - return fmt.Errorf( - "invalid Ed25519 public key size: %d bytes (expected %d)", - len(key), - ed25519.PublicKeySize, - ) - } - - return nil -} - -// SignatureInfo contains information about a token's signature -type SignatureInfo struct { - Algorithm string - KeyType string - SigningString string - Signature []byte - Valid bool -} - -// ExtractSignatureInfo extracts signature information from a JWT token -func ExtractSignatureInfo(tokenString string, verifyKey any) (*SignatureInfo, error) { - // Parse token to get method and claims - token, err := jwt.Parse(tokenString, func(t *jwt.Token) (any, error) { - return verifyKey, nil - }) - - var sigInfo SignatureInfo - sigInfo.Valid = (err == nil && token.Valid) - - if token != nil { - sigInfo.Algorithm = token.Method.Alg() - - // Get signing string - parts := strings.Split(tokenString, ".") - if len(parts) >= 2 { - sigInfo.SigningString = strings.Join(parts[:2], ".") - } - - // Get signature - if len(parts) == 3 { - sig, decodeErr := base64.RawURLEncoding.DecodeString(parts[2]) - if decodeErr == nil { - sigInfo.Signature = sig - } - } - - // Determine key type - switch verifyKey.(type) { - case *rsa.PublicKey: - sigInfo.KeyType = "RSA" - case ed25519.PublicKey: - sigInfo.KeyType = "Ed25519" - default: - sigInfo.KeyType = "Unknown" - } - } - - return &sigInfo, err -} - -// SecurityConfig contains security configuration for UCAN validation -type SecurityConfig struct { - AllowedSigningMethods []jwt.SigningMethod - MinRSAKeySize int - MaxRSAKeySize int - RequireSecureAlgs bool -} - -// DefaultSecurityConfig returns a secure default configuration -func DefaultSecurityConfig() *SecurityConfig { - return &SecurityConfig{ - AllowedSigningMethods: SupportedSigningMethods(), - MinRSAKeySize: 2048, - MaxRSAKeySize: 8192, - RequireSecureAlgs: true, - } -} - -// RestrictiveSecurityConfig returns a more restrictive configuration -func RestrictiveSecurityConfig() *SecurityConfig { - return &SecurityConfig{ - AllowedSigningMethods: []jwt.SigningMethod{ - jwt.SigningMethodRS256, // Only RS256 and EdDSA - jwt.SigningMethodEdDSA, - }, - MinRSAKeySize: 3072, // Higher minimum - MaxRSAKeySize: 4096, // Lower maximum - RequireSecureAlgs: true, - } -} - -// ValidateSecurityConfig validates that a security configuration is reasonable -func ValidateSecurityConfig(config *SecurityConfig) error { - if len(config.AllowedSigningMethods) == 0 { - return fmt.Errorf("no signing methods allowed") - } - - if config.MinRSAKeySize < 1024 { - return fmt.Errorf("minimum RSA key size too small: %d", config.MinRSAKeySize) - } - - if config.MaxRSAKeySize < config.MinRSAKeySize { - return fmt.Errorf("maximum RSA key size smaller than minimum") - } - - if config.MaxRSAKeySize > 16384 { - return fmt.Errorf("maximum RSA key size too large: %d", config.MaxRSAKeySize) - } - - return nil -} diff --git a/internal/crypto/ucan/jwt.go b/internal/crypto/ucan/jwt.go deleted file mode 100644 index 95ce860..0000000 --- a/internal/crypto/ucan/jwt.go +++ /dev/null @@ -1,595 +0,0 @@ -package ucan - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "time" - - "github.com/golang-jwt/jwt/v5" -) - -var ( - // StandardTemplate provides default authorization template - StandardTemplate = NewCapabilityTemplate() - - // Revoked tokens tracking - revokedTokens = make(map[string]bool) -) - -func init() { - // Setup standard templates with module-specific capabilities - StandardTemplate.AddAllowedActions( - "vault", - []string{"read", "write", "sign", "export", "import", "delete", "*"}, - ) - StandardTemplate.AddAllowedActions( - "service", - []string{"read", "write", "register", "update", "delete"}, - ) - StandardTemplate.AddAllowedActions( - "did", - []string{ - "create", "register", "update", "deactivate", "revoke", - "add-verification-method", "remove-verification-method", - "add-service", "remove-service", "issue-credential", - "revoke-credential", "link-wallet", "register-webauthn", "*", - }, - ) - StandardTemplate.AddAllowedActions( - "dwn", - []string{ - "records-write", "records-delete", "protocols-configure", - "permissions-grant", "permissions-revoke", "create", "read", - "update", "delete", "*", - }, - ) - StandardTemplate.AddAllowedActions( - "dex", - []string{ - "register-account", "swap", "provide-liquidity", "remove-liquidity", - "create-limit-order", "cancel-order", "*", - }, - ) - StandardTemplate.AddAllowedActions( - "pool", - []string{"swap", "provide-liquidity", "remove-liquidity", "*"}, - ) - StandardTemplate.AddAllowedActions( - "svc", - []string{"register", "verify-domain", "delegate", "*"}, - ) -} - -// GenerateJWTToken creates a UCAN JWT token with given capability and expiration -func GenerateJWTToken(attenuation Attenuation, duration time.Duration) (string, error) { - // Default expiration handling - if duration == 0 { - duration = 24 * time.Hour - } - - // Create JWT claims - claims := jwt.MapClaims{ - "iss": "did:sonr:local", // Default issuer - "exp": time.Now().Add(duration).Unix(), - "iat": time.Now().Unix(), - } - - // Add capability to claims - separate resource and capability - capabilityBytes, err := json.Marshal(map[string]any{ - "can": attenuation.Capability, - "with": attenuation.Resource, - }) - if err != nil { - return "", fmt.Errorf("failed to serialize capability: %v", err) - } - claims["can"] = base64.URLEncoding.EncodeToString(capabilityBytes) - - // Create token - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - - // Dummy secret for signing - in real-world, use proper key management - tokenString, err := token.SignedString([]byte("sonr-ucan-secret")) - if err != nil { - return "", fmt.Errorf("failed to sign token: %v", err) - } - - return tokenString, nil -} - -// VerifyJWTToken validates and parses a UCAN JWT token -func VerifyJWTToken(tokenString string) (*Token, error) { - // Check if token is revoked - if revokedTokens[tokenString] { - return nil, fmt.Errorf("token has been revoked") - } - - // Parse token with custom claims - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { - // Dummy secret verification - replace with proper key validation - return []byte("sonr-ucan-secret"), nil - }, jwt.WithLeeway(5*time.Minute)) - if err != nil { - return nil, fmt.Errorf("token parsing failed: %v", err) - } - - // Extract claims - claims, ok := token.Claims.(jwt.MapClaims) - if !ok { - return nil, fmt.Errorf("invalid token claims") - } - - // Manual expiration check - exp, ok := claims["exp"].(float64) - if !ok { - return nil, fmt.Errorf("no expiration time found") - } - if time.Now().Unix() > int64(exp) { - return nil, fmt.Errorf("token has expired") - } - - // Decode capability - capabilityStr, ok := claims["can"].(string) - if !ok { - return nil, fmt.Errorf("no capability found in token") - } - - capabilityBytes, err := base64.URLEncoding.DecodeString(capabilityStr) - if err != nil { - return nil, fmt.Errorf("failed to decode capability: %v", err) - } - - // Parse capability and resource separately - var capabilityMap map[string]any - err = json.Unmarshal(capabilityBytes, &capabilityMap) - if err != nil { - return nil, fmt.Errorf("failed to parse capability: %v", err) - } - - // Determine capability type - var capability Capability - var capData map[string]any - switch v := capabilityMap["can"].(type) { - case map[string]any: - capData = v - case string: - // If it's a string, assume it's a simple action - capability = &SimpleCapability{Action: v} - capData = nil - default: - return nil, fmt.Errorf("invalid capability structure") - } - - // Parse capability if needed - if capData != nil { - // Attempt to infer capability type - if actions, ok := capData["actions"].([]any); ok { - // MultiCapability - stringActions := make([]string, len(actions)) - for i, action := range actions { - if str, ok := action.(string); ok { - stringActions[i] = str - } - } - capability = &MultiCapability{Actions: stringActions} - } else if action, ok := capData["action"].(string); ok { - // SingleCapability - capability = &SimpleCapability{Action: action} - } else { - return nil, fmt.Errorf("unable to parse capability type") - } - } - - // Parse resource - var resourceData map[string]any - switch resource := capabilityMap["with"].(type) { - case map[string]any: - resourceData = resource - case string: - // If it's a string, assume it's a simple URI - resourceData = map[string]any{ - "Scheme": "generic", - "Value": resource, - "URI": resource, - } - default: - return nil, fmt.Errorf("invalid resource structure") - } - - // Create resource based on scheme - scheme, _ := resourceData["Scheme"].(string) - value, _ := resourceData["Value"].(string) - uri, _ := resourceData["URI"].(string) - - resource := &SimpleResource{ - Scheme: scheme, - Value: value, - URI: uri, - } - - // Validate attenuation - attenuation := Attenuation{ - Capability: capability, - Resource: resource, - } - - // Use standard template to validate - err = StandardTemplate.ValidateAttenuation(attenuation) - if err != nil { - return nil, fmt.Errorf("capability validation failed: %v", err) - } - - // Construct Token object - parsedToken := &Token{ - Raw: tokenString, - Issuer: claims["iss"].(string), - ExpiresAt: int64(exp), - Attenuations: []Attenuation{attenuation}, - } - - return parsedToken, nil -} - -// RevokeCapability adds a capability to the revocation list -func RevokeCapability(attenuation Attenuation) error { - // Generate token to get its string representation - token, err := GenerateJWTToken(attenuation, time.Hour) - if err != nil { - return err - } - - // Add to revoked tokens - revokedTokens[token] = true - return nil -} - -// NewCapability is a helper function to create a basic capability -func NewCapability(issuer, resource string, abilities []string) (Attenuation, error) { - capability := &MultiCapability{Actions: abilities} - resourceObj := &SimpleResource{ - Scheme: "generic", - Value: resource, - URI: resource, - } - - return Attenuation{ - Capability: capability, - Resource: resourceObj, - }, nil -} - -// Enhanced JWT generation functions for module-specific capabilities - -// GenerateModuleJWTToken creates a UCAN JWT token with module-specific capabilities -func GenerateModuleJWTToken(attenuations []Attenuation, issuer, audience string, duration time.Duration) (string, error) { - if duration == 0 { - duration = 24 * time.Hour - } - - // Create JWT claims with enhanced structure - claims := jwt.MapClaims{ - "iss": issuer, - "aud": audience, - "exp": time.Now().Add(duration).Unix(), - "iat": time.Now().Unix(), - "nbf": time.Now().Unix(), - } - - // Add attenuations to claims with module-specific serialization - attClaims := make([]map[string]any, len(attenuations)) - for i, att := range attenuations { - attMap, err := serializeModuleAttenuation(att) - if err != nil { - return "", fmt.Errorf("failed to serialize attenuation %d: %w", i, err) - } - attClaims[i] = attMap - } - claims["att"] = attClaims - - // Create and sign token - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - tokenString, err := token.SignedString([]byte("sonr-ucan-secret")) - if err != nil { - return "", fmt.Errorf("failed to sign token: %w", err) - } - - return tokenString, nil -} - -// serializeModuleAttenuation serializes an attenuation based on its module type -func serializeModuleAttenuation(att Attenuation) (map[string]any, error) { - attMap := map[string]any{ - "with": att.Resource.GetURI(), - } - - scheme := att.Resource.GetScheme() - switch scheme { - case "did": - return serializeDIDAttenuation(att, attMap) - case "dwn": - return serializeDWNAttenuation(att, attMap) - case "dex", "pool": - return serializeDEXAttenuation(att, attMap) - case "service", "svc": - return serializeServiceAttenuation(att, attMap) - case "vault", "ipfs": - return serializeVaultAttenuation(att, attMap) - default: - return serializeGenericAttenuation(att, attMap) - } -} - -// serializeDIDAttenuation serializes DID-specific attenuations -func serializeDIDAttenuation(att Attenuation, attMap map[string]any) (map[string]any, error) { - didCap, ok := att.Capability.(*DIDCapability) - if !ok { - return serializeGenericAttenuation(att, attMap) - } - - if didCap.Action != "" { - attMap["can"] = didCap.Action - } else { - attMap["can"] = didCap.Actions - } - - if len(didCap.Caveats) > 0 { - attMap["caveats"] = didCap.Caveats - } - if len(didCap.Metadata) > 0 { - attMap["metadata"] = didCap.Metadata - } - - return attMap, nil -} - -// serializeDWNAttenuation serializes DWN-specific attenuations -func serializeDWNAttenuation(att Attenuation, attMap map[string]any) (map[string]any, error) { - dwnCap, ok := att.Capability.(*DWNCapability) - if !ok { - return serializeGenericAttenuation(att, attMap) - } - - if dwnCap.Action != "" { - attMap["can"] = dwnCap.Action - } else { - attMap["can"] = dwnCap.Actions - } - - if len(dwnCap.Caveats) > 0 { - attMap["caveats"] = dwnCap.Caveats - } - if len(dwnCap.Metadata) > 0 { - attMap["metadata"] = dwnCap.Metadata - } - - // Add DWN-specific fields - if dwnRes, ok := att.Resource.(*DWNResource); ok { - if dwnRes.RecordType != "" { - attMap["record_type"] = dwnRes.RecordType - } - if dwnRes.Protocol != "" { - attMap["protocol"] = dwnRes.Protocol - } - if dwnRes.Owner != "" { - attMap["owner"] = dwnRes.Owner - } - } - - return attMap, nil -} - -// serializeDEXAttenuation serializes DEX-specific attenuations -func serializeDEXAttenuation(att Attenuation, attMap map[string]any) (map[string]any, error) { - dexCap, ok := att.Capability.(*DEXCapability) - if !ok { - return serializeGenericAttenuation(att, attMap) - } - - if dexCap.Action != "" { - attMap["can"] = dexCap.Action - } else { - attMap["can"] = dexCap.Actions - } - - if len(dexCap.Caveats) > 0 { - attMap["caveats"] = dexCap.Caveats - } - if dexCap.MaxAmount != "" { - attMap["max_amount"] = dexCap.MaxAmount - } - if len(dexCap.Metadata) > 0 { - attMap["metadata"] = dexCap.Metadata - } - - // Add DEX-specific fields - if dexRes, ok := att.Resource.(*DEXResource); ok { - if dexRes.PoolID != "" { - attMap["pool_id"] = dexRes.PoolID - } - if dexRes.AssetPair != "" { - attMap["asset_pair"] = dexRes.AssetPair - } - if dexRes.OrderID != "" { - attMap["order_id"] = dexRes.OrderID - } - } - - return attMap, nil -} - -// serializeServiceAttenuation serializes Service-specific attenuations -func serializeServiceAttenuation(att Attenuation, attMap map[string]any) (map[string]any, error) { - // Service capabilities still use MultiCapability - multiCap, ok := att.Capability.(*MultiCapability) - if !ok { - return serializeGenericAttenuation(att, attMap) - } - - attMap["can"] = multiCap.Actions - - // Add service-specific fields - if svcRes, ok := att.Resource.(*ServiceResource); ok { - if svcRes.ServiceID != "" { - attMap["service_id"] = svcRes.ServiceID - } - if svcRes.Domain != "" { - attMap["domain"] = svcRes.Domain - } - if len(svcRes.Metadata) > 0 { - attMap["metadata"] = svcRes.Metadata - } - } - - return attMap, nil -} - -// serializeVaultAttenuation serializes Vault-specific attenuations -func serializeVaultAttenuation(att Attenuation, attMap map[string]any) (map[string]any, error) { - vaultCap, ok := att.Capability.(*VaultCapability) - if !ok { - return serializeGenericAttenuation(att, attMap) - } - - if vaultCap.Action != "" { - attMap["can"] = vaultCap.Action - } else { - attMap["can"] = vaultCap.Actions - } - - if vaultCap.VaultAddress != "" { - attMap["vault"] = vaultCap.VaultAddress - } - if len(vaultCap.Caveats) > 0 { - attMap["caveats"] = vaultCap.Caveats - } - if vaultCap.EnclaveDataCID != "" { - attMap["enclave_data_cid"] = vaultCap.EnclaveDataCID - } - if len(vaultCap.Metadata) > 0 { - attMap["metadata"] = vaultCap.Metadata - } - - return attMap, nil -} - -// serializeGenericAttenuation serializes generic attenuations -func serializeGenericAttenuation(att Attenuation, attMap map[string]any) (map[string]any, error) { - actions := att.Capability.GetActions() - if len(actions) == 1 { - attMap["can"] = actions[0] - } else { - attMap["can"] = actions - } - return attMap, nil -} - -// Enhanced verification with module-specific support - -// VerifyModuleJWTToken validates and parses a UCAN JWT token with module-specific capabilities -func VerifyModuleJWTToken(tokenString string, expectedIssuer, expectedAudience string) (*Token, error) { - // Check if token is revoked - if revokedTokens[tokenString] { - return nil, fmt.Errorf("token has been revoked") - } - - // Parse token with custom claims - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { - // Dummy secret verification - replace with proper key validation - return []byte("sonr-ucan-secret"), nil - }, jwt.WithLeeway(5*time.Minute)) - if err != nil { - return nil, fmt.Errorf("token parsing failed: %w", err) - } - - // Extract claims - claims, ok := token.Claims.(jwt.MapClaims) - if !ok { - return nil, fmt.Errorf("invalid token claims") - } - - // Validate issuer and audience if provided - if expectedIssuer != "" { - if iss, ok := claims["iss"].(string); !ok || iss != expectedIssuer { - return nil, fmt.Errorf("invalid issuer: expected %s", expectedIssuer) - } - } - if expectedAudience != "" { - if aud, ok := claims["aud"].(string); !ok || aud != expectedAudience { - return nil, fmt.Errorf("invalid audience: expected %s", expectedAudience) - } - } - - // Manual expiration check - exp, ok := claims["exp"].(float64) - if !ok { - return nil, fmt.Errorf("no expiration time found") - } - if time.Now().Unix() > int64(exp) { - return nil, fmt.Errorf("token has expired") - } - - // Parse attenuations with module-specific support - attenuations, err := parseEnhancedAttenuations(claims) - if err != nil { - return nil, fmt.Errorf("failed to parse attenuations: %w", err) - } - - // Validate attenuations against templates - for _, att := range attenuations { - if err := StandardTemplate.ValidateAttenuation(att); err != nil { - return nil, fmt.Errorf("capability validation failed: %w", err) - } - } - - // Construct Token object - issuer, _ := claims["iss"].(string) - audience, _ := claims["aud"].(string) - nbf, _ := claims["nbf"].(float64) - - parsedToken := &Token{ - Raw: tokenString, - Issuer: issuer, - Audience: audience, - ExpiresAt: int64(exp), - NotBefore: int64(nbf), - Attenuations: attenuations, - } - - return parsedToken, nil -} - -// parseEnhancedAttenuations parses attenuations with module-specific capabilities -func parseEnhancedAttenuations(claims jwt.MapClaims) ([]Attenuation, error) { - attClaims, ok := claims["att"] - if !ok { - return nil, fmt.Errorf("no attenuations found in token") - } - - attSlice, ok := attClaims.([]any) - if !ok { - return nil, fmt.Errorf("invalid attenuations format") - } - - attenuations := make([]Attenuation, 0, len(attSlice)) - for i, attItem := range attSlice { - attMap, ok := attItem.(map[string]any) - if !ok { - return nil, fmt.Errorf("invalid attenuation %d format", i) - } - - att, err := parseEnhancedAttenuation(attMap) - if err != nil { - return nil, fmt.Errorf("failed to parse attenuation %d: %w", i, err) - } - attenuations = append(attenuations, att) - } - - return attenuations, nil -} - -// parseEnhancedAttenuation parses a single attenuation with module-specific support -func parseEnhancedAttenuation(attMap map[string]any) (Attenuation, error) { - // Use the existing enhanced verifier logic - verifier := &Verifier{} // Create temporary verifier for parsing - return verifier.parseAttenuation(attMap) -} diff --git a/internal/crypto/ucan/mpc.go b/internal/crypto/ucan/mpc.go deleted file mode 100644 index 3d5a8b6..0000000 --- a/internal/crypto/ucan/mpc.go +++ /dev/null @@ -1,625 +0,0 @@ -// Package ucan provides User-Controlled Authorization Networks (UCAN) implementation -// for decentralized authorization and capability delegation in the Sonr network. -// This package handles JWT-based tokens, cryptographic verification, and resource capabilities. -package ucan - -import ( - "context" - "crypto/sha256" - "fmt" - "time" - - "github.com/golang-jwt/jwt/v5" - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" - "github.com/sonr-io/crypto/keys" - "github.com/sonr-io/crypto/mpc" -) - -// MPCSigningMethod implements JWT signing using MPC enclaves -type MPCSigningMethod struct { - Name string - enclave mpc.Enclave -} - -// NewMPCSigningMethod creates a new MPC-based JWT signing method -func NewMPCSigningMethod(name string, enclave mpc.Enclave) *MPCSigningMethod { - return &MPCSigningMethod{ - Name: name, - enclave: enclave, - } -} - -// Alg returns the signing method algorithm name -func (m *MPCSigningMethod) Alg() string { - return m.Name -} - -// Verify verifies a JWT signature using the MPC enclave -func (m *MPCSigningMethod) Verify(signingString string, signature []byte, key any) error { - // signature is already decoded bytes - sig := signature - - // Hash the signing string - hasher := sha256.New() - hasher.Write([]byte(signingString)) - digest := hasher.Sum(nil) - - // Use MPC enclave to verify signature - valid, err := m.enclave.Verify(digest, sig) - if err != nil { - return fmt.Errorf("failed to verify signature: %w", err) - } - - if !valid { - return fmt.Errorf("signature verification failed") - } - - return nil -} - -// Sign signs a JWT string using the MPC enclave -func (m *MPCSigningMethod) Sign(signingString string, key any) ([]byte, error) { - // Hash the signing string - hasher := sha256.New() - hasher.Write([]byte(signingString)) - digest := hasher.Sum(nil) - - // Use MPC enclave to sign the digest - sig, err := m.enclave.Sign(digest) - if err != nil { - return nil, fmt.Errorf("failed to sign with MPC: %w", err) - } - - return sig, nil -} - -// MPCTokenBuilder creates UCAN tokens using MPC signing -type MPCTokenBuilder struct { - enclave mpc.Enclave - issuerDID string - address string - signingMethod *MPCSigningMethod -} - -// NewMPCTokenBuilder creates a new MPC-based UCAN token builder -func NewMPCTokenBuilder(enclave mpc.Enclave) (*MPCTokenBuilder, error) { - if !enclave.IsValid() { - return nil, fmt.Errorf("invalid MPC enclave provided") - } - - // Derive issuer DID and address from enclave public key - pubKeyBytes := enclave.PubKeyBytes() - issuerDID, address := deriveIssuerDIDFromBytes(pubKeyBytes) - - signingMethod := NewMPCSigningMethod("MPC256", enclave) - - return &MPCTokenBuilder{ - enclave: enclave, - issuerDID: issuerDID, - address: address, - signingMethod: signingMethod, - }, nil -} - -// GetIssuerDID returns the issuer DID derived from the enclave -func (b *MPCTokenBuilder) GetIssuerDID() string { - return b.issuerDID -} - -// GetAddress returns the address derived from the enclave -func (b *MPCTokenBuilder) GetAddress() string { - return b.address -} - -// CreateOriginToken creates a new origin UCAN token using MPC signing -func (b *MPCTokenBuilder) CreateOriginToken( - audienceDID string, - attenuations []Attenuation, - facts []Fact, - notBefore, expiresAt time.Time, -) (*Token, error) { - return b.createToken(audienceDID, nil, attenuations, facts, notBefore, expiresAt) -} - -// CreateDelegatedToken creates a delegated UCAN token using MPC signing -func (b *MPCTokenBuilder) CreateDelegatedToken( - parent *Token, - audienceDID string, - attenuations []Attenuation, - facts []Fact, - notBefore, expiresAt time.Time, -) (*Token, error) { - proofs, err := prepareDelegationProofs(parent, attenuations) - if err != nil { - return nil, err - } - - return b.createToken(audienceDID, proofs, attenuations, facts, notBefore, expiresAt) -} - -// createToken creates a UCAN token with MPC signing -func (b *MPCTokenBuilder) createToken( - audienceDID string, - proofs []Proof, - attenuations []Attenuation, - facts []Fact, - notBefore, expiresAt time.Time, -) (*Token, error) { - // Validate inputs - if !isValidDID(audienceDID) { - return nil, fmt.Errorf("invalid audience DID format: %s", audienceDID) - } - if len(attenuations) == 0 { - return nil, fmt.Errorf("at least one attenuation is required") - } - - // Create JWT token with MPC signing method - token := jwt.New(b.signingMethod) - - // Set UCAN version in header - token.Header["ucv"] = "0.9.0" - - // Prepare time claims - var nbfUnix, expUnix int64 - if !notBefore.IsZero() { - nbfUnix = notBefore.Unix() - } - if !expiresAt.IsZero() { - expUnix = expiresAt.Unix() - } - - // Convert attenuations to claim format - attClaims := make([]map[string]any, len(attenuations)) - for i, att := range attenuations { - attClaims[i] = map[string]any{ - "can": att.Capability.GetActions(), - "with": att.Resource.GetURI(), - } - } - - // Convert proofs to strings - proofStrings := make([]string, len(proofs)) - for i, proof := range proofs { - proofStrings[i] = string(proof) - } - - // Convert facts to any slice - factData := make([]any, len(facts)) - for i, fact := range facts { - // Facts are stored as raw JSON, convert to any - factData[i] = string(fact.Data) - } - - // Set claims - claims := jwt.MapClaims{ - "iss": b.issuerDID, - "aud": audienceDID, - "att": attClaims, - } - - if nbfUnix > 0 { - claims["nbf"] = nbfUnix - } - if expUnix > 0 { - claims["exp"] = expUnix - } - if len(proofStrings) > 0 { - claims["prf"] = proofStrings - } - if len(factData) > 0 { - claims["fct"] = factData - } - - token.Claims = claims - - // Sign the token using MPC enclave (key parameter is ignored for MPC signing) - tokenString, err := token.SignedString(nil) - if err != nil { - return nil, fmt.Errorf("failed to sign token with MPC: %w", err) - } - - return &Token{ - Raw: tokenString, - Issuer: b.issuerDID, - Audience: audienceDID, - ExpiresAt: expUnix, - NotBefore: nbfUnix, - Attenuations: attenuations, - Proofs: proofs, - Facts: facts, - }, nil -} - -// CreateVaultCapabilityToken creates a vault-specific UCAN token -func (b *MPCTokenBuilder) CreateVaultCapabilityToken( - audienceDID string, - vaultAddress string, - enclaveDataCID string, - actions []string, - expiresAt time.Time, -) (*Token, error) { - // Create vault-specific attenuation - attenuation := CreateVaultAttenuation(actions, enclaveDataCID, vaultAddress) - - return b.CreateOriginToken( - audienceDID, - []Attenuation{attenuation}, - nil, - time.Time{}, // No not-before restriction - expiresAt, - ) -} - -// MPCDIDResolver resolves DIDs with special handling for MPC-derived DIDs -type MPCDIDResolver struct { - enclave mpc.Enclave - issuerDID string - fallback DIDResolver -} - -// NewMPCDIDResolver creates a new MPC DID resolver -func NewMPCDIDResolver(enclave mpc.Enclave, fallback DIDResolver) *MPCDIDResolver { - pubKeyBytes := enclave.PubKeyBytes() - issuerDID, _ := deriveIssuerDIDFromBytes(pubKeyBytes) - - return &MPCDIDResolver{ - enclave: enclave, - issuerDID: issuerDID, - fallback: fallback, - } -} - -// ResolveDIDKey resolves DID keys with MPC enclave support -func (r *MPCDIDResolver) ResolveDIDKey(ctx context.Context, didStr string) (keys.DID, error) { - // Check if this is the MPC-derived DID - if didStr == r.issuerDID { - return r.createDIDFromEnclave() - } - - // Fall back to standard DID resolution - if r.fallback != nil { - return r.fallback.ResolveDIDKey(ctx, didStr) - } - - // Default fallback to string parsing - return keys.Parse(didStr) -} - -// createDIDFromEnclave creates a DID from the MPC enclave's public key -func (r *MPCDIDResolver) createDIDFromEnclave() (keys.DID, error) { - // This would need to be implemented based on how MPC public keys - // are converted to the keys.DID format - // For now, parse from the derived DID string - return keys.Parse(r.issuerDID) -} - -// MPCVerifier provides UCAN verification with MPC support -type MPCVerifier struct { - *Verifier - enclave mpc.Enclave -} - -// NewMPCVerifier creates a UCAN verifier with MPC support -func NewMPCVerifier(enclave mpc.Enclave) *MPCVerifier { - resolver := NewMPCDIDResolver(enclave, StringDIDResolver{}) - verifier := NewVerifier(resolver) - - return &MPCVerifier{ - Verifier: verifier, - enclave: enclave, - } -} - -// VerifyMPCToken verifies a UCAN token that may be signed with MPC -func (v *MPCVerifier) VerifyMPCToken(ctx context.Context, tokenString string) (*Token, error) { - // Try standard verification first - token, err := v.VerifyToken(ctx, tokenString) - if err == nil { - return token, nil - } - - // If standard verification fails, try MPC-specific verification - return v.verifyWithMPC(ctx, tokenString) -} - -// verifyWithMPC attempts to verify using MPC signing method -func (v *MPCVerifier) verifyWithMPC(_ context.Context, tokenString string) (*Token, error) { - // Create MPC signing method for verification - mpcMethod := NewMPCSigningMethod("MPC256", v.enclave) - - // Parse with MPC method - token, err := jwt.Parse(tokenString, func(token *jwt.Token) (any, error) { - // Ensure the token uses MPC signing method - if token.Method.Alg() != mpcMethod.Alg() { - return nil, fmt.Errorf("unexpected signing method: %v", token.Method) - } - // For MPC verification, the key is not used - return nil, nil - }) - if err != nil { - return nil, fmt.Errorf("MPC token verification failed: %w", err) - } - - // Extract and parse claims - claims, ok := token.Claims.(jwt.MapClaims) - if !ok { - return nil, fmt.Errorf("invalid token claims type") - } - - ucanToken, err := v.parseUCANClaims(claims, tokenString) - if err != nil { - return nil, fmt.Errorf("failed to parse UCAN claims: %w", err) - } - - return ucanToken, nil -} - -// MPCTokenValidator provides comprehensive UCAN token validation with MPC support -type MPCTokenValidator struct { - *MPCVerifier - enclaveValidation bool -} - -// NewMPCTokenValidator creates a comprehensive UCAN token validator with MPC support -func NewMPCTokenValidator(enclave mpc.Enclave, enableEnclaveValidation bool) *MPCTokenValidator { - verifier := NewMPCVerifier(enclave) - return &MPCTokenValidator{ - MPCVerifier: verifier, - enclaveValidation: enableEnclaveValidation, - } -} - -// ValidateTokenForVaultOperation performs comprehensive validation for vault operations -func (v *MPCTokenValidator) ValidateTokenForVaultOperation( - ctx context.Context, - tokenString string, - enclaveDataCID string, - requiredAction string, - vaultAddress string, -) (*Token, error) { - // Step 1: Verify token signature and structure - token, err := v.VerifyMPCToken(ctx, tokenString) - if err != nil { - return nil, fmt.Errorf("token verification failed: %w", err) - } - - // Step 2: Validate vault-specific capability - if err := ValidateVaultTokenCapability(token, enclaveDataCID, requiredAction); err != nil { - return nil, fmt.Errorf("vault capability validation failed: %w", err) - } - - // Step 3: Validate enclave data CID if enabled - if v.enclaveValidation { - if err := v.validateEnclaveDataCID(token, enclaveDataCID); err != nil { - return nil, fmt.Errorf("enclave data validation failed: %w", err) - } - } - - // Step 4: Validate vault address if provided - if vaultAddress != "" { - if err := v.validateVaultAddress(token, vaultAddress); err != nil { - return nil, fmt.Errorf("vault address validation failed: %w", err) - } - } - - // Step 5: Verify delegation chain if proofs exist - if len(token.Proofs) > 0 { - if err := v.VerifyDelegationChain(ctx, tokenString); err != nil { - return nil, fmt.Errorf("delegation chain validation failed: %w", err) - } - } - - return token, nil -} - -// ValidateTokenForResource validates token capabilities for a specific resource -func (v *MPCTokenValidator) ValidateTokenForResource( - ctx context.Context, - tokenString string, - resourceURI string, - requiredAbilities []string, -) (*Token, error) { - token, err := v.VerifyCapability(ctx, tokenString, resourceURI, requiredAbilities) - if err != nil { - return nil, fmt.Errorf("capability verification failed: %w", err) - } - - // Additional MPC-specific validation - if v.enclaveValidation { - if err := v.validateMPCIssuer(token); err != nil { - return nil, fmt.Errorf("MPC issuer validation failed: %w", err) - } - } - - return token, nil -} - -// validateEnclaveDataCID validates that the token contains the expected enclave data CID -func (v *MPCTokenValidator) validateEnclaveDataCID(token *Token, expectedCID string) error { - tokenCID, err := GetEnclaveDataCID(token) - if err != nil { - return fmt.Errorf("failed to extract enclave data CID from token: %w", err) - } - - if tokenCID != expectedCID { - return fmt.Errorf("enclave data CID mismatch: token=%s, expected=%s", tokenCID, expectedCID) - } - - return nil -} - -// validateVaultAddress validates the vault address in token capabilities -func (v *MPCTokenValidator) validateVaultAddress(token *Token, expectedAddress string) error { - for _, att := range token.Attenuations { - if vaultCap, ok := att.Capability.(*VaultCapability); ok { - if vaultCap.VaultAddress != "" && vaultCap.VaultAddress != expectedAddress { - return fmt.Errorf("vault address mismatch: token=%s, expected=%s", - vaultCap.VaultAddress, expectedAddress) - } - } - } - return nil -} - -// validateMPCIssuer validates that the token issuer matches the MPC enclave -func (v *MPCTokenValidator) validateMPCIssuer(token *Token) error { - expectedIssuer, _ := deriveIssuerDIDFromBytes(v.enclave.PubKeyBytes()) - - if token.Issuer != expectedIssuer { - return fmt.Errorf("token issuer does not match MPC enclave: token=%s, expected=%s", - token.Issuer, expectedIssuer) - } - - return nil -} - -// createMPCVaultAttenuation creates MPC-specific vault attenuations -func createMPCVaultAttenuation(actions []string, enclaveDataCID, vaultAddress string) Attenuation { - // Use the existing CreateVaultAttenuation function but add MPC-specific validation - return CreateVaultAttenuation(actions, enclaveDataCID, vaultAddress) -} - -// containsAdminAction checks if actions contain admin-level permissions -func containsAdminAction(actions []string) bool { - adminActions := map[string]bool{ - "admin": true, "export": true, "import": true, "delete": true, - } - - for _, action := range actions { - if adminActions[action] { - return true - } - } - return false -} - -// ValidateEnclaveDataIntegrity validates enclave data against IPFS CID -func ValidateEnclaveDataIntegrity(enclaveData *mpc.EnclaveData, expectedCID string) error { - if enclaveData == nil { - return fmt.Errorf("enclave data cannot be nil") - } - - // Basic validation of enclave structure - if len(enclaveData.PubBytes) == 0 { - return fmt.Errorf("enclave public key bytes cannot be empty") - } - - if enclaveData.PubHex == "" { - return fmt.Errorf("enclave public key hex cannot be empty") - } - - // Implement IPFS CID validation against enclave data hash - // Serialize the enclave data for consistent hashing - enclaveDataBytes, err := enclaveData.Marshal() - if err != nil { - return fmt.Errorf("failed to marshal enclave data: %w", err) - } - - // 1. Hash the enclave data using SHA-256 - hasher := sha256.New() - hasher.Write(enclaveDataBytes) - digest := hasher.Sum(nil) - - // 2. Create multihash with SHA-256 prefix - mhash, err := multihash.EncodeName(digest, "sha2-256") - if err != nil { - return fmt.Errorf("failed to create multihash: %w", err) - } - - // 3. Create CID and compare with expected - parsedExpectedCID, err := cid.Parse(expectedCID) - if err != nil { - return fmt.Errorf("failed to parse expected CID: %w", err) - } - - // Create CID v1 with dag-pb codec (IPFS default) - calculatedCID := cid.NewCidV1(cid.DagProtobuf, mhash) - - // Compare CIDs - if !parsedExpectedCID.Equals(calculatedCID) { - return fmt.Errorf( - "CID verification failed: expected %s, calculated %s", - parsedExpectedCID.String(), - calculatedCID.String(), - ) - } - - return nil -} - -// MPCCapabilityBuilder helps build MPC-specific capabilities -type MPCCapabilityBuilder struct { - enclave mpc.Enclave - builder *MPCTokenBuilder -} - -// NewMPCCapabilityBuilder creates a new MPC capability builder -func NewMPCCapabilityBuilder(enclave mpc.Enclave) (*MPCCapabilityBuilder, error) { - builder, err := NewMPCTokenBuilder(enclave) - if err != nil { - return nil, fmt.Errorf("failed to create MPC token builder: %w", err) - } - - return &MPCCapabilityBuilder{ - enclave: enclave, - builder: builder, - }, nil -} - -// CreateVaultAdminCapability creates admin-level vault capabilities -func (b *MPCCapabilityBuilder) CreateVaultAdminCapability( - vaultAddress, enclaveDataCID string, -) Attenuation { - allActions := []string{"read", "write", "sign", "export", "import", "delete", "admin"} - return CreateVaultAttenuation(allActions, enclaveDataCID, vaultAddress) -} - -// CreateVaultReadOnlyCapability creates read-only vault capabilities -func (b *MPCCapabilityBuilder) CreateVaultReadOnlyCapability( - vaultAddress, enclaveDataCID string, -) Attenuation { - readActions := []string{"read"} - return CreateVaultAttenuation(readActions, enclaveDataCID, vaultAddress) -} - -// CreateVaultSigningCapability creates signing-specific vault capabilities -func (b *MPCCapabilityBuilder) CreateVaultSigningCapability( - vaultAddress, enclaveDataCID string, -) Attenuation { - signActions := []string{"read", "sign"} - return CreateVaultAttenuation(signActions, enclaveDataCID, vaultAddress) -} - -// CreateCustomCapability creates a custom capability with specified actions -func (b *MPCCapabilityBuilder) CreateCustomCapability( - actions []string, - vaultAddress, enclaveDataCID string, -) Attenuation { - return CreateVaultAttenuation(actions, enclaveDataCID, vaultAddress) -} - -// Utility functions - -// deriveIssuerDIDFromBytes creates issuer DID and address from public key bytes -// Enhanced version using the crypto/keys package -func deriveIssuerDIDFromBytes(pubKeyBytes []byte) (string, string) { - // Use the enhanced NewFromMPCPubKey method from crypto/keys - did, err := keys.NewFromMPCPubKey(pubKeyBytes) - if err != nil { - // Fallback to simplified implementation - address := fmt.Sprintf("addr_%x", pubKeyBytes[:8]) - issuerDID := fmt.Sprintf("did:sonr:%s", address) - return issuerDID, address - } - - // Use the proper DID generation and address derivation - didStr := did.String() - address, err := did.Address() - if err != nil { - // Fallback to simplified address - address = fmt.Sprintf("addr_%x", pubKeyBytes[:8]) - } - - return didStr, address -} diff --git a/internal/crypto/ucan/source.go b/internal/crypto/ucan/source.go deleted file mode 100644 index ee9fbff..0000000 --- a/internal/crypto/ucan/source.go +++ /dev/null @@ -1,302 +0,0 @@ -// Package ucan provides User-Controlled Authorization Networks (UCAN) implementation -// for decentralized authorization and capability delegation in the Sonr network. -// This package handles JWT-based tokens, cryptographic verification, and resource capabilities. -package ucan - -import ( - "fmt" - "time" - - "github.com/golang-jwt/jwt/v5" - "github.com/sonr-io/crypto/keys" - "github.com/sonr-io/crypto/mpc" - "lukechampine.com/blake3" -) - -// KeyshareSource provides MPC-based UCAN token creation and validation -type KeyshareSource interface { - Address() string - Issuer() string - ChainCode() ([]byte, error) - OriginToken() (*Token, error) - SignData(data []byte) ([]byte, error) - VerifyData(data []byte, sig []byte) (bool, error) - Enclave() mpc.Enclave - - // UCAN token creation methods - NewOriginToken( - audienceDID string, - att []Attenuation, - fct []Fact, - notBefore, expires time.Time, - ) (*Token, error) - NewAttenuatedToken( - parent *Token, - audienceDID string, - att []Attenuation, - fct []Fact, - nbf, exp time.Time, - ) (*Token, error) -} - -// mpcKeyshareSource implements KeyshareSource using MPC enclave -type mpcKeyshareSource struct { - enclave mpc.Enclave - issuerDID string - addr string -} - -// NewMPCKeyshareSource creates a new MPC-based keyshare source from an enclave -func NewMPCKeyshareSource(enclave mpc.Enclave) (KeyshareSource, error) { - if !enclave.IsValid() { - return nil, fmt.Errorf("invalid MPC enclave provided") - } - - pubKeyBytes := enclave.PubKeyBytes() - issuerDID, addr, err := getIssuerDIDFromBytes(pubKeyBytes) - if err != nil { - return nil, fmt.Errorf("failed to derive issuer DID: %w", err) - } - - return &mpcKeyshareSource{ - enclave: enclave, - issuerDID: issuerDID, - addr: addr, - }, nil -} - -// Address returns the address derived from the enclave public key -func (k *mpcKeyshareSource) Address() string { - return k.addr -} - -// Issuer returns the DID of the issuer derived from the enclave public key -func (k *mpcKeyshareSource) Issuer() string { - return k.issuerDID -} - -// Enclave returns the underlying MPC enclave -func (k *mpcKeyshareSource) Enclave() mpc.Enclave { - return k.enclave -} - -// ChainCode derives a deterministic chain code from the enclave -func (k *mpcKeyshareSource) ChainCode() ([]byte, error) { - // Sign the address to create a deterministic chain code - sig, err := k.SignData([]byte(k.addr)) - if err != nil { - return nil, fmt.Errorf("failed to sign address for chain code: %w", err) - } - - // Hash the signature to create a 32-byte chain code - hash := blake3.Sum256(sig) - return hash[:32], nil -} - -// OriginToken creates a default origin token with basic capabilities -func (k *mpcKeyshareSource) OriginToken() (*Token, error) { - // Create basic capability for the MPC keyshare - resource := &SimpleResource{ - Scheme: "mpc", - Value: k.addr, - URI: fmt.Sprintf("mpc://%s", k.addr), - } - - capability := &SimpleCapability{Action: "sign"} - - attenuation := Attenuation{ - Capability: capability, - Resource: resource, - } - - // Create token with no expiration for origin token - zero := time.Time{} - return k.NewOriginToken(k.issuerDID, []Attenuation{attenuation}, nil, zero, zero) -} - -// SignData signs data using the MPC enclave -func (k *mpcKeyshareSource) SignData(data []byte) ([]byte, error) { - if !k.enclave.IsValid() { - return nil, fmt.Errorf("enclave is not valid") - } - - return k.enclave.Sign(data) -} - -// VerifyData verifies a signature using the MPC enclave -func (k *mpcKeyshareSource) VerifyData(data []byte, sig []byte) (bool, error) { - if !k.enclave.IsValid() { - return false, fmt.Errorf("enclave is not valid") - } - - return k.enclave.Verify(data, sig) -} - -// NewOriginToken creates a new UCAN origin token using MPC signing -func (k *mpcKeyshareSource) NewOriginToken( - audienceDID string, - att []Attenuation, - fct []Fact, - notBefore, expires time.Time, -) (*Token, error) { - return k.newToken(audienceDID, nil, att, fct, notBefore, expires) -} - -// NewAttenuatedToken creates a new attenuated UCAN token using MPC signing -func (k *mpcKeyshareSource) NewAttenuatedToken( - parent *Token, - audienceDID string, - att []Attenuation, - fct []Fact, - nbf, exp time.Time, -) (*Token, error) { - // Validate that new attenuations are more restrictive than parent - if !isAttenuationSubset(att, parent.Attenuations) { - return nil, fmt.Errorf("scope of ucan attenuations must be less than its parent") - } - - // Add parent as proof - proofs := []Proof{} - if parent.Raw != "" { - proofs = append(proofs, Proof(parent.Raw)) - } - proofs = append(proofs, parent.Proofs...) - - return k.newToken(audienceDID, proofs, att, fct, nbf, exp) -} - -// newToken creates a new UCAN token with MPC signing -func (k *mpcKeyshareSource) newToken( - audienceDID string, - proofs []Proof, - att []Attenuation, - fct []Fact, - nbf, exp time.Time, -) (*Token, error) { - // Validate audience DID - if !isValidDID(audienceDID) { - return nil, fmt.Errorf("invalid audience DID: %s", audienceDID) - } - - // Create JWT with MPC signing method - signingMethod := NewMPCSigningMethod("MPC256", k.enclave) - t := jwt.New(signingMethod) - - // Set UCAN version header - t.Header["ucv"] = "0.9.0" - - var ( - nbfUnix int64 - expUnix int64 - ) - - if !nbf.IsZero() { - nbfUnix = nbf.Unix() - } - if !exp.IsZero() { - expUnix = exp.Unix() - } - - // Convert attenuations to claim format - attClaims := make([]map[string]any, len(att)) - for i, a := range att { - attClaims[i] = map[string]any{ - "can": a.Capability.GetActions(), - "with": a.Resource.GetURI(), - } - } - - // Convert proofs to strings - proofStrings := make([]string, len(proofs)) - for i, proof := range proofs { - proofStrings[i] = string(proof) - } - - // Convert facts to any slice - factData := make([]any, len(fct)) - for i, fact := range fct { - factData[i] = string(fact.Data) - } - - // Set claims - claims := jwt.MapClaims{ - "iss": k.issuerDID, - "aud": audienceDID, - "att": attClaims, - } - - if nbfUnix > 0 { - claims["nbf"] = nbfUnix - } - if expUnix > 0 { - claims["exp"] = expUnix - } - if len(proofStrings) > 0 { - claims["prf"] = proofStrings - } - if len(factData) > 0 { - claims["fct"] = factData - } - - t.Claims = claims - - // Sign the token using MPC enclave - tokenString, err := t.SignedString(nil) - if err != nil { - return nil, fmt.Errorf("failed to sign token: %w", err) - } - - return &Token{ - Raw: tokenString, - Issuer: k.issuerDID, - Audience: audienceDID, - ExpiresAt: expUnix, - NotBefore: nbfUnix, - Attenuations: att, - Proofs: proofs, - Facts: fct, - }, nil -} - -// getIssuerDIDFromBytes creates an issuer DID and address from public key bytes -func getIssuerDIDFromBytes(pubKeyBytes []byte) (string, string, error) { - // Use the enhanced NewFromMPCPubKey method for proper MPC integration - did, err := keys.NewFromMPCPubKey(pubKeyBytes) - if err != nil { - return "", "", fmt.Errorf("failed to create DID from MPC public key: %w", err) - } - - didStr := did.String() - - // Use the enhanced Address method for blockchain-compatible address derivation - address, err := did.Address() - if err != nil { - return "", "", fmt.Errorf("failed to derive address from DID: %w", err) - } - - return didStr, address, nil -} - -// isAttenuationSubset checks if child attenuations are a subset of parent attenuations -func isAttenuationSubset(child, parent []Attenuation) bool { - for _, childAtt := range child { - if !containsAttenuation(parent, childAtt) { - return false - } - } - return true -} - -// containsAttenuation checks if the parent list contains an equivalent attenuation -func containsAttenuation(parent []Attenuation, att Attenuation) bool { - for _, parentAtt := range parent { - if parentAtt.Resource.Matches(att.Resource) && - parentAtt.Capability.Contains(att.Capability) { - return true - } - } - return false -} - -// Note: MPC signing methods are already implemented in mpc.go -// Note: isValidDID is already implemented in stubs.go diff --git a/internal/crypto/ucan/stubs.go b/internal/crypto/ucan/stubs.go deleted file mode 100644 index c50600d..0000000 --- a/internal/crypto/ucan/stubs.go +++ /dev/null @@ -1,87 +0,0 @@ -package ucan - -import ( - "time" -) - -// TokenBuilderInterface defines token building methods -type TokenBuilderInterface interface { - CreateOriginToken( - issuer string, - capabilities []Attenuation, - facts []Fact, - start, expiry time.Time, - ) (*Token, error) - CreateDelegatedToken( - parentToken *Token, - issuer string, - capabilities []Attenuation, - facts []Fact, - start, expiry time.Time, - ) (*Token, error) -} - -// TokenBuilder implements token builder functionality -type TokenBuilder struct { - Capability Attenuation -} - -// CreateOriginToken creates a new origin token -func (tb *TokenBuilder) CreateOriginToken( - issuer string, - capabilities []Attenuation, - facts []Fact, - start, expiry time.Time, -) (*Token, error) { - return &Token{ - Raw: "", - Issuer: issuer, - Audience: "", - ExpiresAt: expiry.Unix(), - NotBefore: start.Unix(), - Attenuations: capabilities, - Proofs: []Proof{}, - Facts: facts, - }, nil -} - -// CreateDelegatedToken creates a delegated token -func (tb *TokenBuilder) CreateDelegatedToken( - parentToken *Token, - issuer string, - capabilities []Attenuation, - facts []Fact, - start, expiry time.Time, -) (*Token, error) { - proofs := []Proof{} - if parentToken.Raw != "" { - proofs = append(proofs, Proof(parentToken.Raw)) - } - - return &Token{ - Raw: "", - Issuer: issuer, - Audience: parentToken.Issuer, - ExpiresAt: expiry.Unix(), - NotBefore: start.Unix(), - Attenuations: capabilities, - Proofs: proofs, - Facts: facts, - }, nil -} - -// Stub for DID validation -func isValidDID(did string) bool { - // Basic DID validation stub - return did != "" && len(did) > 5 && did[:4] == "did:" -} - -// Stub for preparing delegation proofs -func prepareDelegationProofs(token *Token, capabilities []Attenuation) ([]Proof, error) { - // Minimal stub implementation - proofs := []Proof{} - if token.Raw != "" { - proofs = append(proofs, Proof(token.Raw)) - } - return proofs, nil -} diff --git a/internal/crypto/ucan/ucan_test.go b/internal/crypto/ucan/ucan_test.go deleted file mode 100644 index 3a38d4f..0000000 --- a/internal/crypto/ucan/ucan_test.go +++ /dev/null @@ -1,313 +0,0 @@ -package ucan - -import ( - "crypto/sha256" - "testing" - "time" - - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" -) - -func TestCapabilityCreation(t *testing.T) { - testCases := []struct { - name string - actions []string - expected bool - }{ - { - name: "Basic Capability Creation", - actions: []string{"read", "write"}, - expected: true, - }, - { - name: "Empty Actions", - actions: []string{}, - expected: true, - }, - { - name: "Complex Actions", - actions: []string{"create", "update", "delete", "admin"}, - expected: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - capability := &MultiCapability{Actions: tc.actions} - - assert.NotNil(t, capability) - assert.Equal(t, len(tc.actions), len(capability.Actions)) - - for _, action := range tc.actions { - assert.Contains(t, capability.Actions, action) - } - }) - } -} - -func TestCapabilityValidation(t *testing.T) { - testCases := []struct { - name string - actions []string - resourceScheme string - shouldPass bool - }{ - { - name: "Valid Standard Actions", - actions: []string{"read", "write"}, - resourceScheme: "example", - shouldPass: true, - }, - { - name: "Invalid Actions", - actions: []string{"delete", "admin"}, - resourceScheme: "restricted", - shouldPass: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - capability := &MultiCapability{Actions: tc.actions} - resource := &SimpleResource{ - Scheme: tc.resourceScheme, - Value: "test", - URI: tc.resourceScheme + "://test", - } - - attenuation := Attenuation{ - Capability: capability, - Resource: resource, - } - - StandardTemplate.AddAllowedActions(tc.resourceScheme, []string{"read", "write"}) - err := StandardTemplate.ValidateAttenuation(attenuation) - - if tc.shouldPass { - assert.NoError(t, err) - } else { - assert.Error(t, err) - } - }) - } -} - -func TestJWTTokenLifecycle(t *testing.T) { - testCases := []struct { - name string - actions []string - resourceScheme string - duration time.Duration - shouldPass bool - }{ - { - name: "Valid Token Generation and Verification", - actions: []string{"read", "write"}, - resourceScheme: "example", - duration: time.Hour, - shouldPass: true, - }, - { - name: "Expired Token", - actions: []string{"read"}, - resourceScheme: "test", - duration: -time.Hour, // Expired token - shouldPass: false, - }, - } - - // Use standard service template for testing - StandardTemplate := StandardServiceTemplate() - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - capability := &MultiCapability{Actions: tc.actions} - resource := &SimpleResource{ - Scheme: tc.resourceScheme, - Value: "test", - URI: tc.resourceScheme + "://test", - } - - attenuation := Attenuation{ - Capability: capability, - Resource: resource, - } - - // Validate attenuation against template - err := StandardTemplate.ValidateAttenuation(attenuation) - require.NoError(t, err) - - // Simulate JWT token generation and verification - token := "test_token_" + time.Now().String() - - if tc.shouldPass { - // Simulate verification - verifiedToken := &Token{ - Raw: token, - Issuer: "did:sonr:local", - Attenuations: []Attenuation{attenuation}, - ExpiresAt: time.Now().Add(tc.duration).Unix(), - } - - assert.NotNil(t, verifiedToken) - assert.Equal(t, "did:sonr:local", verifiedToken.Issuer) - assert.Len(t, verifiedToken.Attenuations, 1) - assert.Equal( - t, - tc.resourceScheme+"://test", - verifiedToken.Attenuations[0].Resource.GetURI(), - ) - } else { - // Simulate expired token verification - assert.True(t, time.Now().Unix() > time.Now().Add(tc.duration).Unix()) - } - }) - } -} - -func TestCapabilityRevocation(t *testing.T) { - capability := &MultiCapability{Actions: []string{"read", "write"}} - resource := &SimpleResource{ - Scheme: "example", - Value: "test", - URI: "example://test", - } - - attenuation := Attenuation{ - Capability: capability, - Resource: resource, - } - - // Generate token - token, err := GenerateJWTToken(attenuation, time.Hour) - require.NoError(t, err) - - // Revoke capability - err = RevokeCapability(attenuation) - assert.NoError(t, err) - - // Attempt to verify revoked token should fail - _, err = VerifyJWTToken(token) - assert.Error(t, err) - assert.Contains(t, err.Error(), "token has been revoked") -} - -func TestResourceValidation(t *testing.T) { - testCases := []struct { - name string - resourceScheme string - resourceValue string - resourceURI string - expectValid bool - }{ - { - name: "Valid Resource", - resourceScheme: "sonr", - resourceValue: "test-resource", - resourceURI: "sonr://test-resource", - expectValid: true, - }, - { - name: "Invalid Resource URI", - resourceScheme: "invalid", - resourceValue: "test-resource", - resourceURI: "invalid-malformed-uri", - expectValid: false, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - resource := &SimpleResource{ - Scheme: tc.resourceScheme, - Value: tc.resourceValue, - URI: tc.resourceURI, - } - - // Simplified resource validation - if tc.expectValid { - assert.Regexp(t, `^[a-z]+://[a-z-]+$`, resource.URI) - } else { - assert.NotRegexp(t, `^[a-z]+://[a-z-]+$`, resource.URI) - } - }) - } -} - -func TestValidateEnclaveDataCIDIntegrity(t *testing.T) { - testCases := []struct { - name string - data []byte - expectedCID string - expectError bool - errorContains string - }{ - { - name: "Empty CID", - data: []byte("test data"), - expectedCID: "", - expectError: true, - errorContains: "enclave data CID cannot be empty", - }, - { - name: "Empty data", - data: []byte{}, - expectedCID: "QmTest", - expectError: true, - errorContains: "enclave data cannot be empty", - }, - { - name: "Invalid CID format", - data: []byte("test data"), - expectedCID: "invalid-cid", - expectError: true, - errorContains: "invalid IPFS CID format", - }, - { - name: "Valid CID verification - should pass", - data: []byte("test data"), - expectedCID: generateValidCIDForData([]byte("test data")), - expectError: false, - }, - { - name: "Mismatched CID - should fail", - data: []byte("test data"), - expectedCID: generateValidCIDForData([]byte("different data")), - expectError: true, - errorContains: "CID verification failed", - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - err := ValidateEnclaveDataCIDIntegrity(tc.expectedCID, tc.data) - - if tc.expectError { - assert.Error(t, err) - if tc.errorContains != "" { - assert.Contains(t, err.Error(), tc.errorContains) - } - } else { - assert.NoError(t, err) - } - }) - } -} - -// Helper function to generate a valid CID for test data -func generateValidCIDForData(data []byte) string { - hasher := sha256.New() - hasher.Write(data) - digest := hasher.Sum(nil) - - mhash, err := multihash.EncodeName(digest, "sha2-256") - if err != nil { - panic(err) - } - - calculatedCID := cid.NewCidV1(cid.DagProtobuf, mhash) - return calculatedCID.String() -} diff --git a/internal/crypto/ucan/vault.go b/internal/crypto/ucan/vault.go deleted file mode 100644 index 579f533..0000000 --- a/internal/crypto/ucan/vault.go +++ /dev/null @@ -1,485 +0,0 @@ -// Package ucan provides User-Controlled Authorization Networks (UCAN) implementation -// for decentralized authorization and capability delegation in the Sonr network. -// This package handles JWT-based tokens, cryptographic verification, and resource capabilities. -package ucan - -import ( - "crypto/sha256" - "fmt" - "slices" - "strings" - "time" - - z "github.com/Oudwins/zog" - "github.com/ipfs/go-cid" - "github.com/multiformats/go-multihash" -) - -// Constants for vault capability actions -const ( - VaultAdminAction = "vault/admin" -) - -// VaultCapabilitySchema defines validation specifically for vault capabilities -var VaultCapabilitySchema = z.Struct(z.Shape{ - "can": z.String().Required().OneOf( - []string{ - VaultAdminAction, - "vault/read", - "vault/write", - "vault/sign", - "vault/export", - "vault/import", - "vault/delete", - }, - z.Message("Invalid vault capability"), - ), - "with": z.String(). - Required(). - TestFunc(ValidateIPFSCID, z.Message("Vault resource must be IPFS CID in format 'ipfs://CID'")), - "actions": z.Slice(z.String().OneOf( - []string{"read", "write", "sign", "export", "import", "delete"}, - z.Message("Invalid vault action"), - )).Optional(), - "vault": z.String().Required().Min(1, z.Message("Vault address cannot be empty")), - "cavs": z.Slice(z.String()).Optional(), // Caveats as string array for vault capabilities -}) - -// VaultCapability implements Capability for vault-specific operations -// with support for admin permissions, actions, and enclave data management. -type VaultCapability struct { - Action string `json:"can"` - Actions []string `json:"actions,omitempty"` - VaultAddress string `json:"vault,omitempty"` - Caveats []string `json:"cavs,omitempty"` - EnclaveDataCID string `json:"enclave_data_cid,omitempty"` - Metadata map[string]string `json:"metadata,omitempty"` -} - -// GetActions returns the actions this vault capability grants -func (c *VaultCapability) GetActions() []string { - if c.Action == VaultAdminAction { - // Admin capability grants all vault actions - return []string{"read", "write", "sign", "export", "import", "delete", VaultAdminAction} - } - - if len(c.Actions) > 0 { - return c.Actions - } - - // Extract action from the main capability string - if strings.HasPrefix(c.Action, "vault/") { - return []string{c.Action[6:]} // Remove "vault/" prefix - } - - return []string{c.Action} -} - -// Grants checks if this capability grants the required abilities -func (c *VaultCapability) Grants(abilities []string) bool { - if c.Action == VaultAdminAction { - // Admin capability grants everything - return true - } - - grantedActions := make(map[string]bool) - for _, action := range c.GetActions() { - grantedActions[action] = true - grantedActions["vault/"+action] = true // Support both formats - } - - // Check each required ability - for _, ability := range abilities { - if !grantedActions[ability] { - return false - } - } - - return true -} - -// Contains checks if this capability contains another capability -func (c *VaultCapability) Contains(other Capability) bool { - if c.Action == VaultAdminAction { - // Admin contains all vault capabilities - if otherVault, ok := other.(*VaultCapability); ok { - return strings.HasPrefix(otherVault.Action, "vault/") - } - // Admin contains any action that starts with vault-related actions - for _, action := range other.GetActions() { - if strings.HasPrefix(action, "vault/") || - action == "read" || action == "write" || action == "sign" || - action == "export" || action == "import" || action == "delete" { - return true - } - } - return false - } - - // Check if our actions contain all of the other capability's actions - ourActions := make(map[string]bool) - for _, action := range c.GetActions() { - ourActions[action] = true - ourActions["vault/"+action] = true - } - - for _, otherAction := range other.GetActions() { - if !ourActions[otherAction] { - return false - } - } - - return true -} - -// String returns string representation -func (c *VaultCapability) String() string { - return c.Action -} - -// VaultResourceExt represents an extended IPFS-based vault resource (to avoid redeclaration) -type VaultResourceExt struct { - SimpleResource - VaultAddress string `json:"vault_address"` - EnclaveDataCID string `json:"enclave_data_cid"` -} - -// ValidateIPFSCID validates IPFS CID format for vault resources -func ValidateIPFSCID(value *string, ctx z.Ctx) bool { - if !strings.HasPrefix(*value, "ipfs://") { - return false - } - cidStr := (*value)[7:] // Remove "ipfs://" prefix - - // Enhanced CID validation - return validateCIDFormat(cidStr) -} - -// validateCIDFormat performs comprehensive IPFS CID format validation -func validateCIDFormat(cidStr string) bool { - if len(cidStr) == 0 { - return false - } - - // CIDv0: Base58-encoded SHA-256 multihash (starts with 'Qm' and is 46 characters) - if strings.HasPrefix(cidStr, "Qm") && len(cidStr) == 46 { - return isValidBase58(cidStr) - } - - // CIDv1: Base32 or Base58 encoded (starts with 'b' for base32 or other prefixes) - if len(cidStr) >= 59 { - // CIDv1 in base32 typically starts with 'b' and is longer - if strings.HasPrefix(cidStr, "b") { - return isValidBase32(cidStr[1:]) // Remove 'b' prefix - } - // CIDv1 in base58 or other encodings - return isValidBase58(cidStr) - } - - return false -} - -// isValidBase58 checks if string contains valid base58 characters -func isValidBase58(s string) bool { - base58Chars := "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" - for _, char := range s { - if !strings.Contains(base58Chars, string(char)) { - return false - } - } - return true -} - -// isValidBase32 checks if string contains valid base32 characters -func isValidBase32(s string) bool { - base32Chars := "abcdefghijklmnopqrstuvwxyz234567" - for _, char := range s { - if !strings.Contains(base32Chars, string(char)) { - return false - } - } - return true -} - -// ValidateEnclaveDataCIDIntegrity validates enclave data against expected CID -func ValidateEnclaveDataCIDIntegrity(enclaveDataCID string, enclaveData []byte) error { - if enclaveDataCID == "" { - return fmt.Errorf("enclave data CID cannot be empty") - } - - if len(enclaveData) == 0 { - return fmt.Errorf("enclave data cannot be empty") - } - - // Validate CID format first - if !validateCIDFormat(enclaveDataCID) { - return fmt.Errorf("invalid IPFS CID format: %s", enclaveDataCID) - } - - // Implement actual CID verification by hashing enclave data - // 1. Hash the enclave data using SHA-256 - hasher := sha256.New() - hasher.Write(enclaveData) - digest := hasher.Sum(nil) - - // 2. Create multihash with SHA-256 prefix - mhash, err := multihash.EncodeName(digest, "sha2-256") - if err != nil { - return fmt.Errorf("failed to create multihash: %w", err) - } - - // 3. Create CID and compare with expected - expectedCID, err := cid.Parse(enclaveDataCID) - if err != nil { - return fmt.Errorf("failed to parse expected CID: %w", err) - } - - // Create CID v1 with dag-pb codec (IPFS default) - calculatedCID := cid.NewCidV1(cid.DagProtobuf, mhash) - - // Compare CIDs - if !expectedCID.Equals(calculatedCID) { - return fmt.Errorf( - "CID verification failed: expected %s, calculated %s", - expectedCID.String(), - calculatedCID.String(), - ) - } - - return nil -} - -// ValidateVaultCapability validates vault-specific capabilities -func ValidateVaultCapability(att map[string]any) error { - var validated struct { - Can string `json:"can"` - With string `json:"with"` - Actions []string `json:"actions,omitempty"` - Vault string `json:"vault"` - Cavs []string `json:"cavs,omitempty"` - } - - errs := VaultCapabilitySchema.Parse(att, &validated) - if errs != nil { - return fmt.Errorf("vault capability validation failed: %v", errs) - } - - return nil -} - -// VaultAttenuationConstructor creates vault-specific attenuations with enhanced validation -func VaultAttenuationConstructor(m map[string]any) (Attenuation, error) { - // First validate using vault-specific schema - if err := ValidateVaultCapability(m); err != nil { - return Attenuation{}, fmt.Errorf("vault attenuation validation failed: %w", err) - } - - capStr, withStr, err := extractRequiredFields(m) - if err != nil { - return Attenuation{}, err - } - - vaultCap := createVaultCapability(capStr, m) - resource := createVaultResource(withStr, vaultCap.VaultAddress) - - // Set enclave data CID if using IPFS resource - if vaultRes, ok := resource.(*VaultResource); ok { - vaultCap.EnclaveDataCID = vaultRes.EnclaveDataCID - } - - return Attenuation{ - Capability: vaultCap, - Resource: resource, - }, nil -} - -// extractRequiredFields extracts and validates required 'can' and 'with' fields -func extractRequiredFields(m map[string]any) (string, string, error) { - capValue, exists := m["can"] - if !exists { - return "", "", fmt.Errorf("missing 'can' field in attenuation") - } - capStr, ok := capValue.(string) - if !ok { - return "", "", fmt.Errorf("'can' field must be a string") - } - - withValue, exists := m["with"] - if !exists { - return "", "", fmt.Errorf("missing 'with' field in attenuation") - } - withStr, ok := withValue.(string) - if !ok { - return "", "", fmt.Errorf("'with' field must be a string") - } - - return capStr, withStr, nil -} - -// createVaultCapability creates and populates a VaultCapability from the input map -func createVaultCapability(action string, m map[string]any) *VaultCapability { - vaultCap := &VaultCapability{Action: action} - - if actions, exists := m["actions"]; exists { - vaultCap.Actions = extractStringSlice(actions) - } - - if vault, exists := m["vault"]; exists { - if vaultStr, ok := vault.(string); ok { - vaultCap.VaultAddress = vaultStr - } - } - - if cavs, exists := m["cavs"]; exists { - vaultCap.Caveats = extractStringSlice(cavs) - } - - return vaultCap -} - -// extractStringSlice safely extracts a string slice from an any -func extractStringSlice(value any) []string { - if slice, ok := value.([]any); ok { - result := make([]string, 0, len(slice)) - for _, item := range slice { - if str, ok := item.(string); ok { - result = append(result, str) - } - } - return result - } - return nil -} - -// createVaultResource creates appropriate Resource based on the URI scheme -func createVaultResource(withStr, vaultAddress string) Resource { - parts := strings.SplitN(withStr, "://", 2) - if len(parts) == 2 && parts[0] == "ipfs" { - return &VaultResource{ - SimpleResource: SimpleResource{ - Scheme: "ipfs", - Value: parts[1], - URI: withStr, - }, - VaultAddress: vaultAddress, - EnclaveDataCID: parts[1], - } - } - - return &SimpleResource{ - Scheme: "ipfs", - Value: withStr, - URI: withStr, - } -} - -// NewVaultAdminToken creates a new UCAN token with vault admin capabilities -func NewVaultAdminToken( - builder TokenBuilderInterface, - vaultOwnerDID string, - vaultAddress string, - enclaveDataCID string, - exp time.Time, -) (*Token, error) { - // Validate input parameters - if !isValidDID(vaultOwnerDID) { - return nil, fmt.Errorf("invalid vault owner DID: %s", vaultOwnerDID) - } - - // Create vault admin attenuation with full permissions - vaultResource := &VaultResource{ - SimpleResource: SimpleResource{ - Scheme: "ipfs", - Value: enclaveDataCID, - URI: fmt.Sprintf("ipfs://%s", enclaveDataCID), - }, - VaultAddress: vaultAddress, - EnclaveDataCID: enclaveDataCID, - } - - vaultCap := &VaultCapability{ - Action: VaultAdminAction, - Actions: []string{"read", "write", "sign", "export", "import", "delete"}, - VaultAddress: vaultAddress, - EnclaveDataCID: enclaveDataCID, - } - - // Validate the vault capability using vault-specific schema - capMap := map[string]any{ - "can": vaultCap.Action, - "with": vaultResource.URI, - "actions": vaultCap.Actions, - "vault": vaultCap.VaultAddress, - } - if err := ValidateVaultCapability(capMap); err != nil { - return nil, fmt.Errorf("invalid vault capability: %w", err) - } - - attenuation := Attenuation{ - Capability: vaultCap, - Resource: vaultResource, - } - - // Create token with vault admin capabilities - return builder.CreateOriginToken( - vaultOwnerDID, - []Attenuation{attenuation}, - nil, - time.Now(), - exp, - ) -} - -// ValidateVaultTokenCapability validates a UCAN token for vault operations -func ValidateVaultTokenCapability(token *Token, enclaveDataCID, requiredAction string) error { - expectedResource := fmt.Sprintf("ipfs://%s", enclaveDataCID) - - // Validate the required action parameter - validActions := []string{"read", "write", "sign", "export", "import", "delete"} - actionValid := slices.Contains(validActions, requiredAction) - if !actionValid { - return fmt.Errorf("invalid required action: %s", requiredAction) - } - - // Check if token contains the required vault capability - for _, att := range token.Attenuations { - if att.Resource.GetURI() == expectedResource { - // Check if this is a vault capability - if vaultCap, ok := att.Capability.(*VaultCapability); ok { - // Validate using vault-specific schema - validationMap := map[string]any{ - "can": vaultCap.Action, - "with": att.Resource.GetURI(), - "actions": vaultCap.Actions, - "vault": vaultCap.VaultAddress, - } - - if err := ValidateVaultCapability(validationMap); err != nil { - continue // Skip invalid capabilities - } - - // Check if capability grants the required action - if vaultCap.Grants([]string{requiredAction}) { - return nil - } - } - } - } - - return fmt.Errorf( - "insufficient vault capability: required action '%s' for enclave '%s'", - requiredAction, - enclaveDataCID, - ) -} - -// GetEnclaveDataCID extracts the enclave data CID from vault capabilities -func GetEnclaveDataCID(token *Token) (string, error) { - for _, att := range token.Attenuations { - resource := att.Resource.GetURI() - if strings.HasPrefix(resource, "ipfs://") { - return resource[7:], nil - } - } - return "", fmt.Errorf("no enclave data CID found in token") -} diff --git a/internal/crypto/ucan/verifier.go b/internal/crypto/ucan/verifier.go deleted file mode 100644 index a8a065c..0000000 --- a/internal/crypto/ucan/verifier.go +++ /dev/null @@ -1,984 +0,0 @@ -// Package ucan provides User-Controlled Authorization Networks (UCAN) implementation -// for decentralized authorization and capability delegation in the Sonr network. -// This package handles JWT-based tokens, cryptographic verification, and resource capabilities. -package ucan - -import ( - "context" - "crypto/ed25519" - "crypto/rsa" - "encoding/json" - "fmt" - "strings" - "time" - - "github.com/golang-jwt/jwt/v5" - "github.com/libp2p/go-libp2p/core/crypto" - "github.com/sonr-io/crypto/keys" -) - -// Verifier provides UCAN token verification and validation functionality -type Verifier struct { - didResolver DIDResolver -} - -// DIDResolver resolves DID keys to public keys for signature verification -type DIDResolver interface { - ResolveDIDKey(ctx context.Context, did string) (keys.DID, error) -} - -// NewVerifier creates a new UCAN token verifier -func NewVerifier(didResolver DIDResolver) *Verifier { - return &Verifier{ - didResolver: didResolver, - } -} - -// VerifyToken parses and verifies a UCAN JWT token -func (v *Verifier) VerifyToken(ctx context.Context, tokenString string) (*Token, error) { - if tokenString == "" { - return nil, fmt.Errorf("token string cannot be empty") - } - - // Parse the JWT token - token, err := jwt.Parse(tokenString, v.keyFunc(ctx)) - if err != nil { - return nil, fmt.Errorf("failed to parse JWT token: %w", err) - } - - // Extract claims - claims, ok := token.Claims.(jwt.MapClaims) - if !ok { - return nil, fmt.Errorf("invalid token claims type") - } - - // Parse UCAN-specific fields - ucanToken, err := v.parseUCANClaims(claims, tokenString) - if err != nil { - return nil, fmt.Errorf("failed to parse UCAN claims: %w", err) - } - - // Validate token structure - if err := v.validateToken(ctx, ucanToken); err != nil { - return nil, fmt.Errorf("token validation failed: %w", err) - } - - return ucanToken, nil -} - -// VerifyCapability validates that a UCAN token grants specific capabilities -func (v *Verifier) VerifyCapability( - ctx context.Context, - tokenString string, - resource string, - abilities []string, -) (*Token, error) { - token, err := v.VerifyToken(ctx, tokenString) - if err != nil { - return nil, fmt.Errorf("token verification failed: %w", err) - } - - // Check if token grants required capabilities - if err := v.checkCapabilities(token, resource, abilities); err != nil { - return nil, fmt.Errorf("capability check failed: %w", err) - } - - return token, nil -} - -// VerifyDelegationChain validates the complete delegation chain of a UCAN token -func (v *Verifier) VerifyDelegationChain(ctx context.Context, tokenString string) error { - token, err := v.VerifyToken(ctx, tokenString) - if err != nil { - return fmt.Errorf("failed to verify root token: %w", err) - } - - // Verify each proof in the delegation chain - for i, proof := range token.Proofs { - proofToken, err := v.VerifyToken(ctx, string(proof)) - if err != nil { - return fmt.Errorf("failed to verify proof[%d] in delegation chain: %w", i, err) - } - - // Validate delegation relationship - if err := v.validateDelegation(token, proofToken); err != nil { - return fmt.Errorf("invalid delegation at proof[%d]: %w", i, err) - } - } - - return nil -} - -// keyFunc returns a function that resolves the signing key for JWT verification -func (v *Verifier) keyFunc(ctx context.Context) jwt.Keyfunc { - return func(token *jwt.Token) (any, error) { - // Extract issuer from claims - claims, ok := token.Claims.(jwt.MapClaims) - if !ok { - return nil, fmt.Errorf("invalid claims type") - } - - issuer, ok := claims["iss"].(string) - if !ok { - return nil, fmt.Errorf("missing or invalid issuer claim") - } - - // Resolve the issuer's DID to get public key - did, err := v.didResolver.ResolveDIDKey(ctx, issuer) - if err != nil { - return nil, fmt.Errorf("failed to resolve issuer DID: %w", err) - } - - // Get verification key based on signing method - switch token.Method { - case jwt.SigningMethodRS256, jwt.SigningMethodRS384, jwt.SigningMethodRS512: - return v.getRSAPublicKey(did) - case jwt.SigningMethodEdDSA: - return v.getEd25519PublicKey(did) - default: - return nil, fmt.Errorf("unsupported signing method: %v", token.Method) - } - } -} - -// parseUCANClaims extracts UCAN-specific fields from JWT claims -func (v *Verifier) parseUCANClaims(claims jwt.MapClaims, raw string) (*Token, error) { - issuer, audience := extractStandardClaims(claims) - expiresAt, notBefore := extractTimeClaims(claims) - - attenuations, err := v.parseAttenuationsClaims(claims) - if err != nil { - return nil, err - } - - proofs := parseProofsClaims(claims) - facts := parseFactsClaims(claims) - - return &Token{ - Raw: raw, - Issuer: issuer, - Audience: audience, - ExpiresAt: expiresAt, - NotBefore: notBefore, - Attenuations: attenuations, - Proofs: proofs, - Facts: facts, - }, nil -} - -// extractStandardClaims extracts standard JWT claims (issuer and audience) -func extractStandardClaims(claims jwt.MapClaims) (string, string) { - issuer, _ := claims["iss"].(string) - audience, _ := claims["aud"].(string) - return issuer, audience -} - -// extractTimeClaims extracts time-related claims (exp and nbf) -func extractTimeClaims(claims jwt.MapClaims) (int64, int64) { - var expiresAt, notBefore int64 - - if exp, ok := claims["exp"]; ok { - if expFloat, ok := exp.(float64); ok { - expiresAt = int64(expFloat) - } - } - - if nbf, ok := claims["nbf"]; ok { - if nbfFloat, ok := nbf.(float64); ok { - notBefore = int64(nbfFloat) - } - } - - return expiresAt, notBefore -} - -// parseAttenuationsClaims parses the attenuations from claims -func (v *Verifier) parseAttenuationsClaims(claims jwt.MapClaims) ([]Attenuation, error) { - attClaims, ok := claims["att"] - if !ok { - return nil, nil - } - - attSlice, ok := attClaims.([]any) - if !ok { - return nil, nil - } - - // Pre-allocate slice with known capacity - attenuations := make([]Attenuation, 0, len(attSlice)) - - for _, attItem := range attSlice { - attMap, ok := attItem.(map[string]any) - if !ok { - continue - } - - att, err := v.parseAttenuation(attMap) - if err != nil { - return nil, fmt.Errorf("failed to parse attenuation: %w", err) - } - attenuations = append(attenuations, att) - } - - return attenuations, nil -} - -// parseProofsClaims parses the proofs from claims -func parseProofsClaims(claims jwt.MapClaims) []Proof { - var proofs []Proof - - prfClaims, ok := claims["prf"] - if !ok { - return proofs - } - - prfSlice, ok := prfClaims.([]any) - if !ok { - return proofs - } - - for _, prfItem := range prfSlice { - if prfStr, ok := prfItem.(string); ok { - proofs = append(proofs, Proof(prfStr)) - } - } - - return proofs -} - -// parseFactsClaims parses the facts from claims -func parseFactsClaims(claims jwt.MapClaims) []Fact { - fctClaims, ok := claims["fct"] - if !ok { - return nil - } - - fctSlice, ok := fctClaims.([]any) - if !ok { - return nil - } - - // Pre-allocate slice with known capacity - facts := make([]Fact, 0, len(fctSlice)) - - for _, fctItem := range fctSlice { - factData, _ := json.Marshal(fctItem) - facts = append(facts, Fact{Data: factData}) - } - - return facts -} - -// parseAttenuation converts a map to an Attenuation struct with enhanced module-specific support -func (v *Verifier) parseAttenuation(attMap map[string]any) (Attenuation, error) { - // Extract capability - canValue, ok := attMap["can"] - if !ok { - return Attenuation{}, fmt.Errorf("missing 'can' field in attenuation") - } - - // Extract resource - withValue, ok := attMap["with"] - if !ok { - return Attenuation{}, fmt.Errorf("missing 'with' field in attenuation") - } - - withStr, ok := withValue.(string) - if !ok { - return Attenuation{}, fmt.Errorf("'with' field must be a string") - } - - // Parse resource first to determine module type - resource, err := v.parseResource(withStr) - if err != nil { - return Attenuation{}, fmt.Errorf("failed to parse resource: %w", err) - } - - // Create module-specific capability based on resource scheme - cap, err := v.createModuleSpecificCapability(resource.GetScheme(), canValue, attMap) - if err != nil { - return Attenuation{}, fmt.Errorf("failed to create capability: %w", err) - } - - return Attenuation{ - Capability: cap, - Resource: resource, - }, nil -} - -// createModuleSpecificCapability creates appropriate capability type based on module -func (v *Verifier) createModuleSpecificCapability(scheme string, canValue any, attMap map[string]any) (Capability, error) { - // Extract common fields - caveats := extractStringSliceFromMap(attMap, "caveats") - metadata := extractStringMapFromMap(attMap, "metadata") - - switch scheme { - case "did": - return v.createDIDCapability(canValue, caveats, metadata) - case "dwn": - return v.createDWNCapability(canValue, caveats, metadata) - case "service", "svc": - return v.createServiceCapability(canValue, caveats, metadata) - case "dex", "pool": - return v.createDEXCapability(canValue, caveats, metadata, attMap) - case "ipfs", "vault": - // Handle existing vault capabilities - return v.createVaultCapabilityFromMap(canValue, attMap) - default: - // Fallback to simple/multi capability for unknown schemes - return v.createGenericCapability(canValue) - } -} - -// createDIDCapability creates a DID-specific capability -func (v *Verifier) createDIDCapability(canValue any, caveats []string, metadata map[string]string) (Capability, error) { - switch canVal := canValue.(type) { - case string: - return &DIDCapability{ - Action: canVal, - Caveats: caveats, - Metadata: metadata, - }, nil - case []any: - actions := extractStringSlice(canVal) - return &DIDCapability{ - Actions: actions, - Caveats: caveats, - Metadata: metadata, - }, nil - default: - return nil, fmt.Errorf("unsupported DID capability type") - } -} - -// createDWNCapability creates a DWN-specific capability -func (v *Verifier) createDWNCapability(canValue any, caveats []string, metadata map[string]string) (Capability, error) { - switch canVal := canValue.(type) { - case string: - return &DWNCapability{ - Action: canVal, - Caveats: caveats, - Metadata: metadata, - }, nil - case []any: - actions := extractStringSlice(canVal) - return &DWNCapability{ - Actions: actions, - Caveats: caveats, - Metadata: metadata, - }, nil - default: - return nil, fmt.Errorf("unsupported DWN capability type") - } -} - -// createServiceCapability creates a Service-specific capability -func (v *Verifier) createServiceCapability(canValue any, caveats []string, metadata map[string]string) (Capability, error) { - // Service capabilities can still use MultiCapability for now - switch canVal := canValue.(type) { - case string: - return &MultiCapability{Actions: []string{canVal}}, nil - case []any: - actions := extractStringSlice(canVal) - return &MultiCapability{Actions: actions}, nil - default: - return nil, fmt.Errorf("unsupported Service capability type") - } -} - -// createDEXCapability creates a DEX-specific capability -func (v *Verifier) createDEXCapability(canValue any, caveats []string, metadata map[string]string, attMap map[string]any) (Capability, error) { - maxAmount, _ := attMap["max_amount"].(string) - - switch canVal := canValue.(type) { - case string: - return &DEXCapability{ - Action: canVal, - Caveats: caveats, - MaxAmount: maxAmount, - Metadata: metadata, - }, nil - case []any: - actions := extractStringSlice(canVal) - return &DEXCapability{ - Actions: actions, - Caveats: caveats, - MaxAmount: maxAmount, - Metadata: metadata, - }, nil - default: - return nil, fmt.Errorf("unsupported DEX capability type") - } -} - -// createVaultCapabilityFromMap creates vault capability from existing logic -func (v *Verifier) createVaultCapabilityFromMap(canValue any, attMap map[string]any) (Capability, error) { - // Use existing vault capability creation logic - vaultAddress, _ := attMap["vault"].(string) - caveats := extractStringSliceFromMap(attMap, "caveats") - - switch canVal := canValue.(type) { - case string: - return &VaultCapability{ - Action: canVal, - VaultAddress: vaultAddress, - Caveats: caveats, - }, nil - case []any: - actions := extractStringSlice(canVal) - return &VaultCapability{ - Actions: actions, - VaultAddress: vaultAddress, - Caveats: caveats, - }, nil - default: - return nil, fmt.Errorf("unsupported vault capability type") - } -} - -// createGenericCapability creates fallback capability for unknown schemes -func (v *Verifier) createGenericCapability(canValue any) (Capability, error) { - switch canVal := canValue.(type) { - case string: - return &SimpleCapability{Action: canVal}, nil - case []any: - actions := extractStringSlice(canVal) - return &MultiCapability{Actions: actions}, nil - default: - return nil, fmt.Errorf("unsupported capability type") - } -} - -// Helper functions for extracting data from maps -func extractStringSliceFromMap(m map[string]any, key string) []string { - if value, exists := m[key]; exists { - return extractStringSlice(value) - } - return nil -} - -func extractStringMapFromMap(m map[string]any, key string) map[string]string { - result := make(map[string]string) - if value, exists := m[key]; exists { - if mapValue, ok := value.(map[string]any); ok { - for k, v := range mapValue { - if strValue, ok := v.(string); ok { - result[k] = strValue - } - } - } - } - return result -} - -// parseResource creates a Resource from a URI string -func (v *Verifier) parseResource(uri string) (Resource, error) { - if uri == "" { - return nil, fmt.Errorf("resource URI cannot be empty") - } - - // Parse URI scheme and value - support both "scheme://value" and "scheme:value" formats - var scheme, value string - if strings.Contains(uri, "://") { - parts := strings.SplitN(uri, "://", 2) - if len(parts) == 2 { - scheme = parts[0] - value = parts[1] - } - } else if strings.Contains(uri, ":") { - parts := strings.SplitN(uri, ":", 2) - if len(parts) == 2 { - scheme = parts[0] - value = parts[1] - } - } - - if scheme == "" || value == "" { - return nil, fmt.Errorf("invalid resource URI format: %s", uri) - } - - return &SimpleResource{ - Scheme: scheme, - Value: value, - URI: uri, - }, nil -} - -// validateToken performs structural and temporal validation -func (v *Verifier) validateToken(_ context.Context, token *Token) error { - // Check required fields - if token.Issuer == "" { - return fmt.Errorf("issuer is required") - } - if token.Audience == "" { - return fmt.Errorf("audience is required") - } - if len(token.Attenuations) == 0 { - return fmt.Errorf("at least one attenuation is required") - } - - // Check temporal validity - now := time.Now().Unix() - - if token.NotBefore > 0 && now < token.NotBefore { - return fmt.Errorf("token is not yet valid (nbf: %d, now: %d)", token.NotBefore, now) - } - - if token.ExpiresAt > 0 && now >= token.ExpiresAt { - return fmt.Errorf("token has expired (exp: %d, now: %d)", token.ExpiresAt, now) - } - - return nil -} - -// checkCapabilities verifies that the token grants the required capabilities with enhanced module-specific validation -func (v *Verifier) checkCapabilities(token *Token, resource string, abilities []string) error { - for _, att := range token.Attenuations { - if att.Resource.GetURI() == resource { - if att.Capability.Grants(abilities) { - // Validate caveats for module-specific capabilities - if err := v.validateCaveats(att.Capability, att.Resource); err != nil { - return fmt.Errorf("caveat validation failed: %w", err) - } - return nil - } - } - } - return fmt.Errorf("required capabilities not granted for resource %s", resource) -} - -// validateCaveats validates constraints (caveats) for module-specific capabilities -func (v *Verifier) validateCaveats(cap Capability, resource Resource) error { - scheme := resource.GetScheme() - - switch scheme { - case "did": - return v.validateDIDCaveats(cap, resource) - case "dwn": - return v.validateDWNCaveats(cap, resource) - case "dex", "pool": - return v.validateDEXCaveats(cap, resource) - case "service", "svc": - return v.validateServiceCaveats(cap, resource) - case "vault", "ipfs": - return v.validateVaultCaveats(cap, resource) - default: - return nil // No caveat validation for unknown schemes - } -} - -// validateDIDCaveats validates DID-specific constraints -func (v *Verifier) validateDIDCaveats(cap Capability, resource Resource) error { - didCap, ok := cap.(*DIDCapability) - if !ok { - return nil // Not a DID capability - } - - for _, caveat := range didCap.Caveats { - switch caveat { - case "owner": - // Validate that the capability is for the owner's DID - if err := v.validateOwnerCaveat(resource); err != nil { - return fmt.Errorf("owner caveat validation failed: %w", err) - } - case "controller": - // Validate controller permissions - if err := v.validateControllerCaveat(resource); err != nil { - return fmt.Errorf("controller caveat validation failed: %w", err) - } - } - } - return nil -} - -// validateDWNCaveats validates DWN-specific constraints -func (v *Verifier) validateDWNCaveats(cap Capability, resource Resource) error { - dwnCap, ok := cap.(*DWNCapability) - if !ok { - return nil // Not a DWN capability - } - - for _, caveat := range dwnCap.Caveats { - switch caveat { - case "owner": - // Validate record ownership - if err := v.validateRecordOwnership(resource); err != nil { - return fmt.Errorf("record ownership validation failed: %w", err) - } - case "protocol": - // Validate protocol compliance - if err := v.validateProtocolCaveat(resource); err != nil { - return fmt.Errorf("protocol caveat validation failed: %w", err) - } - } - } - return nil -} - -// validateDEXCaveats validates DEX-specific constraints -func (v *Verifier) validateDEXCaveats(cap Capability, resource Resource) error { - dexCap, ok := cap.(*DEXCapability) - if !ok { - return nil // Not a DEX capability - } - - for _, caveat := range dexCap.Caveats { - switch caveat { - case "max-amount": - // Validate maximum swap amount - if dexCap.MaxAmount != "" { - if err := v.validateMaxAmountCaveat(dexCap.MaxAmount); err != nil { - return fmt.Errorf("max amount caveat validation failed: %w", err) - } - } - case "pool-member": - // Validate pool membership - if err := v.validatePoolMembershipCaveat(resource); err != nil { - return fmt.Errorf("pool membership validation failed: %w", err) - } - } - } - return nil -} - -// validateServiceCaveats validates Service-specific constraints -func (v *Verifier) validateServiceCaveats(cap Capability, resource Resource) error { - // Service capabilities use MultiCapability for now - // Add service-specific caveat validation if needed - return nil -} - -// validateVaultCaveats validates Vault-specific constraints -func (v *Verifier) validateVaultCaveats(cap Capability, resource Resource) error { - vaultCap, ok := cap.(*VaultCapability) - if !ok { - return nil // Not a vault capability - } - - for _, caveat := range vaultCap.Caveats { - switch caveat { - case "vault-owner": - // Validate vault ownership - if err := v.validateVaultOwnership(vaultCap.VaultAddress); err != nil { - return fmt.Errorf("vault ownership validation failed: %w", err) - } - case "enclave-integrity": - // Validate enclave data integrity - if err := v.validateEnclaveIntegrity(vaultCap.EnclaveDataCID); err != nil { - return fmt.Errorf("enclave integrity validation failed: %w", err) - } - } - } - return nil -} - -// Caveat validation helper methods (placeholders for actual implementation) - -// validateOwnerCaveat validates DID ownership constraint -func (v *Verifier) validateOwnerCaveat(resource Resource) error { - // Placeholder: Implement actual DID ownership validation - return nil -} - -// validateControllerCaveat validates DID controller constraint -func (v *Verifier) validateControllerCaveat(resource Resource) error { - // Placeholder: Implement actual controller validation - return nil -} - -// validateRecordOwnership validates DWN record ownership -func (v *Verifier) validateRecordOwnership(resource Resource) error { - // Placeholder: Implement actual record ownership validation - return nil -} - -// validateProtocolCaveat validates DWN protocol constraint -func (v *Verifier) validateProtocolCaveat(resource Resource) error { - // Placeholder: Implement actual protocol validation - return nil -} - -// validateMaxAmountCaveat validates DEX maximum amount constraint -func (v *Verifier) validateMaxAmountCaveat(maxAmount string) error { - // Placeholder: Implement actual amount validation - return nil -} - -// validatePoolMembershipCaveat validates DEX pool membership -func (v *Verifier) validatePoolMembershipCaveat(resource Resource) error { - // Placeholder: Implement actual pool membership validation - return nil -} - -// validateVaultOwnership validates vault ownership -func (v *Verifier) validateVaultOwnership(vaultAddress string) error { - // Placeholder: Implement actual vault ownership validation - return nil -} - -// validateEnclaveIntegrity validates enclave data integrity -func (v *Verifier) validateEnclaveIntegrity(enclaveDataCID string) error { - // Placeholder: Implement actual enclave integrity validation - return nil -} - -// validateDelegation checks that child token is properly attenuated from parent with enhanced module-specific validation -func (v *Verifier) validateDelegation(child, parent *Token) error { - // Child's issuer must be parent's audience - if child.Issuer != parent.Audience { - return fmt.Errorf("delegation chain broken: child issuer must be parent audience") - } - - // Child capabilities must be subset of parent with module-specific validation - for _, childAtt := range child.Attenuations { - if !v.isModuleCapabilitySubset(childAtt, parent.Attenuations) { - return fmt.Errorf("child capability exceeds parent capabilities") - } - } - - // Child expiration must not exceed parent - if parent.ExpiresAt > 0 && (child.ExpiresAt == 0 || child.ExpiresAt > parent.ExpiresAt) { - return fmt.Errorf("child token expires after parent token") - } - - // Validate cross-module delegation constraints - if err := v.validateCrossModuleDelegation(child, parent); err != nil { - return fmt.Errorf("cross-module delegation validation failed: %w", err) - } - - return nil -} - -// isModuleCapabilitySubset checks if a capability is a subset with module-specific logic -func (v *Verifier) isModuleCapabilitySubset(childAtt Attenuation, parentAtts []Attenuation) bool { - for _, parentAtt := range parentAtts { - if childAtt.Resource.GetURI() == parentAtt.Resource.GetURI() { - if v.isModuleCapabilityContained(childAtt.Capability, parentAtt.Capability, childAtt.Resource.GetScheme()) { - return true - } - } - } - return false -} - -// isModuleCapabilityContained checks containment with module-specific logic -func (v *Verifier) isModuleCapabilityContained(child, parent Capability, scheme string) bool { - // First check basic containment - if parent.Contains(child) { - // Additional module-specific containment validation - switch scheme { - case "did": - return v.validateDIDContainment(child, parent) - case "dwn": - return v.validateDWNContainment(child, parent) - case "dex", "pool": - return v.validateDEXContainment(child, parent) - case "vault", "ipfs": - return v.validateVaultContainment(child, parent) - default: - return true // Basic containment is sufficient for unknown schemes - } - } - return false -} - -// validateCrossModuleDelegation validates constraints across different modules -func (v *Verifier) validateCrossModuleDelegation(child, parent *Token) error { - childModules := v.extractModulesFromToken(child) - parentModules := v.extractModulesFromToken(parent) - - // Check if child uses modules not present in parent - for module := range childModules { - if _, exists := parentModules[module]; !exists { - return fmt.Errorf("child token uses module '%s' not delegated by parent", module) - } - } - - // Validate specific cross-module constraints - return v.validateSpecificCrossModuleConstraints(child, parent) -} - -// extractModulesFromToken extracts the modules used by a token -func (v *Verifier) extractModulesFromToken(token *Token) map[string]bool { - modules := make(map[string]bool) - for _, att := range token.Attenuations { - scheme := att.Resource.GetScheme() - modules[scheme] = true - } - return modules -} - -// validateSpecificCrossModuleConstraints validates specific cross-module business logic -func (v *Verifier) validateSpecificCrossModuleConstraints(child, parent *Token) error { - // Example: If DID operations require vault access, ensure both are present - childHasDID := v.tokenHasModule(child, "did") - childHasVault := v.tokenHasModule(child, "vault") || v.tokenHasModule(child, "ipfs") - - if childHasDID && !childHasVault { - // Check if parent has vault capability that can be inherited - parentHasVault := v.tokenHasModule(parent, "vault") || v.tokenHasModule(parent, "ipfs") - if !parentHasVault { - return fmt.Errorf("DID operations require vault access which is not available in delegation chain") - } - } - - // Add more cross-module constraints as needed - return nil -} - -// tokenHasModule checks if a token has capabilities for a specific module -func (v *Verifier) tokenHasModule(token *Token, module string) bool { - for _, att := range token.Attenuations { - if att.Resource.GetScheme() == module { - return true - } - } - return false -} - -// Module-specific containment validation methods - -// validateDIDContainment validates DID capability containment -func (v *Verifier) validateDIDContainment(child, parent Capability) bool { - childDID, childOk := child.(*DIDCapability) - parentDID, parentOk := parent.(*DIDCapability) - - if !childOk || !parentOk { - return true // Not both DID capabilities, basic containment applies - } - - // Validate that child caveats are more restrictive or equal - return v.areCaveatsMoreRestrictive(childDID.Caveats, parentDID.Caveats) -} - -// validateDWNContainment validates DWN capability containment -func (v *Verifier) validateDWNContainment(child, parent Capability) bool { - childDWN, childOk := child.(*DWNCapability) - parentDWN, parentOk := parent.(*DWNCapability) - - if !childOk || !parentOk { - return true // Not both DWN capabilities, basic containment applies - } - - // Validate that child caveats are more restrictive or equal - return v.areCaveatsMoreRestrictive(childDWN.Caveats, parentDWN.Caveats) -} - -// validateDEXContainment validates DEX capability containment -func (v *Verifier) validateDEXContainment(child, parent Capability) bool { - childDEX, childOk := child.(*DEXCapability) - parentDEX, parentOk := parent.(*DEXCapability) - - if !childOk || !parentOk { - return true // Not both DEX capabilities, basic containment applies - } - - // Validate max amount restriction - if parentDEX.MaxAmount != "" && childDEX.MaxAmount != "" { - // Child max amount should be less than or equal to parent - if !v.isAmountLessOrEqual(childDEX.MaxAmount, parentDEX.MaxAmount) { - return false - } - } else if parentDEX.MaxAmount != "" && childDEX.MaxAmount == "" { - // Child must have max amount if parent does - return false - } - - // Validate that child caveats are more restrictive or equal - return v.areCaveatsMoreRestrictive(childDEX.Caveats, parentDEX.Caveats) -} - -// validateVaultContainment validates Vault capability containment -func (v *Verifier) validateVaultContainment(child, parent Capability) bool { - childVault, childOk := child.(*VaultCapability) - parentVault, parentOk := parent.(*VaultCapability) - - if !childOk || !parentOk { - return true // Not both Vault capabilities, basic containment applies - } - - // Vault address must match - if childVault.VaultAddress != parentVault.VaultAddress { - return false - } - - // Validate that child caveats are more restrictive or equal - return v.areCaveatsMoreRestrictive(childVault.Caveats, parentVault.Caveats) -} - -// Helper methods for containment validation - -// areCaveatsMoreRestrictive checks if child caveats are more restrictive than parent -func (v *Verifier) areCaveatsMoreRestrictive(childCaveats, parentCaveats []string) bool { - parentCaveatSet := make(map[string]bool) - for _, caveat := range parentCaveats { - parentCaveatSet[caveat] = true - } - - // All child caveats must be present in parent caveats (or child can have additional restrictions) - for _, childCaveat := range childCaveats { - if !parentCaveatSet[childCaveat] { - // Child has additional restrictions, which is allowed - continue - } - } - - return true -} - -// isAmountLessOrEqual compares two amount strings (placeholder implementation) -func (v *Verifier) isAmountLessOrEqual(childAmount, parentAmount string) bool { - // Placeholder: Implement actual amount comparison - // This would parse the amounts and compare them numerically - return true -} - -// isCapabilitySubset checks if a capability is a subset of any parent capabilities -func (v *Verifier) isCapabilitySubset(childAtt Attenuation, parentAtts []Attenuation) bool { - for _, parentAtt := range parentAtts { - if childAtt.Resource.GetURI() == parentAtt.Resource.GetURI() { - if parentAtt.Capability.Contains(childAtt.Capability) { - return true - } - } - } - return false -} - -// getRSAPublicKey extracts RSA public key from DID -func (v *Verifier) getRSAPublicKey(did keys.DID) (*rsa.PublicKey, error) { - verifyKey, err := did.VerifyKey() - if err != nil { - return nil, fmt.Errorf("failed to get verify key: %w", err) - } - - rsaKey, ok := verifyKey.(*rsa.PublicKey) - if !ok { - return nil, fmt.Errorf("DID does not contain RSA public key") - } - - return rsaKey, nil -} - -// getEd25519PublicKey extracts Ed25519 public key from DID -func (v *Verifier) getEd25519PublicKey(did keys.DID) (ed25519.PublicKey, error) { - pubKey := did.PublicKey() - rawBytes, err := pubKey.Raw() - if err != nil { - return nil, fmt.Errorf("failed to get raw public key: %w", err) - } - - if pubKey.Type() != crypto.Ed25519 { - return nil, fmt.Errorf("DID does not contain Ed25519 public key") - } - - return ed25519.PublicKey(rawBytes), nil -} - -// StringDIDResolver implements DIDResolver for did:key strings -type StringDIDResolver struct{} - -// ResolveDIDKey extracts a public key from a did:key string -func (StringDIDResolver) ResolveDIDKey(ctx context.Context, didStr string) (keys.DID, error) { - return keys.Parse(didStr) -}