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: BOOL _ FALSE] = 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 EXIT]; ENDLOOP; SELECT bs FROM 0 => state.length _ 0; 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]]]]}}; EncryptHandle: TYPE = REF EncryptToolRec; EncryptToolRec: TYPE = RECORD [ outer: Containers.Container _ NIL, key: Rope.ROPE _ NIL, 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]; 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]; 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: BOOL _ FALSE; tdd: TEditDocument.TEditDocumentData _ NARROW[viewer.data]; startLevel, endLevel: INTEGER; startLevel _ Level[start]; endLevel _ Level[end]; IF endLevelJšœ œ˜š˜Jšœ.œ˜6J˜Jšœ˜—J˜ J˜J˜ Jšœ œ˜Jšœ˜J˜—J˜J˜š Ÿ œœœœœ˜(Jš˜Jš œœœœœ˜$Jšœ˜Jšœ1˜8Jšœ˜ Jšœ˜—J˜šŸ œœ œœ˜>Jšœœœ˜AJ˜—š Ÿœœœœœ˜EJšœœœœœœœœœ ˜FJ˜———JšœE™E˜Jšœœœ˜)šœœœ˜Jšœœ˜"Jšœ œœ˜Jšœ"œ˜&Jšœ˜!J˜—Jšœœœ˜ J˜šœ˜Jš˜Jšœœ ˜+J˜6J˜+Jšœœž&˜JJšœ˜J˜—šœ!˜!Jš˜Jšœœ ˜+Jšœ'˜'Jšœ˜J˜—šŸœœœœ˜:Jš˜šœ˜#Jš˜Jšœ7˜7J˜3Jšœœ˜$Jšœ˜—Jšœ ˜Jšœ˜—J˜šœ"˜"Jš˜Jšœœ ˜+Jšœ œ˜ Jš œœœœœ(˜Bšœœœ˜1Jšœœœ˜&—šœœ˜ Jšœ7œ˜=J˜Jš˜—š˜šœ œ˜Jšœœ#˜6J˜;šœœ˜&Jšœœœ˜&—Jšœ˜——Jšœ˜J˜—šœ"˜"Jš˜Jšœœ ˜+Jšœ œ˜ Jš œ œœœœ(˜Cšœœœ˜0Jšœœœ˜$—šœœ˜ Jšœ7œ˜=J˜Jš˜—š˜šœ œ˜Jšœœ!˜6˜9˜Jšœœ˜Jšœœ˜Jšœœ˜Jšœ=œ˜DJšœ˜Jšœ˜ Jšœ˜—J˜—šœœ˜%Jšœœœ˜$—Jšœ˜——Jšœ˜J˜—J˜šœ&˜&Jšœœ ˜J˜šŸœœ˜$Jš˜Jšœ:˜:JšœI˜IJšœ˜J˜—šŸ œœ œ˜:Jš˜šœ œ˜*Jšœ˜ Jšœœ˜ Jšœ œ˜šœ;œœ˜GJšœ2™2—JšœC˜CJšœž1˜CJšœ ˜ Jšœœ˜*Jšœ2œœž˜SJšœž˜0Jšœ2ž˜MJšœ*œ˜Lšœ2œœ˜>Jšœ%™%—Jšœ˜JšœΠisœ˜'J˜KJš˜—š˜Jšœœ˜Jšœ!œ˜'J˜Jšœ˜—Jšœ˜J˜—J˜Jšœ˜Jšœœ ˜+Jšœ œ˜ šœœ˜ Jšœ7œ˜=J˜Jš˜—šœ˜#šœ˜Jšœ5œ˜;J˜Jšœ˜ Jšœ˜—Jšœ˜—Jšœ˜J˜—šœ&˜&Jšœœ ˜J˜šŸœœ˜$Jš˜Jšœ:˜:JšœI˜IJšœ˜J˜—šŸ œœ œ˜:Jš˜Jšœœ"˜,šœœ˜Jšœ˜ J˜Jšœœ˜-Jšœ œ0˜@J˜-Jšœ/˜/Jšœœ˜'JšœG˜GJšœ˜J˜)JšœZœœ˜fJšœ˜Jšœ.ž˜JJšœž˜#Jšœ0ž˜NJšœž˜#Jš˜—š˜Jšœœ˜Jšœ#œ˜)J˜Jšœ˜—Jšœ˜J˜—J˜Jšœ˜Jšœœ ˜+Jšœ œ˜ šœœ˜ Jšœ7œ˜=J˜Jšœ˜Jš˜—šœ˜#šœ˜Jšœ5œ˜;J˜Jšœ˜ Jšœ˜—Jšœ˜—Jšœ˜J˜—š Ÿœœœ œ˜AJšœ ˜ Jšœ œœœ˜EJšœ˜—J˜šŸœœ˜šœ-˜-Jšœœ œ œ˜@Jšœ˜—Jš˜Jšœ3˜3Jšœ œœ˜Jšœ'œ˜;Jšœœ˜Jšœ˜Jšœ˜šœœ˜!Jšœ/œ˜5J˜Jšœ˜Jšœ˜—šœ˜!Jšœ˜Jšœ˜—šœ-œ˜9Jšœ/œ˜5J˜Jšœ˜Jšœ˜—šœœ˜Jšœ œ˜Jšœ$˜$Jšœ˜—Jšœ ˜ Jšœ+˜+Jšœ!˜!š˜Jšœ˜Jšœ˜Jšœ œœ˜Jšœ ˜ Jšœ˜—šœ œ˜Jšœ%˜%Jšœœ˜Jšœ'˜'Jšœ˜—Jšœ*˜*Jšœ ˜ Jšœ œœ˜JJšœ3ž˜PJšœ˜J˜—Jšœœ˜1J˜-J˜&˜J˜˜Jšœž˜0Jšœž˜8J˜J˜—J˜—˜J˜˜Jšœž˜0Jšœž˜8J˜J˜—J˜—˜J˜˜Jšœž˜-Jšœž˜5J˜J˜—J˜—˜J˜˜Jšœž˜-Jšœž˜5J˜J˜—J˜—šœ'ž ˜GJšœž ˜9Jšœœž4˜EJšœž ˜2Jšœž˜2Jšœ œž'˜