Browse Source

Merge pull request #369 from libtom/demo/aesgcm

Add aesgcm demo
Steffen Jaeckel 7 years ago
parent
commit
8b6d9dba76
5 changed files with 443 additions and 2 deletions
  1. 2 0
      .gitignore
  2. 146 0
      demos/aesgcm.c
  3. 91 0
      demos/gcm-file/gcm_file.c
  4. 201 0
      demos/gcm-file/gcm_filehandle.c
  5. 3 2
      makefile_include.mk

+ 2 - 0
.gitignore

@@ -22,6 +22,8 @@ doc/crypt.pdf
 doc/refman.pdf
 
 # *nix/windows test executables
+aesgcm
+aesgcm.exe
 constants
 constants.exe
 ltcrypt

+ 146 - 0
demos/aesgcm.c

@@ -0,0 +1,146 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+
+/**
+  @file aesgcm.c
+  AES128-GCM demo - file en-&decryption, Steffen Jaeckel
+  Uses the format: |ciphertext|tag-16-bytes|
+*/
+
+#define _GNU_SOURCE
+
+#include <tomcrypt.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "gcm-file/gcm_filehandle.c"
+#include "gcm-file/gcm_file.c"
+
+
+static off_t fsize(const char *filename)
+{
+   struct stat st;
+
+   if (stat(filename, &st) == 0) return st.st_size;
+
+   return -1;
+}
+
+static int mv(const char *old_name, const char *new_name)
+{
+   int fd;
+   if (rename(old_name, new_name) == -1) return -1;
+   fd = open(new_name, 0);
+   if (fd == -1) return -1;
+   if (fsync(fd) != 0) goto OUT;
+   syncfs(fd);
+OUT:
+   close(fd);
+   return 0;
+}
+
+/* https://stackoverflow.com/a/23898449 */
+static void scan_hex(const char* str, uint8_t* bytes, size_t blen)
+{
+   uint8_t  pos;
+   uint8_t  idx0;
+   uint8_t  idx1;
+
+   const uint8_t hashmap[] =
+   {
+     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, /* 01234567 */
+     0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 89:;<=>? */
+     0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, /* @ABCDEFG */
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* HIJKLMNO */
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* PQRSTUVW */
+     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* XYZ[\]^_ */
+     0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x00, /* `abcdefg */
+   };
+
+   for (pos = 0; ((pos < (blen*2)) && (pos < strlen(str))); pos += 2)
+   {
+      idx0 = (uint8_t)(str[pos+0] & 0x1F) ^ 0x10;
+      idx1 = (uint8_t)(str[pos+1] & 0x1F) ^ 0x10;
+      bytes[pos/2] = (uint8_t)(hashmap[idx0] << 4) | hashmap[idx1];
+   }
+}
+
+static void die(int ret)
+{
+   fprintf(stderr, "Usage: aesgcm <-e|-d> <infile> <outfile> <96 char hex-string 'IV | key'>\n");
+   exit(ret);
+}
+
+int main(int argc, char **argv)
+{
+   int ret = 0, err, arg, direction, res, tmp;
+   size_t keylen;
+   uint8_t keybuf[48] = {0};
+   char *out = NULL;
+   const char *mode, *in_file, *out_file, *key_string;
+
+   if (argc < 5) die(__LINE__);
+
+   arg = 1;
+   mode = argv[arg++];
+   in_file = argv[arg++];
+   out_file = argv[arg++];
+   key_string = argv[arg++];
+
+   if(strcmp(mode, "-d") == 0) direction = GCM_DECRYPT;
+   else if(strcmp(mode, "-e") == 0) direction = GCM_ENCRYPT;
+   else die(__LINE__);
+
+   if (fsize(in_file) <= 0) die(__LINE__);
+
+   keylen = strlen(key_string);
+   if (keylen != 96) die(__LINE__);
+
+   scan_hex(key_string, keybuf, sizeof(keybuf));
+
+   register_all_ciphers();
+
+   if(asprintf(&out, "%s-XXXXXX", out_file) < 0) die(__LINE__);
+   if((tmp = mkstemp(out)) == -1) {
+      ret = __LINE__;
+      goto cleanup;
+   }
+   close(tmp);
+   if((err = gcm_file(find_cipher("aes"), &keybuf[16], 32, keybuf, 16, NULL, 0, in_file, out, 16, direction, &res)) != CRYPT_OK) {
+      fprintf(stderr, "boooh %s\n", error_to_string(err));
+      ret = __LINE__;
+      goto cleanup;
+   }
+
+   if(res != 1) {
+      ret = __LINE__;
+   }
+   else
+   {
+      if (mv(out, out_file) != 0) ret = __LINE__;
+   }
+
+cleanup:
+   if(ret != 0) unlink(out);
+   free(out);
+
+
+   return ret;
+}
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 91 - 0
demos/gcm-file/gcm_file.c

@@ -0,0 +1,91 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+
+#include "tomcrypt.h"
+
+/**
+  @file gcm_file.c
+  GCM process a file, Steffen Jaeckel
+*/
+
+#ifdef LTC_GCM_MODE
+#ifndef LTC_NO_FILE
+
+/**
+  Process a file.
+
+  c.f. gcm_filehandle() for basic documentation.
+
+  It is possible, that in error-cases the 'out' file
+  will be created and after the error occurred it will
+  be removed again.
+
+  @param cipher            Index of cipher to use
+  @param key               The secret key
+  @param keylen            The length of the secret key
+  @param IV                The initial vector
+  @param IVlen             The length of the initial vector
+  @param adata             The additional authentication data (header)
+  @param adatalen          The length of the adata
+  @param in                The input file
+  @param out               The output file
+  @param taglen            The MAC tag length
+  @param direction         Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT)
+  @return CRYPT_OK on success
+ */
+int gcm_file(      int           cipher,
+               const unsigned char *key,    unsigned long keylen,
+               const unsigned char *IV,     unsigned long IVlen,
+               const unsigned char *adata,  unsigned long adatalen,
+                        const char *in,
+                        const char *out,
+                     unsigned long taglen,
+                               int direction,
+                               int *res)
+{
+    int        err;
+    FILE *f_in = NULL, *f_out = NULL;
+
+    LTC_ARGCHK(in  != NULL);
+    LTC_ARGCHK(out != NULL);
+    LTC_ARGCHK(res != NULL);
+
+    f_in = fopen(in, "rb");
+    if (f_in == NULL) {
+       err = CRYPT_FILE_NOTFOUND;
+       goto LBL_ERR;
+    }
+    f_out = fopen(out, "w+b");
+    if (f_out == NULL) {
+       err = CRYPT_FILE_NOTFOUND;
+       goto LBL_ERR;
+    }
+
+    err = gcm_filehandle(cipher, key, keylen, IV, IVlen, adata, adatalen, f_in, f_out, taglen, direction, res);
+
+LBL_ERR:
+    if (f_out != NULL && fclose(f_out) != 0) {
+       err = CRYPT_ERROR;
+    }
+    if (*res != 1) {
+       remove(out);
+    }
+    if (f_in != NULL && fclose(f_in) != 0) {
+       err = CRYPT_ERROR;
+    }
+
+    return err;
+}
+#endif
+#endif
+
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 201 - 0
demos/gcm-file/gcm_filehandle.c

@@ -0,0 +1,201 @@
+/* LibTomCrypt, modular cryptographic library -- Tom St Denis
+ *
+ * LibTomCrypt is a library that provides various cryptographic
+ * algorithms in a highly modular and flexible manner.
+ *
+ * The library is free for all purposes without any express
+ * guarantee it works.
+ */
+
+#include "tomcrypt.h"
+
+/**
+  @file gcm_filehandle.c
+  GCM process a filehandle, Steffen Jaeckel
+*/
+
+#ifdef LTC_GCM_MODE
+#ifndef LTC_NO_FILE
+
+#if defined(_MSC_VER)
+#define ftruncate _chsize
+#else
+#include <unistd.h>
+#endif
+
+/**
+  Process a filehandle.
+
+  This uses the widely established scheme where the tag is appended
+  to the ciphertext.
+
+  encrypted_file = aesgcm(plain_file) | aesgcm_tag(plain_file)
+
+  Depending on 'direction' this function does:
+
+    Encrypt file 'in' to 'out' and append the tag of length 'taglen'
+    to 'out'.
+
+      or
+
+    Decrypt file 'in' to 'out' and check the tag of length 'taglen'
+    and strip the tag from 'in'.
+
+  In error-cases 'out' will be zeroed out first and then truncated to
+  a length of 0.
+
+  @param cipher            Index of cipher to use
+  @param key               The secret key
+  @param keylen            The length of the secret key
+  @param IV                The initial vector
+  @param IVlen             The length of the initial vector
+  @param adata             The additional authentication data (header)
+  @param adatalen          The length of the adata
+  @param in                The input file
+  @param out               The output file
+  @param taglen            The MAC tag length
+  @param direction         Encrypt or Decrypt mode (GCM_ENCRYPT or GCM_DECRYPT)
+  @return CRYPT_OK on success
+ */
+int gcm_filehandle(      int           cipher,
+                   const unsigned char *key,    unsigned long keylen,
+                   const unsigned char *IV,     unsigned long IVlen,
+                   const unsigned char *adata,  unsigned long adatalen,
+                                  FILE *in,
+                                  FILE *out,
+                         unsigned long taglen,
+                                   int direction,
+                                   int *res)
+{
+    void      *orig;
+    gcm_state *gcm;
+    int        err;
+    unsigned char *buf, tag[16];
+    size_t x, tot_data;
+    unsigned long tag_len;
+
+    LTC_ARGCHK(in  != NULL);
+    LTC_ARGCHK(out != NULL);
+    LTC_ARGCHK(res != NULL);
+
+    *res = 0;
+
+    if ((err = cipher_is_valid(cipher)) != CRYPT_OK) {
+       return err;
+    }
+
+#ifndef LTC_GCM_TABLES_SSE2
+    orig = gcm = XMALLOC(sizeof(*gcm));
+#else
+    orig = gcm = XMALLOC(sizeof(*gcm) + 16);
+#endif
+    if (gcm == NULL) {
+        return CRYPT_MEM;
+    }
+
+    if ((buf = XMALLOC(LTC_FILE_READ_BUFSIZE)) == NULL) {
+        XFREE(gcm);
+        return CRYPT_MEM;
+    }
+
+   /* Force GCM to be on a multiple of 16 so we can use 128-bit aligned operations
+    * note that we only modify gcm and keep orig intact.  This code is not portable
+    * but again it's only for SSE2 anyways, so who cares?
+    */
+#ifdef LTC_GCM_TABLES_SSE2
+   if ((unsigned long)gcm & 15) {
+      gcm = (gcm_state *)((unsigned long)gcm + (16 - ((unsigned long)gcm & 15)));
+   }
+#endif
+
+    if ((err = gcm_init(gcm, cipher, key, keylen)) != CRYPT_OK) {
+       goto LBL_ERR;
+    }
+    if ((err = gcm_add_iv(gcm, IV, IVlen)) != CRYPT_OK) {
+       goto LBL_ERR;
+    }
+    if ((err = gcm_add_aad(gcm, adata, adatalen)) != CRYPT_OK) {
+       goto LBL_ERR;
+    }
+
+    fseek(in, 0, SEEK_END);
+    tot_data = ftell(in);
+    if (direction == GCM_DECRYPT) {
+       tot_data -= taglen;
+    }
+    rewind(in);
+    do {
+       x = MIN(tot_data, LTC_FILE_READ_BUFSIZE);
+       x = fread(buf, 1, x, in);
+       tot_data -= x;
+       if ((err = gcm_process(gcm, buf, (unsigned long)x, buf, direction)) != CRYPT_OK) {
+          goto LBL_CLEANBUF;
+       }
+       if(fwrite(buf, 1, x, out) != x) {
+          err = CRYPT_ERROR;
+          goto LBL_CLEANBUF;
+       }
+    } while (x == LTC_FILE_READ_BUFSIZE);
+
+    tag_len = taglen;
+    if ((err = gcm_done(gcm, tag, &tag_len)) != CRYPT_OK) {
+       goto LBL_CLEANBUF;
+    }
+    if (tag_len != taglen) {
+       err = CRYPT_ERROR;
+       goto LBL_CLEANBUF;
+    }
+
+    if (direction == GCM_DECRYPT) {
+       x = fread(buf, 1, taglen, in);
+       if (x != taglen) {
+          err = CRYPT_ERROR;
+          goto LBL_CLEANBUF;
+       }
+
+       if (XMEM_NEQ(buf, tag, taglen) == 0) {
+          *res = 1;
+       }
+    } else {
+       if(fwrite(tag, 1, taglen, out) != taglen) {
+          err = CRYPT_ERROR;
+          goto LBL_CLEANBUF;
+       }
+       *res = 1;
+    }
+
+LBL_CLEANBUF:
+    zeromem(buf, LTC_FILE_READ_BUFSIZE);
+    zeromem(tag, sizeof(tag));
+LBL_ERR:
+#ifdef LTC_CLEAN_STACK
+#ifndef LTC_GCM_TABLES_SSE2
+    zeromem(orig, sizeof(*gcm));
+#else
+    zeromem(orig, sizeof(*gcm) + 16);
+#endif
+#endif
+    if(*res == 0) {
+       x = ftell(out);
+       rewind(out);
+       while((size_t)ftell(out) < x) {
+          fwrite(buf, 1, LTC_FILE_READ_BUFSIZE, out);
+       }
+       if(ftruncate(fileno(out), 0)) {
+          /* well, what shall we do here... */
+       }
+    }
+    fflush(out);
+
+    XFREE(buf);
+    XFREE(orig);
+
+    return err;
+}
+#endif
+#endif
+
+
+/* ref:         $Format:%D$ */
+/* git commit:  $Format:%H$ */
+/* commit time: $Format:%ai$ */

+ 3 - 2
makefile_include.mk

@@ -147,9 +147,10 @@ USEABLE_DEMOS  = ltcrypt sizes constants
 TEST_DEMOS     = small tv_gen
 
 # Demos that are in one config broken
-#  openssl-enc - can't be build with LTC_EASY
+#  aesgcm      - can't be built with LTC_EASY
+#  openssl-enc - can't be built with LTC_EASY
 #  timing      - not really broken, but older gcc builds spit warnings
-BROKEN_DEMOS   = openssl-enc timing
+BROKEN_DEMOS   = aesgcm openssl-enc timing
 
 # Combine demos in groups
 UNBROKEN_DEMOS = $(TEST_DEMOS) $(USEABLE_DEMOS) $(USEFUL_DEMOS)