1/*- 2 * Copyright 2014 Square Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package jose 18 19import ( 20 "crypto/ecdsa" 21 "crypto/rsa" 22 "errors" 23 "fmt" 24) 25 26// NonceSource represents a source of random nonces to go into JWS objects 27type NonceSource interface { 28 Nonce() (string, error) 29} 30 31// Signer represents a signer which takes a payload and produces a signed JWS object. 32type Signer interface { 33 Sign(payload []byte) (*JsonWebSignature, error) 34 SetNonceSource(source NonceSource) 35 SetEmbedJwk(embed bool) 36} 37 38// MultiSigner represents a signer which supports multiple recipients. 39type MultiSigner interface { 40 Sign(payload []byte) (*JsonWebSignature, error) 41 SetNonceSource(source NonceSource) 42 SetEmbedJwk(embed bool) 43 AddRecipient(alg SignatureAlgorithm, signingKey interface{}) error 44} 45 46type payloadSigner interface { 47 signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) 48} 49 50type payloadVerifier interface { 51 verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error 52} 53 54type genericSigner struct { 55 recipients []recipientSigInfo 56 nonceSource NonceSource 57 embedJwk bool 58} 59 60type recipientSigInfo struct { 61 sigAlg SignatureAlgorithm 62 keyID string 63 publicKey *JsonWebKey 64 signer payloadSigner 65} 66 67// NewSigner creates an appropriate signer based on the key type 68func NewSigner(alg SignatureAlgorithm, signingKey interface{}) (Signer, error) { 69 // NewMultiSigner never fails (currently) 70 signer := NewMultiSigner() 71 72 err := signer.AddRecipient(alg, signingKey) 73 if err != nil { 74 return nil, err 75 } 76 77 return signer, nil 78} 79 80// NewMultiSigner creates a signer for multiple recipients 81func NewMultiSigner() MultiSigner { 82 return &genericSigner{ 83 recipients: []recipientSigInfo{}, 84 embedJwk: true, 85 } 86} 87 88// newVerifier creates a verifier based on the key type 89func newVerifier(verificationKey interface{}) (payloadVerifier, error) { 90 switch verificationKey := verificationKey.(type) { 91 case *rsa.PublicKey: 92 return &rsaEncrypterVerifier{ 93 publicKey: verificationKey, 94 }, nil 95 case *ecdsa.PublicKey: 96 return &ecEncrypterVerifier{ 97 publicKey: verificationKey, 98 }, nil 99 case []byte: 100 return &symmetricMac{ 101 key: verificationKey, 102 }, nil 103 case *JsonWebKey: 104 return newVerifier(verificationKey.Key) 105 default: 106 return nil, ErrUnsupportedKeyType 107 } 108} 109 110func (ctx *genericSigner) AddRecipient(alg SignatureAlgorithm, signingKey interface{}) error { 111 recipient, err := makeJWSRecipient(alg, signingKey) 112 if err != nil { 113 return err 114 } 115 116 ctx.recipients = append(ctx.recipients, recipient) 117 return nil 118} 119 120func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSigInfo, error) { 121 switch signingKey := signingKey.(type) { 122 case *rsa.PrivateKey: 123 return newRSASigner(alg, signingKey) 124 case *ecdsa.PrivateKey: 125 return newECDSASigner(alg, signingKey) 126 case []byte: 127 return newSymmetricSigner(alg, signingKey) 128 case *JsonWebKey: 129 recipient, err := makeJWSRecipient(alg, signingKey.Key) 130 if err != nil { 131 return recipientSigInfo{}, err 132 } 133 recipient.keyID = signingKey.KeyID 134 return recipient, nil 135 default: 136 return recipientSigInfo{}, ErrUnsupportedKeyType 137 } 138} 139 140func (ctx *genericSigner) Sign(payload []byte) (*JsonWebSignature, error) { 141 obj := &JsonWebSignature{} 142 obj.payload = payload 143 obj.Signatures = make([]Signature, len(ctx.recipients)) 144 145 for i, recipient := range ctx.recipients { 146 protected := &rawHeader{ 147 Alg: string(recipient.sigAlg), 148 } 149 150 if recipient.publicKey != nil && ctx.embedJwk { 151 protected.Jwk = recipient.publicKey 152 } 153 if recipient.keyID != "" { 154 protected.Kid = recipient.keyID 155 } 156 157 if ctx.nonceSource != nil { 158 nonce, err := ctx.nonceSource.Nonce() 159 if err != nil { 160 return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err) 161 } 162 protected.Nonce = nonce 163 } 164 165 serializedProtected := mustSerializeJSON(protected) 166 167 input := []byte(fmt.Sprintf("%s.%s", 168 base64URLEncode(serializedProtected), 169 base64URLEncode(payload))) 170 171 signatureInfo, err := recipient.signer.signPayload(input, recipient.sigAlg) 172 if err != nil { 173 return nil, err 174 } 175 176 signatureInfo.protected = protected 177 obj.Signatures[i] = signatureInfo 178 } 179 180 return obj, nil 181} 182 183// SetNonceSource provides or updates a nonce pool to the first recipients. 184// After this method is called, the signer will consume one nonce per 185// signature, returning an error it is unable to get a nonce. 186func (ctx *genericSigner) SetNonceSource(source NonceSource) { 187 ctx.nonceSource = source 188} 189 190// SetEmbedJwk specifies if the signing key should be embedded in the protected 191// header, if any. It defaults to 'true', though that may change in the future. 192// Note that the use of embedded JWKs in the signature header can be dangerous, 193// as you cannot assume that the key received in a payload is trusted. 194func (ctx *genericSigner) SetEmbedJwk(embed bool) { 195 ctx.embedJwk = embed 196} 197 198// Verify validates the signature on the object and returns the payload. 199// This function does not support multi-signature, if you desire multi-sig 200// verification use VerifyMulti instead. 201// 202// Be careful when verifying signatures based on embedded JWKs inside the 203// payload header. You cannot assume that the key received in a payload is 204// trusted. 205func (obj JsonWebSignature) Verify(verificationKey interface{}) ([]byte, error) { 206 verifier, err := newVerifier(verificationKey) 207 if err != nil { 208 return nil, err 209 } 210 211 if len(obj.Signatures) > 1 { 212 return nil, errors.New("square/go-jose: too many signatures in payload; expecting only one") 213 } 214 215 signature := obj.Signatures[0] 216 headers := signature.mergedHeaders() 217 if len(headers.Crit) > 0 { 218 // Unsupported crit header 219 return nil, ErrCryptoFailure 220 } 221 222 input := obj.computeAuthData(&signature) 223 alg := SignatureAlgorithm(headers.Alg) 224 err = verifier.verifyPayload(input, signature.Signature, alg) 225 if err == nil { 226 return obj.payload, nil 227 } 228 229 return nil, ErrCryptoFailure 230} 231 232// VerifyMulti validates (one of the multiple) signatures on the object and 233// returns the index of the signature that was verified, along with the signature 234// object and the payload. We return the signature and index to guarantee that 235// callers are getting the verified value. 236func (obj JsonWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) { 237 verifier, err := newVerifier(verificationKey) 238 if err != nil { 239 return -1, Signature{}, nil, err 240 } 241 242 for i, signature := range obj.Signatures { 243 headers := signature.mergedHeaders() 244 if len(headers.Crit) > 0 { 245 // Unsupported crit header 246 continue 247 } 248 249 input := obj.computeAuthData(&signature) 250 alg := SignatureAlgorithm(headers.Alg) 251 err := verifier.verifyPayload(input, signature.Signature, alg) 252 if err == nil { 253 return i, signature, obj.payload, nil 254 } 255 } 256 257 return -1, Signature{}, nil, ErrCryptoFailure 258} 259