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