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/aes"
21	"crypto/cipher"
22	"crypto/hmac"
23	"crypto/rand"
24	"crypto/sha256"
25	"crypto/sha512"
26	"crypto/subtle"
27	"errors"
28	"hash"
29	"io"
30
31	"gopkg.in/square/go-jose.v1/cipher"
32)
33
34// Random reader (stubbed out in tests)
35var randReader = rand.Reader
36
37// Dummy key cipher for shared symmetric key mode
38type symmetricKeyCipher struct {
39	key []byte // Pre-shared content-encryption key
40}
41
42// Signer/verifier for MAC modes
43type symmetricMac struct {
44	key []byte
45}
46
47// Input/output from an AEAD operation
48type aeadParts struct {
49	iv, ciphertext, tag []byte
50}
51
52// A content cipher based on an AEAD construction
53type aeadContentCipher struct {
54	keyBytes     int
55	authtagBytes int
56	getAead      func(key []byte) (cipher.AEAD, error)
57}
58
59// Random key generator
60type randomKeyGenerator struct {
61	size int
62}
63
64// Static key generator
65type staticKeyGenerator struct {
66	key []byte
67}
68
69// Create a new content cipher based on AES-GCM
70func newAESGCM(keySize int) contentCipher {
71	return &aeadContentCipher{
72		keyBytes:     keySize,
73		authtagBytes: 16,
74		getAead: func(key []byte) (cipher.AEAD, error) {
75			aes, err := aes.NewCipher(key)
76			if err != nil {
77				return nil, err
78			}
79
80			return cipher.NewGCM(aes)
81		},
82	}
83}
84
85// Create a new content cipher based on AES-CBC+HMAC
86func newAESCBC(keySize int) contentCipher {
87	return &aeadContentCipher{
88		keyBytes:     keySize * 2,
89		authtagBytes: 16,
90		getAead: func(key []byte) (cipher.AEAD, error) {
91			return josecipher.NewCBCHMAC(key, aes.NewCipher)
92		},
93	}
94}
95
96// Get an AEAD cipher object for the given content encryption algorithm
97func getContentCipher(alg ContentEncryption) contentCipher {
98	switch alg {
99	case A128GCM:
100		return newAESGCM(16)
101	case A192GCM:
102		return newAESGCM(24)
103	case A256GCM:
104		return newAESGCM(32)
105	case A128CBC_HS256:
106		return newAESCBC(16)
107	case A192CBC_HS384:
108		return newAESCBC(24)
109	case A256CBC_HS512:
110		return newAESCBC(32)
111	default:
112		return nil
113	}
114}
115
116// newSymmetricRecipient creates a JWE encrypter based on AES-GCM key wrap.
117func newSymmetricRecipient(keyAlg KeyAlgorithm, key []byte) (recipientKeyInfo, error) {
118	switch keyAlg {
119	case DIRECT, A128GCMKW, A192GCMKW, A256GCMKW, A128KW, A192KW, A256KW:
120	default:
121		return recipientKeyInfo{}, ErrUnsupportedAlgorithm
122	}
123
124	return recipientKeyInfo{
125		keyAlg: keyAlg,
126		keyEncrypter: &symmetricKeyCipher{
127			key: key,
128		},
129	}, nil
130}
131
132// newSymmetricSigner creates a recipientSigInfo based on the given key.
133func newSymmetricSigner(sigAlg SignatureAlgorithm, key []byte) (recipientSigInfo, error) {
134	// Verify that key management algorithm is supported by this encrypter
135	switch sigAlg {
136	case HS256, HS384, HS512:
137	default:
138		return recipientSigInfo{}, ErrUnsupportedAlgorithm
139	}
140
141	return recipientSigInfo{
142		sigAlg: sigAlg,
143		signer: &symmetricMac{
144			key: key,
145		},
146	}, nil
147}
148
149// Generate a random key for the given content cipher
150func (ctx randomKeyGenerator) genKey() ([]byte, rawHeader, error) {
151	key := make([]byte, ctx.size)
152	_, err := io.ReadFull(randReader, key)
153	if err != nil {
154		return nil, rawHeader{}, err
155	}
156
157	return key, rawHeader{}, nil
158}
159
160// Key size for random generator
161func (ctx randomKeyGenerator) keySize() int {
162	return ctx.size
163}
164
165// Generate a static key (for direct mode)
166func (ctx staticKeyGenerator) genKey() ([]byte, rawHeader, error) {
167	cek := make([]byte, len(ctx.key))
168	copy(cek, ctx.key)
169	return cek, rawHeader{}, nil
170}
171
172// Key size for static generator
173func (ctx staticKeyGenerator) keySize() int {
174	return len(ctx.key)
175}
176
177// Get key size for this cipher
178func (ctx aeadContentCipher) keySize() int {
179	return ctx.keyBytes
180}
181
182// Encrypt some data
183func (ctx aeadContentCipher) encrypt(key, aad, pt []byte) (*aeadParts, error) {
184	// Get a new AEAD instance
185	aead, err := ctx.getAead(key)
186	if err != nil {
187		return nil, err
188	}
189
190	// Initialize a new nonce
191	iv := make([]byte, aead.NonceSize())
192	_, err = io.ReadFull(randReader, iv)
193	if err != nil {
194		return nil, err
195	}
196
197	ciphertextAndTag := aead.Seal(nil, iv, pt, aad)
198	offset := len(ciphertextAndTag) - ctx.authtagBytes
199
200	return &aeadParts{
201		iv:         iv,
202		ciphertext: ciphertextAndTag[:offset],
203		tag:        ciphertextAndTag[offset:],
204	}, nil
205}
206
207// Decrypt some data
208func (ctx aeadContentCipher) decrypt(key, aad []byte, parts *aeadParts) ([]byte, error) {
209	aead, err := ctx.getAead(key)
210	if err != nil {
211		return nil, err
212	}
213
214	return aead.Open(nil, parts.iv, append(parts.ciphertext, parts.tag...), aad)
215}
216
217// Encrypt the content encryption key.
218func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
219	switch alg {
220	case DIRECT:
221		return recipientInfo{
222			header: &rawHeader{},
223		}, nil
224	case A128GCMKW, A192GCMKW, A256GCMKW:
225		aead := newAESGCM(len(ctx.key))
226
227		parts, err := aead.encrypt(ctx.key, []byte{}, cek)
228		if err != nil {
229			return recipientInfo{}, err
230		}
231
232		return recipientInfo{
233			header: &rawHeader{
234				Iv:  newBuffer(parts.iv),
235				Tag: newBuffer(parts.tag),
236			},
237			encryptedKey: parts.ciphertext,
238		}, nil
239	case A128KW, A192KW, A256KW:
240		block, err := aes.NewCipher(ctx.key)
241		if err != nil {
242			return recipientInfo{}, err
243		}
244
245		jek, err := josecipher.KeyWrap(block, cek)
246		if err != nil {
247			return recipientInfo{}, err
248		}
249
250		return recipientInfo{
251			encryptedKey: jek,
252			header:       &rawHeader{},
253		}, nil
254	}
255
256	return recipientInfo{}, ErrUnsupportedAlgorithm
257}
258
259// Decrypt the content encryption key.
260func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
261	switch KeyAlgorithm(headers.Alg) {
262	case DIRECT:
263		cek := make([]byte, len(ctx.key))
264		copy(cek, ctx.key)
265		return cek, nil
266	case A128GCMKW, A192GCMKW, A256GCMKW:
267		aead := newAESGCM(len(ctx.key))
268
269		parts := &aeadParts{
270			iv:         headers.Iv.bytes(),
271			ciphertext: recipient.encryptedKey,
272			tag:        headers.Tag.bytes(),
273		}
274
275		cek, err := aead.decrypt(ctx.key, []byte{}, parts)
276		if err != nil {
277			return nil, err
278		}
279
280		return cek, nil
281	case A128KW, A192KW, A256KW:
282		block, err := aes.NewCipher(ctx.key)
283		if err != nil {
284			return nil, err
285		}
286
287		cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
288		if err != nil {
289			return nil, err
290		}
291		return cek, nil
292	}
293
294	return nil, ErrUnsupportedAlgorithm
295}
296
297// Sign the given payload
298func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
299	mac, err := ctx.hmac(payload, alg)
300	if err != nil {
301		return Signature{}, errors.New("square/go-jose: failed to compute hmac")
302	}
303
304	return Signature{
305		Signature: mac,
306		protected: &rawHeader{},
307	}, nil
308}
309
310// Verify the given payload
311func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureAlgorithm) error {
312	expected, err := ctx.hmac(payload, alg)
313	if err != nil {
314		return errors.New("square/go-jose: failed to compute hmac")
315	}
316
317	if len(mac) != len(expected) {
318		return errors.New("square/go-jose: invalid hmac")
319	}
320
321	match := subtle.ConstantTimeCompare(mac, expected)
322	if match != 1 {
323		return errors.New("square/go-jose: invalid hmac")
324	}
325
326	return nil
327}
328
329// Compute the HMAC based on the given alg value
330func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) {
331	var hash func() hash.Hash
332
333	switch alg {
334	case HS256:
335		hash = sha256.New
336	case HS384:
337		hash = sha512.New384
338	case HS512:
339		hash = sha512.New
340	default:
341		return nil, ErrUnsupportedAlgorithm
342	}
343
344	hmac := hmac.New(hash, ctx.key)
345
346	// According to documentation, Write() on hash never fails
347	_, _ = hmac.Write(payload)
348	return hmac.Sum(nil), nil
349}
350