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 "bytes" 21 "crypto/ecdsa" 22 "crypto/rsa" 23 "encoding/base64" 24 "errors" 25 "fmt" 26 27 "golang.org/x/crypto/ed25519" 28 29 "gopkg.in/square/go-jose.v2/json" 30) 31 32// NonceSource represents a source of random nonces to go into JWS objects 33type NonceSource interface { 34 Nonce() (string, error) 35} 36 37// Signer represents a signer which takes a payload and produces a signed JWS object. 38type Signer interface { 39 Sign(payload []byte) (*JSONWebSignature, error) 40 Options() SignerOptions 41} 42 43// SigningKey represents an algorithm/key used to sign a message. 44type SigningKey struct { 45 Algorithm SignatureAlgorithm 46 Key interface{} 47} 48 49// SignerOptions represents options that can be set when creating signers. 50type SignerOptions struct { 51 NonceSource NonceSource 52 EmbedJWK bool 53 54 // Optional map of additional keys to be inserted into the protected header 55 // of a JWS object. Some specifications which make use of JWS like to insert 56 // additional values here. All values must be JSON-serializable. 57 ExtraHeaders map[HeaderKey]interface{} 58} 59 60// WithHeader adds an arbitrary value to the ExtraHeaders map, initializing it 61// if necessary. It returns itself and so can be used in a fluent style. 62func (so *SignerOptions) WithHeader(k HeaderKey, v interface{}) *SignerOptions { 63 if so.ExtraHeaders == nil { 64 so.ExtraHeaders = map[HeaderKey]interface{}{} 65 } 66 so.ExtraHeaders[k] = v 67 return so 68} 69 70// WithContentType adds a content type ("cty") header and returns the updated 71// SignerOptions. 72func (so *SignerOptions) WithContentType(contentType ContentType) *SignerOptions { 73 return so.WithHeader(HeaderContentType, contentType) 74} 75 76// WithType adds a type ("typ") header and returns the updated SignerOptions. 77func (so *SignerOptions) WithType(typ ContentType) *SignerOptions { 78 return so.WithHeader(HeaderType, typ) 79} 80 81// WithCritical adds the given names to the critical ("crit") header and returns 82// the updated SignerOptions. 83func (so *SignerOptions) WithCritical(names ...string) *SignerOptions { 84 if so.ExtraHeaders[headerCritical] == nil { 85 so.WithHeader(headerCritical, make([]string, 0, len(names))) 86 } 87 crit := so.ExtraHeaders[headerCritical].([]string) 88 so.ExtraHeaders[headerCritical] = append(crit, names...) 89 return so 90} 91 92// WithBase64 adds a base64url-encode payload ("b64") header and returns the updated 93// SignerOptions. When the "b64" value is "false", the payload is not base64 encoded. 94func (so *SignerOptions) WithBase64(b64 bool) *SignerOptions { 95 if !b64 { 96 so.WithHeader(headerB64, b64) 97 so.WithCritical(headerB64) 98 } 99 return so 100} 101 102type payloadSigner interface { 103 signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) 104} 105 106type payloadVerifier interface { 107 verifyPayload(payload []byte, signature []byte, alg SignatureAlgorithm) error 108} 109 110type genericSigner struct { 111 recipients []recipientSigInfo 112 nonceSource NonceSource 113 embedJWK bool 114 extraHeaders map[HeaderKey]interface{} 115} 116 117type recipientSigInfo struct { 118 sigAlg SignatureAlgorithm 119 publicKey func() *JSONWebKey 120 signer payloadSigner 121} 122 123func staticPublicKey(jwk *JSONWebKey) func() *JSONWebKey { 124 return func() *JSONWebKey { 125 return jwk 126 } 127} 128 129// NewSigner creates an appropriate signer based on the key type 130func NewSigner(sig SigningKey, opts *SignerOptions) (Signer, error) { 131 return NewMultiSigner([]SigningKey{sig}, opts) 132} 133 134// NewMultiSigner creates a signer for multiple recipients 135func NewMultiSigner(sigs []SigningKey, opts *SignerOptions) (Signer, error) { 136 signer := &genericSigner{recipients: []recipientSigInfo{}} 137 138 if opts != nil { 139 signer.nonceSource = opts.NonceSource 140 signer.embedJWK = opts.EmbedJWK 141 signer.extraHeaders = opts.ExtraHeaders 142 } 143 144 for _, sig := range sigs { 145 err := signer.addRecipient(sig.Algorithm, sig.Key) 146 if err != nil { 147 return nil, err 148 } 149 } 150 151 return signer, nil 152} 153 154// newVerifier creates a verifier based on the key type 155func newVerifier(verificationKey interface{}) (payloadVerifier, error) { 156 switch verificationKey := verificationKey.(type) { 157 case ed25519.PublicKey: 158 return &edEncrypterVerifier{ 159 publicKey: verificationKey, 160 }, nil 161 case *rsa.PublicKey: 162 return &rsaEncrypterVerifier{ 163 publicKey: verificationKey, 164 }, nil 165 case *ecdsa.PublicKey: 166 return &ecEncrypterVerifier{ 167 publicKey: verificationKey, 168 }, nil 169 case []byte: 170 return &symmetricMac{ 171 key: verificationKey, 172 }, nil 173 case JSONWebKey: 174 return newVerifier(verificationKey.Key) 175 case *JSONWebKey: 176 return newVerifier(verificationKey.Key) 177 } 178 if ov, ok := verificationKey.(OpaqueVerifier); ok { 179 return &opaqueVerifier{verifier: ov}, nil 180 } 181 return nil, ErrUnsupportedKeyType 182} 183 184func (ctx *genericSigner) addRecipient(alg SignatureAlgorithm, signingKey interface{}) error { 185 recipient, err := makeJWSRecipient(alg, signingKey) 186 if err != nil { 187 return err 188 } 189 190 ctx.recipients = append(ctx.recipients, recipient) 191 return nil 192} 193 194func makeJWSRecipient(alg SignatureAlgorithm, signingKey interface{}) (recipientSigInfo, error) { 195 switch signingKey := signingKey.(type) { 196 case ed25519.PrivateKey: 197 return newEd25519Signer(alg, signingKey) 198 case *rsa.PrivateKey: 199 return newRSASigner(alg, signingKey) 200 case *ecdsa.PrivateKey: 201 return newECDSASigner(alg, signingKey) 202 case []byte: 203 return newSymmetricSigner(alg, signingKey) 204 case JSONWebKey: 205 return newJWKSigner(alg, signingKey) 206 case *JSONWebKey: 207 return newJWKSigner(alg, *signingKey) 208 } 209 if signer, ok := signingKey.(OpaqueSigner); ok { 210 return newOpaqueSigner(alg, signer) 211 } 212 return recipientSigInfo{}, ErrUnsupportedKeyType 213} 214 215func newJWKSigner(alg SignatureAlgorithm, signingKey JSONWebKey) (recipientSigInfo, error) { 216 recipient, err := makeJWSRecipient(alg, signingKey.Key) 217 if err != nil { 218 return recipientSigInfo{}, err 219 } 220 if recipient.publicKey != nil && recipient.publicKey() != nil { 221 // recipient.publicKey is a JWK synthesized for embedding when recipientSigInfo 222 // was created for the inner key (such as a RSA or ECDSA public key). It contains 223 // the pub key for embedding, but doesn't have extra params like key id. 224 publicKey := signingKey 225 publicKey.Key = recipient.publicKey().Key 226 recipient.publicKey = staticPublicKey(&publicKey) 227 228 // This should be impossible, but let's check anyway. 229 if !recipient.publicKey().IsPublic() { 230 return recipientSigInfo{}, errors.New("square/go-jose: public key was unexpectedly not public") 231 } 232 } 233 return recipient, nil 234} 235 236func (ctx *genericSigner) Sign(payload []byte) (*JSONWebSignature, error) { 237 obj := &JSONWebSignature{} 238 obj.payload = payload 239 obj.Signatures = make([]Signature, len(ctx.recipients)) 240 241 for i, recipient := range ctx.recipients { 242 protected := map[HeaderKey]interface{}{ 243 headerAlgorithm: string(recipient.sigAlg), 244 } 245 246 if recipient.publicKey != nil && recipient.publicKey() != nil { 247 // We want to embed the JWK or set the kid header, but not both. Having a protected 248 // header that contains an embedded JWK while also simultaneously containing the kid 249 // header is confusing, and at least in ACME the two are considered to be mutually 250 // exclusive. The fact that both can exist at the same time is a somewhat unfortunate 251 // result of the JOSE spec. We've decided that this library will only include one or 252 // the other to avoid this confusion. 253 // 254 // See https://github.com/square/go-jose/issues/157 for more context. 255 if ctx.embedJWK { 256 protected[headerJWK] = recipient.publicKey() 257 } else { 258 keyID := recipient.publicKey().KeyID 259 if keyID != "" { 260 protected[headerKeyID] = keyID 261 } 262 } 263 } 264 265 if ctx.nonceSource != nil { 266 nonce, err := ctx.nonceSource.Nonce() 267 if err != nil { 268 return nil, fmt.Errorf("square/go-jose: Error generating nonce: %v", err) 269 } 270 protected[headerNonce] = nonce 271 } 272 273 for k, v := range ctx.extraHeaders { 274 protected[k] = v 275 } 276 277 serializedProtected := mustSerializeJSON(protected) 278 needsBase64 := true 279 280 if b64, ok := protected[headerB64]; ok { 281 if needsBase64, ok = b64.(bool); !ok { 282 return nil, errors.New("square/go-jose: Invalid b64 header parameter") 283 } 284 } 285 286 var input bytes.Buffer 287 288 input.WriteString(base64.RawURLEncoding.EncodeToString(serializedProtected)) 289 input.WriteByte('.') 290 291 if needsBase64 { 292 input.WriteString(base64.RawURLEncoding.EncodeToString(payload)) 293 } else { 294 input.Write(payload) 295 } 296 297 signatureInfo, err := recipient.signer.signPayload(input.Bytes(), recipient.sigAlg) 298 if err != nil { 299 return nil, err 300 } 301 302 signatureInfo.protected = &rawHeader{} 303 for k, v := range protected { 304 b, err := json.Marshal(v) 305 if err != nil { 306 return nil, fmt.Errorf("square/go-jose: Error marshalling item %#v: %v", k, err) 307 } 308 (*signatureInfo.protected)[k] = makeRawMessage(b) 309 } 310 obj.Signatures[i] = signatureInfo 311 } 312 313 return obj, nil 314} 315 316func (ctx *genericSigner) Options() SignerOptions { 317 return SignerOptions{ 318 NonceSource: ctx.nonceSource, 319 EmbedJWK: ctx.embedJWK, 320 ExtraHeaders: ctx.extraHeaders, 321 } 322} 323 324// Verify validates the signature on the object and returns the payload. 325// This function does not support multi-signature, if you desire multi-sig 326// verification use VerifyMulti instead. 327// 328// Be careful when verifying signatures based on embedded JWKs inside the 329// payload header. You cannot assume that the key received in a payload is 330// trusted. 331func (obj JSONWebSignature) Verify(verificationKey interface{}) ([]byte, error) { 332 err := obj.DetachedVerify(obj.payload, verificationKey) 333 if err != nil { 334 return nil, err 335 } 336 return obj.payload, nil 337} 338 339// UnsafePayloadWithoutVerification returns the payload without 340// verifying it. The content returned from this function cannot be 341// trusted. 342func (obj JSONWebSignature) UnsafePayloadWithoutVerification() []byte { 343 return obj.payload 344} 345 346// DetachedVerify validates a detached signature on the given payload. In 347// most cases, you will probably want to use Verify instead. DetachedVerify 348// is only useful if you have a payload and signature that are separated from 349// each other. 350func (obj JSONWebSignature) DetachedVerify(payload []byte, verificationKey interface{}) error { 351 verifier, err := newVerifier(verificationKey) 352 if err != nil { 353 return err 354 } 355 356 if len(obj.Signatures) > 1 { 357 return errors.New("square/go-jose: too many signatures in payload; expecting only one") 358 } 359 360 signature := obj.Signatures[0] 361 headers := signature.mergedHeaders() 362 critical, err := headers.getCritical() 363 if err != nil { 364 return err 365 } 366 367 for _, name := range critical { 368 if !supportedCritical[name] { 369 return ErrCryptoFailure 370 } 371 } 372 373 input, err := obj.computeAuthData(payload, &signature) 374 if err != nil { 375 return ErrCryptoFailure 376 } 377 378 alg := headers.getSignatureAlgorithm() 379 err = verifier.verifyPayload(input, signature.Signature, alg) 380 if err == nil { 381 return nil 382 } 383 384 return ErrCryptoFailure 385} 386 387// VerifyMulti validates (one of the multiple) signatures on the object and 388// returns the index of the signature that was verified, along with the signature 389// object and the payload. We return the signature and index to guarantee that 390// callers are getting the verified value. 391func (obj JSONWebSignature) VerifyMulti(verificationKey interface{}) (int, Signature, []byte, error) { 392 idx, sig, err := obj.DetachedVerifyMulti(obj.payload, verificationKey) 393 if err != nil { 394 return -1, Signature{}, nil, err 395 } 396 return idx, sig, obj.payload, nil 397} 398 399// DetachedVerifyMulti validates a detached signature on the given payload with 400// a signature/object that has potentially multiple signers. This returns the index 401// of the signature that was verified, along with the signature object. We return 402// the signature and index to guarantee that callers are getting the verified value. 403// 404// In most cases, you will probably want to use Verify or VerifyMulti instead. 405// DetachedVerifyMulti is only useful if you have a payload and signature that are 406// separated from each other, and the signature can have multiple signers at the 407// same time. 408func (obj JSONWebSignature) DetachedVerifyMulti(payload []byte, verificationKey interface{}) (int, Signature, error) { 409 verifier, err := newVerifier(verificationKey) 410 if err != nil { 411 return -1, Signature{}, err 412 } 413 414outer: 415 for i, signature := range obj.Signatures { 416 headers := signature.mergedHeaders() 417 critical, err := headers.getCritical() 418 if err != nil { 419 continue 420 } 421 422 for _, name := range critical { 423 if !supportedCritical[name] { 424 continue outer 425 } 426 } 427 428 input, err := obj.computeAuthData(payload, &signature) 429 if err != nil { 430 continue 431 } 432 433 alg := headers.getSignatureAlgorithm() 434 err = verifier.verifyPayload(input, signature.Signature, alg) 435 if err == nil { 436 return i, signature, nil 437 } 438 } 439 440 return -1, Signature{}, ErrCryptoFailure 441} 442