#include <string.h>

#include "proof.h"

#include "crypto-internal.h"

static bool
verify_leaf_signature (const struct sigsum_pubkey *key, const struct sigsum_hash *checksum,
		       const struct sigsum_signature *signature)
{
  /* The signed message consists of a prefix, a NUL character, and the
     checksum, total 56 octets. */
  static const uint8_t prefix[24] = "sigsum.org/v1/tree-leaf\0";
  uint8_t data[sizeof(prefix) + SIGSUM_DIGEST_SIZE];
  memcpy (data, prefix, sizeof (prefix));
  /* TODO: Micro-optimization: eliminate this memcpy, by calling
     sigsum_hash_digest to write the digest at the right place. */
  memcpy (data + sizeof (prefix), checksum->h, SIGSUM_DIGEST_SIZE);

  return _sigsum_signature_verify (key, sizeof (data), data, signature);
}

bool
sigsum_proof_verify (const struct sigsum_proof *proof, const struct sigsum_hash *msg,
		     /* Submitters must be sorted by key hash. */
		     const struct sigsum_key_map *submitters,
		     const struct sigsum_policy *policy,
		     const char **err, void *scratch)
{
  struct sigsum_hash_ctx ctx;
  struct sigsum_hash checksum;
  struct sigsum_hash leaf_hash;

#define FAIL(error_msg) do {	\
    if (err) *err = error_msg;	\
    return false;		\
} while (0)

  if (!sigsum_policy_verify_tree_head (policy, &proof->tree_head,
				       &proof->log_key_hash,
				       &proof->log_signature,
				       proof->num_cosignatures,
				       proof->cosignatures,
				       err, scratch))
    return false;

  const struct sigsum_pubkey *submitter_key
    = sigsum_key_map_lookup (submitters, &proof->submitter_key_hash,
			     NULL);
  if (!submitter_key)
    FAIL ("unknown submitter key");

  _sigsum_hash_init (&ctx);
  _sigsum_hash_update (&ctx, sizeof (msg->h), msg->h);
  _sigsum_hash_digest (&ctx, &checksum);

  if (!verify_leaf_signature (submitter_key, &checksum, &proof->leaf_signature))
    FAIL ("invalid leaf signature");

  static const uint8_t leaf_prefix = 0;
  _sigsum_hash_update (&ctx, 1, &leaf_prefix);
  _sigsum_hash_update (&ctx, sizeof (checksum.h), checksum.h);
  _sigsum_hash_update (&ctx, sizeof (proof->leaf_signature.s), proof->leaf_signature.s);
  _sigsum_hash_update (&ctx, sizeof (proof->submitter_key_hash.h), proof->submitter_key_hash.h);
  _sigsum_hash_digest (&ctx, &leaf_hash);

  if (!sigsum_verify_inclusion (&leaf_hash, proof->leaf_index, &proof->tree_head,
				proof->path_length, proof->path))
    FAIL ("invalid inclusion proof");

  return true;
}
