/* * * 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 ( "crypto/aes" "crypto/cipher" core "google.golang.org/grpc/credentials/alts/internal" ) const ( // Overflow length n in bytes, never encrypt more than 2^(n*8) frames (in // each direction). overflowLenAES128GCM = 5 ) // aes128gcm is the struct that holds necessary information for ALTS record. // The counter value is NOT included in the payload during the encryption and // decryption operations. type aes128gcm struct { // inCounter is used in ALTS record to check that incoming counters are // as expected, since ALTS record guarantees that messages are unwrapped // in the same order that the peer wrapped them. inCounter Counter outCounter Counter aead cipher.AEAD } // NewAES128GCM creates an instance that uses aes128gcm for ALTS record. func NewAES128GCM(side core.Side, key []byte) (ALTSRecordCrypto, error) { c, err := aes.NewCipher(key) if err != nil { return nil, err } a, err := cipher.NewGCM(c) if err != nil { return nil, err } return &aes128gcm{ inCounter: NewInCounter(side, overflowLenAES128GCM), outCounter: NewOutCounter(side, overflowLenAES128GCM), aead: a, }, nil } // Encrypt is the encryption function. dst can contain bytes at the beginning of // the ciphertext that will not be encrypted but will be authenticated. If dst // has enough capacity to hold these bytes, the ciphertext and the tag, no // allocation and copy operations will be performed. dst and plaintext do not // overlap. func (s *aes128gcm) Encrypt(dst, plaintext []byte) ([]byte, error) { // If we need to allocate an output buffer, we want to include space for // GCM tag to avoid forcing ALTS record to reallocate as well. dlen := len(dst) dst, out := SliceForAppend(dst, len(plaintext)+GcmTagSize) seq, err := s.outCounter.Value() if err != nil { return nil, err } data := out[:len(plaintext)] copy(data, plaintext) // data may alias plaintext // Seal appends the ciphertext and the tag to its first argument and // returns the updated slice. However, SliceForAppend above ensures that // dst has enough capacity to avoid a reallocation and copy due to the // append. dst = s.aead.Seal(dst[:dlen], seq, data, nil) s.outCounter.Inc() return dst, nil } func (s *aes128gcm) EncryptionOverhead() int { return GcmTagSize } func (s *aes128gcm) Decrypt(dst, ciphertext []byte) ([]byte, error) { seq, err := s.inCounter.Value() if err != nil { return nil, err } // If dst is equal to ciphertext[:0], ciphertext storage is reused. plaintext, err := s.aead.Open(dst, seq, ciphertext, nil) if err != nil { return nil, ErrAuth } s.inCounter.Inc() return plaintext, nil }