The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#! /bin/false

# vim: tabstop=4
# $Id: Twofish_PP.pm,v 1.8 2003/11/27 15:28:41 guido Exp $

# Twofish in pure Perl.
# Copyright (C) 2003 Guido Flohr <guido@imperia.net>, all rights reserved.

# This program is free software; you can redistribute it and/or modify it
# under the same terms and conditions as Perl itsels (see the Artistic
# license included).

package Crypt::Twofish_PP;

use strict;
use integer;

use Locale::TextDomain qw (Crypt-Twofish_PP);

use vars qw ($VERSION);

$VERSION = '0.17';

my @q0 = (
	  169, 103, 179, 232, 4, 253, 163, 118, 154, 146, 128, 120, 228, 221, 209,
	  56, 13, 198, 53, 152, 24, 247, 236, 108, 67, 117, 55, 38, 250, 19, 148,
	  72, 242, 208, 139, 48, 132, 84, 223, 35, 25, 91, 61, 89, 243, 174, 162,
	  130, 99, 1, 131, 46, 217, 81, 155, 124, 166, 235, 165, 190, 22, 12, 227,
	  97, 192, 140, 58, 245, 115, 44, 37, 11, 187, 78, 137, 107, 83, 106, 180,
	  241, 225, 230, 189, 69, 226, 244, 182, 102, 204, 149, 3, 86, 212, 28, 30,
	  215, 251, 195, 142, 181, 233, 207, 191, 186, 234, 119, 57, 175, 51, 201,
	  98, 113, 129, 121, 9, 173, 36, 205, 249, 216, 229, 197, 185, 77, 68, 8,
	  134, 231, 161, 29, 170, 237, 6, 112, 178, 210, 65, 123, 160, 17, 49, 194,
	  39, 144, 32, 246, 96, 255, 150, 92, 177, 171, 158, 156, 82, 27, 95, 147,
	  10, 239, 145, 133, 73, 238, 45, 79, 143, 59, 71, 135, 109, 70, 214, 62,
	  105, 100, 42, 206, 203, 47, 252, 151, 5, 122, 172, 127, 213, 26, 75, 14,
	  167, 90, 40, 20, 63, 41, 136, 60, 76, 2, 184, 218, 176, 23, 85, 31, 138,
	  125, 87, 199, 141, 116, 183, 196, 159, 114, 126, 21, 34, 18, 88, 7, 153,
	  52, 110, 80, 222, 104, 101, 188, 219, 248, 200, 168, 43, 64, 220, 254,
	  50, 164, 202, 16, 33, 240, 211, 93, 15, 0, 111, 157, 54, 66, 74, 94, 193,
	  224
		   );
my @q1 = (
	  117, 243, 198, 244, 219, 123, 251, 200, 74, 211, 230, 107, 69, 125, 232,
	  75, 214, 50, 216, 253, 55, 113, 241, 225, 48, 15, 248, 27, 135, 250, 6,
	  63, 94, 186, 174, 91, 138, 0, 188, 157, 109, 193, 177, 14, 128, 93, 210,
	  213, 160, 132, 7, 20, 181, 144, 44, 163, 178, 115, 76, 84, 146, 116, 54,
	  81, 56, 176, 189, 90, 252, 96, 98, 150, 108, 66, 247, 16, 124, 40, 39,
	  140, 19, 149, 156, 199, 36, 70, 59, 112, 202, 227, 133, 203, 17, 208,
	  147, 184, 166, 131, 32, 255, 159, 119, 195, 204, 3, 111, 8, 191, 64, 231,
	  43, 226, 121, 12, 170, 130, 65, 58, 234, 185, 228, 154, 164, 151, 126,
	  218, 122, 23, 102, 148, 161, 29, 61, 240, 222, 179, 11, 114, 167, 28,
	  239, 209, 83, 62, 143, 51, 38, 95, 236, 118, 42, 73, 129, 136, 238, 33,
	  196, 26, 235, 217, 197, 57, 153, 205, 173, 49, 139, 1, 24, 35, 221, 31,
	  78, 45, 249, 72, 79, 242, 101, 142, 120, 92, 88, 25, 141, 229, 152, 87,
	  103, 127, 5, 100, 175, 99, 182, 254, 245, 183, 60, 165, 206, 233, 104,
	  68, 224, 77, 67, 105, 41, 46, 172, 21, 89, 168, 10, 158, 110, 71, 223,
	  52, 53, 106, 207, 220, 34, 201, 192, 155, 137, 212, 237, 171, 18, 162,
	  13, 82, 187, 2, 47, 169, 215, 97, 30, 180, 80, 4, 246, 194, 22, 37, 134,
	  86, 85, 9, 190, 145
	 );

my @m0 = 
	(
	  3166450293, 3974898163, 538985414, 3014904308, 3671720923,
	  33721211, 3806473211, 2661219016, 3385453642, 3570665939,
	  404253670, 505323371, 2560101957, 2998024317, 2795950824,
	  640071499, 1010587606, 2475919922, 2189618904, 1381144829,
	  2071712823, 3149608817, 1532729329, 1195869153, 606354480,
	  1364320783, 3132802808, 1246425883, 3216984199, 218984698,
	  2964370182, 1970658879, 3537042782, 2105352378, 1717973422,
	  976921435, 1499012234, 0, 3452801980, 437969053, 2930650221,
	  2139073473, 724289457, 3200170254, 3772817536, 2324303965,
	  993743570, 1684323029, 3638069408, 3890718084, 1600120839,
	  454758676, 741130933, 4244419728, 825304876, 2155898275,
	  1936927410, 202146163, 2037997388, 1802191188, 1263207058,
	  1397975412, 2492763958, 2206408529, 707409464, 3301219504,
	  572704957, 3587569754, 3183330300, 1212708960, 4294954594,
	  1280051094, 1094809452, 3351766594, 3958056183, 471602192,
	  1566401404, 909517352, 1734852647, 3924406156, 1145370899,
	  336915093, 4126522268, 3486456007, 1061104932, 3233866566,
	  1920129851, 1414818928, 690572490, 4042274275, 134807173,
	  3334870987, 4092808977, 2358043856, 2762234259, 3402274488,
	  1751661478, 3099086211, 943204384, 3857002239, 2913818271,
	  185304183, 3368558019, 2577006540, 1482222851, 421108335,
	  235801096, 2509602495, 1886408768, 4160172263, 1852755755,
	  522153698, 3048553849, 151588620, 1633760426, 1465325186,
	  2678000449, 2644344890, 286352618, 623234489, 2947538404,
	  1162152090, 3755969956, 2745392279, 3941258622, 892688602,
	  3991785594, 1128528919, 4177054566, 4227576212, 926405537,
	  4210704413, 3267520573, 3031747824, 842161630, 2627498419,
	  1448535819, 3823360626, 2273796263, 353704732, 4193860335,
	  1667481553, 875866451, 2593817918, 2981184143, 2088554803,
	  2290653990, 1027450463, 2711738348, 3840204662, 2172752938,
	  2442199369, 252705665, 4008618632, 370565614, 3621221153,
	  2543318468, 2779097114, 4278075371, 1835906521, 2021174981,
	  3318050105, 488498585, 1987486925, 1044307117, 3419105073,
	  3065399179, 4025441025, 303177240, 1616954659, 1785376989,
	  1296954911, 3469666638, 3739122733, 1431674361, 2122209864,
	  555856463, 50559730, 2694850149, 1583225230, 1515873912,
	  1701137244, 1650609752, 4261233945, 101119117, 1077970661,
	  4075994776, 859024471, 387420263, 84250239, 3907542533,
	  1330609508, 2307484335, 269522275, 1953771446, 168457726,
	  1549570805, 2610656439, 757936956, 808507045, 774785486,
	  1229556201, 1179021928, 2004309316, 2829637856, 2526413901,
	  673758531, 2846435689, 3654908201, 2256965934, 3520169900,
	  4109650453, 2374833497, 3604382376, 3115957258, 1111625118,
	  4143366510, 791656519, 3722249951, 589510964, 3435946549,
	  4059153514, 3250655951, 2240146396, 2408554018, 1903272393,
	  2425417920, 2863289243, 16904585, 2341200340, 1313770733,
	  2391699371, 2880152082, 1869561506, 3873854477, 3688624722,
	  2459073467, 3082270210, 1768540719, 960092585, 3553823959,
	  2812748641, 2728570142, 3284375988, 1819034704, 117900548,
	  67403766, 656885442, 2896996118, 3503322661, 1347425158,
	  3705468758, 2223250005, 3789639945, 2054825406, 320073617
	  );

my @m1 = 
	(
	  2849585465, 1737496343, 3010567324, 3906119334, 67438343,
	  4254618194, 2741338240, 1994384612, 2584233285, 2449623883,
	  2158026976, 2019973722, 3839733679, 3719326314, 3518980963,
	  943073834, 223667942, 3326287904, 895667404, 2562650866,
	  404623890, 4146392043, 3973554593, 1819754817, 1136470056,
	  1966259388, 936672123, 647727240, 4201647373, 335103044,
	  2494692347, 1213890174, 4068082435, 3504639116, 2336732854,
	  809247780, 2225465319, 1413573483, 3741769181, 600137824,
	  424017405, 1537423930, 1030275778, 1494584717, 4079086828,
	  2922473062, 2722000751, 2182502231, 1670713360, 22802415,
	  2202908856, 781289094, 3652545901, 1361019779, 2605951658,
	  2086886749, 2788911208, 3946839806, 2782277680, 3190127226,
	  380087468, 202311945, 3811963120, 1629726631, 3236991120,
	  2360338921, 981507485, 4120009820, 1937837068, 740766001,
	  628543696, 199710294, 3145437842, 1323945678, 2314273025,
	  1805590046, 1403597876, 1791291889, 3029976003, 4053228379,
	  3783477063, 3865778200, 3184009762, 1158584472, 3798867743,
	  4106859443, 3056563316, 1724643576, 3439303065, 2515145748,
	  65886296, 1459084508, 3571551115, 471536917, 514695842,
	  3607942099, 4213957346, 3273509064, 2384027230, 3049401388,
	  3918088521, 3474112961, 3212744085, 3122691453, 3932426513,
	  2005142283, 963495365, 2942994825, 869366908, 3382800753,
	  1657733119, 1899477947, 2180714255, 2034087349, 156361185,
	  2916892222, 606945087, 3450107510, 4187837781, 3639509634,
	  3850780736, 3316545656, 3117229349, 1292146326, 1146451831,
	  134876686, 2249412688, 3878746103, 2714974007, 490797818,
	  2855559521, 3985395278, 112439472, 1886147668, 2989126515,
	  3528604475, 1091280799, 2072707586, 2693322968, 290452467,
	  828885963, 3259377447, 666920807, 2427780348, 539506744,
	  4135519236, 1618495560, 4281263589, 2517060684, 1548445029,
	  2982619947, 2876214926, 2651669058, 2629563893, 1391647707,
	  468929098, 1604730173, 2472125604, 180140473, 4013619705,
	  2448364307, 2248017928, 1224839569, 3999340054, 763158238,
	  1337073953, 2403512753, 1004237426, 1203253039, 2269691839,
	  1831644846, 1189331136, 3596041276, 1048943258, 1764338089,
	  1685933903, 714375553, 3460902446, 3407333062, 801794409,
	  4240686525, 2539430819, 90106088, 2060512749, 2894582225,
	  2140013829, 3585762404, 447260069, 1270294054, 247054014,
	  2808121223, 1526257109, 673330742, 336665371, 1071543669,
	  695851481, 2292903662, 1009986861, 1281325433, 45529015,
	  3096890058, 3663213877, 2963064004, 402408259, 1427801220,
	  536235341, 2317113689, 2100867762, 1470903091, 3340292047,
	  2381579782, 1953059667, 3077872539, 3304429463, 2673257901,
	  1926947811, 2127948522, 357233908, 580816783, 312650667,
	  1481532002, 132669279, 2581929245, 876159779, 1858205430,
	  1346661484, 3730649650, 1752319558, 1697030304, 3163803085,
	  3674462938, 4173773498, 3371867806, 2827146966, 735014510,
	  1079013488, 3706422661, 4269083146, 847942547, 2760761311,
	  3393988905, 269753372, 561240023, 4039947444, 3540636884,
	  1561365130, 266490193, 0, 1872369945, 2648709658, 915379348,
	  1122420679, 1257032137, 1593692882, 3249241983, 3772295336
	 );

my @m2 = 
	(
	  3161832498, 3975408673, 549855299, 3019158473, 3671841283,
	  41616011, 3808158251, 2663948026, 3377121772, 3570652169,
	  417732715, 510336671, 2554697742, 2994582072, 2800264914,
	  642459319, 1020673111, 2469565322, 2195227374, 1392333464,
	  2067233748, 3144792887, 1542544279, 1205946243, 607134780,
	  1359958498, 3136862918, 1243302643, 3213344584, 234491248,
	  2953228467, 1967093214, 3529429757, 2109373728, 1722705457,
	  979057315, 1502239004, 0, 3451702675, 446503648, 2926423596,
	  2143387563, 733031367, 3188637369, 3766542496, 2321386000,
	  1003633490, 1691706554, 3634419848, 3884246949, 1594318824,
	  454302481, 750070978, 4237360308, 824979751, 2158198885,
	  1941074730, 208866433, 2035054943, 1800694593, 1267878658,
	  1400132457, 2486604943, 2203157279, 708323894, 3299919004,
	  582820552, 3579500024, 3187457475, 1214269560, 4284678094,
	  1284918279, 1097613687, 3343042534, 3958893348, 470817812,
	  1568431459, 908604962, 1730635712, 3918326191, 1142113529,
	  345314538, 4120704443, 3485978392, 1059340077, 3225862371,
	  1916498651, 1416647788, 701114700, 4041470005, 142936318,
	  3335243287, 4078039887, 2362477796, 2761139289, 3401108118,
	  1755736123, 3095640141, 941635624, 3858752814, 2912922966,
	  192351108, 3368273949, 2580322815, 1476614381, 426711450,
	  235408906, 2512360830, 1883271248, 4159174448, 1848340175,
	  534912878, 3044652349, 151783695, 1638555956, 1468159766,
	  2671877899, 2637864320, 300552548, 632890829, 2951000029,
	  1167738120, 3752124301, 2744623964, 3934186197, 903492952,
	  3984256464, 1125598204, 4167497931, 4220844977, 933312467,
	  4196268608, 3258827368, 3035673804, 853422685, 2629016689,
	  1443583719, 3815957466, 2275903328, 354161947, 4193253690,
	  1674666943, 877868201, 2587794053, 2978984258, 2083749073,
	  2284226715, 1029651878, 2716639703, 3832997087, 2167046548,
	  2437517569, 260116475, 4001951402, 384702049, 3609319283,
	  2546243573, 2769986984, 4276878911, 1842965941, 2026207406,
	  3308897645, 496573925, 1993176740, 1051541212, 3409038183,
	  3062609479, 4009881435, 303567390, 1612931269, 1792895664,
	  1293897206, 3461271273, 3727548028, 1442403741, 2118680154,
	  558834098, 66192250, 2691014694, 1586388505, 1517836902,
	  1700554059, 1649959502, 4246338885, 109905652, 1088766086,
	  4070109886, 861352876, 392632208, 92210574, 3892701278,
	  1331974013, 2309982570, 274927765, 1958114351, 184420981,
	  1559583890, 2612501364, 758918451, 816132310, 785264201,
	  1240025481, 1181238898, 2000975701, 2833295576, 2521667076,
	  675489981, 2842274089, 3643398521, 2251196049, 3517763975,
	  4095079498, 2371456277, 3601389186, 3104487868, 1117667853,
	  4134467265, 793194424, 3722435846, 590619449, 3426077794,
	  4050317764, 3251618066, 2245821931, 2401406878, 1909027233,
	  2428539120, 2862328403, 25756145, 2345962465, 1324174988,
	  2393607791, 2870127522, 1872916286, 3859670612, 3679640562,
	  2461766267, 3070408630, 1764714954, 967391705, 3554136844,
	  2808194851, 2719916717, 3283403673, 1817209924, 117704453,
	  83231871, 667035462, 2887167143, 3492139126, 1350979603,
	  3696680183, 2220196890, 3775521105, 2059303461, 328274927
	 );

my @m3 =
	 (
	  3644434905, 2417452944, 1906094961, 3534153938, 84345861,
	  2555575704, 1702929253, 3756291807, 138779144, 38507010,
	  2699067552, 1717205094, 3719292125, 2959793584, 3210990015,
	  908736566, 1424362836, 1126221379, 1657550178, 3203569854,
	  504502302, 619444004, 3617713367, 2000776311, 3173532605,
	  851211570, 3564845012, 2609391259, 1879964272, 4181988345,
	  2986054833, 1518225498, 2047079034, 3834433764, 1203145543,
	  1009004604, 2783413413, 1097552961, 115203846, 3311412165,
	  1174214981, 2738510755, 1757560168, 361584917, 569176865,
	  828812849, 1047503422, 374833686, 2500879253, 1542390107,
	  1303937869, 2441490065, 3043875253, 528699679, 1403689811,
	  1667071075, 996714043, 1073670975, 3593512406, 628801061,
	  2813073063, 252251151, 904979253, 598171939, 4036018416,
	  2951318703, 2157787776, 2455565714, 2165076865, 657533991,
	  1993352566, 3881176039, 2073213819, 3922611945, 4043409905,
	  2669570975, 2838778793, 3304155844, 2579739801, 2539385239,
	  2202526083, 1796793963, 3357720008, 244860174, 1847583342,
	  3384014025, 796177967, 3422054091, 4288269567, 3927217642,
	  3981968365, 4158412535, 3784037601, 454368283, 2913083053,
	  215209740, 736295723, 499696413, 425627161, 3257710018,
	  2303322505, 314691346, 2123743102, 545110560, 1678895716,
	  2215344004, 1841641837, 1787408234, 3514577873, 2708588961,
	  3472843470, 935031095, 4212097531, 1035303229, 1373702481,
	  3695095260, 759112749, 2759249316, 2639657373, 4001552622,
	  2252400006, 2927150510, 3441801677, 76958980, 1433879637,
	  168691722, 324044307, 821552944, 3543638483, 1090133312,
	  878815796, 2353982860, 3014657715, 1817473132, 712225322,
	  1379652178, 194986251, 2332195723, 2295898248, 1341329743,
	  1741369703, 1177010758, 3227985856, 3036450996, 674766888,
	  2131031679, 2018009208, 786825006, 122459655, 1264933963,
	  3341529543, 1871620975, 222469645, 3153435835, 4074459890,
	  4081720307, 2789040038, 1503957849, 3166243516, 989458234,
	  4011037167, 4261971454, 26298625, 1628892769, 2094935420,
	  2988527538, 1118932802, 3681696731, 3090106296, 1220511560,
	  749628716, 3821029091, 1463604823, 2241478277, 698968361,
	  2102355069, 2491493012, 1227804233, 398904087, 3395891146,
	  3284008131, 1554224988, 1592264030, 3505224400, 2278665351,
	  2382725006, 3127170490, 2829392552, 3072740279, 3116240569,
	  1619502944, 4174732024, 573974562, 286987281, 3732226014,
	  2044275065, 2867759274, 858602547, 1601784927, 3065447094,
	  2529867926, 1479924312, 2630135964, 4232255484, 444880154,
	  4132249590, 475630108, 951221560, 2889045932, 416270104,
	  4094070260, 1767076969, 1956362100, 4120364277, 1454219094,
	  3672339162, 3588914901, 1257510218, 2660180638, 2729120418,
	  1315067982, 3898542056, 3843922405, 958608441, 3254152897,
	  1147949124, 1563614813, 1917216882, 648045862, 2479733907,
	  64674563, 3334142150, 4204710138, 2195105922, 3480103887,
	  1349533776, 3951418603, 1963654773, 2324902538, 2380244109,
	  1277807180, 337383444, 1943478643, 3434410188, 164942601,
	  277503248, 3796963298, 0, 2585358234, 3759840736, 2408855183,
	  3871818470, 3972614892, 4258422525, 2877276587, 3634946264
	  );

sub __mds_rem;
sub __usage;
sub __padding_error;

use vars qw ($KEYSIZE);

# See method keysize() below for an explanation.
$KEYSIZE = 32;

sub new
{
    __usage (__PACKAGE__ .  "->new (key)")
        unless @_ == 2;

	my ($class, $key) = @_;

	my @key = unpack "C*", $key;
	my $keylength = @key;

	# FIXME: Turn off UTF-8 on the key!
	__padding_error ($keylength)
		unless $keylength == 16 || $keylength == 24 || $keylength == 32;

	# The key consists of k=len/8 (2, 3 or 4) 64-bit units.
	$keylength /= 8;

    my $self = {
		__keylength => $keylength
		};

    # We must derive three vectors Me, Mo, and S, each with k 32-bit
    # words, from the 2k words in the key.
    #
    #       Me = (key[0], key[2], ..., key[2k-2]) (even words)
    #       Mo = (key[1], key[3], ..., key[2k-1]) (odd  words)
    #
    # The third vector is derived by multiplying each of the k groups
    # of 8 bytes from the key by a 4x8 matrix, to get k 32-bit words.
    #
    #       S = (S[k-1], S[k-2], ..., S[0])
    #
    # where S[i] are the 4 bytes from the multiplication, interpreted
    # as a 32-bit word. As described later, mds_rem is equivalent to
    # the matrix multiplication, but faster.
	my @le_longs = unpack "V*", $key;

    # The words of the expanded key K are defined using the h function:
    #
    #       rho     = 2^24 + 2^16 + 2^8 + 2^0 (0x01010101)
    #       A[i]    = h(2i*rho, Me)
    #       B[i]    = ROL(h(2(i+1)*rho, Mo), 8)
    #       K[2i]   = (A[i] + B[i]) mod 2^32
    #       K[2i+1] = ROL((A[i] + 2B[i]) mod 2^32, 9)
    #
    # rho has the property that, for i = 0..255, the word i*rho
    # consists of four equal bytes, each with the value i. The function
    # h is only applied to words of this type, so we only pass it the
    # value of i.
	my @K;

    # The key-dependent S-boxes used in the g() function are created
    # below. They are defined by g(X) = h(X, S), where S is the vector
    # derived from the key. That is, for i=0..3, the S-box S[i] is
    # formed by mapping from x[i] to y[i] in the h function.
    #
    # The relevant lookup tables qN have been precomputed and stored in
    # tables.h; we also perform full key precomputations incorporating
    # the MDS matrix multiplications.
	my (@S0, @S1, @S2, @S3) = @_;
	my ($A, $B, $C);
	my ($i, $j);
	if ($keylength == 2) {
		my ($s7, $s6, $s5, $s4) = __mds_rem ($le_longs[0], $le_longs[1]);
		my ($s3, $s2, $s1, $s0) = __mds_rem ($le_longs[2], $le_longs[3]);

		for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
			$A = $m0[$q0[$q0[$i] ^ $key[8]] ^ $key[0]] ^
				$m1[$q0[$q1[$i] ^ $key[9]] ^ $key[1]] ^
				$m2[$q1[$q0[$i] ^ $key[10]] ^ $key[2]] ^
				$m3[$q1[$q1[$i] ^ $key[11]]	^ $key[3]];
			$B = $m0[$q0[$q0[$j] ^ $key[12]] ^ $key[4]] ^
				$m1[$q0[$q1[$j] ^ $key[13]] ^ $key[5]] ^
				$m2[$q1[$q0[$j] ^ $key[14]] ^ $key[6]] ^
				$m3[$q1[$q1[$j] ^ $key[15]] ^ $key[7]];

			$B = ($B << 8) | ($B >> 24 & 0xff);
			push @K, $A += $B;
			push @K, (($A += $B) << 9 | $A >> 23 & 0x1ff);
		}
		for (0 .. 255) {
            $S0[$_] = $m0[$q0[$q0[$_] ^ $s4] ^ $s0];
            $S1[$_] = $m1[$q0[$q1[$_] ^ $s5] ^ $s1];
            $S2[$_] = $m2[$q1[$q0[$_] ^ $s6] ^ $s2];
            $S3[$_] = $m3[$q1[$q1[$_] ^ $s7] ^ $s3];
        }	
	} elsif ($keylength == 3) {
		my ($sb, $sa, $s9, $s8) = __mds_rem ($le_longs[0], $le_longs[1]);
		my ($s7, $s6, $s5, $s4) = __mds_rem ($le_longs[2], $le_longs[3]);
		my ($s3, $s2, $s1, $s0) = __mds_rem ($le_longs[4], $le_longs[5]);

		for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
			$A = $m0[$q0[$q0[$q1[$i] ^ $key[16]] ^ $key[8]] ^ $key[0]] ^
				$m1[$q0[$q1[$q1[$i] ^ $key[17]] ^ $key[9]] ^ $key[1]] ^
				$m2[$q1[$q0[$q0[$i] ^ $key[18]] ^ $key[10]] ^ $key[2]] ^
				$m3[$q1[$q1[$q0[$i] ^ $key[19]] ^ $key[11]] ^ $key[3]];
			$B = $m0[$q0[$q0[$q1[$j] ^ $key[20]] ^ $key[12]] ^ $key[4]] ^
				$m1[$q0[$q1[$q1[$j] ^ $key[21]] ^ $key[13]]	^ $key[5]] ^
				$m2[$q1[$q0[$q0[$j] ^ $key[22]] ^ $key[14]]	^ $key[6]] ^
				$m3[$q1[$q1[$q0[$j] ^ $key[23]] ^ $key[15]]	^ $key[7]];
			$B = ($B << 8) | ($B >> 24 & 0xff);
			push @K, $A += $B;
			push @K, (($A += $B) << 9 | $A >> 23 & 0x1ff);
		}
        for (0 .. 255) {
			$S0[$_] = $m0[$q0[$q0[$q1[$_] ^ $s8] ^ $s4] ^ $s0];
            $S1[$_] = $m1[$q0[$q1[$q1[$_] ^ $s9] ^ $s5] ^ $s1];
            $S2[$_] = $m2[$q1[$q0[$q0[$_] ^ $sa] ^ $s6] ^ $s2];
            $S3[$_] = $m3[$q1[$q1[$q0[$_] ^ $sb] ^ $s7] ^ $s3];
        }	
	} elsif ($keylength == 4) {
		my ($sf, $se, $sd, $sc) = __mds_rem ($le_longs[0], $le_longs[1]);
		my ($sb, $sa, $s9, $s8) = __mds_rem ($le_longs[2], $le_longs[3]);
		my ($s7, $s6, $s5, $s4) = __mds_rem ($le_longs[4], $le_longs[5]);
		my ($s3, $s2, $s1, $s0) = __mds_rem ($le_longs[6], $le_longs[7]);

		for ($i = 0, $j = 1; $i < 40; $i += 2, $j += 2) {
			$A = $m0[$q0[$q0[$q1[$q1[$i] ^ $key[24]] ^ $key[16]] ^ $key[8]]
					 ^ $key[0]] ^
			    $m1[$q0[$q1[$q1[$q0[$i] ^ $key[25]] ^ $key[17]] ^ $key[9]]
					^ $key[1]] ^
				$m2[$q1[$q0[$q0[$q0[$i] ^ $key[26]] ^ $key[18]] ^ $key[10]]
					^ $key[2]] ^
				$m3[$q1[$q1[$q0[$q1[$i] ^ $key[27]] ^ $key[19]] ^ $key[11]]
					^ $key[3]];
			$B = $m0[$q0[$q0[$q1[$q1[$j] ^ $key[28]] ^ $key[20]] ^ $key[12]]
					 ^ $key[4]] ^
				$m1[$q0[$q1[$q1[$q0[$j] ^ $key[29]] ^ $key[21]] ^ $key[13]]	
					^ $key[5]] ^
				$m2[$q1[$q0[$q0[$q0[$j] ^ $key[30]] ^ $key[22]] ^ $key[14]] 
					^ $key[6]] ^
				$m3[$q1[$q1[$q0[$q1[$j] ^ $key[31]] ^ $key[23]] ^ $key[15]] 
					^ $key[7]];

			$B = ($B << 8) | ($B >> 24 & 0xff);
			push @K, $A += $B;
			push @K, (($A += $B) << 9 | $A >> 23 & 0x1ff);
		}
        for (0 .. 255) {
            $S0[$_] = $m0[$q0[$q0[$q1[$q1[$_]^$sc]^$s8]^$s4]^$s0];
            $S1[$_] = $m1[$q0[$q1[$q1[$q0[$_]^$sd]^$s9]^$s5]^$s1];
            $S2[$_] = $m2[$q1[$q0[$q0[$q0[$_]^$se]^$sa]^$s6]^$s2];
            $S3[$_] = $m3[$q1[$q1[$q0[$q1[$_]^$sf]^$sb]^$s7]^$s3];
        }
	}

	$self->{__K} = \@K;
	$self->{__S} = [ \@S0, \@S1, \@S2, \@S3 ];

	bless $self, $class;
}

sub keysize
{
	my $self = shift;

	if (ref $self) {
		return $self->{__keylength} * 8;
	} else {
		# When called as a class method, return a constant value.
		return $KEYSIZE;
	}
}

sub blocksize
{
	16;
}

sub new_encrypt { 
	my ($self, $plain) = @_;
	
	&{$self->{__encrypt}} ($self, $plain); 
}

sub encrypt
{
	my ($self, $cipher) = @_;

	my @words = unpack "V4", $cipher;
	my $K = $self->{__K};

	my $R0 = $K->[0] ^ $words[0];
	my $R1 = $K->[1] ^ $words[1];
	my $R2 = $K->[2] ^ $words[2];
	my $R3 = $K->[3] ^ $words[3];

	my ($t0, $t1);
	my ($S0, $S1, $S2, $S3) = @{$self->{__S}};

	# i = 0
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[($R0 >> 8) & 0xff] ^
		$S2->[($R0 >> 16) & 0xff] ^
		$S3->[($R0 >> 24) & 0xff];
	$t1 = $S0->[($R1 >> 24) & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[($R1 >> 8) & 0xff] ^
		$S3->[($R1 >> 16) & 0xff];
	
	$R2  ^= ($t0 + $t1 + $K->[8]);
	$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
	
	$R3 = (($R3 >> 31) & 1) | ($R3 << 1);
	$R3 ^= ($t0 + ($t1 << 1) + $K->[9]);
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[($R2 >> 8) & 0xff] ^
		$S2->[($R2 >> 16) & 0xff] ^
		$S3->[($R2 >> 24) & 0xff];
	$t1 = $S0->[($R3 >> 24) & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[($R3 >> 8) & 0xff] ^
		$S3->[($R3 >> 16) & 0xff];
	
	$R0 ^= ($t0 + $t1 + $K->[10]);
	$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
	
	$R1 = (($R1 >> 31) & 1) | ($R1 << 1);
	$R1 ^= ($t0 + ($t1 << 1) + $K->[11]);	
	
	# i = 1
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[($R0 >> 8) & 0xff] ^
		$S2->[($R0 >> 16) & 0xff] ^
		$S3->[($R0 >> 24) & 0xff];
	$t1 = $S0->[($R1 >> 24) & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[($R1 >> 8) & 0xff] ^
		$S3->[($R1 >> 16) & 0xff];
	
	$R2  ^= ($t0 + $t1 + $K->[12]);
	$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
	
	$R3 = (($R3 >> 31) & 1) | ($R3 << 1);
	$R3 ^= ($t0 + ($t1 << 1) + $K->[13]);
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[($R2 >> 8) & 0xff] ^
		$S2->[($R2 >> 16) & 0xff] ^
		$S3->[($R2 >> 24) & 0xff];
	$t1 = $S0->[($R3 >> 24) & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[($R3 >> 8) & 0xff] ^
		$S3->[($R3 >> 16) & 0xff];
	
	$R0 ^= ($t0 + $t1 + $K->[14]);
	$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
	
	$R1 = (($R1 >> 31) & 1) | ($R1 << 1);
	$R1 ^= ($t0 + ($t1 << 1) + $K->[15]);	
	
	# i = 2
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[($R0 >> 8) & 0xff] ^
		$S2->[($R0 >> 16) & 0xff] ^
		$S3->[($R0 >> 24) & 0xff];
	$t1 = $S0->[($R1 >> 24) & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[($R1 >> 8) & 0xff] ^
		$S3->[($R1 >> 16) & 0xff];
	
	$R2  ^= ($t0 + $t1 + $K->[16]);
	$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
	
	$R3 = (($R3 >> 31) & 1) | ($R3 << 1);
	$R3 ^= ($t0 + ($t1 << 1) + $K->[17]);
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[($R2 >> 8) & 0xff] ^
		$S2->[($R2 >> 16) & 0xff] ^
		$S3->[($R2 >> 24) & 0xff];
	$t1 = $S0->[($R3 >> 24) & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[($R3 >> 8) & 0xff] ^
		$S3->[($R3 >> 16) & 0xff];
	
	$R0 ^= ($t0 + $t1 + $K->[18]);
	$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
	
	$R1 = (($R1 >> 31) & 1) | ($R1 << 1);
	$R1 ^= ($t0 + ($t1 << 1) + $K->[19]);	
	
	# i = 3
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[($R0 >> 8) & 0xff] ^
		$S2->[($R0 >> 16) & 0xff] ^
		$S3->[($R0 >> 24) & 0xff];
	$t1 = $S0->[($R1 >> 24) & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[($R1 >> 8) & 0xff] ^
		$S3->[($R1 >> 16) & 0xff];
	
	$R2  ^= ($t0 + $t1 + $K->[20]);
	$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
	
	$R3 = (($R3 >> 31) & 1) | ($R3 << 1);
	$R3 ^= ($t0 + ($t1 << 1) + $K->[21]);
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[($R2 >> 8) & 0xff] ^
		$S2->[($R2 >> 16) & 0xff] ^
		$S3->[($R2 >> 24) & 0xff];
	$t1 = $S0->[($R3 >> 24) & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[($R3 >> 8) & 0xff] ^
		$S3->[($R3 >> 16) & 0xff];
	
	$R0 ^= ($t0 + $t1 + $K->[22]);
	$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
	
	$R1 = (($R1 >> 31) & 1) | ($R1 << 1);
	$R1 ^= ($t0 + ($t1 << 1) + $K->[23]);	

	# i = 4
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[($R0 >> 8) & 0xff] ^
		$S2->[($R0 >> 16) & 0xff] ^
		$S3->[($R0 >> 24) & 0xff];
	$t1 = $S0->[($R1 >> 24) & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[($R1 >> 8) & 0xff] ^
		$S3->[($R1 >> 16) & 0xff];
	
	$R2  ^= ($t0 + $t1 + $K->[24]);
	$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
	
	$R3 = (($R3 >> 31) & 1) | ($R3 << 1);
	$R3 ^= ($t0 + ($t1 << 1) + $K->[25]);
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[($R2 >> 8) & 0xff] ^
		$S2->[($R2 >> 16) & 0xff] ^
		$S3->[($R2 >> 24) & 0xff];
	$t1 = $S0->[($R3 >> 24) & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[($R3 >> 8) & 0xff] ^
		$S3->[($R3 >> 16) & 0xff];
	
	$R0 ^= ($t0 + $t1 + $K->[26]);
	$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
	
	$R1 = (($R1 >> 31) & 1) | ($R1 << 1);
	$R1 ^= ($t0 + ($t1 << 1) + $K->[27]);	

	# i = 5
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[($R0 >> 8) & 0xff] ^
		$S2->[($R0 >> 16) & 0xff] ^
		$S3->[($R0 >> 24) & 0xff];
	$t1 = $S0->[($R1 >> 24) & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[($R1 >> 8) & 0xff] ^
		$S3->[($R1 >> 16) & 0xff];
	
	$R2  ^= ($t0 + $t1 + $K->[28]);
	$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
	
	$R3 = (($R3 >> 31) & 1) | ($R3 << 1);
	$R3 ^= ($t0 + ($t1 << 1) + $K->[29]);
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[($R2 >> 8) & 0xff] ^
		$S2->[($R2 >> 16) & 0xff] ^
		$S3->[($R2 >> 24) & 0xff];
	$t1 = $S0->[($R3 >> 24) & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[($R3 >> 8) & 0xff] ^
		$S3->[($R3 >> 16) & 0xff];
	
	$R0 ^= ($t0 + $t1 + $K->[30]);
	$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
	
	$R1 = (($R1 >> 31) & 1) | ($R1 << 1);
	$R1 ^= ($t0 + ($t1 << 1) + $K->[31]);	
	
	# i = 6
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[($R0 >> 8) & 0xff] ^
		$S2->[($R0 >> 16) & 0xff] ^
		$S3->[($R0 >> 24) & 0xff];
	$t1 = $S0->[($R1 >> 24) & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[($R1 >> 8) & 0xff] ^
		$S3->[($R1 >> 16) & 0xff];
	
	$R2  ^= ($t0 + $t1 + $K->[32]);
	$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
	
	$R3 = (($R3 >> 31) & 1) | ($R3 << 1);
	$R3 ^= ($t0 + ($t1 << 1) + $K->[33]);
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[($R2 >> 8) & 0xff] ^
		$S2->[($R2 >> 16) & 0xff] ^
		$S3->[($R2 >> 24) & 0xff];
	$t1 = $S0->[($R3 >> 24) & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[($R3 >> 8) & 0xff] ^
		$S3->[($R3 >> 16) & 0xff];
	
	$R0 ^= ($t0 + $t1 + $K->[34]);
	$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
	
	$R1 = (($R1 >> 31) & 1) | ($R1 << 1);
	$R1 ^= ($t0 + ($t1 << 1) + $K->[35]);	
	
	# i = 7
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[($R0 >> 8) & 0xff] ^
		$S2->[($R0 >> 16) & 0xff] ^
		$S3->[($R0 >> 24) & 0xff];
	$t1 = $S0->[($R1 >> 24) & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[($R1 >> 8) & 0xff] ^
		$S3->[($R1 >> 16) & 0xff];
	
	$R2  ^= ($t0 + $t1 + $K->[36]);
	$R2 = ($R2 >> 1 & 0x7fffffff) | ($R2 << 31);
	
	$R3 = (($R3 >> 31) & 1) | ($R3 << 1);
	$R3 ^= ($t0 + ($t1 << 1) + $K->[37]);
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[($R2 >> 8) & 0xff] ^
		$S2->[($R2 >> 16) & 0xff] ^
		$S3->[($R2 >> 24) & 0xff];
	$t1 = $S0->[($R3 >> 24) & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[($R3 >> 8) & 0xff] ^
		$S3->[($R3 >> 16) & 0xff];
	
	$R0 ^= ($t0 + $t1 + $K->[38]);
	$R0 = ($R0 >> 1 & 0x7fffffff) | ($R0 << 31);
	
	$R1 = (($R1 >> 31) & 1) | ($R1 << 1);
	$R1 ^= ($t0 + ($t1 << 1) + $K->[39]);	
	
	return pack "V4", ($K->[4] ^ $R2, 
					   $K->[5] ^ $R3, 
					   $K->[6] ^ $R0, 
					   $K->[7] ^ $R1);
}

sub decrypt
{
	my ($self, $plain) = @_;
	my @words = unpack "V4", $plain;
	my $K = $self->{__K};
	my $S = $self->{__S};

	my $R0 = $K->[4] ^ $words[0];
	my $R1 = $K->[5] ^ $words[1];
	my $R2 = $K->[6] ^ $words[2];
	my $R3 = $K->[7] ^ $words[3];

	my ($t0, $t1);
	my ($S0, $S1, $S2, $S3) = @{$self->{__S}};

	# i = 7
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[$R0 >> 8 & 0xff] ^
		$S2->[$R0 >> 16 & 0xff] ^
		$S3->[$R0 >> 24 & 0xff];
	$t1 = $S0->[$R1 >> 24 & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[$R1 >> 8 & 0xff] ^
		$S3->[$R1 >> 16 & 0xff];
	
	$R2 = $R2 >> 31 & 0x1 | $R2 << 1;
	$R2 ^= $t0 + $t1 + $K->[38];
	
	$R3 ^= $t0 + ($t1 << 1) + $K->[39];
	$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
	
	$t0 = $S0->[$R2 & 0xff] ^
			$S1->[$R2 >> 8 & 0xff] ^
			$S2->[$R2 >> 16 & 0xff] ^
			$S3->[$R2 >> 24 & 0xff];
	$t1 = $S0->[$R3 >> 24 & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[$R3 >> 8 & 0xff] ^
		$S3->[$R3 >> 16 & 0xff];
	
	$R0 = $R0 >> 31 & 0x1 | $R0 << 1;
	$R0 ^= $t0 + $t1 + $K->[36];
	
	$R1 ^= $t0 + ($t1 << 1) + $K->[37];
	$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
	
	# i = 6
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[$R0 >> 8 & 0xff] ^
		$S2->[$R0 >> 16 & 0xff] ^
		$S3->[$R0 >> 24 & 0xff];
	$t1 = $S0->[$R1 >> 24 & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[$R1 >> 8 & 0xff] ^
		$S3->[$R1 >> 16 & 0xff];
	
	$R2 = $R2 >> 31 & 0x1 | $R2 << 1;
	$R2 ^= $t0 + $t1 + $K->[34];
	
	$R3 ^= $t0 + ($t1 << 1) + $K->[35];
	$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;

	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[$R2 >> 8 & 0xff] ^
		$S2->[$R2 >> 16 & 0xff] ^
		$S3->[$R2 >> 24 & 0xff];
	$t1 = $S0->[$R3 >> 24 & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[$R3 >> 8 & 0xff] ^
		$S3->[$R3 >> 16 & 0xff];
	
	$R0 = $R0 >> 31 & 0x1 | $R0 << 1;
	$R0 ^= $t0 + $t1 + $K->[32];
	
	$R1 ^= $t0 + ($t1 << 1) + $K->[33];
	$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;

	# i = 5
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[$R0 >> 8 & 0xff] ^
		$S2->[$R0 >> 16 & 0xff] ^
		$S3->[$R0 >> 24 & 0xff];
	$t1 = $S0->[$R1 >> 24 & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[$R1 >> 8 & 0xff] ^
		$S3->[$R1 >> 16 & 0xff];
	
	$R2 = $R2 >> 31 & 0x1 | $R2 << 1;
	$R2 ^= $t0 + $t1 + $K->[30];
	
	$R3 ^= $t0 + ($t1 << 1) + $K->[31];
	$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[$R2 >> 8 & 0xff] ^
		$S2->[$R2 >> 16 & 0xff] ^
		$S3->[$R2 >> 24 & 0xff];
	$t1 = $S0->[$R3 >> 24 & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[$R3 >> 8 & 0xff] ^
		$S3->[$R3 >> 16 & 0xff];
	
	$R0 = $R0 >> 31 & 0x1 | $R0 << 1;
	$R0 ^= $t0 + $t1 + $K->[28];
	
	$R1 ^= $t0 + ($t1 << 1) + $K->[29];
	$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
	
	# i = 4
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[$R0 >> 8 & 0xff] ^
		$S2->[$R0 >> 16 & 0xff] ^
		$S3->[$R0 >> 24 & 0xff];
	$t1 = $S0->[$R1 >> 24 & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[$R1 >> 8 & 0xff] ^
		$S3->[$R1 >> 16 & 0xff];
	
	$R2 = $R2 >> 31 & 0x1 | $R2 << 1;
	$R2 ^= $t0 + $t1 + $K->[26];
	
	$R3 ^= $t0 + ($t1 << 1) + $K->[27];
	$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[$R2 >> 8 & 0xff] ^
		$S2->[$R2 >> 16 & 0xff] ^
		$S3->[$R2 >> 24 & 0xff];
	$t1 = $S0->[$R3 >> 24 & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[$R3 >> 8 & 0xff] ^
		$S3->[$R3 >> 16 & 0xff];
	
	$R0 = $R0 >> 31 & 0x1 | $R0 << 1;
	$R0 ^= $t0 + $t1 + $K->[24];
	
	$R1 ^= $t0 + ($t1 << 1) + $K->[25];
	$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
	
	# i = 3
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[$R0 >> 8 & 0xff] ^
		$S2->[$R0 >> 16 & 0xff] ^
		$S3->[$R0 >> 24 & 0xff];
	$t1 = $S0->[$R1 >> 24 & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[$R1 >> 8 & 0xff] ^
		$S3->[$R1 >> 16 & 0xff];
	
	$R2 = $R2 >> 31 & 0x1 | $R2 << 1;
	$R2 ^= $t0 + $t1 + $K->[22];
	
	$R3 ^= $t0 + ($t1 << 1) + $K->[23];
	$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[$R2 >> 8 & 0xff] ^
		$S2->[$R2 >> 16 & 0xff] ^
		$S3->[$R2 >> 24 & 0xff];
	$t1 = $S0->[$R3 >> 24 & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[$R3 >> 8 & 0xff] ^
		$S3->[$R3 >> 16 & 0xff];
	
	$R0 = $R0 >> 31 & 0x1 | $R0 << 1;
	$R0 ^= $t0 + $t1 + $K->[20];
	
	$R1 ^= $t0 + ($t1 << 1) + $K->[21];
	$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
	
	# i = 2
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[$R0 >> 8 & 0xff] ^
		$S2->[$R0 >> 16 & 0xff] ^
		$S3->[$R0 >> 24 & 0xff];
	$t1 = $S0->[$R1 >> 24 & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[$R1 >> 8 & 0xff] ^
		$S3->[$R1 >> 16 & 0xff];
	
	$R2 = $R2 >> 31 & 0x1 | $R2 << 1;
	$R2 ^= $t0 + $t1 + $K->[18];
	
	$R3 ^= $t0 + ($t1 << 1) + $K->[19];
	$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[$R2 >> 8 & 0xff] ^
		$S2->[$R2 >> 16 & 0xff] ^
		$S3->[$R2 >> 24 & 0xff];
	$t1 = $S0->[$R3 >> 24 & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[$R3 >> 8 & 0xff] ^
		$S3->[$R3 >> 16 & 0xff];

	$R0 = $R0 >> 31 & 0x1 | $R0 << 1;
	$R0 ^= $t0 + $t1 + $K->[16];
	
	$R1 ^= $t0 + ($t1 << 1) + $K->[17];
	$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;

	# i = 1
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[$R0 >> 8 & 0xff] ^
		$S2->[$R0 >> 16 & 0xff] ^
		$S3->[$R0 >> 24 & 0xff];
	$t1 = $S0->[$R1 >> 24 & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[$R1 >> 8 & 0xff] ^
		$S3->[$R1 >> 16 & 0xff];
	
	$R2 = $R2 >> 31 & 0x1 | $R2 << 1;
	$R2 ^= $t0 + $t1 + $K->[14];
	
	$R3 ^= $t0 + ($t1 << 1) + $K->[15];
	$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[$R2 >> 8 & 0xff] ^
		$S2->[$R2 >> 16 & 0xff] ^
		$S3->[$R2 >> 24 & 0xff];
	$t1 = $S0->[$R3 >> 24 & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[$R3 >> 8 & 0xff] ^
		$S3->[$R3 >> 16 & 0xff];
	
	$R0 = $R0 >> 31 & 0x1 | $R0 << 1;
	$R0 ^= $t0 + $t1 + $K->[12];
	
	$R1 ^= $t0 + ($t1 << 1) + $K->[13];
	$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
	
	# i = 0
	$t0 = $S0->[$R0 & 0xff] ^
		$S1->[$R0 >> 8 & 0xff] ^
		$S2->[$R0 >> 16 & 0xff] ^
		$S3->[$R0 >> 24 & 0xff];
	$t1 = $S0->[$R1 >> 24 & 0xff] ^
		$S1->[$R1 & 0xff] ^
		$S2->[$R1 >> 8 & 0xff] ^
		$S3->[$R1 >> 16 & 0xff];
	
	$R2 = $R2 >> 31 & 0x1 | $R2 << 1;
	$R2 ^= $t0 + $t1 + $K->[10];
	
	$R3 ^= $t0 + ($t1 << 1) + $K->[11];
	$R3 = $R3 >> 1 & 0x7fffffff | $R3 << 31;
	
	$t0 = $S0->[$R2 & 0xff] ^
		$S1->[$R2 >> 8 & 0xff] ^
		$S2->[$R2 >> 16 & 0xff] ^
		$S3->[$R2 >> 24 & 0xff];
	$t1 = $S0->[$R3 >> 24 & 0xff] ^
		$S1->[$R3 & 0xff] ^
		$S2->[$R3 >> 8 & 0xff] ^
		$S3->[$R3 >> 16 & 0xff];
	
	$R0 = $R0 >> 31 & 0x1 | $R0 << 1;
	$R0 ^= $t0 + $t1 + $K->[8];
	
	$R1 ^= $t0 + ($t1 << 1) + $K->[9];
	$R1 = $R1 >> 1 & 0x7fffffff | $R1 << 31;
	
	return pack "V4", ($K->[0] ^ $R2, 
					   $K->[1] ^ $R3, 
					   $K->[2] ^ $R0, 
					   $K->[3] ^ $R1);
}

sub __usage
{
	my $msg = shift;

    my ($package, $filename, $line, $subr) = caller (1);
	require Carp;
    $Carp::CarpLevel = 2;
    Carp::croak (__"Usage:", " $msg");
}

sub __padding_error
{
	my $keylength = shift;

	require Carp;
    $Carp::CarpLevel = 2;
    Carp::croak (__x ("wrong key length {keylength}: "
					  . "key must be 16, 24 or 32 bytes long", 
					  $keylength));
}

# The (12, 8) Reed Solomon code has the generator polynomial:
#
#      g(x) = x^4 + (a + 1/a) * x^3 + a * x^2 + (a + 1/a) * x + 1
#
#   where the coefficients are in the finite field GF(2^8) with a modular
#   polynomial a^8+a^6+a^3+a^2+1. To generate the remainder, we have to
#   start with a 12th order polynomial with our eight input bytes as the
#   coefficients of the 4th to 11th terms:
#
#        m[7] * x^11 + m[6] * x^10 ... + m[0] * x^4 + 0 * x^3 +... + 0
#
#   We then multiply the generator polynomial by m[7]*x^7 and subtract it
#   (XOR in GF(2^8)) from the above to eliminate the x^7 term (the
#   arithmetic on the coefficients is done in GF(2^8)). We then multiply
#   the generator polynomial by m[6]*x^6 and use this to remove the x^10
#   term, and so on until the x^4 term is removed, and we are left with:
#
#        r[3] * x^3 + r[2] * x^2 + r[1] 8 x^1 + r[0]
#
#   which give the resulting 4 bytes of the remainder. This is equivalent
#   to the matrix multiplication described in the Twofish paper, but is
#   much faster.
sub __mds_rem
{
	my ($A, $B) = @_;

	# use constant G_MOD => 0x14d;

	# No gain by unrolling this loop.
	my ($t, $u);
	foreach (0 .. 7) {
        # Get most significant coefficient.
        $t = 0xff & ($B >> 24);

        # Shift the others up.
        $B = ($B << 8) | (0xff & ($A >> 24));
        $A <<= 8;

        $u = $t << 1;

        # Subtract the modular polynomial on overflow.
		$u ^= 0x14d if $t & 0x80;

        # Remove t * (a * x^2 + 1).
        $B ^= $t ^ ($u << 16);

        # Form u = a*t + t/a = t*(a + 1/a).
        $u ^= 0x7fffffff & ($t >> 1);

        # Add the modular polynomial on underflow.
		$u ^= 0xa6 if $t & 0x01;

		# Remove t * (a + 1/a) * (x^3 + x).
        $B ^= ($u << 24) | ($u << 8);
	}

	return map 0xff & $_, $B >> 24, $B >> 16, $B >> 8, $B;
}

1;

__END__

=head1 NAME

Crypt::Twofish_PP - The Twofish Algorithm in Pure Perl

=head1 SYNOPSIS

  use Crypt::Twofish_PP;

  $cipher = Crypt::Twofish_PP->new ($key);
  $ciphertext = $cipher->encrypt ($key);
  $plaintext = $cipher->decrypt ($ciphertext);

  $keysize = $cipher->keysize;
  $blocksize = $cipher->blocksize;

  $keysize = Crypt::Twofish_PP->keysize;
  $blocksize = Crypt::Twofish_PP->blocksize;

  use Crypt::CBC;
  $cipher = Crypt::CBC->new (key    => 'my secret key',
                             cipher => 'Twofish_PP');
  $cipher = Crypt::CBC->new (key    => 'my secret key',
                             cipher => 'Twofish_PP::Key24');
  $cipher = Crypt::CBC->new (key    => 'my secret key',
                             cipher => 'Twofish_PP::Key16');

  use Crypt::CBC;
  use Crypt::Twofish_PP;
  $Crypt::Twofish_PP::KEYSIZE = 24;
  $cipher = Crypt::CBC->new (key   => 'my secret key',
                             cipher => 'Twofish_PP');
  $Crypt::Twofish_PP::KEYSIZE = 32;
  $cipher = Crypt::CBC->new (key   => 'my secret key',
                             cipher => 'Twofish_PP');

=head1 DESCRIPTION

Twofish is a 128-bit symmetric block cipher with a variable key length 
(128, 192, or 256 bits) key, developed by Counterpane Labs. It is unpatented 
and free for all uses, as described at 
L<http://www.counterpane.com/twofish.html>.  It has been one of the
five finalists for AES.

This module is written in pure Perl, it should run everywhere where
Perl runs.

=head1 METHODS

The following methods are part of the B<Crypt::Twofish_PP> API:

=over 4

=item B<new KEY>

The constructor takes as its single argument a key of 16, 24 or
32 bytes length.  Calling it with other key lenghts, will cause
the module to throw an exception.

=item B<encrypt BLOCK>

Returns the encrypted block.  The length of the block must be exactly
16 bytes, otherwise the behaviour will be undefined.  You can safely
right-pad shorter blocks with null bytes.

=item B<decrypt BLOCK>

Returns the encrypted block.  The length of the block must be exactly
16 bytes, otherwise the behaviour will be undefined.  You can safely
right-pad shorter blocks with null bytes.

=item B<blocksize>

Returns the constant value 16.

=item B<keysize>

Returns the length of the key in bytes.   When called as a class
method, it returns the value C<$Crypt::Twofish_PP::KEYSIZE> which 
is initialized to 32.

=back

=head1 CIPHER BLOCK CHAINING (CBC) MODE

When encrypting streams of data you will need an additional block
chaining mechanism like CBC as provided by Crypt::CBC(3).  When
used with Crypt::CBC(3), Crypt::Twofish_PP(3) will usually work
with a fixed key length of 32 bytes, since Crypt::CBC(3) is not
capable of handling variable length keys.

If you need to use shorter keys with Crypt::CBC(3), you have two
choices: You can either overwrite the variable C<$Crypt::CBC::KEYSIZE>
with the desired length (16 or 24) in bytes, or you can specify
'Twofish_PP::Key16' resp. 'Twofish_PP::Key24' as the cipher algorithm
to Crypt::CBC(3).  The modules Crypt::Twofish_PP::Key32(3),
Crypt::Twofish_PP::Key24(3), and Crypt::Twofish_PP::Key16(3), inherit
all functionality from B<Crypt::Twofish_PP> but overwrite the 
C<keysize()> method, such that another default key length is reported
back to Crypt::CBC(3).

=head1 UTF-8 NUISANCES

Beginning with Perl 5.6, Perl scalars might be internally flagged as
being UTF-8 strings, and are treated as character-oriented data, not
as byte-oriented data (one character may require one to six bytes
for its internal representation).  In most cases you will gain nothing
from the introduction of that flag, and rather find yourself trying
to get rid of it.  B<Crypt::Twofish_PP> uses byte-oriented keys, and
encrypts/decrypts blocks of 16 bytes, and it is the callers responsability
to clean input data from that flag.

=head1 PERFORMANCE ISSUES

The most expansive method by far is the constructor.  The constructor
will set up the key scheduling which is a time-consuming process
that has to be repeated for every new key.  Processing one 16 byte
key currently (on my machine) takes about 15 times longer than
encrypting one 16 byte data block with that key.  If you plan to
use the same key several times in your application, you will probably
want to keep the encryption/decryption module around for later
perusal.  By the way, this behavior of B<Crypt::Twofish_PP> is a
typical characteristic of most modern encryption algorithms.  Although
the details may differ a lot between algorithms, setting up the
decoder/encoder with a key usually takes a lot more time than performing
the encryption/decryption.

The length of your key is also important.  The longer it is, the more
time is consumed to set up the key scheduling.  Once you have the module
ready to encrypt/decrypt, the key length has no impact on performance.
This is a general property of the Twofish algorithm, other algorithms
show a different behavior, and may vary in speed depending on the 
particular key length (Rijndael now AES is an example for this).

The subdirectory F<benchmark> of the source distribution contains
a script F<benchmark.pl> that you can use to test the performance of
a variety of cryptographic modules installed on your system.

There are two other modules available on CPAN that also implement
the Twofish algorith, but in C, not in Perl as B<Crypt::Twofish_PP>
does.  Of course the C implementations are a lot faster than the
pure Perl implementation, and you should rather use one of them whenever
possible.  However, at the time of this writing (November 2003),
Crypt::Twofish_PP offers by far the fastest pure Perl 256 bit encryption 
available on CPAN.  For shorter key lengths Crypt::CAST5_PP(3) is
faster only when encrypting/decrypting large chunks of data.

=head1 ENVIRONMENT

The environement variables LANG, LANGUAGE, LC_MESSAGES, resp.
LC_ALL control the language for messages produced by the module.
The environemt variable OUTPUT_CHARSET may be used to control
the output character set.  See the file F<README-NLS> in the
source distribution for details.

=head1 BUGS

The module has been tested on big- and little-endian machines with
integer sizes of 32 and 64 bits, and no bugs showed up.  It should
therefore be considered safe to use it everywhere.

=head1 AUTHOR

Copyright (C) 2003, Guido Flohr E<lt>guido@imperia.netE<gt>, all
rights reserved.  See the source code for details.

This software is contributed to the Perl community by Imperia
(L<http://www.imperia.net/>).

=head1 SEE ALSO

Crypt::Twofish_PP::Key32(3), Crypt::Twofish_PP::Key24(3),
Crypt::Twofish_PP::Key16(3), Crypt::CBC(3), Crypt::Twofish2(3), 
Crypt::Twofish(3), perl(1)

=cut
Local Variables:
mode: perl
perl-indent-level: 4
perl-continued-statement-offset: 4
perl-continued-brace-offset: 0
perl-brace-offset: -4
perl-brace-imaginary-offset: 0
perl-label-offset: -4
cperl-indent-level: 4
cperl-continued-statement-offset: 2
tab-width: 4
End:
=cut