1package jwt 2 3import ( 4 "crypto" 5 "crypto/ecdsa" 6 "crypto/rand" 7 "errors" 8 "math/big" 9) 10 11var ( 12 // Sadly this is missing from crypto/ecdsa compared to crypto/rsa 13 ErrECDSAVerification = errors.New("crypto/ecdsa: verification error") 14) 15 16// Implements the ECDSA family of signing methods signing methods 17// Expects *ecdsa.PrivateKey for signing and *ecdsa.PublicKey for verification 18type SigningMethodECDSA struct { 19 Name string 20 Hash crypto.Hash 21 KeySize int 22 CurveBits int 23} 24 25// Specific instances for EC256 and company 26var ( 27 SigningMethodES256 *SigningMethodECDSA 28 SigningMethodES384 *SigningMethodECDSA 29 SigningMethodES512 *SigningMethodECDSA 30) 31 32func init() { 33 // ES256 34 SigningMethodES256 = &SigningMethodECDSA{"ES256", crypto.SHA256, 32, 256} 35 RegisterSigningMethod(SigningMethodES256.Alg(), func() SigningMethod { 36 return SigningMethodES256 37 }) 38 39 // ES384 40 SigningMethodES384 = &SigningMethodECDSA{"ES384", crypto.SHA384, 48, 384} 41 RegisterSigningMethod(SigningMethodES384.Alg(), func() SigningMethod { 42 return SigningMethodES384 43 }) 44 45 // ES512 46 SigningMethodES512 = &SigningMethodECDSA{"ES512", crypto.SHA512, 66, 521} 47 RegisterSigningMethod(SigningMethodES512.Alg(), func() SigningMethod { 48 return SigningMethodES512 49 }) 50} 51 52func (m *SigningMethodECDSA) Alg() string { 53 return m.Name 54} 55 56// Implements the Verify method from SigningMethod 57// For this verify method, key must be an ecdsa.PublicKey struct 58func (m *SigningMethodECDSA) Verify(signingString, signature string, key interface{}) error { 59 var err error 60 61 // Decode the signature 62 var sig []byte 63 if sig, err = DecodeSegment(signature); err != nil { 64 return err 65 } 66 67 // Get the key 68 var ecdsaKey *ecdsa.PublicKey 69 switch k := key.(type) { 70 case *ecdsa.PublicKey: 71 ecdsaKey = k 72 default: 73 return ErrInvalidKeyType 74 } 75 76 if len(sig) != 2*m.KeySize { 77 return ErrECDSAVerification 78 } 79 80 r := big.NewInt(0).SetBytes(sig[:m.KeySize]) 81 s := big.NewInt(0).SetBytes(sig[m.KeySize:]) 82 83 // Create hasher 84 if !m.Hash.Available() { 85 return ErrHashUnavailable 86 } 87 hasher := m.Hash.New() 88 hasher.Write([]byte(signingString)) 89 90 // Verify the signature 91 if verifystatus := ecdsa.Verify(ecdsaKey, hasher.Sum(nil), r, s); verifystatus == true { 92 return nil 93 } else { 94 return ErrECDSAVerification 95 } 96} 97 98// Implements the Sign method from SigningMethod 99// For this signing method, key must be an ecdsa.PrivateKey struct 100func (m *SigningMethodECDSA) Sign(signingString string, key interface{}) (string, error) { 101 // Get the key 102 var ecdsaKey *ecdsa.PrivateKey 103 switch k := key.(type) { 104 case *ecdsa.PrivateKey: 105 ecdsaKey = k 106 default: 107 return "", ErrInvalidKeyType 108 } 109 110 // Create the hasher 111 if !m.Hash.Available() { 112 return "", ErrHashUnavailable 113 } 114 115 hasher := m.Hash.New() 116 hasher.Write([]byte(signingString)) 117 118 // Sign the string and return r, s 119 if r, s, err := ecdsa.Sign(rand.Reader, ecdsaKey, hasher.Sum(nil)); err == nil { 120 curveBits := ecdsaKey.Curve.Params().BitSize 121 122 if m.CurveBits != curveBits { 123 return "", ErrInvalidKey 124 } 125 126 keyBytes := curveBits / 8 127 if curveBits%8 > 0 { 128 keyBytes += 1 129 } 130 131 // We serialize the outpus (r and s) into big-endian byte arrays and pad 132 // them with zeros on the left to make sure the sizes work out. Both arrays 133 // must be keyBytes long, and the output must be 2*keyBytes long. 134 rBytes := r.Bytes() 135 rBytesPadded := make([]byte, keyBytes) 136 copy(rBytesPadded[keyBytes-len(rBytes):], rBytes) 137 138 sBytes := s.Bytes() 139 sBytesPadded := make([]byte, keyBytes) 140 copy(sBytesPadded[keyBytes-len(sBytes):], sBytes) 141 142 out := append(rBytesPadded, sBytesPadded...) 143 144 return EncodeSegment(out), nil 145 } else { 146 return "", err 147 } 148} 149