/*
* This code implements the lucifer encryption/decryption
* code but has been modified to work just on small strings.
*
* Originally written by Arthur Sorkin in 1984 in FORTRAN
*
* Rewritten in 1991 by Jonathan M. Smith
*
* This code is in the public domain. Patents have expired.
*
* To encrypt a string (must be 16 bytes or less) instantiate
* a Mangle object, call In() passing the string, key and an
* output buffer to store the result. To decrypt the string
* instantiate a Mangle object, call Out() passing in the 32
* character (hex) string and the originally key, string will
* be returned in the output buffer in the same way as In().
*
* Notes:
*
* data must be 16 bytes or less (except see below).
* key will be truncated to a maximum of 16 characters.
*
* As part of the security improvements the digest stored in the
* server is no longer usable on the client, in order to mangle
* the 32 character digest (data > 16 characters) alternative
* interfaces InMD5() and OutMD5() handle the "raw" digest.
*/
#include <stdhdrs.h>
#include <error.h>
#include <strbuf.h>
#include <strops.h>
#include <msgsupp.h>
#include "mangle.h"
#define BPB 8
Mangle::Mangle()
{
// diffusion pattern
o[0] = 7; o[1] = 6; o[2] = 2; o[3] = 1;
o[4] = 5; o[5] = 0; o[6] = 3; o[7] = 4;
// inverse of fixed permutation
pr[0] = 2; pr[1] = 5; pr[2] = 4; pr[3] = 0;
pr[4] = 3; pr[5] = 1; pr[6] = 7; pr[7] = 6;
// S-box permutations
s0[0] = 12; s0[1] = 15; s0[2] = 7; s0[3] = 10;
s0[4] = 14; s0[5] = 13; s0[6] = 11; s0[7] = 0;
s0[8] = 2; s0[9] = 6; s0[10] = 3; s0[11] = 1;
s0[12] = 9; s0[13] = 4; s0[14] = 5; s0[15] = 8;
s1[0] = 7; s1[1] = 2; s1[2] = 14; s1[3] = 9;
s1[4] = 3; s1[5] = 11; s1[6] = 0; s1[7] = 4;
s1[8] = 12; s1[9] = 13; s1[10] = 1; s1[11] = 10;
s1[12] = 6; s1[13] = 15; s1[14] = 8; s1[15] = 5;
// S-box mix
s2[0] = 10; s2[1] = 1; s2[2] = 13; s2[3] = 12;
s2[4] = 4; s2[5] = 0; s2[6] = 11; s2[7] = 3;
}
void
Mangle::DoIt(
const StrPtr &data,
const StrPtr &key,
StrBuf &result,
int decipher,
int digest,
Error *e )
{
int m[128];
int k[128];
char *p, *q;
char src[17];
char enc[17];
char buf[17];
int counter;
int output;
int c, i, j;
if( ( decipher && data.Length() != 32 && data.Length() != 0 ) ||
( !decipher && data.Length() > 16 && !digest ) ||
( !decipher && data.Length() != 32 && digest ) )
{
e->Set( MsgSupp::BadMangleParams );
}
if( e->Test() )
return;
memset( src, 0, 17 );
memset( enc, 0, 17 );
memset( buf, 0, 17 );
// truncate key to 16 character max
memcpy( buf, key.Text(), key.Length() > 16 ? 16 : key.Length() );
if( decipher || digest )
StrOps::XtoO( data.Text(), (unsigned char *)src, 16 );
else
memcpy( src, data.Text(), data.Length() );
p = src;
q = enc;
for( counter = 0; counter < 16; counter += 1 )
{
c = buf[counter] & 0xFF;
for( i = 0; i < BPB; i += 1 )
{
k[(BPB*counter)+i] = c & 0x1;
c = c>>1;
}
}
counter = 0;
/* Mix it up a bit
*/
if( decipher )
{
s1[4] = s2[0]; s1[5] = s2[1]; s1[6] = s2[2]; s1[7] = s2[3];
}
/* Lose check for NULL, since its a valid character, assume its a
* 16 byte cipher block (thats all it ever should be, lose the
* while( (c=*p++) != '\0' )
*/
for( j = 0; j < 16; ++j )
{
c = *p++;
if( counter == 16 )
{
Getdval( decipher, m, k );
for( counter = 0; counter < 16; counter += 1 )
{
output = 0;
for( i = BPB-1; i >= 0; i -= 1 )
output = (output<<1) + m[(BPB*counter)+i];
*q++ = output;
}
counter = 0;
}
for( i = 0; i < BPB; i += 1 )
{
m[(BPB*counter)+i] = c & 0x1;
c = c>>1;
}
counter += 1;
}
for( ;counter < 16; counter += 1 )
for( i = 0; i < BPB; i += 1 )
m[(BPB*counter)+i] = 0;
Getdval( decipher, m, k );
for( counter = 0; counter < 16; counter += 1 )
{
output = 0;
for( i = BPB-1; i >= 0; i -= 1 )
output = (output<<1) + m[(BPB*counter)+i];
*q++ = output;
}
*q = '\0';
result.Clear();
if( decipher && !digest )
result.Set( enc );
else
StrOps::OtoX( (const unsigned char *)enc, 16, result );
}
void
Mangle::In( const StrPtr &in, const StrPtr &key, StrBuf &result, Error *e )
{
StrBuf mangledValue;
StrBuf inputSegment;
int inputLen = in.Length();
int offset = 0;
while( offset < inputLen )
{
StrBuf data, mangledChunk;
int chunkSize = inputLen - offset;
if( chunkSize > 16 ) chunkSize = 16;
data.Extend( &in.Text()[offset], chunkSize );
data.Terminate();
DoIt( data, key, mangledChunk, 0, 0, e );
if( e->Test() )
return;
mangledValue.Append( &mangledChunk );
offset += chunkSize;
}
result = mangledValue;
}
void
Mangle::Out( const StrPtr &out, const StrPtr &key, StrBuf &result, Error *e )
{
// Extract the password (cleartext) using the key
StrBuf extractedValue;
StrBuf inputSegment;
int inputLen = out.Length();
int offset = 0;
while( offset < inputLen )
{
StrBuf data, extractedChunk;
int chunkSize = inputLen - offset;
if( chunkSize > 32 ) chunkSize = 32;
data.Extend( &out.Text()[offset], chunkSize );
data.Terminate();
DoIt( data, key, extractedChunk, 1, 0, e );
if( e->Test() )
return;
extractedValue.Append( &extractedChunk );
offset += chunkSize;
}
result = extractedValue;
}
void
Mangle::Getdval( int decipher, int m[], int k[] )
{
int tcbindex, tcbcontrol; /* transfer control byte indices */
int round, hi, lo, h_0, h_1;
int bit, temp1;
int byte, index, v, tr[BPB];
h_0 = 0;
h_1 = 1;
tcbcontrol = decipher ? 8 : 0;
/* mix it up a bit
*/
if( decipher )
{
s1[8] = s2[4]; s1[9] = s2[5]; s1[10] = s2[6]; s1[11] = s2[7];
}
for( round=0; round<16; round += 1 )
{
if( decipher )
tcbcontrol = (tcbcontrol+1) & 0xF;
tcbindex = tcbcontrol;
for( byte = 0; byte < 8; byte +=1 )
{
lo = (m[(h_1*64)+(BPB*byte)+7])*8
+(m[(h_1*64)+(BPB*byte)+6])*4
+(m[(h_1*64)+(BPB*byte)+5])*2
+(m[(h_1*64)+(BPB*byte)+4]);
hi = (m[(h_1*64)+(BPB*byte)+3])*8
+(m[(h_1*64)+(BPB*byte)+2])*4
+(m[(h_1*64)+(BPB*byte)+1])*2
+(m[(h_1*64)+(BPB*byte)+0]);
v = (s0[lo]+16*s1[hi])*(1-k[(BPB*tcbindex)+byte])
+(s0[hi]+16*s1[lo])*k[(BPB*tcbindex)+byte];
for( temp1 = 0; temp1 < BPB; temp1 += 1 )
{
tr[temp1] = v & 0x1;
v = v>>1;
}
for( bit = 0; bit < BPB; bit += 1 )
{
index = (o[bit]+byte) & 0x7;
temp1 = m[(h_0*64)+(BPB*index)+bit]
+k[(BPB*tcbcontrol)+pr[bit]]
+tr[pr[bit]];
m[(h_0*64)+(BPB*index)+bit] = temp1 & 0x1;
}
if( byte<7 || decipher )
tcbcontrol = (tcbcontrol+1) & 0xF;
}
temp1 = h_0;
h_0 = h_1;
h_1 = temp1;
}
/* final swap */
for( byte = 0; byte < 8; byte += 1 )
{
for( bit = 0; bit < BPB; bit += 1 )
{
temp1 = m[(BPB*byte)+bit];
m[(BPB*byte)+bit] = m[64+(BPB*byte)+bit];
m[64+(BPB*byte)+bit] = temp1;
}
}
}
void
Mangle::XOR(
StrBuf &data,
const StrPtr &key,
Error *e )
{
if( data.Length() != 32 && key.Length() != 32 )
e->Set( MsgSupp::BadMangleParams );
if( e->Test() )
return;
char src[17];
char enc[17];
char buf[17];
StrOps::XtoO( data.Text(), (unsigned char *)src, 16 );
StrOps::XtoO( key.Text(), (unsigned char *)enc, 16 );
for( int i = 0; i < 16; ++i )
buf[i] = src[i] ^ enc[i];
data.Clear();
StrOps::OtoX( (const unsigned char *)buf, 16, data );
}