/*
 * Copyright 2010 Trident Microsystems (Far East) Ltd. All rights reserved.
 * All prior copyright rights in this work and the accompanying software
 * products transferred to Trident Microsystems (Far East) Ltd. by written
 * agreement.
 */
/*****************************************************************************/
/* Copyright (c) 2010 NXP B.V.                                               */
/*                                                                           */
/* This program is free software; you can redistribute it and/or modify      */
/* it under the terms of the GNU General Public License as published by      */
/* the Free Software Foundation, using version 2 of the License.             */
/*                                                                           */
/* This program is distributed in the hope that it will be useful,           */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of            */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the              */
/* GNU General Public License for more details.                              */
/*                                                                           */
/* You should have received a copy of the GNU General Public License         */
/* along with this program; if not, write to the Free Software               */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307       */
/* USA.                                                                      */
/*                                                                           */
/*****************************************************************************/
/*

Rev Date        Author      Comments
--------------------------------------------------------------------------------
  1 20040917    amccurdy    Original created.
  2 20070703    amccurdy    Update imga -> aimage, tweak for STB225
  3 20080215    batelaan    Update file copyright/license text
--------------------------------------------------------------------------------
*/

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

#include <openssl/bn.h>
#include <openssl/sha.h>

#include "aimage.h"
#include "aimage_keyfile.h"


typedef struct
{
    void *mbuf;                                     /* malloc'ed buffer */
    aimage_v1_header_t *header;                     /* data follows directly after header */
}
image_t;

#define IMGA_PUBLIC_KEY         (&aimage_key)
#define MODULUS                 ((unsigned int *) &aimage_key.mod_data[1])
#define MOD_DASH                (aimage_key.mod_data[0])

#define IMAGE_LENGTH_MAX        (128 * 1024 * 1024)

#ifndef REVISION_STRING
#define REVISION_STRING 	"UNKNOWN_REVISION"
#endif


/*******************************************************************************
*******************************************************************************/

static char *appname;

static int verbose_load_image;


/*******************************************************************************
*******************************************************************************/

static int display_image_details (image_t *image, unsigned int max_length)
{
   return aimage_v1_display_info (image->header, max_length, IMGA_PUBLIC_KEY);
}

static int blob_dump_contents (image_t *blobimage)
{
   int count = 0;
   aimage_v1_header_t *blob_header = blobimage->header;
   unsigned char *base_address = ((unsigned char *) blob_header) + blob_header->data_start_offset;
   unsigned char *limit_address = ((unsigned char *) blob_header) + blob_header->length;

   while ((limit_address - base_address) > sizeof(aimage_v1_header_t)) {
      unsigned int length, start_offset;
      aimage_v1_header_t *subimage = (aimage_v1_header_t *) base_address;

      /*
         If the blob image was generated with lots of trailing padding, then
         the above test for 'more than a headers worth of data' isn't a reliable
         way to detect that more sub images are expected. Therefore sanity check
         any potential subimage here and bail out of if we don't find one...
      */
      if (aimage_v1_sanity_check (subimage, 0, (limit_address - base_address)) != 0)
         break;

      start_offset = base_address - ((unsigned char *) blob_header);
      length = subimage->length;

      /* fixup fake double header for cramfs images... */
      if ((++count == 1) && (subimage->type == IMG_TYPE_INITFS_CRAMFS)) {
         start_offset -= sizeof(aimage_v1_header_t);
         length += sizeof(aimage_v1_header_t);
      }

//    printf ("0x%08x, 0x%08x, (%8d), ", (unsigned int) base_address, (unsigned int) limit_address, (limit_address - base_address));
      printf ("%s %d %d\n", aimage_type_to_string (subimage->type), start_offset, length);

      base_address += subimage->length;
   }

   return 0;
}

static int load_image (image_t *image, char *filename)
{
   int fd;
   struct stat file_info;
   unsigned char *buffer;
   unsigned int buffer_length;
   unsigned int data_length;

   if ((fd = open (filename, O_RDONLY)) < 0) {
      char buf[2048];
      memset (buf, 0, sizeof(buf));
      snprintf (buf, sizeof(buf) - 1, "\nError opening '%s' for reading ", filename);
      perror (buf);
      fprintf (stderr, "\n");
      exit (1);
   }

   fstat (fd, &file_info);
   data_length = file_info.st_size;                                 /* length of original data */

   buffer_length = (2 * sizeof(aimage_v1_header_t)) +
                   data_length + HOST_STRING_LENGTH_MAX + 3;        /* worst case - probably less */

   buffer = malloc (buffer_length);

   if (buffer == NULL) {
      fprintf (stderr, "Unable to allocate %d bytes of buffer memory\n", buffer_length);
      exit (1);
   }

   image->mbuf = buffer;
   image->header = (aimage_v1_header_t *) buffer;

   if (verbose_load_image)
      fprintf (stderr, "Reading %8d bytes from '%s'...\n", data_length, filename);

   if (read (fd, buffer, data_length) != data_length) {
      char buf[2048];
      memset (buf, 0, sizeof(buf));
      snprintf (buf, sizeof(buf) - 1, "\nError reading from '%s' ", filename);
      perror (buf);
      fprintf (stderr, "\n");
      exit (1);
   }

   close (fd);

   return 0;
}

static void syntax (void)
{
   char spacer[16];
   int appname_length = strlen (appname);

   if (appname_length >= sizeof(spacer))
      appname_length = (sizeof(spacer) - 1);

   memset (spacer, 0, sizeof(spacer));
   memset (spacer, ' ', appname_length);

   fprintf (stderr,
            "\n"
            "Usage: %s <input file>\n"
            "\n",
            appname);

   exit (1);
}

int main (int argc, char *argv[])
{
   char *s;
   int result;
   image_t image;
   int verify_hash = 0;
   int verify_signature = 0;
   int blobdump = 0;
   int keyinfo = 0;

   char *input_filename = NULL;

   appname = argv[0];

   for (s = appname; *s != '\0'; )
      if (*s++ == '/')
         appname = s;

   if ((strncmp (appname, "aimage_verify_", 14) != 0) &&
       (strncmp (appname, "aimage_verify.", 14) != 0))
   {
      appname = "aimage_verify";
   }

   printf ("\n%s: (c) Andre McCurdy, Trident Microsystems. Rev %s (%s)\n\n", appname, REVISION_STRING, __DATE__);

   if ((argc == 3) && (strcmp (argv[1], "--verifyhash") == 0)) {
      input_filename = argv[2];
      verify_hash = 1;
   }
   else if ((argc == 3) && (strcmp (argv[1], "--verifysignature") == 0)) {
      input_filename = argv[2];
      verify_signature = 1;
   }
   else if ((argc == 3) && (strcmp (argv[1], "--blobdump") == 0)) {
      input_filename = argv[2];
      blobdump = 1;
   }
   else if ((argc == 2) && (strcmp (argv[1], "--keyinfo") == 0))
      keyinfo = 1;
   else if (argc == 2)
      input_filename = argv[1];
   else {
      printf ("\nPlease specify an input filename\n");
      syntax();
   }

   if (keyinfo) {
      printf ("aimage_pubkey_mod_bits: %d\n", aimage_pubkey_mod_bits (IMGA_PUBLIC_KEY));
      printf ("aimage_pubkey_exp     : %d\n\n", aimage_pubkey_exp (IMGA_PUBLIC_KEY));
      return 0;
   }

   load_image (&image, input_filename);

   if (verify_hash) {
      result = aimage_v1_verify_hash (image.header, 0, IMAGE_LENGTH_MAX);
      printf ("Hash check %s\n", (result == 0) ? "passed" : "failed");
      return result;
   }

   if (verify_signature) {
      result = aimage_v1_verify_signature_with_given_key (image.header, IMGA_PUBLIC_KEY, 0, IMAGE_LENGTH_MAX);
      printf ("Signature check %s\n", (result == 0) ? "passed" : "failed");
      return result;
   }

   if (blobdump) {
      result = aimage_v1_verify_signature_with_given_key (image.header, IMGA_PUBLIC_KEY, IMG_TYPE_FIRMWARE_BLOB, IMAGE_LENGTH_MAX);
      if (result != 0) {
         printf ("Signature check failed\n");
         return result;
      }
      return blob_dump_contents (&image);
   }

   verbose_load_image = 1;
   display_image_details (&image, IMAGE_LENGTH_MAX);  /* fixme: use a realistic max length... */

   return 0;
}


