Skip to content
This repository has been archived by the owner on May 21, 2022. It is now read-only.

Commit

Permalink
Native support for key rotation in verifications
Browse files Browse the repository at this point in the history
Add native support for key rotation for ES*, HS*, RS*, and PS*
verifications.

In those SigningMethod's Verify implementations, also allow the key to
be the type of the slice of the supported key type, so that the caller
can implement the KeyFunc to return all the accepted keys together to
support key rotation.

While key rotation verification can be done on the callers' side without
this change, this change provides better performance because:

- When trying the next key, the steps before actually using the key do
  not need to be performed again.

- If a verification process failed for non-key reasons (for example,
  because it's already expired), it saves the effort to try the next
  key.
  • Loading branch information
fishy committed Jan 9, 2020
1 parent dc14462 commit 3718e07
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 62 deletions.
50 changes: 32 additions & 18 deletions ecdsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,9 @@ func (m *SigningMethodECDSA) Alg() string {
return m.Name
}

// Implements the Verify method from SigningMethod
// For this verify method, key must be an ecdsa.PublicKey struct
// Implements the Verify method from SigningMethod.
// For this verify method, key must be in types of either *ecdsa.PublicKey or
// []*ecdsa.PublicKey (for rotation keys).
func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error {
var err error

Expand All @@ -64,35 +65,48 @@ func (m *SigningMethodECDSA) Verify(signingString, signature string, key interfa
return err
}

// Get the key
var ecdsaKey *ecdsa.PublicKey
switch k := key.(type) {
case *ecdsa.PublicKey:
ecdsaKey = k
default:
return ErrInvalidKeyType
}

if len(sig) != 2*m.KeySize {
return ErrECDSAVerification
}

r := big.NewInt(0).SetBytes(sig[:m.KeySize])
s := big.NewInt(0).SetBytes(sig[m.KeySize:])

// Create hasher
if !m.Hash.Available() {
return ErrHashUnavailable
}
hasher := m.Hash.New()
hasher.Write([]byte(signingString))

// Verify the signature
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
return nil
} else {
f := func(ecdsaKey *ecdsa.PublicKey) error {
// Create hasher
hasher := m.Hash.New()
hasher.Write([]byte(signingString))

// Verify the signature
if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true {
return nil
}
return ErrECDSAVerification
}

// Get the key
switch v := key.(type) {
case *ecdsa.PublicKey:
return f(v)
case []*ecdsa.PublicKey:
if len(v) == 0 {
return ErrInvalidKeyType
}
var lastErr error
for _, ecdsaKey := range v {
lastErr = f(ecdsaKey)
if lastErr == nil {
return nil
}
}
return lastErr
default:
return ErrInvalidKeyType
}
}

// Implements the Sign method from SigningMethod
Expand Down
47 changes: 32 additions & 15 deletions hmac.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,6 @@ func (m *SigningMethodHMAC) Alg() string {

// Verify the signature of HSXXX tokens. Returns nil if the signature is valid.
func (m *SigningMethodHMAC) Verify(signingString, signature string, key interface{}) error {
// Verify the key is the right type
keyBytes, ok := key.([]byte)
if !ok {
return ErrInvalidKeyType
}

// Decode signature, for comparison
sig, err := DecodeSegment(signature)
if err != nil {
Expand All @@ -64,17 +58,40 @@ func (m *SigningMethodHMAC) Verify(signingString, signature string, key interfac
return ErrHashUnavailable
}

// This signing method is symmetric, so we validate the signature
// by reproducing the signature from the signing string and key, then
// comparing that against the provided signature.
hasher := hmac.New(m.Hash.New, keyBytes)
hasher.Write([]byte(signingString))
if !hmac.Equal(sig, hasher.Sum(nil)) {
return ErrSignatureInvalid
// verifications to be done with each key.
f := func(keyBytes []byte) error {
// This signing method is symmetric, so we validate the signature
// by reproducing the signature from the signing string and key, then
// comparing that against the provided signature.
hasher := hmac.New(m.Hash.New, keyBytes)
hasher.Write([]byte(signingString))
if !hmac.Equal(sig, hasher.Sum(nil)) {
return ErrSignatureInvalid
}

// No validation errors. Signature is good.
return nil
}

// No validation errors. Signature is good.
return nil
// Verify the key is the right type
switch v := key.(type) {
case []byte:
return f(v)
case [][]byte:
if len(v) == 0 {
return ErrInvalidKeyType
}
var lastErr error
for _, keyBytes := range v {
lastErr = f(keyBytes)
if lastErr == nil {
return nil
}
}
return lastErr
default:
return ErrInvalidKeyType
}
}

// Implements the Sign method from SigningMethod for this signing method.
Expand Down
42 changes: 29 additions & 13 deletions rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ func (m *SigningMethodRSA) Alg() string {
}

// Implements the Verify method from SigningMethod
// For this signing method, must be an *rsa.PublicKey structure.
// For this signing method, key must be in types of either *rsa.PublicKey or
// []*rsa.PublicKey (for rotation keys).
func (m *SigningMethodRSA) Verify(signingString, signature string, key interface{}) error {
var err error

Expand All @@ -55,22 +56,37 @@ func (m *SigningMethodRSA) Verify(signingString, signature string, key interface
return err
}

var rsaKey *rsa.PublicKey
var ok bool

if rsaKey, ok = key.(*rsa.PublicKey); !ok {
return ErrInvalidKeyType
}

// Create hasher
if !m.Hash.Available() {
return ErrHashUnavailable
}
hasher := m.Hash.New()
hasher.Write([]byte(signingString))

// Verify the signature
return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig)
f := func(rsaKey *rsa.PublicKey) error {
// Create hasher
hasher := m.Hash.New()
hasher.Write([]byte(signingString))

// Verify the signature
return rsa.VerifyPKCS1v15(rsaKey, m.Hash, hasher.Sum(nil), sig)
}

switch v := key.(type) {
case *rsa.PublicKey:
return f(v)
case []*rsa.PublicKey:
if len(v) == 0 {
return ErrInvalidKeyType
}
var lastErr error
for _, rsaKey := range v {
lastErr = f(rsaKey)
if lastErr == nil {
return nil
}
}
return lastErr
default:
return ErrInvalidKeyType
}
}

// Implements the Sign method from SigningMethod
Expand Down
47 changes: 31 additions & 16 deletions rsa_pss.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ func init() {
}

// Implements the Verify method from SigningMethod
// For this verify method, key must be an rsa.PublicKey struct
// For this verify method, key must be in the types of either *rsa.PublicKey or
// []*rsa.PublicKey (for rotation keys).
func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interface{}) error {
var err error

Expand All @@ -90,27 +91,41 @@ func (m *SigningMethodRSAPSS) Verify(signingString, signature string, key interf
return err
}

var rsaKey *rsa.PublicKey
switch k := key.(type) {
case *rsa.PublicKey:
rsaKey = k
default:
return ErrInvalidKey
}

// Create hasher
if !m.Hash.Available() {
return ErrHashUnavailable
}
hasher := m.Hash.New()
hasher.Write([]byte(signingString))

opts := m.Options
if m.VerifyOptions != nil {
opts = m.VerifyOptions
f := func(rsaKey *rsa.PublicKey) error {
// Create hasher
hasher := m.Hash.New()
hasher.Write([]byte(signingString))

opts := m.Options
if m.VerifyOptions != nil {
opts = m.VerifyOptions
}

return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts)
}

return rsa.VerifyPSS(rsaKey, m.Hash, hasher.Sum(nil), sig, opts)
switch v := key.(type) {
case *rsa.PublicKey:
return f(v)
case []*rsa.PublicKey:
if len(v) == 0 {
return ErrInvalidKeyType
}
var lastErr error
for _, rsaKey := range v {
lastErr = f(rsaKey)
if lastErr == nil {
return nil
}
}
return lastErr
default:
return ErrInvalidKey
}
}

// Implements the Sign method from SigningMethod
Expand Down

0 comments on commit 3718e07

Please sign in to comment.