Reconstruction of des.exe/undes.exe

This commit is contained in:
2026-09-09 23:28:46 +02:00
parent b90136f137
commit 5aa37bf374
64 changed files with 13344 additions and 2 deletions
+410
View File
@@ -0,0 +1,410 @@
/*
* idea.c - C source code for IDEA block cipher.
* IDEA (International Data Encryption Algorithm), formerly known as
* IPES (Improved Proposed Encryption Standard).
* Algorithm developed by Xuejia Lai and James L. Massey, of ETH Zurich.
* This implementation modified and derived from original C code
* developed by Xuejia Lai.
* Zero-based indexing added, names changed from IPES to IDEA.
* CFB functions added. Random number routines added.
*
* Extensively optimized and restructured by Colin Plumb.
*
* There are two adjustments that can be made to this code to
* speed it up. Defaults may be used for PCs. Only the -DIDEA32
* pays off significantly if selectively set or not set.
* Experiment to see what works best for your machine.
*
* Multiplication: default is inline, -DAVOID_JUMPS uses a
* different version that does not do any conditional
* jumps (a few percent worse on a SPARC), while
* -DSMALL_CACHE takes it out of line to stay
* within a small on-chip code cache.
* Variables: normally, 16-bit variables are used, but some
* machines (notably RISCs) do not have 16-bit registers,
* so they do a great deal of masking. -DIDEA32 uses "int"
* register variables and masks explicitly only where
* necessary. On a SPARC, for example, this boosts
* performace by 30%.
*
* The IDEA(tm) block cipher is covered by patents held by ETH and a
* Swiss company called Ascom-Tech AG. The Swiss patent number is
* PCT/CH91/00117, the European patent number is EP 0 482 154 B1, and
* the U.S. patent number is US005214703. IDEA(tm) is a trademark of
* Ascom-Tech AG. There is no license fee required for noncommercial
* use. Commercial users may obtain licensing details from Dieter
* Profos, Ascom Tech AG, Solothurn Lab, Postfach 151, 4502 Solothurn,
* Switzerland, Tel +41 65 242885, Fax +41 65 235761.
*
* The IDEA block cipher uses a 64-bit block size, and a 128-bit key
* size. It breaks the 64-bit cipher block into four 16-bit words
* because all of the primitive inner operations are done with 16-bit
* arithmetic. It likewise breaks the 128-bit cipher key into eight
* 16-bit words.
*
* For further information on the IDEA cipher, see the book:
* Xuejia Lai, "On the Design and Security of Block Ciphers",
* ETH Series on Information Processing (ed. J.L. Massey) Vol 1,
* Hartung-Gorre Verlag, Konstanz, Switzerland, 1992. ISBN
* 3-89191-573-X.
*
* This code runs on arrays of bytes by taking pairs in big-endian
* order to make the 16-bit words that IDEA uses internally. This
* produces the same result regardless of the byte order of the
* native CPU.
*/
#include <string.h>
#ifdef _MSC_VER
#include "../crypt.h"
#include "idea.h"
#else
#include "crypt.h"
#include "idea/idea.h"
#endif /* _MSC_VER */
#ifdef BIG_ENDIAN /* This code uses slightly different names */
#define HIGHFIRST
#endif /* BIG_ENDIAN */
#ifdef IDEA32 /* Use >16-bit temporaries */
#define low16(x) ((x) & 0xFFFF)
typedef unsigned int uint16; /* at LEAST 16 bits, maybe more */
#else
#define low16(x) (x) /* this is only ever applied to uint16's */
typedef word16 uint16;
#endif
#ifdef _GNUC_
/* __const__ simply means there are no side effects for this function,
* which is useful info for the gcc optimizer
*/
#define CONST __const__
#else
#define CONST
#endif
/*
* Multiplication, modulo (2**16)+1
* Note that this code is structured on the assumption that
* untaken branches are cheaper than taken branches, and the
* compiler doesn't schedule branches.
*/
#ifdef SMALL_CACHE
CONST static uint16
mul(register uint16 a, register uint16 b)
{
register word32 p;
p = (word32)a * b;
if (p) {
b = low16(p);
a = p>>16;
return (b - a) + (b < a);
} else if (a) {
return 1-b;
} else {
return 1-a;
}
} /* mul */
#endif /* SMALL_CACHE */
/*
* Compute the multiplicative inverse of x, modulo 65537, using Euclid's
* algorithm. It is unrolled twice to avoid swapping the registers each
* iteration, and some subtracts of t have been changed to adds.
*/
CONST static uint16
mulInv(uint16 x)
{
uint16 t0, t1;
uint16 q, y;
if (x <= 1)
return x; /* 0 and 1 are self-inverse */
t1 = 0x10001L / x; /* Since x >= 2, this fits into 16 bits */
y = 0x10001L % x;
if (y == 1)
return ( uint16 ) low16(1-t1);
t0 = 1;
do {
q = x / y;
x = x % y;
t0 += q * t1;
if (x == 1)
return t0;
q = y / x;
y = y % x;
t1 += q * t0;
} while (y != 1);
return ( uint16 ) low16(1-t1);
} /* mukInv */
/*
* Expand a 128-bit user key to a working encryption key EK
*/
void
ideaExpandKey(byte const *userkey, word16 *EK)
{
int i,j;
for (j=0; j<8; j++) {
EK[j] = (userkey[0]<<8) + userkey[1];
userkey += 2;
}
for (i=0; j < IDEAKEYLEN; j++) {
i++;
EK[i+7] = (EK[i & 7] << 9) | (EK[i+1 & 7] >> 7);
EK += i & 8;
i &= 7;
}
} /* ideaExpandKey */
/*
* Compute IDEA decryption key DK from an expanded IDEA encryption key EK
* Note that the input and output may be the same. Thus, the key is
* inverted into an internal buffer, and then copied to the output.
*/
void
#ifdef _DCC
ideaInvertKey(word16 *EK, word16 DK[IDEAKEYLEN])
#else
ideaInvertKey(word16 const *EK, word16 DK[IDEAKEYLEN])
#endif
{
int i;
uint16 t1, t2, t3;
word16 temp[IDEAKEYLEN];
word16 *p = temp + IDEAKEYLEN;
t1 = mulInv(*EK++);
t2 = - ( int ) *EK++;
t3 = - ( int ) *EK++;
*--p = mulInv(*EK++);
*--p = t3;
*--p = t2;
*--p = t1;
for (i = 0; i < IDEAROUNDS-1; i++) {
t1 = *EK++;
*--p = *EK++;
*--p = t1;
t1 = mulInv(*EK++);
t2 = - ( int ) *EK++;
t3 = - ( int ) *EK++;
*--p = mulInv(*EK++);
*--p = t2;
*--p = t3;
*--p = t1;
}
t1 = *EK++;
*--p = *EK++;
*--p = t1;
t1 = mulInv(*EK++);
t2 = - ( int ) *EK++;
t3 = - ( int ) *EK++;
*--p = mulInv(*EK++);
*--p = t3;
*--p = t2;
*--p = t1;
/* Copy and destroy temp copy */
memcpy(DK, temp, sizeof(temp));
burn(temp);
} /* ideaInvertKey */
/*
* MUL(x,y) computes x = x*y, modulo 0x10001. Requires two temps,
* t16 and t32. x is modified, and must me a side-effect-free lvalue.
* y may be anything, but unlike x, must be strictly 16 bits even if
* low16() is #defined.
* All of these are equivalent - see which is faster on your machine
*/
#ifdef SMALL_CACHE
#define MUL(x,y) (x = mul(low16(x),y))
#else /* !SMALL_CACHE */
#ifdef AVOID_JUMPS
#define MUL(x,y) (x = low16(x-1), t16 = low16((y)-1), \
t32 = (word32)x*t16 + x + t16 + 1, x = low16(t32), \
t16 = t32>>16, x = (x-t16) + (x<t16) )
#else /* !AVOID_JUMPS (default) */
#define MUL(x,y) \
((t16 = (y)) ? \
(x=low16(x)) ? \
t32 = (word32)x*t16, \
x = low16(t32), \
t16 = t32>>16, \
x = (x-t16)+(x<t16) \
: \
(x = 1-t16) \
: \
(x = 1-x))
#endif
#endif
/* IDEA encryption/decryption algorithm */
/* Note that in and out can be the same buffer */
void
#ifdef _DCC
ideaCipher(byte (inbuf[8]), byte (outbuf[8]), word16 *key)
#else
ideaCipher(byte const (inbuf[8]), byte (outbuf[8]), word16 const *key)
#endif
{
register uint16 x1, x2, x3, x4, s2, s3;
word16 *in, *out;
#ifndef SMALL_CACHE
register uint16 t16; /* Temporaries needed by MUL macro */
register word32 t32;
#endif
int r = IDEAROUNDS;
in = (word16 *)inbuf;
x1 = *in++; x2 = *in++;
x3 = *in++; x4 = *in;
#ifndef HIGHFIRST
x1 = (x1>>8) | (x1<<8);
x2 = (x2>>8) | (x2<<8);
x3 = (x3>>8) | (x3<<8);
x4 = (x4>>8) | (x4<<8);
#endif
do {
MUL(x1,*key++);
x2 += *key++;
x3 += *key++;
MUL(x4, *key++);
s3 = x3;
x3 ^= x1;
MUL(x3, *key++);
s2 = x2;
x2 ^= x4;
x2 += x3;
MUL(x2, *key++);
x3 += x2;
x1 ^= x2; x4 ^= x3;
x2 ^= s3; x3 ^= s2;
} while (--r);
MUL(x1, *key++);
x3 += *key++;
x2 += *key++;
MUL(x4, *key);
out = (word16 *)outbuf;
#ifdef HIGHFIRST
*out++ = x1;
*out++ = x3;
*out++ = x2;
*out = x4;
#else /* !HIGHFIRST */
x1 = low16(x1);
x2 = low16(x2);
x3 = low16(x3);
x4 = low16(x4);
*out++ = (x1>>8) | (x1<<8);
*out++ = (x3>>8) | (x3<<8);
*out++ = (x2>>8) | (x2<<8);
*out = (x4>>8) | (x4<<8);
#endif
} /* ideaCipher */
/*-------------------------------------------------------------*/
#ifdef TEST
#include <stdio.h>
#include <time.h>
/*
* This is the number of Kbytes of test data to encrypt.
* It defaults to 1 MByte.
*/
#ifndef BLOCKS
#ifndef KBYTES
#define KBYTES 1024
#endif
#define BLOCKS (64*KBYTES)
#endif
int
main(void)
{ /* Test driver for IDEA cipher */
int i, j, k;
byte userkey[16];
word16 EK[IDEAKEYLEN], DK[IDEAKEYLEN];
byte XX[8], YY[8], ZZ[8];
clock_t start, end;
long l;
/* Make a sample user key for testing... */
for(i=0; i<16; i++)
userkey[i] = i+1;
/* Compute encryption subkeys from user key... */
ideaExpandKey(userkey, EK);
printf("\nEncryption key subblocks: ");
for (j=0; j<IDEAROUNDS+1; j++) {
printf("\nround %d: ", j+1);
if (j < IDEAROUNDS)
for(i=0; i<6; i++)
printf(" %6u", EK[j*6+i]);
else
for(i=0; i<4; i++)
printf(" %6u", EK[j*6+i]);
}
/* Compute decryption subkeys from encryption subkeys... */
ideaInvertKey(EK, DK);
printf("\nDecryption key subblocks: ");
for (j=0; j<IDEAROUNDS+1; j++) {
printf("\nround %d: ", j+1);
if (j < IDEAROUNDS)
for(i=0; i<6; i++)
printf(" %6u", DK[j*6+i]);
else
for(i=0; i<4; i++)
printf(" %6u", DK[j*6+i]);
}
/* Make a sample plaintext pattern for testing... */
for (k=0; k<8; k++)
XX[k] = k;
printf("\n Encrypting %d bytes (%ld blocks)...", BLOCKS*16, BLOCKS);
fflush(stdout);
start = clock();
memcpy(YY, XX, 8);
for (l = 0; l < BLOCKS; l++)
ideaCipher(YY, YY, EK); /* repeated encryption */
memcpy(ZZ, YY, 8);
for (l = 0; l < BLOCKS; l++)
ideaCipher(ZZ, ZZ, DK); /* repeated decryption */
end = clock() - start;
l = end / (CLOCKS_PER_SEC/1000) + 1;
i = l/1000;
j = l%1000;
l = (16 * BLOCKS * (CLOCKS_PER_SEC/1000)) / (end/1000);
printf("%d.%03d seconds = %ld bytes per second\n", i, j, l);
printf("\nX %3u %3u %3u %3u %3u %3u %3u %3u\n",
XX[0], XX[1], XX[2], XX[3], XX[4], XX[5], XX[6], XX[7]);
printf("\nY %3u %3u %3u %3u %3u %3u %3u %3u\n",
YY[0], YY[1], YY[2], YY[3], YY[4], YY[5], YY[6], YY[7]);
printf("\nZ %3u %3u %3u %3u %3u %3u %3u %3u\n",
ZZ[0], ZZ[1], ZZ[2], ZZ[3], ZZ[4], ZZ[5], ZZ[6], ZZ[7]);
/* Now decrypted ZZ should be same as original XX */
for (k=0; k<8; k++)
if (XX[k] != ZZ[k]) {
printf("\n\07Error! Noninvertable encryption.\n");
exit(-1); /* error exit */
}
printf("\nNormal exit.\n");
return 0; /* normal exit */
} /* main */
#endif /* TEST */
/* end of idea.c */
+34
View File
@@ -0,0 +1,34 @@
#ifndef _IDEA_DEFINED
#define _IDEA_DEFINED
/* Defines for the PGP-style types used in IDEA.C */
#define byte unsigned char
#define word16 unsigned short int
#define word32 unsigned long int
/* Macros from PGP */
#define burn(x) memset( x, 0, sizeof( x ) )
/* IDEA algorithm constants */
#define IDEAKEYSIZE 16
#define IDEABLOCKSIZE 8
#define IDEAROUNDS 8
#define IDEAKEYLEN ( 6 * IDEAROUNDS + 4 )
/* Routines used to implement the IDEA encryption */
void ideaExpandKey( byte const *userkey, word16 *EK );
#ifdef _DCC
void ideaInvertKey( word16 *EK, word16 DK[IDEAKEYLEN] );
void ideaCipher( byte (inbuf[8]), byte (outbuf[8]), word16 *key );
#else
void ideaInvertKey( word16 const *EK, word16 DK[IDEAKEYLEN] );
void ideaCipher( byte const (inbuf[8]), byte (outbuf[8]), word16 const *key );
#endif
#endif /* _IDEA_DEFINED */
+173
View File
@@ -0,0 +1,173 @@
;-------------------------------------------------------------------------
; idea68k.a
;
; 68000 Assembler version of idea cipher, direct translation from c code
; from PGP.
;
; Author: Risto Paasivirta, paasivir@jyu.fi.
;
section text,code
xdef _asm_mul,_asm_inv
xdef _asm_cipher_idea
; key schedule block
ROUNDS equ 8
Z0 equ 0
Z1 equ (ROUNDS+1)*2
Z2 equ (ROUNDS+1)*4
Z3 equ (ROUNDS+1)*6
Z4 equ (ROUNDS+1)*8
Z5 equ (ROUNDS+1)*10
ZSIZE equ (ROUNDS+1)*12
KSSIZE equ ZSIZE*2
;-------------------------------------------------------------------------
;
; idmul da,db -- db = da * db mod 65537, d0 = scratch (da may be d0)
;
idmul macro
tst.w \2
bne.b idmul1.\@
moveq #1,\2
sub.w \1,\2
bra.b idmul3.\@
idmul1.\@ tst.w \1
bne.b idmul2.\@
moveq #1,d0
sub.w \2,d0
move.w d0,\2
bra.b idmul3.\@
idmul2.\@ mulu.w \1,\2
move.l \2,d0
swap d0
sub.w d0,\2
bcc.b idmul3.\@
addq.w #1,\2
idmul3.\@
endm
;-------------------------------------------------------------------------
; idea_cip (a0=inblock,a1=outblock,a2=keyshedule) (d0-d7/a3 scratch)
;
;
;
idea_cip movem.w (a0),d1-d4
moveq #0,d7
idea_cip_loop lea 0(a2,d7.w),a3
idmul (a3),d1
idmul Z3(a3),d4
add.w Z1(a3),d2
add.w Z2(a3),d3
move.w d1,d6
eor.w d3,d6
idmul Z4(a3),d6
move.w d4,d5
eor.w d2,d5
add.w d6,d5
idmul Z5(a3),d5
add.w d5,d6
eor.w d5,d1
eor.w d6,d4
eor.w d6,d2
eor.w d5,d3
exg d2,d3
addq.w #2,d7
cmp.w #ROUNDS*2,d7
bcs idea_cip_loop
lea 0(a2,d7.w),a3
idmul (a3),d1
idmul Z3(a3),d4
add.w Z1(a3),d3
add.w Z2(a3),d2
exg d2,d3
movem.w d1-d4,(a1)
rts
;-------------------------------------------------------------------------
; _asm_cipher_idea(word16 *in,word16 *out,word16 *ks)
;
;
;
_asm_cipher_idea
movem.l a2-a3/d2-d7,-(sp)
movem.l 36(sp),a0-a2
bsr idea_cip
movem.l (sp)+,a2-a3/d2-d7
rts
;-------------------------------------------------------------------------
; word16 _asm_mul(word16, word16);
;
;
_asm_mul move.w 6(sp),d1
idmul 10(sp),d1
moveq #0,d0
move.w d1,d0
rts
;-------------------------------------------------------------------------
; d0:16 = inv(d0)
;
_asm_inv move.w 6(sp),d0
inv cmp.w #2,d0 ; inv(0)=0,inv(1)=1
bcs.b 1$
cmp.w #3,d0
bcc.b 2$
move.w #32769,d0 ; inv(2)
1$ rts
2$ movem.l d1-d7,-(sp)
move.l #$10001,d1 ; d1 = n1
moveq #1,d2 ; d2 = b2
moveq #0,d3 ; d3 = b1
inv_loop divu.w d0,d1
move.l d1,d4
swap d4 ; r = d4
tst.w d4
beq.b inv_done
move.w d2,d5
muls.w d1,d5
exg d3,d2
sub.l d5,d2
moveq #0,d1
move.w d0,d1
move.w d4,d0
bra.b inv_loop
inv_done tst.l d2
bpl.b 1$
move.l #$10001,d0
add.l d2,d0
bra.b 2$
1$ move.l d2,d0
2$ movem.l (sp)+,d1-d7
rts
;-------------------------------------------------------------------------
end
+285
View File
@@ -0,0 +1,285 @@
;A while ago I posted a message claiming a speed of 238,000
;bytes/sec for an implementation of IDEA on a 33Mh 486. Below is
;an explanation and some code to show how it works. The basic
;trick should be useful on many (but not all) processors. I
;expect only those familiar with IDEA and its reference
;implementation will be able to follow the discussion. See:
;
;Lai, Xueja and Massey, James L. A Proposal for a New Block
;Encryption Standard, Eurocrypt 90
;
;For those who have been asking for the code, sorry I kept
;putting it off. I wanted to get it out of Turbo Pascal
;ideal-mode, but I never had the time.
;
;Colin Plum wrote IDEA-386 code which is included in PGP
;2.3a and uses the same tricks. I don't know who's is
;faster, but I expect they will be very close. Now
;here's how it's done.
;
;A major bottleneck in software IDEA is the mul() routine, which
;is used 34 times per 64 bit block. The routine performs
;multiplication in the multiplicative group mod 2^16+1. The two
;factors are each in a 16 bit word, and the output is also in a 16
;bit word. Note that 0 is not a member of the multiplicative
;group and 2^16 does not fit in 16 bits. We therefor use the 0
;word to represent 2^16. Now group elements map one to one onto
;all possible 16 bit words, since 2^16+1 is prime.
;
;Here is (essentially) the reference implementation from [Lai].
;
;
;unsigned mul( unsigned a, unsigned b ) {
; long int p ;
; long unsigned q ;
; if( a==0 ) p= 0x00010001 - b ;
; else if( b==0 ) p= 0x00010001 - a ;
; else {
; q= a*b;
; p= (q & 0xffff) - (q>>16)
; if( p<0 ) p= p + 0x00010001 ;
; }
; return (unsigned)(p & 0xffff) ;
;}
;
;
;Note the method of reducing a 32 bit word modulo 2^16-1. We
;subtract the high word from the low word, and add the modulus
;back if the result is less than 0. [Lai] contains a proof that
;this works, and you can convince yourself fairly easily.
;
;To speed up this routine, we note that the tests for a=0 and b=0
;will rarely be false. With the possible exception of the first 2
;of the 34 multiplications, 0 should be no more likely than any of
;the other 65535 numbers. Note that if (and only if) either a or
;b is 0 then q will also be 0, and we can check for this in one
;instruction if our processor sets a zero flag for multiplication
;(as the 68000 does but 80x86 does not).
;
;Fortunately p will also be zero after the subtraction if and only
;if either a or b is 0. Proof: r will be zero when the high order
;word of q equals the low order word, and that happens when q is
;divisible by 00010001 hex. Since 00010001h = 2^16+1 is prime,
;this happens if either a or b is a multiple of 2^16+1, and 0 is
;the only such multiple which will fit in a 16 bit word.
;
;The speed-up strategy is to proceed under the assumption that a
;and b are not 0, check to be sure in one instruction, and
;recompute if the assumption was wrong. Here's some 8086
;assembler code:
;
; mov ax, [a]
; mul [b] ; ax is implied. q is now in DX AX
; sub ax, dx ; mod 2^16+1
; jnz not0 ; Jump if neither op was 0. Usually taken.
;
; mov ax, 1 ; recompute result knowing one op is 0.
; sub ax, [a]
; sub ax, [b]
; jmp out ; Just jump over adding the carry.
;not0:
; adc ax, 0 ; If r<0 add 1, otherwise do nothing.
;out: ; Result is now in ax
;
;
;Note that when r<0 we add 1 instead of 2^16+1 since the 2^16 part
;overflows out of the result. The "adc ax, 0" does all the work
;of checking for a negative result and adding the modulus if
;needed.
;
;The multiplication takes 9 instructions, 4 of which are rarely
;executed. I believe similar tricks are possible on many
;processors. The one drawback to the check-after-multiply tactic
;is that we can't let the multiply overwrite the only copy of an
;operand.
;
;Note that most software implementations of IDEA will run at
;slightly different speeds when 0's come up in the multiply
;routine. The reference implementation is faster on 0, this one
;is faster on non-zero. This may be a problem for some real-time
;stuff, and also suggests an attack based on timing.
;
;Finally, below is an implementation of the complete encryption
;function in 8086 assembler, to replace the cipher_idea() function
;in PGP. It takes the same parameters as the function from PGP,
;and uses the c language calling conventions. I tested it using
;the debug features of the idea.c file in PGP. You will need to
;add segment/assume directives. This version uses no global data
;and should be reentrant.
;
;The handling of zero multipliers is outside the inner loop so
;that a short conditional jump can loop back to the beginning.
;Forward conditional jumps are usually not taken and backward
;jumps are usually taken, which is consistent with 586 branch
;prediction (or so I've heard). Stalls where the output of one
;instruction is needed for the next seem unavoidable.
;
;Last I heard, IDEA was patent pending. My code is up for grabs,
;although I would get a kick out being credited if you use it.
;On the other hand Colin's code is already tested and ready
;to assemble and link with PGP.
;
;--Bryan
;
;____________________CODE STARTS BELOW THIS LINE_________
; Called as: asmcrypt( inbuff, outbuff, zkey ) just like PGP
PROC _asmcrypt
; establish parameter and local space on stack
; follow c language calling conventions
ARG inblock:Word, outblock:Word, zkey:Word
LOCAL sx1:Word,sx4:Word,skk:Word,done8:Word =stacksize
push bp
mov bp, sp
sub sp, stacksize
; push ax ; My compiler assumes these are not saved.
; push bx
; push cx
; push dx
push si
push di
; Put the 16 bit sub-blocks in registers and/or local variables
mov si, [inblock]
mov ax, [si]
mov [sx1], ax ; x1 is in ax and sx1
mov di, [si+2] ; x2 is in di
mov bx, [si+4] ; x3 is in bx
mov dx, [si+6]
mov [sx4], dx ; x4 is in sx4
mov si, [zkey] ; si points to next subkey
mov [done8], si
add [done8], 96 ; we will be finished with 8 rounds
; when si=done8
@@loop: ; 8 rounds of this
add di, [si+2] ; x2+=zkey[2] is in di
add bx, [si+4] ; x3+=zkey[4] is in bx
mul [Word si] ;x1 *= zkey[0]
sub ax, dx
jz @@x1 ; if 0, use special case multiply
adc ax, 0
@@x1out:
mov [sx1], ax ; x1 is in ax and sx1
xor ax, bx ; ax= x1^x3
mul [Word si+8] ; compute kk
sub ax, dx ; if 0, use special case multiply
jz @@kk
adc ax, 0
@@kkout:
mov cx, ax ; kk is in cx
mov ax, [sx4] ; x4 *= zkey[6]
mul [Word si+6]
sub ax, dx
jz @@x4 ; if 0, use special case multiply
adc ax, 0
@@x4out:
mov [sx4], ax ; x4 is in sx4 and ax
xor ax, di ; x4^x2
add ax, cx ; kk+(x2^x4)
mul [Word si+10] ; compute t1
sub ax, dx
jz @@t1 ; if 0, use special case multiply
adc ax, 0
@@t1out: ; t1 is in ax
add cx, ax ; t2 is in cx kk+t1
xor [sx4], cx ; x4 in sx4
xor di, cx ; new x3 in di
xor bx, ax ; new x2 in bx
xchg bx, di ; x2 in di, x3 in bx
xor ax, [sx1] ; x1 in ax
mov [sx1], ax ; and [sx1]
add si, 12 ; point to next subkey
cmp si, [done8]
jne @@loop
jmp @@out8
;------------------------------------------
; Special case multiplications, when one factor is 0
@@x1: mov ax, 1
sub ax, [sx1]
sub ax, [Word si]
jmp @@x1out
@@kk: mov ax, [sx1] ; rebuild overwritten operand
xor ax, bx
neg ax
inc ax
sub ax, [si+8]
jmp @@kkout
@@x4: mov ax, 1
sub ax, [sx4]
sub ax, [Word si+6]
jmp @@x4out
@@t1: mov ax, [sx4] ; rebuild
xor ax, di
add ax, cx
neg ax
inc ax
sub ax, [si+10]
jmp @@t1out
;---------------------------------------------------
; 8 rounds are done, now that extra pseudo-round
@@out8:
push di
mov di, [outblock]
mul [Word si]
sub ax, dx
jnz @@o1n ; jump over special case code
mov ax, 1
sub ax, [sx1]
sub ax, [si]
jmp @@o1out
@@o1n: adc ax, 0
@@o1out: mov [di], ax ; final ciphertext block 1
mov ax, [sx4]
mul [Word si+6]
sub ax, dx
jnz @@o4n ; jump over special case code
mov ax, 1
sub ax, [sx4]
sub ax, [si+6]
jmp @@o4out
@@o4n: adc ax, 0
@@o4out: mov [di+6], ax ; final ciphertext block 4
add bx, [si+2]
mov [di+2], bx ; final ciphertext block 2
pop ax
add ax, [si+4]
mov [di+4], ax ; final ciphertext block 3
; Restore the stack and return
pop di
pop si
; pop dx
; pop cx
; pop bx
; pop ax
mov sp, bp
pop bp
ret
ENDP _asmcrypt