/* Copyright (c) 2021 Michael Baeuerle
 *
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to deal in the Software without restriction, including
 * without limitation the rights to use, copy, modify, merge, publish,
 * distribute, and/or sell copies of the Software, and to permit persons
 * to whom the Software is furnished to do so, provided that the above
 * copyright notice(s) and this permission notice appear in all copies of
 * the Software and that both the above copyright notice(s) and this
 * permission notice appear in supporting documentation.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
 * OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
 * HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY
 * SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER
 * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
 * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * Except as contained in this notice, the name of a copyright holder
 * shall not be used in advertising or otherwise to promote the sale, use
 * or other dealings in this Software without prior written authorization
 * of the copyright holder.
 *
 * SPDX-License-Identifier: ICU
 */

/* Create Cancel-Lock header field
 *
 * Example program for POSIX operating systems.
 *
 * The local secret can be loaded from a file with portable stdio too.
 * This example uses the POSIX API to bypass the stdio buffering.
 * For programs with longer runtime the function cl_clear_secret() can
 * be used to overwrite the local secret in memory (after use). In this
 * case no additional copies are desired.
 *
 * For insertion into a message header, the canonical line termination
 * CRLF must be used instead.
 */

#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <libcanlock-3/canlock.h>


/* 512 bit secret, large enough with all supported hash algorithms */
#define SECRET_SIZE  (size_t) 64
static unsigned char secret[SECRET_SIZE];


/*
 * Example User-ID and Message-ID
 *
 * For the algorithm recommended by RFC 8315 in Section 4, the Message-ID
 * without surrounding whitespace is used for <mid>.
 *
 * An optional User-ID <uid> can be prepended.
 *
 * Note that RFC 8315 uses angle brackets like quotes in the continuous text.
 * Therefore <uid> or <mid> does not mean that additional angle brackets are
 * required. A User-ID must not contain angle brackets and a Message-ID always
 * includes angle brackets (as defined by RFC 5536 in Section 3.1.3).
 */
static const unsigned char umid[] = "testuser<ThisIsAMessageID@example.org>";


int error_handler(const char *msg)
{
   fprintf(stderr, "Error: %s\n", msg);
   exit(EXIT_FAILURE);
}


/*
 * Load local secret from file
 *
 * Only the owner (of the secret) should have read permission for the file.
 * The secret should be a cryptographically random value.
 * The size should be at least the size of the hash used for <scheme>.
 */
int load_secret(void)
{
   /* Open file with secret */
   int fd = open("secret_512bits", O_RDONLY);
   if (-1 == fd)
      error_handler("Opening file failed");

   /* Read secret from file */
   {
      ssize_t num;
      size_t i = 0;
      do
      {
         num = read(fd, &secret[i], SECRET_SIZE - i);
         if (0 == num)
            error_handler("Secret is too short");
         if (0 < num)
            i += (size_t) num;
      }
      while ( (ssize_t) SECRET_SIZE > num ||
              ((ssize_t) -1 == num && EINTR == errno) );

      (void) close(fd);

      if ((ssize_t) -1 == num)
         return -1;
      else
         return 0;
   }
}


/* Create an (initial) Cancel-Lock header field */
int main(void)
{
   /* Load secret */
   if (-1 == load_secret())
      error_handler("Reading secret failed");

   /* Create (initial) Cancel-Lock header field */
   {
      char *lock = cl_get_lock(CL_SHA512,
                               secret, SECRET_SIZE,
                               umid, strlen(umid));
      if (NULL == lock)
         error_handler("Creating Cancel-Lock failed");

      printf("Cancel-Lock: %s\n", lock);

      /* Release memory allocated by libcanlock */
      free(lock);
   }

   exit(EXIT_SUCCESS);
}


/* EOF */
