You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

132 lines
4.0 KiB

  1. /*
  2. *
  3. * Copyright 2018 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. package conn
  19. import (
  20. "bytes"
  21. "crypto/aes"
  22. "crypto/cipher"
  23. "crypto/hmac"
  24. "crypto/sha256"
  25. "encoding/binary"
  26. "fmt"
  27. "strconv"
  28. )
  29. // rekeyAEAD holds the necessary information for an AEAD based on
  30. // AES-GCM that performs nonce-based key derivation and XORs the
  31. // nonce with a random mask.
  32. type rekeyAEAD struct {
  33. kdfKey []byte
  34. kdfCounter []byte
  35. nonceMask []byte
  36. nonceBuf []byte
  37. gcmAEAD cipher.AEAD
  38. }
  39. // KeySizeError signals that the given key does not have the correct size.
  40. type KeySizeError int
  41. func (k KeySizeError) Error() string {
  42. return "alts/conn: invalid key size " + strconv.Itoa(int(k))
  43. }
  44. // newRekeyAEAD creates a new instance of aes128gcm with rekeying.
  45. // The key argument should be 44 bytes, the first 32 bytes are used as a key
  46. // for HKDF-expand and the remainining 12 bytes are used as a random mask for
  47. // the counter.
  48. func newRekeyAEAD(key []byte) (*rekeyAEAD, error) {
  49. k := len(key)
  50. if k != kdfKeyLen+nonceLen {
  51. return nil, KeySizeError(k)
  52. }
  53. return &rekeyAEAD{
  54. kdfKey: key[:kdfKeyLen],
  55. kdfCounter: make([]byte, kdfCounterLen),
  56. nonceMask: key[kdfKeyLen:],
  57. nonceBuf: make([]byte, nonceLen),
  58. gcmAEAD: nil,
  59. }, nil
  60. }
  61. // Seal rekeys if nonce[2:8] is different than in the last call, masks the nonce,
  62. // and calls Seal for aes128gcm.
  63. func (s *rekeyAEAD) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
  64. if err := s.rekeyIfRequired(nonce); err != nil {
  65. panic(fmt.Sprintf("Rekeying failed with: %s", err.Error()))
  66. }
  67. maskNonce(s.nonceBuf, nonce, s.nonceMask)
  68. return s.gcmAEAD.Seal(dst, s.nonceBuf, plaintext, additionalData)
  69. }
  70. // Open rekeys if nonce[2:8] is different than in the last call, masks the nonce,
  71. // and calls Open for aes128gcm.
  72. func (s *rekeyAEAD) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
  73. if err := s.rekeyIfRequired(nonce); err != nil {
  74. return nil, err
  75. }
  76. maskNonce(s.nonceBuf, nonce, s.nonceMask)
  77. return s.gcmAEAD.Open(dst, s.nonceBuf, ciphertext, additionalData)
  78. }
  79. // rekeyIfRequired creates a new aes128gcm AEAD if the existing AEAD is nil
  80. // or cannot be used with given nonce.
  81. func (s *rekeyAEAD) rekeyIfRequired(nonce []byte) error {
  82. newKdfCounter := nonce[kdfCounterOffset : kdfCounterOffset+kdfCounterLen]
  83. if s.gcmAEAD != nil && bytes.Equal(newKdfCounter, s.kdfCounter) {
  84. return nil
  85. }
  86. copy(s.kdfCounter, newKdfCounter)
  87. a, err := aes.NewCipher(hkdfExpand(s.kdfKey, s.kdfCounter))
  88. if err != nil {
  89. return err
  90. }
  91. s.gcmAEAD, err = cipher.NewGCM(a)
  92. return err
  93. }
  94. // maskNonce XORs the given nonce with the mask and stores the result in dst.
  95. func maskNonce(dst, nonce, mask []byte) {
  96. nonce1 := binary.LittleEndian.Uint64(nonce[:sizeUint64])
  97. nonce2 := binary.LittleEndian.Uint32(nonce[sizeUint64:])
  98. mask1 := binary.LittleEndian.Uint64(mask[:sizeUint64])
  99. mask2 := binary.LittleEndian.Uint32(mask[sizeUint64:])
  100. binary.LittleEndian.PutUint64(dst[:sizeUint64], nonce1^mask1)
  101. binary.LittleEndian.PutUint32(dst[sizeUint64:], nonce2^mask2)
  102. }
  103. // NonceSize returns the required nonce size.
  104. func (s *rekeyAEAD) NonceSize() int {
  105. return s.gcmAEAD.NonceSize()
  106. }
  107. // Overhead returns the ciphertext overhead.
  108. func (s *rekeyAEAD) Overhead() int {
  109. return s.gcmAEAD.Overhead()
  110. }
  111. // hkdfExpand computes the first 16 bytes of the HKDF-expand function
  112. // defined in RFC5869.
  113. func hkdfExpand(key, info []byte) []byte {
  114. mac := hmac.New(sha256.New, key)
  115. mac.Write(info)
  116. mac.Write([]byte{0x01}[:])
  117. return mac.Sum(nil)[:aeadKeyLen]
  118. }