TEA Source Code

Here is source code for the Tiny Encryption Algorithm in a variety of forms:

ANSI C
Motorola PowerPC
Motorola 680x0
New Variant (in ANSI C)
New Variant (in 16-bit x86 assembly language)

Please feel free to use any of this code in your applications. The TEA algorithm (including new-variant TEA) has been placed in the public domain, as have my assembly language implementations.


ANSI C

void encipher(unsigned long *const v,unsigned long *const w,
   const unsigned long *const k)
{
   register unsigned long       y=v[0],z=v[1],sum=0,delta=0x9E3779B9,
				a=k[0],b=k[1],c=k[2],d=k[3],n=32;

   while(n-->0)
      {
      sum += delta;
      y += (z << 4)+a ^ z+sum ^ (z >> 5)+b;
      z += (y << 4)+c ^ y+sum ^ (y >> 5)+d;
      }

   w[0]=y; w[1]=z;
}

void decipher(unsigned long *const v,unsigned long *const w,
   const unsigned long *const k)
{
   register unsigned long       y=v[0],z=v[1],sum=0xC6EF3720,
				delta=0x9E3779B9,a=k[0],b=k[1],
				c=k[2],d=k[3],n=32;

   /* sum = delta<<5, in general sum = delta * n */

   while(n-->0)
      {
      z -= (y << 4)+c ^ y+sum ^ (y >> 5)+d;
      y -= (z << 4)+a ^ z+sum ^ (z >> 5)+b;
      sum -= delta;
      }
   
   w[0]=y; w[1]=z;
}


Motorola PowerPC (Metrowerks CodeWarrior Style)

asm void encipher(register const unsigned long * const v,
   register unsigned long * const w,
   register const unsigned long * const k)
{
      // On entry, v = r3, w = r4, k = r5
      // use r3 and r5 as scratch
      
      // r0 = v[0]
      // r6 = v[1]
      // r7 - r10 = k[0] - k[3]
      // r11 = sum
      // r12 = delta
      
      li    r11,0 // sum = 0
      
      li    r12,0x79B9  // delta =0x9E3779B9
      addis r12,r12,0x9E37
            
      li    r0,16 // loop counter into count register
      mtctr r0
      
      lwz   r0,0(r3) // put the contents of v and k into the registers
      lwz   r6,4(r3)
      lwz   r7,0(r5)
      lwz   r8,4(r5)
      lwz   r9,8(r5)
      lwz   r10,12(r5)

loop: add   r11,r11,r12 // sum += delta
      slwi  r5,r6,4     // z << 4
      add   r5,r5,r7    // (z << 4) + a
      add   r3,r6,r11   // z + sum
      xor   r3,r5,r3    // ((z << 4) + a) ^ (z + sum)
      srwi  r5,r6,5     // z >> 5
      add   r5,r5,r8    // (z >> 5) + b
      xor   r3,r5,r3    // ((z << 4)+a)^(z+sum)^((z >> 5)+b);
      add   r0,r0,r3    // y += result
      
      slwi  r5,r0,4     // y << 4
      add   r5,r5,r9    // (y << 4) +c
      add   r3,r0,r11   // y + sum
      xor   r3,r5,r3    // ((y << 4) +c) ^ (y + sum)
      srwi  r5,r0,5     // y >> 5
      add   r5,r5,r10   // (y >> 5) + d
      xor   r3,r5,r3    // ((y << 4)+c)^(y+sum)^((y >> 5)+d);
      add   r6,r6,r3    // z += result
      
      bdnz+ loop        // decrement CTR and branch
      
      stw   r0,0(r4)    // store result back in w
      stw   r6,4(r4)
      
      blr
}
   
asm void decipher(register const unsigned long * const v,
   register unsigned long * const w,
   register const unsigned long * const k)
{
      // On entry, v = r3, w = r4, k = r5
      // use r3 and r5 as scratch
      
      // r0 = v[0]
      // r6 = v[1]
      // r7 - r10 = k[0] - k[3]
      // r11 = sum
      // r12 = delta
      
      li    r11,0x9B90;       // sum =0xE3779B90
      addis r11,r11,0xE378;
      
      li    r12,0x79B9  // delta =0x9E3779B9
      addis r12,r12,0x9E37
      
      li    r0,16 // loop counter into count register
      mtctr r0
      
      lwz   r0,0(r3) // put the contents of v and k into the registers
      lwz   r6,4(r3)
      lwz   r7,0(r5)
      lwz   r8,4(r5)
      lwz   r9,8(r5)
      lwz   r10,12(r5)
      
loop: slwi  r5,r0,4     // y << 4
      add   r5,r5,r9    // (y << 4) + c
      add   r3,r0,r11   // y + sum
      xor   r3,r5,r3    // ((y << 4) +  c) ^ (y + sum)
      srwi  r5,r0,5     // y >> 5
      add   r5,r5,r10   // (y >> 5) + d
      xor   r3,r5,r3    // ((y << 4)+c)^(y+sum)^((y >> 5)+d)
      sub   r6,r6,r3    // z -= result
      
      slwi  r5,r6,4     // z << 4
      add   r5,r5,r7    // (z << 4) + a
      add   r3,r6,r11   // z + sum
      xor   r3,r5,r3    // ((z << 4) + a) ^ (z + sum)
      srwi  r5,r6,5     // z >> 5
      add   r5,r5,r8    // (z >> 5) + b
      xor   r3,r5,r3    // ((z << 4)+a)^(z+sum)^((z >> 5)+b);
      sub   r0,r0,r3    // y -= result
      
      sub   r11,r11,r12 // sum -= delta
      
      bdnz+ loop        // decrement CTR and branch
      
      stw   r0,0(r4)    // store result back in w
      stw   r6,4(r4)

      blr
}


Motorola 680x0 (Metrowerks CodeWarrior style):


asm void encipher(const unsigned long* const v,unsigned long* const w,
   const unsigned long* const k)
{
      fralloc 
      movem.l  d3-d7/a2,-(a7)
   
      /* load initial registers
         d0:   y = v[0]
         d1:   z = v[1]
         d2:   a = k[0]
         d3:   b = k[1]
         d4:   c = k[2]
         d5:   loop counter (k[3] in a2)
         d6:   scratch register 1
         d7:   scratch register 2
         a0:   sum
         a1:   delta = 0x9E3779B9;
         a2:   d = k[3] */
               
      move.l   v,a0
      move.l   (a0),d0
      move.l   4(a0),d1
      
      move.l   k,a0
      move.l   (a0),d2
      move.l   4(a0),d3
      move.l   8(a0),d4
      move.l   12(a0),a2
      
      move.l   #0x9E3779B9,a0
      move.l   #0x9E3779B9,a1
      
      moveq.l  #15,d5      // sixteen rounds
      
      // d6 = (z<<4)+a
loop: move.l   d1,d6
      lsl.l    #4,d6
      add.l    d2,d6
      
      // d7 = z+sum
      move.l   d1,d7
      add.l    a0,d7
      
      // d7 = ((z<<4)+a)^(z+sum)
      eor.l    d6,d7
      
      // d6 = (z>>5)+b
      move.l   d1,d6
      lsr.l    #5,d6
      add.l    d3,d6
      
      // d7 = ((z<<4)+a)^(z+sum)^((z>>5)+b)
      eor.l    d6,d7
      
      // add back into y
      add.l    d7,d0
      
      // d6 = (y<<4)+c 
      move.l   d0,d6
      lsl.l    #4,d6
      add.l    d4,d6
      
      // d7 = y+sum
      move.l   d0,d7
      add.l    a0,d7
      
      // d7 = ((y<<4)+c)^(y+sum)
      eor.l    d6,d7
      
      // d6 = (y>>5)+d
      move.l   d0,d6
      lsr.l    #5,d6
      add.l    a2,d6
      
      // d7 = ((y<<4)+c)^(y+sum)^((y>>5)+d)
      eor.l    d6,d7
      
      // add back into z
      add.l    d7,d1
      
      // sum+=delta
      adda.l   a1,a0
               
      // branch back and do it again
      dbra     d5,loop
      
      // place the result back into w
      move.l   w,a0
      move.l   d0,(a0)
      move.l   d1,4(a0)
      
      movem.l  (a7)+,d3-d7/a2
      
      frfree
      rts
}

asm void decipher(const unsigned long *const v,unsigned long *const w,
   const unsigned long *const k)
{
   fralloc
   movem.l  d3-d7/a2,-(a7)
   
     /* load initial registers
         d0:   y = v[0]
         d1:   z = v[1]
         d2:   a = k[0]
         d3:   b = k[1]
         d4:   c = k[2]
         d5:   loop counter (k[3] in a2)
         d6:   scratch register 1
         d7:   scratch register 2
         a0:   sum = 0xE3779B90 (delta * 16)
         a1:   delta = 0x9E3779B9;
         a2:   d = k[3] */
      
      move.l   v,a0
      move.l   (a0),d0
      move.l   4(a0),d1
      
      move.l   k,a0
      move.l   (a0),d2
      move.l   4(a0),d3
      move.l   8(a0),d4
      move.l   12(a0),a2
      
      move.l   #0xE3779B90,a0
      move.l   #0x9E3779B9,a1
      
      moveq.l  #15,d5      // sixteen rounds
      
      // d6 = (y<<4)+c 
loop: move.l   d0,d6
      lsl.l    #4,d6
      add.l    d4,d6
      
      // d7 = y+sum
      move.l   d0,d7
      add.l    a0,d7
      
      // d7 = ((y<<4)+c)^(y+sum)
      eor.l    d6,d7
      
      // d6 = (y>>5)+d
      move.l   d0,d6
      lsr.l    #5,d6
      add.l    a2,d6
      
      // d7 = ((y<<4)+c)^(y+sum)^((y>>5)+d)
      eor.l    d6,d7
      
      // subtract from z
      sub.l    d7,d1
      
      // d6 = (z<<4)+a
      move.l   d1,d6
      lsl.l    #4,d6
      add.l    d2,d6
      
      // d7 = z+sum
      move.l   d1,d7
      add.l    a0,d7
      
      // d7 = ((z<<4)+a)^(z+sum)
      eor.l    d6,d7
      
      // d6 = (z>>5)+b
      move.l   d1,d6
      lsr.l    #5,d6
      add.l    d3,d6
      
      // d7 = ((z<<4)+a)^(z+sum)^((z>>5)+b)
      eor.l    d6,d7
      
      // subtract from y
      sub.l    d7,d0
      
      // sum-=delta
      suba.l   a1,a0
      
      // branch back and do it again
      dbra     d5,loop
      
      // place the result back into w
      move.l   w,a0
      move.l   d0,(a0)
      move.l   d1,4(a0)
      
      movem.l  (a7)+,d3-d7/a2
      
      frfree
      rts
}


ANSI C (New Variant)

void encipher(const unsigned long *const v,unsigned long *const w,
   const unsigned long * const k)
{
   register unsigned long       y=v[0],z=v[1],sum=0,delta=0x9E3779B9,n=32;

   while(n-->0)
      {
      y += (z << 4 ^ z >> 5) + z ^ sum + k[sum&3];
      sum += delta;
      z += (y << 4 ^ y >> 5) + y ^ sum + k[sum>>11 & 3];
      }

   w[0]=y; w[1]=z;
}

void decipher(const unsigned long *const v,unsigned long *const w,
   const unsigned long * const k)
{
   register unsigned long       y=v[0],z=v[1],sum=0xC6EF3720,
				delta=0x9E3779B9,n=32;

   /* sum = delta<<5, in general sum = delta * n */

   while(n-->0)
      {
      z -= (y << 4 ^ y >> 5) + y ^ sum + k[sum>>11 & 3];
      sum -= delta;
      y -= (z << 4 ^ z >> 5) + z ^ sum + k[sum&3];
      }
   
   w[0]=y; w[1]=z;
}


16-bit x86 (New Variant)

Many thanks to Rafael R. Sevilla for contributing this version.



	;; 
	;; An implementation of the XTEA algorithm in 16-bit 80x86 assembly
	;; language.  This should work on any processor in the 80x86 family
	;; but works best with the 16-bit members of the family (80x86 for
	;; x <= 2).  This assembly language is suitable for use with linux-86
	;; and the as86 assembler, but should be fairly trivial to convert
	;; so it will assemble with some other assembler easily.  It has been
	;; tested with 16-bit objects for Linux-8086 (ELKS), and should work
	;; under DOS with the tiny and small memory models.  To make it
	;; work with the large and huge memory models, it will probably be
	;; necessary to reset the parameters (bp+4 becomes bp+6 and so on),
	;; and segment loads may need to be done as well (les and lds).
	;;
	;; Wasn't so easy to write because the number of registers available
	;; on the 80x86 is kinda small...and they're 16-bit registers too!
	;;
	;; Placed in the Public Domain by Rafael R. Sevilla
	;;				  
	;; 

	.text

	export _xtea_encipher_asm
_xtea_encipher_asm:
	push	bp
	mov	bp,sp
	sub	sp,#14		; space for y, z, sum, and n
	push	si
	push	di
	;; bp+8 = pointer to key information
	;; bp+6 = pointer to ciphertext to return to caller
	;; bp+4 = pointer to plaintext
	;; bp+2 = return address from caller
	;; bp = pushed bp
	;; bp-2 = y high word
	;; bp-4 = y low word
	;; bp-6 = z high word
	;; bp-8 = z low word
	;; bp-10 = sum high word
	;; bp-12 = sum low word
	;; bp-14 = n
	;; bp-16 = pushed si
	;; bp-18 = pushed di
	mov	bx,[bp+4]	; get address of plaintext
	mov	ax,[bx]		; low word of first dword of plaintext
	mov	[bp-4],ax
	mov	ax,[bx+2]	; high word
	mov	[bp-2],ax
	mov	ax,[bx+4]	; second dword of plaintext (low)
	mov	[bp-8],ax
	mov	ax,[bx+6]	; (high)
	mov	[bp-6],ax
	xor	ax,ax		; zero the sum initially
	mov	[bp-10],ax
	mov	[bp-12],ax
	mov	byte ptr [bp-14],#32 ; set n (just 8 bits), # rounds
	;; begin encryption
encipher_rounds:
	;; compute new y
	mov	ax,[bp-8]	; low word z
	mov	bx,[bp-6]	; high word z
	mov	cx,ax		; copy to the rest of the registers
	mov	dx,bx
	mov	si,ax
	mov	di,bx
	;; (z<<4) ^ (z>>5)
	shl	ax,#1		; shift left once
	rcl	bx,#1
	shl	ax,#1		; shift twice
	rcl	bx,#1
	shl	ax,#1		; shift three times
	rcl	bx,#1
	shl	ax,#1		; shift four times
	rcl	bx,#1
	shr	dx,#1		; shift right once
	rcr	cx,#1
	shr	dx,#1		; shift right twice
	rcr	cx,#1
	shr	dx,#1		; shift right three times
	rcr	cx,#1
	shr	dx,#1		; shift right four times
	rcr	cx,#1
	shr	dx,#1		; shift right five times
	rcr	cx,#1
	xor	ax,cx		; combine
	xor	dx,bx		; dx:ax has result
	xor	si,[bp-12]	; combine to sum
	xor	di,[bp-10]
	add	ax,si		; add them together
	adc	dx,di
	mov	bx,[bp-12]	; get low word of sum (all we need for this)
	and	bx,#3		; get low two bits (modulo 4)
	shl	bx,#1		; convert to dword offset
	shl	bx,#1
	add	bx,[bp+8]	; add to base address of key info
	add	ax,[bx]		; low word of key
	adc	dx,[bx+2]	; high word of key
	add	[bp-4],ax	; add back to y
	adc	[bp-2],dx
	;; update sum
	mov	ax,#0x79b9	; low word of delta
	add	[bp-12],ax
	mov	ax,#0x9e37	; high word of delta
	adc	[bp-10],ax
	;; compute new z
	mov	ax,[bp-4]	; low word of y
	mov	bx,[bp-2]	; high word of y
	mov	cx,ax		; copy to the rest of the registers
	mov	dx,bx
	mov	si,ax
	mov	di,bx
	;; (y<<4) ^ (y>>5)
	shl	ax,#1		; shift left once
	rcl	bx,#1
	shl	ax,#1		; shift twice
	rcl	bx,#1
	shl	ax,#1		; shift three times
	rcl	bx,#1
	shl	ax,#1		; shift four times
	rcl	bx,#1
	shr	dx,#1		; shift right once
	rcr	cx,#1
	shr	dx,#1		; shift right twice
	rcr	cx,#1
	shr	dx,#1		; shift right three times
	rcr	cx,#1
	shr	dx,#1		; shift right four times
	rcr	cx,#1
	shr	dx,#1		; shift right five times
	rcr	cx,#1
	xor	ax,cx		; combine
	xor	dx,bx		; dx:ax has result
	xor	si,[bp-12]	; combine to sum
	xor	di,[bp-10]
	add	ax,si		; add them together
	adc	dx,di
	mov	bx,[bp-12]	; get sum low word
	mov	cx,[bp-10]	; get sum high word
	shr	cx,#1		; shift right once
	rcr	bx,#1
	shr	cx,#1		; shift right twice
	rcr	bx,#1
	shr	cx,#1		; shift right three times
	rcr	bx,#1
	shr	cx,#1		; shift right four times
	rcr	bx,#1
	shr	cx,#1		; shift right five times
	rcr	bx,#1
	shr	cx,#1		; shift right six times
	rcr	bx,#1
	shr	cx,#1		; shift right seven times
	rcr	bx,#1
	shr	cx,#1		; shift right eight times
	rcr	bx,#1
	shr	cx,#1		; shift right nine times
	rcr	bx,#1
	shr	cx,#1		; shift right ten times
	rcr	bx,#1
	shr	cx,#1		; shift right eleven times
	rcr	bx,#1
	and	bx,#3
	shl	bx,#1		; convert to dword offset
	shl	bx,#1
	add	bx,[bp+8]	; add to base address of key
	add	ax,[bx]		; low word of key
	adc	dx,[bx+2]	; high word of key
	add	[bp-8],ax	; add back to z
	adc	[bp-6],dx
	dec	byte ptr [bp-14] ; decrement rounds counter
	jz	finish_encipher
	jmp	near encipher_rounds
finish_encipher:	
	mov	bx,[bp+6]	; get address of ciphertext storage
	mov	ax,[bp-4]	; y, low word
	mov	[bx],ax
	mov	ax,[bp-2]	; y, high word
	mov	[bx+2],ax
	mov	ax,[bp-8]	; z, low word
	mov	[bx+4],ax
	mov	ax,[bp-6]	; z, high word
	mov	[bx+6],ax
	pop	di
	pop	si
	add	sp,#14		; discard local vars
	pop	bp
	ret

	export _xtea_decipher_asm
_xtea_decipher_asm:
	push	bp
	mov	bp,sp
	sub	sp,#14		; space for y, z, sum, and n
	push	si
	push	di
	;; bp+8 = pointer to key information
	;; bp+6 = pointer to plaintext to return to caller
	;; bp+4 = pointer to ciphertext
	;; bp+2 = return address from caller
	;; bp = pushed bp
	;; bp-2 = y high word
	;; bp-4 = y low word
	;; bp-6 = z high word
	;; bp-8 = z low word
	;; bp-10 = sum high word
	;; bp-12 = sum low word
	;; bp-14 = n
	;; bp-16 = pushed si
	;; bp-18 = pushed di
	mov	bx,[bp+4]	; get address of ciphertext
	mov	ax,[bx]		; low word of first dword of ciphertext
	mov	[bp-4],ax
	mov	ax,[bx+2]	; high word
	mov	[bp-2],ax
	mov	ax,[bx+4]	; second dword of ciphertext (low)
	mov	[bp-8],ax
	mov	ax,[bx+6]	; (high)
	mov	[bp-6],ax
	mov	ax,#0x3720	; low word of initial sum
	mov	[bp-12],ax
	mov	ax,#0xc6ef
	mov	[bp-10],ax
	mov	byte ptr [bp-14],#32 ; set n (just 8 bits), # rounds
	;; begin decryption
decipher_rounds:	
	mov	ax,[bp-4]	; low word of y
	mov	bx,[bp-2]	; high word of y
	mov	cx,ax		; copy to the rest of the registers
	mov	dx,bx
	mov	si,ax
	mov	di,bx
	;; (y<<4) ^ (y>>5)
	shl	ax,#1		; shift left once
	rcl	bx,#1
	shl	ax,#1		; shift twice
	rcl	bx,#1
	shl	ax,#1		; shift three times
	rcl	bx,#1
	shl	ax,#1		; shift four times
	rcl	bx,#1
	shr	dx,#1		; shift right once
	rcr	cx,#1
	shr	dx,#1		; shift right twice
	rcr	cx,#1
	shr	dx,#1		; shift right three times
	rcr	cx,#1
	shr	dx,#1		; shift right four times
	rcr	cx,#1
	shr	dx,#1		; shift right five times
	rcr	cx,#1
	xor	ax,cx		; combine
	xor	dx,bx		; dx:ax has result
	xor	si,[bp-12]	; combine to sum
	xor	di,[bp-10]
	add	ax,si		; add them together
	adc	dx,di
	mov	bx,[bp-12]	; get sum low word
	mov	cx,[bp-10]	; get sum high word
	shr	cx,#1		; shift right once
	rcr	bx,#1
	shr	cx,#1		; shift right twice
	rcr	bx,#1
	shr	cx,#1		; shift right three times
	rcr	bx,#1
	shr	cx,#1		; shift right four times
	rcr	bx,#1
	shr	cx,#1		; shift right five times
	rcr	bx,#1
	shr	cx,#1		; shift right six times
	rcr	bx,#1
	shr	cx,#1		; shift right seven times
	rcr	bx,#1
	shr	cx,#1		; shift right eight times
	rcr	bx,#1
	shr	cx,#1		; shift right nine times
	rcr	bx,#1
	shr	cx,#1		; shift right ten times
	rcr	bx,#1
	shr	cx,#1		; shift right eleven times
	rcr	bx,#1
	and	bx,#3
	shl	bx,#1		; convert to dword offset
	shl	bx,#1
	add	bx,[bp+8]	; add to base address of key
	add	ax,[bx]		; low word of key
	adc	dx,[bx+2]	; high word of key
	sub	[bp-8],ax	; subtract from z
	sbb	[bp-6],dx
	;; update sum
	mov	ax,#0x79b9	; low word of delta
	sub	[bp-12],ax
	mov	ax,#0x9e37	; high word of delta
	sbb	[bp-10],ax
	;; compute new y
	mov	ax,[bp-8]	; low word z
	mov	bx,[bp-6]	; high word z
	mov	cx,ax		; copy to the rest of the registers
	mov	dx,bx
	mov	si,ax
	mov	di,bx
	;; (z<<4) ^ (z>>5)
	shl	ax,#1		; shift left once
	rcl	bx,#1
	shl	ax,#1		; shift twice
	rcl	bx,#1
	shl	ax,#1		; shift three times
	rcl	bx,#1
	shl	ax,#1		; shift four times
	rcl	bx,#1
	shr	dx,#1		; shift right once
	rcr	cx,#1
	shr	dx,#1		; shift right twice
	rcr	cx,#1
	shr	dx,#1		; shift right three times
	rcr	cx,#1
	shr	dx,#1		; shift right four times
	rcr	cx,#1
	shr	dx,#1		; shift right five times
	rcr	cx,#1
	xor	ax,cx		; combine
	xor	dx,bx		; dx:ax has result
	xor	si,[bp-12]	; combine to sum
	xor	di,[bp-10]
	add	ax,si		; add them together
	adc	dx,di
	mov	bx,[bp-12]	; get low word of sum (all we need for this)
	and	bx,#3		; get low two bits (modulo 4)
	shl	bx,#1		; convert to dword offset
	shl	bx,#1
	add	bx,[bp+8]	; add to base address of key info
	add	ax,[bx]		; low word of key
	adc	dx,[bx+2]	; high word of key
	sub	[bp-4],ax	; subtract from y
	sbb	[bp-2],dx
	dec	byte ptr [bp-14] ; decrement rounds counter
	jz	finish_decipher
	jmp	near decipher_rounds
finish_decipher:	
	mov	bx,[bp+6]	; get address of ciphertext storage
	mov	ax,[bp-4]	; y, low word
	mov	[bx],ax
	mov	ax,[bp-2]	; y, high word
	mov	[bx+2],ax
	mov	ax,[bp-8]	; z, low word
	mov	[bx+4],ax
	mov	ax,[bp-6]	; z, high word
	mov	[bx+6],ax
	pop	di
	pop	si
	add	sp,#14		; discard local vars
	pop	bp
	ret


Return to TEA page