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