/*
 * cs.s - Linux i386 GNU Assembler implementation of CipherSaber-1
 * By Dave Maez <sellout@dharmadevil.com>
 * September 2003
 * Released into the public domain.
 * To Compile:
 *   as cs.s -o cs.o && ld cs.o -o cs && strip cs
 *
 * This code can be linked to in C.  The header file would look like:
 * struct ARC4cx {
 *   unsigned char state[256];
 *   unsigned char i,j;
 * };
 * extern void keyinit(struct ARC4cx *cx, unsigned char *key, int keylen);
 * extern void cryptbyte(struct ARC4cx *cx, unsigned char *cbyte);
 *
 * The keyinit() function  initializes the ARC4cx struct, and the cryptbyte()
 * funtion encrypts cbyte.
 */

.bss
        .lcomm  ARC4cx, 258
        .lcomm  inbuf, 1024
        .lcomm  inkey, 256

.data
prngfn:         .string "/dev/random"
useage1:        .ascii  "Usage: "
useage2:        .ascii  " [-d] key < infile > outfile", "\n"

.text
        .globl keyinit
        .globl cryptbyte
        .globl _start

/* extern void keyinit(ARC4cx *cx, unsigned char *key, int keylen); */
keyinit:
        pushl   %ebx
        pushl   %ecx
        pushl   %edx
        pushl   %ebp
        pushl   %esi
        pushl   %edi
        movl    28(%esp),%ebp   # Set EBP to the offset of the state array.
        xorb    %al,%al         # set AL to 0.
sloop:  movb    %al,(%ebp)      # begin loop, set state[al] to al
        incb    %al             # increment AL
        incl    %ebp            # increment state position.
        testb   %al,%al         # Test if AL is zero.
        jnz     sloop           # if not, loop again
        movw    $0,(%ebp)       # Set i and j to 0.
        xorl    %eax,%eax       # set EAX to 0.
        xorl    %ebx,%ebx       # set EBX to 0.
        movl    36(%esp),%ecx   # set ECX to the key length.
        cmpl    $0,%ecx         # Check if key length is zero.
        je      ki_end          # if it is, exit.
        movl    32(%esp),%edx   # set EDX to the offset of key[0].
        addl    %edx,%ecx       # set ECX to the end of the key array.
kloopM: movl    32(%esp),%esi   # set ESI to the offset of key[0].
kloop:  cmpl    %esi,%ecx       # see if we're at the end of the key,
        je      kloopM          # if so, then reset the key to the beginning.
        movb    (%esi),%dh      # set DH to the keybyte.
        movl    28(%esp),%ebp   # set EBP to the offset of state[0].
        movl    %ebp,%edi       # set EDI to " " ".
        addl    %eax,%ebp       # set EDI to offset of state[AL].
        movb    (%ebp),%dl      # set DL to state[AL].
        addb    %dl,%bl         # set BL to BL + DL.
        addb    %dh,%bl         # set BL to BL + DH.
        addl    %ebx,%edi       # set EDI to state[BL]
        movb    (%edi),%dh      # set DH to state[BL].
        movb    %dh,(%ebp)      # set state[AL] to DH.
        movb    %dl,(%edi)      # set setate[BL] to DL.
        incb    %al             # AL + 1
        incl    %esi            # key + 1
        testb   %al,%al         # test if AL = 0.
        jnz     kloop           # if not, loop again.
ki_end: popl    %edi
        popl    %esi
        popl    %ebp
        popl    %edx
        popl    %ecx
        popl    %ebx
        xorl    %eax,%eax
        ret


/* extern unsigned char cryptbyte(ARC4cx *cx, unsigned char *cbyte); */
cryptbyte:
        pushl   %ebx
        pushl   %ecx
        pushl   %edx
        pushl   %ebp
        pushl   %esi
        pushl   %edi
        xorl    %eax,%eax       # set EAX to 0.
        movl    32(%esp),%ecx   # set AL to the byte to be encrypted.
        movb    (%ecx),%al
        movl    28(%esp),%ebp   # set EBP to the offset of the state array.
        movl    %ebp,%esi
        addl    $256,%esi       # set ESI to the offset of i.
        movl    %esi,%edi
        incl    %edi            # set EDI to the offset of j.
        xorl    %ebx,%ebx       # set EBX to 0.
        xorl    %ecx,%ecx       # set ECX to 0.
        movb    (%esi),%bl      # set BL to i.
        incb    %bl             # increment i.
        movb    %bl,(%esi)      # update i.
        movb    (%edi),%cl      # set CL to j.
        movl    %ebp,%esi       # set ESI to the offset of the state array.
        addl    %ebx,%esi       # set ESI to the offset of state[i]
        movb    (%esi),%dl      # set DL to state[i]
        addb    %dl,%cl         # add state[i] to j.
        movb    %cl,(%edi)      # update j.
        movl    %ebp,%edi       # set EDI to the offest of the state array.
        addl    %ecx,%edi       # set EDI to the offset of state[j]
        movb    (%edi),%dh      # set DH to state[j]
        movb    %dh,(%esi)      # set state[i] to DH
        movb    %dl,(%edi)      # set state[j] to DL
        addb    %dh,%dl         # add DL and DL (state[i]+state[j])
        xorb    %dh,%dh         # zero out DH
        addl    %edx,%ebp       # set EBP to offset of state[s[i]+s[j]]
        movb    (%ebp),%dl      # set DL to state[s[i]+s[j]]
        xorb    %dl,%al         # XOR the result...
        movl    32(%esp),%ecx   # set AL to the byte to be encrypted.
        movb    %al,(%ecx)
        popl    %edi
        popl    %esi
        popl    %ebp
        popl    %edx
        popl    %ecx
        popl    %ebx
        ret

showuseage:
        movl    $4,%eax
        movl    $1,%ebx
        movl    $useage1,%ecx
        movl    $7,%edx
        int     $0x80
        popl    %ecx
        movl    %ecx,%ebx
        xorl    %edx,%edx
slen:   movb    (%ebx),%al
        incl    %edx
        incl    %ebx
        cmpb    $0,%al
        jne     slen
        movl    $4,%eax
        movl    $1,%ebx
        int     $0x80
        movl    $4,%eax
        movl    $useage2,%ecx
        movl    $29,%edx
        int     $0x80
        jmp     _end

/* The following code is just plain stupid.  Dealing with command line
 * arguments in Assmbler?!?!  No, that's idiotic.  This part should be done
 * in C and call the two ARC4 functions.  Oh well.  This was done to make
 * the CipherSaber-1 Linux GNU ASM implementation complete.
 */
_start:
        popl    %ecx            # Set ECX to ARGC.
        cmpl    $2,%ecx         # Check ARGC.
        jb      showuseage      # If ARGC < 2, showuseage.
        je      encodekey       # If ARGC = 2, encode.
        cmpl    $3,%ecx         # Check ARGC again.
        ja      showuseage      # If ARGC > 3, showuseage.
        je      decodekey       # If ARGC = 3, decode.
encodekey:
        popl    %ecx            # ARGV0
        popl    %ecx            # ARGV1 - the key.
        movl    $inkey,%ebp     # Set EBP to the keybuffer
        movl    %ebp,%esi       # Set ESI = EBP
        addl    $256,%esi       # Set ESI to end of keybuffer.
        xorl    %edi,%edi
ekloop: movb    (%ecx),%al      # AL = keybyte.
        movb    %al,(%ebp)      # Set keybuf to AL.
        incl    %ecx
        incl    %ebp
        incl    %edi
        cmpl    %esi,%ebp
        je      ekdone
        cmpb    $0,%al
        jne     ekloop
        decl    %ebp
        decl    %edi
ekdone: movl    $5,%eax         # Set EAX to system open(2) command.
        movl    $prngfn,%ebx    # Set EBX to /dev/random.
        xorl    %ecx,%ecx       # Set ECX = 0 (O_RDONLY).
        int     $0x80           # Linux Syscall.
        cmpl    $0,%eax         # Test EAX
        jbe     _end            # Finish if it couldn't open.
        movl    %eax,%ebx       # Set EBX to the FD.
        movl    $3,%eax         # Set EAX to system read(2) command.
        movl    $inbuf,%ecx     # Set ECX to the inbuf.
        movl    $10,%edx        # Set EDX to 10, the size of the IV.
        int     $0x80           # Linux Syscall.
        cmpl    $10,%eax        # Test EAX.
        jne     finish          # If we didn't get all 10 bytes, exit.
        movl    $6,%eax         # Set EAX to close(2) command.
        int     $0x80           # Linux Syscall.
        movl    $4,%eax         # Set EAX to write(2) command.
        movl    $1,%ebx         # Set EBX to 1 (stdout).
        int     $0x80           # Write the IV to stdout.
        movb    $10,%al
eivloop:
        cmpl    %ebp,%esi       # Test EBP == ESI.
        je      eivdone         # Finish if they equal.
        movb    (%ecx),%ah
        movb    %ah,(%ebp)
        incl    %ebp
        incl    %ecx
        incl    %edi
        decb    %al
        test    %al,%al
        jnz     eivloop
eivdone:
        pushl   %edi            # Push it to the stack.
        movl    $inkey,%ebp     # Get the Key offset.
        pushl   %ebp            # Push it to the stack.
        movl    $ARC4cx,%esi    # Get the ARC4cx.
        pushl   %esi            # Push it to the stack.
        call    keyinit         # Init the key.
        popl    %esi
        popl    %esi
        popl    %esi
        jmp     mloop

decodekey:
        popl    %ecx            # ARGV0
        popl    %ecx            # ARGV1
        popl    %ecx            # ARGV2 - the key.
        movl    $inkey,%ebp     # Set EBP to the keybuffer
        movl    %ebp,%esi       # Set ESI = EBP
        addl    $256,%esi       # Set ESI to end of keybuffer.
        xorl    %edi,%edi
dkloop: movb    (%ecx),%al      # AL = keybyte.
        movb    %al,(%ebp)      # Set keybuf to AL.
        incl    %ecx
        incl    %ebp
        incl    %edi
        cmpl    %esi,%ebp
        je      dkdone
        cmpb    $0,%al
        jne     dkloop
        decl    %ebp
        decl    %edi
dkdone: xorl    %ebx,%ebx       # Set EBX to 0 (stdin)
        movl    $3,%eax         # Set EAX to system read(2) command.
        movl    $inbuf,%ecx     # Set ECX to the inbuf.
        movl    $10,%edx        # Set EDX to 10, the size of the IV.
        int     $0x80           # Linux Syscall.
        cmpl    $10,%eax        # Test EAX.
        jne     finish          # If we didn't get all 10 bytes, exit.
divloop:
        cmpl    %ebp,%esi       # Test EBP == ESI.
        je      divdone         # Finish if they equal.
        movb    (%ecx),%ah
        movb    %ah,(%ebp)
        incl    %ebp
        incl    %ecx
        incl    %edi
        decb    %al
        test    %al,%al
        jnz     divloop
divdone:
        pushl   %edi            # Push it to the stack.
        movl    $inkey,%ebp     # Get the Key offset.
        pushl   %ebp            # Push it to the stack.
        movl    $ARC4cx,%esi    # Get the ARC4cx.
        pushl   %esi            # Push it to the stack.
        call    keyinit         # Init the key.
        popl    %esi
        popl    %esi
        popl    %esi
        jmp     mloop

mloop:                          # The main encryption loop.
        movl    $inbuf,%ecx     # Set ECX to the input buffer
        movl    $1024,%edx      # Set EDX to maxlen
        movl    $3,%eax         # Set EAX to system read(2) command
        xorl    %ebx,%ebx       # Set EBX to 0 (stdin)
        int     $0x80           # Linux Syscall.
        cmpl    $0,%eax         # Check the return value.
        jbe     finish          # If it's <= 0 then we're done.
        movl    %eax,%edx       # Set EDX to the # of bytes read.
        movl    %ecx,%ebx       # Set EBX to the end of the input buffer.
        addl    %edx,%ebx
        pushl   %ecx            # Push the location of the inbuf to the stack
        movl    $ARC4cx,%ebp
        pushl   %ebp            # Push the ARC4cx buffer to the stack.
iloop:
        movl    %ecx,4(%esp)    # Push the location of the inbuf to the stack.
        call    cryptbyte       # Call the Cryptbyte function.
        incl    %ecx            # Incrment the inbuf pointer.
        cmpl    %ecx,%ebx       # See if we're at the end of the input.
        jne     iloop           # if not, loop again.

        movl    $4,%eax         # Set EAX to system write(2) command.
        movl    $inbuf,%ecx     # Set ECX to the input buffer
        movl    $1,%ebx         # Set EBX to 1 (stdout)
        int     $0x80           # Linux Syscall
        popl    %esi            # Pop the inbuf and ARC4cx pointers.
        popl    %esi
        jmp     mloop           # Wash, rinse, repeat.

finish: movl    $ARC4cx,%edi
        movl    $258,%ecx
        call    clrmem
        movl    $inbuf,%edi
        movl    $1024,%ecx
        call    clrmem
        movl    $inkey,%edi
        movl    $256,%ecx
        call    clrmem
        jmp     _end

clrmem: movb    $0,(%edi)       # Clear the memory...
        incl    %edi
        decl    %ecx
        test    %ecx,%ecx
        jnz     clrmem
        ret

_end:   movl    $1,%eax         # Set EAX to system exit(2) command.
        xorl    %ebx,%ebx       # Return code of 0.
        int     $0x80           # Linux Syscall.
        ret