go-sample-webpage/vendor/github.com/lestrrat-go/jwx/jws/ecdsa.go
2021-11-04 02:14:51 +01:00

165 lines
4 KiB
Go

package jws
import (
"crypto"
"crypto/ecdsa"
"crypto/rand"
"io"
"github.com/lestrrat-go/jwx/internal/keyconv"
"github.com/lestrrat-go/jwx/internal/pool"
"github.com/lestrrat-go/jwx/jwa"
"github.com/pkg/errors"
)
var ecdsaSigners map[jwa.SignatureAlgorithm]*ecdsaSigner
var ecdsaVerifiers map[jwa.SignatureAlgorithm]*ecdsaVerifier
func init() {
algs := map[jwa.SignatureAlgorithm]crypto.Hash{
jwa.ES256: crypto.SHA256,
jwa.ES384: crypto.SHA384,
jwa.ES512: crypto.SHA512,
jwa.ES256K: crypto.SHA256,
}
ecdsaSigners = make(map[jwa.SignatureAlgorithm]*ecdsaSigner)
ecdsaVerifiers = make(map[jwa.SignatureAlgorithm]*ecdsaVerifier)
for alg, hash := range algs {
ecdsaSigners[alg] = &ecdsaSigner{
alg: alg,
hash: hash,
}
ecdsaVerifiers[alg] = &ecdsaVerifier{
alg: alg,
hash: hash,
}
}
}
func newECDSASigner(alg jwa.SignatureAlgorithm) Signer {
return ecdsaSigners[alg]
}
// ecdsaSigners are immutable.
type ecdsaSigner struct {
alg jwa.SignatureAlgorithm
hash crypto.Hash
}
func (s ecdsaSigner) Algorithm() jwa.SignatureAlgorithm {
return s.alg
}
type ecdsaCryptoSigner struct {
key *ecdsa.PrivateKey
hash crypto.Hash
}
func (s *ecdsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
if key == nil {
return nil, errors.New(`missing private key while signing payload`)
}
signer, ok := key.(crypto.Signer)
if ok {
// We support crypto.Signer, but we DON'T support
// ecdsa.PrivateKey as a crypto.Signer, because it encodes
// the result in ASN1 format.
if pk, ok := key.(*ecdsa.PrivateKey); ok {
signer = newECDSACryptoSigner(pk, s.hash)
}
} else {
var privkey ecdsa.PrivateKey
if err := keyconv.ECDSAPrivateKey(&privkey, key); err != nil {
return nil, errors.Wrapf(err, `failed to retrieve ecdsa.PrivateKey out of %T`, key)
}
signer = newECDSACryptoSigner(&privkey, s.hash)
}
h := s.hash.New()
if _, err := h.Write(payload); err != nil {
return nil, errors.Wrap(err, "failed to write payload using ecdsa")
}
return signer.Sign(rand.Reader, h.Sum(nil), s.hash)
}
func newECDSACryptoSigner(key *ecdsa.PrivateKey, hash crypto.Hash) crypto.Signer {
return &ecdsaCryptoSigner{
key: key,
hash: hash,
}
}
func (cs *ecdsaCryptoSigner) Public() crypto.PublicKey {
return cs.key.PublicKey
}
func (cs *ecdsaCryptoSigner) Sign(seed io.Reader, digest []byte, _ crypto.SignerOpts) ([]byte, error) {
r, s, err := ecdsa.Sign(seed, cs.key, digest)
if err != nil {
return nil, errors.Wrap(err, "failed to sign payload using ecdsa")
}
curveBits := cs.key.Curve.Params().BitSize
keyBytes := curveBits / 8
// Curve bits do not need to be a multiple of 8.
if curveBits%8 > 0 {
keyBytes++
}
rBytes := r.Bytes()
rBytesPadded := make([]byte, keyBytes)
copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
sBytes := s.Bytes()
sBytesPadded := make([]byte, keyBytes)
copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
out := append(rBytesPadded, sBytesPadded...)
return out, nil
}
// ecdsaVerifiers are immutable.
type ecdsaVerifier struct {
alg jwa.SignatureAlgorithm
hash crypto.Hash
}
func newECDSAVerifier(alg jwa.SignatureAlgorithm) Verifier {
return ecdsaVerifiers[alg]
}
func (v ecdsaVerifier) Algorithm() jwa.SignatureAlgorithm {
return v.alg
}
func (v *ecdsaVerifier) Verify(payload []byte, signature []byte, key interface{}) error {
if key == nil {
return errors.New(`missing public key while verifying payload`)
}
var pubkey ecdsa.PublicKey
if err := keyconv.ECDSAPublicKey(&pubkey, key); err != nil {
return errors.Wrapf(err, `failed to retrieve ecdsa.PublicKey out of %T`, key)
}
r := pool.GetBigInt()
s := pool.GetBigInt()
defer pool.ReleaseBigInt(r)
defer pool.ReleaseBigInt(s)
n := len(signature) / 2
r.SetBytes(signature[:n])
s.SetBytes(signature[n:])
h := v.hash.New()
if _, err := h.Write(payload); err != nil {
return errors.Wrap(err, "failed to write payload using ecdsa")
}
if !ecdsa.Verify(&pubkey, h.Sum(nil), r, s) {
return errors.New(`failed to verify signature using ecdsa`)
}
return nil
}