-- 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.