cbc_hmac.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197
  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. package josecipher
  17. import (
  18. "bytes"
  19. "crypto/cipher"
  20. "crypto/hmac"
  21. "crypto/sha256"
  22. "crypto/sha512"
  23. "crypto/subtle"
  24. "encoding/binary"
  25. "errors"
  26. "hash"
  27. )
  28. const (
  29. nonceBytes = 16
  30. )
  31. // NewCBCHMAC instantiates a new AEAD based on CBC+HMAC.
  32. func NewCBCHMAC(key []byte, newBlockCipher func([]byte) (cipher.Block, error)) (cipher.AEAD, error) {
  33. keySize := len(key) / 2
  34. integrityKey := key[:keySize]
  35. encryptionKey := key[keySize:]
  36. blockCipher, err := newBlockCipher(encryptionKey)
  37. if err != nil {
  38. return nil, err
  39. }
  40. var hash func() hash.Hash
  41. switch keySize {
  42. case 16:
  43. hash = sha256.New
  44. case 24:
  45. hash = sha512.New384
  46. case 32:
  47. hash = sha512.New
  48. }
  49. return &cbcAEAD{
  50. hash: hash,
  51. blockCipher: blockCipher,
  52. authtagBytes: keySize,
  53. integrityKey: integrityKey,
  54. }, nil
  55. }
  56. // An AEAD based on CBC+HMAC
  57. type cbcAEAD struct {
  58. hash func() hash.Hash
  59. authtagBytes int
  60. integrityKey []byte
  61. blockCipher cipher.Block
  62. }
  63. func (ctx *cbcAEAD) NonceSize() int {
  64. return nonceBytes
  65. }
  66. func (ctx *cbcAEAD) Overhead() int {
  67. // Maximum overhead is block size (for padding) plus auth tag length, where
  68. // the length of the auth tag is equivalent to the key size.
  69. return ctx.blockCipher.BlockSize() + ctx.authtagBytes
  70. }
  71. // Seal encrypts and authenticates the plaintext.
  72. func (ctx *cbcAEAD) Seal(dst, nonce, plaintext, data []byte) []byte {
  73. // Output buffer -- must take care not to mangle plaintext input.
  74. ciphertext := make([]byte, uint64(len(plaintext))+uint64(ctx.Overhead()))[:len(plaintext)]
  75. copy(ciphertext, plaintext)
  76. ciphertext = padBuffer(ciphertext, ctx.blockCipher.BlockSize())
  77. cbc := cipher.NewCBCEncrypter(ctx.blockCipher, nonce)
  78. cbc.CryptBlocks(ciphertext, ciphertext)
  79. authtag := ctx.computeAuthTag(data, nonce, ciphertext)
  80. ret, out := resize(dst, uint64(len(dst))+uint64(len(ciphertext))+uint64(len(authtag)))
  81. copy(out, ciphertext)
  82. copy(out[len(ciphertext):], authtag)
  83. return ret
  84. }
  85. // Open decrypts and authenticates the ciphertext.
  86. func (ctx *cbcAEAD) Open(dst, nonce, ciphertext, data []byte) ([]byte, error) {
  87. if len(ciphertext) < ctx.authtagBytes {
  88. return nil, errors.New("square/go-jose: invalid ciphertext (too short)")
  89. }
  90. offset := len(ciphertext) - ctx.authtagBytes
  91. expectedTag := ctx.computeAuthTag(data, nonce, ciphertext[:offset])
  92. match := subtle.ConstantTimeCompare(expectedTag, ciphertext[offset:])
  93. if match != 1 {
  94. return nil, errors.New("square/go-jose: invalid ciphertext (auth tag mismatch)")
  95. }
  96. cbc := cipher.NewCBCDecrypter(ctx.blockCipher, nonce)
  97. // Make copy of ciphertext buffer, don't want to modify in place
  98. buffer := append([]byte{}, []byte(ciphertext[:offset])...)
  99. if len(buffer)%ctx.blockCipher.BlockSize() > 0 {
  100. return nil, errors.New("square/go-jose: invalid ciphertext (invalid length)")
  101. }
  102. cbc.CryptBlocks(buffer, buffer)
  103. // Remove padding
  104. plaintext, err := unpadBuffer(buffer, ctx.blockCipher.BlockSize())
  105. if err != nil {
  106. return nil, err
  107. }
  108. ret, out := resize(dst, uint64(len(dst))+uint64(len(plaintext)))
  109. copy(out, plaintext)
  110. return ret, nil
  111. }
  112. // Compute an authentication tag
  113. func (ctx *cbcAEAD) computeAuthTag(aad, nonce, ciphertext []byte) []byte {
  114. buffer := make([]byte, uint64(len(aad))+uint64(len(nonce))+uint64(len(ciphertext))+8)
  115. n := 0
  116. n += copy(buffer, aad)
  117. n += copy(buffer[n:], nonce)
  118. n += copy(buffer[n:], ciphertext)
  119. binary.BigEndian.PutUint64(buffer[n:], uint64(len(aad))*8)
  120. // According to documentation, Write() on hash.Hash never fails.
  121. hmac := hmac.New(ctx.hash, ctx.integrityKey)
  122. _, _ = hmac.Write(buffer)
  123. return hmac.Sum(nil)[:ctx.authtagBytes]
  124. }
  125. // resize ensures the the given slice has a capacity of at least n bytes.
  126. // If the capacity of the slice is less than n, a new slice is allocated
  127. // and the existing data will be copied.
  128. func resize(in []byte, n uint64) (head, tail []byte) {
  129. if uint64(cap(in)) >= n {
  130. head = in[:n]
  131. } else {
  132. head = make([]byte, n)
  133. copy(head, in)
  134. }
  135. tail = head[len(in):]
  136. return
  137. }
  138. // Apply padding
  139. func padBuffer(buffer []byte, blockSize int) []byte {
  140. missing := blockSize - (len(buffer) % blockSize)
  141. ret, out := resize(buffer, uint64(len(buffer))+uint64(missing))
  142. padding := bytes.Repeat([]byte{byte(missing)}, missing)
  143. copy(out, padding)
  144. return ret
  145. }
  146. // Remove padding
  147. func unpadBuffer(buffer []byte, blockSize int) ([]byte, error) {
  148. if len(buffer)%blockSize != 0 {
  149. return nil, errors.New("square/go-jose: invalid padding")
  150. }
  151. last := buffer[len(buffer)-1]
  152. count := int(last)
  153. if count == 0 || count > blockSize || count > len(buffer) {
  154. return nil, errors.New("square/go-jose: invalid padding")
  155. }
  156. padding := bytes.Repeat([]byte{last}, count)
  157. if !bytes.HasSuffix(buffer, padding) {
  158. return nil, errors.New("square/go-jose: invalid padding")
  159. }
  160. return buffer[:len(buffer)-count], nil
  161. }