Click here to Skip to main content
Click here to Skip to main content

Secure Encryption For Data Storage with Key Re-use

By , 22 Jul 2008
Rate this:
Please Sign up or sign in to vote.

Introduction

Many single file encryption algorithms use a simple stream cipher. These ciphers XOR the bytes in the file to be encrypted with a series of outputs from a pseudo-random number generator (PRNG). If the user encrypts the same file twice using the same key, the exact same encrypted output will be generated.

If a cryptanalyst thinks that a user has used the same key to encrypt two different files, he can XOR the two files together and cancel out the output from the PRNG. This leaves the cryptanalyst with a file that contains only the two original files XORed against each other. Guessing at the original contents becomes much easier in this case.

Additionally, the cryptanalyst can use plain text attacks. In these attacks, if the cryptanalyst knows the file type, then he can use knowledge of header formats to guess at the key stream used to encode the file.

The algorithm here uses the PRNG seeded with a high entropy source to insert a block of data at the beginning of the file. This data is the first data encrypted. On decryption, the first block of data is discarded, restoring the original file. Since the algorithm is designed so that single bit changes avalanche throughout the file, this initial block of data that is introduced makes the same file encrypt differently each time it is encrypted with the same key.

Background

In Bruce Schneier's book "Applied Cryptography", he spoke of a block cipher where each block is XORed with the hash of the previous block's cipher text concatenated with the key.

A simple variation on this theme is to XOR the first block with the hash of the key and XOR subsequent blocks with the previous block's plain text and the hash used on the previous block.

Since the starting hash was generated from the key, the key still avalanches throughout the cipher text since each hash is used to generate the next block's hash.

Using the Code

The attached source code was developed to demonstrate the principal described above. It was developed using Visual C++ as a console application. All the code is in a single file; not very pretty but it works.

Points of Interest

On my 3GHz dual core Pentium, the code encrypts and decrypts at a rate of 8MB/s.

History

  • 22nd July, 2008: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Tom Stone

Unknown
No Biography provided

Comments and Discussions

 
GeneralUpdated to include passphrase validation PinmemberTom Stone8-Mar-09 15:51 
/*
 *  The following code is a stream cipher cascaded with a block cypher.  The
 *  intent is to make the passphrase the weakest link for attacking the cipher
 *  text.
 *
 *  Copyright notices have been kept with the public domain code that was used.
 *  The SHA-256 hash code was downloaded from thecodeproject.com
 *
 *  The block cipher is a variation on an algorithm described in Bruce Schneier's
 *  book "Applied Cryptography".  That algorithm XORed the first block of plain
 *  text with the hash of the passphrase.  Each subsequent block is then XORed 
 *  with the hash of the previous block's cipher text concatenated with the hash 
 *  used to encrypt the previous block.  In the code here, we hash the plain text 
 *  of the previous block concatenated with the hash used for encrypting the previous 
 *  block.
 *
 *  For the stream cipher we use a modified Mersenne Twister (MT) developed by Matsumoto.
 *  The modification is to return the running sum of the next 2 outputs of the generator 
 *  instead of returning just the outputs of the generator.  We then use only the 
 *  high order 8 bits of the return.  The modification defeats one of the weaknesses 
 *  in the generator.  Since the generator is a Linear Shift Feedback Register (LSFR) with
 *  19937 bits, the internal state of the generator can be determine if you have access
 *  to 19937 consecutive bits of output from the generator.  Using only the high order
 *  8 bits of the cummulative sum makes it so that the internal state can not be 
 *  determined.  
 *
 *  Even if the entire 32 bit return of the modified generator were available, the analyst
 *  would not be able to determine the internal state of the generator.  This is because they
 *  would only be able to determine what was added to the previous return.  Since the value
 *  added to the previous return is the sum of 2 LSFR outputs, none of the bits of either
 *  LSFR output can be determined.
 *
 *  One of the advantages of using the MT is that it can be seeded with a variable length 
 *  seed.  This means we do not need to run any functions over the passphrase to seed
 *  the generator, we can use the raw bits of the passphrase directly as the seed.
 *
 *  For making the block cipher encrypt differently each time it is invoked we need to
 *  generate an initialization vector (IV) which is different every time we run the generator.
 *  To accomplish this we take a number used only once (NONCE) and append it to the
 *  passphrase to seed the generator and then use the generator to create the IV.  The NONCE
 *  assures that a different IV will be generated every time the program is run.
 *
 *  The seed of the generator determines where the generator starts in the sequence of 
 *  pseudo random returns from the generator.  Using the NONCE changes the starting point.
 *  As long as the NONCE is never re-used, the generated IV should be different every
 *  time the program is run even if the user uses the same passphrase every time.  In this 
 *  algorithm we concatenate current time, kernal time, user time, and idle
 *  time to create a NONCE that contains a few bits of entropy.
 *
 *  This generated IV becomes the first block of data that is encrypted.  It is encrypted
 *  by XORing it against the hash of the passphrase.  The file to be encrypted starts as
 *  the 2nd block of 32 bytes of text and it is encrypted by XORing it with the hash of the previous
 *  block's plain text concatenated with the hash used to encrypt the previous block.
 *
 *  In order to get the stream cipher to encrypt differently every time.  The (MT) is reseeded
 *  with the IV concatenated with the passphrase.  The stream cipher is not used for encrypting
 *  the IV.  It is only used for encrypting the rest of the file.  As each byte in the file
 *  is XORed with the next hash output byte it is also XORed with the next return from the
 *  MT (again, only using the high order 8 bits of the 32 bit returns).
 *
 *  Encrypting a 10MB file requires 312501 hashes.  This makes the algorithm computationally 
 *  intensive which makes passphrase guessing very time consuming.
 *
 *  Decrypting is fairly straight forward.  The IV is recovered by XORing the first 32 bytes
 *  with the hash of the passphrase.  It is then concatenated with the passphrase to seed the
 *  MT.
 *
 *  On encryption, the cipher appends the hash of the IV concatenated with the input file and appends
 *  that hash to the end of the output file.  On decryption, as the output file is decrypted, the
 *  hash is recomputed.  The recomputed hash is then compared against the stored hash.  If the
 *  computed hash is equal to the stored hash then the proper passphrase was entered for
 *  decrypting the file.  If the hashes do not match then an error message is printed and the
 *  decrypted data is deleted.  because the IV changes every time the file is encrypted, the stored
 *  hash will change also.  This means that the stored hash does not yeild any information
 *  regarding the file that was encrypted.
 *
 *  On a 3GHz Duo-Core Pentium the code encrypts about 4MB/s.
 *
 *
 */
 
#include <io.h>
#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>
#include <comutil.h>
#include <time.h>
 
#pragma once
/*
SHA256 is from this source
 ---------------------------------------------------------------------------
 Copyright (c) 2002, Dr Brian Gladman <brg@gladman.me.uk>, Worcester, UK.
 All rights reserved.
 
 LICENSE TERMS
 
 The free distribution and use of this software in both source and binary 
 form is allowed (with or without changes) provided that:
 
   1. distributions of this source code include the above copyright 
      notice, this list of conditions and the following disclaimer;
 
   2. distributions in binary form include the above copyright
      notice, this list of conditions and the following disclaimer
      in the documentation and/or other associated materials;
 
   3. the copyright holder's name is not used to endorse products 
      built using this software without specific written permission. 
 
 ALTERNATIVELY, provided that this notice is retained in full, this product
 may be distributed under the terms of the GNU General Public License (GPL),
 in which case the provisions of the GPL apply INSTEAD OF those given above.
 
 DISCLAIMER
 
 This software is provided 'as is' with no explicit or implied warranties
 in respect of its properties, including, but not limited to, correctness 
 and/or fitness for purpose.
 ---------------------------------------------------------------------------
 Issue Date: 30/11/2002
*/
 
#ifndef _SHA2_H
#define _SHA2_H
 
#include <limits.h>
 
/*  Defines for suffixes to 32 and 64 bit unsigned numeric values   */
 
#define sfx_lo(x,y) x##y
#define sfx_hi(x,y) sfx_lo(x,y)
#define n_u32(p)    sfx_hi(0x##p,s_u32)
#define n_u64(p)    sfx_hi(0x##p,s_u64)
 
/* define an unsigned 32-bit type */
 
#if UINT_MAX == 0xffffffff
  typedef   unsigned int     sha2_32t;
  #define s_u32    u
#elif ULONG_MAX == 0xffffffff
  typedef   unsigned long    sha2_32t;
  #define s_u32   ul
#else
#error Please define sha2_32t as an unsigned 32 bit type in sha2.h
#endif
 
/* define an unsigned 64-bit type */
 
#if defined( _MSC_VER )
  typedef unsigned __int64   sha2_64t;
  #define s_u64 ui64
#elif ULONG_MAX == 0xffffffffffffffff
  typedef unsigned long      sha2_64t;
  #define s_u64   ul
#elif ULONG_MAX == 0xffffffff
  typedef unsigned long long sha2_64t;   /* a somewhat dangerous guess */
  #define s_u64  ull
#else
#error Please define sha2_64t as an unsigned 64 bit type in sha2.h
#endif
 
#if defined(__cplusplus)
extern "C"
{
#endif
 
#define BYTES_PER_BLOCK     32
 
#define SHA256_DIGEST_SIZE  32
#define SHA384_DIGEST_SIZE  48
#define SHA512_DIGEST_SIZE  64
 
#define SHA256_BLOCK_SIZE   64
#define SHA384_BLOCK_SIZE  128
#define SHA512_BLOCK_SIZE  128
 
#define SHA2_DIGEST_SIZE        SHA256_DIGEST_SIZE
#define SHA2_MAX_DIGEST_SIZE    SHA512_DIGEST_SIZE
 
#define SHA2_GOOD   0
#define SHA2_BAD    1
 
/* type to hold the SHA256 context				*/
 
typedef struct
{   sha2_32t count[2];
    sha2_32t hash[8];
    sha2_32t wbuf[16];
} sha256_ctx;
 
/* type to hold the SHA384/512 context			*/
 
typedef struct
{   sha2_64t count[2];
    sha2_64t hash[8];
    sha2_64t wbuf[16];
} sha512_ctx;
 
typedef sha512_ctx  sha384_ctx;
 
/* type to hold a SHA2 context (256/384/512)  */
 
typedef struct
{   union
    {   sha256_ctx  ctx256[1];
        sha512_ctx  ctx512[1];
    } uu[1];
    sha2_32t    sha2_len;
} sha2_ctx;
 
void sha256_compile(sha256_ctx ctx[1]);
void sha512_compile(sha512_ctx ctx[1]);
 
void sha256_begin(sha256_ctx ctx[1]);
void sha256_hash(const unsigned char data[], unsigned long len, sha256_ctx ctx[1]);
void sha256_end(unsigned char hval[], sha256_ctx ctx[1]);
void sha256(unsigned char hval[], const unsigned char data[], unsigned long len); 
 
void sha384_begin(sha384_ctx ctx[1]);
#define sha384_hash sha512_hash
void sha384_end(unsigned char hval[], sha384_ctx ctx[1]);
void sha384(unsigned char hval[], const unsigned char data[], unsigned long len); 
 
void sha512_begin(sha512_ctx ctx[1]);
void sha512_hash(const unsigned char data[], unsigned long len, sha512_ctx ctx[1]);
void sha512_end(unsigned char hval[], sha512_ctx ctx[1]);
void sha512(unsigned char hval[], const unsigned char data[], unsigned long len); 
 
int sha2_begin(unsigned long size, sha2_ctx ctx[1]);
void sha2_hash(const unsigned char data[], unsigned long len, sha2_ctx ctx[1]);
void sha2_end(unsigned char hval[], sha2_ctx ctx[1]);
int sha2(unsigned char hval[], unsigned long size, const unsigned char data[], unsigned long len); 
 
#if defined(__cplusplus)
}
#endif
 
#endif
 
#define MAX_WORDS 524288
 
/* Period parameters */  
#define N 624
#define M 397
#define MATRIX_A 0x9908b0dfUL   /* constant vector a */
#define UPPER_MASK 0x80000000UL /* most significant w-r bits */
#define LOWER_MASK 0x7fffffffUL /* least significant r bits */
 
unsigned long genrand_int32(void);
 
static unsigned long mt[N]; /* the array for the state vector  */
static int mti=N+1; /* mti==N+1 means mt[N] is not initialized */
static unsigned long accum; /* the array for the state vector  */
 
/* define the hash functions that you need          */
 
#define SHA_2           /* for dynamic hash length  */
#define SHA_256
//#define SHA_384
//#define SHA_512

#include <string.h>     /* for memcpy() etc.        */
#include <stdlib.h>     /* for _lrotr with VC++     */
 
//#include "sha2.h"

/*  1. PLATFORM SPECIFIC INCLUDES */
 
#if defined(__GNU_LIBRARY__)
#  include <byteswap.h>
#  include <endian.h>
#elif defined(__CRYPTLIB__)
#  if defined( INC_ALL )
#    include "crypt.h"
#  elif defined( INC_CHILD )
#    include "../crypt.h"
#  else
#    include "crypt.h"
#  endif
#  if defined(DATA_LITTLEENDIAN)
#    define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN
#  else
#    define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN
#  endif
#elif defined(_MSC_VER)
#  include <stdlib.h>
#elif !defined(WIN32)
#  include <stdlib.h>
#  if !defined (_ENDIAN_H)
#    include 
#  else
#    include _ENDIAN_H
#  endif
#endif
 
/*  2. BYTE ORDER IN 32-BIT WORDS
 
    To obtain the highest speed on processors with 32-bit words, this code 
    needs to determine the order in which bytes are packed into such words.
    The following block of code is an attempt to capture the most obvious 
    ways in which various environemnts specify their endian definitions. 
    It may well fail, in which case the definitions will need to be set by 
    editing at the points marked **** EDIT HERE IF NECESSARY **** below.
*/
#define SHA_LITTLE_ENDIAN   1234 /* byte 0 is least significant (i386) */
#define SHA_BIG_ENDIAN      4321 /* byte 0 is most significant (mc68k) */
 
#if !defined(PLATFORM_BYTE_ORDER)
#if defined(LITTLE_ENDIAN) || defined(BIG_ENDIAN)
#  if defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
#    if defined(BYTE_ORDER)
#      if   (BYTE_ORDER == LITTLE_ENDIAN)
#        define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN
#      elif (BYTE_ORDER == BIG_ENDIAN)
#        define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN
#      endif
#    endif
#  elif defined(LITTLE_ENDIAN) && !defined(BIG_ENDIAN) 
#    define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN
#  elif !defined(LITTLE_ENDIAN) && defined(BIG_ENDIAN)
#    define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN
#  endif
#elif defined(_LITTLE_ENDIAN) || defined(_BIG_ENDIAN)
#  if defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)
#    if defined(_BYTE_ORDER)
#      if   (_BYTE_ORDER == _LITTLE_ENDIAN)
#        define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN
#      elif (_BYTE_ORDER == _BIG_ENDIAN)
#        define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN
#      endif
#    endif
#  elif defined(_LITTLE_ENDIAN) && !defined(_BIG_ENDIAN) 
#    define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN
#  elif !defined(_LITTLE_ENDIAN) && defined(_BIG_ENDIAN)
#    define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN
#  endif
#elif 0     /* **** EDIT HERE IF NECESSARY **** */
#define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN
#elif 0     /* **** EDIT HERE IF NECESSARY **** */
#define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN
#elif (('1234' >> 24) == '1')
#  define PLATFORM_BYTE_ORDER SHA_LITTLE_ENDIAN
#elif (('4321' >> 24) == '1')
#  define PLATFORM_BYTE_ORDER SHA_BIG_ENDIAN
#endif
#endif
 
#if !defined(PLATFORM_BYTE_ORDER)
#  error Please set undetermined byte order (lines 159 or 161 of sha2.c).
#endif
 
#ifdef _MSC_VER
#pragma intrinsic(memcpy)
#endif
 
#define rotr32(x,n)   (((x) >> n) | ((x) << (32 - n)))
 
#if !defined(bswap_32)
#define bswap_32(x) (rotr32((x), 24) & 0x00ff00ff | rotr32((x), 8) & 0xff00ff00)
#endif
 
#if (PLATFORM_BYTE_ORDER == SHA_LITTLE_ENDIAN)
#define SWAP_BYTES
#else
#undef  SWAP_BYTES
#endif
 
#if defined(SHA_2) || defined(SHA_256)
 
#define SHA256_MASK (SHA256_BLOCK_SIZE - 1)
 
#if defined(SWAP_BYTES)
#define	bsw_32(p,n)	{ int _i = (n);	while(_i--) p[_i] = bswap_32(p[_i]); }
#else
#define	bsw_32(p,n)	
#endif
 
/* SHA256 mixing function definitions   */
 
#define ch(x,y,z)   (((x) & (y)) ^ (~(x) & (z)))
#define maj(x,y,z)  (((x) & (y)) ^ ((x) & (z)) ^ ((y) & (z)))
 
#define s256_0(x) (rotr32((x),  2) ^ rotr32((x), 13) ^ rotr32((x), 22)) 
#define s256_1(x) (rotr32((x),  6) ^ rotr32((x), 11) ^ rotr32((x), 25)) 
#define g256_0(x) (rotr32((x),  7) ^ rotr32((x), 18) ^ ((x) >>  3)) 
#define g256_1(x) (rotr32((x), 17) ^ rotr32((x), 19) ^ ((x) >> 10)) 
 
/* rotated SHA256 round definition. Rather than swapping variables as in    */
/* FIPS-180, different variables are 'rotated' on each round, returning     */
/* to their starting positions every eight rounds                           */
 
#define h2(i) ctx->wbuf[i & 15] += \
    g256_1(ctx->wbuf[(i + 14) & 15]) + ctx->wbuf[(i + 9) & 15] + g256_0(ctx->wbuf[(i + 1) & 15])
 
#define h2_cycle(i,j)  \
    v[(7 - i) & 7] += (j ? h2(i) : ctx->wbuf[i & 15]) + k256[i + j] \
        + s256_1(v[(4 - i) & 7]) + ch(v[(4 - i) & 7], v[(5 - i) & 7], v[(6 - i) & 7]); \
    v[(3 - i) & 7] += v[(7 - i) & 7]; \
    v[(7 - i) & 7] += s256_0(v[(0 - i) & 7]) + maj(v[(0 - i) & 7], v[(1 - i) & 7], v[(2 - i) & 7])
 
/* SHA256 mixing data   */
 
const sha2_32t k256[64] =
{   n_u32(428a2f98), n_u32(71374491), n_u32(b5c0fbcf), n_u32(e9b5dba5), 
    n_u32(3956c25b), n_u32(59f111f1), n_u32(923f82a4), n_u32(ab1c5ed5), 
    n_u32(d807aa98), n_u32(12835b01), n_u32(243185be), n_u32(550c7dc3), 
    n_u32(72be5d74), n_u32(80deb1fe), n_u32(9bdc06a7), n_u32(c19bf174), 
    n_u32(e49b69c1), n_u32(efbe4786), n_u32(0fc19dc6), n_u32(240ca1cc), 
    n_u32(2de92c6f), n_u32(4a7484aa), n_u32(5cb0a9dc), n_u32(76f988da), 
    n_u32(983e5152), n_u32(a831c66d), n_u32(b00327c8), n_u32(bf597fc7), 
    n_u32(c6e00bf3), n_u32(d5a79147), n_u32(06ca6351), n_u32(14292967), 
    n_u32(27b70a85), n_u32(2e1b2138), n_u32(4d2c6dfc), n_u32(53380d13), 
    n_u32(650a7354), n_u32(766a0abb), n_u32(81c2c92e), n_u32(92722c85),
    n_u32(a2bfe8a1), n_u32(a81a664b), n_u32(c24b8b70), n_u32(c76c51a3), 
    n_u32(d192e819), n_u32(d6990624), n_u32(f40e3585), n_u32(106aa070), 
    n_u32(19a4c116), n_u32(1e376c08), n_u32(2748774c), n_u32(34b0bcb5), 
    n_u32(391c0cb3), n_u32(4ed8aa4a), n_u32(5b9cca4f), n_u32(682e6ff3), 
    n_u32(748f82ee), n_u32(78a5636f), n_u32(84c87814), n_u32(8cc70208), 
    n_u32(90befffa), n_u32(a4506ceb), n_u32(bef9a3f7), n_u32(c67178f2),
};
 
/* SHA256 initialisation data */
 
const sha2_32t i256[8] =
{
    n_u32(6a09e667), n_u32(bb67ae85), n_u32(3c6ef372), n_u32(a54ff53a),
    n_u32(510e527f), n_u32(9b05688c), n_u32(1f83d9ab), n_u32(5be0cd19)
};
 

void sha256_begin(sha256_ctx ctx[1])
{
    ctx->count[0] = ctx->count[1] = 0;
    memcpy(ctx->hash, i256, 8 * sizeof(sha2_32t));
}
 
/* Compile 64 bytes of hash data into SHA256 digest value   */
/* NOTE: this routine assumes that the byte order in the    */
/* ctx->wbuf[] at this point is in such an order that low   */
/* address bytes in the ORIGINAL byte stream placed in this */
/* buffer will now go to the high end of words on BOTH big  */
/* and little endian systems                                */
 
void sha256_compile(sha256_ctx ctx[1])
{   sha2_32t	v[8], j;
 
    memcpy(v, ctx->hash, 8 * sizeof(sha2_32t));
 
    for(j = 0; j < 64; j += 16)
    {
        h2_cycle( 0, j); h2_cycle( 1, j); h2_cycle( 2, j); h2_cycle( 3, j);
        h2_cycle( 4, j); h2_cycle( 5, j); h2_cycle( 6, j); h2_cycle( 7, j);
        h2_cycle( 8, j); h2_cycle( 9, j); h2_cycle(10, j); h2_cycle(11, j);
        h2_cycle(12, j); h2_cycle(13, j); h2_cycle(14, j); h2_cycle(15, j);
    }
 
    ctx->hash[0] += v[0]; ctx->hash[1] += v[1]; ctx->hash[2] += v[2]; ctx->hash[3] += v[3];
    ctx->hash[4] += v[4]; ctx->hash[5] += v[5]; ctx->hash[6] += v[6]; ctx->hash[7] += v[7];
}
 
/* SHA256 hash data in an array of bytes into hash buffer   */
/* and call the hash_compile function as required.          */
 
void sha256_hash(const unsigned char data[], unsigned long len, sha256_ctx ctx[1])
{   sha2_32t pos = (sha2_32t)(ctx->count[0] & SHA256_MASK), 
             space = SHA256_BLOCK_SIZE - pos;
    const unsigned char *sp = data;
 
    if((ctx->count[0] += len) < len)
        ++(ctx->count[1]);
 
    while(len >= space)     /* tranfer whole blocks while possible  */
    {
        memcpy(((unsigned char*)ctx->wbuf) + pos, sp, space);
        sp += space; len -= space; space = SHA256_BLOCK_SIZE; pos = 0; 
		bsw_32(ctx->wbuf, SHA256_BLOCK_SIZE >> 2)
        sha256_compile(ctx);
    }
 
    memcpy(((unsigned char*)ctx->wbuf) + pos, sp, len);
}
 
/* SHA256 Final padding and digest calculation  */
 
static sha2_32t  m1[4] =
{
    n_u32(00000000), n_u32(ff000000), n_u32(ffff0000), n_u32(ffffff00)
};
 
static sha2_32t  b1[4] =
{
    n_u32(80000000), n_u32(00800000), n_u32(00008000), n_u32(00000080)
};
 
void sha256_end(unsigned char hval[], sha256_ctx ctx[1])
{   sha2_32t    i = (sha2_32t)(ctx->count[0] & SHA256_MASK);
 
	bsw_32(ctx->wbuf, (i + 3) >> 2)
    /* bytes in the buffer are now in an order in which references  */
    /* to 32-bit words will put bytes with lower addresses into the */
    /* top of 32 bit words on BOTH big and little endian machines   */
    
    /* we now need to mask valid bytes and add the padding which is */
    /* a single 1 bit and as many zero bits as necessary.           */
    ctx->wbuf[i >> 2] = (ctx->wbuf[i >> 2] & m1[i & 3]) | b1[i & 3];
 
    /* we need 9 or more empty positions, one for the padding byte  */
    /* (above) and eight for the length count.  If there is not     */
    /* enough space pad and empty the buffer                        */
    if(i > SHA256_BLOCK_SIZE - 9)
    {
        if(i < 60) ctx->wbuf[15] = 0;
        sha256_compile(ctx);
        i = 0;
    }
    else    /* compute a word index for the empty buffer positions  */
        i = (i >> 2) + 1;
 
    while(i < 14) /* and zero pad all but last two positions      */ 
        ctx->wbuf[i++] = 0;
    
    /* the following 32-bit length fields are assembled in the      */
    /* wrong byte order on little endian machines but this is       */
    /* corrected later since they are only ever used as 32-bit      */
    /* word values.                                                 */
 
    ctx->wbuf[14] = (ctx->count[1] << 3) | (ctx->count[0] >> 29);
    ctx->wbuf[15] = ctx->count[0] << 3;
 
    sha256_compile(ctx);
 
    /* extract the hash value as bytes in case the hash buffer is   */
    /* mislaigned for 32-bit words                                  */
    for(i = 0; i < SHA256_DIGEST_SIZE; ++i)
        hval[i] = (unsigned char)(ctx->hash[i >> 2] >> 8 * (~i & 3));
}
 
void sha256(unsigned char hval[], const unsigned char data[], unsigned long len) 
{   sha256_ctx  cx[1];
    
    sha256_begin(cx); sha256_hash(data, len, cx); sha256_end(hval, cx);
}
 
#endif
 
#if defined(SHA_2)
 
#define CTX_256(x)  ((x)->uu->ctx256)
 
/* SHA2 initialisation */
 
int sha2_begin(unsigned long len, sha2_ctx ctx[1])
{   unsigned long   l = len;
    switch(len)
    {
        case 256:   l = len >> 3;
        case  32:   CTX_256(ctx)->count[0] = CTX_256(ctx)->count[1] = 0;
                    memcpy(CTX_256(ctx)->hash, i256, 32); break;
 
        default:    return SHA2_BAD;
    }
    
    ctx->sha2_len = l; return SHA2_GOOD;
}
 
void sha2_hash(const unsigned char data[], unsigned long len, sha2_ctx ctx[1])
{
    switch(ctx->sha2_len)
    {
        case 32: sha256_hash(data, len, CTX_256(ctx)); return;
 
    }
}
 
void sha2_end(unsigned char hval[], sha2_ctx ctx[1])
{
    switch(ctx->sha2_len)
    {
        case 32: sha256_end(hval, CTX_256(ctx)); return;
 
    }
}
 
int sha2(unsigned char hval[], unsigned long size,
                                const unsigned char data[], unsigned long len)
{   sha2_ctx    cx[1];
 
    if(sha2_begin(256, cx) == SHA2_GOOD)
    {
        sha2_hash(data, len, cx); sha2_end(hval, cx); return SHA2_GOOD;
    }
    else
        return SHA2_BAD;
}
 
#endif
 
// The random number generator is based on the Mersenne Twistor
// It has been modified to return the sum of the outputs

/* 
   A C-program for MT19937, with initialization improved 2002/1/26.
   Coded by Takuji Nishimura and Makoto Matsumoto.
 
   Before using, initialize the state by using init_genrand(seed)  
   or init_by_array(init_key, key_length).
 
   Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
   All rights reserved.                          
 
   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.
 
     3. The names of its contributors may not be used to endorse or promote 
        products derived from this software without specific prior written 
        permission.
 
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 

   Any feedback is very welcome.
   http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
   email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
*/
 

/* initializes mt[N] with a seed */
void init_genrand(unsigned long s)
{
    mt[0]= s & 0xffffffffUL;
    accum = 0;                         /* this line not part of the original MT */
    for (mti=1; mti<n;>        mt[mti] = 
	    (1812433253UL * (mt[mti-1] ^ (mt[mti-1] >> 30)) + mti); 
        /* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
        /* In the previous versions, MSBs of the seed affect   */
        /* only MSBs of the array mt[].                        */
        /* 2002/01/09 modified by Makoto Matsumoto             */
        mt[mti] &= 0xffffffffUL;
        /* for >32 bit machines */
    }
}
 
/* initialize by an array with array-length */
/* init_key is the array for initializing keys */
/* key_length is its length */
/* slight change for C++, 2004/2/26 */
void init_by_array(unsigned long init_key[], int key_length)
{
    int i, j, k, dummy;
 
    memset(mt, 0, 4*N);
    init_genrand(19650218UL);
    i=1; j=0;
 

 
    k = (N>key_length ? N : key_length);
    for (; k; k--) {
        mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1664525UL))
          + init_key[j] + j; /* non linear */
        mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
        i++; j++;
        if (i>=N) { mt[0] = mt[N-1]; i=1; }
        if (j>=key_length) j=0;
    }
    for (k=N-1; k; k--) {
        mt[i] = (mt[i] ^ ((mt[i-1] ^ (mt[i-1] >> 30)) * 1566083941UL))
          - i; /* non linear */
        mt[i] &= 0xffffffffUL; /* for WORDSIZE > 32 machines */
        i++;
        if (i>=N) { mt[0] = mt[N-1]; i=1; }
    }
 
    mt[0] = 0x80000000UL; /* MSB is 1; assuring non-zero initial array */ 
 
    /*
     * The line below performs an initial mixing of the accumulator so that the
     * internal state of the LSFR remains hidden.
     */
    for (i=0; i<64; i++) dummy = genrand_int32();  /* added to initially mix the sum */
 
}
 
/* generates a random number on [0,0xffffffff]-interval */
unsigned long genrand_int32(void)
{
    int repetition = 0;    /* further non-linearization, we will execute at least 2 times */
    unsigned long y;
    static unsigned long mag01[2]={0x0UL, MATRIX_A};
    /* mag01[x] = x * MATRIX_A  for x=0,1 */
 
    for (; repetition<2; repetition++) {   /* loop and accum added by tgs */
        if (mti >= N) { /* generate N words at one time */
            int kk;
 
            if (mti == N+1)   /* if init_genrand() has not been called, */
                init_genrand(5489UL); /* a default initial seed is used */
 
            for (kk=0;kk<n-m;kk++)>                y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
                mt[kk] = mt[kk+M] ^ (y >> 1) ^ mag01[y & 0x1UL];
            }
            for (;kk<n-1;kk++)>                y = (mt[kk]&UPPER_MASK)|(mt[kk+1]&LOWER_MASK);
                mt[kk] = mt[kk+(M-N)] ^ (y >> 1) ^ mag01[y & 0x1UL];
            }
            y = (mt[N-1]&UPPER_MASK)|(mt[0]&LOWER_MASK);
            mt[N-1] = mt[M-1] ^ (y >> 1) ^ mag01[y & 0x1UL];
 
            mti = 0;
        }
 
        y = mt[mti++];
 
        /* Tempering */
        y ^= (y >> 11);
        y ^= (y << 7) & 0x9d2c5680UL;
        y ^= (y << 15) & 0xefc60000UL;
        y ^= (y >> 18);
 
        /*
         * This line adds a non-linear component to the twistor.  If we guaranteed
         * that only the high order byte would be used then we wouldn't need to add
         * twice (the repetition loop), but adding twice makes it so that the internal
         * state of the twistor can not be recovered even if all 32 bits of the returned
         * accum were available to an analyst.  If we didn't repeat then the analyst
         * could determine algebraically the value of y that was added to the accum.
         * With the repeat, the analyst can only determine the sum of the two y's that
         * were added to the accum; insufficient information for recovering the internal
         * state of the twister.
         */
        accum += y;
    }
 
    return accum;
}
 
#define ENCRYPT 0
#define DECRYPT 1
 
/*
** Author: Tom Stone
**
** Date:   12/15/2009
**
** Algorithm:
** The encryption algorithm is designed to provide some protection for the user
** who re-uses the same pass phrase when encrypting various files.  The algorithm
** will generate different cipher text even if it is invoked on the exact same plain
** text with the same pass phrase.
**
** This is accomplished by cascading a stream cipher with a block cipher.  The stream
** cipher uses a PRNG that is first seeded with both the passphrase and NONCE
** and used to generate an initial 32-byte IV.  The IV concatenated with the passphrase
** is used to seed the PRNG for the stream cipher.  The IV is encrypted by XORing it with 
** the hash of the passphrase and printing the encrypted IV as the initial 32 bytes of the 
** output file.  
**
** Each block is calculated from the text of the previous
** block concatenated with the hash used to encrypt the previous block.
**
** The re-keyed stream cipher(IV+passphrase) is run against the cipher text generated by the
** XORing of the hash.
**
** A hash of the IV concatenated with the input file is appended to to the end of the file
** on encryption.  On decryption, the hash is recomputed on the decrypted data.  If the proper
** passphrase was entered then the computed hash will match the hash that was appended to
** the cipher text.
**
** On decryption the first and last blocks are stripped.
**
** It is not as slow as I expected.  On a 3GHz dual core Pentium it encrypts 4MB/s
**
*/
 
int main(int argc, char * argv[])
{
    int IV_processed = 0;
    FILE * fd_in, * fd_out;
    unsigned char buffer_area[MAX_WORDS], keystream;
    size_t bytes_read = 0;
    unsigned int i, j, k, keysize = 0;
    int eof, filename_size, direction, muddle = 0;
    int start_fill, blocks_processed = 0;
    char output_filename[1000];
    const char * my_out = output_filename;
    unsigned long multipart_key[N];
    sha256_ctx m_sha256, m2_sha256, file_sha256, comp_sha256;
    unsigned char pBuf[4096], pTemp[256]; 
    unsigned long uRead = 0;
 
    FILETIME idleTimePtr, kernelTimePtr, userTimePtr;
 
    if (argc == 1) {
        printf("Invoke encryption using\n\n");
        printf("   \"%s file passphrase\"\n\n", argv[0]);
        printf("Encrypted/decrypted output will be the filename with an\n");
        printf("underscore appended/removed.  The encrypted file will be\n");
        printf("32 bytes larger than the plain text file\n");
        exit(0);
    } else {
        if (argc < 3) {
            printf("too few arguments\n");
            exit(0);
        }
    }
 
    memset(buffer_area, 0, sizeof(buffer_area));
    memset(output_filename, 0, sizeof(output_filename));
 
    memset(multipart_key, 0, 4*N);
    /*
     ** for generating the IV, get a NONCE that will also add
     ** a bit of entropy to the passphrase and seed the PRNG
     */
    GetSystemTimes(&idleTimePtr, &kernelTimePtr, &userTimePtr);
    multipart_key[0] = idleTimePtr.dwLowDateTime;
    multipart_key[1] = idleTimePtr.dwHighDateTime;
    multipart_key[2] = kernelTimePtr.dwLowDateTime;
    multipart_key[3] = kernelTimePtr.dwHighDateTime;
    multipart_key[4] = userTimePtr.dwLowDateTime;
    multipart_key[5] = userTimePtr.dwHighDateTime;
    multipart_key[6] = time(NULL);
    multipart_key[7] = clock();
    /*
     ** so much for the adding the NONCE, entropy stuff, now add the passphrase
     */
    char * string = argv[2];
    for (i = 0; i < strlen(argv[2]); i++) {
        multipart_key[8+(i>>2)] |= string[i] << (8*(i&3));
    }
    keysize = (i >> 2) + 1;
    if (i&3) keysize++;
    keysize += 8;
    init_by_array(&multipart_key[0], keysize);
    /*
     ** Clear out the first 32 bytes of the multipart_key, the passphrase portion will be re-used
     */
    memset(&multipart_key[0], 0, 32);
    /*
     ** generate the 32 byte IV in case we are encrypting
     */
    for (i = 0; i < BYTES_PER_BLOCK; i++) {
        buffer_area[i] = genrand_int32() >> 24;
    }
    /*
     ** put the IV into the key for reseeding the PRNG when encrypting the file.
     ** it will be encrypted later and put into the output file.
     */
    for (k = 0; k < 32; k++) {
        multipart_key[(k>>2)] |= buffer_area[k] << (8*(k&3));
    }
 
    /*
     * generate a hash of the passphrase.  This will be used for encrypting the IV
     */
    if (argc >= 3) {
        strcpy((char *)&pBuf[0], argv[2]);
        sha256_begin(&m_sha256);
        sha256_hash(pBuf, (unsigned long)strlen(argv[2]), &m_sha256);
        sha256_end(pTemp, &m_sha256);
    }
 
    if (_access((const char *)argv[1], 0) != 0) {
        printf("file does not exist\n");
        exit(0);
    } 
 
    filename_size = strlen(argv[1]);
    strcpy(output_filename, argv[1]);
    if (output_filename[filename_size-1] == 0x5f) {  // is the last character an "_"
        direction = DECRYPT;
        output_filename[filename_size-1] = 0;
        start_fill = 0;
    } else {
        direction = ENCRYPT;
        output_filename[filename_size] = 0x5f;
        start_fill = BYTES_PER_BLOCK;
        init_by_array(&multipart_key[0], keysize);
    }
 
    fd_in = fopen((const char *)argv[1], (const char *)("rb"));
    fd_out = fopen(my_out, (const char *)("wb"));
 
    sha256_begin(&file_sha256);
 
    do {
 
        bytes_read = fread( &buffer_area[start_fill], 1, (MAX_WORDS-start_fill), fd_in);
 
        eof = feof(fd_in);
 
        bytes_read += start_fill;
        blocks_processed++;
        i = 0;
        while (i < bytes_read) {
            if (ENCRYPT == direction) {
                if ((i % 32) == 0) {  // at end of a block, transfer the block
                                      // text for calculated the next hash
                    for (j = i; j <= i+31; j++) {
                        pBuf[j%32] = buffer_area[i+(j%32)];
                    }
                    if ((i+31) > bytes_read) {
                        sha256_hash(pBuf, bytes_read-i, &file_sha256);
                    } else {
                        sha256_hash(pBuf, 32, &file_sha256);
                    }
                }
                if (((i+1) % 32) == 0) { // calculate hash for next block
                    for (j=0; j<8; j++) {
                        pBuf[32+4*j] = (m_sha256.hash[j] >> 24) & 0xff;
                        pBuf[33+4*j] = (m_sha256.hash[j] >> 16) & 0xff;
                        pBuf[34+4*j] = (m_sha256.hash[j] >> 8) & 0xff;
                        pBuf[35+4*j] =  m_sha256.hash[j] & 0xff;
                    }  
 
                    sha256_begin(&m2_sha256);
                    sha256_hash(pBuf, 64, &m2_sha256);
                    sha256_end(pTemp, &m2_sha256);
                }
 
                buffer_area[i] ^= m_sha256.hash[((i%32)/4)] >> ((i%4)*8);
 
                /* the 32 byte IV is only XORed with the hash */
                if ((blocks_processed > 1) || (i>31)) {
                    keystream = genrand_int32() >> 24;
                    buffer_area[i] ^= keystream;
                }
                if ((++i % 32) == 0) { // transfer new hash into current hash
                    memcpy(&m_sha256, &m2_sha256, sizeof(m_sha256));
                }
            } else { // DECRYPT
                /*
                 ** to decrypt
                 ** for each block
                 ** stream and hash
                 ** calculate next hash
                 */
 
                // encrypt if not trailing hash 
                if ((eof == 0) || (eof && (i < (bytes_read - 32)))) { 
                    buffer_area[i] ^= m_sha256.hash[((i%32)/4)] >> ((i%4)*8);
                    if (IV_processed) {
                        keystream = genrand_int32() >> 24;
                        buffer_area[i] ^= keystream;
                    }
                }
 
                if (blocks_processed == 1) {/* not processed IV yet? */
                    if (i == 31) {         /* is this the end of the IV */
                        memset(&multipart_key[0], 0, 32);
                        IV_processed = 1;   /* set IV to processed */
                        for (k = 0; k < 32; k++) {
                            multipart_key[(k>>2)] |= buffer_area[k] << (8*(k&3));
                        }
 
                        init_by_array(&multipart_key[0], keysize);
                    }
                }
                if (((i+1) % 32) == 0) {
                    for (j = i-31; j <= i; j++) {
                        pBuf[0 + (j-i+31)] = buffer_area[j];
                    }
                    for (j=0; j<8; j++) {
                        pBuf[32+4*j] = (m_sha256.hash[j] >> 24) & 0xff;
                        pBuf[33+4*j] = (m_sha256.hash[j] >> 16) & 0xff;
                        pBuf[34+4*j] = (m_sha256.hash[j] >> 8) & 0xff;
                        pBuf[35+4*j] =  m_sha256.hash[j] & 0xff;
                    }
                    sha256_begin(&m_sha256);
                    sha256_hash(pBuf, 64, &m_sha256);
                    sha256_end(pTemp, &m_sha256);
                }
                i++;
            }
        }
 
        if (DECRYPT == direction) {
            if (blocks_processed == 1) {
                if (eof == 0) { // if not the end of file, hash all
                    fwrite(&buffer_area[32],1,bytes_read-32,fd_out);
                    sha256_hash(&buffer_area[0],bytes_read, &file_sha256);
                } else {  // don't hash stored hash at the end.
                    fwrite(&buffer_area[32],1,bytes_read-64,fd_out);
                    sha256_hash(&buffer_area[0],bytes_read-32, &file_sha256);
                    memset(comp_sha256.hash, 0, 32);
                    for (j=0; j<8; j++) {
                        comp_sha256.hash[j] |= buffer_area[bytes_read-32+4*j] << 24;
                        comp_sha256.hash[j] |= buffer_area[bytes_read-31+4*j] << 16;
                        comp_sha256.hash[j] |= buffer_area[bytes_read-30+4*j] << 8;
                        comp_sha256.hash[j] |= buffer_area[bytes_read-29+4*j];
                    }
                }
            } else {
                if (eof == 0) { // if not the end of file, hash all
                    fwrite(&buffer_area[0],1,bytes_read,fd_out);
                    sha256_hash(&buffer_area[0],bytes_read, &file_sha256);
                } else {  // don't hash stored hash at the end.
                    fwrite(&buffer_area[0],1,bytes_read-32,fd_out);
                    sha256_hash(&buffer_area[0],bytes_read-32, &file_sha256);
                    memset(comp_sha256.hash, 0, 32);
                    for (j=0; j<8; j++) {
                        comp_sha256.hash[j] |= buffer_area[bytes_read-32+4*j] << 24;
                        comp_sha256.hash[j] |= buffer_area[bytes_read-31+4*j] << 16;
                        comp_sha256.hash[j] |= buffer_area[bytes_read-30+4*j] << 8;
                        comp_sha256.hash[j] |= buffer_area[bytes_read-29+4*j];
                    }
                }
            }
        } else {
            fwrite(&buffer_area[0],1,bytes_read,fd_out);
        }
        start_fill = 0;
    } while (eof == 0);
 
    fclose(fd_in);
 
    sha256_end(pTemp, &file_sha256);
 
    for (j=0; j<8; j++) {
        pBuf[0+4*j] = (file_sha256.hash[j] >> 24) & 0xff;
        pBuf[1+4*j] = (file_sha256.hash[j] >> 16) & 0xff;
        pBuf[2+4*j] = (file_sha256.hash[j] >> 8) & 0xff;
        pBuf[3+4*j] =  file_sha256.hash[j] & 0xff;
    }
 
#if 0
    printf("\nHash = ");
    for (j = 0; j<8; j++) {
        printf("0x%8x ", file_sha256.hash[j]);
    }
    printf("\n");
#endif
 
    if (ENCRYPT == direction) { // need to output the encrypted hash
        fwrite(&pBuf[0],1,32,fd_out);
    } else {  // need to compare computed hash with decrypted hash

        if (memcmp(&comp_sha256.hash, &file_sha256.hash, 32) != 0) {
#if 0
            printf("\nstored hash     = ");
            for (j = 0; j<8; j++) {
                printf("0x%8x ", comp_sha256.hash[j]);
            }
            printf("\n");
            printf("calculated hash = ");
            for (j = 0; j<8; j++) {
                printf("0x%8x ", file_sha256.hash[j]);
            }
            printf("\n");
#endif
            fclose(fd_out);
 
            _unlink(my_out);
            printf("\nIncorrect passphrase, try again\n");
            exit(0);
        }
    }
 
    fclose(fd_out);
    exit(0);
}
</stdlib.h></stdlib.h></endian.h></byteswap.h></stdlib.h></string.h></limits.h></time.h></comutil.h></string.h></memory.h></stdlib.h></stdio.h></io.h>

GeneralUpdated code, includes bug fixes PinmemberTom Stone19-Dec-08 9:35 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140415.2 | Last Updated 22 Jul 2008
Article Copyright 2008 by Tom Stone
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid