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