Initial QSfera import
This commit is contained in:
+21
@@ -0,0 +1,21 @@
|
||||
load("@rules_go//go:def.bzl", "go_library")
|
||||
|
||||
go_library(
|
||||
name = "legacy",
|
||||
srcs = [
|
||||
"ecdsa.go",
|
||||
"eddsa.go",
|
||||
"hmac.go",
|
||||
"legacy.go",
|
||||
"rsa.go",
|
||||
],
|
||||
importpath = "github.com/lestrrat-go/jwx/v3/jws/legacy",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//internal/ecutil",
|
||||
"//internal/keyconv",
|
||||
"//internal/pool",
|
||||
"//jwa",
|
||||
"//jws/internal/keytype",
|
||||
],
|
||||
)
|
||||
+204
@@ -0,0 +1,204 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"encoding/asn1"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/ecutil"
|
||||
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
|
||||
"github.com/lestrrat-go/jwx/v3/internal/pool"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
"github.com/lestrrat-go/jwx/v3/jws/internal/keytype"
|
||||
)
|
||||
|
||||
var ecdsaSigners = make(map[jwa.SignatureAlgorithm]*ecdsaSigner)
|
||||
var ecdsaVerifiers = make(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,
|
||||
}
|
||||
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 (es ecdsaSigner) Algorithm() jwa.SignatureAlgorithm {
|
||||
return es.alg
|
||||
}
|
||||
|
||||
func (es *ecdsaSigner) Sign(payload []byte, key any) ([]byte, error) {
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf(`missing private key while signing payload`)
|
||||
}
|
||||
|
||||
h := es.hash.New()
|
||||
if _, err := h.Write(payload); err != nil {
|
||||
return nil, fmt.Errorf(`failed to write payload using ecdsa: %w`, err)
|
||||
}
|
||||
|
||||
signer, ok := key.(crypto.Signer)
|
||||
if ok {
|
||||
if !keytype.IsValidECDSAKey(key) {
|
||||
return nil, fmt.Errorf(`cannot use key of type %T to generate ECDSA based signatures`, key)
|
||||
}
|
||||
switch key.(type) {
|
||||
case ecdsa.PrivateKey, *ecdsa.PrivateKey:
|
||||
// if it's a ecdsa.PrivateKey, it's more efficient to
|
||||
// go through the non-crypto.Signer route. Set ok to false
|
||||
ok = false
|
||||
}
|
||||
}
|
||||
|
||||
var r, s *big.Int
|
||||
var curveBits int
|
||||
if ok {
|
||||
signed, err := signer.Sign(rand.Reader, h.Sum(nil), es.hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var p struct {
|
||||
R *big.Int
|
||||
S *big.Int
|
||||
}
|
||||
if _, err := asn1.Unmarshal(signed, &p); err != nil {
|
||||
return nil, fmt.Errorf(`failed to unmarshal ASN1 encoded signature: %w`, err)
|
||||
}
|
||||
|
||||
// Okay, this is silly, but hear me out. When we use the
|
||||
// crypto.Signer interface, the PrivateKey is hidden.
|
||||
// But we need some information about the key (its bit size).
|
||||
//
|
||||
// So while silly, we're going to have to make another call
|
||||
// here and fetch the Public key.
|
||||
// This probably means that this should be cached some where.
|
||||
cpub := signer.Public()
|
||||
pubkey, ok := cpub.(*ecdsa.PublicKey)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf(`expected *ecdsa.PublicKey, got %T`, pubkey)
|
||||
}
|
||||
curveBits = pubkey.Curve.Params().BitSize
|
||||
|
||||
r = p.R
|
||||
s = p.S
|
||||
} else {
|
||||
var privkey ecdsa.PrivateKey
|
||||
if err := keyconv.ECDSAPrivateKey(&privkey, key); err != nil {
|
||||
return nil, fmt.Errorf(`failed to retrieve ecdsa.PrivateKey out of %T: %w`, key, err)
|
||||
}
|
||||
curveBits = privkey.Curve.Params().BitSize
|
||||
rtmp, stmp, err := ecdsa.Sign(rand.Reader, &privkey, h.Sum(nil))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(`failed to sign payload using ecdsa: %w`, err)
|
||||
}
|
||||
r = rtmp
|
||||
s = stmp
|
||||
}
|
||||
|
||||
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 any) error {
|
||||
if key == nil {
|
||||
return fmt.Errorf(`missing public key while verifying payload`)
|
||||
}
|
||||
|
||||
var pubkey ecdsa.PublicKey
|
||||
if cs, ok := key.(crypto.Signer); ok {
|
||||
cpub := cs.Public()
|
||||
switch cpub := cpub.(type) {
|
||||
case ecdsa.PublicKey:
|
||||
pubkey = cpub
|
||||
case *ecdsa.PublicKey:
|
||||
pubkey = *cpub
|
||||
default:
|
||||
return fmt.Errorf(`failed to retrieve ecdsa.PublicKey out of crypto.Signer %T`, key)
|
||||
}
|
||||
} else {
|
||||
if err := keyconv.ECDSAPublicKey(&pubkey, key); err != nil {
|
||||
return fmt.Errorf(`failed to retrieve ecdsa.PublicKey out of %T: %w`, key, err)
|
||||
}
|
||||
}
|
||||
|
||||
if !pubkey.Curve.IsOnCurve(pubkey.X, pubkey.Y) {
|
||||
return fmt.Errorf(`public key used does not contain a point (X,Y) on the curve`)
|
||||
}
|
||||
|
||||
r := pool.BigInt().Get()
|
||||
s := pool.BigInt().Get()
|
||||
defer pool.BigInt().Put(r)
|
||||
defer pool.BigInt().Put(s)
|
||||
|
||||
keySize := ecutil.CalculateKeySize(pubkey.Curve)
|
||||
if len(signature) != keySize*2 {
|
||||
return fmt.Errorf(`invalid signature length for curve %q`, pubkey.Curve.Params().Name)
|
||||
}
|
||||
|
||||
r.SetBytes(signature[:keySize])
|
||||
s.SetBytes(signature[keySize:])
|
||||
|
||||
h := v.hash.New()
|
||||
if _, err := h.Write(payload); err != nil {
|
||||
return fmt.Errorf(`failed to write payload using ecdsa: %w`, err)
|
||||
}
|
||||
|
||||
if !ecdsa.Verify(&pubkey, h.Sum(nil), r, s) {
|
||||
return fmt.Errorf(`failed to verify signature using ecdsa`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"fmt"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
"github.com/lestrrat-go/jwx/v3/jws/internal/keytype"
|
||||
)
|
||||
|
||||
type eddsaSigner struct{}
|
||||
|
||||
func NewEdDSASigner() Signer {
|
||||
return &eddsaSigner{}
|
||||
}
|
||||
|
||||
func (s eddsaSigner) Algorithm() jwa.SignatureAlgorithm {
|
||||
return jwa.EdDSA()
|
||||
}
|
||||
|
||||
func (s eddsaSigner) Sign(payload []byte, key any) ([]byte, error) {
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf(`missing private key while signing payload`)
|
||||
}
|
||||
|
||||
// The ed25519.PrivateKey object implements crypto.Signer, so we should
|
||||
// simply accept a crypto.Signer here.
|
||||
signer, ok := key.(crypto.Signer)
|
||||
if ok {
|
||||
if !keytype.IsValidEDDSAKey(key) {
|
||||
return nil, fmt.Errorf(`cannot use key of type %T to generate EdDSA based signatures`, key)
|
||||
}
|
||||
} else {
|
||||
// This fallback exists for cases when jwk.Key was passed, or
|
||||
// users gave us a pointer instead of non-pointer, etc.
|
||||
var privkey ed25519.PrivateKey
|
||||
if err := keyconv.Ed25519PrivateKey(&privkey, key); err != nil {
|
||||
return nil, fmt.Errorf(`failed to retrieve ed25519.PrivateKey out of %T: %w`, key, err)
|
||||
}
|
||||
signer = privkey
|
||||
}
|
||||
|
||||
return signer.Sign(rand.Reader, payload, crypto.Hash(0))
|
||||
}
|
||||
|
||||
type eddsaVerifier struct{}
|
||||
|
||||
func NewEdDSAVerifier() Verifier {
|
||||
return &eddsaVerifier{}
|
||||
}
|
||||
|
||||
func (v eddsaVerifier) Verify(payload, signature []byte, key any) (err error) {
|
||||
if key == nil {
|
||||
return fmt.Errorf(`missing public key while verifying payload`)
|
||||
}
|
||||
|
||||
var pubkey ed25519.PublicKey
|
||||
signer, ok := key.(crypto.Signer)
|
||||
if ok {
|
||||
v := signer.Public()
|
||||
pubkey, ok = v.(ed25519.PublicKey)
|
||||
if !ok {
|
||||
return fmt.Errorf(`expected crypto.Signer.Public() to return ed25519.PublicKey, but got %T`, v)
|
||||
}
|
||||
} else {
|
||||
if err := keyconv.Ed25519PublicKey(&pubkey, key); err != nil {
|
||||
return fmt.Errorf(`failed to retrieve ed25519.PublicKey out of %T: %w`, key, err)
|
||||
}
|
||||
}
|
||||
|
||||
if !ed25519.Verify(pubkey, payload, signature) {
|
||||
return fmt.Errorf(`failed to match EdDSA signature`)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"fmt"
|
||||
"hash"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
)
|
||||
|
||||
func init() {
|
||||
algs := map[jwa.SignatureAlgorithm]func() hash.Hash{
|
||||
jwa.HS256(): sha256.New,
|
||||
jwa.HS384(): sha512.New384,
|
||||
jwa.HS512(): sha512.New,
|
||||
}
|
||||
|
||||
for alg, h := range algs {
|
||||
hmacSignFuncs[alg] = makeHMACSignFunc(h)
|
||||
}
|
||||
}
|
||||
|
||||
// HMACSigner uses crypto/hmac to sign the payloads.
|
||||
// This is for legacy support only.
|
||||
type HMACSigner struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
sign hmacSignFunc
|
||||
}
|
||||
|
||||
type HMACVerifier struct {
|
||||
signer Signer
|
||||
}
|
||||
|
||||
type hmacSignFunc func(payload []byte, key []byte) ([]byte, error)
|
||||
|
||||
var hmacSignFuncs = make(map[jwa.SignatureAlgorithm]hmacSignFunc)
|
||||
|
||||
func NewHMACSigner(alg jwa.SignatureAlgorithm) Signer {
|
||||
return &HMACSigner{
|
||||
alg: alg,
|
||||
sign: hmacSignFuncs[alg], // we know this will succeed
|
||||
}
|
||||
}
|
||||
|
||||
func makeHMACSignFunc(hfunc func() hash.Hash) hmacSignFunc {
|
||||
return func(payload []byte, key []byte) ([]byte, error) {
|
||||
h := hmac.New(hfunc, key)
|
||||
if _, err := h.Write(payload); err != nil {
|
||||
return nil, fmt.Errorf(`failed to write payload using hmac: %w`, err)
|
||||
}
|
||||
return h.Sum(nil), nil
|
||||
}
|
||||
}
|
||||
|
||||
func (s HMACSigner) Algorithm() jwa.SignatureAlgorithm {
|
||||
return s.alg
|
||||
}
|
||||
|
||||
func (s HMACSigner) Sign(payload []byte, key any) ([]byte, error) {
|
||||
var hmackey []byte
|
||||
if err := keyconv.ByteSliceKey(&hmackey, key); err != nil {
|
||||
return nil, fmt.Errorf(`invalid key type %T. []byte is required: %w`, key, err)
|
||||
}
|
||||
|
||||
if len(hmackey) == 0 {
|
||||
return nil, fmt.Errorf(`missing key while signing payload`)
|
||||
}
|
||||
|
||||
return s.sign(payload, hmackey)
|
||||
}
|
||||
|
||||
func NewHMACVerifier(alg jwa.SignatureAlgorithm) Verifier {
|
||||
s := NewHMACSigner(alg)
|
||||
return &HMACVerifier{signer: s}
|
||||
}
|
||||
|
||||
func (v HMACVerifier) Verify(payload, signature []byte, key any) (err error) {
|
||||
expected, err := v.signer.Sign(payload, key)
|
||||
if err != nil {
|
||||
return fmt.Errorf(`failed to generated signature: %w`, err)
|
||||
}
|
||||
|
||||
if !hmac.Equal(signature, expected) {
|
||||
return fmt.Errorf(`failed to match hmac signature`)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
// Package legacy provides support for legacy implementation of JWS signing and verification.
|
||||
// Types, functions, and variables in this package are exported only for legacy support,
|
||||
// and should not be relied upon for new code.
|
||||
//
|
||||
// This package will be available until v3 is sunset, but it will be removed in v4
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
)
|
||||
|
||||
// Signer generates the signature for a given payload.
|
||||
// This is for legacy support only.
|
||||
type Signer interface {
|
||||
// Sign creates a signature for the given payload.
|
||||
// The second argument is the key used for signing the payload, and is usually
|
||||
// the private key type associated with the signature method. For example,
|
||||
// for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the
|
||||
// `*"crypto/rsa".PrivateKey` type.
|
||||
// Check the documentation for each signer for details
|
||||
Sign([]byte, any) ([]byte, error)
|
||||
|
||||
Algorithm() jwa.SignatureAlgorithm
|
||||
}
|
||||
|
||||
// Verifier is for legacy support only.
|
||||
type Verifier interface {
|
||||
// Verify checks whether the payload and signature are valid for
|
||||
// the given key.
|
||||
// `key` is the key used for verifying the payload, and is usually
|
||||
// the public key associated with the signature method. For example,
|
||||
// for `jwa.RSXXX` and `jwa.PSXXX` types, you need to pass the
|
||||
// `*"crypto/rsa".PublicKey` type.
|
||||
// Check the documentation for each verifier for details
|
||||
Verify(payload []byte, signature []byte, key any) error
|
||||
}
|
||||
+145
@@ -0,0 +1,145 @@
|
||||
package legacy
|
||||
|
||||
import (
|
||||
"crypto"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"fmt"
|
||||
|
||||
"github.com/lestrrat-go/jwx/v3/internal/keyconv"
|
||||
"github.com/lestrrat-go/jwx/v3/jwa"
|
||||
"github.com/lestrrat-go/jwx/v3/jws/internal/keytype"
|
||||
)
|
||||
|
||||
var rsaSigners = make(map[jwa.SignatureAlgorithm]*rsaSigner)
|
||||
var rsaVerifiers = make(map[jwa.SignatureAlgorithm]*rsaVerifier)
|
||||
|
||||
func init() {
|
||||
data := map[jwa.SignatureAlgorithm]struct {
|
||||
Hash crypto.Hash
|
||||
PSS bool
|
||||
}{
|
||||
jwa.RS256(): {
|
||||
Hash: crypto.SHA256,
|
||||
},
|
||||
jwa.RS384(): {
|
||||
Hash: crypto.SHA384,
|
||||
},
|
||||
jwa.RS512(): {
|
||||
Hash: crypto.SHA512,
|
||||
},
|
||||
jwa.PS256(): {
|
||||
Hash: crypto.SHA256,
|
||||
PSS: true,
|
||||
},
|
||||
jwa.PS384(): {
|
||||
Hash: crypto.SHA384,
|
||||
PSS: true,
|
||||
},
|
||||
jwa.PS512(): {
|
||||
Hash: crypto.SHA512,
|
||||
PSS: true,
|
||||
},
|
||||
}
|
||||
|
||||
for alg, item := range data {
|
||||
rsaSigners[alg] = &rsaSigner{
|
||||
alg: alg,
|
||||
hash: item.Hash,
|
||||
pss: item.PSS,
|
||||
}
|
||||
rsaVerifiers[alg] = &rsaVerifier{
|
||||
alg: alg,
|
||||
hash: item.Hash,
|
||||
pss: item.PSS,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type rsaSigner struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
hash crypto.Hash
|
||||
pss bool
|
||||
}
|
||||
|
||||
func NewRSASigner(alg jwa.SignatureAlgorithm) Signer {
|
||||
return rsaSigners[alg]
|
||||
}
|
||||
|
||||
func (rs *rsaSigner) Algorithm() jwa.SignatureAlgorithm {
|
||||
return rs.alg
|
||||
}
|
||||
|
||||
func (rs *rsaSigner) Sign(payload []byte, key any) ([]byte, error) {
|
||||
if key == nil {
|
||||
return nil, fmt.Errorf(`missing private key while signing payload`)
|
||||
}
|
||||
|
||||
signer, ok := key.(crypto.Signer)
|
||||
if ok {
|
||||
if !keytype.IsValidRSAKey(key) {
|
||||
return nil, fmt.Errorf(`cannot use key of type %T to generate RSA based signatures`, key)
|
||||
}
|
||||
} else {
|
||||
var privkey rsa.PrivateKey
|
||||
if err := keyconv.RSAPrivateKey(&privkey, key); err != nil {
|
||||
return nil, fmt.Errorf(`failed to retrieve rsa.PrivateKey out of %T: %w`, key, err)
|
||||
}
|
||||
signer = &privkey
|
||||
}
|
||||
|
||||
h := rs.hash.New()
|
||||
if _, err := h.Write(payload); err != nil {
|
||||
return nil, fmt.Errorf(`failed to write payload to hash: %w`, err)
|
||||
}
|
||||
if rs.pss {
|
||||
return signer.Sign(rand.Reader, h.Sum(nil), &rsa.PSSOptions{
|
||||
Hash: rs.hash,
|
||||
SaltLength: rsa.PSSSaltLengthEqualsHash,
|
||||
})
|
||||
}
|
||||
return signer.Sign(rand.Reader, h.Sum(nil), rs.hash)
|
||||
}
|
||||
|
||||
type rsaVerifier struct {
|
||||
alg jwa.SignatureAlgorithm
|
||||
hash crypto.Hash
|
||||
pss bool
|
||||
}
|
||||
|
||||
func NewRSAVerifier(alg jwa.SignatureAlgorithm) Verifier {
|
||||
return rsaVerifiers[alg]
|
||||
}
|
||||
|
||||
func (rv *rsaVerifier) Verify(payload, signature []byte, key any) error {
|
||||
if key == nil {
|
||||
return fmt.Errorf(`missing public key while verifying payload`)
|
||||
}
|
||||
|
||||
var pubkey rsa.PublicKey
|
||||
if cs, ok := key.(crypto.Signer); ok {
|
||||
cpub := cs.Public()
|
||||
switch cpub := cpub.(type) {
|
||||
case rsa.PublicKey:
|
||||
pubkey = cpub
|
||||
case *rsa.PublicKey:
|
||||
pubkey = *cpub
|
||||
default:
|
||||
return fmt.Errorf(`failed to retrieve rsa.PublicKey out of crypto.Signer %T`, key)
|
||||
}
|
||||
} else {
|
||||
if err := keyconv.RSAPublicKey(&pubkey, key); err != nil {
|
||||
return fmt.Errorf(`failed to retrieve rsa.PublicKey out of %T: %w`, key, err)
|
||||
}
|
||||
}
|
||||
|
||||
h := rv.hash.New()
|
||||
if _, err := h.Write(payload); err != nil {
|
||||
return fmt.Errorf(`failed to write payload to hash: %w`, err)
|
||||
}
|
||||
|
||||
if rv.pss {
|
||||
return rsa.VerifyPSS(&pubkey, rv.hash, h.Sum(nil), signature, nil)
|
||||
}
|
||||
return rsa.VerifyPKCS1v15(&pubkey, rv.hash, h.Sum(nil), signature)
|
||||
}
|
||||
Reference in New Issue
Block a user