/* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0"
 *
 * Written by Nir Drucker, Shay Gueron and Dusan Kostic,
 * AWS Cryptographic Algorithms Group.
 */

#include "aes_ctr_prf.h"
#include "utilities.h"

ret_t init_aes_ctr_prf_state(OUT aes_ctr_prf_state_t *s,
                             IN const uint32_t        max_invokations,
                             IN const seed_t *seed)
{
  if(0 == max_invokations) {
    BIKE_ERROR(E_AES_CTR_PRF_INIT_FAIL);
  }

  // Set the key schedule (from seed).
  // Make sure the size matches the AES256 key size.
  DEFER_CLEANUP(aes256_key_t key, aes256_key_cleanup);

  bike_static_assert(sizeof(*seed) == sizeof(key.raw), seed_size_equals_ky_size);
  bike_memcpy(key.raw, seed->raw, sizeof(key.raw));

  POSIX_GUARD(aes256_key_expansion(&s->ks, &key));

  // Initialize buffer and counter
  s->ctr.u.qw[0]    = 0;
  s->ctr.u.qw[1]    = 0;
  s->buffer.u.qw[0] = 0;
  s->buffer.u.qw[1] = 0;

  s->pos             = AES256_BLOCK_BYTES;
  s->rem_invokations = max_invokations;

  DMSG("    Init aes_prf_ctr state:\n");
  DMSG("      s.pos = %d\n", s->pos);
  DMSG("      s.rem_invokations = %u\n", s->rem_invokations);

  return SUCCESS;
}

_INLINE_ ret_t perform_aes(OUT uint8_t *ct, IN OUT aes_ctr_prf_state_t *s)
{
  // Ensure that the CTR is large enough
  bike_static_assert(
    ((sizeof(s->ctr.u.qw[0]) == 8) && (BIT(33) >= MAX_AES_INVOKATION)),
    ctr_size_is_too_small);

  if(0 == s->rem_invokations) {
    BIKE_ERROR(E_AES_OVER_USED);
  }

  POSIX_GUARD(aes256_enc(ct, s->ctr.u.bytes, &s->ks));

  s->ctr.u.qw[0]++;
  s->rem_invokations--;

  return SUCCESS;
}

ret_t aes_ctr_prf(OUT uint8_t *a,
                  IN OUT aes_ctr_prf_state_t *s,
                  IN const uint32_t           len)
{
  // When Len is smaller than use what's left in the buffer,
  // there is no need for additional AES invocations.
  if((len + s->pos) <= AES256_BLOCK_BYTES) {
    bike_memcpy(a, &s->buffer.u.bytes[s->pos], len);
    s->pos += len;

    return SUCCESS;
  }

  // If s.pos != AES256_BLOCK_BYTES then copy what's left in the buffer.
  // Else copy zero bytes
  uint32_t idx = AES256_BLOCK_BYTES - s->pos;
  bike_memcpy(a, &s->buffer.u.bytes[s->pos], idx);

  // Init s.pos
  s->pos = 0;

  // Copy full AES blocks
  while((len - idx) >= AES256_BLOCK_BYTES) {
    POSIX_GUARD(perform_aes(&a[idx], s));
    idx += AES256_BLOCK_BYTES;
  }

  POSIX_GUARD(perform_aes(s->buffer.u.bytes, s));

  // Copy the tail
  s->pos = len - idx;
  bike_memcpy(&a[idx], s->buffer.u.bytes, s->pos);

  return SUCCESS;
}
