text: // Copyright 2017 The Fuchsia Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #pragma once
  
  #include <stddef.h>
  #include <stdint.h>
  
  #include <crypto/bytes.h>
  #include <fbl/macros.h>
  #include <zircon/types.h>
  #include <crypto/secret.h>
  
  // |crypto::Cipher| is used to encrypt and decrypt data.  Ciphers differ from AEADs in that they
  // require block-aligned lengths and do not check data integrity.  This implementation can be used
  // as either a stream cipher, or as a random access cipher if the cipher has a "tweaked codebook
  // mode".  See the variants of |Init| and |Transform| for details.  A 64 bit nonce is used to seal
  // plain texts, meaning a given key and IV can be used for at most 2^64 - 1 operations.
  namespace crypto {
  
  class Cipher final {
  public:
      // Algorithm enumerates the supported secret key ciphers.
      enum Algorithm {
          kUninitialized = 0,
          kAES256_XTS, // A "tweaked cipher
      };
  
      // Indicates whether the objects turns plaintext into ciphertext or vice versa.
      enum Direction {
          kUnset = 0,
          kEncrypt,
          kDecrypt,
      };
  
      Cipher();
      ~Cipher();
  
      Direction direction() const { return direction_; }
      uint64_t alignment() const { return alignment_; }
  
      // Gets the number of bytes needed for the symmetric key used by the given |cipher|.
      static zx_status_t GetKeyLen(Algorithm cipher, size_t* out);
  
      // Gets the number of bytes needed for the initialization vector (IV) used by the given
      // |cipher|.
      static zx_status_t GetIVLen(Algorithm cipher, size_t* out);
  
      // Gets the cipher block size in bytes for  the given |cipher|.  Data passed to |Encrypt| or
      // |Decrypt| must be a multiple of this size.
      static zx_status_t GetBlockSize(Algorithm cipher, size_t* out);
  
      // Sets up the cipher to encrypt or decrypt data using the given |key| and |iv|,  based on the
      // given |direction|, either as:
      //   - A stream ciphers, using the first variant that omits the |alignment|.
      //   - As a random access cipher, using the second variant.  All offsets must be
      //   |alignment|-aligned, and |alignment| must be a power of 2.
      zx_status_t Init(Algorithm algo, Direction direction, const Secret& key, const Bytes& iv,
                       uint64_t alignment);
  
      // Sets up the cipher to encrypt data using the given |key| and |iv|, either as a stream cipher
      // or a random access cipher, as described above in |Init|.
      zx_status_t InitEncrypt(Algorithm algo, const Secret& key, const Bytes& iv) {
          return Init(algo, kEncrypt, key, iv, 0);
      }
      zx_status_t InitEncrypt(Algorithm algo, const Secret& key, const Bytes& iv, uint64_t alignment) {
          return Init(algo, kEncrypt, key, iv, alignment);
      }
  
      // Sets up the cipher to decrypt data using the given |key| and |iv|, either as a stream cipher
      // or a random access cipher, as described above in |Init|.
      zx_status_t InitDecrypt(Algorithm algo, const Secret& key, const Bytes& iv) {
          return Init(algo, kDecrypt, key, iv, 0);
      }
      zx_status_t InitDecrypt(Algorithm algo, const Secret& key, const Bytes& iv, uint64_t alignment) {
          return Init(algo, kDecrypt, key, iv, alignment);
      }
  
      // Encrypts or decrypts |length| bytes from |in| to |out|, based on the given |direction| and
      // the parameters set in |Init|:
      //  - Must have been configured with the same |direction|.
      //  - If |alignment| was non-zero, |offset| must be a multiple of it.
      // Finally, |length| must be a multiple of cipher blocks, and |out| must have room for |length|
      // bytes.  This method will fail if called 2^64 or more times with the same key and IV.
      zx_status_t Transform(const uint8_t* in, zx_off_t offset, size_t length, uint8_t* out,
                            Direction Direction);
  
      // Encrypts |len| bytes from |in| to |out|, as described above in |Transform|.
      zx_status_t Encrypt(const uint8_t* in, size_t length, uint8_t* out) {
          return Transform(in, 0, length, out, kEncrypt);
      }
      zx_status_t Encrypt(const uint8_t* in, zx_off_t offset, size_t length, uint8_t* out) {
          return Transform(in, offset, length, out, kEncrypt);
      }
  
      // Decrypts |len| bytes from |in| to |out|, as described above in |Transform|.
      zx_status_t Decrypt(const uint8_t* in, size_t length, uint8_t* out) {
          return Transform(in, 0, length, out, kDecrypt);
      }
      zx_status_t Decrypt(const uint8_t* in, zx_off_t offset, size_t length, uint8_t* out) {
          return Transform(in, offset, length, out, kDecrypt);
      }
  
      // Clears all state from this instance.
      void Reset();
  
  private:
      DISALLOW_COPY_ASSIGN_AND_MOVE(Cipher);
  
      // Opaque crypto implementation context.
      struct Context;
  
      // Opaque pointer to the crypto implementation context.
      fbl::unique_ptr<Context> ctx_;
      // Indicates which algorithm to use to transform data.
      Algorithm cipher_;
      // Indicates whether configured to encrypt or decrypt.
      Direction direction_;
      // Cipher block size.
      size_t block_size_;
      // Buffer holding initial vector.  The IV is expanded to be |zx_off_t|-aligned.
      fbl::unique_ptr<zx_off_t[]> iv_;
      // Original value of |iv_[0]|.  |Transform| will fail if |iv_[0]| wraps around to this value.
      zx_off_t iv0_;
      // If non-zero, indicates how many bytes to use a nonce for before incrementing.
      uint64_t alignment_;
  };
  } // namespace crypto
  