diff --git a/go.mod b/go.mod index 19a71aa..5776412 100644 --- a/go.mod +++ b/go.mod @@ -8,24 +8,27 @@ require ( github.com/gobwas/glob v0.2.3 github.com/ipfs/go-cid v0.4.1 github.com/ipld/go-ipld-prime v0.21.0 - github.com/multiformats/go-multibase v0.0.3 - github.com/multiformats/go-varint v0.0.6 + github.com/libp2p/go-libp2p v0.36.2 + github.com/multiformats/go-multibase v0.2.0 + github.com/multiformats/go-multicodec v0.9.0 + github.com/multiformats/go-varint v0.0.7 github.com/stretchr/testify v1.9.0 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect - github.com/minio/sha256-simd v1.0.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.8 // indirect + github.com/minio/sha256-simd v1.0.1 // indirect github.com/mr-tron/base58 v1.2.0 // indirect - github.com/multiformats/go-base32 v0.0.3 // indirect - github.com/multiformats/go-base36 v0.1.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.2.0 // indirect github.com/multiformats/go-multihash v0.2.3 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/polydawn/refmt v0.89.0 // indirect github.com/spaolacci/murmur3 v1.1.0 // indirect - golang.org/x/crypto v0.1.0 // indirect - golang.org/x/sys v0.1.0 // indirect + golang.org/x/crypto v0.25.0 // indirect + golang.org/x/sys v0.22.0 // indirect + google.golang.org/protobuf v1.34.2 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - lukechampine.com/blake3 v1.1.6 // indirect + lukechampine.com/blake3 v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index abe7c04..2b9f555 100644 --- a/go.sum +++ b/go.sum @@ -17,30 +17,30 @@ github.com/ipld/go-ipld-prime v0.21.0 h1:n4JmcpOlPDIxBcY037SVfpd1G+Sj1nKZah0m6QH 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.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.8 h1:+StwCXwm9PdpiEkPyzBXIy+M9KUb4ODm0Zarf1kS5BM= +github.com/klauspost/cpuid/v2 v2.2.8/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mr-tron/base58 v1.1.0/go.mod h1:xcD2VGqlgYjBdcBLw+TuYLr8afG+Hj8g2eTVqeSzSU8= +github.com/libp2p/go-libp2p v0.36.2 h1:BbqRkDaGC3/5xfaJakLV/BrpjlAuYqSB0lRvtzL3B/U= +github.com/libp2p/go-libp2p v0.36.2/go.mod h1:XO3joasRE4Eup8yCTTP/+kX+g92mOgRaadk46LmPhHY= +github.com/minio/sha256-simd v1.0.1 h1:6kaan5IFmwTNynnKKpDHe6FWHohJOHhCPchzK49dzMM= +github.com/minio/sha256-simd v1.0.1/go.mod h1:Pz6AKMiUdngCLpeTL/RJY1M9rUuPMYujV5xJjtbRSN8= github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= -github.com/multiformats/go-base32 v0.0.3 h1:tw5+NhuwaOjJCC5Pp82QuXbrmLzWg7uxlMFp8Nq/kkI= -github.com/multiformats/go-base32 v0.0.3/go.mod h1:pLiuGC8y0QR3Ue4Zug5UzK9LjgbkL8NSQj0zQ5Nz/AA= -github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= -github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= -github.com/multiformats/go-multibase v0.0.3 h1:l/B6bJDQjvQ5G52jw4QGSYeOTZoAwIO77RblWplfIqk= -github.com/multiformats/go-multibase v0.0.3/go.mod h1:5+1R4eQrT3PkYZ24C3W2Ue2tPwIdYQD509ZjSb5y9Oc= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +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-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.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.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= -github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.7 h1:sWSGR+f/eu5ABZA2ZpYKBILXTTs9JWpdEM/nEGOHFS8= +github.com/multiformats/go-varint v0.0.7/go.mod h1:r8PUYw/fD/SjBCiKOoDlGF6QawOELpZAu9eioSos/OU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polydawn/refmt v0.89.0 h1:ADJTApkvkeBZsN0tBTx8QjpD9JkmxbKp0cxfr9qszm4= @@ -61,14 +61,17 @@ github.com/urfave/cli v1.22.10/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60Nt 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.1.0 h1:MDRAIl0xIo9Io2xV565hzXHw3zVseKrJKodhohM5CjU= -golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw= +golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30= +golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M= 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.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= +google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= @@ -76,5 +79,5 @@ 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.1.6 h1:H3cROdztr7RCfoaTpGZFQsrqvweFLrqS73j7L7cmR5c= -lukechampine.com/blake3 v1.1.6/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +lukechampine.com/blake3 v1.3.0 h1:sJ3XhFINmHSrYCgl958hscfIa3bw8x4DqMP3u1YvoYE= +lukechampine.com/blake3 v1.3.0/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k= diff --git a/internal/varsig/varsig.go b/internal/varsig/varsig.go new file mode 100644 index 0000000..5c1fa5b --- /dev/null +++ b/internal/varsig/varsig.go @@ -0,0 +1,134 @@ +// Package varsig implements the portion of the [varsig specification] +// that's needed for the UCAN [Envelope]. +// +// While the [Envelope] specification has a field that's labelled +// "VarsigHeader", this field is actually the prefix, header and segments +// of the body excluding the signature itself (which is a different field +// in the [Envelope]). +// +// Given that [go-ucan] supports a limited number of public key types, +// and that the signature isn't part of the resulting field, the values +// that are used are constants. Note that for key types that are fully +// specified in the [did:key], the [VarsigHeader] field isn't technically +// needed and could theoretically conflict with the DID. +// +// Treating these values as constants has no impact when issuing or +// delegating tokens. When decoding tokens, simply matching the strings +// will allow us to detect errors but won't provide as much detail (e.g. +// we can't indicate that the signature was incorrectly generated from +// a DAG-JSON encoding.) +// +// [varsig specification]: https://github.com/ChainAgnostic/varsig +// [Envelope]:https://github.com/ucan-wg/spec#envelope +// [go-ucan]: https://github.com/ucan-wg/go-ucan +package varsig + +import ( + "encoding/base64" + "encoding/binary" + "errors" + "fmt" + + "github.com/libp2p/go-libp2p/core/crypto/pb" + "github.com/multiformats/go-multicodec" +) + +const ( + Prefix = 0x34 +) + +// ErrUnknownHeader is returned when it's not possible to decode the +// provided string into a libp2p public key type. +var ErrUnknownHeader = errors.New("could not decode unknown header") + +// ErrUnknownKeyType is returned when value provided is not a valid +// libp2p public key type. +var ErrUnknownKeyType = errors.New("could not encode unsupported key type") + +var ( + decMap = headerToKeyType() + encMap = keyTypeToHeader() +) + +// Decode returns either the pb.KeyType associated with the provided Header +// or an error. +// +// Currently, only the four key types supported by the [go-libp2p/core/crypto] +// library are supported. +// +// [go-libp2p/core/crypto]: github.com/libp2p/go-libp2p/core/crypto +func Decode(header string) (pb.KeyType, error) { + keyType, ok := decMap[header] + if !ok { + return -1, fmt.Errorf("%w: %s", ErrUnknownHeader, header) + } + + return keyType, nil +} + +// Encode returns either the header associated with the provided pb.KeyType +// or an error indicating the header was unknown. +// +// Currently, only the four key types supported by the [go-libp2p/core/crypto] +// library are supported. +// +// [go-libp2p/core/crypto]: github.com/libp2p/go-libp2p/core/crypto +func Encode(keyType pb.KeyType) (string, error) { + header, ok := encMap[keyType] + if !ok { + return "", fmt.Errorf("%w: %s", ErrUnknownKeyType, keyType.String()) + } + + return header, nil +} + +func keyTypeToHeader() map[pb.KeyType]string { + const rsaSigLen = 0x100 + + return map[pb.KeyType]string{ + pb.KeyType_RSA: header( + Prefix, + multicodec.RsaPub, + multicodec.Sha2_256, + rsaSigLen, + multicodec.DagCbor, + ), + pb.KeyType_Ed25519: header( + Prefix, + multicodec.Ed25519Pub, + multicodec.DagCbor, + ), + pb.KeyType_Secp256k1: header( + Prefix, + multicodec.Secp256k1Pub, + multicodec.Sha2_256, + multicodec.DagCbor, + ), + pb.KeyType_ECDSA: header( + Prefix, + multicodec.Es256, + multicodec.Sha2_256, + multicodec.DagCbor, + ), + } +} + +func headerToKeyType() map[string]pb.KeyType { + out := make(map[string]pb.KeyType, len(encMap)) + + for keyType, header := range encMap { + out[header] = keyType + } + + return out +} + +func header(vals ...multicodec.Code) string { + var buf []byte + + for _, val := range vals { + buf = binary.AppendUvarint(buf, uint64(val)) + } + + return base64.RawStdEncoding.EncodeToString(buf) +} diff --git a/internal/varsig/varsig_test.go b/internal/varsig/varsig_test.go new file mode 100644 index 0000000..4b8277b --- /dev/null +++ b/internal/varsig/varsig_test.go @@ -0,0 +1,43 @@ +package varsig_test + +import ( + "encoding/base64" + "fmt" + "testing" + + "github.com/libp2p/go-libp2p/core/crypto/pb" + "github.com/stretchr/testify/assert" + "github.com/ucan-wg/go-ucan/v1/internal/varsig" +) + +func TestDecode(t *testing.T) { + t.Parallel() + + notAHeader := base64.RawStdEncoding.EncodeToString([]byte("not a header")) + keyType, err := varsig.Decode(notAHeader) + assert.Equal(t, pb.KeyType(-1), keyType) + assert.ErrorIs(t, err, varsig.ErrUnknownHeader) +} + +func ExampleDecode() { + keyType, _ := varsig.Decode("NIUkEoACcQ") + fmt.Println(keyType.String()) + // Output: + // RSA +} + +func TestEncode(t *testing.T) { + t.Parallel() + + header, err := varsig.Encode(pb.KeyType(99)) + assert.Empty(t, header) + assert.ErrorIs(t, err, varsig.ErrUnknownKeyType) +} + +func ExampleEncode() { + header, _ := varsig.Encode(pb.KeyType_RSA) + fmt.Println(header) + + // Output: + // NIUkEoACcQ +}