EncryptTool.mesa; User/client interface for DES encryption
Edited by McCreight, June 2, 1983 10:44 am
Edited by: Mitchell, May 23, 1983 5:44 pm
Edited by: McGregor, June 2, 1983 3:42 pm
DIRECTORY
Buttons,
Containers,
DESFace,
FileIO,
IO,
Menus,
MessageWindow,
PutGet,
Rope,
TEditDocument,
TiogaExtraOps,
TiogaOps,
VFonts,
ViewerClasses,
ViewerTools;
EncryptTool: CEDAR PROGRAM
IMPORTS
Buttons, Containers, DESFace, FileIO, IO, Menus, MessageWindow, PutGet, Rope, TiogaExtraOps, TiogaOps, VFonts, ViewerTools =
BEGIN OPEN IO;
DESId: ROPE = "*DES-1*";
DESBlkSize: INTEGER = 8;
EncryptionState: TYPE = RECORD [
seed: DESFace.Block ← ALL[0], -- last encrypted block
k: DESFace.Key,
position: [0..DESBlkSize] ← 0,
length: [0..DESBlkSize] ← 0,
source, dest: REF TEXT,
otherStream: STREAM
];
RefEncryptionState: TYPE = REF EncryptionState;
encryptedOutStreamProcs: REF StreamProcs = CreateRefStreamProcs[
putChar: PutCharEncrypted,
close: CloseOutputEncrypted,
name: "DES Encrypting Stream"];
CreateEncryptedOutStream: PUBLIC PROC [to: STREAM, key: Rope.ROPE] RETURNS [STREAM] =
BEGIN
state: RefEncryptionState = NEW[EncryptionState ← [
k: RopeToDESKey[key],
source: NEW[TEXT[DESBlkSize]],
dest: NEW[TEXT[DESBlkSize]],
otherStream: to]];
crypter: STREAM = CreateProcsStream[
streamProcs: encryptedOutStreamProcs,
streamData: state];
iv: REF TEXT = GetRandomText[];
state.source.length ← state.dest.length ← DESBlkSize;
to.PutRope[DESId]; -- encryption algorithm id, in the clear
crypter.PutText[iv];
FOR i: [0..20) IN [0..LOOPHOLE[iv[0], [0..255]] MOD 20) DO
crypter.PutChar[GetRandomText[][0]];
ENDLOOP;
RETURN[crypter];
END;
PutCharEncrypted: PROC [self: STREAM, char: CHAR] =
BEGIN
state: RefEncryptionState = NARROW[self.streamData];
state.source[state.position] ← char;
state.position ← state.position+1;
IF state.position = DESBlkSize THEN
TRUSTED BEGIN
DESFace.CBCEncrypt[key: state.k, nBlks: 1, seed: state.seed,
from: RefTextToBlocks[state.source], to: RefTextToBlocks[state.dest]];
state.seed ← RefTextToBlocks[state.dest][0];
state.otherStream.PutBlock[block: state.dest, stopIndexPlusOne: DESBlkSize];
state.position ← 0;
END;
END;
CloseOutputEncrypted: PROC [self: STREAM, abort: BOOLFALSE] =
BEGIN
r: REF TEXT = GetRandomText[];
self.PutChar['.];
FOR i: (0..DESBlkSize) IN (0..DESBlkSize) DO
self.PutChar[IF r[i]='. THEN '? ELSE r[i]];
ENDLOOP;
END;
decryptedInStreamProcs: REF StreamProcs = CreateRefStreamProcs[
getChar: GetCharEncrypted,
endOf: EndOfEncrypted,
name: "DES Decrypting Stream"];
CreateDecryptedInStream: PUBLIC PROC [from: STREAM, key: Rope.ROPE] RETURNS [STREAM] =
BEGIN
state: RefEncryptionState = NEW[EncryptionState ← [
k: RopeToDESKey[key],
source: NEW[TEXT[DESBlkSize]],
dest: NEW[TEXT[DESBlkSize]],
otherStream: from]];
decrypter: STREAM = CreateProcsStream[
streamProcs: decryptedInStreamProcs,
streamData: state];
state.source.length ← state.dest.length ← DESBlkSize;
BEGIN
start: CHAR;
FOR i: NAT IN [0..DESId.Length[]) DO
IF from.GetChar[ ! EndOfStream => GOTO BadSyntax]#DESId.Fetch[i] THEN
GOTO BadSyntax;
ENDLOOP;
start ← decrypter.GetChar[ ! EndOfStream => GOTO BadSyntax];
FOR i: [0..27) IN [0..7+(LOOPHOLE[start, NAT] MOD 20)) DO
[] ← decrypter.GetChar[ ! EndOfStream => GOTO BadSyntax];
ENDLOOP;
EXITS BadSyntax => ERROR BadEncryption;
END;
RETURN[decrypter];
END;
BadEncryption: PUBLIC ERROR = CODE;
GetCharEncrypted: PROC [self: STREAM] RETURNS [CHAR] =
BEGIN
state: RefEncryptionState = NARROW[self.streamData];
c: CHAR;
IF state.position>=state.length THEN FillSource[self, state];
c ← state.dest[state.position];
state.position ← (state.position+1) MOD DESBlkSize;
IF state.position = 0 THEN state.length ← 0; -- next char needs new block
RETURN[c];
END;
EndOfEncrypted: PROC [self: STREAM] RETURNS [BOOL] =
BEGIN
state: RefEncryptionState = NARROW[self.streamData];
IF state.position>=state.length THEN FillSource[self, state ! IO.EndOfStream => GOTO IsEnd];
RETURN[FALSE];
EXITS IsEnd => RETURN[TRUE];
END;
FillSource: PROC [self: STREAM, state: RefEncryptionState] =
BEGIN
bs: [0..DESBlkSize] ← 0;
WHILE bs<DESBlkSize AND NOT state.otherStream.EndOf[] DO
bs ← bs+state.otherStream.GetBlock[
block: state.source, startIndex: bs, stopIndexPlusOne: DESBlkSize
! EndOfStream => EXIT];
ENDLOOP;
SELECT bs FROM
0 => state.length ← 0;
<DESBlkSize => ERROR BadEncryption;
ENDCASE -- = DESBlkSize -- =>
TRUSTED BEGIN
DESFace.CBCDecrypt[key: state.k, nBlks: 1, seed: state.seed,
from: RefTextToBlocks[state.source], to: RefTextToBlocks[state.dest]];
state.seed ← RefTextToBlocks[state.source][0];
IF state.otherStream.EndOf[] THEN
BEGIN
state.length ← DESBlkSize-1;
WHILE state.dest[state.length]#'. AND state.length>0 DO state.length ← state.length-1 ENDLOOP;
END
ELSE state.length ← DESBlkSize;
END;
IF state.length=0 THEN ERROR EndOfStream[self];
END;
EncryptRope: PUBLIC PROC [plain, key: Rope.ROPE] RETURNS [cipher: Rope.ROPE] =
BEGIN
in, out, crypt: STREAM;
IF Rope.Size[plain]=0 THEN RETURN [""];
in ← RIS[plain];
out ← ROS[];
crypt ← CreateEncryptedOutStream[key: key, to: out];
DO
crypt.PutChar[in.GetChar[ ! EndOfStream => EXIT ]];
ENDLOOP;
in.Close[];
crypt.Close[];
cipher ← out.GetOutputStreamRope[];
out.Close[];
END;
DecryptRope: PUBLIC PROC [cipher, key: Rope.ROPE] RETURNS [plain: Rope.ROPE] =
BEGIN
in, out, decrypt: STREAM;
IF Rope.Size[cipher]=0 THEN RETURN [""];
in ← RIS[cipher];
out ← ROS[];
decrypt ← CreateDecryptedInStream[key: key, from: in];
DO
out.PutChar[decrypt.GetChar[ ! EndOfStream => EXIT ]];
ENDLOOP;
in.Close[];
decrypt.Close[];
plain ← out.GetOutputStreamRope[];
out.Close[];
END;
EncryptFile: PUBLIC PROC [key, plain, cipher: Rope.ROPE] =
BEGIN
in: STREAM = FileIO.Open[fileName: plain, raw: TRUE
! FileIO.OpenFailed =>
BEGIN OPEN MessageWindow;
Append["Can't open file """, TRUE];
Append[fileName, FALSE];
Append[""".", FALSE];
Blink[];
GOTO Punt;
END
];
out: STREAM = FileIO.Open[fileName: cipher, accessOptions: overwrite, raw: TRUE];
crypt: STREAM = CreateEncryptedOutStream[key: key, to: out];
byteCount: INT ← 0;
DO
crypt.PutChar[in.GetChar[ ! EndOfStream => EXIT ]];
byteCount ← byteCount+1;
ENDLOOP;
in.Close[];
crypt.Close[];
out.Close[];
EXITS Punt => NULL;
END;
DecryptFile: PUBLIC PROC [key, plain, cipher: Rope.ROPE] =
BEGIN
in: STREAM = FileIO.Open[fileName: cipher, raw: TRUE! FileIO.OpenFailed =>
BEGIN OPEN MessageWindow;
Append["Can't open file """, TRUE];
Append[fileName, FALSE];
Append[""".", FALSE];
Blink[];
GOTO Punt;
END
];
out: STREAM = FileIO.Open[fileName: plain, accessOptions: overwrite, raw: TRUE];
decrypt: STREAM = CreateDecryptedInStream[key: key, from: in];
byteCount: INT ← 0;
DO
out.PutChar[decrypt.GetChar[ ! EndOfStream => EXIT ]];
byteCount ← byteCount+1;
ENDLOOP;
in.Close[];
decrypt.Close[];
out.Close[];
EXITS Punt => NULL;
END;
GetRandomText: PROC RETURNS [REF TEXT] =
BEGIN
t: REF TEXT = NEW[TEXT[DESBlkSize]];
t.length ← DESBlkSize;
TRUSTED {RefTextToBlocks[t][0] ← DESFace.GetRandomIV[]};
RETURN[t];
END;
RopeToDESKey: PROC [key: Rope.ROPE] RETURNS [k: DESFace.Key] =
TRUSTED {RETURN[DESFace.MakeKey[LOOPHOLE[Rope.ToRefText[key]]]]};
RefTextToBlocks: PROC [t: REF TEXT] RETURNS [DESFace.Blocks] = INLINE
{TRUSTED {RETURN[LOOPHOLE[LOOPHOLE[t, LONG POINTER]+SIZE[TEXT[0]]]]}};
T o o l t o e n c r y p t / d e c r y p t T i o g a N o d e s
EncryptHandle: TYPE = REF EncryptToolRec;
EncryptToolRec: TYPE = RECORD [
outer: Containers.Container ← NIL,
key: Rope.ROPENIL,
keyViewer: ViewerClasses.Viewer ← NIL,
files: ViewerClasses.Viewer ← NIL
];
EncryptedText: TYPE = Rope.ROPE;
SelectKey: Buttons.ButtonProc =
BEGIN
handle: EncryptHandle = NARROW[clientData];
ViewerTools.SetContents[handle.keyViewer, handle.key];
ViewerTools.SetSelection[handle.keyViewer];
handle.keyViewer.newVersion ← TRUE; -- forces stars to return on button op
END;
SelectFiles: Buttons.ButtonProc =
BEGIN
handle: EncryptHandle = NARROW[clientData];
ViewerTools.SetSelection[handle.files];
END;
GetKey: PROC [handle: EncryptHandle] RETURNS [Rope.ROPE] =
BEGIN
IF handle.keyViewer.newVersion THEN
BEGIN
handle.key ← ViewerTools.GetContents[handle.keyViewer];
ViewerTools.SetContents[handle.keyViewer, "*****"];
handle.keyViewer.newVersion ← FALSE;
END;
RETURN[handle.key];
END;
EncryptFiles: Buttons.ButtonProc =
BEGIN
handle: EncryptHandle = NARROW[clientData];
key: Rope.ROPE = GetKey[handle];
inputs: IO.STREAM = IO.RIS[ViewerTools.GetContents[handle.files]];
sourceFile: Rope.ROPE ← inputs.GetToken[IO.IDProc
! IO.EndOfStream => sourceFile ← NIL];
IF Rope.Length[key]=0 THEN BEGIN
MessageWindow.Append["Please enter an encryption key", TRUE];
MessageWindow.Blink[];
END
ELSE
WHILE sourceFile#NIL DO
destFile: Rope.ROPE = Rope.Concat[sourceFile, ".des"];
EncryptFile[plain: sourceFile, cipher: destFile, key: key];
sourceFile ← inputs.GetToken[IO.IDProc
! IO.EndOfStream => sourceFile ← NIL];
ENDLOOP;
END;
DecryptFiles: Buttons.ButtonProc =
BEGIN
handle: EncryptHandle = NARROW[clientData];
key: Rope.ROPE = GetKey[handle];
outputs: IO.STREAM = IO.RIS[ViewerTools.GetContents[handle.files]];
destFile: Rope.ROPE ← outputs.GetToken[IO.IDProc
! IO.EndOfStream => destFile ← NIL];
IF Rope.Length[key]=0 THEN BEGIN
MessageWindow.Append["Please enter an encryption key", TRUE];
MessageWindow.Blink[];
END
ELSE
WHILE destFile#NIL DO
sourceFile: Rope.ROPE = Rope.Concat[destFile, ".des"];
DecryptFile[plain: destFile, cipher: sourceFile, key: key
! BadEncryption =>
BEGIN OPEN MessageWindow;
Append["File """, TRUE];
Append[sourceFile, FALSE];
Append[""" does not have the form of a DES encrypted file.", FALSE];
Blink[];
CONTINUE;
END;
];
destFile ← outputs.GetToken[IO.IDProc
! IO.EndOfStream => destFile ← NIL];
ENDLOOP;
END;
EncryptSelection: Buttons.ButtonProc =
BEGIN OPEN TiogaOps;
Encrypt: PROC [root: TiogaOps.Ref] =
BEGIN
[start: start, end: end, viewer: viewer] ← GetSelection[];
ProcessSelection[root, start.node, end.node, EncryptBranch, key, viewer];
END;
EncryptBranch: PROC [node: TiogaOps.Ref, key: Rope.ROPE] =
BEGIN
IF GetProp[node, $EncryptedChars]=NIL THEN
TRUSTED BEGIN
doNext: BOOL;
cipher: Rope.ROPE;
TiogaOps.SetSelection[viewer, [node, 0], [node, 0], point, TRUE, TRUE];
point selection at start of branch to be encrypted
doNext ← Rope.Size[TiogaOps.GetRope[TiogaOps.GetCaret[].node]] # 0;
TiogaOps.Break[]; -- insert a fake root node in front of the branch
node ← TiogaOps.GetCaret[].node;
IF doNext THEN node ← TiogaOps.Next[node];
TiogaOps.SelectBranches[viewer, node, node, node, TRUE, TRUE]; -- select the branch
TiogaOps.Nest[]; -- nest it under the extra node
node ← TiogaOps.Parent[TiogaOps.GetCaret[].node]; -- get the extra node again
cipher ← EncryptRope[plain: PutGet.ToRope[LOOPHOLE[node]].output, key: key];
TiogaOps.SelectBranches[viewer, node, node, node, TRUE, TRUE];
select the whole thing pending delete
TiogaOps.SetLooks["is", caret];
TiogaOps.InsertRope["...encrypted..."];
PutProp[n: TiogaOps.GetCaret[].node, name: $EncryptedChars, value: cipher];
END
ELSE
BEGIN OPEN MessageWindow;
Append["Node already encrypted", TRUE];
Blink[];
END;
END;
start, end: Location;
viewer: ViewerClasses.Viewer;
handle: EncryptHandle = NARROW[clientData];
key: Rope.ROPE = GetKey[handle];
IF Rope.Length[key]=0 THEN BEGIN
MessageWindow.Append["Please enter an encryption key", TRUE];
MessageWindow.Blink[];
END
ELSE TiogaOps.CallWithLocks[Encrypt
! TiogaOps.NoSelection => BEGIN
MessageWindow.Append["Please make a text selection", TRUE];
MessageWindow.Blink[];
CONTINUE;
END;
];
END;
DecryptSelection: Buttons.ButtonProc =
BEGIN OPEN TiogaOps;
Decrypt: PROC [root: TiogaOps.Ref] =
BEGIN
[start: start, end: end, viewer: viewer] ← GetSelection[];
ProcessSelection[root, start.node, end.node, DecryptBranch, key, viewer];
END;
DecryptBranch: PROC [node: TiogaOps.Ref, key: Rope.ROPE] =
BEGIN
chars: REF = GetProp[node, $EncryptedChars];
IF chars#NIL THEN
TRUSTED BEGIN
new: TiogaOps.Ref;
encryptedText: EncryptedText = NARROW[chars];
plain: Rope.ROPE = DecryptRope[cipher: encryptedText, key: key];
TiogaExtraOps.RemProp[node, $EncryptedChars];
TiogaOps.SelectNodes[viewer, node, node, node];
new ← LOOPHOLE[PutGet.FromRope[plain]];
TiogaOps.SaveSpanForPaste[[new, 0], TiogaOps.LastLocWithin[new], node];
TiogaOps.Paste[];
new ← TiogaOps.GetSelection[].start.node;
TiogaOps.SelectBranches[viewer, TiogaOps.FirstChild[new], TiogaOps.FirstChild[new], node, TRUE, TRUE];
TiogaOps.UnNest[];
TiogaOps.SelectNodes[viewer, new, new, node]; -- select the fake root node
TiogaOps.Delete[]; -- get rid of it
TiogaOps.SelectNodes[viewer, node, node, node]; -- select the "encrypted" node
TiogaOps.Delete[]; -- get rid of it
END
ELSE
BEGIN OPEN MessageWindow;
Append["Node already unencrypted", TRUE];
Blink[];
END;
END;
start, end: Location;
viewer: ViewerClasses.Viewer;
handle: EncryptHandle = NARROW[clientData];
key: Rope.ROPE = GetKey[handle];
IF Rope.Length[key]=0 THEN BEGIN
MessageWindow.Append["Please enter an encryption key", TRUE];
MessageWindow.Blink[];
RETURN;
END
ELSE TiogaOps.CallWithLocks[Decrypt
! TiogaOps.NoSelection => BEGIN
MessageWindow.Append["Please make a text selection", TRUE];
MessageWindow.Blink[];
CONTINUE;
END;
];
END;
Level: PROC [node: TiogaOps.Ref] RETURNS [level: INTEGER] = BEGIN
level ← 0;
UNTIL (node ← TiogaOps.Parent[node])=NIL DO level ← level+1; ENDLOOP;
END;
ProcessSelection: PROC [
root: TiogaOps.Ref, start, end: TiogaOps.Ref,
proc: PROC [node: TiogaOps.Ref, key: Rope.ROPE], key: Rope.ROPE,
viewer: ViewerClasses.Viewer] =
BEGIN
node, next, selectAfter, selectUntil: TiogaOps.Ref;
readOnly: BOOLFALSE;
tdd: TEditDocument.TEditDocumentData ← NARROW[viewer.data];
startLevel, endLevel: INTEGER;
startLevel ← Level[start];
endLevel ← Level[end];
IF endLevel<startLevel THEN BEGIN
MessageWindow.Append["Please select a branch", TRUE];
MessageWindow.Blink[];
RETURN;
END;
THROUGH [startLevel..endLevel) DO
end ← TiogaOps.Parent[end];
ENDLOOP;
IF TiogaOps.Parent[start]#TiogaOps.Parent[end] THEN BEGIN
MessageWindow.Append["Please select a branch", TRUE];
MessageWindow.Blink[];
RETURN;
END;
IF tdd.readOnly THEN BEGIN
readOnly ← TRUE;
ViewerTools.EnableUserEdits[viewer];
END;
node ← start;
selectAfter ← TiogaOps.StepBackward[start];
selectUntil ← TiogaOps.Next[end];
DO
next ← TiogaOps.Next[node];
proc[node, key];
IF node=end THEN EXIT;
node ← next;
ENDLOOP;
IF readOnly THEN BEGIN
ViewerTools.InhibitUserEdits[viewer];
viewer.newVersion ← FALSE;
ViewerOps.PaintViewer[viewer, caption];
END;
start ← TiogaOps.StepForward[selectAfter];
end ← start;
UNTIL TiogaOps.Next[end]=selectUntil DO end ← TiogaOps.Next[end]; ENDLOOP;
TiogaOps.SelectBranches[viewer, start, end, node]; -- restore original selection
END;
encryptTool: EncryptHandle = NEW[EncryptToolRec];
encryptMenu: Menus.Menu = Menus.CreateMenu[];
keyPrompt, filePrompt: Buttons.Button;
Menus.AppendMenuEntry[
menu: encryptMenu,
entry: Menus.CreateEntry[
name: "EncryptSelection", -- name of the command
proc: EncryptSelection,  -- proc associated with command
clientData: encryptTool
]
];
Menus.AppendMenuEntry[
menu: encryptMenu,
entry: Menus.CreateEntry[
name: "DecryptSelection", -- name of the command
proc: DecryptSelection,  -- proc associated with command
clientData: encryptTool
]
];
Menus.AppendMenuEntry[
menu: encryptMenu,
entry: Menus.CreateEntry[
name: "EncryptFiles",  -- name of the command
proc: EncryptFiles,   -- proc associated with command
clientData: encryptTool
]
];
Menus.AppendMenuEntry[
menu: encryptMenu,
entry: Menus.CreateEntry[
name: "DecryptFiles",  -- name of the command
proc: DecryptFiles,   -- proc associated with command
clientData: encryptTool
]
];
encryptTool.outer ← Containers.Create[[-- construct the outer container
name: "Encryption Tool", -- name displayed in the caption
iconic: TRUE,    -- so tool will be iconic (small) when first created
column: right,    -- initially in the right column
menu: encryptMenu,  -- displaying our menu command
scrollable: FALSE,   -- inhibit user from scrolling contents
openHeight: 73,
data: encryptTool ]];
keyPrompt ← Buttons.Create[
info: [
name: "Encryption key:",
wy: 0,
wh: 15,
parent: encryptTool.outer,
border: FALSE],
proc: SelectKey,
clientData: encryptTool];
encryptTool.keyViewer ← ViewerTools.MakeNewTextViewer[[
parent: encryptTool.outer,
wx: keyPrompt.wx+keyPrompt.ww+10,
wy: 1, -- to align with button baseline
ww: 10,
wh: 15,
data: "",
scrollable: FALSE,
border: FALSE]];
filePrompt ← Buttons.Create[
info: [
name: "File names:",
wy: 16,
wh: 15,
parent: encryptTool.outer,
border: FALSE],
proc: SelectFiles,
clientData: encryptTool];
encryptTool.files ← ViewerTools.MakeNewTextViewer[[
parent: encryptTool.outer,
wx: filePrompt.wx+filePrompt.ww+10,
wy: 17, -- to align with button baseline
ww: 10,
wh: 30,
data: "",
scrollable: TRUE,
border: FALSE]];
Containers.ChildXBound[encryptTool.outer, encryptTool.keyViewer];
Containers.ChildXBound[encryptTool.outer, encryptTool.files];
Containers.ChildYBound[encryptTool.outer, encryptTool.files];
END.