Skip to content

Commit

Permalink
feat(keys): JSON unmarshal hardening. (#275)
Browse files Browse the repository at this point in the history
* feat(keys): JSON unmarshal hardening.

* Add io.LimitReader to block public key content larger than 512Kb.
* Fix Ed25519 signer constructor to be referenced as ECDSASigner.
* Rename `NewEd25519Signer` to `NewEd25519SignerFromKey` to support
  the signer registry registration and prevent double declaration.
* Add ECDSA unmarshaller tests.

Signed-off-by: Thibault Normand <me@zenithar.org>

* feat(key): add crypto related controls.

Signed-off-by: Thibault Normand <me@zenithar.org>

* feat(key): ecdsa unmarshal already check if point is on curve.

Signed-off-by: Thibault Normand <me@zenithar.org>

* fix(key): test all errors.

Signed-off-by: Thibault Normand <me@zenithar.org>

* fix(key): use appropriate type for random seed.

Signed-off-by: Thibault Normand <me@zenithar.org>

* fix(key): display ed25519 public key size on error.

Signed-off-by: Thibault Normand <me@zenithar.org>

* feat(keys): support unknown fields for json decoder.

Signed-off-by: Thibault Normand <me@zenithar.org>

* feat(types): support unknown fields for json decoder.

Signed-off-by: Thibault Normand <me@zenithar.org>

* style(go): convert switch to if.

Signed-off-by: Thibault Normand <me@zenithar.org>
  • Loading branch information
Zenithar committed Jul 20, 2022
1 parent 2e6c621 commit 4febe4c
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 40 deletions.
7 changes: 6 additions & 1 deletion data/types.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package data

import (
"bytes"
"crypto/sha256"
"encoding/hex"
"encoding/json"
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
27 changes: 5 additions & 22 deletions go.sum
Original file line number Diff line number Diff line change
@@ -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=
Expand All @@ -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=
Expand All @@ -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=
Expand All @@ -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=
Expand All @@ -86,39 +76,33 @@ 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=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
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=
Expand All @@ -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=
19 changes: 17 additions & 2 deletions pkg/keys/ecdsa.go
Original file line number Diff line number Diff line change
@@ -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"
Expand Down Expand Up @@ -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
}
92 changes: 92 additions & 0 deletions pkg/keys/ecdsa_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
57 changes: 47 additions & 10 deletions pkg/keys/ed25519.go
Original file line number Diff line number Diff line change
@@ -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{}
}

Expand All @@ -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
}
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit 4febe4c

Please sign in to comment.