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