-module(xxtea). -export([encode/2, decode/2]). -export([test/0]). -define(DELTA, 16#9E3779B9). %% XXTEA? encode(<>, Plaintext) -> K = {A, B, C, D}, V = int32list_from_binary(Plaintext), Rounds = 6 + 52 div length(V), encode0(V, K, Rounds, lists:last(V), ?DELTA). % encode0(V, K, Rounds, Z, Sum) when Rounds > 0 -> V0 = [Z0|_] = encode1(V, K, Z, Sum, (Sum bsr 2) band 3, 0, []), encode0(lists:reverse(V0), K, Rounds - 1, Z0, Sum + ?DELTA); encode0(V, _K, 0, _Z, _Sum) -> int32list_to_binary(V). % encode1([H|T = [Y|_]], K, Z, Sum, E, P, Acc) -> Z0 = int32(H + mx(K, Z, Y, Sum, E, P)), encode1(T, K, Z0, Sum, E, P + 1, [Z0|Acc]); encode1([H], K, Z, Sum, E, P, Acc) -> Z0 = int32(H + mx(K, Z, lists:last(Acc), Sum, E, P)), [Z0|Acc]. %% XXTEA? decode(<>, Ciphertext) -> K = {A, B, C, D}, V = int32list_from_binary(Ciphertext), Rounds = 6 + 52 div length(V), decode0(V, K, Rounds, hd(V), Rounds * ?DELTA). % decode0(V, K, Rounds, Y, Sum) when Rounds > 0 -> VR = lists:reverse(V), V0 = [Y0|_] = decode1(VR, K, Y, Sum, (Sum bsr 2) band 3, length(V) - 1, []), decode0(V0, K, Rounds - 1, Y0, Sum - ?DELTA); decode0(V, _K, 0, _Y, 0) -> int32list_to_binary(V). % decode1([H|T = [Z|_]], K, Y, Sum, E, P, Acc) -> Y0 = int32(H - mx(K, Z, Y, Sum, E, P)), decode1(T, K, Y0, Sum, E, P - 1, [Y0|Acc]); decode1([H], K, Y, Sum, E, P = 0, Acc) -> Y0 = int32(H - mx(K, lists:last(Acc), Y, Sum, E, P)), [Y0|Acc]. % #define MX ((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (k[(p & 3) ^ e] ^ z)); mx(K, Z, Y, Sum, E, P) -> X1 = (Z bsr 5) bxor (Y bsl 2), X2 = (Y bsr 3) bxor (Z bsl 4), X3 = element((P band 3 bxor E) + 1, K), (X1 + X2) bxor ((Sum bxor Y) + (X3 bxor Z)). int32list_from_binary(Bin) -> int32list_from_binary(Bin, []). int32list_from_binary(<>, Acc) -> int32list_from_binary(Bin, [X|Acc]); int32list_from_binary(<<>>, Acc) -> lists:reverse(Acc). int32list_to_binary(List) -> list_to_binary([<> || X <- List]). int32(Num) -> Num band 16#FFFFFFFF. test() -> Vectors = [ {<<"00000000000000000000000000000000">>, <<"0000000000000000">>, <<"ab043705808c5d57">>}, {<<"0102040810204080fffefcf8f0e0c080">>, <<"0000000000000000">>, <<"d1e78be2c746728a">>}, {<<"9e3779b99b9773e9b979379e6b695156">>, <<"ffffffffffffffff">>, <<"67ed0ea8e8973fc5">>}, {<<"0102040810204080fffefcf8f0e0c080">>, <<"fffefcf8f0e0c080">>, <<"8c3707c01c7fccc4">>}, {<<"ffffffffffffffffffffffffffffffff">>, <<"157c13a850ba5e57306d7791">>, <<"b2601cefb078b772abccba6a">>}, {<<"9e3779b99b9773e9b979379e6b695156">>, <<"157c13a850ba5e57306d7791">>, <<"579016d143ed6247ac6710dd">>}, {<<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">>, <<"0102040810204080fffefcf8f0e0c080">>, <<"c0a19f06ebb0d63925aa27f74cc6b2d0">>}, {<<"9e3779b99b9773e9b979379e6b695156">>, <<"0102040810204080fffefcf8f0e0c080">>, <<"01b815fd2e4894d13555da434c9d868a">>} ], [test(from_hex(Key), from_hex(Text), from_hex(Cipher)) || {Key, Text, Cipher} <- Vectors], ok. test(Key, Text, Cipher) -> Cipher0 = encode(Key, Text), Text0 = decode(Key, Cipher0), io:format(user, "~p~n", [{test, Cipher0 =:= Cipher, Text0 =:= Text, {to_hex(Cipher0), to_hex(Cipher)}}]). -define(REFERENCE_SOURCE, "http://en.wikipedia.org/wiki/XXTEA /* According to Needham and Wheeler: BTEA will encode or decode n words as a single block where n > 1 v is the n word data vector k is the 4 word key n is negative for decoding if n is zero result is 1 and no coding or decoding takes place, otherwise the result is zero assumes 32 bit 'long' and same endian coding and decoding */ #include #define DELTA 0x9e3779b9 #define MX (((z >> 5 ^ y << 2) + (y >> 3 ^ z << 4)) ^ ((sum ^ y) + (key[(p & 3) ^ e] ^ z))) void btea(uint32_t *v, int n, uint32_t const key[4]) { uint32_t y, z, sum; unsigned p, rounds, e; if (n > 1) { /* Coding Part */ rounds = 6 + 52/n; sum = 0; z = v[n-1]; do { sum += DELTA; e = (sum >> 2) & 3; for (p = 0; p < n-1; p++) { y = v[p+1]; z = v[p] += MX; } y = v[0]; z = v[n-1] += MX; } while (--rounds); } else if (n < -1) { /* Decoding Part */ n = -n; rounds = 6 + 52/n; sum = rounds*DELTA; y = v[0]; do { e = (sum >> 2) & 3; for (p = n-1; p > 0; p--) { z = v[p-1]; y = v[p] -= MX; } z = v[n-1]; y = v[0] -= MX; } while ((sum -= DELTA) != 0); } } "). -define(TEST_VECTORS, "http://read.pudn.com/downloads187/sourcecode/windows/other/877059/xxtea.cpp__.htm XXTEA (Corrected Block Tiny Encryption Algorithm) is a variable length symmetric block cipher, published in the paper ¡°Correction to xtea¡± (Wheeler & Needham, 1998). The algorithm can be implemented in under 20 lines of C code, and runs efficiently on most platforms. Several implementations of XXTEA exist in a variety of languages, but implementation errors and lack of clarity in the specification mean that some implementations are incompatible. In an effort to correct this problem I have worked from the reference implementation and common assumptions of other implementations to construct a set of test vectors. The test vectors are given as (key, plaintext, ciphertext) tuples. An XXTEA key is always 128 bits. # 64-bit block 00000000000000000000000000000000, 0000000000000000, ab043705808c5d57 0102040810204080fffefcf8f0e0c080, 0000000000000000, d1e78be2c746728a 9e3779b99b9773e9b979379e6b695156, ffffffffffffffff, 67ed0ea8e8973fc5 0102040810204080fffefcf8f0e0c080, fffefcf8f0e0c080, 8c3707c01c7fccc4 # 96-bit block ffffffffffffffffffffffffffffffff, 157c13a850ba5e57306d7791, b2601cefb078b772abccba6a 9e3779b99b9773e9b979379e6b695156, 157c13a850ba5e57306d7791, 579016d143ed6247ac6710dd # 128-bit block aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa, 0102040810204080fffefcf8f0e0c080, c0a19f06ebb0d63925aa27f74cc6b2d0 9e3779b99b9773e9b979379e6b695156, 0102040810204080fffefcf8f0e0c080, 01b815fd2e4894d13555da434c9d868a # 160-bit block 0102040810204080fffefcf8f0e0c080, 157c13a850ba5e57306d77916fa2c37be1949616, 51f0ffeb46012a245e0c6c4fa097db27caec698d # 192-bit block 9e3779b99b9773e9b979379e6b695156, 690342f45054a708c475c91db77761bc01b815fd2e4894d1, 759e5b212ee58be734d610248e1daa1c9d0647d428b4f95a # 224-bit block 9e3779b99b9773e9b979379e6b695156, 3555da434c9d868a1431e73e73372fc0688e09ce11d00b6fd936a764, 8e63ae7d8a119566990eb756f16abf94ff87359803ca12fbaa03fdfb # 256-bit block 0102040810204080fffefcf8f0e0c080, db9af3c96e36a30c643c6e97f4d75b7a4b51a40e9d8759e581e3c40b341b4436, 5ef1b6e010a2227ba337374b59beffc5263503054745fb513000641e2c7dd107 Updated 2007/09/12 John Kendall has contributed test vectors for messages that are not a multiple of 64 bits. While the XXTEA specification does not handle such cases it is possible to pad any message to a suitable length; John¡¯s test vectors use zero padding. I have reproduced them here in the format of my original test vectors: # 8-bit message, zero-padded to 64-bit 6a6f686e636b656e64616c6c6a6f686e, 4100000000000000, 014e7a34874eeb29 # 16-bit message, zero-padded to 64-bit 6a6f686e636b656e64616c6c6a6f686e, 4142000000000000, e9d39f636e9ed090 # 24-bit message, zero-padded to 64-bit 6a6f686e636b656e64616c6c6a6f686e, 4142430000000000, d20ec51c06feaf0e # 32-bit message, zero-padded to 64-bit 6a6f686e636b656e64616c6c6a6f686e, 4142434400000000, b1551d6ffcd4b61b # 40-bit message, zero-padded to 64-bit 6a6f686e636b656e64616c6c6a6f686e, 4142434445000000, 0ff91e518b9837e3 # 48-bit message, zero-padded to 64-bit 6a6f686e636b656e64616c6c6a6f686e, 4142434445460000, 7003fc98b6788a77 # 56-bit message, zero-padded to 64-bit 6a6f686e636b656e64616c6c6a6f686e, 4142434445464700, 93951ad360650022 # 64-bit message 6a6f686e636b656e64616c6c6a6f686e, 4142434445464748, cdeb72b9c903ce52 "). to_hex(Bin) -> to_hex(Bin, <<>>). to_hex(<>, Acc) -> U = to_hex_digit(A), L = to_hex_digit(B), to_hex(Bin, <>); to_hex(<<>>, Acc) -> Acc. to_hex_digit(D) when D >= 0, D =< 9 -> $0 + D; to_hex_digit(D) when D >= 10, D =< 15 -> $a + D - 10. from_hex(Bin) -> case byte_size(Bin) rem 2 of 0 -> from_hex(Bin, <<>>); _ -> from_hex(<<$0, Bin/binary>>, <<>>) end. from_hex(<>, Acc) -> I = from_hex_digit(A) bsl 4 bor from_hex_digit(B), from_hex(Bin, <>); from_hex(<<>>, Acc) -> Acc. from_hex_digit(H) when H >= $0, H =< $9 -> H - $0; from_hex_digit(H) when H >= $a, H =< $f -> H - $a + 10; from_hex_digit(H) when H >= $A, H =< $F -> H - $A + 10.