-- Copyright (C) 1982, 1983, 1984 by Xerox Corporation. All rights reserved. -- DESFace.mesa -- Michael D. Schroeder, October 30, 1981 3:11 PM -- MESA access to the DES encryption facilities. -- JMaloney, 23-Jan-84 15:53:15. -- JMaloney, 22-Dec-83 14:59:34. -- JMaloney, 28-Oct-83 17:08:04. -- Andrew Birrell, 16-Mar-82 8:36:51. DESFace: DEFINITIONS = BEGIN -- Public types and constants -- Block: TYPE = ARRAY [0..3] OF UNSPECIFIED; -- Plain or cipher text block. Blocks: TYPE = LONG POINTER TO ARRAY OF Block; Key: TYPE = PACKED ARRAY [0..7] OF MACHINE DEPENDENT RECORD [ b: [0..127], p: [0..1]]; nullKey: Key = ALL[[b: 0, p: 0]]; -- Note that this has incorrect parity! IV: TYPE = Block; -- Initialization vector for CBC. hardwareExists: READONLY BOOLEAN; -- TRUE if the there is DES hardware available. -- Errors -- BadKey: ERROR; -- Key parity bad: may be raised by any en/decryption proc. -- Utilities -- CheckKeyParity: PROC [keyP: LONG POINTER TO DESFace.Key] RETURNS [ok: BOOLEAN]; -- Returns TRUE iff the key parity is OK. CorrectParity: PROC [keyP: LONG POINTER TO Key]; -- Forces parity bits of keyP­ to odd parity. GetRandomIV: PROC RETURNS [IV]; GetRandomKey: PROC RETURNS [Key]; -- Returns pseudo-random or random key (depending on -- available hardware), with odd parity. MakeKey: PROC [source: LONG STRING] RETURNS [Key]; -- Canonical conversion of characters into key; -- Parity bits are set to odd parity. parityTable: PACKED ARRAY [0..127] OF [0..1] = [ 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0, 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, 1,0,0,1,0,1,1,0,0,1,1,0,1,0,0,1, 0,1,1,0,1,0,0,1,1,0,0,1,0,1,1,0]; ValidateEncryptionHardware: PROC RETURNS [ok: BOOLEAN]; -- Runs the DES chip through its paces and makes sure -- it is doing correct en/decryption. -- Single-block encryption and decryption -- EncryptBlock: PROC [key: Key, from, to: LONG POINTER TO Block] = INLINE { CryptData[@key, 1, LOOPHOLE[from], LOOPHOLE[to], encrypt, ecb, NIL]}; DecryptBlock: PROC [key: Key, from, to: LONG POINTER TO Block] = INLINE { CryptData[@key, 1, LOOPHOLE[from], LOOPHOLE[to], decrypt, ecb, NIL]}; -- Encryption and decryption of runs of blocks -- -- "from" and "to" must be totally disjoint or fully congruent! -- For speed, try to align them on quad-word boundaries. -- There are three modes: ECB, CBC and CBCCheck. -- "ECB" = Electronic Code Book: -- No state is kept between 64-bit blocks. -- -- "CBC" = Cipher Block Chaining: -- 64-bits of state is kept between blocks. -- This state is XOR-ed with the next incoming clear text block -- before encryption. -- seed is the 64-bit initial value of this state. -- Encryption: -- to[0] ¬ Encrypt[from[0] XOR seed] -- to[i] ¬ Encrypt[from[i] XOR to[i-1]] -- Decryption: -- to[0] ¬ Decrypt[from[0]] XOR seed -- to[i] ¬ Decrypt[from[i]] XOR from[i-1] -- -- "CBCCheck" = Cipher Block Chaining with Checksum: -- Encryption: -- Let chk ¬ 64-bit XOR of from[0] through from[last-1]. -- to[0] ¬ Encrypt[from[0] XOR seed] -- to[i] ¬ Encrypt[from[i] XOR to[i-1]] -- to[last] ¬ Encrypt[from[last] XOR to[last-1] XOR chk] -- Decryption: -- to[0] ¬ Decrypt[from[0]] XOR seed -- to[i] ¬ Decrypt[from[i]] XOR from[i-1] -- Let chk ¬ 64-bit XOR of to[0] through to[last-1]. -- to[last] ¬ Decrypt[from[last]] XOR from[last-1] XOR chk -- -- After decryption to[last] should hold its initial value. -- If any cipher text is modified by an intruder, this is likely to -- alter decrypted to[last]: for any change to cipher text, each bit -- in to[last] is altered with probability 1/2. So if the client -- can verify N bits of to[last], he can detect modified cipher -- text with probability (1 - 2­-N). ECBEncrypt: PROC [key: Key, nBlks: CARDINAL, from, to: Blocks] = INLINE { CryptData[@key, nBlks, from, to, encrypt, ecb]}; ECBDecrypt: PROC [key: Key, nBlks: CARDINAL, from, to: Blocks] = INLINE { CryptData[@key, nBlks, from, to, decrypt, ecb]}; CBCEncrypt: PROC [key: Key, nBlks: CARDINAL, from, to: Blocks, seed: IV] = INLINE {CryptData[@key, nBlks, from, to, encrypt, cbc, @seed]}; CBCDecrypt: PROC [key: Key, nBlks: CARDINAL, from, to: Blocks, seed: IV] = INLINE {CryptData[@key, nBlks, from, to, decrypt, cbc, @seed]}; CBCCheckEncrypt: PROC [key: Key, nBlks: CARDINAL, from, to: Blocks, seed: IV] = INLINE {CryptData[@key, nBlks, from, to, encrypt, cbcCheck, @seed]}; CBCCheckDecrypt: PROC [key: Key, nBlks: CARDINAL, from, to: Blocks, seed: IV] = INLINE {CryptData[@key, nBlks, from, to, decrypt, cbcCheck, @seed]}; Checksum: PROC [key: Key, nBlks: CARDINAL, from: Blocks, seed: IV] RETURNS [newSeed: IV] = INLINE { CryptData[@key, nBlks, from, NIL, encrypt, checksum, @seed]; RETURN[seed]}; -- Calculate CBC checksum function; "from" is undisturbed. -- Internal types and procedures Direction: PRIVATE TYPE = {encrypt, decrypt}; Mode: PRIVATE TYPE = {ecb, cbc, cbcCheck, checksum}; CryptData: PRIVATE PROC [ keyP: POINTER TO Key, nBlks: CARDINAL, from, to: Blocks, direction: Direction, mode: Mode, seedP: LONG POINTER TO IV ¬ NIL]; END. LOG JMaloney -- October 28, 1983 -- Converted to Klamath and merged in DESFaceExtras. JMaloney -- December 22, 1983 -- Added ValidateEncryptionHardware. JMaloney -- January 20, 1983 -- Clarified comments.