diff --git a/README.md b/README.md
index e4d6a7c..8e6a27a 100644
--- a/README.md
+++ b/README.md
@@ -7,12 +7,12 @@
Standards-based HTTP request signing and verification for Go
## Introduction
@@ -106,9 +106,11 @@ computation is based on version `05` of [Digest Headers][dighdr]
| create multiple signatures | ✅ | | |
| verify from multiple signatures | ✅ | | |
| `rsa-pss-sha512` | ✅ | | |
-| `rsa-v1_5-sha256` | | ❌ | |
+| `rsa-v1_5-sha256` | ✅ | | |
| `hmac-sha256` | ✅ | | |
| `ecdsa-p256-sha256` | ✅ | | |
+| `ecdsa-p384-sha384` | ✅ | | |
+| `ed25519` | ✅ | | |
| custom signature formats | | ❌ | `eddsa` is not part of the spec, so custom support here would be nice! |
| JSON Web Signatures | | ❌ | JWS doesn't support any additional algs, but it is part of the spec |
| Signature-Input as trailer | | ❌ | Trailers can be dropped. accept for verification only. |
@@ -117,13 +119,13 @@ computation is based on version `05` of [Digest Headers][dighdr]
| response digests | | ❌ | Tricky to support for signature use according to the spec |
| multiple digests | | ❌ | |
| digest: `sha-256` | | ❌ | |
-| digest: `sha-512` | | ❌ | |
+| digest: `sha-512` | ✅ | ❌ | |
| digest: `md5` | | ❌ | Deprecated in the spec. Unlikely to be supported. |
| digest: `sha` | | ❌ | Deprecated in the spec. Unlikely to be supported. |
| digest: `unixsum` | | ❌ | |
| digest: `unixcksum` | | ❌ | |
| digest: `id-sha-512` | | ❌ | |
-| digest: `id-sha-256` | ✅ | | `id-*` digests are more resilient for `content-encoding` support |
+| digest: `id-sha-256` | | ❌ | |
| custom digest formats | | ❌ | |
## Contributing
@@ -151,7 +153,7 @@ I would love your help!
[dighdr]: https://datatracker.ietf.org/doc/draft-ietf-httpbis-digest-headers/
[myblog]: https://repl.ca/modern-webhook-signatures/
-[godoc]: https://pkg.go.dev/github.com/jbowes/httpsig
+[godoc]: https://pkg.go.dev/github.com/offblocks/httpsig
[issues]: ./issues
[bug]: ./issues/new?labels=bug
[enhancement]: ./issues/new?labels=enhancement
diff --git a/canonicalize.go b/canonicalize.go
index 2d9df8b..28c8d47 100644
--- a/canonicalize.go
+++ b/canonicalize.go
@@ -99,7 +99,7 @@ type signatureParams struct {
alg string
created time.Time
expires *time.Time
- nonce string
+ nonce *string
}
func (sp *signatureParams) canonicalize() string {
@@ -118,6 +118,10 @@ func (sp *signatureParams) canonicalize() string {
o += fmt.Sprintf(";keyid=\"%s\"", sp.keyID)
}
+ if sp.nonce != nil {
+ o += fmt.Sprintf(";nonce=\"%s\"", *sp.nonce)
+ }
+
if sp.alg != "" {
o += fmt.Sprintf(";alg=\"%s\"", sp.alg)
}
@@ -168,7 +172,8 @@ func parseSignatureInput(in string) (*signatureParams, error) {
case "keyid":
sp.keyID = strings.Trim(paramParts[1], `"`)
case "nonce":
- sp.nonce = strings.Trim(paramParts[1], `"`)
+ nonce := strings.Trim(paramParts[1], `"`)
+ sp.nonce = &nonce
case "created":
i, err := strconv.ParseInt(paramParts[1], 10, 64)
if err != nil {
diff --git a/digest.go b/digest.go
index 9992c1e..ba30110 100644
--- a/digest.go
+++ b/digest.go
@@ -5,7 +5,7 @@
package httpsig
import (
- "crypto/sha256"
+ "crypto/sha512"
"crypto/subtle"
"encoding/base64"
"fmt"
@@ -17,9 +17,9 @@ import (
// TODO: support more algorithms, and maybe do its own package.
func calcDigest(in []byte) string {
- dig := sha256.Sum256(in)
+ dig := sha512.Sum512(in)
- return fmt.Sprintf("id-sha256=%s", base64.StdEncoding.EncodeToString(dig[:]))
+ return fmt.Sprintf("sha-512=:%s:", base64.StdEncoding.EncodeToString(dig[:]))
}
func verifyDigest(in []byte, dig string) bool {
diff --git a/example_test.go b/example_test.go
index 4fbb70c..3092cf6 100644
--- a/example_test.go
+++ b/example_test.go
@@ -10,7 +10,7 @@ import (
"net/http"
"time"
- "github.com/Gh0u1L5/httpsig"
+ "github.com/offblocks/httpsig"
)
const secret = "support-your-local-cat-bonnet-store"
diff --git a/go.mod b/go.mod
index 725e64d..0f8637f 100644
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-module github.com/Gh0u1L5/httpsig
+module github.com/offblocks/httpsig
go 1.18
diff --git a/httpsig.go b/httpsig.go
index b625de1..ef4a83d 100644
--- a/httpsig.go
+++ b/httpsig.go
@@ -7,6 +7,7 @@ package httpsig
import (
"bytes"
"crypto/ecdsa"
+ "crypto/ed25519"
"crypto/rsa"
"errors"
"io"
@@ -27,14 +28,11 @@ func sliceHas(haystack []string, needle string) bool {
}
type Signer struct {
- signer
+ *signer
}
func NewSigner(opts ...signOption) *Signer {
- s := signer{
- keys: make(map[string]sigHolder),
- nowFunc: time.Now,
- }
+ s := signer{}
for _, o := range opts {
o.configureSign(&s)
@@ -47,13 +45,13 @@ func NewSigner(opts ...signOption) *Signer {
// TODO: normalize headers? lowercase & de-dupe
// specialty components and digest first, for aesthetics
- for _, comp := range []string{"digest", "@query", "@path", "@method"} {
+ for _, comp := range []string{"content-digest", "@query", "@path", "@method"} {
if !sliceHas(s.headers, comp) {
s.headers = append([]string{comp}, s.headers...)
}
}
- return &Signer{s}
+ return &Signer{&s}
}
func (s *Signer) Sign(r *http.Request) error {
@@ -72,7 +70,7 @@ func (s *Signer) Sign(r *http.Request) error {
// Always set a digest (for now)
// TODO: we could skip setting digest on an empty body if content-length is included in the sig
- r.Header.Set("Digest", calcDigest(b.Bytes()))
+ r.Header.Set("Content-Digest", calcDigest(b.Bytes()))
msg := messageFromRequest(r)
hdr, err := s.signer.Sign(msg)
@@ -131,9 +129,9 @@ func (v *Verifier) Verify(r *http.Request) (keyID string, err error) {
}
}
- // Check the digest if set. We only support id-sha-256 for now.
+ // Check the digest if set. We only support sha-512 for now.
// TODO: option to require this?
- if dig := r.Header.Get("Digest"); dig != "" {
+ if dig := r.Header.Get("Content-Digest"); dig != "" {
if !verifyDigest(b.Bytes(), dig) {
return keyID, errors.New("digest mismatch")
}
@@ -233,11 +231,27 @@ func WithVerifyingKeyResolver(resolver VerifyingKeyResolver) verifyOption {
}
}
+// WithSignRsaPkcs1v15Sha256 adds signing using `rsa-v1_5-sha256` with the given private key
+// using the given key id.
+func WithSignRsaPkcs1v15Sha256(keyID string, pk *rsa.PrivateKey) signOption {
+ return &optImpl{
+ s: func(s *signer) { s.keys.Store(keyID, signRsaPkcs1v15Sha256(pk)) },
+ }
+}
+
+// WithVerifyRsaPkcs1v15Sha256 adds signature verification using `rsa-v1_5-sha256` with the
+// given public key using the given key id.
+func WithVerifyRsaPkcs1v15Sha256(keyID string, pk *rsa.PublicKey) verifyOption {
+ return &optImpl{
+ v: func(v *verifier) { v.keys.Store(keyID, verifyRsaPkcs1v15Sha256(pk)) },
+ }
+}
+
// WithSignRsaPssSha512 adds signing using `rsa-pss-sha512` with the given private key
// using the given key id.
func WithSignRsaPssSha512(keyID string, pk *rsa.PrivateKey) signOption {
return &optImpl{
- s: func(s *signer) { s.keys[keyID] = signRsaPssSha512(pk) },
+ s: func(s *signer) { s.keys.Store(keyID, signRsaPssSha512(pk)) },
}
}
@@ -253,7 +267,7 @@ func WithVerifyRsaPssSha512(keyID string, pk *rsa.PublicKey) verifyOption {
// using the given key id.
func WithSignEcdsaP256Sha256(keyID string, pk *ecdsa.PrivateKey) signOption {
return &optImpl{
- s: func(s *signer) { s.keys[keyID] = signEccP256(pk) },
+ s: func(s *signer) { s.keys.Store(keyID, signEccP256(pk)) },
}
}
@@ -265,11 +279,43 @@ func WithVerifyEcdsaP256Sha256(keyID string, pk *ecdsa.PublicKey) verifyOption {
}
}
+// WithSignEcdsaP384Sha384 adds signing using `ecdsa-p384-sha384` with the given private key
+// using the given key id.
+func WithSignEcdsaP384Sha384(keyID string, pk *ecdsa.PrivateKey) signOption {
+ return &optImpl{
+ s: func(s *signer) { s.keys.Store(keyID, signEccP384(pk)) },
+ }
+}
+
+// WithVerifyEcdsaP384Sha384 adds signature verification using `ecdsa-p384-sha384` with the
+// given public key using the given key id.
+func WithVerifyEcdsaP384Sha384(keyID string, pk *ecdsa.PublicKey) verifyOption {
+ return &optImpl{
+ v: func(v *verifier) { v.keys.Store(keyID, verifyEccP384(pk)) },
+ }
+}
+
+// WithSignEd25519 adds signing using `ed25519` with the given private key
+// using the given key id.
+func WithSignEd25519(keyID string, pk *ed25519.PrivateKey) signOption {
+ return &optImpl{
+ s: func(s *signer) { s.keys.Store(keyID, signEd25519(pk)) },
+ }
+}
+
+// WithVerifyEd25519 adds signature verification using `ed25519` with the
+// given public key using the given key id.
+func WithVerifyEd25519(keyID string, pk *ed25519.PublicKey) verifyOption {
+ return &optImpl{
+ v: func(v *verifier) { v.keys.Store(keyID, verifyEd25519(pk)) },
+ }
+}
+
// WithHmacSha256 adds signing or signature verification using `hmac-sha256` with the
// given shared secret using the given key id.
func WithHmacSha256(keyID string, secret []byte) signOrVerifyOption {
return &optImpl{
- s: func(s *signer) { s.keys[keyID] = signHmacSha256(secret) },
+ s: func(s *signer) { s.keys.Store(keyID, signHmacSha256(secret)) },
v: func(v *verifier) { v.keys.Store(keyID, verifyHmacSha256(secret)) },
}
}
diff --git a/sign.go b/sign.go
index 6364741..06928b4 100644
--- a/sign.go
+++ b/sign.go
@@ -8,34 +8,51 @@ import (
"bytes"
"crypto"
"crypto/ecdsa"
+ "crypto/ed25519"
"crypto/hmac"
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
+ "crypto/sha512"
"encoding/base64"
"fmt"
"io"
"net/http"
"strings"
+ "sync"
"time"
)
type sigImpl struct {
w io.Writer
- sign func() []byte
+ sign func() ([]byte, error)
}
type sigHolder struct {
alg string
+ nonce func() *string
signer func() sigImpl
}
type signer struct {
headers []string
- keys map[string]sigHolder
+ keys sync.Map
- // For testing
- nowFunc func() time.Time
+ created *time.Time
+ expires *time.Time
+ nonce *string
+}
+
+func withCreated(t time.Time) signOption {
+ return &optImpl{
+ s: func(s *signer) { s.created = &t },
+ }
+}
+
+func withNonce(nonce string) signOption {
+ return &optImpl{
+ s: func(s *signer) { s.nonce = &nonce },
+ }
}
func (s *signer) Sign(msg *message) (http.Header, error) {
@@ -73,33 +90,47 @@ func (s *signer) Sign(msg *message) (http.Header, error) {
items = append(items, h)
}
- now := s.nowFunc()
+ var now time.Time
+ if s.created != nil {
+ now = *s.created
+ } else {
+ now = time.Now()
+ }
sps := make(map[string]string)
sigs := make(map[string]string)
i := 1 // 1 indexed icky
- for k, si := range s.keys {
+ s.keys.Range(func(k, si any) bool {
sp := &signatureParams{
items: items,
- keyID: k,
+ keyID: k.(string),
created: now,
- alg: si.alg,
+ expires: s.expires,
+ nonce: s.nonce,
+ alg: si.(sigHolder).alg,
}
sps[fmt.Sprintf("sig%d", i)] = sp.canonicalize()
- signer := si.signer()
+ signer := si.(sigHolder).signer()
if _, err := signer.w.Write(b.Bytes()); err != nil {
- return nil, err
+ panic(err)
}
if err := canonicalizeSignatureParams(signer.w, sp); err != nil {
- return nil, err
+ panic(err)
+ }
+
+ signed, err := signer.sign()
+ if err != nil {
+ panic(err)
}
- sigs[fmt.Sprintf("sig%d", i)] = base64.StdEncoding.EncodeToString(signer.sign())
+ sigs[fmt.Sprintf("sig%d", i)] = base64.StdEncoding.EncodeToString(signed)
i++
- }
+
+ return true
+ })
// for each configured key id,
// canonicalize signing options appended to byte slice
@@ -125,17 +156,39 @@ func (s *signer) Sign(msg *message) (http.Header, error) {
func signRsaPssSha512(pk *rsa.PrivateKey) sigHolder {
return sigHolder{
alg: "rsa-pss-sha512",
+ nonce: func() *string {
+ n := nonce()
+ return &n
+ },
signer: func() sigImpl {
- h := sha256.New()
+ h := sha512.New()
return sigImpl{
w: h,
- sign: func() []byte {
+ sign: func() ([]byte, error) {
b := h.Sum(nil)
+ return rsa.SignPSS(rand.Reader, pk, crypto.SHA512, b, nil)
+ },
+ }
+ },
+ }
+}
+
+func signRsaPkcs1v15Sha256(pk *rsa.PrivateKey) sigHolder {
+ return sigHolder{
+ alg: "rsa-v1_5-sha256",
+ nonce: func() *string {
+ n := nonce()
+ return &n
+ },
+ signer: func() sigImpl {
+ h := sha256.New()
- // TODO: might have to deal with this error :)
- sig, _ := rsa.SignPSS(rand.Reader, pk, crypto.SHA512, b, nil)
- return sig
+ return sigImpl{
+ w: h,
+ sign: func() ([]byte, error) {
+ b := h.Sum(nil)
+ return rsa.SignPKCS1v15(rand.Reader, pk, crypto.SHA512, b)
},
}
},
@@ -145,17 +198,60 @@ func signRsaPssSha512(pk *rsa.PrivateKey) sigHolder {
func signEccP256(pk *ecdsa.PrivateKey) sigHolder {
return sigHolder{
alg: "ecdsa-p256-sha256",
+ nonce: func() *string {
+ n := nonce()
+ return &n
+ },
signer: func() sigImpl {
h := sha256.New()
return sigImpl{
w: h,
- sign: func() []byte {
+ sign: func() ([]byte, error) {
+ b := h.Sum(nil)
+ return ecdsa.SignASN1(rand.Reader, pk, b)
+ },
+ }
+ },
+ }
+}
+
+func signEccP384(pk *ecdsa.PrivateKey) sigHolder {
+ return sigHolder{
+ alg: "ecdsa-p384-sha384",
+ nonce: func() *string {
+ n := nonce()
+ return &n
+ },
+ signer: func() sigImpl {
+ h := sha512.New384()
+
+ return sigImpl{
+ w: h,
+ sign: func() ([]byte, error) {
b := h.Sum(nil)
+ return ecdsa.SignASN1(rand.Reader, pk, b)
+ },
+ }
+ },
+ }
+}
+
+func signEd25519(pk *ed25519.PrivateKey) sigHolder {
+ return sigHolder{
+ alg: "ed25519",
+ nonce: func() *string {
+ n := nonce()
+ return &n
+ },
+ signer: func() sigImpl {
+ var h bytes.Buffer
- // TODO: might have to deal with this error :)
- sig, _ := ecdsa.SignASN1(rand.Reader, pk, b)
- return sig
+ return sigImpl{
+ w: &h,
+ sign: func() ([]byte, error) {
+ b := h.Bytes()
+ return ed25519.Sign(*pk, b), nil
},
}
},
@@ -170,8 +266,17 @@ func signHmacSha256(secret []byte) sigHolder {
return sigImpl{
w: h,
- sign: func() []byte { return h.Sum(nil) },
+ sign: func() ([]byte, error) { return h.Sum(nil), nil },
}
},
}
}
+
+func nonce() string {
+ b := make([]byte, 16)
+ if _, err := rand.Read(b); err != nil {
+ panic(err)
+ }
+
+ return base64.URLEncoding.EncodeToString(b)
+}
diff --git a/standard_test.go b/standard_test.go
index a322ce2..718d1aa 100644
--- a/standard_test.go
+++ b/standard_test.go
@@ -5,8 +5,12 @@
package httpsig
import (
+ "crypto/ecdsa"
+ "crypto/ed25519"
"crypto/rsa"
"crypto/x509"
+ "crypto/x509/pkix"
+ "encoding/asn1"
"encoding/base64"
"encoding/pem"
"net/http"
@@ -36,42 +40,54 @@ func testReq() *message {
"Host": []string{"example.com"},
"Date": []string{"Tue, 20 Apr 2021 02:07:55 GMT"},
"Content-Type": []string{"application/json"},
- "Digest": []string{"SHA-256=X48E9qOokqqrvdts8nOJRJN3OWDUoyWxBf7kbu9DBPE="},
+ "Content-Digest": []string{"sha-512=:WZDPaVn/7XgHaAy8pmojAkGWoRx2UFChF41A2svX+TaPm+AbwAgBWnrIiYllu7BNNyealdVLvRwEmTHWXvJwew==:"},
"Content-Length": []string{"18"},
},
}
}
-func TestSign_B_2_5(t *testing.T) {
- k, err := base64.StdEncoding.DecodeString(testSharedSecret)
- if err != nil {
- panic("could not decode test shared secret")
+func TestSign_RSA_PSS_SHA_512_Minimal_B_2_1(t *testing.T) {
+ block, _ := pem.Decode([]byte(testKeyRSAPSS))
+ if block == nil {
+ panic("could not decode test private key pem")
}
- s := &signer{
- headers: []string{"@authority", "date", "content-type"},
- keys: map[string]sigHolder{
- "test-shared-secret": signHmacSha256(k),
- },
+ // taken from crypto/x509/pkcs8.go
+ type pkcs8 struct {
+ Version int
+ Algo pkix.AlgorithmIdentifier
+ PrivateKey []byte
+ // optional attributes omitted.
+ }
+ var privKey pkcs8
+ if _, err := asn1.Unmarshal(block.Bytes, &privKey); err != nil {
+ panic("could not decode test private key pem")
+ }
- nowFunc: func() time.Time { return time.Unix(1618884475, 0) },
+ pk, err := x509.ParsePKCS1PrivateKey(privKey.PrivateKey)
+ if err != nil {
+ panic("could not decode test private key: " + err.Error())
}
+ s := signer{}
+ withCreated(time.Unix(1618884473, 0)).configureSign(&s)
+ withNonce("b3k2pp5k7z-50gnwp.yemd").configureSign(&s)
+
+ s.keys.Store("test-key-rsa-pss", signRsaPssSha512(pk))
+
hdr, err := s.Sign(testReq())
if err != nil {
t.Error("signing failed:", err)
}
- if hdr.Get("Signature-Input") != `sig1=("@authority" "date" "content-type");created=1618884475;keyid="test-shared-secret"` {
+ if hdr.Get("Signature-Input") != `sig1=();created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd";alg="rsa-pss-sha512"` {
t.Error("signature input did not match. Got:", hdr.Get("Signature-Input"))
}
- if hdr.Get("Signature") != `sig1=:fN3AMNGbx0V/cIEKkZOvLOoC3InI+lM2+gTv22x3ia8=:` {
- t.Error("signature did not match. Got:", hdr.Get("Signature"))
- }
+ // can't verify signature as it is randomised
}
-func TestVerify_B_2_1(t *testing.T) {
+func TestVerify_RSA_PSS_SHA_512_Minimal_B_2_1(t *testing.T) {
block, _ := pem.Decode([]byte(testKeyRSAPSSPub))
if block == nil {
panic("could not decode test public key pem")
@@ -90,8 +106,8 @@ func TestVerify_B_2_1(t *testing.T) {
v.keys.Store("test-key-rsa-pss", verifyRsaPssSha512(pk))
req := testReq()
- req.Header.Set("Signature-Input", `sig1=();created=1618884475;keyid="test-key-rsa-pss";alg="rsa-pss-sha512"`)
- req.Header.Set("Signature", `sig1=:HWP69ZNiom9Obu1KIdqPPcu/C1a5ZUMBbqS/xwJECV8bhIQVmEAAAzz8LQPvtP1iFSxxluDO1KE9b8L+O64LEOvhwYdDctV5+E39Jy1eJiD7nYREBgxTpdUfzTO+Trath0vZdTylFlxK4H3l3s/cuFhnOCxmFYgEa+cw+StBRgY1JtafSFwNcZgLxVwialuH5VnqJS4JN8PHD91XLfkjMscTo4jmVMpFd3iLVe0hqVFl7MDt6TMkwIyVFnEZ7B/VIQofdShO+C/7MuupCSLVjQz5xA+Zs6Hw+W9ESD/6BuGs6LF1TcKLxW+5K+2zvDY/Cia34HNpRW5io7Iv9/b7iQ==:`)
+ req.Header.Set("Signature-Input", `sig1=();created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd"`)
+ req.Header.Set("Signature", `sig1=:d2pmTvmbncD3xQm8E9ZV2828BjQWGgiwAaw5bAkgibUopemLJcWDy/lkbbHAve4cRAtx31Iq786U7it++wgGxbtRxf8Udx7zFZsckzXaJMkA7ChG52eSkFxykJeNqsrWH5S+oxNFlD4dzVuwe8DhTSja8xxbR/Z2cOGdCbzR72rgFWhzx2VjBqJzsPLMIQKhO4DGezXehhWwE56YCE+O6c0mKZsfxVrogUvA4HELjVKWmAvtl6UnCh8jYzuVG5WSb/QEVPnP5TmcAnLH1g+s++v6d4s8m0gCw1fV5/SITLq9mhho8K3+7EPYTU8IU1bLhdxO5Nyt8C8ssinQ98Xw9Q==:`)
_, err = v.Verify(req)
if err != nil {
@@ -99,8 +115,111 @@ func TestVerify_B_2_1(t *testing.T) {
}
}
-func TestVerify_B_2_2(t *testing.T) {
- // TODO: key parsing is duplicated
+func TestRoundtrip_RSA_PSS_SHA_512_Minimal_B_2_1(t *testing.T) {
+ blockPrivate, _ := pem.Decode([]byte(testKeyRSAPSS))
+ if blockPrivate == nil {
+ panic("could not decode test private key pem")
+ }
+
+ // taken from crypto/x509/pkcs8.go
+ type pkcs8 struct {
+ Version int
+ Algo pkix.AlgorithmIdentifier
+ PrivateKey []byte
+ // optional attributes omitted.
+ }
+ var privKey pkcs8
+ if _, err := asn1.Unmarshal(blockPrivate.Bytes, &privKey); err != nil {
+ panic("could not decode test private key pem")
+ }
+
+ pk, err := x509.ParsePKCS1PrivateKey(privKey.PrivateKey)
+ if err != nil {
+ panic("could not decode test private key: " + err.Error())
+ }
+
+ s := signer{}
+ withCreated(time.Unix(1618884473, 0)).configureSign(&s)
+ withNonce("b3k2pp5k7z-50gnwp.yemd").configureSign(&s)
+
+ s.keys.Store("test-key-rsa-pss", signRsaPssSha512(pk))
+
+ req := testReq()
+ hdr, err := s.Sign(req)
+ if err != nil {
+ t.Error("signing failed:", err)
+ }
+
+ blockPub, _ := pem.Decode([]byte(testKeyRSAPSSPub))
+ if blockPub == nil {
+ panic("could not decode test public key pem")
+ }
+
+ pki, err := x509.ParsePKIXPublicKey(blockPub.Bytes)
+ if err != nil {
+ panic("could not decode test public key: " + err.Error())
+ }
+
+ pubk := pki.(*rsa.PublicKey)
+
+ v := &verifier{
+ nowFunc: func() time.Time { return time.Unix(1618884475, 0) },
+ }
+ v.keys.Store("test-key-rsa-pss", verifyRsaPssSha512(pubk))
+
+ req.Header.Set("Signature-Input", hdr["Signature-Input"][0])
+ req.Header.Set("Signature", hdr["Signature"][0])
+
+ _, err = v.Verify(req)
+ if err != nil {
+ t.Error("verification failed:", err)
+ }
+}
+
+func TestSign_RSA_PSS_SHA_512_Selective_B_2_2(t *testing.T) {
+ block, _ := pem.Decode([]byte(testKeyRSAPSS))
+ if block == nil {
+ panic("could not decode test private key pem")
+ }
+
+ // taken from crypto/x509/pkcs8.go
+ type pkcs8 struct {
+ Version int
+ Algo pkix.AlgorithmIdentifier
+ PrivateKey []byte
+ // optional attributes omitted.
+ }
+ var privKey pkcs8
+ if _, err := asn1.Unmarshal(block.Bytes, &privKey); err != nil {
+ panic("could not decode test private key pem")
+ }
+
+ pk, err := x509.ParsePKCS1PrivateKey(privKey.PrivateKey)
+ if err != nil {
+ panic("could not decode test private key: " + err.Error())
+ }
+
+ s := signer{
+ headers: []string{"@authority", "content-digest"},
+ }
+ withCreated(time.Unix(1618884473, 0)).configureSign(&s)
+ withNonce("b3k2pp5k7z-50gnwp.yemd").configureSign(&s)
+
+ s.keys.Store("test-key-rsa-pss", signRsaPssSha512(pk))
+
+ hdr, err := s.Sign(testReq())
+ if err != nil {
+ t.Error("signing failed:", err)
+ }
+
+ if hdr.Get("Signature-Input") != `sig1=("@authority" "content-digest");created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd";alg="rsa-pss-sha512"` {
+ t.Error("signature input did not match. Got:", hdr.Get("Signature-Input"))
+ }
+
+ // can't verify signature as it is randomised
+}
+
+func TestVerify_RSA_PSS_SHA_512_Selective_B_2_2(t *testing.T) {
block, _ := pem.Decode([]byte(testKeyRSAPSSPub))
if block == nil {
panic("could not decode test public key pem")
@@ -114,13 +233,13 @@ func TestVerify_B_2_2(t *testing.T) {
pk := pki.(*rsa.PublicKey)
v := &verifier{
- nowFunc: func() time.Time { return time.Unix(1618884475, 0) },
+ nowFunc: func() time.Time { return time.Unix(1618884473, 0) },
}
v.keys.Store("test-key-rsa-pss", verifyRsaPssSha512(pk))
req := testReq()
- req.Header.Set("Signature-Input", `sig1=("@authority" content-type");created=1618884475;keyid="test-key-rsa-pss"`)
- req.Header.Set("Signature", `sig1=:ik+OtGmM/kFqENDf9Plm8AmPtqtC7C9a+zYSaxr58b/E6h81ghJS3PcH+m1asiMp8yvccnO/RfaexnqanVB3C72WRNZN7skPTJmUVmoIeqZncdP2mlfxlLP6UbkrgYsk91NS6nwkKC6RRgLhBFqzP42oq8D2336OiQPDAo/04SxZt4Wx9nDGuy2SfZJUhsJqZyEWRk4204x7YEB3VxDAAlVgGt8ewilWbIKKTOKp3ymUeQIwptqYwv0l8mN404PPzRBTpB7+HpClyK4CNp+SVv46+6sHMfJU4taz10s/NoYRmYCGXyadzYYDj0BYnFdERB6NblI/AOWFGl5Axhhmjg==:`)
+ req.Header.Set("Signature-Input", `sig1=("@authority" "content-digest");created=1618884473;keyid="test-key-rsa-pss";nonce="b3k2pp5k7z-50gnwp.yemd";alg="rsa-pss-sha512"`)
+ req.Header.Set("Signature", `sig1=:e7vSoRHcw4hxLAp129Qdxui1KTgTnI8LM8/gNK7PZJwWm/HCcz+Mxwrzs97fNVCeiu0XPjtPdUcc5mz6/rD644aj0FpvSZzRhlP3KLBU8QMCI80m8blQhDBQeVR/XX9CGLD9BSgWPmd9J4FOf1b/giseT6dbxof1gVvZbHBVPurIGVyht7kNDUTLzxPEFlm7hQBKz0U5UCuqm4Fxw1jRaFm5WhWHwU1A3iqgf7QqE1HT+bCn/MCPl9KstKY5XgKDnJjGA0+qDfFrsNpii1hx/GNsAPWfcJnc7NjASfXtkyItr1e0Wqk2c2gpejiTxW7Qu9mYUmODBiCDn75rK9hSyQ==:`)
_, err = v.Verify(req)
if err != nil {
@@ -128,9 +247,43 @@ func TestVerify_B_2_2(t *testing.T) {
}
}
-func TestVerify_B_2_3(t *testing.T) {
- t.Skip("not working as of draft 06 changes")
- // TODO: key parsing is duplicated
+func TestRoundtrip_RSA_PSS_SHA_512_Selective_B_2_2(t *testing.T) {
+ blockPrivate, _ := pem.Decode([]byte(testKeyRSAPSS))
+ if blockPrivate == nil {
+ panic("could not decode test private key pem")
+ }
+
+ // taken from crypto/x509/pkcs8.go
+ type pkcs8 struct {
+ Version int
+ Algo pkix.AlgorithmIdentifier
+ PrivateKey []byte
+ // optional attributes omitted.
+ }
+ var privKey pkcs8
+ if _, err := asn1.Unmarshal(blockPrivate.Bytes, &privKey); err != nil {
+ panic("could not decode test private key pem")
+ }
+
+ pk, err := x509.ParsePKCS1PrivateKey(privKey.PrivateKey)
+ if err != nil {
+ panic("could not decode test private key: " + err.Error())
+ }
+
+ s := signer{
+ headers: []string{"@authority", "content-digest"},
+ }
+ withCreated(time.Unix(1618884473, 0)).configureSign(&s)
+ withNonce("b3k2pp5k7z-50gnwp.yemd").configureSign(&s)
+
+ s.keys.Store("test-key-rsa-pss", signRsaPssSha512(pk))
+
+ req := testReq()
+ hdr, err := s.Sign(req)
+ if err != nil {
+ t.Error("signing failed:", err)
+ }
+
block, _ := pem.Decode([]byte(testKeyRSAPSSPub))
if block == nil {
panic("could not decode test public key pem")
@@ -141,54 +294,199 @@ func TestVerify_B_2_3(t *testing.T) {
panic("could not decode test public key: " + err.Error())
}
- pk := pki.(*rsa.PublicKey)
+ pkpub := pki.(*rsa.PublicKey)
v := &verifier{
- nowFunc: func() time.Time { return time.Unix(1618884475, 0) },
+ nowFunc: func() time.Time { return time.Unix(1618884473, 0) },
+ }
+ v.keys.Store("test-key-rsa-pss", verifyRsaPssSha512(pkpub))
+
+ req.Header.Set("Signature-Input", hdr["Signature-Input"][0])
+ req.Header.Set("Signature", hdr["Signature"][0])
+
+ _, err = v.Verify(req)
+ if err != nil {
+ t.Error("verification failed:", err)
+ }
+}
+
+func TestRoundtrip_ECDSA_P256_SHA256(t *testing.T) {
+ blockPrivate, _ := pem.Decode([]byte(testKeyECCP256))
+ if blockPrivate == nil {
+ panic("could not decode test private key pem")
+ }
+
+ pk, err := x509.ParseECPrivateKey(blockPrivate.Bytes)
+ if err != nil {
+ panic("could not decode test private key: " + err.Error())
+ }
+
+ s := signer{}
+ withCreated(time.Unix(1618884473, 0)).configureSign(&s)
+ withNonce("b3k2pp5k7z-50gnwp.yemd").configureSign(&s)
+
+ s.keys.Store("test-key-ecc-p256", signEccP256(pk))
+
+ req := testReq()
+ hdr, err := s.Sign(req)
+ if err != nil {
+ t.Error("signing failed:", err)
+ }
+
+ block, _ := pem.Decode([]byte(testKeyECCP256Pub))
+ if block == nil {
+ panic("could not decode test public key pem")
+ }
+
+ pki, err := x509.ParsePKIXPublicKey(block.Bytes)
+ if err != nil {
+ panic("could not decode test public key: " + err.Error())
+ }
+
+ pkpub := pki.(*ecdsa.PublicKey)
+
+ v := &verifier{
+ nowFunc: func() time.Time { return time.Unix(1618884473, 0) },
+ }
+ v.keys.Store("test-key-ecc-p256", verifyEccP256(pkpub))
+
+ req.Header.Set("Signature-Input", hdr["Signature-Input"][0])
+ req.Header.Set("Signature", hdr["Signature"][0])
+
+ _, err = v.Verify(req)
+ if err != nil {
+ t.Error("verification failed:", err)
+ }
+}
+
+func TestRoundtrip_ECDSA_P384_SHA384(t *testing.T) {
+ blockPrivate, _ := pem.Decode([]byte(testKeyECCP384))
+ if blockPrivate == nil {
+ panic("could not decode test private key pem")
+ }
+
+ pk, err := x509.ParseECPrivateKey(blockPrivate.Bytes)
+ if err != nil {
+ panic("could not decode test private key: " + err.Error())
}
- v.keys.Store("test-key-rsa-pss", verifyRsaPssSha512(pk))
+
+ s := signer{}
+ withCreated(time.Unix(1618884473, 0)).configureSign(&s)
+ withNonce("b3k2pp5k7z-50gnwp.yemd").configureSign(&s)
+
+ s.keys.Store("test-key-ecc-p384", signEccP384(pk))
req := testReq()
- req.Header.Set("Signature-Input", `sig1=("date" "@method" "@path" "@query" "@authority" "content-type" "digest" "content-length");created=1618884475;keyid="test-key-rsa-pss"`)
- req.Header.Set("Signature", `sig1=:JuJnJMFGD4HMysAGsfOY6N5ZTZUknsQUdClNG51VezDgPUOW03QMe74vbIdndKwW1BBrHOHR3NzKGYZJ7X3ur23FMCdANe4VmKb3Rc1Q/5YxOO8p7KoyfVa4uUcMk5jB9KAn1M1MbgBnqwZkRWsbv8ocCqrnD85Kavr73lx51k1/gU8w673WT/oBtxPtAn1eFjUyIKyA+XD7kYph82I+ahvm0pSgDPagu917SlqUjeaQaNnlZzO03Iy1RZ5XpgbNeDLCqSLuZFVID80EohC2CQ1cL5svjslrlCNstd2JCLmhjL7xV3NYXerLim4bqUQGRgDwNJRnqobpS6C1NBns/Q==:`)
+ hdr, err := s.Sign(req)
+ if err != nil {
+ t.Error("signing failed:", err)
+ }
+
+ block, _ := pem.Decode([]byte(testKeyECCP384Pub))
+ if block == nil {
+ panic("could not decode test public key pem")
+ }
+
+ pki, err := x509.ParsePKIXPublicKey(block.Bytes)
+ if err != nil {
+ panic("could not decode test public key: " + err.Error())
+ }
+
+ pkpub := pki.(*ecdsa.PublicKey)
+
+ v := &verifier{
+ nowFunc: func() time.Time { return time.Unix(1618884473, 0) },
+ }
+ v.keys.Store("test-key-ecc-p384", verifyEccP384(pkpub))
+
+ req.Header.Set("Signature-Input", hdr["Signature-Input"][0])
+ req.Header.Set("Signature", hdr["Signature"][0])
+
_, err = v.Verify(req)
if err != nil {
t.Error("verification failed:", err)
}
}
-func TestVerify_B_2_4(t *testing.T) {
- t.Skip("not working yet")
- /*
- block, _ := pem.Decode([]byte(testKeyECCP256Pub))
- if block == nil {
- panic("could not decode test public key pem")
- }
-
- pk, err := x509.ParsePKIXPublicKey(block.Bytes)
- if err != nil {
- panic("could not decode test public key: " + err.Error())
- }
-
- v := &verifier{
- keys: map[string]verHolder{
- "test-key-ecc-p256": verifyEccP256(pk.(*ecdsa.PublicKey)),
- },
-
- nowFunc: func() time.Time { return time.Unix(1618884475, 0) },
- }
-
- req := testReq()
- req.Header.Set("Signature-Input", `sig1=("content-type" "digest" "content-length");created=1618884475;keyid="test-key-ecc-p256"`)
- req.Header.Set("Signature", `sig1=:n8RKXkj0iseWDmC6PNSQ1GX2R9650v+lhbb6rTGoSrSSx18zmn6fPOtBx48/WffYLO0n1RHHf9scvNGAgGq52Q==:`)
- err = v.Verify(req)
- if err != nil {
- t.Error("verification failed:", err)
- }
- */
+func TestRoundtrip_ED25519(t *testing.T) {
+ blockPrivate, _ := pem.Decode([]byte(testKeyEd25519))
+ if blockPrivate == nil {
+ panic("could not decode test private key pem")
+ }
+
+ pki, err := x509.ParsePKCS8PrivateKey(blockPrivate.Bytes)
+ if err != nil {
+ panic("could not decode test private key: " + err.Error())
+ }
+
+ pk := pki.(ed25519.PrivateKey)
+
+ s := signer{}
+ withCreated(time.Unix(1618884473, 0)).configureSign(&s)
+ withNonce("b3k2pp5k7z-50gnwp.yemd").configureSign(&s)
+
+ s.keys.Store("test-key-ed25519", signEd25519(&pk))
+
+ req := testReq()
+ hdr, err := s.Sign(req)
+ if err != nil {
+ t.Error("signing failed:", err)
+ }
+
+ block, _ := pem.Decode([]byte(testKeyEd25519Pub))
+ if block == nil {
+ panic("could not decode test public key pem")
+ }
+
+ pkpubi, err := x509.ParsePKIXPublicKey(block.Bytes)
+ if err != nil {
+ panic("could not decode test public key: " + err.Error())
+ }
+
+ pkpub := pkpubi.(ed25519.PublicKey)
+
+ v := &verifier{
+ nowFunc: func() time.Time { return time.Unix(1618884473, 0) },
+ }
+ v.keys.Store("test-key-ed25519", verifyEd25519(&pkpub))
+
+ req.Header.Set("Signature-Input", hdr["Signature-Input"][0])
+ req.Header.Set("Signature", hdr["Signature"][0])
+
+ _, err = v.Verify(req)
+ if err != nil {
+ t.Error("verification failed:", err)
+ }
+}
+
+func TestSign_HMAC_SHA_256_B_2_5(t *testing.T) {
+ k, err := base64.StdEncoding.DecodeString(testSharedSecret)
+ if err != nil {
+ panic("could not decode test shared secret")
+ }
+
+ s := signer{
+ headers: []string{"@authority", "date", "content-type"},
+ }
+ withCreated(time.Unix(1618884475, 0)).configureSign(&s)
+
+ s.keys.Store("test-shared-secret", signHmacSha256(k))
+
+ hdr, err := s.Sign(testReq())
+ if err != nil {
+ t.Error("signing failed:", err)
+ }
+
+ if hdr.Get("Signature-Input") != `sig1=("@authority" "date" "content-type");created=1618884475;keyid="test-shared-secret";alg="hmac-sha256"` {
+ t.Error("signature input did not match. Got:", hdr.Get("Signature-Input"))
+ }
+
+ if hdr.Get("Signature") != `sig1=:Ss67se+mIHEQhqCYpEpp521HLd+2KuQyXRtHr1RfIRk=:` {
+ t.Error("signature did not match. Got:", hdr.Get("Signature"))
+ }
}
-func TestVerify_B_2_5(t *testing.T) {
+func TestVerify_HMAC_SHA_256_B_2_5(t *testing.T) {
k, err := base64.StdEncoding.DecodeString(testSharedSecret)
if err != nil {
panic("could not decode test shared secret")
@@ -209,39 +507,52 @@ func TestVerify_B_2_5(t *testing.T) {
}
}
+func TestRoundtrip_HMAC_SHA_256_B_2_5(t *testing.T) {
+ k, err := base64.StdEncoding.DecodeString(testSharedSecret)
+ if err != nil {
+ panic("could not decode test shared secret")
+ }
+
+ s := signer{
+ headers: []string{"@authority", "date", "content-type"},
+ }
+ withCreated(time.Unix(1618884475, 0)).configureSign(&s)
+
+ s.keys.Store("test-shared-secret", signHmacSha256(k))
+
+ req := testReq()
+ hdr, err := s.Sign(req)
+ if err != nil {
+ t.Error("signing failed:", err)
+ }
+
+ v := &verifier{
+ nowFunc: func() time.Time { return time.Unix(1618884475, 0) },
+ }
+ v.keys.Store("test-shared-secret", verifyHmacSha256(k))
+
+ req.Header.Set("Signature-Input", hdr["Signature-Input"][0])
+ req.Header.Set("Signature", hdr["Signature"][0])
+
+ _, err = v.Verify(req)
+ if err != nil {
+ t.Error("verification failed:", err)
+ }
+}
+
// The following keypairs are taken from the Draft Standard, so we may recreate the examples in tests.
// If your robot scans this repo and says it's leaking keys I will be mildly amused.
-/*
-
-var testKeyRSA = `
------BEGIN RSA PRIVATE KEY-----
-MIIEqAIBAAKCAQEAhAKYdtoeoy8zcAcR874L8cnZxKzAGwd7v36APp7Pv6Q2jdsP
-BRrwWEBnez6d0UDKDwGbc6nxfEXAy5mbhgajzrw3MOEt8uA5txSKobBpKDeBLOsd
-JKFqMGmXCQvEG7YemcxDTRPxAleIAgYYRjTSd/QBwVW9OwNFhekro3RtlinV0a75
-jfZgkne/YiktSvLG34lw2zqXBDTC5NHROUqGTlML4PlNZS5Ri2U4aCNx2rUPRcKI
-lE0PuKxI4T+HIaFpv8+rdV6eUgOrB2xeI1dSFFn/nnv5OoZJEIB+VmuKn3DCUcCZ
-SFlQPSXSfBDiUGhwOw76WuSSsf1D4b/vLoJ10wIDAQABAoIBAG/JZuSWdoVHbi56
-vjgCgkjg3lkO1KrO3nrdm6nrgA9P9qaPjxuKoWaKO1cBQlE1pSWp/cKncYgD5WxE
-CpAnRUXG2pG4zdkzCYzAh1i+c34L6oZoHsirK6oNcEnHveydfzJL5934egm6p8DW
-+m1RQ70yUt4uRc0YSor+q1LGJvGQHReF0WmJBZHrhz5e63Pq7lE0gIwuBqL8SMaA
-yRXtK+JGxZpImTq+NHvEWWCu09SCq0r838ceQI55SvzmTkwqtC+8AT2zFviMZkKR
-Qo6SPsrqItxZWRty2izawTF0Bf5S2VAx7O+6t3wBsQ1sLptoSgX3QblELY5asI0J
-YFz7LJECgYkAsqeUJmqXE3LP8tYoIjMIAKiTm9o6psPlc8CrLI9CH0UbuaA2JCOM
-cCNq8SyYbTqgnWlB9ZfcAm/cFpA8tYci9m5vYK8HNxQr+8FS3Qo8N9RJ8d0U5Csw
-DzMYfRghAfUGwmlWj5hp1pQzAuhwbOXFtxKHVsMPhz1IBtF9Y8jvgqgYHLbmyiu1
-mwJ5AL0pYF0G7x81prlARURwHo0Yf52kEw1dxpx+JXER7hQRWQki5/NsUEtv+8RT
-qn2m6qte5DXLyn83b1qRscSdnCCwKtKWUug5q2ZbwVOCJCtmRwmnP131lWRYfj67
-B/xJ1ZA6X3GEf4sNReNAtaucPEelgR2nsN0gKQKBiGoqHWbK1qYvBxX2X3kbPDkv
-9C+celgZd2PW7aGYLCHq7nPbmfDV0yHcWjOhXZ8jRMjmANVR/eLQ2EfsRLdW69bn
-f3ZD7JS1fwGnO3exGmHO3HZG+6AvberKYVYNHahNFEw5TsAcQWDLRpkGybBcxqZo
-81YCqlqidwfeO5YtlO7etx1xLyqa2NsCeG9A86UjG+aeNnXEIDk1PDK+EuiThIUa
-/2IxKzJKWl1BKr2d4xAfR0ZnEYuRrbeDQYgTImOlfW6/GuYIxKYgEKCFHFqJATAG
-IxHrq1PDOiSwXd2GmVVYyEmhZnbcp8CxaEMQoevxAta0ssMK3w6UsDtvUvYvF22m
-qQKBiD5GwESzsFPy3Ga0MvZpn3D6EJQLgsnrtUPZx+z2Ep2x0xc5orneB5fGyF1P
-WtP+fG5Q6Dpdz3LRfm+KwBCWFKQjg7uTxcjerhBWEYPmEMKYwTJF5PBG9/ddvHLQ
-EQeNC8fHGg4UXU8mhHnSBt3EA10qQJfRDs15M38eG2cYwB1PZpDHScDnDA0=
------END RSA PRIVATE KEY-----
+var testKeyRSAPSSPub = `
+-----BEGIN PUBLIC KEY-----
+MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4tmm3r20Wd/PbqvP1s2
++QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry53mm+
+oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7OyrFAHq
+gDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUAAN5W
+Utzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw9lq4
+aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oyc6XI
+2wIDAQAB
+-----END PUBLIC KEY-----
`
var testKeyRSAPSS = `
@@ -274,21 +585,14 @@ S7Fnk6ZVVVHsxjtaHy1uJGFlaZzKR4AGNaUTOJMs6NadzCmGPAxNQQOCqoUjn4XR
rOjr9w349JooGXhOxbu8nOxX
-----END PRIVATE KEY-----
`
-*/
-var testKeyRSAPSSPub = `
+var testKeyECCP256Pub = `
-----BEGIN PUBLIC KEY-----
-MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAr4tmm3r20Wd/PbqvP1s2
-+QEtvpuRaV8Yq40gjUR8y2Rjxa6dpG2GXHbPfvMs8ct+Lh1GH45x28Rw3Ry53mm+
-oAXjyQ86OnDkZ5N8lYbggD4O3w6M6pAvLkhk95AndTrifbIFPNU8PPMO7OyrFAHq
-gDsznjPFmTOtCEcN2Z1FpWgchwuYLPL+Wokqltd11nqqzi+bJ9cvSKADYdUAAN5W
-Utzdpiy6LbTgSxP7ociU4Tn0g5I6aDZJ7A8Lzo0KSyZYoA485mqcO0GVAdVw9lq4
-aOT9v6d+nb4bnNkQVklLQ3fVAvJm+xdDOp9LCNCN48V2pnDOkFV6+U9nV5oyc6XI
-2wIDAQAB
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lf
+w0EkjqF7xB4FivAxzic30tMM4GF+hR6Dxh71Z50VGGdldkkDXZCnTNnoXQ==
-----END PUBLIC KEY-----
- `
+`
-/*
var testKeyECCP256 = `
-----BEGIN EC PRIVATE KEY-----
MHcCAQEEIFKbhfNZfpDsW43+0+JjUr9K+bTeuxopu653+hBaXGA7oAoGCCqGSM49
@@ -297,12 +601,33 @@ AwEHoUQDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lfw0EkjqF7xB4FivAxzic30tMM
-----END EC PRIVATE KEY-----
`
-var testKeyECCP256Pub = `
+var testKeyECCP384Pub = `
-----BEGIN PUBLIC KEY-----
-MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEqIVYZVLCrPZHGHjP17CTW0/+D9Lf
-w0EkjqF7xB4FivAxzic30tMM4GF+hR6Dxh71Z50VGGdldkkDXZCnTNnoXQ==
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEUWosCCtXp4eJkinU2XaDGSrrSfMynZkI
+EELa7Ratog6SrkIFD9nowhLoxc3Px4zAwxQzD8j5Th+vCtswq7ExACNiM6kM9974
+mF1l1Ll2Pn19pJCE2SutyxcMeAr4Lrgi
+-----END PUBLIC KEY-----
+`
+
+var testKeyECCP384 = `
+-----BEGIN EC PRIVATE KEY-----
+MIGkAgEBBDDayXurkt5pieok3TsD5CdPvrUgljTE8n5o9M1bapc8yMz94WCAiQZb
+TXi9MwOv4TWgBwYFK4EEACKhZANiAARRaiwIK1enh4mSKdTZdoMZKutJ8zKdmQgQ
+QtrtFq2iDpKuQgUP2ejCEujFzc/HjMDDFDMPyPlOH68K2zCrsTEAI2IzqQz33viY
+XWXUuXY+fX2kkITZK63LFwx4CvguuCI=
+-----END EC PRIVATE KEY-----
+`
+
+var testKeyEd25519Pub = `
+-----BEGIN PUBLIC KEY-----
+MCowBQYDK2VwAyEAJrQLj5P/89iXES9+vFgrIy29clF9CC/oPPsw3c5D0bs=
-----END PUBLIC KEY-----
`
-*/
+
+var testKeyEd25519 = `
+-----BEGIN PRIVATE KEY-----
+MC4CAQAwBQYDK2VwBCIEIJ+DYvh6SEqVTm50DFtMDoQikTmiCqirVv9mWG9qfSnF
+-----END PRIVATE KEY-----
+`
var testSharedSecret = `uzvJfB4u3N0Jy4T7NZ75MDVcr8zSTInedJtkgcu46YW4XByzNJjxBdtjUkdJPBtbmHhIDi6pcl8jsasjlTMtDQ==`
diff --git a/verify.go b/verify.go
index 57c1e46..a7a2765 100644
--- a/verify.go
+++ b/verify.go
@@ -8,6 +8,7 @@ import (
"bytes"
"crypto"
"crypto/ecdsa"
+ "crypto/ed25519"
"crypto/hmac"
"crypto/rsa"
"crypto/sha256"
@@ -32,7 +33,7 @@ type verHolder struct {
}
type verifier struct {
- keys sync.Map // map[string]verHolder
+ keys sync.Map
resolver VerifyingKeyResolver
// For testing
@@ -233,6 +234,24 @@ func verifyRsaPssSha512(pk *rsa.PublicKey) verHolder {
}
}
+func verifyRsaPkcs1v15Sha256(pk *rsa.PublicKey) verHolder {
+ return verHolder{
+ alg: "rsa-v1_5-sha256",
+ verifier: func() verImpl {
+ h := sha256.New()
+
+ return verImpl{
+ w: h,
+ verify: func(s []byte) error {
+ b := h.Sum(nil)
+
+ return rsa.VerifyPKCS1v15(pk, crypto.SHA512, b, s)
+ },
+ }
+ },
+ }
+}
+
func verifyEccP256(pk *ecdsa.PublicKey) verHolder {
return verHolder{
alg: "ecdsa-p256-sha256",
@@ -255,6 +274,50 @@ func verifyEccP256(pk *ecdsa.PublicKey) verHolder {
}
}
+func verifyEccP384(pk *ecdsa.PublicKey) verHolder {
+ return verHolder{
+ alg: "ecdsa-p384-sha384",
+ verifier: func() verImpl {
+ h := sha512.New384()
+
+ return verImpl{
+ w: h,
+ verify: func(s []byte) error {
+ b := h.Sum(nil)
+
+ if !ecdsa.VerifyASN1(pk, b, s) {
+ return errInvalidSignature
+ }
+
+ return nil
+ },
+ }
+ },
+ }
+}
+
+func verifyEd25519(pk *ed25519.PublicKey) verHolder {
+ return verHolder{
+ alg: "ed25519",
+ verifier: func() verImpl {
+ var h bytes.Buffer
+
+ return verImpl{
+ w: &h,
+ verify: func(s []byte) error {
+ b := h.Bytes()
+
+ if !ed25519.Verify(*pk, b, s) {
+ return errInvalidSignature
+ }
+
+ return nil
+ },
+ }
+ },
+ }
+}
+
func verifyHmacSha256(secret []byte) verHolder {
return verHolder{
alg: "hmac-sha256",