#include <string.h>

#include <nettle/base64.h>

#include "ascii-internal.h"
#include "key-map.h"
#include "sort-internal.h"
#include "xalloc.h"

/* TODO: Duplicated with policy-parse.c */
DEFINE_LIST (key_list, key_alloc, struct sigsum_key_with_hash);

bool
sigsum_key_map_parse_ssh (struct sigsum_key_map *map,
			  size_t size, const char *data,
			  struct sigsum_error *err)
{
  struct sigsum_buffer buffer = { size, data };
  struct key_list keys = { 0, 0, NULL };
  size_t lineno = 0;

#define FAIL(error_msg) do {			\
    if (err)					\
      {						\
	err->msg = error_msg;			\
	err->line = lineno;			\
      }						\
    goto fail;					\
  } while (0)

  while (buffer.size > 0)
    {
      struct sigsum_buffer line;
      struct sigsum_buffer token;

      _sigsum_buffer_line (&buffer, &line);
      lineno++;

      /* Ignore comment lines. Must start with an unindented # character. */
      if (line.size && line.data[0] == '#')
	continue;

      _sigsum_buffer_token (&line, &token);

      if (!token.size)
	/* Empty line with only whitspace, ignore. */
	continue;

      /* Require an "ssh-ed25519" key */
      if (token.size != 11 || memcmp(token.data, "ssh-ed25519", 11) != 0)
	FAIL ("invalid key, expected ssh-ed25519");

      _sigsum_buffer_token (&line, &token);

      if (token.size != 68)
	FAIL ("invalid key, unexpected base64 blob size");

      uint8_t blob[BASE64_DECODE_LENGTH(68)];
      size_t blob_size = sizeof(blob);
      struct base64_decode_ctx ctx;
      base64_decode_init (&ctx);
      if (!(base64_decode_update (&ctx, &blob_size, blob, token.size, token.data)
	    && base64_decode_final (&ctx)))
	FAIL ("invalid key, invalid base64");

      static const uint8_t prefix[] = {
	0, 0, 0, 11, 's', 's', 'h', '-', 'e', 'd', '2', '5', '5', '1', '9',
	0, 0, 0, 32
      };
      if (blob_size != sizeof (prefix) + SIGSUM_PUBKEY_SIZE)
	FAIL ("invalid key, unexpected blob size");

      if (memcmp (blob, prefix, sizeof (prefix)) != 0)
	FAIL ("invalid key, not an ed25519 blob");

      struct sigsum_key_with_hash *entry = key_alloc (&keys);
      memcpy (entry->key.k, blob + sizeof (prefix), sizeof (entry->key.k));
      sigsum_hash_key (&entry->hash, &entry->key);
    }

  if (!keys.size)
    FAIL("no valid keys");

  if (!SIGSUM_SORT_BY_HASH (keys.size, keys.list, hash))
    FAIL ("duplicate keys");

#undef FAIL

  map->num_keys = keys.size;
  map->keys = keys.list;
  return true;

 fail:
  free (keys.list);
  return false;
}

void
sigsum_key_map_clear (struct sigsum_key_map *map)
{
  XFREE (map->keys);
}
