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

Secure Encryption For Data Storage with Key Re-use

, 22 Jul 2008
Rate this:
Please Sign up or sign in to vote.
Enhanced encryption algorthim for data storage (console app)

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 
GeneralUpdated code, includes bug fixes PinmemberTom Stone19-Dec-08 9:35 

#include
#include
#include
#include
#include
#include
#include
 
#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
 
/* 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 BYTES_PER_WORD 4
#define WORDS_PER_BLOCK (BYTES_PER_BLOCK/BYTES_PER_WORD)
#define REHASH_SIZE (BYTES_PER_BLOCK*2)
 
#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 /* for memcpy() etc. */
#include /* for _lrotr with VC++ */
 
//#include "sha2.h"
 
/* 1. PLATFORM SPECIFIC INCLUDES */
 
#if defined(__GNU_LIBRARY__)
# include
# include
#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
#elif !defined(WIN32)
# include
# 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;
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 */
 
for (i=0; i<64; i++) dummy = genrand_int32();
 
}
 
/* generates a random number on [0,0xffffffff]-interval */
unsigned long genrand_int32(void)
{
unsigned long y;
static unsigned long mag01[2]={0x0UL, MATRIX_A};
/* mag01[x] = x * MATRIX_A for x=0,1 */
 
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);
 
accum += y;
 
return accum;
}
 
#define ENCRYPT 0
#define DECRYPT 1
 
/*
** 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 some high entropy
** data 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 cipher 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.
**
** On decryption the first block is stripped.
**
** It is not as slow as I expected. On a 3GHz dual core Pentium it encrypts 8MB/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;
unsigned char pBuf[4096], pTemp[256];
unsigned long uRead = 0;
 
FILETIME idleTimePtr, kernelTimePtr, userTimePtr;
 
memset(buffer_area, 0, sizeof(buffer_area));
memset(output_filename, 0, sizeof(output_filename));
 
memset(multipart_key, 0, 4*N);
/*
** for encryption
** get high entropy seed for PRNG
** use PRNG to generate 32 byte block
** reseed the PRNG with KEY
*/
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 entropy stuff, now add the passphrase
*/
char * string = argv[2];
for (i = 0; i < strlen(argv[2]); i++) {
multipart_key[WORDS_PER_BLOCK+(i>>2)] |= string[i] << (WORDS_PER_BLOCK*(i&3));
}
keysize = (i >> 2) + 1;
if (i&3) keysize++;
keysize += WORDS_PER_BLOCK;
init_by_array(&multipart_key[0], keysize);
/*
** Clear out the first BYTES_PER_BLOCK bytes of the multipart_key, the passphrase portion will be re-used
*/
memset(&multipart_key[0], 0, BYTES_PER_BLOCK);
/*
** 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
*/
for (k = 0; k < BYTES_PER_BLOCK; k++) {
multipart_key[(k>>2)] |= buffer_area[k] << (WORDS_PER_BLOCK*(k&3));
}
 
if (argc == 1) {
printf("Invoke encryption using\n\n");
printf(" \"%s file passphrase\"\n", argv[0]);
printf("Encrypted/decrypted output will be the filename with an\nunderscore appended/removed\n");
printf("The encrypted file will be 32 bytes larger than the plain text file\n");
exit(0);
} else {
if (argc < 3) {
printf("too few arguments\n");
exit(0);
}
}
 

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);
} else {
printf("insufficient arguments\n");
exit(0);
}
 
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) {
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"));
/*
** for each 32 byte block
** compute next blocks hash
** for each byte in block
** a(i) = a(i-1) + p(i) & 0xff
** apply p(i) ^= a(i-1)
** apply p(i) ^= k(i)
** endfor
** block ^= current blocks hash
** endfor
*/
 

do {
bytes_read = fread( &buffer_area[start_fill], 1, (MAX_WORDS-start_fill), fd_in);
bytes_read += start_fill;
blocks_processed++;
i = 0;
while (i < bytes_read) {
if (ENCRYPT == direction) {
if ((i % BYTES_PER_BLOCK) == 0) {
for (j = i; j <= i+BYTES_PER_BLOCK-1; j++) {
pBuf[j%BYTES_PER_BLOCK] = buffer_area[i+(j%BYTES_PER_BLOCK)];
}
}
if (((i+1) % BYTES_PER_BLOCK) == 0) { // calculate hash for next block
for (j=0; j<8; j++) {
pBuf[BYTES_PER_BLOCK+4*j] = (m_sha256.hash[j] >> 24) & 0xff;
pBuf[BYTES_PER_BLOCK+1+4*j] = (m_sha256.hash[j] >> 16) & 0xff;
pBuf[BYTES_PER_BLOCK+2+4*j] = (m_sha256.hash[j] >> 8) & 0xff;
pBuf[BYTES_PER_BLOCK+3+4*j] = m_sha256.hash[j] & 0xff;
}
sha256_begin(&m2_sha256);
sha256_hash(pBuf, REHASH_SIZE, &m2_sha256);
sha256_end(pTemp, &m2_sha256);
}
buffer_area[i] ^= m_sha256.hash[((i%BYTES_PER_BLOCK)/BYTES_PER_WORD)] >> ((i%4)*8);
/* the 32 byte IV is only XORed with the hash */
if ((blocks_processed > 1) || (i>(BYTES_PER_BLOCK-1))) {
keystream = genrand_int32() >> 24;
buffer_area[i] ^= keystream;
}
if ((++i % BYTES_PER_BLOCK) == 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
*/
 
buffer_area[i] ^= m_sha256.hash[((i%BYTES_PER_BLOCK)/BYTES_PER_WORD)] >> ((i%4)*8);
if (IV_processed) {
keystream = genrand_int32() >> 24;
buffer_area[i] ^= keystream;
}
 
if (blocks_processed == 1) {/* not processed IV yet? */
if (i == (BYTES_PER_BLOCK-1)) { /* is this the end of the IV */
memset(&multipart_key[0], 0, BYTES_PER_BLOCK);
IV_processed = 1; /* set IV to processed */
for (k = 0; k < BYTES_PER_BLOCK; k++) {
multipart_key[(k>>2)] |= buffer_area[k] << (8*(k&3));
}
 
init_by_array(&multipart_key[0], keysize);
}
}
if (((i+1) % BYTES_PER_BLOCK) == 0) {
for (j = i-(BYTES_PER_BLOCK-1); j <= i; j++) {
pBuf[0 + (j-i+BYTES_PER_BLOCK-1)] = buffer_area[j];
}
for (j=0; j<8; j++) {
pBuf[BYTES_PER_BLOCK+4*j] = (m_sha256.hash[j] >> 24) & 0xff;
pBuf[BYTES_PER_BLOCK+1+4*j] = (m_sha256.hash[j] >> 16) & 0xff;
pBuf[BYTES_PER_BLOCK+2+4*j] = (m_sha256.hash[j] >> 8) & 0xff;
pBuf[BYTES_PER_BLOCK+3+4*j] = m_sha256.hash[j] & 0xff;
}
sha256_begin(&m_sha256);
sha256_hash(pBuf, REHASH_SIZE, &m_sha256);
sha256_end(pTemp, &m_sha256);
}
i++;
}
}
if ((DECRYPT == direction) && (blocks_processed == 1)) {
fwrite(&buffer_area[BYTES_PER_BLOCK],1,bytes_read-BYTES_PER_BLOCK,fd_out);
} else {
fwrite(&buffer_area[0],1,bytes_read,fd_out);
}
start_fill = 0;
eof = feof(fd_in);
} while (eof == 0);
 
fclose(fd_in);
fclose(fd_out);
exit(0);
}
 

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
Web01 | 2.8.140721.1 | Last Updated 22 Jul 2008
Article Copyright 2008 by Tom Stone
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid