-- EncryptedIOStreamImpl.mesa -- last edit June 22, 1984 1:06:02 pm PDT Sturgis DIRECTORY Basics USING[BITAND, BITOR, BITXOR], EncryptedIOStream USING[], FS USING[StreamOpen], IO USING[Close, CreateStreamProcs, EndOf, GetChar, STREAM, STREAMRecord, PutChar, StreamProcs], Rope USING[Fetch, Length, ROPE]; EncryptedIOStreamImpl: PROGRAM IMPORTS Basics, FS, IO, Rope EXPORTS EncryptedIOStream = BEGIN -- global EncryptedStreamData: TYPE = REF EncryptedStreamDataBody; EncryptedStreamDataBody: TYPE = RECORD[ ordinaryStream: IO.STREAM, x13, x33: CARDINAL, A33: ARRAY [1..33] OF CARDINAL, y41, y20: CARDINAL, B41: ARRAY[1..41] OF CARDINAL, recent: REF RecentBody ]; RecentBody: TYPE = RECORD[x: CARDINAL, chars: SEQUENCE maxN: CARDINAL OF CHARACTER]; -- output stream EncryptedOutStreamProcs: REF IO.StreamProcs ← IO.CreateStreamProcs[variety: output, class: $NewCalcEncryption, putChar: EncryptedOut]; CreateEncryptedOutStream: PUBLIC PROCEDURE[to: IO.STREAM, key: Rope.ROPE] RETURNS[IO.STREAM] = {RETURN[NEW[IO.STREAMRecord ← [EncryptedOutStreamProcs, CreateEncryptedStreamData[to, key]]]]}; EncryptedOut: SAFE PROCEDURE[self: IO.STREAM, char: CHARACTER] = TRUSTED BEGIN data: EncryptedStreamData ← NARROW[self.streamData]; data.recent.x ← data.recent.x+1; IF data.recent.x = data.recent.maxN THEN data.recent.x ← 0; data.recent.chars[data.recent.x] ← char; data.ordinaryStream.PutChar[CodeChar[data, char]]; END; -- intput stream EncryptedInStreamProcs: REF IO.StreamProcs ← IO.CreateStreamProcs[variety: input, class: $NewCalcEncryption, getChar: DecryptedGetChar, endOf: DecryptedEndOf]; CreateDecryptedInStream: PUBLIC PROCEDURE[from: IO.STREAM, key: Rope.ROPE] RETURNS[IO.STREAM] = {RETURN[NEW[IO.STREAMRecord ← [EncryptedInStreamProcs, CreateEncryptedStreamData[from, key]]]]}; DecryptedGetChar: SAFE PROCEDURE[self: IO.STREAM] RETURNS[CHARACTER] = TRUSTED BEGIN data: EncryptedStreamData ← NARROW[self.streamData]; data.recent.x ← data.recent.x+1; IF data.recent.x = data.recent.maxN THEN data.recent.x ← 0; RETURN[data.recent.chars[data.recent.x] ← CodeChar[data, data.ordinaryStream.GetChar[]]]; END; DecryptedEndOf: SAFE PROCEDURE[self: IO.STREAM] RETURNS[BOOLEAN] = TRUSTED BEGIN data: EncryptedStreamData ← NARROW[self.streamData]; RETURN[data.ordinaryStream.EndOf[]]; END; -- common CreateEncryptedStreamData: PROCEDURE[stream: IO.STREAM, key: Rope.ROPE] RETURNS[EncryptedStreamData] = BEGIN data: EncryptedStreamData ← NEW[EncryptedStreamDataBody]; data.ordinaryStream ← stream; Seed33[data, key]; Seed41[data, key]; data.recent ← NEW[RecentBody[100]]; data.recent.x ← data.recent.maxN-1; FOR I: CARDINAL IN [0..data.recent.maxN) DO data.recent.chars[I] ← '| ENDLOOP; RETURN[data]; END; CodeChar: PROCEDURE[data: EncryptedStreamData, char: CHARACTER] RETURNS[CHARACTER] = BEGIN r: CARDINAL ← Basics.BITXOR[Random33[data], Random41[data]]; xr: CARDINAL ← r/400B; coded: CARDINAL ← Basics.BITAND[Basics.BITXOR[LOOPHOLE[char, CARDINAL], xr], 377B]; codedChar: CHARACTER ← LOOPHOLE[coded, CHARACTER]; RETURN[codedChar]; END; -- random 33 code -- note (13, 33) come from the table on page 28 of Knuth Volumn 2 second edition. Random33: PROCEDURE[data: EncryptedStreamData] RETURNS[CARDINAL] = BEGIN r: CARDINAL; r ← data.A33[data.x33] ← data.A33[data.x33] + data.A33[data.x13]; data.x33 ← IF data.x33 = 1 THEN 33 ELSE data.x33-1; data.x13 ← IF data.x13 = 1 THEN 33 ELSE data.x13-1; RETURN[r]; END; Seed33: PROCEDURE[data: EncryptedStreamData, key: Rope.ROPE] = BEGIN i: CARDINAL; FOR i IN [1..33] DO data.A33[i] ← 0 ENDLOOP; i ← 33; FOR j: INT IN [0..Rope.Length[key]) DO data.A33[i] ← LOOPHOLE[Rope.Fetch[key, j],CARDINAL]; i ← IF i = 1 THEN 33 ELSE i - 1; ENDLOOP; data.A33[33] ← Basics.BITOR[data.A33[33], 1]; -- make sure at least one low order bit is non 0 data.x33 ← 33; data.x13 ← 13; THROUGH [1..2000] DO [] ← Random33[data] ENDLOOP; END; -- random 41 code -- note (41, 20) were chosen from the table on page 28 of Knuth volumn 2 second edition so as not to have any common factors with 13 or 33. Random41: PROCEDURE[data: EncryptedStreamData] RETURNS[CARDINAL] = BEGIN r: CARDINAL; r ← data.B41[data.y41] ← data.B41[data.y41] + data.B41[data.y20]; data.y41 ← IF data.y41 = 1 THEN 41 ELSE data.y41-1; data.y20 ← IF data.y20 = 1 THEN 41 ELSE data.y20-1; RETURN[r]; END; Seed41: PROCEDURE[data: EncryptedStreamData, key: Rope.ROPE] = BEGIN i: CARDINAL; FOR i IN [1..41] DO data.B41[i] ← 0 ENDLOOP; i ← 41; FOR j: INT IN [0..Rope.Length[key]) DO data.B41[i] ← LOOPHOLE[Rope.Fetch[key, j],CARDINAL]; i ← IF i = 1 THEN 41 ELSE i - 1; ENDLOOP; data.B41[41] ← Basics.BITOR[data.B41[41], 1]; -- make sure at least one low order bit is non 0 data.y41 ← 41; data.y20 ← 20; THROUGH [1..3000] DO [] ← Random41[data] ENDLOOP; END; -- test code Test: PROCEDURE[key: Rope.ROPE] = BEGIN testIn: IO.STREAM ← FS.StreamOpen["TestIn"]; testOut: IO.STREAM ← FS.StreamOpen["TestOut"]; decryptedIn: IO.STREAM ← CreateDecryptedInStream[testIn, key]; WHILE NOT decryptedIn.EndOf[] DO testOut.PutChar[decryptedIn.GetChar[]] ENDLOOP; testOut.Close[]; END; CopyDecrypted: PROCEDURE[from: Rope.ROPE, to: Rope.ROPE, key: Rope.ROPE] = BEGIN fromStream: IO.STREAM ← FS.StreamOpen[from]; toStream: IO.STREAM ← FS.StreamOpen[to, write]; decrypted: IO.STREAM ← CreateDecryptedInStream[fromStream, key]; WHILE NOT decrypted.EndOf[] DO toStream.PutChar[decrypted.GetChar[]] ENDLOOP; toStream.Close[]; END; -- main code END.. -- 14-Mar-82 10:28:05: Sturgis, started EncryptedIOStreamImpl.mesa -- remark: 14-Mar-82 10:53:23: basic stream structure completed -- RTE: 14-Mar-82 11:40:57: used out procs for in stream. -- Remark: 14-Mar-82 11:46:27: successfully decrypted one of my previoiusly encrypted journals. Thus single random scheme now working. -- remark: 14-Mar-82 11:59:47: added the random 41 code, and invoked it along with the ndom 33 code. -- RTE: 14-Mar-82 12:49:21: forgot to call Seed41. -- change: 14-Mar-82 13:37:07: due to confusion, save coded char in a place where it can be seen -- change: July 31, 1982 1:37 pm: change to Cedar 3.2 -- change: October 10, 1982 11:41 am: convert to cedar 3.4 (IO.CreateFileStream becomes FileIO.Open). -- change: October 10, 1982 3:11 pm: add code to save last 100 chars, unencrypted. -- change: October 10, 1982 3:35 pm: add code to copy from an encrypted file to decrypted file. -- Change: June 22, 1984 1:05:54 pm PDT: conversion to Cedar 5.2