diff --git a/data/types.go b/data/types.go index ebf7e5ca..087a17e8 100644 --- a/data/types.go +++ b/data/types.go @@ -1,6 +1,7 @@ package data import ( + "bytes" "crypto/sha256" "encoding/hex" "encoding/json" @@ -287,7 +288,11 @@ func (d *DelegatedRole) MarshalJSON() ([]byte, error) { func (d *DelegatedRole) UnmarshalJSON(b []byte) error { type delegatedRoleAlias DelegatedRole - if err := json.Unmarshal(b, (*delegatedRoleAlias)(d)); err != nil { + // Prepare decoder + dec := json.NewDecoder(bytes.NewReader(b)) + + // Unmarshal delegated role + if err := dec.Decode((*delegatedRoleAlias)(d)); err != nil { return err } diff --git a/go.mod b/go.mod index 46f535b4..c90f0349 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.18 require ( github.com/dustin/go-humanize v1.0.0 github.com/flynn/go-docopt v0.0.0-20140912013429-f6dd2ebbb31e + github.com/google/gofuzz v1.2.0 github.com/secure-systems-lab/go-securesystemslib v0.4.0 github.com/stretchr/testify v1.8.0 github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7 @@ -19,7 +20,6 @@ require ( github.com/kr/pretty v0.2.1 // indirect github.com/kr/text v0.1.0 // indirect github.com/onsi/ginkgo v1.16.4 // indirect - github.com/onsi/gomega v1.18.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd // indirect golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e // indirect diff --git a/go.sum b/go.sum index eb7d6464..39c3f224 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,3 @@ -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= @@ -19,17 +16,14 @@ github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrU github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= @@ -43,12 +37,9 @@ github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108 github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1 h1:o0+MgICZLuZ7xjH7Vx6zS/zcu93/BEp1VwkIW1mEXCE= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= -github.com/onsi/gomega v1.18.1 h1:M1GfJqGRrBrrGGsbxzV5dqM2U2ApXefZCQpkukxYRLE= -github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= 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/secure-systems-lab/go-securesystemslib v0.4.0 h1:b23VGrQhTA8cN2CbBw7/FulN9fTtqYUdS5+Oxzt+DUE= @@ -74,7 +65,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200813134508-3edf25e44fcc/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk= golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -86,23 +76,18 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200814200057-3d37ad5750ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e h1:fLOSk5Q00efkSvAm+4xcoXD+RRmLmmulPn5I3Y9F2EM= golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -110,6 +95,7 @@ golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4f golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -117,8 +103,6 @@ google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQ google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 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= @@ -127,9 +111,8 @@ gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkep gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/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.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/keys/ecdsa.go b/pkg/keys/ecdsa.go index 1471235b..22ed12b0 100644 --- a/pkg/keys/ecdsa.go +++ b/pkg/keys/ecdsa.go @@ -1,12 +1,15 @@ package keys import ( + "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/sha256" "encoding/asn1" "encoding/json" "errors" + "fmt" + "io" "math/big" "github.com/theupdateframework/go-tuf/data" @@ -59,13 +62,25 @@ func (p *p256Verifier) MarshalPublicKey() *data.PublicKey { } func (p *p256Verifier) UnmarshalPublicKey(key *data.PublicKey) error { - if err := json.Unmarshal(key.Value, p); err != nil { + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(key.Value), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(p); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the public key is truncated or too large: %w", err) + } return err } - x, _ := elliptic.Unmarshal(elliptic.P256(), p.PublicKey) + + curve := elliptic.P256() + + // Parse as uncompressed marshalled point. + x, _ := elliptic.Unmarshal(curve, p.PublicKey) if x == nil { return errors.New("tuf: invalid ecdsa public key point") } + p.key = key return nil } diff --git a/pkg/keys/ecdsa_test.go b/pkg/keys/ecdsa_test.go new file mode 100644 index 00000000..a141d582 --- /dev/null +++ b/pkg/keys/ecdsa_test.go @@ -0,0 +1,92 @@ +package keys + +import ( + "crypto/ecdsa" + "crypto/elliptic" + "crypto/rand" + "encoding/hex" + "encoding/json" + "errors" + "io" + "strings" + + fuzz "github.com/google/gofuzz" + "github.com/theupdateframework/go-tuf/data" + . "gopkg.in/check.v1" +) + +type ECDSASuite struct{} + +var _ = Suite(&ECDSASuite{}) + +func (ECDSASuite) TestUnmarshalECDSA(c *C) { + priv, err := ecdsa.GenerateKey(elliptic.P256(), strings.NewReader("00001-deterministic-buffer-for-key-generation")) + c.Assert(err, IsNil) + + // Marshall as non compressed point + pub := elliptic.Marshal(elliptic.P256(), priv.X, priv.Y) + + publicKey, err := json.Marshal(map[string]string{ + "public": hex.EncodeToString(pub), + }) + c.Assert(err, IsNil) + + badKey := &data.PublicKey{ + Type: data.KeyTypeECDSA_SHA2_P256, + Scheme: data.KeySchemeECDSA_SHA2_P256, + Algorithms: data.HashAlgorithms, + Value: publicKey, + } + verifier := NewEcdsaVerifier() + c.Assert(verifier.UnmarshalPublicKey(badKey), IsNil) +} + +func (ECDSASuite) TestUnmarshalECDSA_Invalid(c *C) { + badKeyValue, err := json.Marshal(true) + c.Assert(err, IsNil) + + badKey := &data.PublicKey{ + Type: data.KeyTypeECDSA_SHA2_P256, + Scheme: data.KeySchemeECDSA_SHA2_P256, + Algorithms: data.HashAlgorithms, + Value: badKeyValue, + } + verifier := NewEcdsaVerifier() + c.Assert(verifier.UnmarshalPublicKey(badKey), ErrorMatches, "json: cannot unmarshal.*") +} + +func (ECDSASuite) TestUnmarshalECDSA_FastFuzz(c *C) { + verifier := NewEcdsaVerifier() + for i := 0; i < 50; i++ { + // Ensure no basic panic + + f := fuzz.New() + var publicData data.PublicKey + f.Fuzz(&publicData) + + verifier.UnmarshalPublicKey(&publicData) + } +} + +func (ECDSASuite) TestUnmarshalECDSA_TooLongContent(c *C) { + randomSeed := make([]byte, MaxJSONKeySize) + _, err := io.ReadFull(rand.Reader, randomSeed) + c.Assert(err, IsNil) + + tooLongPayload, err := json.Marshal( + &ed25519Verifier{ + PublicKey: data.HexBytes(hex.EncodeToString(randomSeed)), + }, + ) + c.Assert(err, IsNil) + + badKey := &data.PublicKey{ + Type: data.KeyTypeECDSA_SHA2_P256, + Scheme: data.KeySchemeECDSA_SHA2_P256, + Algorithms: data.HashAlgorithms, + Value: tooLongPayload, + } + verifier := NewEcdsaVerifier() + err = verifier.UnmarshalPublicKey(badKey) + c.Assert(errors.Is(err, io.ErrUnexpectedEOF), Equals, true) +} diff --git a/pkg/keys/ed25519.go b/pkg/keys/ed25519.go index 88f6f864..82178f5d 100644 --- a/pkg/keys/ed25519.go +++ b/pkg/keys/ed25519.go @@ -1,25 +1,29 @@ package keys import ( + "bytes" "crypto" "crypto/ed25519" "crypto/rand" + "crypto/subtle" "encoding/json" "errors" + "fmt" + "io" "github.com/theupdateframework/go-tuf/data" ) func init() { - SignerMap.Store(data.KeySchemeEd25519, NewP256Signer) - VerifierMap.Store(data.KeySchemeEd25519, NewP256Verifier) + SignerMap.Store(data.KeySchemeEd25519, NewEd25519Signer) + VerifierMap.Store(data.KeySchemeEd25519, NewEd25519Verifier) } -func NewP256Signer() Signer { +func NewEd25519Signer() Signer { return &ed25519Signer{} } -func NewP256Verifier() Verifier { +func NewEd25519Verifier() Verifier { return &ed25519Verifier{} } @@ -45,11 +49,19 @@ func (e *ed25519Verifier) MarshalPublicKey() *data.PublicKey { func (e *ed25519Verifier) UnmarshalPublicKey(key *data.PublicKey) error { e.key = key - if err := json.Unmarshal(key.Value, e); err != nil { + + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(key.Value), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(e); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the public key is truncated or too large: %w", err) + } return err } - if len(e.PublicKey) != ed25519.PublicKeySize { - return errors.New("tuf: unexpected public key length for ed25519 key") + if n := len(e.PublicKey); n != ed25519.PublicKeySize { + return fmt.Errorf("tuf: unexpected public key length for ed25519 key, expected %d, got %d", ed25519.PublicKeySize, n) } return nil } @@ -83,7 +95,7 @@ func GenerateEd25519Key() (*ed25519Signer, error) { }, nil } -func NewEd25519Signer(keyValue Ed25519PrivateKeyValue) *ed25519Signer { +func NewEd25519SignerFromKey(keyValue Ed25519PrivateKeyValue) *ed25519Signer { return &ed25519Signer{ PrivateKey: ed25519.PrivateKey(data.HexBytes(keyValue.Private)), keyType: data.KeyTypeEd25519, @@ -114,9 +126,34 @@ func (e *ed25519Signer) MarshalPrivateKey() (*data.PrivateKey, error) { func (e *ed25519Signer) UnmarshalPrivateKey(key *data.PrivateKey) error { keyValue := &Ed25519PrivateKeyValue{} - if err := json.Unmarshal(key.Value, keyValue); err != nil { - return err + + // Prepare decoder limited to 512Kb + dec := json.NewDecoder(io.LimitReader(bytes.NewReader(key.Value), MaxJSONKeySize)) + + // Unmarshal key value + if err := dec.Decode(keyValue); err != nil { + if errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) { + return fmt.Errorf("tuf: the private key is truncated or too large: %w", err) + } + } + + // Check private key length + if n := len(keyValue.Private); n != ed25519.PrivateKeySize { + return fmt.Errorf("tuf: invalid ed25519 private key length, expected %d, got %d", ed25519.PrivateKeySize, n) } + + // Generate public key from private key + pub, _, err := ed25519.GenerateKey(bytes.NewReader(keyValue.Private)) + if err != nil { + return fmt.Errorf("tuf: unable to derive public key from private key: %w", err) + } + + // Compare keys + if subtle.ConstantTimeCompare(keyValue.Public, pub) != 1 { + return errors.New("tuf: public and private keys don't match") + } + + // Prepare signer *e = ed25519Signer{ PrivateKey: ed25519.PrivateKey(data.HexBytes(keyValue.Private)), keyType: key.Type, diff --git a/pkg/keys/ed25519_test.go b/pkg/keys/ed25519_test.go index 657b16ce..a17cc147 100644 --- a/pkg/keys/ed25519_test.go +++ b/pkg/keys/ed25519_test.go @@ -1,8 +1,15 @@ package keys import ( + "crypto/ed25519" + "crypto/rand" + "encoding/hex" "encoding/json" + "errors" + "io" + "strings" + fuzz "github.com/google/gofuzz" "github.com/theupdateframework/go-tuf/data" . "gopkg.in/check.v1" ) @@ -12,17 +19,73 @@ type Ed25519Suite struct{} var _ = Suite(&Ed25519Suite{}) func (Ed25519Suite) TestUnmarshalEd25519(c *C) { - badKeyValue, _ := json.Marshal(true) + pub, _, err := ed25519.GenerateKey(strings.NewReader("00001-deterministic-buffer-for-key-generation")) + c.Assert(err, IsNil) + + publicKey, err := json.Marshal(map[string]string{ + "public": hex.EncodeToString(pub), + }) + c.Assert(err, IsNil) + + badKey := &data.PublicKey{ + Type: data.KeyTypeEd25519, + Scheme: data.KeySchemeEd25519, + Algorithms: data.HashAlgorithms, + Value: publicKey, + } + verifier := NewEd25519Verifier() + c.Assert(verifier.UnmarshalPublicKey(badKey), IsNil) +} + +func (Ed25519Suite) TestUnmarshalEd25519_Invalid(c *C) { + badKeyValue, err := json.Marshal(true) + c.Assert(err, IsNil) badKey := &data.PublicKey{ - Type: data.KeyTypeRSASSA_PSS_SHA256, - Scheme: data.KeySchemeRSASSA_PSS_SHA256, + Type: data.KeyTypeEd25519, + Scheme: data.KeySchemeEd25519, Algorithms: data.HashAlgorithms, Value: badKeyValue, } - verifier := NewP256Verifier() + verifier := NewEd25519Verifier() c.Assert(verifier.UnmarshalPublicKey(badKey), ErrorMatches, "json: cannot unmarshal.*") } +func (Ed25519Suite) TestUnmarshalEd25519_FastFuzz(c *C) { + verifier := NewEd25519Verifier() + for i := 0; i < 50; i++ { + // Ensure no basic panic + + f := fuzz.New() + var publicData data.PublicKey + f.Fuzz(&publicData) + + verifier.UnmarshalPublicKey(&publicData) + } +} + +func (Ed25519Suite) TestUnmarshalEd25519_TooLongContent(c *C) { + randomSeed := make([]byte, MaxJSONKeySize) + _, err := io.ReadFull(rand.Reader, randomSeed) + c.Assert(err, IsNil) + + tooLongPayload, err := json.Marshal( + &ed25519Verifier{ + PublicKey: data.HexBytes(hex.EncodeToString(randomSeed)), + }, + ) + c.Assert(err, IsNil) + + badKey := &data.PublicKey{ + Type: data.KeyTypeEd25519, + Scheme: data.KeySchemeEd25519, + Algorithms: data.HashAlgorithms, + Value: tooLongPayload, + } + verifier := NewEd25519Verifier() + err = verifier.UnmarshalPublicKey(badKey) + c.Assert(errors.Is(err, io.ErrUnexpectedEOF), Equals, true) +} + func (Ed25519Suite) TestSignVerify(c *C) { signer, err := GenerateEd25519Key() c.Assert(err, IsNil) diff --git a/pkg/keys/keys.go b/pkg/keys/keys.go index 702c420c..dc5f3ea2 100644 --- a/pkg/keys/keys.go +++ b/pkg/keys/keys.go @@ -8,6 +8,9 @@ import ( "github.com/theupdateframework/go-tuf/data" ) +// MaxJSONKeySize defines the maximum length of a JSON payload. +const MaxJSONKeySize = 512 * 1024 // 512Kb + // SignerMap stores mapping between key type strings and signer constructors. var SignerMap sync.Map