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