-- Copyright (C) 1983, 1984  by Xerox Corporation. All rights reserved. 
-- DESSoft.mesa
-- JMaloney, 19-Jul-83 19:16:35.

-- Last modified: JMaloney, 23-Feb-84 21:31:51.

-- Software implementation of DES Face operations.
--
-- Performance notes:
--    MakeInternalKeys takes ~10.0 milliseconds.
--    CryptABlock takes ~11.1 milliseconds.
-- The following times all assume that the internal keys are in the cache.
--    ECB takes ~11.2 milliseconds/block (1 block test case).
--    CryptData (ECB Mode) takes ~11.6 milliseconds/block (1 block test case).
--    CBC takes ~12.1 milliseconds/block (10 block test case).
--    CBCCheck takes ~12.2 milliseconds/block (10 block test case).
-- (Measured on a DLion with 384K words, using System.GetClockPulses.)

DIRECTORY
  DESFace USING [Blocks, CheckKeyParity, Direction, IV, Key, Mode, nullKey],
  Environment USING [Byte, Word],
  Inline USING [BITXOR, DBITXOR];

DESSoft: MONITOR
  IMPORTS DESFace, Inline
  EXPORTS DESFace =
  BEGIN


-- Public data --


hardwareExists: PUBLIC BOOLEAN ← FALSE;
  -- If the hardware existed, we wouldn't be using this head!!


-- Types --


  DESBlock: TYPE = Rec64;
  DESBlocksPtr: TYPE = LONG POINTER TO ARRAY OF DESBlock;
  DESKey: TYPE = Rec64;
  IntKeys: TYPE = ARRAY [1..16] OF Rec48;
  IVector: TYPE = Rec64;


-- Bit hacking type definitions --


  Bit: TYPE = [0..1];
  Nibble: TYPE = [0..15];

  Rec32: TYPE = MACHINE DEPENDENT RECORD [
    SELECT OVERLAID * FROM
    by1 => [bit: PACKED ARRAY [1..32] OF Bit],
    by4 => [nibble: PACKED ARRAY [1..8] OF Nibble],
    by8 => [byte: PACKED ARRAY [1..4] OF Environment.Byte],
    by16 => [word1, word2: Environment.Word],
    leftShift28in => [
      f1in: [0..1],
      f2in: [0..77777B],
      f3in: [0..1],
      f4in: [0..3777B],
      extraBits: [0..15]], -- not interesting
    leftShift28out => [
      f1out: [0..77777B],
      f2out: [0..1],
      f3out: [0..3777B],
      f4out: [0..1],
      extraBits: [0..15]],  -- not interesting
    double => [long: LONG UNSPECIFIED]
    ENDCASE];

  Rec48: TYPE = MACHINE DEPENDENT RECORD [
    SELECT OVERLAID * FROM
    by1 => [bit: PACKED ARRAY [1..48] OF Bit],
    by4 => [nibble: PACKED ARRAY [1..12] OF Nibble],
    by8 => [byte: PACKED ARRAY [1..6] OF Environment.Byte],
    sixes => [
      s1, s2: [0..63],
      s31: [0..15],
      s32: [0..3],
      s4, s5: [0..63],
      s61: [0..3],
      s62: [0..15],
      s7, s8: [0..63]],
    by16 => [word: PACKED ARRAY [1..3] OF Environment.Word],
    a32a16 => [big: LONG CARDINAL, small: CARDINAL],
    ENDCASE];

  Rec64: TYPE = MACHINE DEPENDENT RECORD [
    SELECT OVERLAID * FROM
    by1 => [bit: PACKED ARRAY [1..64] OF Bit],
    by4 => [nibble: PACKED ARRAY [1..16] OF Nibble],
    by8 => [byte: PACKED ARRAY [1..8] OF Environment.Byte],
    by16 => [word: PACKED ARRAY [1..4] OF Environment.Word],
    by32 => [long: PACKED ARRAY [1..2] OF LONG CARDINAL],
    ENDCASE];

  Sbox: TYPE = PACKED ARRAY [0..63] OF Nibble;


-- Constants --


  sbox1: Sbox = [
    14, 0, 4, 15, 13, 7, 1, 4, 2, 14, 15, 2, 11, 13, 8, 1, 3, 10, 10, 6, 6, 12,
    12, 11, 5, 9, 9, 5, 0, 3, 7, 8, 4, 15, 1, 12, 14, 8, 8, 2, 13, 4, 6, 9, 2, 1,
    11, 7, 15, 5, 12, 11, 9, 3, 7, 14, 3, 10, 10, 0, 5, 6, 0, 13];

  sbox2: Sbox = [
    15, 3, 1, 13, 8, 4, 14, 7, 6, 15, 11, 2, 3, 8, 4, 14, 9, 12, 7, 0, 2, 1, 13,
    10, 12, 6, 0, 9, 5, 11, 10, 5, 0, 13, 14, 8, 7, 10, 11, 1, 10, 3, 4, 15, 13,
    4, 1, 2, 5, 11, 8, 6, 12, 7, 6, 12, 9, 0, 3, 5, 2, 14, 15, 9];

  sbox3: Sbox = [
    10, 13, 0, 7, 9, 0, 14, 9, 6, 3, 3, 4, 15, 6, 5, 10, 1, 2, 13, 8, 12, 5, 7,
    14, 11, 12, 4, 11, 2, 15, 8, 1, 13, 1, 6, 10, 4, 13, 9, 0, 8, 6, 15, 9, 3, 8,
    0, 7, 11, 4, 1, 15, 2, 14, 12, 3, 5, 11, 10, 5, 14, 2, 7, 12];

  sbox4: Sbox = [
    7, 13, 13, 8, 14, 11, 3, 5, 0, 6, 6, 15, 9, 0, 10, 3, 1, 4, 2, 7, 8, 2, 5, 12,
    11, 1, 12, 10, 4, 14, 15, 9, 10, 3, 6, 15, 9, 0, 0, 6, 12, 10, 11, 1, 7, 13,
    13, 8, 15, 9, 1, 4, 3, 5, 14, 11, 5, 12, 2, 7, 8, 2, 4, 14];

  sbox5: Sbox = [
    2, 14, 12, 11, 4, 2, 1, 12, 7, 4, 10, 7, 11, 13, 6, 1, 8, 5, 5, 0, 3, 15, 15,
    10, 13, 3, 0, 9, 14, 8, 9, 6, 4, 11, 2, 8, 1, 12, 11, 7, 10, 1, 13, 14, 7, 2,
    8, 13, 15, 6, 9, 15, 12, 0, 5, 9, 6, 10, 3, 4, 0, 5, 14, 3];

  sbox6: Sbox = [
    12, 10, 1, 15, 10, 4, 15, 2, 9, 7, 2, 12, 6, 9, 8, 5, 0, 6, 13, 1, 3, 13, 4,
    14, 14, 0, 7, 11, 5, 3, 11, 8, 9, 4, 14, 3, 15, 2, 5, 12, 2, 9, 8, 5, 12, 15,
    3, 10, 7, 11, 0, 14, 4, 1, 10, 7, 1, 6, 13, 0, 11, 8, 6, 13];

  sbox7: Sbox = [
    4, 13, 11, 0, 2, 11, 14, 7, 15, 4, 0, 9, 8, 1, 13, 10, 3, 14, 12, 3, 9, 5, 7,
    12, 5, 2, 10, 15, 6, 8, 1, 6, 1, 6, 4, 11, 11, 13, 13, 8, 12, 1, 3, 4, 7, 10,
    14, 7, 10, 9, 15, 5, 6, 0, 8, 15, 0, 14, 5, 2, 9, 3, 2, 12];

  sbox8: Sbox = [
    13, 1, 2, 15, 8, 13, 4, 8, 6, 10, 15, 3, 11, 7, 1, 4, 10, 12, 9, 5, 3, 6, 14,
    11, 5, 0, 0, 14, 12, 9, 7, 2, 7, 2, 11, 1, 4, 14, 1, 7, 9, 4, 12, 10, 14, 8,
    2, 13, 0, 15, 6, 12, 10, 9, 13, 0, 15, 3, 3, 5, 5, 6, 8, 11];


-- Private procedures --


PermInitial: PROC [
  in: LONG POINTER TO Rec64, outLeft, outRight: POINTER TO Rec32] =
  BEGIN
    outLeft.bit ← [
      in.bit[58],  --1
      in.bit[50],  --2
      in.bit[42],  --3
      in.bit[34],  --4
      in.bit[26],  --5
      in.bit[18],  --6
      in.bit[10],  --7
      in.bit[2],   --8
      in.bit[60],  --9
      in.bit[52],  --10
      in.bit[44],  --11
      in.bit[36],  --12
      in.bit[28],  --13
      in.bit[20],  --14
      in.bit[12],  --15
      in.bit[4],   --16
      in.bit[62],  --17
      in.bit[54],  --18
      in.bit[46],  --19
      in.bit[38],  --20
      in.bit[30],  --21
      in.bit[22],  --22
      in.bit[14],  --23
      in.bit[6],   --24
      in.bit[64],  --25
      in.bit[56],  --26
      in.bit[48],  --27
      in.bit[40],  --28
      in.bit[32],  --29
      in.bit[24],  --30
      in.bit[16],  --31
      in.bit[8]];  --32

    outRight.bit ← [
      in.bit[57],  --1
      in.bit[49],  --2
      in.bit[41],  --3
      in.bit[33],  --4
      in.bit[25],  --5
      in.bit[17],  --6
      in.bit[9],   --7
      in.bit[1],   --8
      in.bit[59],  --9
      in.bit[51],  --10
      in.bit[43],  --11
      in.bit[35],  --12
      in.bit[27],  --13
      in.bit[19],  --14
      in.bit[11],  --15
      in.bit[3],   --16
      in.bit[61],  --17
      in.bit[53],  --18
      in.bit[45],  --19
      in.bit[37],  --20
      in.bit[29],  --21
      in.bit[21],  --22
      in.bit[13],  --23
      in.bit[5],   --24
      in.bit[63],  --25
      in.bit[55],  --26
      in.bit[47],  --27
      in.bit[39],  --28
      in.bit[31],  --29
      in.bit[23],  --30
      in.bit[15],  --31
      in.bit[7]];  --32
  END;

PermInverseInitial: PROC [
  inLeft, inRight: POINTER TO Rec32, out: LONG POINTER TO Rec64] =
  BEGIN
    out.bit ← [
      inRight.bit[8],  --1
      inLeft.bit[8],   --2
      inRight.bit[16], --3
      inLeft.bit[16],  --4
      inRight.bit[24], --5
      inLeft.bit[24],  --6
      inRight.bit[32], --7
      inLeft.bit[32],  --8
      inRight.bit[7],  --9
      inLeft.bit[7],   --10
      inRight.bit[15], --11
      inLeft.bit[15],  --12
      inRight.bit[23], --13
      inLeft.bit[23],  --14
      inRight.bit[31], --15
      inLeft.bit[31],  --16
      inRight.bit[6],  --17
      inLeft.bit[6],   --18
      inRight.bit[14], --19
      inLeft.bit[14],  --20
      inRight.bit[22], --21
      inLeft.bit[22],  --22
      inRight.bit[30], --23
      inLeft.bit[30],  --24
      inRight.bit[5],  --25
      inLeft.bit[5],   --26
      inRight.bit[13], --27
      inLeft.bit[13],  --28
      inRight.bit[21], --29
      inLeft.bit[21],  --30
      inRight.bit[29], --31
      inLeft.bit[29],  --32
      inRight.bit[4],  --33
      inLeft.bit[4],   --34
      inRight.bit[12], --35
      inLeft.bit[12],  --36
      inRight.bit[20], --37
      inLeft.bit[20],  --38
      inRight.bit[28], --39
      inLeft.bit[28],  --40
      inRight.bit[3],  --41
      inLeft.bit[3],   --42
      inRight.bit[11], --43
      inLeft.bit[11],  --44
      inRight.bit[19], --45
      inLeft.bit[19],  --46
      inRight.bit[27], --47
      inLeft.bit[27],  --48
      inRight.bit[2],  --49
      inLeft.bit[2],  --50
      inRight.bit[10],  --51
      inLeft.bit[10],  --52
      inRight.bit[18],  --53
      inLeft.bit[18],  --54
      inRight.bit[26],  --55
      inLeft.bit[26],  --56
      inRight.bit[1],  --57
      inLeft.bit[1],  --58
      inRight.bit[9],  --59
      inLeft.bit[9],  --60
      inRight.bit[17],  --61
      inLeft.bit[17],  --62
      inRight.bit[25],  --63
      inLeft.bit[25]];  --64
  END;

PermE: PROC [in: POINTER TO Rec32, out: POINTER TO Rec48] = INLINE
  BEGIN
    InputBits: TYPE = MACHINE DEPENDENT RECORD [
      SELECT OVERLAID * FROM
	a => [bit1: [0..1], trailing15Bits: [0..32767]],
	b => [bits1to5: [0..31], trailing11Bits: [0..2047]],
	c => [leading03Bits: [0..7], bits4to9: [0..63], trailing07Bits: [0..127]],
	d => [
	  leading03Bits: [0..7],
	  bits4to5: [0..3],
	  bits6to9: [0..15],
	  trailing07Bits: [0..127]],
	e => [leading07Bits: [0..127], bits8to13: [0..63], trailing03Bits: [0..7]],
	f => [
	  leading07Bits: [0..127],
	  bits8to11: [0..15],
	  bits12to13: [0..3],
	  trailing03Bits: [0..7]],
	g => [leading11Bits: [0..2047], bits12to16: [0..31]],
	h => [leading15Bits: [0..32767], bit16: [0..1]],
      ENDCASE];

    BEGIN
      OPEN inWord1: LOOPHOLE[in, POINTER TO InputBits]; -- saves one load
      inWord2: POINTER TO InputBits = LOOPHOLE[@in.word2];
      out↑ ← [
	sixes[
	  s1:  inWord2.bit16*32 + inWord1.bits1to5,
	  s2:  inWord1.bits4to9,
	  s31: inWord1.bits8to11,
	  s32: inWord1.bits12to13,
	  s4:  inWord1.bits12to16*2 + inWord2.bit1,
	  s5:  inWord1.bit16*32 + inWord2.bits1to5,
	  s61: inWord2.bits4to5,
	  s62: inWord2.bits6to9,
	  s7:  inWord2.bits8to13,
	  s8:  inWord2.bits12to16*2 + inWord1.bit1]];
      END;
    END;

SMap: PROC [in: POINTER TO Rec48, out: POINTER TO Rec32] = INLINE
  BEGIN
    out.nibble ← [
      sbox1[in.s1],
      sbox2[in.s2],
      sbox3[in.s31*4 + in.s32],
      sbox4[in.s4],
      sbox5[in.s5],
      sbox6[in.s61*16 + in.s62],
      sbox7[in.s7],
      sbox8[in.s8]];
  END;

PermP: PROC [in, out: POINTER TO Rec32] = INLINE
  BEGIN
    out.bit ← [
      in.bit[16],  --1
      in.bit[7],   --2
      in.bit[20],  --3
      in.bit[21],  --4
      in.bit[29],  --5
      in.bit[12],  --6
      in.bit[28],  --7
      in.bit[17],  --8
      in.bit[1],   --9
      in.bit[15],  --10
      in.bit[23],  --11
      in.bit[26],  --12
      in.bit[5],   --13
      in.bit[18],  --14
      in.bit[31],  --15
      in.bit[10],  --16
      in.bit[2],   --17
      in.bit[8],   --18
      in.bit[24],  --19
      in.bit[14],  --20
      in.bit[32],  --21
      in.bit[27],  --22
      in.bit[3],   --23
      in.bit[9],   --24
      in.bit[19],  --25
      in.bit[13],  --26
      in.bit[30],  --27
      in.bit[6],   --28
      in.bit[22],  --29
      in.bit[11],  --30
      in.bit[4],   --31
      in.bit[25]]; --32
  END;

PermPC1: PROC [in: POINTER TO Rec64, outC, outD: POINTER TO Rec32] = INLINE
  BEGIN
  -- converts 64 bit key into two 28 bit key parts.
  -- (Only first 28 bits of outC and outD are set.)
    outC.bit ← [
      in.bit[57],  --1
      in.bit[49],  --2
      in.bit[41],  --3
      in.bit[33],  --4
      in.bit[25],  --5
      in.bit[17],  --6
      in.bit[9],   --7
      in.bit[1],   --8
      in.bit[58],  --9
      in.bit[50],  --10
      in.bit[42],  --11
      in.bit[34],  --12
      in.bit[26],  --13
      in.bit[18],  --14
      in.bit[10],  --15
      in.bit[2],   --16
      in.bit[59],  --17
      in.bit[51],  --18
      in.bit[43],  --19
      in.bit[35],  --20
      in.bit[27],  --21
      in.bit[19],  --22
      in.bit[11],  --23
      in.bit[3],   --24
      in.bit[60],  --25
      in.bit[52],  --26
      in.bit[44],  --27
      in.bit[36], , , , ];  --28

    outD.bit ← [
      in.bit[63],  --1
      in.bit[55],  --2
      in.bit[47],  --3
      in.bit[39],  --4
      in.bit[31],  --5
      in.bit[23],  --6
      in.bit[15],  --7
      in.bit[7],   --8
      in.bit[62],  --9
      in.bit[54],  --10
      in.bit[46],  --11
      in.bit[38],  --12
      in.bit[30],  --13
      in.bit[22],  --14
      in.bit[14],  --15
      in.bit[6],   --16
      in.bit[61],  --17
      in.bit[53],  --18
      in.bit[45],  --19
      in.bit[37],  --20
      in.bit[29],  --21
      in.bit[21],  --22
      in.bit[13],  --23
      in.bit[5],   --24
      in.bit[28],  --25
      in.bit[20],  --26
      in.bit[12],  --27
      in.bit[4], , , , ];  --28
  END;

PermPC2: PROC [inC, inD: POINTER TO Rec32,
  out: LONG POINTER TO Rec48] = INLINE
  -- Converts inC, inD into an element of the internal key array.
  BEGIN
  out.bit ← [
    inC.bit[14],  --1
    inC.bit[17],  --2
    inC.bit[11],  --3
    inC.bit[24],  --4
    inC.bit[1],   --5
    inC.bit[5],   --6
    inC.bit[3],   --7
    inC.bit[28],  --8
    inC.bit[15],  --9
    inC.bit[6],   --10
    inC.bit[21],  --11
    inC.bit[10],  --12
    inC.bit[23],  --13
    inC.bit[19],  --14
    inC.bit[12],  --15
    inC.bit[4],   --16
    inC.bit[26],  --17
    inC.bit[8],   --18
    inC.bit[16],  --19
    inC.bit[7],   --20
    inC.bit[27],  --21
    inC.bit[20],  --22
    inC.bit[13],  --23
    inC.bit[2],   --24

    inD.bit[13],  --25
    inD.bit[24],  --26
    inD.bit[3],   --27
    inD.bit[9],   --28
    inD.bit[19],  --29
    inD.bit[27],  --30
    inD.bit[2],   --31
    inD.bit[12],  --32
    inD.bit[23],  --33
    inD.bit[17],  --34
    inD.bit[5],   --35
    inD.bit[20],  --36
    inD.bit[16],  --37
    inD.bit[21],  --38
    inD.bit[11],  --39
    inD.bit[28],  --40
    inD.bit[6],   --41
    inD.bit[25],  --42
    inD.bit[18],  --43
    inD.bit[14],  --44
    inD.bit[22],  --45
    inD.bit[8],   --46
    inD.bit[1],   --47
    inD.bit[4]];  --48
  END;

LoopBody: PROC [oldLeft32, oldRight32: Rec32,
  ithKeyPtr: LONG POINTER TO Rec48]
    RETURNS [newLeft32, newRight32: Rec32] = INLINE
  BEGIN
    temp48: Rec48;
    temp32, anotherTemp32: Rec32;

  -- new Left is old Right
    newLeft32 ← oldRight32;

  -- new Right is computed as follows:
  --   E-permute the old Right; XOR that with ith key array element;
  --   apply the SBoxes; P-permute that and XOR result with old Left

    PermE[@oldRight32, @temp48];
    temp48 ← [
      a32a16[
      big: Inline.DBITXOR[temp48.big, ithKeyPtr.big],
      small: Inline.BITXOR[temp48.small, ithKeyPtr.small] ]];
    SMap[@temp48, @temp32];
    PermP[@temp32, @anotherTemp32];
    newRight32.long ← Inline.DBITXOR[oldLeft32.long, anotherTemp32.long];
  END;

CryptABlock: PROC [
  internalKeys: LONG POINTER TO IntKeys, from, to: LONG POINTER TO DESBlock,
  direction: DESFace.Direction] =
  BEGIN
    left32, right32: Rec32;
    i: CARDINAL;

    PermInitial[from, @left32, @right32];
    IF direction = encrypt THEN
      FOR i IN [1..16] DO
	[left32, right32] ← LoopBody[left32, right32, @(internalKeys[i])];
      ENDLOOP
    ELSE -- direction = decrypt --
      FOR i DECREASING IN [1..16] DO
	[left32, right32] ← LoopBody[left32, right32, @(internalKeys[i])];
      ENDLOOP;
     PermInverseInitial[@right32, @left32, to];
  END;

-- A one key cache, to avoid recomputing internal keys unnecessarily
--   (This is monitored data!):
lastKeyUsed: DESKey ← LOOPHOLE[DESFace.nullKey];
lastIntKeys: IntKeys;
hits: LONG CARDINAL ← 0;
misses: LONG CARDINAL ← 0;

GetInternalKey: ENTRY PROC [
  key: POINTER TO DESKey, internalKeys: LONG POINTER TO IntKeys] =
  BEGIN
    ENABLE UNWIND => NULL; -- Just to be safe!
    IF (key↑ # lastKeyUsed) THEN
      BEGIN
	misses ← misses +1;
	lastKeyUsed ← key↑;
	MakeInternalKeys[key, @lastIntKeys];
      END
    ELSE hits ← hits + 1;
    internalKeys↑ ← lastIntKeys;
  END;

shifts: ARRAY [1..16] OF CARDINAL = [
  1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];

MakeInternalKeys: PROC [
  key: POINTER TO DESKey, internalKeys: LONG POINTER TO IntKeys] =
  BEGIN
    c: Rec32; cPtr: POINTER TO Rec32 ← @c;
    d: Rec32; dPtr: POINTER TO Rec32 ← @d;

    LeftShift28: PROC [in: POINTER TO Rec32] RETURNS [out: Rec32] = INLINE
      BEGIN
	out ← [
	  leftShift28out[
	    f1out: in.f2in, f2out: in.f3in, f3out: in.f4in, f4out: in.f1in,
	    extraBits: NULL]];
      END;

    PermPC1[key, cPtr, dPtr];
    FOR i: CARDINAL IN [1..16] DO
      THROUGH [1..shifts[i]] DO
	c ← LeftShift28[cPtr]; d ← LeftShift28[dPtr];
      ENDLOOP;
      PermPC2[cPtr, dPtr, @(internalKeys[i])];
    ENDLOOP;
  END;

XOR64: PROC [a, b, out: LONG POINTER TO Rec64] = INLINE
  BEGIN
    out.long[1] ← Inline.DBITXOR[a.long[1], b.long[1]];
    out.long[2] ← Inline.DBITXOR[a.long[2], b.long[2]];
  END;


-- Public procedures and errors --


BadKey: PUBLIC ERROR = CODE; -- Key parity bad.

CryptData: PUBLIC PROC [
  keyP: POINTER TO DESFace.Key, nBlks: CARDINAL,
  from, to: DESFace.Blocks, direction: DESFace.Direction,
  mode: DESFace.Mode, seedP: LONG POINTER TO DESFace.IV] =
  BEGIN
    IF ~(mode = checksum) THEN -- don't check key parity if we just want checksum
      IF ~DESFace.CheckKeyParity[keyP] THEN ERROR BadKey;
    SELECT mode FROM
      ecb =>
	ECB[
	  LOOPHOLE[keyP, POINTER TO DESKey], nBlks,
	  LOOPHOLE[from, DESBlocksPtr],
	  LOOPHOLE[to, DESBlocksPtr], direction];
      cbc =>
	CBC[
	  LOOPHOLE[keyP, POINTER TO DESKey], nBlks,
	  LOOPHOLE[from, DESBlocksPtr],
	  LOOPHOLE[to, DESBlocksPtr], direction,
	  LOOPHOLE[seedP, LONG POINTER TO IVector]];
      cbcCheck =>
	CBCCheck[
	  LOOPHOLE[keyP, POINTER TO DESKey], nBlks,
	  LOOPHOLE[from, DESBlocksPtr],
	  LOOPHOLE[to, DESBlocksPtr], direction,
	  LOOPHOLE[seedP, LONG POINTER TO IVector]];
      checksum =>
	XORChecksum[nBlks,
	  LOOPHOLE[from, DESBlocksPtr],
	  LOOPHOLE[seedP, LONG POINTER TO IVector]];
    ENDCASE => ERROR;
  END;


-- Support for different encryption modes --


ECB: PROC [
  key: POINTER TO DESKey, nBlks: CARDINAL, from, to: DESBlocksPtr,
  direction: DESFace.Direction] =
  BEGIN
    internalKeys: IntKeys;

    GetInternalKey[key, @internalKeys];
    FOR n: CARDINAL IN [0..nBlks) DO
      CryptABlock[@internalKeys, @from[n], @to[n], direction];
    ENDLOOP;
  END;

CBC: PROC [
  key: POINTER TO DESKey, nBlks: CARDINAL, from, to: DESBlocksPtr,
  direction: DESFace.Direction, seedP: LONG POINTER TO IVector] =
  BEGIN
    internalKeys: IntKeys;
    temp: Rec64;

    GetInternalKey[key, @internalKeys];

    IF direction = encrypt THEN
      BEGIN
     -- Do the first block:
	XOR64[a: @from[0], b: seedP, out: @temp];
	CryptABlock[@internalKeys, @temp, @to[0], encrypt];
     -- Do the rest:
	FOR n: CARDINAL IN [1..nBlks) DO
	  XOR64[a: @from[n], b: @to[n-1], out: @temp];
	  CryptABlock[@internalKeys, @temp, @to[n], encrypt];
	ENDLOOP;
      END
    ELSE -- direction = decrypt
      BEGIN
     -- Do all but the first block:
	FOR n: CARDINAL DECREASING IN [1..nBlks) DO
	  CryptABlock[@internalKeys, @from[n], @temp, decrypt];
	  XOR64[a: @temp, b: @from[n-1], out: @to[n]];
	ENDLOOP;
     -- Do the first block:
	CryptABlock[@internalKeys, @from[0], @temp, decrypt];
	XOR64[a: @temp, b: seedP, out: @to[0]];
      END;
  END;

CBCCheck: PROC [
  key: POINTER TO DESKey, nBlks: CARDINAL, from, to: DESBlocksPtr,
  direction: DESFace.Direction, seedP: LONG POINTER TO IVector] =
  BEGIN
    internalKeys: IntKeys;
    temp: Rec64;
    secondToLastCipherBlock: Rec64;
    chk: Rec64;

    IF nBlks = 0 THEN RETURN; -- Noop.
    IF nBlks = 1 THEN -- One block case is same as CBC.
      {CBC[key, nBlks, from, to, direction, seedP]; RETURN};

  -- Assume: nBlks >= 2 from here on.

  -- In case to↑ = from↑,
  --    Compute checksum BEFORE doing encryption, or
  --    Remember penultimate cipher block BEFORE doing decryption:
    IF direction = encrypt
      THEN XORChecksum[nBlks-1, from, @chk]
      ELSE secondToLastCipherBlock ← from[nBlks-2];

  -- Perform CBC en/decryption on first n-1 blocks:
    CBC[key, nBlks-1, from, to, direction, seedP];

  -- On encryption, to[n] ← Encrypt[from[n] XOR to[n-1] XOR chk]
  -- On decryption, to[n] ← Decrypt[from[n]] XOR from[n-1] XOR chk
  --    where in both cases chk is the XOR sum of the n-1 plaintext blocks:
    GetInternalKey[key, @internalKeys];
    IF direction = encrypt
      THEN
	BEGIN
	-- Assume: chk has been set
	  XOR64[a: @from[nBlks-1], b: @chk, out: @temp];
	  XOR64[a: @temp, b: @to[nBlks-2], out: @temp];
	  CryptABlock[@internalKeys, @temp, @to[nBlks-1], encrypt]
	END
      ELSE -- (direction = decrypt)
	BEGIN
	  XORChecksum[nBlks-1, to, @chk];
	  CryptABlock[@internalKeys, @from[nBlks-1], @temp, decrypt];
	  XOR64[a: @temp, b: @chk, out: @temp];
	  XOR64[a: @temp, b: @secondToLastCipherBlock, out: @to[nBlks-1]];
	END;
  END;

XORChecksum: PROC [
  nBlks: CARDINAL, from: DESBlocksPtr, out: LONG POINTER TO DESBlock] =
  BEGIN
    out↑.long ← [0,0];
    FOR n: CARDINAL IN [0..nBlks) DO
      XOR64[a: out, b: @from[n], out: out];
    ENDLOOP;
  END;


-- Initialization --


MakeInternalKeys[@lastKeyUsed, @lastIntKeys];


END.

LOG

May 28, 1982 -- JMaloney -- Incorporated Jerry Farrell's improvements.
June  8, 1982 -- JMaloney -- Fixed PermP and incorporated Jerry's fix for PermE.
June 15, 1982 -- JMaloney -- Made changes to match Crypt interface.
June 15, 1982 -- JMaloney -- Added MakeRandomKey, MakeRandomIVector.
June 16, 1982 -- JMaloney -- Added BulkEncrypt/Decrypt, CorrectParity.
June 24, 1982 -- JMaloney -- Fixed BulkEncrypt/Decrypt. They were running over by one block.
May 10, 1983 -- JMaloney -- Performance improvement in PermP. My "clever" code wasn't as fast as the straight-forward code.
May 12, 1983 -- JMaloney -- Renamed to DESSoft and made it implement DESFace.
May 19, 1983 -- JMaloney -- Fixed CBCCheck as per Andrew's message.
May 31, 1983 -- JMaloney -- Fixed bug in CheckKeyParity found by Bruce Malasky.
July 19, 1983 -- JMaloney -- Use parityTable in DESFace.
August 9, 1983 -- JMaloney -- Fixed CBCCheck.
October 31, 1983 -- JMaloney -- Removed references to DESFaceExtras.
January 20, 1983 -- JMaloney -- Added hardwareExists.
February 23, 1983 -- JMaloney -- Moved CheckKeyParity to DESAux.


-- Performance testing:

tStart, tFinish: LONG CARDINAL;
tLoop: LONG CARDINAL;
iterations: CARDINAL ← 1000;

t1, t2, t3: LONG CARDINAL ← 0;
t1InMicroseconds,
t2InMicroseconds,
t3InMicroseconds: LONG CARDINAL ← 0;

tempKeys: IntKeys;
tempBlock: DESBlock;
tempKey: DESKey;

tStart ← System.GetClockPulses[];
THROUGH [1..iterations] DO
ENDLOOP;
tFinish ← System.GetClockPulses[];
tLoop ← tFinish - tStart;

tStart ← System.GetClockPulses[];
THROUGH [1..iterations] DO
  CryptABlock[@tempKeys, @tempBlock, @tempBlock, encrypt];
ENDLOOP;
tFinish ← System.GetClockPulses[];
t1 ← tFinish - tStart - tLoop;
t1InMicroseconds ← System.PulsesToMicroseconds[[t1]];

tStart ← System.GetClockPulses[];
THROUGH [1..iterations] DO
  MakeInternalKeys[@tempKey, @tempKeys];
ENDLOOP;
tFinish ← System.GetClockPulses[];
t2 ← tFinish - tStart - tLoop;
t2InMicroseconds ← System.PulsesToMicroseconds[[t2]];

tStart ← System.GetClockPulses[];
THROUGH [1..iterations] DO
  ECB[@tempKey, 1, LOOPHOLE[LONG[@tempBlock]],
    LOOPHOLE[LONG[@tempBlock]], encrypt];
ENDLOOP;
tFinish ← System.GetClockPulses[];
t3 ← tFinish - tStart - tLoop;
t3InMicroseconds ← System.PulsesToMicroseconds[[t3]];