blob: 1a7aafd8df9c3a0b04bb938b71c3c927cd9e7de2 [file] [log] [blame]
/*
-----------------------------------------------------------------------
Copyright (c) 2001 Dr Brian Gladman <brg@gladman.uk.net>, Worcester, UK
TERMS
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
This software is provided 'as is' with no guarantees of correctness or
fitness for purpose.
-----------------------------------------------------------------------
*/
/* Example of the use of the AES (Rijndael) algorithm for file */
/* encryption. Note that this is an example application, it is */
/* not intended for real operational use. The Command line is: */
/* */
/* aesxam input_file_name output_file_name [D|E] hexadecimalkey */
/* */
/* where E gives encryption and D decryption of the input file */
/* into the output file using the given hexadecimal key string */
/* The later is a hexadecimal sequence of 32, 48 or 64 digits */
/* Examples to encrypt or decrypt aes.c into aes.enc are: */
/* */
/* aesxam file.c file.enc E 0123456789abcdeffedcba9876543210 */
/* */
/* aesxam file.enc file2.c D 0123456789abcdeffedcba9876543210 */
/* */
/* which should return a file 'file2.c' identical to 'file.c' */
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <ctype.h>
#include "aes.h"
/* A Pseudo Random Number Generator (PRNG) used for the */
/* Initialisation Vector. The PRNG is George Marsaglia's */
/* Multiply-With-Carry (MWC) PRNG that concatenates two */
/* 16-bit MWC generators: */
/* x(n)=36969 * x(n-1) + carry mod 2^16 */
/* y(n)=18000 * y(n-1) + carry mod 2^16 */
/* to produce a combined PRNG with a period of about 2^60. */
/* The Pentium cycle counter is used to initialise it. This */
/* is crude but the IV does not need to be secret. */
/* void cycles(unsigned long *rtn) */
/* { // read the Pentium Time Stamp Counter */
/* __asm */
/* { */
/* _emit 0x0f // complete pending operations */
/* _emit 0xa2 */
/* _emit 0x0f // read time stamp counter */
/* _emit 0x31 */
/* mov ebx,rtn */
/* mov [ebx],eax */
/* mov [ebx+4],edx */
/* _emit 0x0f // complete pending operations */
/* _emit 0xa2 */
/* } */
/* } */
#define RAND(a,b) (((a = 36969 * (a & 65535) + (a >> 16)) << 16) + (b = 18000 * (b & 65535) + (b >> 16)) )
void fillrand(char *buf, int len)
{ static unsigned long a[2], mt = 1, count = 4;
static char r[4];
int i;
if(mt) {
mt = 0;
/*cycles(a);*/
a[0]=0xeaf3;
a[1]=0x35fe;
}
for(i = 0; i < len; ++i)
{
if(count == 4)
{
*(unsigned long*)r = RAND(a[0], a[1]);
count = 0;
}
buf[i] = r[count++];
}
}
int encfile(FILE *fin, FILE *fout, aes *ctx, char* fn)
{ char inbuf[16], outbuf[16];
long flen;
unsigned long i=0, l=0;
fillrand(outbuf, 16); /* set an IV for CBC mode */
fseek(fin, 0, SEEK_END); /* get the length of the file */
flen = ftell(fin); /* and then reset to start */
fseek(fin, 0, SEEK_SET);
fwrite(outbuf, 1, 16, fout); /* write the IV to the output */
fillrand(inbuf, 1); /* make top 4 bits of a byte random */
l = 15; /* and store the length of the last */
/* block in the lower 4 bits */
inbuf[0] = ((char)flen & 15) | (inbuf[0] & ~15);
while(!feof(fin)) /* loop to encrypt the input file */
{ /* input 1st 16 bytes to buf[1..16] */
i = fread(inbuf + 16 - l, 1, l, fin); /* on 1st round byte[0] */
/* is the length code */
if(i < l) break; /* if end of the input file reached */
for(i = 0; i < 16; ++i) /* xor in previous cipher text */
inbuf[i] ^= outbuf[i];
encrypt(inbuf, outbuf, ctx); /* and do the encryption */
if(fwrite(outbuf, 1, 16, fout) != 16)
{
printf("Error writing to output file: %s\n", fn);
return -7;
}
/* in all but first round read 16 */
l = 16; /* bytes into the buffer */
}
/* except for files of length less than two blocks we now have one */
/* byte from the previous block and 'i' bytes from the current one */
/* to encrypt and 15 - i empty buffer positions. For files of less */
/* than two blocks (0 or 1) we have i + 1 bytes and 14 - i empty */
/* buffer position to set to zero since the 'count' byte is extra */
if(l == 15) /* adjust for extra byte in the */
++i; /* in the first block */
if(i) /* if bytes remain to be output */
{
while(i < 16) /* clear empty buffer positions */
inbuf[i++] = 0;
for(i = 0; i < 16; ++i) /* xor in previous cipher text */
inbuf[i] ^= outbuf[i];
encrypt(inbuf, outbuf, ctx); /* encrypt and output it */
if(fwrite(outbuf, 1, 16, fout) != 16)
{
printf("Error writing to output file: %s\n", fn);
return -8;
}
}
return 0;
}
int decfile(FILE *fin, FILE *fout, aes *ctx, char* ifn, char* ofn)
{ char inbuf1[16], inbuf2[16], outbuf[16], *bp1, *bp2, *tp;
int i, l, flen;
if(fread(inbuf1, 1, 16, fin) != 16) /* read Initialisation Vector */
{
printf("Error reading from input file: %s\n", ifn);
return 9;
}
i = fread(inbuf2, 1, 16, fin); /* read 1st encrypted file block */
if(i && i != 16)
{
printf("\nThe input file is corrupt");
return -10;
}
decrypt(inbuf2, outbuf, ctx); /* decrypt it */
for(i = 0; i < 16; ++i) /* xor with previous input */
outbuf[i] ^= inbuf1[i];
flen = outbuf[0] & 15; /* recover length of the last block and set */
l = 15; /* the count of valid bytes in block to 15 */
bp1 = inbuf1; /* set up pointers to two input buffers */
bp2 = inbuf2;
while(1)
{
i = fread(bp1, 1, 16, fin); /* read next encrypted block */
/* to first input buffer */
if(i != 16) /* no more bytes in input - the decrypted */
break; /* partial final buffer needs to be output */
/* if a block has been read the previous block must have been */
/* full lnegth so we can now write it out */
if(fwrite(outbuf + 16 - l, 1, l, fout) != (unsigned long)l)
{
printf("Error writing to output file: %s\n", ofn);
return -11;
}
decrypt(bp1, outbuf, ctx); /* decrypt the new input block and */
for(i = 0; i < 16; ++i) /* xor it with previous input block */
outbuf[i] ^= bp2[i];
/* set byte count to 16 and swap buffer pointers */
l = i; tp = bp1, bp1 = bp2, bp2 = tp;
}
/* we have now output 16 * n + 15 bytes of the file with any left */
/* in outbuf waiting to be output. If x bytes remain to be written, */
/* we know that (16 * n + x + 15) % 16 = flen, giving x = flen + 1 */
/* But we must also remember that the first block is offset by one */
/* in the buffer - we use the fact that l = 15 rather than 16 here */
l = (l == 15 ? 1 : 0); flen += 1 - l;
if(flen)
if(fwrite(outbuf + l, 1, flen, fout) != (unsigned long)flen)
{
printf("Error writing to output file: %s\n", ofn);
return -12;
}
return 0;
}
int main(int argc, char *argv[])
{ FILE *fin = 0, *fout = 0;
char *cp, ch, key[32];
int i=0, by=0, key_len=0, err = 0;
aes ctx[1];
if(argc != 5 || (toupper(*argv[3]) != 'D' && toupper(*argv[3]) != 'E'))
{
printf("usage: rijndael in_filename out_filename [d/e] key_in_hex\n");
err = -1; goto exit;
}
cp = argv[4]; /* this is a pointer to the hexadecimal key digits */
i = 0; /* this is a count for the input digits processed */
while(i < 64 && *cp) /* the maximum key length is 32 bytes and */
{ /* hence at most 64 hexadecimal digits */
ch = toupper(*cp++); /* process a hexadecimal digit */
if(ch >= '0' && ch <= '9')
by = (by << 4) + ch - '0';
else if(ch >= 'A' && ch <= 'F')
by = (by << 4) + ch - 'A' + 10;
else /* error if not hexadecimal */
{
printf("key must be in hexadecimal notation\n");
err = -2; goto exit;
}
/* store a key byte for each pair of hexadecimal digits */
if(i++ & 1)
key[i / 2 - 1] = by & 0xff;
}
if(*cp)
{
printf("The key value is too long\n");
err = -3; goto exit;
}
else if(i < 32 || (i & 15))
{
printf("The key length must be 32, 48 or 64 hexadecimal digits\n");
err = -4; goto exit;
}
key_len = i / 2;
if(!(fin = fopen(argv[1], "rb"))) /* try to open the input file */
{
printf("The input file: %s could not be opened\n", argv[1]);
err = -5; goto exit;
}
if(!(fout = fopen(argv[2], "wb"))) /* try to open the output file */
{
printf("The output file: %s could not be opened\n", argv[1]);
err = -6; goto exit;
}
if(toupper(*argv[3]) == 'E')
{ /* encryption in Cipher Block Chaining mode */
set_key(key, key_len, enc, ctx);
err = encfile(fin, fout, ctx, argv[1]);
}
else
{ /* decryption in Cipher Block Chaining mode */
set_key(key, key_len, dec, ctx);
#if 1
err = decfile(fin, stdout, ctx, argv[1], argv[2]);
#else
err = decfile(fin, fout, ctx, argv[1], argv[2]);
#endif
}
exit:
if(fout)
fclose(fout);
if(fin)
fclose(fin);
return err;
}