8
\$\begingroup\$

Introduction

A while back I stumbled across the tiny encryption algorithm (TEA) and since then I have recommended it whenever special cryptographic security properties were un-needed and a self-implementation was a requirement.
Now today, we want to take the name *tiny* encryption algorithm literally and your task will be to implement the tiniest encryption algorithm!

Specification

Input

The input is a list of 8 bytes (or your language equivalent), this is the plaintext, and another list of 16 bytes (or your language equivalent), this is the key.
Input conversion from decimal, hexadecimal, octal or binary is allowed.
Reading 1 64-bit, 2 32-bit or 4 16-bit integers as output (optionally as a list) is allowed (for the plaintext, double the numbers for the key)

Output

The output is a list of 8 bytes (or your language equivalent), this is the ciphertext.
Output conversion to decimal, hexadecimal, octal or binary is allowed, but not mandatory.
Writing 1 64-bit, 2 32-bit or 4 16-bit integers as output (optionally as a list) is allowed.

What to do?

Your task is to implement the encryption direction of the tiny encryption algorithm (TEA), note XTEA and XXTEA are other algorithms.
Wikipedia has a C-code example and a list of references to some implementations in other languages, this is the original description (PDF).

More Formally:

Let k1, k2, k3, k4, v1, v2, sum be unsigned 32-bit integers.
(k1,k2,k3,k4) <- key input
(v1,v2) <- plaintext input
sum <- 0
repeat 32 times:
    sum <- ( sum + 0x9e3779b9 ) mod 2^32
    v0  <- ( v0 + ( ((v1 << 4) + k0) XOR (v1 + sum) XOR ((v1 >> 5) + k1) ) ) mod 2^32
    v1  <- ( v1 + ( ((v0 << 4) + k2) XOR (v0 + sum) XOR ((v0 >> 5) + k3) ) ) mod 2^32
output <- (v0,v1)

where 0x9e3779b9 is a hexadecimal constant and
"<<" denotes logical left shift and ">>" denotes logical right shift.

Potential corner cases

\0 is a valid character and you may not shorten your input nor output.
Integer encoding is assumed to be little-endian (e.g. what you probably already have).

Who wins?

This is code-golf so the shortest answer in bytes wins!
Standard rules apply of course.

Test-cases

Test vectors were generated using the Wikipedia C code example.

Key: all zero (16x \0)
Plaintext -> Ciphertext (all values as two 32-bit hexadecimal words)
00000000 00000000 -> 41ea3a0a 94baa940
3778001e 2bf2226f -> 96269d3e 82680480
48da9c6a fbcbe120 -> 2cc31f2e 228ad143
9bf3ceb8 1e076ffd -> 4931fc15 22550a01
ac6dd615 9c593455 -> 392eabb4 505e0755
ebad4b59 7b962f3c -> b0dbe165 cfdba177
ca2d9099 a18d3188 -> d4641d84 a4bccce6
b495318a 23a1d131 -> 39f73ca0 bda2d96c
bd7ce8da b69267bf -> e80efb71 84336af3
235eaa32 c670cdcf -> 80e59ecd 6944f065
762f9c23 f767ea2c -> 3f370ca2 23def34c

Here are some self-generated test vectors with non-zero keys:

format: ( 4x 32-bit word key , 2x 32-bit word plaintext ) -> ( 2x 32-bit word ciphertext )
(all in hexadecimal)

( 4300e123 e39877ae 7c4d7a3c 98335923 , a9afc671 79dcdb73 ) -> ( 5d357799 2ac30c80 )
( 4332fe8f 3a127ee4 a9ca9de9 dad404ad , d1fe145a c84694ee ) -> ( a70b1d53 e7a9c00e )
( 7f62ac9b 2a0771f4 647db7f8 62619859 , 618f1ac2 67c3e795 ) -> ( 0780b34d 2ca7d378 )
( 0462183a ce7edfc6 27abbd9a a634d96e , 887a585d a3f83ef2 ) -> ( d246366c 81b87462 )
( 34c7b65d 78aa9068 599d1582 c42b7e33 , 4e81fa1b 3d22ecd8 ) -> ( 9d5ecc3b 947fa620 )
( f36c977a 0606b8a0 9e3fe947 6e46237b , 5d8e0fbe 2d3b259a ) -> ( f974c6b3 67e2decf )
( cd4b3820 b2f1e5a2 485dc7b3 843690d0 , 48db41bb 5ad77d7a ) -> ( b4add44a 0c401e70 )
( ee2744ac ef5f53ec 7dab871d d58b3f70 , 70c94e92 802f6c66 ) -> ( 61e14e3f 89408981 )
( 58fda015 c4ce0afb 49c71f9c 7e0a16f0 , 6ecdfbfa a705a912 ) -> ( 8c2a9f0c 2f56c18e )
( 87132255 41623986 bcc3fb61 7e6142ce , 9d0eff09 55ac6631 ) -> ( 8919ea55 c7d430c6 )
\$\endgroup\$
  • 1
    \$\begingroup\$ When I tried the Wikipedia C code, using a zero key and plaintext I got the second row of your plaintext as my ciphertext... \$\endgroup\$ – Neil Jun 24 '16 at 21:31
  • \$\begingroup\$ Well, the first two do now; I'm too lazy to try the rest though. \$\endgroup\$ – Neil Jun 24 '16 at 22:10
  • \$\begingroup\$ @Neil, that's the nice thing about crypto: Chances are high, that if a trivial (0-based) and a nontrivial testvector pass, all test vectors will pass :) \$\endgroup\$ – SEJPM Jun 24 '16 at 22:13
  • 2
    \$\begingroup\$ I suggest you add some test cases with a non-zero key. \$\endgroup\$ – Dennis Jun 25 '16 at 3:49
  • \$\begingroup\$ @Dennis those can now be found at the bottom of the question :) \$\endgroup\$ – SEJPM Jun 25 '16 at 12:12
5
\$\begingroup\$

JavaScript (ES6), 122 118 114 113 bytes

f=
(v,w,k,l,m,n)=>{for(s=0;s<84e9;v+=w*16+k^w+s^(w>>>5)+l,w+=v*16+m^v+s^(v>>>5)+n)s+=0x9e3779b9;return[v>>>0,w>>>0]}
;
<div oninput="[r0.value,r1.value]=f(...[v0,v1,k0,k1,k2,k3].map(i=>parseInt(i.value,16))).map(i=>(i+(1<<30)*4).toString(16).slice(1))">
Plain 0: <input id=v0><br>
Plain 1: <input id=v1><br>
Key 0: <input id=k0><br>
Key 1: <input id=k1><br>
Key 2: <input id=k2><br>
Key 3: <input id=k3><br>
</div>
Cipher 0: <input id=r0 readonly><br>
Cipher 1: <input id=r1 readonly><br>

Note: Uses little-endian arithmetic. Accepts and returns unsigned 32-bit integer values. Edit: Saved 4 bytes by using multiplication instead of left shift. Saved 4 bytes by testing on s thus avoiding a separate loop variable. Saved 1 byte by moving the +=s inside the for.

\$\endgroup\$
  • \$\begingroup\$ Why not use eval with its implicit return instead of return? \$\endgroup\$ – Downgoat Jun 26 '16 at 17:47
  • \$\begingroup\$ Replacing {return} with eval('') doesn't save me any bytes \$\endgroup\$ – Neil Jun 26 '16 at 23:00
  • \$\begingroup\$ Oh okay. Thought it save a couple of bytes :( \$\endgroup\$ – Downgoat Jun 26 '16 at 23:18
4
\$\begingroup\$

Ruby, 114 113 106 bytes

Because Ruby doesn't have 32-bit arithmetic, the extra mod operations caused it to almost tie with Javascript anyways despite the other byte-saving operations Ruby has...

  • (-1 byte from extra semicolon)
  • (-7 bytes from trimming out unnecessary mod operations and shifting stuff around)

Try it online!

->v,w,a,b,c,d{s=0
32.times{s+=0x9e3779b9;v+=w*16+a^w+s^w/32+b;v%=m=2**32;w+=v*16+c^v+s^v/32+d;w%=m}
[v,w]}
\$\endgroup\$
2
\$\begingroup\$

C, 116 bytes

T(v,w,k,l,m,n,s)unsigned*v,*w;{for(s=0;s+957401312;*v+=*w*16+k^*w+s^*w/32+l,*w+=*v*16+m^*v+s^*v/32+n)s+=2654435769;}

Takes plaintext as v and w; key as k, l, m, and n; and stores the ciphertext in v and w.

Test it on Ideone.

\$\endgroup\$
2
\$\begingroup\$

J, 149 bytes

4 :0
'u v'=.y
s=.0
m=.2x^32
for.i.32
do.s=.m|s+2654435769
u=.m|u+(22 b.)/(s+v),(2{.x)+4 _5(33 b.)v
v=.m|v+(22 b.)/(s+u),(2}.x)+4 _5(33 b.)u
end.u,v
)

Yay! explicit (Boo!) J! I think there's a way to do this tacitly, but it'd probably become unreadable.

Usage

Takes the keys as four 32-bit integers on the LHS and the plaintext as two 32-bit integers on the RHS. The prefix 16b is equivalent to 0x in other languages and represents a hex value. The builtin hfd formats the output into a hex string for ease in testing.

    f =: 4 :0
'u v'=.y
s=.0
m=.2x^32
for.i.32
do.s=.m|s+2654435769
u=.m|u+(22 b.)/(s+v),(2{.x)+4 _5(33 b.)v
v=.m|v+(22 b.)/(s+u),(2}.x)+4 _5(33 b.)u
end.u,v
)
   hfd 0 0 0 0 f 0 0
41ea3a0a
94baa940
   hfd 0 0 0 0 f 16b3778001e 16b2bf2226f
96269d3e
82680480
   hfd 16b4300e123 16be39877ae 16b7c4d7a3c 16b98335923 f 16ba9afc671 16b79dcdb73 
5d357799
2ac30c80
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.