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