/*############################################################################
  # Copyright 2017 Intel Corporation
  #
  # 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.
  ############################################################################*/
/// Host non-revoked proof helper implementation
/*! \file */

#include "epid/member/src/nrprove_commitment.h"

#include <stdint.h>
#include "epid/common/math/finitefield.h"
#include "epid/common/src/memory.h"

/// Handle SDK Error with Break
#define BREAK_ON_EPID_ERROR(ret) \
  if (kEpidNoErr != (ret)) {     \
    break;                       \
  }

#pragma pack(1)
/// Storage for values to create commitment in NrProve algorithm
typedef struct NrProveCommitValues {
  BigNumStr p;    //!< A large prime (256-bit)
  G1ElemStr g1;   //!< Generator of G1 (512-bit)
  G1ElemStr B;    //!< (element of G1): part of basic signature Sigma0
  G1ElemStr K;    //!< (element of G1): part of basic signature Sigma0
  G1ElemStr rlB;  //!< (element of G1): one entry in SigRL
  G1ElemStr rlK;  //!< (element of G1): one entry in SigRL
  NrProveCommitOutput commit_out;  //!< output of NrProveCommit
  uint8_t msg[1];                  //!< message
} NrProveCommitValues;
#pragma pack()

EpidStatus HashNrProveCommitment(FiniteField* Fp, HashAlg hash_alg,
                                 G1ElemStr const* B_str, G1ElemStr const* K_str,
                                 SigRlEntry const* sigrl_entry,
                                 NrProveCommitOutput const* commit_out,
                                 void const* msg, size_t msg_len,
                                 FpElemStr* c_str) {
  EpidStatus sts = kEpidErr;
  FfElement* c = NULL;
  NrProveCommitValues* commit_values = NULL;

  if (!Fp || !B_str || !K_str || !sigrl_entry || !commit_out ||
      (0 != msg_len && !msg) || !c_str) {
    return kEpidBadArgErr;
  }

  if (msg_len >
      ((SIZE_MAX - sizeof(*commit_values)) + sizeof(*commit_values->msg)))
    return kEpidBadArgErr;

  do {
    size_t const commit_len =
        sizeof(*commit_values) - sizeof(*commit_values->msg) + msg_len;
    Epid2Params params = {
#include "epid/common/src/epid2params_ate.inc"
    };

    commit_values = SAFE_ALLOC(commit_len);
    if (!commit_values) {
      sts = kEpidMemAllocErr;
      BREAK_ON_EPID_ERROR(sts);
    }

    commit_values->p = params.p;
    commit_values->g1 = params.g1;
    commit_values->B = *B_str;
    commit_values->K = *K_str;
    commit_values->rlB = sigrl_entry->b;
    commit_values->rlK = sigrl_entry->k;
    commit_values->commit_out = *commit_out;

    // commit_values is allocated such that there are msg_len bytes available
    // starting at commit_values->msg
    if (msg) {
      // Memory copy is used to copy a message of variable length
      if (0 != memcpy_S(&commit_values->msg[0], msg_len, msg, msg_len)) {
        sts = kEpidBadArgErr;
        BREAK_ON_EPID_ERROR(sts);
      }
    }

    sts = NewFfElement(Fp, &c);
    BREAK_ON_EPID_ERROR(sts);

    // 7.  The member computes c = Fp.hash(p || g1 || B || K || B' ||
    //     K' || T || R1 || R2 || m).
    sts = FfHash(Fp, commit_values, commit_len, hash_alg, c);
    BREAK_ON_EPID_ERROR(sts);

    sts = WriteFfElement(Fp, c, c_str, sizeof(*c_str));
    BREAK_ON_EPID_ERROR(sts);

    sts = kEpidNoErr;
  } while (0);

  SAFE_FREE(commit_values);
  DeleteFfElement(&c);

  return sts;
}