#!perl
package des;

##
## Copyright (c) 1990 Dennis Ferguson.  All rights reserved.
##
## Commercial use is permitted only if products which are derived from
## or include this software are made available for purchase and/or use
## in Canada.  Otherwise, redistribution and use in source and binary
## forms are permitted.
##

#include "des_tables.h"
sub IP	{ $des_IP_table[$_[0]]; }
sub FP	{ $des_FP_table[$_[0]]; }
sub SP	{ $des_SP_table[$_[0]*64+$_[1]]; }

sub DES_SP_ENCRYPT_ROUND { # (left, right, temp, kp);
  local ($left, $right, $temp, $i, $kp) = @_;
  local($tmpxor);
  $temp = (($_[1] >> 11) | ($_[1] << 21));
  ($tmpxor) = unpack("x$i L",$kp); 
  $temp ^= $tmpxor;
  $_[0] ^=  &SP(0,($temp >> 24) & 0x3f) 
    | &SP(1,($temp >> 16) & 0x3f) 
    | &SP(2,($temp >>  8) & 0x3f) 
    | &SP(3,($temp      ) & 0x3f);
  $temp = (($_[1] >> 23) | ($_[1] << 9));
  ($tmpxor) = unpack("x$i xxxx L",$kp); 
  $temp ^= $tmpxor;
  $_[0] ^=  &SP(4,($temp >> 24) & 0x3f) 
    | &SP(5,($temp >> 16) & 0x3f) 
    | &SP(6,($temp >>  8) & 0x3f) 
    | &SP(7,($temp      ) & 0x3f);
}
  
sub DES_SP_DECRYPT_ROUND { # (left, right, temp, kp);
  local($left, $right, $temp, $i, $kp)  = @_;
  local($tmpxor);
  $i = $i-8;
  $temp = (($_[1] >> 23) | ($_[1] << 9));
  ($tmpxor) = unpack("x$i xxxx L",$kp);
  $temp ^= $tmpxor;
  $_[0] ^= &SP(7,($temp      ) & 0x3f) 
    | &SP(6,($temp >>  8) & 0x3f)
    | &SP(5,($temp >> 16) & 0x3f)
    | &SP(4,($temp >> 24) & 0x3f);
  $temp = (($_[1] >> 11) | ($_[1] << 21));
  ($tmpxor) = unpack("x$i L",$kp); 
  $temp ^= $tmpxor;
  $_[0] ^= &SP(3,($temp      ) & 0x3f) 
    | &SP(2,($temp >>  8) & 0x3f)
    | &SP(1,($temp >> 16) & 0x3f)
    | &SP(0,($temp >> 24) & 0x3f);
}

sub DES_IP_LEFT_BITS { # (left, right);
  local($left, $right) = @_;
  ((($left & 0x55555555) << 1) | ($right & 0x55555555));
}
sub DES_IP_RIGHT_BITS{ # (left, right);
  local($left, $right) = @_;
  (($left & 0xaaaaaaaa) | (($right & 0xaaaaaaaa) >> 1));
}
sub DES_FP_LEFT_BITS{ # (left, right);
  local($left, $right) = @_;
  ((($left & 0x0f0f0f0f) << 4) | ($right & 0x0f0f0f0f));
}
sub DES_FP_RIGHT_BITS{ # (left, right);
  local($left, $right) = @_;
  (($left & 0xf0f0f0f0) | (($right & 0xf0f0f0f0) >> 4));
}

sub DES_INITIAL_PERM{ # (left, right, temp);
  local($left, $right, $temp) = @_;
  
  $_[2] = &DES_IP_RIGHT_BITS($_[0], $_[1]); 
  $_[1] = &DES_IP_LEFT_BITS($_[0], $_[1]); 
  $_[0] = &IP(($_[1] >> 24) & 0xff) 
    | (&IP(($_[1] >> 16) & 0xff) << 1) 
    | (&IP(($_[1] >>  8) & 0xff) << 2) 
    | (&IP($_[1] & 0xff) << 3); 
  $_[1] = &IP(($_[2] >> 24) & 0xff) 
    | (&IP(($_[2] >> 16) & 0xff) << 1) 
    | (&IP(($_[2] >>  8) & 0xff) << 2) 
    | (&IP($_[2] & 0xff) << 3);
}
sub DES_FINAL_PERM{ # (left, right, temp);
  local($left, $right, $temp) = @_;

  $_[2] = &DES_FP_RIGHT_BITS($_[1], $_[0]); 
  $_[1] = &DES_FP_LEFT_BITS($_[1], $_[0]); 
  $_[0] = (&FP(($_[1] >> 24) & 0xff) << 6) 
    | (&FP(($_[1] >> 16) & 0xff) << 4) 
    | (&FP(($_[1] >>  8) & 0xff) << 2) 
    |  &FP($_[1] & 0xff); 
  $_[1] = (&FP(($_[2] >> 24) & 0xff) << 6) 
    | (&FP(($_[2] >> 16) & 0xff) << 4) 
    | (&FP(($_[2] >>  8) & 0xff) << 2) 
    |  &FP($_[2] & 0xff);
}
sub main'DES_DO_ENCRYPT{ # (left, right, temp, kp);
  local($left, $right, $temp, $kp) = @_;
  local($i); 
  &DES_INITIAL_PERM($_[0], $_[1], $_[2]); 
  for ($i=0; $i<127; $i+=16) { 
##    printf "[0x%08x.0x%08x]->",$_[0],$_[1];
    &DES_SP_ENCRYPT_ROUND($_[0], $_[1], $_[2], $i, $kp); 
##    printf "[0x%08x.0x%08x]->",$_[0],$_[1];
    &DES_SP_ENCRYPT_ROUND($_[1], $_[0], $_[2], $i+8, $kp); 
##    printf "[0x%08x.0x%08x]\n",$_[0],$_[1];
  } 
  &DES_FINAL_PERM($_[0], $_[1], $_[2]); 
}
sub main'DES_DO_DECRYPT{ # (left, right, temp, kp);
  local($left, $right, $temp, $kp) = @_;
  local($i); 
  &DES_INITIAL_PERM($_[0], $_[1], $_[2]); 
  for ($i=128; $i>0; $i-=16) { 
    &DES_SP_DECRYPT_ROUND($_[0], $_[1], $_[2], $i, $kp); 
    &DES_SP_DECRYPT_ROUND($_[1], $_[0], $_[2], $i-8, $kp); 
  } 
  &DES_FINAL_PERM($_[0], $_[1], $_[2]); 
}

@des_IP_table = (
	0x00000000, 0x00000010, 0x00000001, 0x00000011,
	0x00001000, 0x00001010, 0x00001001, 0x00001011,
	0x00000100, 0x00000110, 0x00000101, 0x00000111,
	0x00001100, 0x00001110, 0x00001101, 0x00001111,
	0x00100000, 0x00100010, 0x00100001, 0x00100011,
	0x00101000, 0x00101010, 0x00101001, 0x00101011,
	0x00100100, 0x00100110, 0x00100101, 0x00100111,
	0x00101100, 0x00101110, 0x00101101, 0x00101111,
	0x00010000, 0x00010010, 0x00010001, 0x00010011,
	0x00011000, 0x00011010, 0x00011001, 0x00011011,
	0x00010100, 0x00010110, 0x00010101, 0x00010111,
	0x00011100, 0x00011110, 0x00011101, 0x00011111,
	0x00110000, 0x00110010, 0x00110001, 0x00110011,
	0x00111000, 0x00111010, 0x00111001, 0x00111011,
	0x00110100, 0x00110110, 0x00110101, 0x00110111,
	0x00111100, 0x00111110, 0x00111101, 0x00111111,
	0x10000000, 0x10000010, 0x10000001, 0x10000011,
	0x10001000, 0x10001010, 0x10001001, 0x10001011,
	0x10000100, 0x10000110, 0x10000101, 0x10000111,
	0x10001100, 0x10001110, 0x10001101, 0x10001111,
	0x10100000, 0x10100010, 0x10100001, 0x10100011,
	0x10101000, 0x10101010, 0x10101001, 0x10101011,
	0x10100100, 0x10100110, 0x10100101, 0x10100111,
	0x10101100, 0x10101110, 0x10101101, 0x10101111,
	0x10010000, 0x10010010, 0x10010001, 0x10010011,
	0x10011000, 0x10011010, 0x10011001, 0x10011011,
	0x10010100, 0x10010110, 0x10010101, 0x10010111,
	0x10011100, 0x10011110, 0x10011101, 0x10011111,
	0x10110000, 0x10110010, 0x10110001, 0x10110011,
	0x10111000, 0x10111010, 0x10111001, 0x10111011,
	0x10110100, 0x10110110, 0x10110101, 0x10110111,
	0x10111100, 0x10111110, 0x10111101, 0x10111111,
	0x01000000, 0x01000010, 0x01000001, 0x01000011,
	0x01001000, 0x01001010, 0x01001001, 0x01001011,
	0x01000100, 0x01000110, 0x01000101, 0x01000111,
	0x01001100, 0x01001110, 0x01001101, 0x01001111,
	0x01100000, 0x01100010, 0x01100001, 0x01100011,
	0x01101000, 0x01101010, 0x01101001, 0x01101011,
	0x01100100, 0x01100110, 0x01100101, 0x01100111,
	0x01101100, 0x01101110, 0x01101101, 0x01101111,
	0x01010000, 0x01010010, 0x01010001, 0x01010011,
	0x01011000, 0x01011010, 0x01011001, 0x01011011,
	0x01010100, 0x01010110, 0x01010101, 0x01010111,
	0x01011100, 0x01011110, 0x01011101, 0x01011111,
	0x01110000, 0x01110010, 0x01110001, 0x01110011,
	0x01111000, 0x01111010, 0x01111001, 0x01111011,
	0x01110100, 0x01110110, 0x01110101, 0x01110111,
	0x01111100, 0x01111110, 0x01111101, 0x01111111,
	0x11000000, 0x11000010, 0x11000001, 0x11000011,
	0x11001000, 0x11001010, 0x11001001, 0x11001011,
	0x11000100, 0x11000110, 0x11000101, 0x11000111,
	0x11001100, 0x11001110, 0x11001101, 0x11001111,
	0x11100000, 0x11100010, 0x11100001, 0x11100011,
	0x11101000, 0x11101010, 0x11101001, 0x11101011,
	0x11100100, 0x11100110, 0x11100101, 0x11100111,
	0x11101100, 0x11101110, 0x11101101, 0x11101111,
	0x11010000, 0x11010010, 0x11010001, 0x11010011,
	0x11011000, 0x11011010, 0x11011001, 0x11011011,
	0x11010100, 0x11010110, 0x11010101, 0x11010111,
	0x11011100, 0x11011110, 0x11011101, 0x11011111,
	0x11110000, 0x11110010, 0x11110001, 0x11110011,
	0x11111000, 0x11111010, 0x11111001, 0x11111011,
	0x11110100, 0x11110110, 0x11110101, 0x11110111,
	0x11111100, 0x11111110, 0x11111101, 0x11111111
);

@des_FP_table = (
	0x00000000, 0x02000000, 0x00020000, 0x02020000,
	0x00000200, 0x02000200, 0x00020200, 0x02020200,
	0x00000002, 0x02000002, 0x00020002, 0x02020002,
	0x00000202, 0x02000202, 0x00020202, 0x02020202,
	0x01000000, 0x03000000, 0x01020000, 0x03020000,
	0x01000200, 0x03000200, 0x01020200, 0x03020200,
	0x01000002, 0x03000002, 0x01020002, 0x03020002,
	0x01000202, 0x03000202, 0x01020202, 0x03020202,
	0x00010000, 0x02010000, 0x00030000, 0x02030000,
	0x00010200, 0x02010200, 0x00030200, 0x02030200,
	0x00010002, 0x02010002, 0x00030002, 0x02030002,
	0x00010202, 0x02010202, 0x00030202, 0x02030202,
	0x01010000, 0x03010000, 0x01030000, 0x03030000,
	0x01010200, 0x03010200, 0x01030200, 0x03030200,
	0x01010002, 0x03010002, 0x01030002, 0x03030002,
	0x01010202, 0x03010202, 0x01030202, 0x03030202,
	0x00000100, 0x02000100, 0x00020100, 0x02020100,
	0x00000300, 0x02000300, 0x00020300, 0x02020300,
	0x00000102, 0x02000102, 0x00020102, 0x02020102,
	0x00000302, 0x02000302, 0x00020302, 0x02020302,
	0x01000100, 0x03000100, 0x01020100, 0x03020100,
	0x01000300, 0x03000300, 0x01020300, 0x03020300,
	0x01000102, 0x03000102, 0x01020102, 0x03020102,
	0x01000302, 0x03000302, 0x01020302, 0x03020302,
	0x00010100, 0x02010100, 0x00030100, 0x02030100,
	0x00010300, 0x02010300, 0x00030300, 0x02030300,
	0x00010102, 0x02010102, 0x00030102, 0x02030102,
	0x00010302, 0x02010302, 0x00030302, 0x02030302,
	0x01010100, 0x03010100, 0x01030100, 0x03030100,
	0x01010300, 0x03010300, 0x01030300, 0x03030300,
	0x01010102, 0x03010102, 0x01030102, 0x03030102,
	0x01010302, 0x03010302, 0x01030302, 0x03030302,
	0x00000001, 0x02000001, 0x00020001, 0x02020001,
	0x00000201, 0x02000201, 0x00020201, 0x02020201,
	0x00000003, 0x02000003, 0x00020003, 0x02020003,
	0x00000203, 0x02000203, 0x00020203, 0x02020203,
	0x01000001, 0x03000001, 0x01020001, 0x03020001,
	0x01000201, 0x03000201, 0x01020201, 0x03020201,
	0x01000003, 0x03000003, 0x01020003, 0x03020003,
	0x01000203, 0x03000203, 0x01020203, 0x03020203,
	0x00010001, 0x02010001, 0x00030001, 0x02030001,
	0x00010201, 0x02010201, 0x00030201, 0x02030201,
	0x00010003, 0x02010003, 0x00030003, 0x02030003,
	0x00010203, 0x02010203, 0x00030203, 0x02030203,
	0x01010001, 0x03010001, 0x01030001, 0x03030001,
	0x01010201, 0x03010201, 0x01030201, 0x03030201,
	0x01010003, 0x03010003, 0x01030003, 0x03030003,
	0x01010203, 0x03010203, 0x01030203, 0x03030203,
	0x00000101, 0x02000101, 0x00020101, 0x02020101,
	0x00000301, 0x02000301, 0x00020301, 0x02020301,
	0x00000103, 0x02000103, 0x00020103, 0x02020103,
	0x00000303, 0x02000303, 0x00020303, 0x02020303,
	0x01000101, 0x03000101, 0x01020101, 0x03020101,
	0x01000301, 0x03000301, 0x01020301, 0x03020301,
	0x01000103, 0x03000103, 0x01020103, 0x03020103,
	0x01000303, 0x03000303, 0x01020303, 0x03020303,
	0x00010101, 0x02010101, 0x00030101, 0x02030101,
	0x00010301, 0x02010301, 0x00030301, 0x02030301,
	0x00010103, 0x02010103, 0x00030103, 0x02030103,
	0x00010303, 0x02010303, 0x00030303, 0x02030303,
	0x01010101, 0x03010101, 0x01030101, 0x03030101,
	0x01010301, 0x03010301, 0x01030301, 0x03030301,
	0x01010103, 0x03010103, 0x01030103, 0x03030103,
	0x01010303, 0x03010303, 0x01030303, 0x03030303
);


# des_SP_table[8][64] = {
@des_SP_table = (
	0x00100000, 0x02100001, 0x02000401, 0x00000000,
	0x00000400, 0x02000401, 0x00100401, 0x02100400,
	0x02100401, 0x00100000, 0x00000000, 0x02000001,
	0x00000001, 0x02000000, 0x02100001, 0x00000401,
	0x02000400, 0x00100401, 0x00100001, 0x02000400,
	0x02000001, 0x02100000, 0x02100400, 0x00100001,
	0x02100000, 0x00000400, 0x00000401, 0x02100401,
	0x00100400, 0x00000001, 0x02000000, 0x00100400,
	0x02000000, 0x00100400, 0x00100000, 0x02000401,
	0x02000401, 0x02100001, 0x02100001, 0x00000001,
	0x00100001, 0x02000000, 0x02000400, 0x00100000,
	0x02100400, 0x00000401, 0x00100401, 0x02100400,
	0x00000401, 0x02000001, 0x02100401, 0x02100000,
	0x00100400, 0x00000000, 0x00000001, 0x02100401,
	0x00000000, 0x00100401, 0x02100000, 0x00000400,
	0x02000001, 0x02000400, 0x00000400, 0x00100001,

	0x00808200, 0x00000000, 0x00008000, 0x00808202,
	0x00808002, 0x00008202, 0x00000002, 0x00008000,
	0x00000200, 0x00808200, 0x00808202, 0x00000200,
	0x00800202, 0x00808002, 0x00800000, 0x00000002,
	0x00000202, 0x00800200, 0x00800200, 0x00008200,
	0x00008200, 0x00808000, 0x00808000, 0x00800202,
	0x00008002, 0x00800002, 0x00800002, 0x00008002,
	0x00000000, 0x00000202, 0x00008202, 0x00800000,
	0x00008000, 0x00808202, 0x00000002, 0x00808000,
	0x00808200, 0x00800000, 0x00800000, 0x00000200,
	0x00808002, 0x00008000, 0x00008200, 0x00800002,
	0x00000200, 0x00000002, 0x00800202, 0x00008202,
	0x00808202, 0x00008002, 0x00808000, 0x00800202,
	0x00800002, 0x00000202, 0x00008202, 0x00808200,
	0x00000202, 0x00800200, 0x00800200, 0x00000000,
	0x00008002, 0x00008200, 0x00000000, 0x00808002,

	0x00000104, 0x04010100, 0x00000000, 0x04010004,
	0x04000100, 0x00000000, 0x00010104, 0x04000100,
	0x00010004, 0x04000004, 0x04000004, 0x00010000,
	0x04010104, 0x00010004, 0x04010000, 0x00000104,
	0x04000000, 0x00000004, 0x04010100, 0x00000100,
	0x00010100, 0x04010000, 0x04010004, 0x00010104,
	0x04000104, 0x00010100, 0x00010000, 0x04000104,
	0x00000004, 0x04010104, 0x00000100, 0x04000000,
	0x04010100, 0x04000000, 0x00010004, 0x00000104,
	0x00010000, 0x04010100, 0x04000100, 0x00000000,
	0x00000100, 0x00010004, 0x04010104, 0x04000100,
	0x04000004, 0x00000100, 0x00000000, 0x04010004,
	0x04000104, 0x00010000, 0x04000000, 0x04010104,
	0x00000004, 0x00010104, 0x00010100, 0x04000004,
	0x04010000, 0x04000104, 0x00000104, 0x04010000,
	0x00010104, 0x00000004, 0x04010004, 0x00010100,

	0x00000080, 0x01040080, 0x01040000, 0x21000080,
	0x00040000, 0x00000080, 0x20000000, 0x01040000,
	0x20040080, 0x00040000, 0x01000080, 0x20040080,
	0x21000080, 0x21040000, 0x00040080, 0x20000000,
	0x01000000, 0x20040000, 0x20040000, 0x00000000,
	0x20000080, 0x21040080, 0x21040080, 0x01000080,
	0x21040000, 0x20000080, 0x00000000, 0x21000000,
	0x01040080, 0x01000000, 0x21000000, 0x00040080,
	0x00040000, 0x21000080, 0x00000080, 0x01000000,
	0x20000000, 0x01040000, 0x21000080, 0x20040080,
	0x01000080, 0x20000000, 0x21040000, 0x01040080,
	0x20040080, 0x00000080, 0x01000000, 0x21040000,
	0x21040080, 0x00040080, 0x21000000, 0x21040080,
	0x01040000, 0x00000000, 0x20040000, 0x21000000,
	0x00040080, 0x01000080, 0x20000080, 0x00040000,
	0x00000000, 0x20040000, 0x01040080, 0x20000080,

	0x80401000, 0x80001040, 0x80001040, 0x00000040,
	0x00401040, 0x80400040, 0x80400000, 0x80001000,
	0x00000000, 0x00401000, 0x00401000, 0x80401040,
	0x80000040, 0x00000000, 0x00400040, 0x80400000,
	0x80000000, 0x00001000, 0x00400000, 0x80401000,
	0x00000040, 0x00400000, 0x80001000, 0x00001040,
	0x80400040, 0x80000000, 0x00001040, 0x00400040,
	0x00001000, 0x00401040, 0x80401040, 0x80000040,
	0x00400040, 0x80400000, 0x00401000, 0x80401040,
	0x80000040, 0x00000000, 0x00000000, 0x00401000,
	0x00001040, 0x00400040, 0x80400040, 0x80000000,
	0x80401000, 0x80001040, 0x80001040, 0x00000040,
	0x80401040, 0x80000040, 0x80000000, 0x00001000,
	0x80400000, 0x80001000, 0x00401040, 0x80400040,
	0x80001000, 0x00001040, 0x00400000, 0x80401000,
	0x00000040, 0x00400000, 0x00001000, 0x00401040,

	0x10000008, 0x10200000, 0x00002000, 0x10202008,
	0x10200000, 0x00000008, 0x10202008, 0x00200000,
	0x10002000, 0x00202008, 0x00200000, 0x10000008,
	0x00200008, 0x10002000, 0x10000000, 0x00002008,
	0x00000000, 0x00200008, 0x10002008, 0x00002000,
	0x00202000, 0x10002008, 0x00000008, 0x10200008,
	0x10200008, 0x00000000, 0x00202008, 0x10202000,
	0x00002008, 0x00202000, 0x10202000, 0x10000000,
	0x10002000, 0x00000008, 0x10200008, 0x00202000,
	0x10202008, 0x00200000, 0x00002008, 0x10000008,
	0x00200000, 0x10002000, 0x10000000, 0x00002008,
	0x10000008, 0x10202008, 0x00202000, 0x10200000,
	0x00202008, 0x10202000, 0x00000000, 0x10200008,
	0x00000008, 0x00002000, 0x10200000, 0x00202008,
	0x00002000, 0x00200008, 0x10002008, 0x00000000,
	0x10202000, 0x10000000, 0x00200008, 0x10002008,

	0x08000820, 0x00000800, 0x00020000, 0x08020820,
	0x08000000, 0x08000820, 0x00000020, 0x08000000,
	0x00020020, 0x08020000, 0x08020820, 0x00020800,
	0x08020800, 0x00020820, 0x00000800, 0x00000020,
	0x08020000, 0x08000020, 0x08000800, 0x00000820,
	0x00020800, 0x00020020, 0x08020020, 0x08020800,
	0x00000820, 0x00000000, 0x00000000, 0x08020020,
	0x08000020, 0x08000800, 0x00020820, 0x00020000,
	0x00020820, 0x00020000, 0x08020800, 0x00000800,
	0x00000020, 0x08020020, 0x00000800, 0x00020820,
	0x08000800, 0x00000020, 0x08000020, 0x08020000,
	0x08020020, 0x08000000, 0x00020000, 0x08000820,
	0x00000000, 0x08020820, 0x00020020, 0x08000020,
	0x08020000, 0x08000800, 0x08000820, 0x00000000,
	0x08020820, 0x00020800, 0x00020800, 0x00000820,
	0x00000820, 0x00020020, 0x08000000, 0x08020800,

	0x40084010, 0x40004000, 0x00004000, 0x00084010,
	0x00080000, 0x00000010, 0x40080010, 0x40004010,
	0x40000010, 0x40084010, 0x40084000, 0x40000000,
	0x40004000, 0x00080000, 0x00000010, 0x40080010,
	0x00084000, 0x00080010, 0x40004010, 0x00000000,
	0x40000000, 0x00004000, 0x00084010, 0x40080000,
	0x00080010, 0x40000010, 0x00000000, 0x00084000,
	0x00004010, 0x40084000, 0x40080000, 0x00004010,
	0x00000000, 0x00084010, 0x40080010, 0x00080000,
	0x40004010, 0x40080000, 0x40084000, 0x00004000,
	0x40080000, 0x40004000, 0x00000010, 0x40084010,
	0x00084010, 0x00000010, 0x00004000, 0x40000000,
	0x00004010, 0x40084000, 0x00080000, 0x40000010,
	0x00080010, 0x40004010, 0x40000010, 0x00080010,
	0x00084000, 0x00000000, 0x40004000, 0x00004010,
	0x40000000, 0x40080010, 0x40084010, 0x00084000
);

# des_make_sched...

@PC1_CL = (
	0x00000000, 0x00000010, 0x00001000, 0x00001010,
	0x00100000, 0x00100010, 0x00101000, 0x00101010
);

@PC1_DL = (
	0x00000000, 0x00100000, 0x00001000, 0x00101000,
	0x00000010, 0x00100010, 0x00001010, 0x00101010,
	0x00000001, 0x00100001, 0x00001001, 0x00101001,
	0x00000011, 0x00100011, 0x00001011, 0x00101011
);

@PC1_CR = (
	0x00000000, 0x00000001, 0x00000100, 0x00000101,
	0x00010000, 0x00010001, 0x00010100, 0x00010101,
	0x01000000, 0x01000001, 0x01000100, 0x01000101,
	0x01010000, 0x01010001, 0x01010100, 0x01010101
);

@PC1_DR = (
	0x00000000, 0x01000000, 0x00010000, 0x01010000,
	0x00000100, 0x01000100, 0x00010100, 0x01010100
);

$TWO_BIT_SHIFTS	= 0x7efc;

# @PC2_C[4][64];
    
@_PC2_C = (
	0x00000000, 0x00000004, 0x00010000, 0x00010004,
	0x00000400, 0x00000404, 0x00010400, 0x00010404,
	0x00000020, 0x00000024, 0x00010020, 0x00010024,
	0x00000420, 0x00000424, 0x00010420, 0x00010424,
	0x01000000, 0x01000004, 0x01010000, 0x01010004,
	0x01000400, 0x01000404, 0x01010400, 0x01010404,
	0x01000020, 0x01000024, 0x01010020, 0x01010024,
	0x01000420, 0x01000424, 0x01010420, 0x01010424,
	0x00020000, 0x00020004, 0x00030000, 0x00030004,
	0x00020400, 0x00020404, 0x00030400, 0x00030404,
	0x00020020, 0x00020024, 0x00030020, 0x00030024,
	0x00020420, 0x00020424, 0x00030420, 0x00030424,
	0x01020000, 0x01020004, 0x01030000, 0x01030004,
	0x01020400, 0x01020404, 0x01030400, 0x01030404,
	0x01020020, 0x01020024, 0x01030020, 0x01030024,
	0x01020420, 0x01020424, 0x01030420, 0x01030424,

	0x00000000, 0x02000000, 0x00000800, 0x02000800,
	0x00080000, 0x02080000, 0x00080800, 0x02080800,
	0x00000001, 0x02000001, 0x00000801, 0x02000801,
	0x00080001, 0x02080001, 0x00080801, 0x02080801,
	0x00000100, 0x02000100, 0x00000900, 0x02000900,
	0x00080100, 0x02080100, 0x00080900, 0x02080900,
	0x00000101, 0x02000101, 0x00000901, 0x02000901,
	0x00080101, 0x02080101, 0x00080901, 0x02080901,
	0x10000000, 0x12000000, 0x10000800, 0x12000800,
	0x10080000, 0x12080000, 0x10080800, 0x12080800,
	0x10000001, 0x12000001, 0x10000801, 0x12000801,
	0x10080001, 0x12080001, 0x10080801, 0x12080801,
	0x10000100, 0x12000100, 0x10000900, 0x12000900,
	0x10080100, 0x12080100, 0x10080900, 0x12080900,
	0x10000101, 0x12000101, 0x10000901, 0x12000901,
	0x10080101, 0x12080101, 0x10080901, 0x12080901,

	0x00000000, 0x00040000, 0x00002000, 0x00042000,
	0x00100000, 0x00140000, 0x00102000, 0x00142000,
	0x20000000, 0x20040000, 0x20002000, 0x20042000,
	0x20100000, 0x20140000, 0x20102000, 0x20142000,
	0x00000008, 0x00040008, 0x00002008, 0x00042008,
	0x00100008, 0x00140008, 0x00102008, 0x00142008,
	0x20000008, 0x20040008, 0x20002008, 0x20042008,
	0x20100008, 0x20140008, 0x20102008, 0x20142008,
	0x00200000, 0x00240000, 0x00202000, 0x00242000,
	0x00300000, 0x00340000, 0x00302000, 0x00342000,
	0x20200000, 0x20240000, 0x20202000, 0x20242000,
	0x20300000, 0x20340000, 0x20302000, 0x20342000,
	0x00200008, 0x00240008, 0x00202008, 0x00242008,
	0x00300008, 0x00340008, 0x00302008, 0x00342008,
	0x20200008, 0x20240008, 0x20202008, 0x20242008,
	0x20300008, 0x20340008, 0x20302008, 0x20342008,

	0x00000000, 0x00000010, 0x08000000, 0x08000010,
	0x00000200, 0x00000210, 0x08000200, 0x08000210,
	0x00000002, 0x00000012, 0x08000002, 0x08000012,
	0x00000202, 0x00000212, 0x08000202, 0x08000212,
	0x04000000, 0x04000010, 0x0c000000, 0x0c000010,
	0x04000200, 0x04000210, 0x0c000200, 0x0c000210,
	0x04000002, 0x04000012, 0x0c000002, 0x0c000012,
	0x04000202, 0x04000212, 0x0c000202, 0x0c000212,
	0x00001000, 0x00001010, 0x08001000, 0x08001010,
	0x00001200, 0x00001210, 0x08001200, 0x08001210,
	0x00001002, 0x00001012, 0x08001002, 0x08001012,
	0x00001202, 0x00001212, 0x08001202, 0x08001212,
	0x04001000, 0x04001010, 0x0c001000, 0x0c001010,
	0x04001200, 0x04001210, 0x0c001200, 0x0c001210,
	0x04001002, 0x04001012, 0x0c001002, 0x0c001012,
	0x04001202, 0x04001212, 0x0c001202, 0x0c001212
);
sub PC2_C { $_PC2_C[$_[0]*64+$_[1]]; }

# static unsigned long PC2_D[4][64] 
@_PC2_D = (
	0x00000000, 0x02000000, 0x00020000, 0x02020000,
	0x00000100, 0x02000100, 0x00020100, 0x02020100,
	0x00000008, 0x02000008, 0x00020008, 0x02020008,
	0x00000108, 0x02000108, 0x00020108, 0x02020108,
	0x00200000, 0x02200000, 0x00220000, 0x02220000,
	0x00200100, 0x02200100, 0x00220100, 0x02220100,
	0x00200008, 0x02200008, 0x00220008, 0x02220008,
	0x00200108, 0x02200108, 0x00220108, 0x02220108,
	0x00000200, 0x02000200, 0x00020200, 0x02020200,
	0x00000300, 0x02000300, 0x00020300, 0x02020300,
	0x00000208, 0x02000208, 0x00020208, 0x02020208,
	0x00000308, 0x02000308, 0x00020308, 0x02020308,
	0x00200200, 0x02200200, 0x00220200, 0x02220200,
	0x00200300, 0x02200300, 0x00220300, 0x02220300,
	0x00200208, 0x02200208, 0x00220208, 0x02220208,
	0x00200308, 0x02200308, 0x00220308, 0x02220308,

	0x00000000, 0x00001000, 0x00000020, 0x00001020,
	0x00100000, 0x00101000, 0x00100020, 0x00101020,
	0x08000000, 0x08001000, 0x08000020, 0x08001020,
	0x08100000, 0x08101000, 0x08100020, 0x08101020,
	0x00000004, 0x00001004, 0x00000024, 0x00001024,
	0x00100004, 0x00101004, 0x00100024, 0x00101024,
	0x08000004, 0x08001004, 0x08000024, 0x08001024,
	0x08100004, 0x08101004, 0x08100024, 0x08101024,
	0x00000400, 0x00001400, 0x00000420, 0x00001420,
	0x00100400, 0x00101400, 0x00100420, 0x00101420,
	0x08000400, 0x08001400, 0x08000420, 0x08001420,
	0x08100400, 0x08101400, 0x08100420, 0x08101420,
	0x00000404, 0x00001404, 0x00000424, 0x00001424,
	0x00100404, 0x00101404, 0x00100424, 0x00101424,
	0x08000404, 0x08001404, 0x08000424, 0x08001424,
	0x08100404, 0x08101404, 0x08100424, 0x08101424,

	0x00000000, 0x10000000, 0x00010000, 0x10010000,
	0x00000002, 0x10000002, 0x00010002, 0x10010002,
	0x00002000, 0x10002000, 0x00012000, 0x10012000,
	0x00002002, 0x10002002, 0x00012002, 0x10012002,
	0x00040000, 0x10040000, 0x00050000, 0x10050000,
	0x00040002, 0x10040002, 0x00050002, 0x10050002,
	0x00042000, 0x10042000, 0x00052000, 0x10052000,
	0x00042002, 0x10042002, 0x00052002, 0x10052002,
	0x20000000, 0x30000000, 0x20010000, 0x30010000,
	0x20000002, 0x30000002, 0x20010002, 0x30010002,
	0x20002000, 0x30002000, 0x20012000, 0x30012000,
	0x20002002, 0x30002002, 0x20012002, 0x30012002,
	0x20040000, 0x30040000, 0x20050000, 0x30050000,
	0x20040002, 0x30040002, 0x20050002, 0x30050002,
	0x20042000, 0x30042000, 0x20052000, 0x30052000,
	0x20042002, 0x30042002, 0x20052002, 0x30052002,

	0x00000000, 0x04000000, 0x00000001, 0x04000001,
	0x01000000, 0x05000000, 0x01000001, 0x05000001,
	0x00000010, 0x04000010, 0x00000011, 0x04000011,
	0x01000010, 0x05000010, 0x01000011, 0x05000011,
	0x00080000, 0x04080000, 0x00080001, 0x04080001,
	0x01080000, 0x05080000, 0x01080001, 0x05080001,
	0x00080010, 0x04080010, 0x00080011, 0x04080011,
	0x01080010, 0x05080010, 0x01080011, 0x05080011,
	0x00000800, 0x04000800, 0x00000801, 0x04000801,
	0x01000800, 0x05000800, 0x01000801, 0x05000801,
	0x00000810, 0x04000810, 0x00000811, 0x04000811,
	0x01000810, 0x05000810, 0x01000811, 0x05000811,
	0x00080800, 0x04080800, 0x00080801, 0x04080801,
	0x01080800, 0x05080800, 0x01080801, 0x05080801,
	0x00080810, 0x04080810, 0x00080811, 0x04080811,
	0x01080810, 0x05080810, 0x01080811, 0x05080811
);
sub PC2_D { $_PC2_D[$_[0]*64+$_[1]]; }


# ok... key is pack("C8"), schedule is s[8]...; 
# now schedule is pack("L32");

sub main'make_key_sched{ # (key, schedule);
  local($key, $schedule) = @_;
  local($c,$d);
  local($tmp);
  local(@k) = unpack("C8",$key);

#  for(@k) { printf "0x%02x,",$_; } print "\n";

  $tmp =  $k[0] << 24;
  $tmp |= $k[1] << 16;
  $tmp |= $k[2] << 8;
  $tmp |= $k[3];
  
  
  $c = $PC1_CL[($tmp >> 29) & 0x7]
    | ($PC1_CL[($tmp >> 21) & 0x7] << 1)
    | ($PC1_CL[($tmp >> 13) & 0x7] << 2)
    | ($PC1_CL[($tmp >>  5) & 0x7] << 3);
  $d = $PC1_DL[($tmp >> 25) & 0xf]
    | ($PC1_DL[($tmp >> 17) & 0xf] << 1)
    | ($PC1_DL[($tmp >>  9) & 0xf] << 2)
    | ($PC1_DL[($tmp >>  1) & 0xf] << 3);

  $tmp =  $k[4] << 24;
  $tmp |= $k[5] << 16;
  $tmp |= $k[6] << 8;
  $tmp |= $k[7];
  
  $c |= $PC1_CR[($tmp >> 28) & 0xf]
     | ($PC1_CR[($tmp >> 20) & 0xf] << 1)
     | ($PC1_CR[($tmp >> 12) & 0xf] << 2)
     | ($PC1_CR[($tmp >>  4) & 0xf] << 3);
  $d |= $PC1_DR[($tmp >> 25) & 0x7]
     | ($PC1_DR[($tmp >> 17) & 0x7] << 1)
     | ($PC1_DR[($tmp >>  9) & 0x7] << 2)
     | ($PC1_DR[($tmp >>  1) & 0x7] << 3);


  local($ltmp, $rtmp);
  local($two_bit_shifts);
  local($i);


  $two_bit_shifts = $TWO_BIT_SHIFTS;
  for (0..15) {

    if ($two_bit_shifts & 0x1) {
      $c = (($c << 2) & 0xffffffc) | ($c >> 26);
      $d = (($d << 2) & 0xffffffc) | ($d >> 26);
    } else {
      $c = (($c << 1) & 0xffffffe) | ($c >> 27);
      $d = (($d << 1) & 0xffffffe) | ($d >> 27);
    }
    $two_bit_shifts >>= 1;	

    $ltmp = &PC2_C(0,(($c >> 22) & 0x3f))
      | &PC2_C(1,(($c >> 15) & 0xf) | (($c >> 16) & 0x30))
      | &PC2_C(2,(($c >>  4) & 0x3) | (($c >>  9) & 0x3c))
      | &PC2_C(3,(($c      ) & 0x7) | (($c >>  4) & 0x38));

    $rtmp = &PC2_D(0,(($d >> 22) & 0x3f))
      | &PC2_D(1,(($d >> 14) & 0xf) | (($d >> 15) & 0x30))
      | &PC2_D(2,(($d >>  7) & 0x3f))
      | &PC2_D(3,(($d      ) & 0x3) | (($d >>  1) & 0x3c));
			
    $_[1] .= pack("L L",($ltmp & 0x00ffff00) | ($rtmp & 0xff0000ff),
                        ($ltmp & 0xff0000ff) | ($rtmp & 0x00ffff00));
  }
}

# cbc_encrypt...
sub main'des_cbc_encrypt { # (in, out, length, schedule, ivec, encrypt);
  local($in, $out, $length, $schedule, $ivec, $encrypt)=@_;
  if($encrypt) {
    local($cleft,$cright,$i,$xleft,$xright,$temp);
    ($cleft,$cright) = unpack("N N",$ivec);
    
    local($ip,$op) = (0,"");
    
    while ($length > 0) {
      if ($length >= 8) {
	($xleft,$xright) = unpack("x$ip N N",$in);
	$cleft ^= $xleft;
	$cright ^= $xright;
	$length -= 8; $ip += 8;
      } else {
	($xleft,$xright) = unpack("x$ip N N",pack("a$ip x8",$in));
	$cleft ^= $xleft;
	$cright ^= $xright;
	$length = 0;
      }
      &main'DES_DO_ENCRYPT($cleft, $cright, $temp, $schedule);
      $op .= pack("N N",$cleft,$cright);
    }
    $_[1] = $op;
  } else {
    local($cleft,$cright,$i,$ocipherl,$ocipherr,$temp,$cipherl,$cipherr);
    ($ocipherl,$ocipherr) = unpack("N N",$ivec);

    local($ip,$op) = (0,"");

    for (;;) {
      ($cleft,$cright) = unpack("x$ip N N",$in);

      ($cipherl,$cipherr) = ($cleft,$cright);
      
      &main'DES_DO_DECRYPT($cleft, $cright, $temp, $schedule);

      $cleft ^= $ocipherl;
      $cright ^= $ocipherr;

      $length -= 8; $ip += 8;
      $op .= pack("N N",$cleft,$cright);
      ($ocipherl,$ocipherr) = ($cipherl,$cipherr);
      if ($length < 0) {
	last;
      }
    }
    $_[1] = $op;
  }
}

# cbc_cksum...
sub main'des_cbc_cksum { # (in, out, length, schedule, ivec);
  local($in, $out, $length, $schedule, $ivec)=@_;
  local($cleft,$cright,$xleft,$xright,$temp);

  ($cleft,$cright) = unpack("N N",$ivec);
    
  local($ip) = (0);
    
  while ($length > 0) {
    if ($length >= 8) {
      ($xleft,$xright) = unpack("x$ip N N",$in);
      $cleft ^= $xleft;
      $cright ^= $xright;
      $length -= 8; $ip += 8;
    } else {
      local($st);
#      $st=pack("x".(8-$length)." a$length",unpack("x$ip a$length",$in));
      $st=pack(" a$length"."x".(8-$length),unpack("x$ip a$length",$in));
      ($xleft,$xright) = unpack("N N",$st);
      $cleft ^= $xleft;
      $cright ^= $xright;
      $length = 0;
    }
    &main'DES_DO_ENCRYPT($cleft, $cright, $temp, $schedule);
  }
  $_[1] = pack("N N",$cleft,$cright);
  $cright;
}



## now throw in the MIT string_to_key...

sub fix_parity_byte { # use $_[0]
  local($a,$b);
  $a = $_[0] >> 1;
  $a = (($a & 0xf0)>>4) ^ ($a & 0xf);
  $a = (($a & 0xc)>>2) ^ ($a & 0x3);
  $a = (($a & 0x2)>>1) ^ ($a & 0x1);
  $b = $_[0] & ~1;
  if(!$a) { $b |= 1; }
  $b;
}

sub fix_parity_key { # use $_[0]
  local($kk,$okk);
  $okk = "";
  for $kk (unpack("C8",$_[0])) {
     $okk .= pack("C",&fix_parity_byte($kk));
  }
  $okk;
}

sub main'string2key {
  local($string) = @_;
  local($k,$j,$i,$temp,$forward);
  local(@p,$p,$ky);
  $forward = 1;
  $p = 0; $i=1;

  @strchars=unpack("C*",$string);
  for $temp (@strchars) {
    for $j (0..6) {
      if($forward) {
        $p[$p++] ^= $temp & 1;
      } else {
        $p[--$p] ^= $temp & 1;
      }
      $temp >>= 1;
    }
    if (($i % 8) == 0) { $forward = !$forward; }
    $i++;
  }  
  $p = 0;
  for $i (0..7) {
    $temp = 0;
    for $j (0..6) {
      $temp |= $p[$p++] << (1+$j);
    }
    $ky .= pack("C",$temp);
  }

  $ky=&fix_parity_key($ky);

  local($sched,$outdat);
  $sched=""; $outdat = "";
  &main'make_key_sched($ky,$sched);
  &main'des_cbc_cksum($string, $outdat, length($string), $sched, $ky);
  $outdat=&fix_parity_key($outdat);


$outdat;
}


#pcbc_encrypt is almost the same as cbc_encrypt...
sub main'des_pcbc_encrypt { # (in, out, length, schedule, ivec, encrypt);
  local($in, $out, $length, $schedule, $ivec, $encrypt)=@_;
  if($encrypt) {
    local($cleft,$cright,$i,$xleft,$xright,$temp);
    ($cleft,$cright) = unpack("N N",$ivec);
    
    local($ip,$op) = (0,"");
    
    while ($length > 0) {
      if ($length >= 8) {
	($xleft,$xright) = unpack("x$ip N N",$in);
	$cleft ^= $xleft;
	$cright ^= $xright;
	$length -= 8; $ip += 8;
      } else {
	($xleft,$xright) = unpack("x$ip N N",pack("a$ip x8",$in));
	$cleft ^= $xleft;
	$cright ^= $xright;
	$length = 0;
      }
      &main'DES_DO_ENCRYPT($cleft, $cright, $temp, $schedule);
      $op .= pack("N N",$cleft,$cright);
	$cleft ^= $xleft;	# only difference from cbc_encrypt...
	$cright ^= $xright;	# only difference from cbc_encrypt...
    }
    $_[1] = $op;
  } else {
    local($cleft,$cright,$i,$ocipherl,$ocipherr,$temp,$cipherl,$cipherr);
    ($ocipherl,$ocipherr) = unpack("N N",$ivec);

    local($ip,$op) = (0,"");

    for (;;) {
      ($cleft,$cright) = unpack("x$ip N N",$in);

      ($cipherl,$cipherr) = ($cleft,$cright);
      
      &main'DES_DO_DECRYPT($cleft, $cright, $temp, $schedule);

      $cleft ^= $ocipherl;
      $cright ^= $ocipherr;

      $length -= 8; $ip += 8;
      $op .= pack("N N",$cleft,$cright);
      ($ocipherl,$ocipherr) = ($cipherl^$cleft,$cipherr^$cright);
      if ($length < 0) {
	last;
      }
    }
    $_[1] = $op;
  }
}


1;
