123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131 |
- /*
- *
- * Copyright 2018 gRPC authors.
- *
- * 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 conn
- import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/hmac"
- "crypto/sha256"
- "encoding/binary"
- "fmt"
- "strconv"
- )
- // rekeyAEAD holds the necessary information for an AEAD based on
- // AES-GCM that performs nonce-based key derivation and XORs the
- // nonce with a random mask.
- type rekeyAEAD struct {
- kdfKey []byte
- kdfCounter []byte
- nonceMask []byte
- nonceBuf []byte
- gcmAEAD cipher.AEAD
- }
- // KeySizeError signals that the given key does not have the correct size.
- type KeySizeError int
- func (k KeySizeError) Error() string {
- return "alts/conn: invalid key size " + strconv.Itoa(int(k))
- }
- // newRekeyAEAD creates a new instance of aes128gcm with rekeying.
- // The key argument should be 44 bytes, the first 32 bytes are used as a key
- // for HKDF-expand and the remainining 12 bytes are used as a random mask for
- // the counter.
- func newRekeyAEAD(key []byte) (*rekeyAEAD, error) {
- k := len(key)
- if k != kdfKeyLen+nonceLen {
- return nil, KeySizeError(k)
- }
- return &rekeyAEAD{
- kdfKey: key[:kdfKeyLen],
- kdfCounter: make([]byte, kdfCounterLen),
- nonceMask: key[kdfKeyLen:],
- nonceBuf: make([]byte, nonceLen),
- gcmAEAD: nil,
- }, nil
- }
- // Seal rekeys if nonce[2:8] is different than in the last call, masks the nonce,
- // and calls Seal for aes128gcm.
- func (s *rekeyAEAD) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
- if err := s.rekeyIfRequired(nonce); err != nil {
- panic(fmt.Sprintf("Rekeying failed with: %s", err.Error()))
- }
- maskNonce(s.nonceBuf, nonce, s.nonceMask)
- return s.gcmAEAD.Seal(dst, s.nonceBuf, plaintext, additionalData)
- }
- // Open rekeys if nonce[2:8] is different than in the last call, masks the nonce,
- // and calls Open for aes128gcm.
- func (s *rekeyAEAD) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
- if err := s.rekeyIfRequired(nonce); err != nil {
- return nil, err
- }
- maskNonce(s.nonceBuf, nonce, s.nonceMask)
- return s.gcmAEAD.Open(dst, s.nonceBuf, ciphertext, additionalData)
- }
- // rekeyIfRequired creates a new aes128gcm AEAD if the existing AEAD is nil
- // or cannot be used with given nonce.
- func (s *rekeyAEAD) rekeyIfRequired(nonce []byte) error {
- newKdfCounter := nonce[kdfCounterOffset : kdfCounterOffset+kdfCounterLen]
- if s.gcmAEAD != nil && bytes.Equal(newKdfCounter, s.kdfCounter) {
- return nil
- }
- copy(s.kdfCounter, newKdfCounter)
- a, err := aes.NewCipher(hkdfExpand(s.kdfKey, s.kdfCounter))
- if err != nil {
- return err
- }
- s.gcmAEAD, err = cipher.NewGCM(a)
- return err
- }
- // maskNonce XORs the given nonce with the mask and stores the result in dst.
- func maskNonce(dst, nonce, mask []byte) {
- nonce1 := binary.LittleEndian.Uint64(nonce[:sizeUint64])
- nonce2 := binary.LittleEndian.Uint32(nonce[sizeUint64:])
- mask1 := binary.LittleEndian.Uint64(mask[:sizeUint64])
- mask2 := binary.LittleEndian.Uint32(mask[sizeUint64:])
- binary.LittleEndian.PutUint64(dst[:sizeUint64], nonce1^mask1)
- binary.LittleEndian.PutUint32(dst[sizeUint64:], nonce2^mask2)
- }
- // NonceSize returns the required nonce size.
- func (s *rekeyAEAD) NonceSize() int {
- return s.gcmAEAD.NonceSize()
- }
- // Overhead returns the ciphertext overhead.
- func (s *rekeyAEAD) Overhead() int {
- return s.gcmAEAD.Overhead()
- }
- // hkdfExpand computes the first 16 bytes of the HKDF-expand function
- // defined in RFC5869.
- func hkdfExpand(key, info []byte) []byte {
- mac := hmac.New(sha256.New, key)
- mac.Write(info)
- mac.Write([]byte{0x01}[:])
- return mac.Sum(nil)[:aeadKeyLen]
- }
|