...

Source file src/github.com/lestrrat-go/jwx/jws/ecdsa.go

Documentation: github.com/lestrrat-go/jwx/jws

     1  package jws
     2  
     3  import (
     4  	"crypto"
     5  	"crypto/ecdsa"
     6  	"crypto/rand"
     7  	"encoding/asn1"
     8  	"fmt"
     9  	"math/big"
    10  
    11  	"github.com/lestrrat-go/jwx/internal/keyconv"
    12  	"github.com/lestrrat-go/jwx/internal/pool"
    13  	"github.com/lestrrat-go/jwx/jwa"
    14  	"github.com/pkg/errors"
    15  )
    16  
    17  var ecdsaSigners map[jwa.SignatureAlgorithm]*ecdsaSigner
    18  var ecdsaVerifiers map[jwa.SignatureAlgorithm]*ecdsaVerifier
    19  
    20  func init() {
    21  	algs := map[jwa.SignatureAlgorithm]crypto.Hash{
    22  		jwa.ES256:  crypto.SHA256,
    23  		jwa.ES384:  crypto.SHA384,
    24  		jwa.ES512:  crypto.SHA512,
    25  		jwa.ES256K: crypto.SHA256,
    26  	}
    27  	ecdsaSigners = make(map[jwa.SignatureAlgorithm]*ecdsaSigner)
    28  	ecdsaVerifiers = make(map[jwa.SignatureAlgorithm]*ecdsaVerifier)
    29  
    30  	for alg, hash := range algs {
    31  		ecdsaSigners[alg] = &ecdsaSigner{
    32  			alg:  alg,
    33  			hash: hash,
    34  		}
    35  		ecdsaVerifiers[alg] = &ecdsaVerifier{
    36  			alg:  alg,
    37  			hash: hash,
    38  		}
    39  	}
    40  }
    41  
    42  func newECDSASigner(alg jwa.SignatureAlgorithm) Signer {
    43  	return ecdsaSigners[alg]
    44  }
    45  
    46  // ecdsaSigners are immutable.
    47  type ecdsaSigner struct {
    48  	alg  jwa.SignatureAlgorithm
    49  	hash crypto.Hash
    50  }
    51  
    52  func (es ecdsaSigner) Algorithm() jwa.SignatureAlgorithm {
    53  	return es.alg
    54  }
    55  
    56  func (es *ecdsaSigner) Sign(payload []byte, key interface{}) ([]byte, error) {
    57  	if key == nil {
    58  		return nil, errors.New(`missing private key while signing payload`)
    59  	}
    60  
    61  	h := es.hash.New()
    62  	if _, err := h.Write(payload); err != nil {
    63  		return nil, errors.Wrap(err, "failed to write payload using ecdsa")
    64  	}
    65  
    66  	signer, ok := key.(crypto.Signer)
    67  	if ok {
    68  		switch key.(type) {
    69  		case ecdsa.PrivateKey, *ecdsa.PrivateKey:
    70  			// if it's a ecdsa.PrivateKey, it's more efficient to
    71  			// go through the non-crypto.Signer route. Set ok to false
    72  			ok = false
    73  		}
    74  	}
    75  
    76  	var r, s *big.Int
    77  	var curveBits int
    78  	if ok {
    79  		signed, err := signer.Sign(rand.Reader, h.Sum(nil), es.hash)
    80  		if err != nil {
    81  			return nil, err
    82  		}
    83  
    84  		var p struct {
    85  			R *big.Int
    86  			S *big.Int
    87  		}
    88  		if _, err := asn1.Unmarshal(signed, &p); err != nil {
    89  			return nil, errors.Wrap(err, `failed to unmarshal ASN1 encoded signature`)
    90  		}
    91  
    92  		// Okay, this is silly, but hear me out. When we use the
    93  		// crypto.Signer interface, the PrivateKey is hidden.
    94  		// But we need some information about the key (it's bit size).
    95  		//
    96  		// So while silly, we're going to have to make another call
    97  		// here and fetch the Public key.
    98  		// This probably means that this should be cached some where.
    99  		cpub := signer.Public()
   100  		pubkey, ok := cpub.(*ecdsa.PublicKey)
   101  		if !ok {
   102  			return nil, fmt.Errorf(`expected *ecdsa.PublicKey, got %T`, pubkey)
   103  		}
   104  		curveBits = pubkey.Curve.Params().BitSize
   105  
   106  		r = p.R
   107  		s = p.S
   108  	} else {
   109  		var privkey ecdsa.PrivateKey
   110  		if err := keyconv.ECDSAPrivateKey(&privkey, key); err != nil {
   111  			return nil, errors.Wrapf(err, `failed to retrieve ecdsa.PrivateKey out of %T`, key)
   112  		}
   113  		curveBits = privkey.Curve.Params().BitSize
   114  		rtmp, stmp, err := ecdsa.Sign(rand.Reader, &privkey, h.Sum(nil))
   115  		if err != nil {
   116  			return nil, errors.Wrap(err, "failed to sign payload using ecdsa")
   117  		}
   118  		r = rtmp
   119  		s = stmp
   120  	}
   121  
   122  	keyBytes := curveBits / 8
   123  	// Curve bits do not need to be a multiple of 8.
   124  	if curveBits%8 > 0 {
   125  		keyBytes++
   126  	}
   127  
   128  	rBytes := r.Bytes()
   129  	rBytesPadded := make([]byte, keyBytes)
   130  	copy(rBytesPadded[keyBytes-len(rBytes):], rBytes)
   131  
   132  	sBytes := s.Bytes()
   133  	sBytesPadded := make([]byte, keyBytes)
   134  	copy(sBytesPadded[keyBytes-len(sBytes):], sBytes)
   135  
   136  	out := append(rBytesPadded, sBytesPadded...)
   137  	return out, nil
   138  }
   139  
   140  // ecdsaVerifiers are immutable.
   141  type ecdsaVerifier struct {
   142  	alg  jwa.SignatureAlgorithm
   143  	hash crypto.Hash
   144  }
   145  
   146  func newECDSAVerifier(alg jwa.SignatureAlgorithm) Verifier {
   147  	return ecdsaVerifiers[alg]
   148  }
   149  
   150  func (v ecdsaVerifier) Algorithm() jwa.SignatureAlgorithm {
   151  	return v.alg
   152  }
   153  
   154  func (v *ecdsaVerifier) Verify(payload []byte, signature []byte, key interface{}) error {
   155  	if key == nil {
   156  		return errors.New(`missing public key while verifying payload`)
   157  	}
   158  
   159  	var pubkey ecdsa.PublicKey
   160  	if cs, ok := key.(crypto.Signer); ok {
   161  		cpub := cs.Public()
   162  		switch cpub := cpub.(type) {
   163  		case ecdsa.PublicKey:
   164  			pubkey = cpub
   165  		case *ecdsa.PublicKey:
   166  			pubkey = *cpub
   167  		default:
   168  			return errors.Errorf(`failed to retrieve ecdsa.PublicKey out of crypto.Signer %T`, key)
   169  		}
   170  	} else {
   171  		if err := keyconv.ECDSAPublicKey(&pubkey, key); err != nil {
   172  			return errors.Wrapf(err, `failed to retrieve ecdsa.PublicKey out of %T`, key)
   173  		}
   174  	}
   175  
   176  	r := pool.GetBigInt()
   177  	s := pool.GetBigInt()
   178  	defer pool.ReleaseBigInt(r)
   179  	defer pool.ReleaseBigInt(s)
   180  
   181  	n := len(signature) / 2
   182  	r.SetBytes(signature[:n])
   183  	s.SetBytes(signature[n:])
   184  
   185  	h := v.hash.New()
   186  	if _, err := h.Write(payload); err != nil {
   187  		return errors.Wrap(err, "failed to write payload using ecdsa")
   188  	}
   189  
   190  	if !ecdsa.Verify(&pubkey, h.Sum(nil), r, s) {
   191  		return errors.New(`failed to verify signature using ecdsa`)
   192  	}
   193  	return nil
   194  }
   195  

View as plain text