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