.radix 16 ; RIJNDAEL cipher program (DOS) using ; CFB block chaining, supporting 128, ; 192, and 256-bit blocks and keys and ; UC file name (repeated) as IV ; This program incorporates a Davies- ; Meyer type key hash with 00 as IV ; The input key length is 2*blockbytes, ; which more closely matches entropy ; of user input to that of cipher ; This version can be assembled on the ; A86 shareware assembler, available ; from www.eji.com and runs on any '86 ; Copyright April 12, 2000 by ; Robert G. Durnal (afn21533@afn.org) ; Released for free use by anyone! ; Source posted under 'Unrestricted ; Source Code' license exception to ; EAR regulations ; Block size in bits = 32 x NCOLS ; Key size in bits = 32 x NKEYS NCOLS equ 8 ; May be 4, 6, or 8 NKEYS equ 8 ; May be 4, 6, or 8 KEYBYTES equ NKEYS*4 BLOCKBYTES equ NCOLS*4 READSIZE equ 0C000 DATABUF equ 1084 ; Value saves 1 byte PWR_TAB equ 0E00 LOG_TAB equ 0F00 SBOX equ LOG_TAB CFBBUF equ PWR_TAB #if NCOLS GT NKEYS ROUNDS equ 6+NCOLS #else ROUNDS equ 6+NKEYS #endif ; Routines for RIJNDAEL S-box creation ; derived from formulas in Dr. Brian ; Gladman's RIJNDAEL implementation ; First prepare Pwr and Log tables. ; ; for (i=0; p=1; i<256; ++i) ; { ; pwr_tab[i]=p; log_tab[p]=i; ; p=p^(p<<1)^(p&0x80?0x01b:0) ; } MAKETABLES: mov di,pwr_tab mov bp,di ; Save pointer to CFBBUF mov bh,log_tab/100 mov cx,100 inc ax ; DOS starts AX at 0000 MAKETABLOOP: mov bl,al xchg ax,di mov [bx],al ; Store Log value xchg ax,di stosb ; And Pwr value mov ah,al ; Compute next Pwr call xtime ; (p<<1)^(p&0x80?0x1b:0) xor al,ah ; p^(p<<1)^(p&0x80?0x1b:0) loop maketabloop ; Now use those tables to create S-box ; OK to overwrite Log table. ; ; for (i=0; i<256; ++i) ; { ; p=(i?(pwr_tab[255-log_tab[i]]):0); ; q=p; ; q=(q>>7) | (q<<1); p^=q; ; q=(q>>7) | (q<<1); p^=q; ; q=(q>>7) | (q<<1); p^=q; ; q=(q>>7) | (q<<1); p^=q^0x63; ; sbx_tab[i]=p; ; } MAKESBOX: ; Starts with CX = 0 mov bx,bp ; --> PWR_TAB for XLAT db 0D6 ; Clears AL to start SBOXLOOP: ; AL = p, AH = q mov ah,al ; q=p mov ch,4 ; CX always 04xx here MAKES1: ; C formula for ROL q,1 rol ah,1 ; q=(q>>7) | (q<<1) xor al,ah ; p ^= q dec ch jnz makes1 ; 4 times xor al,63 ; Here CH always 00 stosb ; Store computed value mov al,[di] ; Get next log_tab[i] not al ; 255-log_tab[i] xlat ; pwr_tab[255-log_tab[i]] loop sboxloop CHECKCMD: ; Check cmd line syntax mov si,80 ; Default DTA location lodsw cmp al,4 ; Check for parameters jc exit1 lodsw sub al,'+' ; Test for encrypt (+) jz getkey add b[direction],al sub al,2 ; Test for decrypt (-) jnz exit1 GETKEY: push si ; SI --> filename in DTA mov dx,askforkey mov ah,9 ; Solicit via STDOUT int 21 mov dl,keybuf mod 100 mov ah,0A int 21 ; Get key via STDIN mov ax,3 int 10 ; Clear key from screen xchg si,dx ; SI --> KEYBUF DX = 0084 lodsb lodsb ; SI --> KEYARRAY xchg ax,di ; DI = Length of key add di,si ; DI = CR in KEYARRAY mov cl,2*keybytes rep movsb ; Move more than enough mov di,bp ; CFBBUF mov cl,2*keybytes PUSHKEY: ; Push all key bytes dec si ; Push in reverse order mov al,[si] push ax ; Only AL will be used db 0D6 ; Zero AL by SALC stosb ; Clear CFBBUF this loop loop pushkey ; Sets 00 IV for hash HASHKEY: ; Hash the input key mov cl,blockbytes mov di,bp POPKEY: ; Pop key mat'l to CFBBUF pop ax stosb push bx ; BL always 00 here pop bx ; Overwrite key in stack cmp sp,0FFFC ; Restrict POPs ó PUSHs loopnz popkey EXPANDKEY: call keysched ; Make key and encrypt mov di,bp mov cl,blockbytes/2 mov si,keyarray KEYCRYPT: ; Mix with original key mov ax,[di] xor [si],ax movsw ; Dup in CFBBUF = new IV cmp si,keyarray+keybytes ; End of key? loopnz keycrypt cmp sp,0FFFC jnc endhash ; No more PUSHed data inc cx loop keycrypt-3 ; ð JCXNZ jmp hashkey EXIT1: ; Bad cmd line show usage mov si,offset usage EXIT: ; Print error messages via INT29 lodsb int 29 or al,al jnz exit ret ; INT20 exit via PSP 00 ENDHASH: call keysched ; Final key schedule pop dx mov si,dx ; DX --> fname in DTA mov di,bp mov cl,blockbytes PARSEFNAME: ; Set into CFBBUF as IV lodsb and al,5F ; Quasi U/C, OK for IV stosb cmp b[si],20 ja parsefname mov [si],ch ; Make ASCIIZ in DTA mov si,bp ; Finish filling CFBBUF rep movsb ; More than enough bytes OPENFILE: ; Open the data file mov ax,3D02 ; Read/write mode int 21 ; Open data file xchg ax,bx ; Handle mov dh,databuf/100 REREAD: ; Loop here until read = 0 bytes mov si,filerrmsg jc exit mov ch,readsize/100 mov ah,3F int 21 jc reread GOODREAD: ; Check on read length or ax,ax ; 0 length read = done jz endprog ; Any RET within ñ 80h push dx ; Databuf address mov si,dx push ax ; Bytes read push bx ; Handle BLOCK: ; Process block of data push ax ; # bytes left in DATABUF push si ; Databuf pointer call cipher ; Encrypt CFBBUF mov di,bp ; CFBbuf pop si ; Databuf pointer mov cl,blockbytes/2 DATACRYPT: ; XOR CFBBUF & DATA mov ax,[si] ; Old data value xchg [di],ax ; AX = CFB, [DI] = Data xor [si],ax ; [SI] = newdata DIRECTION: movsw ; CMPSW for decrypt loop datacrypt pop ax sub ax,blockbytes ja block PTRSET: ; Decrement file pointer pop bx ; Handle pop dx ; Bytes read push dx neg dx dec cx ; CX:DX=-(bytes read) mov ax,4201 int 21 ; Back file ptr by bytes read WRITEDATA: ; Write # bytes read pop cx ; Bytes read pop dx ; Databuf address mov ah,40 int 21 jmp reread ; Start of subroutines XTIME: ; Subroutine for multiplication ; in GF(2^8) modulo x^8+x^4+x^3+x+1 shl al,1 jnc endprog xor al,1B ENDPROG: ; INT 20 via PSP 00 ret KEYSCHED: ; Run key schedule on key ; RIJNDAEL key schedule expands first ; KEYBYTES of key into sufficient key ; material for ROUNDS+1 key mixing ; Rounds+2 avoids roundoff error mov di,keyarray+keybytes ; Fall thru RJ mov dl,1 ; Initialize RCON mov cl,(rounds+2)*ncols/nkeys KEYSCHEDLOOP: push cx ; On entry CH = 0, saved mov bx,-4 ; Effective rotation of key cmp ch,3 ; material achieved by jnz $+4 ; index manipulation rcl bl,1 adc bl,0 ; BX = -3, -4, or -7 mov al,[di+bx] test ch,-(ncols/8*10+4) jnz keycont ; TEST with 0EC or 0FC KEYSUB: mov bx,sbox ; Bytesub xlat or ch,ch jnz keycont xor al,dl ; CH = 0, mix with RCON xchg ax,dx call xtime ; Update RCON xchg ax,dx KEYCONT: ; Final step for all keys xor al,[di-keybytes] stosb inc ch cmp ch,keybytes jb keyschedloop+1 pop cx loop keyschedloop CIPHER: ; The RIJNDAEL encryption cipher ; The 'state' of the cipher is located ; in CFBBUF and is treated as a byte ; matrix of 4 rows by NCOLS columns ; for purpuses of SHIFTROW and ; MIXCOLUMN routines. BP is pointer ; to CFBBUF throughout, SI is pointer ; to next byte of key material. ; This routine automatically follows ; the KEYSCHED routine, or is called ; directly from the BLOCK sequence. mov si,keyarray mov cx,rounds ; CH non-zero after read push cx RIJNLOOP: ; Encrypt CFBBUF mov di,bp ; CFBBUF mov cl,blockbytes mov bx,sbox ADDKEYLOOP: lodsb ; Next key byte xor al,[di] xlat ; Do the BYTESUB here stosb loop addkeyloop SHIFTROW: mov cl,3 ; CX is row # base 0 std ; Move right to left SR_OUTER: mov bx,cx #if ncols EQ 8 cmp bx,2 jc $+3 inc bx ; 1,2,3 --> 1,3,4 #endif mov di,cfbbuf+blockbytes-4 add di,cx ; Add row number SR_INNER: mov ch,ncols-1 push di mov al,[di] SHIFTER: ; Shift row 1 space <-- scasw scasw ; Move DI <-- 1 col xchg al,[di] dec ch jnz shifter pop di mov [di],al dec bx jnz sr_inner loop sr_outer ENDSR: cld pop ax ; Round counter dec ax jz lastaddkey ; Skip last MIXCOLUMN push ax ; Restack round counter MIXCOLUMN: ; Matrix mult in GF(2^8) mov di,bp ; CFBBUF mov cl,ncols NEXTCOL: ; Multiplier matrix is mov ch,4 ; º 2 3 1 1 º mov dx,[di] ; º 1 2 3 1 º mov bx,[di+2] ; º 1 1 2 3 º COLUMNLOOP: ; º 3 1 1 2 º mov ax,dx call xtime ; 2x byte 0 xchg ah,al call xtime ; 2x byte 1 xor al,dh ; 3x byte 1 xor ax,bx ; Add in bytes 2 and 3 xor al,ah ; AL=matrix product byte stosb ; Store in original byte xchg dh,dl ; Proceed to next byte xchg bl,dh ; 3 XCHGs equivalent to xchg bh,bl ; ROL 8 on 32-bit reg dec ch jnz columnloop loop nextcol jmp rijnloop LASTADDKEY: mov cl,blockbytes/2 mov di,bp LASTKEYLOOP: lodsw xor [di],ax scasw loop lastkeyloop ret USAGE db 'TINYRIJN ñ file',0 ASKFORKEY: db 'Key:',0,24 FILERRMSG: db 'File error',0 KEYBUF: db 2*keybytes+1 KEYARRAY equ $+1 ; Leave space for count FILESIZE equ $-100