-- 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]];