123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- /*-
- * Copyright 2014 Square Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package jose
- import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/hmac"
- "crypto/rand"
- "crypto/sha256"
- "crypto/sha512"
- "crypto/subtle"
- "errors"
- "fmt"
- "hash"
- "io"
- "golang.org/x/crypto/pbkdf2"
- "gopkg.in/square/go-jose.v2/cipher"
- )
- // Random reader (stubbed out in tests)
- var RandReader = rand.Reader
- const (
- // RFC7518 recommends a minimum of 1,000 iterations:
- // https://tools.ietf.org/html/rfc7518#section-4.8.1.2
- // NIST recommends a minimum of 10,000:
- // https://pages.nist.gov/800-63-3/sp800-63b.html
- // 1Password uses 100,000:
- // https://support.1password.com/pbkdf2/
- defaultP2C = 100000
- // Default salt size: 128 bits
- defaultP2SSize = 16
- )
- // Dummy key cipher for shared symmetric key mode
- type symmetricKeyCipher struct {
- key []byte // Pre-shared content-encryption key
- p2c int // PBES2 Count
- p2s []byte // PBES2 Salt Input
- }
- // Signer/verifier for MAC modes
- type symmetricMac struct {
- key []byte
- }
- // Input/output from an AEAD operation
- type aeadParts struct {
- iv, ciphertext, tag []byte
- }
- // A content cipher based on an AEAD construction
- type aeadContentCipher struct {
- keyBytes int
- authtagBytes int
- getAead func(key []byte) (cipher.AEAD, error)
- }
- // Random key generator
- type randomKeyGenerator struct {
- size int
- }
- // Static key generator
- type staticKeyGenerator struct {
- key []byte
- }
- // Create a new content cipher based on AES-GCM
- func newAESGCM(keySize int) contentCipher {
- return &aeadContentCipher{
- keyBytes: keySize,
- authtagBytes: 16,
- getAead: func(key []byte) (cipher.AEAD, error) {
- aes, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- return cipher.NewGCM(aes)
- },
- }
- }
- // Create a new content cipher based on AES-CBC+HMAC
- func newAESCBC(keySize int) contentCipher {
- return &aeadContentCipher{
- keyBytes: keySize * 2,
- authtagBytes: keySize,
- getAead: func(key []byte) (cipher.AEAD, error) {
- return josecipher.NewCBCHMAC(key, aes.NewCipher)
- },
- }
- }
- // Get an AEAD cipher object for the given content encryption algorithm
- func getContentCipher(alg ContentEncryption) contentCipher {
- switch alg {
- case A128GCM:
- return newAESGCM(16)
- case A192GCM:
- return newAESGCM(24)
- case A256GCM:
- return newAESGCM(32)
- case A128CBC_HS256:
- return newAESCBC(16)
- case A192CBC_HS384:
- return newAESCBC(24)
- case A256CBC_HS512:
- return newAESCBC(32)
- default:
- return nil
- }
- }
- // getPbkdf2Params returns the key length and hash function used in
- // pbkdf2.Key.
- func getPbkdf2Params(alg KeyAlgorithm) (int, func() hash.Hash) {
- switch alg {
- case PBES2_HS256_A128KW:
- return 16, sha256.New
- case PBES2_HS384_A192KW:
- return 24, sha512.New384
- case PBES2_HS512_A256KW:
- return 32, sha512.New
- default:
- panic("invalid algorithm")
- }
- }
- // getRandomSalt generates a new salt of the given size.
- func getRandomSalt(size int) ([]byte, error) {
- salt := make([]byte, size)
- _, err := io.ReadFull(RandReader, salt)
- if err != nil {
- return nil, err
- }
- return salt, nil
- }
- // newSymmetricRecipient creates a JWE encrypter based on AES-GCM key wrap.
- func newSymmetricRecipient(keyAlg KeyAlgorithm, key []byte) (recipientKeyInfo, error) {
- switch keyAlg {
- case DIRECT, A128GCMKW, A192GCMKW, A256GCMKW, A128KW, A192KW, A256KW:
- case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
- default:
- return recipientKeyInfo{}, ErrUnsupportedAlgorithm
- }
- return recipientKeyInfo{
- keyAlg: keyAlg,
- keyEncrypter: &symmetricKeyCipher{
- key: key,
- },
- }, nil
- }
- // newSymmetricSigner creates a recipientSigInfo based on the given key.
- func newSymmetricSigner(sigAlg SignatureAlgorithm, key []byte) (recipientSigInfo, error) {
- // Verify that key management algorithm is supported by this encrypter
- switch sigAlg {
- case HS256, HS384, HS512:
- default:
- return recipientSigInfo{}, ErrUnsupportedAlgorithm
- }
- return recipientSigInfo{
- sigAlg: sigAlg,
- signer: &symmetricMac{
- key: key,
- },
- }, nil
- }
- // Generate a random key for the given content cipher
- func (ctx randomKeyGenerator) genKey() ([]byte, rawHeader, error) {
- key := make([]byte, ctx.size)
- _, err := io.ReadFull(RandReader, key)
- if err != nil {
- return nil, rawHeader{}, err
- }
- return key, rawHeader{}, nil
- }
- // Key size for random generator
- func (ctx randomKeyGenerator) keySize() int {
- return ctx.size
- }
- // Generate a static key (for direct mode)
- func (ctx staticKeyGenerator) genKey() ([]byte, rawHeader, error) {
- cek := make([]byte, len(ctx.key))
- copy(cek, ctx.key)
- return cek, rawHeader{}, nil
- }
- // Key size for static generator
- func (ctx staticKeyGenerator) keySize() int {
- return len(ctx.key)
- }
- // Get key size for this cipher
- func (ctx aeadContentCipher) keySize() int {
- return ctx.keyBytes
- }
- // Encrypt some data
- func (ctx aeadContentCipher) encrypt(key, aad, pt []byte) (*aeadParts, error) {
- // Get a new AEAD instance
- aead, err := ctx.getAead(key)
- if err != nil {
- return nil, err
- }
- // Initialize a new nonce
- iv := make([]byte, aead.NonceSize())
- _, err = io.ReadFull(RandReader, iv)
- if err != nil {
- return nil, err
- }
- ciphertextAndTag := aead.Seal(nil, iv, pt, aad)
- offset := len(ciphertextAndTag) - ctx.authtagBytes
- return &aeadParts{
- iv: iv,
- ciphertext: ciphertextAndTag[:offset],
- tag: ciphertextAndTag[offset:],
- }, nil
- }
- // Decrypt some data
- func (ctx aeadContentCipher) decrypt(key, aad []byte, parts *aeadParts) ([]byte, error) {
- aead, err := ctx.getAead(key)
- if err != nil {
- return nil, err
- }
- if len(parts.iv) != aead.NonceSize() || len(parts.tag) < ctx.authtagBytes {
- return nil, ErrCryptoFailure
- }
- return aead.Open(nil, parts.iv, append(parts.ciphertext, parts.tag...), aad)
- }
- // Encrypt the content encryption key.
- func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipientInfo, error) {
- switch alg {
- case DIRECT:
- return recipientInfo{
- header: &rawHeader{},
- }, nil
- case A128GCMKW, A192GCMKW, A256GCMKW:
- aead := newAESGCM(len(ctx.key))
- parts, err := aead.encrypt(ctx.key, []byte{}, cek)
- if err != nil {
- return recipientInfo{}, err
- }
- header := &rawHeader{}
- header.set(headerIV, newBuffer(parts.iv))
- header.set(headerTag, newBuffer(parts.tag))
- return recipientInfo{
- header: header,
- encryptedKey: parts.ciphertext,
- }, nil
- case A128KW, A192KW, A256KW:
- block, err := aes.NewCipher(ctx.key)
- if err != nil {
- return recipientInfo{}, err
- }
- jek, err := josecipher.KeyWrap(block, cek)
- if err != nil {
- return recipientInfo{}, err
- }
- return recipientInfo{
- encryptedKey: jek,
- header: &rawHeader{},
- }, nil
- case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
- if len(ctx.p2s) == 0 {
- salt, err := getRandomSalt(defaultP2SSize)
- if err != nil {
- return recipientInfo{}, err
- }
- ctx.p2s = salt
- }
- if ctx.p2c <= 0 {
- ctx.p2c = defaultP2C
- }
- // salt is UTF8(Alg) || 0x00 || Salt Input
- salt := bytes.Join([][]byte{[]byte(alg), ctx.p2s}, []byte{0x00})
- // derive key
- keyLen, h := getPbkdf2Params(alg)
- key := pbkdf2.Key(ctx.key, salt, ctx.p2c, keyLen, h)
- // use AES cipher with derived key
- block, err := aes.NewCipher(key)
- if err != nil {
- return recipientInfo{}, err
- }
- jek, err := josecipher.KeyWrap(block, cek)
- if err != nil {
- return recipientInfo{}, err
- }
- header := &rawHeader{}
- header.set(headerP2C, ctx.p2c)
- header.set(headerP2S, newBuffer(ctx.p2s))
- return recipientInfo{
- encryptedKey: jek,
- header: header,
- }, nil
- }
- return recipientInfo{}, ErrUnsupportedAlgorithm
- }
- // Decrypt the content encryption key.
- func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
- switch headers.getAlgorithm() {
- case DIRECT:
- cek := make([]byte, len(ctx.key))
- copy(cek, ctx.key)
- return cek, nil
- case A128GCMKW, A192GCMKW, A256GCMKW:
- aead := newAESGCM(len(ctx.key))
- iv, err := headers.getIV()
- if err != nil {
- return nil, fmt.Errorf("square/go-jose: invalid IV: %v", err)
- }
- tag, err := headers.getTag()
- if err != nil {
- return nil, fmt.Errorf("square/go-jose: invalid tag: %v", err)
- }
- parts := &aeadParts{
- iv: iv.bytes(),
- ciphertext: recipient.encryptedKey,
- tag: tag.bytes(),
- }
- cek, err := aead.decrypt(ctx.key, []byte{}, parts)
- if err != nil {
- return nil, err
- }
- return cek, nil
- case A128KW, A192KW, A256KW:
- block, err := aes.NewCipher(ctx.key)
- if err != nil {
- return nil, err
- }
- cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
- if err != nil {
- return nil, err
- }
- return cek, nil
- case PBES2_HS256_A128KW, PBES2_HS384_A192KW, PBES2_HS512_A256KW:
- p2s, err := headers.getP2S()
- if err != nil {
- return nil, fmt.Errorf("square/go-jose: invalid P2S: %v", err)
- }
- if p2s == nil || len(p2s.data) == 0 {
- return nil, fmt.Errorf("square/go-jose: invalid P2S: must be present")
- }
- p2c, err := headers.getP2C()
- if err != nil {
- return nil, fmt.Errorf("square/go-jose: invalid P2C: %v", err)
- }
- if p2c <= 0 {
- return nil, fmt.Errorf("square/go-jose: invalid P2C: must be a positive integer")
- }
- // salt is UTF8(Alg) || 0x00 || Salt Input
- alg := headers.getAlgorithm()
- salt := bytes.Join([][]byte{[]byte(alg), p2s.bytes()}, []byte{0x00})
- // derive key
- keyLen, h := getPbkdf2Params(alg)
- key := pbkdf2.Key(ctx.key, salt, p2c, keyLen, h)
- // use AES cipher with derived key
- block, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
- cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
- if err != nil {
- return nil, err
- }
- return cek, nil
- }
- return nil, ErrUnsupportedAlgorithm
- }
- // Sign the given payload
- func (ctx symmetricMac) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
- mac, err := ctx.hmac(payload, alg)
- if err != nil {
- return Signature{}, errors.New("square/go-jose: failed to compute hmac")
- }
- return Signature{
- Signature: mac,
- protected: &rawHeader{},
- }, nil
- }
- // Verify the given payload
- func (ctx symmetricMac) verifyPayload(payload []byte, mac []byte, alg SignatureAlgorithm) error {
- expected, err := ctx.hmac(payload, alg)
- if err != nil {
- return errors.New("square/go-jose: failed to compute hmac")
- }
- if len(mac) != len(expected) {
- return errors.New("square/go-jose: invalid hmac")
- }
- match := subtle.ConstantTimeCompare(mac, expected)
- if match != 1 {
- return errors.New("square/go-jose: invalid hmac")
- }
- return nil
- }
- // Compute the HMAC based on the given alg value
- func (ctx symmetricMac) hmac(payload []byte, alg SignatureAlgorithm) ([]byte, error) {
- var hash func() hash.Hash
- switch alg {
- case HS256:
- hash = sha256.New
- case HS384:
- hash = sha512.New384
- case HS512:
- hash = sha512.New
- default:
- return nil, ErrUnsupportedAlgorithm
- }
- hmac := hmac.New(hash, ctx.key)
- // According to documentation, Write() on hash never fails
- _, _ = hmac.Write(payload)
- return hmac.Sum(nil), nil
- }
|