/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * 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.
 */
#pragma once

#include <gtest/gtest.h>
#include <stdint.h>

#include <string>
#include <vector>

namespace android {
namespace kernel {

class Cipher {
 public:
  virtual ~Cipher() {}
  bool Encrypt(const std::vector<uint8_t> &key, const uint8_t *iv,
               const uint8_t *src, uint8_t *dst, int nbytes) const {
    if (key.size() != keysize()) {
      ADD_FAILURE() << "Bad key size";
      return false;
    }
    return DoCrypt(key.data(), iv, src, dst, nbytes, true);
  }
  bool Decrypt(const std::vector<uint8_t> &key, const uint8_t *iv,
               const uint8_t *src, uint8_t *dst, int nbytes) const {
    if (key.size() != keysize()) {
      ADD_FAILURE() << "Bad key size";
      return false;
    }
    return DoCrypt(key.data(), iv, src, dst, nbytes, false);
  }
  virtual int keysize() const = 0;
  virtual int ivsize() const = 0;

 protected:
  virtual bool DoCrypt(const uint8_t *key, const uint8_t *iv,
                       const uint8_t *src, uint8_t *dst, int nbytes,
                       bool encrypt) const = 0;
};

// aes_256_xts.cpp

constexpr int kAesBlockSize = 16;
constexpr int kAes256KeySize = 32;
constexpr int kAes256XtsKeySize = 2 * kAes256KeySize;

class Aes256XtsCipher : public Cipher {
 public:
  int keysize() const { return kAes256XtsKeySize; }
  int ivsize() const { return kAesBlockSize; }

 private:
  bool DoCrypt(const uint8_t *key, const uint8_t *iv, const uint8_t *src,
               uint8_t *dst, int nbytes, bool encrypt) const;
};

// adiantum.cpp

constexpr int kAdiantumKeySize = 32;

// It's variable-length in general, but the Linux kernel always uses 32.
constexpr int kAdiantumIVSize = 32;

class AdiantumCipher : public Cipher {
 public:
  int keysize() const { return kAdiantumKeySize; }
  int ivsize() const { return kAdiantumIVSize; }

 private:
  bool DoCrypt(const uint8_t *key, const uint8_t *iv, const uint8_t *src,
               uint8_t *dst, int nbytes, bool encrypt) const;
};

// utils.cpp

std::string Errno();

void DeleteRecursively(const std::string &path);

void RandomBytesForTesting(std::vector<uint8_t> &bytes);

std::vector<uint8_t> GenerateTestKey(size_t size);

std::string BytesToHex(const std::vector<uint8_t> &bytes);

template <size_t N>
static inline std::string BytesToHex(const uint8_t (&array)[N]) {
  return BytesToHex(std::vector<uint8_t>(&array[0], &array[N]));
}

bool GetFirstApiLevel(int *first_api_level);

constexpr int kFilesystemUuidSize = 16;

struct FilesystemUuid {
  uint8_t bytes[kFilesystemUuidSize];
};

struct DiskMapEntry {
  std::string fs_blk_device;
  std::string raw_blk_device;
  int64_t start_blkaddr;
  int64_t end_blkaddr;
};

struct FilesystemInfo {
  std::string type;
  FilesystemUuid uuid;

  // The filesystem's block devices in sorted order of filesystem block address.
  // The covered addresses are guaranteed to be contiguous and non-overlapping.
  // The first device, starting at address 0, is the filesystem's "main" block
  // device.
  // Note, the disk_map's end_blkaddr is inclusive like below:
  // [disk number]   [start_blkaddr]   [end_blkaddr]
  // 0               0                 X - 1
  // 1               X                 Y - 1
  // 2               Y                 Z
  std::vector<DiskMapEntry> disk_map;
};

bool GetFilesystemInfo(const std::string &mountpoint, FilesystemInfo *info);

bool VerifyDataRandomness(const std::vector<uint8_t> &bytes);

bool CreateHwWrappedKey(std::vector<uint8_t> *master_key,
                        std::vector<uint8_t> *exported_key);

bool DeriveHwWrappedEncryptionKey(const std::vector<uint8_t> &master_key,
                                  std::vector<uint8_t> *enc_key);

bool DeriveHwWrappedRawSecret(const std::vector<uint8_t> &master_key,
                              std::vector<uint8_t> *secret);
}  // namespace kernel
}  // namespace android
