DIRECTORY Basics USING [BITXOR, BITOR, BITAND, BITSHIFT, HighByte, LowByte], BigCardinals USING [BigCARD, BigCARDRec, BigToDecimalRope, BigFromDecimalRope, BigToRope, BigFromRope], Commander USING [CommandProc, Register], DESFace USING [Key, Block, GetRandomKey, CorrectParity, EncryptBlock, nullKey], Encrypt USING [DecryptRopeWithKey, EncryptRopeWithKey, EncryptFile, DecryptFile], EncryptionKeys USING [GetPrivateKey, GetPublicKey, CreateKey, KeyFail], FS USING [Copy, Delete, Error, FileInfo, StreamOpen], IO USING [STREAM, PutRope, GetLineRope, Flush, Close], Menus USING [MenuProc], MessageWindow USING [Append, Blink, Clear], PrincOpsUtils USING [IsBound], Rope USING [ROPE, Concat, Cat, Equal, Substr, Index, Length, Fetch, FromChar], RSA USING [Decrypt, Encrypt], UserCredentials USING [Get], ViewerClasses USING [Viewer], ViewerOps USING [DestroyViewer, FetchProp], ViewerTools USING [EnableUserEdits, TiogaContents, TiogaContentsRec, SetTiogaContents, GetTiogaContents, GetContents, MakeNewTextViewer], WalnutDisplayerOps USING [AddToMsgMenu], WalnutSendOps USING [AddToSendMenu, BuildSendViewer, ClearSendViewer, StuffForm, Send], WalnutSendOpsExtras USING [GetFieldBody, GetRecipients, RemoveFromSendMenu, RemoveFromMsgMenu], WalnutSendInternal USING [SenderInfo]; EncryptMailImpl: CEDAR PROGRAM IMPORTS Basics, BigCardinals, Commander, DESFace, Encrypt, FS, IO, EncryptionKeys, MessageWindow, PrincOpsUtils, Rope, RSA, UserCredentials, ViewerOps, ViewerTools, WalnutDisplayerOps, WalnutSendOps, WalnutSendOpsExtras = BEGIN Startup: PROC[] ~ { userName, publicKeyFileName, localPrivateKeyFileName, remotePrivateKeyFileName: Rope.ROPE; publicExists, localPrivateExists, remotePrivateExists: BOOL _ TRUE; size: INT; figLog: IO.STREAM; BEGIN IF ~PrincOpsUtils.IsBound[WalnutDisplayerOps.AddToMsgMenu] THEN BEGIN MessageWindow.Append["WalnutDisplayerOps.AddToMsgMenu Unbound", TRUE]; MessageWindow.Append[" Run Walnut first."]; RETURN; END; IF ~PrincOpsUtils.IsBound[WalnutSendOps.AddToSendMenu] THEN BEGIN MessageWindow.Append["WalnutDisplayerOps.AddToSendMenu Unbound", TRUE]; MessageWindow.Append[" Run Walnut first."]; RETURN; END; WalnutSendOpsExtras.RemoveFromMsgMenu["D"]; WalnutDisplayerOps.AddToMsgMenu["D", DecryptRead, TRUE]; WalnutSendOpsExtras.RemoveFromMsgMenu["V"]; WalnutDisplayerOps.AddToMsgMenu["V", VerifyDecryptRead, TRUE]; WalnutSendOpsExtras.RemoveFromSendMenu["ES"]; WalnutSendOps.AddToSendMenu["ES", EncryptMail]; WalnutSendOpsExtras.RemoveFromSendMenu["SS"]; WalnutSendOps.AddToSendMenu["SS", SignEncryptMail]; userName _ UserCredentials.Get[].name; userName _ Rope.Substr[userName, 0, Rope.Index[userName, 0, "."]]; publicKeyFileName _ Rope.Cat["[Indigo]Keys>", userName, ".PublicKey"]; localPrivateKeyFileName _ Rope.Cat["///Keys/", userName, ".PrivateKey"]; remotePrivateKeyFileName _ Rope.Cat["[Ivy]<", userName, ">", userName, ".encryptedPrivateKey"]; size _ FS.FileInfo[publicKeyFileName! FS.Error => IF error.code = $unknownFile THEN {publicExists _ FALSE; CONTINUE;}].bytes; size _ FS.FileInfo[localPrivateKeyFileName! FS.Error => IF error.code = $unknownFile THEN {localPrivateExists _ FALSE; CONTINUE;}].bytes; size _ FS.FileInfo[remotePrivateKeyFileName! FS.Error => IF error.code = $unknownFile THEN {remotePrivateExists _ FALSE; CONTINUE;}].bytes; IF ~publicExists AND ~localPrivateExists AND ~remotePrivateExists THEN GOTO NoKeys; IF ~publicExists AND ~localPrivateExists THEN GOTO NoPublicRemotePrivate; IF ~publicExists AND ~remotePrivateExists THEN GOTO NoPublicLocalPrivate; IF ~publicExists THEN GOTO NoPublicBothPrivate; IF ~localPrivateExists AND ~remotePrivateExists THEN GOTO PublicNoPrivate; IF ~localPrivateExists THEN GOTO Retrieve; EXITS NoKeys => { MessageWindow.Append["Please wait several minutes for key generation.", TRUE]; MessageWindow.Blink; EncryptionKeys.CreateKey[! EncryptionKeys.KeyFail => CONTINUE]; MessageWindow.Clear; }; NoPublicRemotePrivate => { MessageWindow.Append["Key Trouble. Read Fig.log", TRUE]; MessageWindow.Blink; figLog _ FS.StreamOpen["Fig.log", $create ! FS.Error => GOTO CannotOpenLog]; IO.PutRope[figLog, "You have no public key on Indigo.\n"]; IO.PutRope[figLog, Rope.Cat["Please delete all versions of ", remotePrivateKeyFileName, " and execute _ EncryptionKeys.CreateKey[].\n"]]; IO.PutRope[figLog, "Messages saved under the old keys will no longer be decipherable.\n"]; figLog.Close[]; EXITS CannotOpenLog => { MessageWindow.Append["Cannot create Fig.log.", TRUE]; MessageWindow.Blink; }; }; NoPublicLocalPrivate => { MessageWindow.Append["Key Trouble. Read Fig.log", TRUE]; MessageWindow.Blink; figLog _ FS.StreamOpen["Fig.log", $create ! FS.Error => GOTO CannotOpenLog]; IO.PutRope[figLog, "You have no public key on Indigo.\n"]; IO.PutRope[figLog, Rope.Cat["Please delete all versions of ", localPrivateKeyFileName, " and execute _ EncryptionKeys.CreateKey[].\n"]]; IO.PutRope[figLog, "Messages saved under the old keys will no longer be decipherable.\n"]; figLog.Close[]; EXITS CannotOpenLog => { MessageWindow.Append["Cannot create Fig.log.", TRUE]; MessageWindow.Blink; }; }; NoPublicBothPrivate => { MessageWindow.Append["Key Trouble. Read Fig.log", TRUE]; MessageWindow.Blink; figLog _ FS.StreamOpen["Fig.log", $create ! FS.Error => GOTO CannotOpenLog]; IO.PutRope[figLog, "You have no public key on Indigo.\n"]; IO.PutRope[figLog, Rope.Cat["Please delete all versions of ", localPrivateKeyFileName, " and ", remotePrivateKeyFileName, " and execute _ EncryptionKeys.CreateKey[].\n"]]; IO.PutRope[figLog, "Messages saved under the old keys will no longer be decipherable.\n"]; figLog.Close[]; EXITS CannotOpenLog => { MessageWindow.Append["Cannot create Fig.log.", TRUE]; MessageWindow.Blink; }; }; PublicNoPrivate => { MessageWindow.Append["Key Trouble. Read Fig.log", TRUE]; MessageWindow.Blink; figLog _ FS.StreamOpen["Fig.log", $create ! FS.Error => GOTO CannotOpenLog]; IO.PutRope[figLog, "You have no private key.\n"]; IO.PutRope[figLog, Rope.Cat["Please delete all versions of ", publicKeyFileName, " and execute _ EncryptionKeys.CreateKey[].\n"]]; IO.PutRope[figLog, "Messages saved under the old keys will no longer be decipherable.\n"]; figLog.Close[]; EXITS CannotOpenLog => { MessageWindow.Append["Cannot create Fig.log.", TRUE]; MessageWindow.Blink; }; }; Retrieve => { MessageWindow.Append["Please execute RetrievePrivateKey before proceeding.", TRUE]; MessageWindow.Blink; }; END; }; EncryptMail: Menus.MenuProc ~ { parseError: BOOL; public: BigCardinals.BigCARD; key: DESFace.Key; cipher, plain, encryptedKey, subject, recipient, otherRecipients, newMessage: Rope.ROPE; rList, rL: LIST OF Rope.ROPE; viewer, newViewer: ViewerClasses.Viewer; viewerContents: ViewerTools.TiogaContents; BEGIN viewer _ NARROW[parent]; viewerContents _ ViewerTools.GetTiogaContents[viewer]; plain _ Rope.Concat[viewerContents.contents, viewerContents.formatting]; IF plain.Fetch[0] = '\n THEN subject _ WalnutSendOpsExtras.GetFieldBody[Rope.Substr[plain, 1, Rope.Length[plain]-1], "Subject"] ELSE subject _ WalnutSendOpsExtras.GetFieldBody[plain, "Subject"]; TRUSTED {key _ DESFace.GetRandomKey[]; WHILE ZeroKey[key] DO key _ DESFace.GetRandomKey[]; ENDLOOP}; cipher _ Encrypt.EncryptRopeWithKey[plain, key]; viewer.newVersion _ TRUE; newViewer _ WalnutSendOps.BuildSendViewer[TRUE, FALSE]; [rList, parseError] _ WalnutSendOpsExtras.GetRecipients[viewer]; IF parseError THEN GOTO BadMessageHeader; IF rList=NIL THEN GOTO NoRecipients; FOR rL _ rList, rL.rest UNTIL rL=NIL DO foundKey: BOOLEAN _ TRUE; recipient _ Rope.Substr[rL.first, 0, Rope.Index[rL.first, 0, "."]]; public _ EncryptionKeys.GetPublicKey[recipient !EncryptionKeys.KeyFail => { IF subclass = $PublicKeyNotFound THEN foundKey _ FALSE ELSE GOTO Return; CONTINUE;}]; IF foundKey THEN BEGIN encryptedKey _ BigCardinals.BigToDecimalRope[RSA.Encrypt[BigCardinals.BigFromRope[Replicate[KeyToRope[key]]], public]]; otherRecipients _ DeleteMemberConcat[rList, rL.first]; newMessage _ Rope.Cat["To: ", rL.first, "\n"]; newMessage _ Rope.Cat[newMessage, "AlsoTo: ", otherRecipients, "\n"]; newMessage _ Rope.Cat[newMessage, "Subject: ", subject, "\n"]; newMessage _ Rope.Cat[newMessage, "EncryptedKey: ", encryptedKey, "\n\n"]; newMessage _ Rope.Concat[newMessage, cipher]; viewerContents _ NEW[ViewerTools.TiogaContentsRec _ [newMessage, NIL]]; WalnutSendOps.StuffForm[newViewer, viewerContents]; IF ~WalnutSendOps.Send[newViewer] THEN GOTO CannotSend; END; ENDLOOP; ViewerOps.DestroyViewer[newViewer]; CleanUp[viewer]; EXITS BadMessageHeader => { MessageWindow.Append["Cannot parse message header.", TRUE]; MessageWindow.Blink; }; NoRecipients => { MessageWindow.Append["Please specify recipient(s).", TRUE]; MessageWindow.Blink; }; CannotSend => { MessageWindow.Append["WalnutSendOps cannot Send message.", TRUE]; MessageWindow.Blink; }; Return => NULL; END; }; SignEncryptMail: Menus.MenuProc ~ { parseError: BOOL; keyHalfLength: INT; recipientPublic, senderPublic, private: BigCardinals.BigCARD; key: DESFace.Key; cipher, plain, hash, signature, encryptedKey, subject, recipient, otherRecipients, newMessage, sender, raw1, raw2: Rope.ROPE; rList, rL: LIST OF Rope.ROPE; viewer, newViewer: ViewerClasses.Viewer; viewerContents: ViewerTools.TiogaContents; BEGIN viewer _ NARROW[parent]; viewerContents _ ViewerTools.GetTiogaContents[viewer]; plain _ Rope.Concat[viewerContents.contents, viewerContents.formatting]; IF plain.Fetch[0] = '\n THEN subject _ WalnutSendOpsExtras.GetFieldBody[Rope.Substr[plain, 1, Rope.Length[plain]-1], "Subject"] ELSE subject _ WalnutSendOpsExtras.GetFieldBody[plain, "Subject"]; TRUSTED {key _ DESFace.GetRandomKey[]; WHILE ZeroKey[key] DO key _ DESFace.GetRandomKey[]; ENDLOOP}; cipher _ Encrypt.EncryptRopeWithKey[plain, key]; hash _ Hash[cipher]; private _ EncryptionKeys.GetPrivateKey[!EncryptionKeys.KeyFail => GOTO Return;]; viewer.newVersion _ TRUE; newViewer _ WalnutSendOps.BuildSendViewer[TRUE, FALSE]; [rList, parseError] _ WalnutSendOpsExtras.GetRecipients[viewer]; IF parseError THEN GOTO BadMessageHeader; IF rList=NIL THEN GOTO NoRecipients; sender _ UserCredentials.Get[].name; sender _ Rope.Substr[sender, 0, Rope.Index[sender, 0, "."]]; senderPublic _ EncryptionKeys.GetPublicKey[sender !EncryptionKeys.KeyFail => GOTO Return;]; FOR rL _ rList, rL.rest UNTIL rL=NIL DO foundKey: BOOLEAN _ TRUE; recipient _ Rope.Substr[rL.first, 0, Rope.Index[rL.first, 0, "."]]; recipientPublic _ EncryptionKeys.GetPublicKey[recipient, TRUE !EncryptionKeys.KeyFail => {IF subclass = $PublicKeyNotFound THEN foundKey _ FALSE ELSE GOTO Return; CONTINUE};]; IF foundKey THEN BEGIN encryptedKey _ BigCardinals.BigToRope[RSA.Encrypt[BigCardinals.BigFromRope[Replicate[KeyToRope[key]]], recipientPublic]]; keyHalfLength _ Rope.Length[encryptedKey] / 2; raw1 _ Rope.Concat[hash, Rope.Substr[encryptedKey,0, keyHalfLength]]; raw2 _ Rope.Substr[encryptedKey, keyHalfLength]; signature _ Rope.Cat[ BigCardinals.BigToDecimalRope [RSA.Decrypt [BigCardinals.BigFromRope[raw1], senderPublic, private]], "/", BigCardinals.BigToDecimalRope[BigCardinals.BigFromRope[raw2]] ]; otherRecipients _ DeleteMemberConcat[rList, rL.first]; newMessage _ Rope.Cat["To: ", rL.first, "\n"]; newMessage _ Rope.Cat[newMessage, "AlsoTo: ", otherRecipients, "\n"]; newMessage _ Rope.Cat[newMessage, "Subject: ", subject, "\n"]; newMessage _ Rope.Cat[newMessage, "Signature: ", signature, "\n\n"]; newMessage _ Rope.Concat[newMessage, cipher]; viewerContents _ NEW[ViewerTools.TiogaContentsRec _ [newMessage, NIL]]; WalnutSendOps.StuffForm[newViewer, viewerContents]; IF ~WalnutSendOps.Send[newViewer] THEN GOTO CannotSend; END; ENDLOOP; ViewerOps.DestroyViewer[newViewer]; CleanUp[viewer]; EXITS BadMessageHeader => { MessageWindow.Append["Cannot parse message header.", TRUE]; MessageWindow.Blink; }; NoRecipients => { MessageWindow.Append["Please specify recipient(s).", TRUE]; MessageWindow.Blink; }; CannotSend => { MessageWindow.Append["WalnutSendOps cannot Send message.", TRUE]; MessageWindow.Blink; }; Return => NULL; END; }; DecryptRead: Menus.MenuProc ~ { private, public: BigCardinals.BigCARD; cipher, plain, text, formatting, encryptedKey, reader: Rope.ROPE; contents: ViewerTools.TiogaContents; key: DESFace.Key; viewer: ViewerClasses.Viewer; start: INT; viewer _ NARROW[parent]; cipher _ ViewerTools.GetContents[viewer]; encryptedKey _ WalnutSendOpsExtras.GetFieldBody[cipher, "EncryptedKey"]; IF encryptedKey = NIL THEN GOTO WrongMenuProc; reader _ UserCredentials.Get[].name; reader _ Rope.Substr[reader, 0, Rope.Index[reader, 0, "."]]; public _ EncryptionKeys.GetPublicKey[reader !EncryptionKeys.KeyFail => GOTO Return;]; private _ EncryptionKeys.GetPrivateKey[ !EncryptionKeys.KeyFail => GOTO Return;]; key _ KeyFromRope[UnReplicate[BigCardinals.BigToRope[RSA.Decrypt[BigCardinals.BigFromDecimalRope[encryptedKey], public, private]]]]; start _ Rope.Index[cipher, 0, "\n\n"] + 2; cipher _ Rope.Substr[cipher, start]; plain _ Encrypt.DecryptRopeWithKey[cipher, key]; start _ Rope.Index[plain, 0, "\000\000"]; text _ Rope.Substr[plain, 0, start]; formatting _ Rope.Substr[plain, start]; contents _ NEW[ViewerTools.TiogaContentsRec _ [text, formatting]]; ViewerTools.SetTiogaContents[ViewerTools.MakeNewTextViewer[[iconic: FALSE]], contents]; EXITS Return => NULL; WrongMenuProc => { MessageWindow.Append["Press V if msg has Signature; otherwise, it is plaintext.", TRUE]; MessageWindow.Blink; }; }; VerifyDecryptRead: Menus.MenuProc ~ { private, recipientPublic, senderPublic: BigCardinals.BigCARD; cipher, plain, text, formatting, encryptedKey, sender, recipient, hash, signature, sig1, sig2, raw1, raw2: Rope.ROPE; contents: ViewerTools.TiogaContents; key: DESFace.Key; viewer: ViewerClasses.Viewer; start, sepr: INT; viewer _ NARROW[parent]; cipher _ ViewerTools.GetContents[viewer]; signature _ WalnutSendOpsExtras.GetFieldBody[cipher, "Signature"]; IF signature = NIL THEN GOTO WrongMenuProc; private _ EncryptionKeys.GetPrivateKey[ !EncryptionKeys.KeyFail => GOTO Return;]; sender _ WalnutSendOpsExtras.GetFieldBody[cipher, "From"]; sender _ Rope.Substr[sender, 0, Rope.Index[sender, 0, "."]]; senderPublic _ EncryptionKeys.GetPublicKey[sender, TRUE !EncryptionKeys.KeyFail => GOTO Return;]; recipient _ UserCredentials.Get[].name; recipient _ Rope.Substr[recipient, 0, Rope.Index[recipient, 0, "."]]; recipientPublic _ EncryptionKeys.GetPublicKey[recipient !EncryptionKeys.KeyFail => GOTO Return;]; sepr _ Rope.Index[signature, 0, "/"]; sig1 _ Rope.Substr[signature, 0, sepr]; sig2 _ Rope.Substr[signature, sepr+1]; raw1 _ BigCardinals.BigToRope[RSA.Encrypt[BigCardinals.BigFromDecimalRope[sig1], senderPublic]]; raw2 _ BigCardinals.BigToRope[BigCardinals.BigFromDecimalRope[sig2]]; hash _ Rope.Substr[raw1, 0, 8]; encryptedKey_ Rope.Concat[Rope.Substr[raw1, 8], raw2]; key _ KeyFromRope[BigCardinals.BigToRope[RSA.Decrypt[BigCardinals.BigFromRope[encryptedKey], recipientPublic, private]]]; cipher _ Rope.Substr[cipher, Rope.Index[cipher, 0, "\n\n"] + 2]; plain _ Encrypt.DecryptRopeWithKey[cipher, key]; start _ Rope.Index[plain, 0, "\000\000"]; text _ Rope.Substr[plain, 0, start]; formatting _ Rope.Substr[plain, start]; contents _ NEW[ViewerTools.TiogaContentsRec _ [text, formatting]]; IF NOT Rope.Equal[hash, Hash[cipher]] THEN GOTO CannotVerify; ViewerTools.SetTiogaContents[ViewerTools.MakeNewTextViewer[[iconic: FALSE]], contents]; EXITS Return => NULL; WrongMenuProc => { MessageWindow.Append["Press D if msg has EncryptedKey; otherwise, it is plaintext.", TRUE]; MessageWindow.Blink; }; CannotVerify => {MessageWindow.Append["Cannot Verify Signature.", TRUE]; MessageWindow.Blink}; }; StorePrivateKey: Commander.CommandProc ~ { passWord, privateFileName, userName, encryptedKeyFileName: Rope.ROPE; BEGIN userName _ UserCredentials.Get[].name; userName _ Rope.Substr[userName, 0, Rope.Index[userName, 0, "."]]; privateFileName _ Rope.Cat["///Keys/", userName, ".PrivateKey"]; IO.PutRope[cmd.out, "Please enter a password for private key (and Master public key) encryption.\n"]; IO.PutRope[cmd.out, "You must remember it in order to retrieve your key later.\n"]; IO.PutRope[cmd.out, "Type in password and hit return.\n"]; IO.Flush[cmd.out]; passWord _ IO.GetLineRope[cmd.in]; Encrypt.EncryptFile[passWord, privateFileName, "///Keys/encryptedPrivateKey"! FS.Error => GOTO CannotEncrypt]; encryptedKeyFileName _ Rope.Cat["[Ivy]<", userName, ">", userName, ".encryptedPrivateKey"]; FS.Copy["///Keys/encryptedPrivateKey", encryptedKeyFileName ! FS.Error => GOTO CannotStore]; FS.Delete[privateFileName]; IO.PutRope[cmd.out, Rope.Cat["Encrypted Private Key Stored in ", encryptedKeyFileName, ".\n"]]; IO.PutRope[cmd.out, Rope.Concat[privateFileName, " Deleted.\n"]]; Encrypt.EncryptFile[passWord, "///Keys/Master.PublicKey", "///Keys/Master.encryptedPublicKey"! FS.Error => GOTO CannotEncryptMaster]; encryptedKeyFileName _ Rope.Cat["[Ivy]<", userName, ">Master.encryptedPublicKey"]; FS.Copy["///Keys/Master.encryptedPublicKey", encryptedKeyFileName ! FS.Error => GOTO CannotStoreMaster]; IO.PutRope[cmd.out, Rope.Cat["Encrypted Master Public Key Stored in ", encryptedKeyFileName, ".\n"]]; EXITS CannotEncrypt => { MessageWindow.Append[Rope.Concat["Cannot Encrypt ", privateFileName], TRUE]; MessageWindow.Blink; }; CannotStore => { MessageWindow.Append["Cannot Store Encrypted Key File on Ivy.", TRUE]; MessageWindow.Blink; }; CannotEncryptMaster => { MessageWindow.Append["Cannot Encrypt Master Public Key", TRUE]; MessageWindow.Blink; }; CannotStoreMaster => { MessageWindow.Append["Cannot Store Encrypted Master Public Key File on Ivy.", TRUE]; MessageWindow.Blink; }; END; }; RetrievePrivateKey: Commander.CommandProc ~ { passWord, privateFileName, userName, encryptedKeyFileName: Rope.ROPE; BEGIN userName _ UserCredentials.Get[].name; userName _ Rope.Substr[userName, 0, Rope.Index[userName, 0, "."]]; privateFileName _ Rope.Cat["///Keys/", userName, ".PrivateKey"]; encryptedKeyFileName _ Rope.Cat["[Ivy]<", userName, ">", userName, ".encryptedPrivateKey"]; FS.Copy[encryptedKeyFileName, "///Keys/encryptedPrivateKey" ! FS.Error => GOTO CannotCopy]; IO.PutRope[cmd.out, "Please enter the password under which you stored your private key (and Master public key), then hit return.\n"]; passWord _ IO.GetLineRope[cmd.in]; Encrypt.DecryptFile[passWord, privateFileName, "///Keys/encryptedPrivateKey"! FS.Error => GOTO CannotDecrypt]; IO.PutRope[cmd.out, Rope.Cat["Decrypted Private Key Stored in ", privateFileName, ".\n"]]; encryptedKeyFileName _ Rope.Cat["[Ivy]<", userName, ">Master.encryptedPublicKey"]; FS.Copy[encryptedKeyFileName, "///Keys/Master.encryptedPublicKey" ! FS.Error => GOTO CannotRetrieveMaster]; Encrypt.DecryptFile[passWord, "///Keys/Master.PublicKey", "///Keys/Master.encryptedPublicKey"! FS.Error => GOTO CannotRetrieveMaster]; IO.PutRope[cmd.out, "///Keys/Master.publicKey has been restored to local disk.\n"]; EXITS CannotCopy => { MessageWindow.Append[Rope.Cat["Cannot Copy ", encryptedKeyFileName, " to Local Disk."], TRUE]; MessageWindow.Blink; }; CannotDecrypt => { MessageWindow.Append[Rope.Concat["Cannot Decrypt ///Keys/encryptedKey, copied from", encryptedKeyFileName], TRUE]; MessageWindow.Blink; }; CannotRetrieveMaster => { MessageWindow.Append["Master.PublicKey not brought over to local disk.\n", TRUE]; MessageWindow.Blink; }; END; }; Replicate: PROC[in: Rope.ROPE] RETURNS[out: Rope.ROPE] ~ { out _ Rope.Cat[in, in, in, in]; }; UnReplicate: PROC[in: Rope.ROPE] RETURNS[out: Rope.ROPE] ~ { out _ Rope.Substr[in, Rope.Length[in]-7, 7]; }; ZeroKey: PROC[key: DESFace.Key] RETURNS[BOOL] ~ { FOR i: INT IN [0 .. 6] DO IF key[i].b ~= 0 THEN RETURN[FALSE]; ENDLOOP; RETURN[TRUE]; }; Hash: PROC[in: Rope.ROPE] RETURNS[out: Rope.ROPE] ~ { mKey, key: DESFace.Key; mRope: Rope.ROPE; zBlockIn, zBlockOut: DESFace.Block; i, j, l: INT; i _ 0; l _ Rope.Length[in]; mRope _ "abcdefgh"; zBlockIn _ BlockFromRope[mRope]; TRUSTED {WHILE i < l - 6 DO mRope _ Rope.Substr[in, i, 7]; i _ i + 7; mKey _ KeyFromRope[mRope]; XOR64[@mKey, @zBlockIn, @key]; DESFace.EncryptBlock[key, @zBlockIn, @zBlockOut]; zBlockIn _ zBlockOut; ENDLOOP}; IF i < l THEN BEGIN j _ l - i; mRope _ Rope.Substr[in, i, j]; WHILE j <= 7 DO mRope _ Rope.Concat[mRope, "z"]; j _ j + 1; ENDLOOP; mKey _ KeyFromRope[mRope]; TRUSTED {XOR64[@mKey, @zBlockIn, @key]; DESFace.EncryptBlock[key, @zBlockIn, @zBlockOut]}; END; out _ BlockToRope[zBlockOut]; }; DeleteMemberConcat: PROC[rList: LIST OF Rope.ROPE, rMember: Rope.ROPE] RETURNS[fieldRope: Rope.ROPE] ~ { rL: LIST OF Rope.ROPE; fieldRope _ NIL; FOR rL _ rList, rL.rest UNTIL rL=NIL DO IF ~Rope.Equal[rL.first, rMember] THEN fieldRope _ Rope.Cat[fieldRope, rL.first, ", "]; ENDLOOP; fieldRope _ Rope.Substr[fieldRope, 0, Rope.Length[fieldRope]-2]; }; KeyFromRope: PROC[in: Rope.ROPE] RETURNS[out: DESFace.Key] ~ { i: INT; out _ DESFace.nullKey; FOR i IN [0..6] DO out[i].b _ Basics.BITSHIFT[Basics.BITAND[(in.Fetch[i] - 0C), 376B], -1]; ENDLOOP; FOR i IN [0..5] DO out[7].b _ Basics.BITSHIFT[Basics.BITOR[out[7].b, Basics.BITAND[(in.Fetch[i] - 0C), 1B]],1]; ENDLOOP; out[7].b _ Basics.BITOR[out[7].b, Basics.BITAND[(in.Fetch[6] - 0C), 1B]]; TRUSTED {DESFace.CorrectParity[@out]}; }; KeyToRope: PROC[in: DESFace.Key] RETURNS[out: Rope.ROPE] ~ { mask: CARDINAL _ 1B; i: INT _ 6; out _ ""; UNTIL i < 0 DO highHalf: CARDINAL _ Basics.BITSHIFT[in[i].b, 1]; lowHalf: CARDINAL _ Basics.BITAND[in[7].b, mask]; lowHalf _ Basics.BITSHIFT[lowHalf, i-6]; out _ Rope.Concat[Rope.FromChar[Basics.LowByte[Basics.BITOR[highHalf, lowHalf]] + 0C], out]; mask _ mask * 2; i _ i - 1; ENDLOOP; }; BlockToRope: PROC[in: DESFace.Block] RETURNS[out: Rope.ROPE] ~ { out _ ""; FOR i: INT IN [0 .. 3] DO out _ Rope.Concat[out, Rope.FromChar[Basics.HighByte[in[i]] + 0C]]; out _ Rope.Concat[out, Rope.FromChar[Basics.LowByte[in[i]] + 0C]]; ENDLOOP; }; BlockFromRope: PROC[in: Rope.ROPE] RETURNS[out: DESFace.Block] ~ { FOR i: INT IN [0 .. 3] DO u: UNSPECIFIED _ (in.Fetch[2*i] - 0C)*256 + (in.Fetch[2*i + 1] - 0C); out[i] _ u; ENDLOOP; }; Words4: TYPE = MACHINE DEPENDENT RECORD[w1, w2, w3, w4: WORD]; XOR64: PROC[a: LONG POINTER TO DESFace.Key, b: LONG POINTER TO DESFace.Block, c: LONG POINTER TO DESFace.Key] ~ TRUSTED { DESFace.CorrectParity[LOOPHOLE[b, LONG POINTER TO DESFace.Key]]; BEGIN OPEN aLP: LOOPHOLE[a, LONG POINTER TO Words4], bLP: LOOPHOLE[b, LONG POINTER TO Words4], cLP: LOOPHOLE[c, LONG POINTER TO Words4]; cLP.w1 _ Basics.BITXOR[aLP.w1, bLP.w1]; cLP.w2 _ Basics.BITXOR[aLP.w2, bLP.w2]; cLP.w3 _ Basics.BITXOR[aLP.w3, bLP.w3]; cLP.w4 _ Basics.BITXOR[aLP.w4, bLP.w4]; DESFace.CorrectParity[c]; END; }; CleanUp: PROC [viewer: ViewerClasses.Viewer] ~ { senderInfo: WalnutSendInternal.SenderInfo_ NARROW[ViewerOps.FetchProp[viewer, $SenderInfo]]; senderInfo.successfullySent_ TRUE; senderInfo.prevMsg _ ViewerTools.GetTiogaContents[viewer]; ViewerTools.EnableUserEdits[viewer]; -- to be sure WalnutSendOps.ClearSendViewer[viewer]; }; Commander.Register["StorePrivateKey", StorePrivateKey, "Stores encrypted private key on ivy.\n"]; Commander.Register["RetrievePrivateKey", RetrievePrivateKey, "Retrieves private key from ivy and decrypts.\n"]; Startup[]; END. ¤EncryptMailImpl.mesa Last Edited by: Feigenbaum, June 7, 1984 8:02 pm PDT Last Edited by: Greene, June 27, 1984 4:52:46 pm PDT These button names are very short for now, because the window is narrow. In the CommandProcs StorePrivateKey and RetrievePrivateKey, you should be able to suppress password echoing using EditedStream.SetMode, once EditedStream is implemented properly. Now Store Master Public Key. ÊݘJšœ™Jšœ5™5J™4J™JšÏk ˜ Jš œ œœœœœ˜DJšœœV˜jJšœ œ˜*Jšœ œB˜QJšœ œD˜SJšœœ3˜IJšœœœ-˜7Jšœœœœ&˜8Jšœœ ˜Jšœœ˜-Jšœœ ˜!Jšœœœ>˜PJšœœœ˜Jšœœ˜Jšœœœ˜LJšœœx˜‹Jšœœ˜*JšœœD˜YJšœœn˜‰J˜Jšœœ˜Jšœ4œœ6œc˜ÝJ˜Jš˜J˜JšÏnœœ˜J˜JšœYœ˜^Jšœ;œœ˜GJšœ œ˜Jšœ œœ˜J˜Jšœ˜ J˜JšœH™HJšœœ9œ˜DJšœ˜ JšœHœ˜NJšœ4˜4Jšœœ˜Jšœœ˜Jšœœ5œ˜AJšœ˜ JšœIœ˜OJšœ4˜4Jšœœ˜Jšœœ˜J˜/Jšœ6œ˜˜FJšœœ˜*Jšœœœœ˜AJšœQ˜QJšœ.œœ˜;J˜DJšœœ œœ˜-Jš œœœœœ˜(J˜Jšœœœœ˜+Jšœœœ˜!J˜KJ˜6Jšœ(˜(Jšœ œœ ˜BJšœ œœ ˜Jšœ œ˜Jšœœ ˜Jšœ˜ Jšœ9œG˜ƒJ˜BJ˜:J˜QJ˜JJ˜VJ˜9Jšœœ-œ˜SJ˜?Jšœ œ œœ˜OJšœ œ˜Jšœœ˜ Jšœ#˜#J˜J˜Jšœ˜ J˜JšœAœ˜GJ˜ J˜ J˜JšœAœ˜GJ˜ J˜!JšœGœ˜MJ˜ J˜ Jšœœ˜Jšœœ˜J˜J˜Jšœ#˜#Jšœœ˜Jšœœ˜J˜AJ˜Jšœ|œ˜Jšœœœœ˜!J˜,Jšœ.˜.J™Jšœ˜ J™Jšœ œ ˜J˜:J˜LJšœœ˜Jšœœc˜kJšœœ>˜FJšœœ˜*Jšœœœœ˜AJšœ4˜4J˜JšœFœ'˜qJšœ.œœ˜;J˜DJšœœ œœ˜-Jš œœœœœ˜(Jšœ(˜(J˜@J˜5Jšœ#œ ˜1J˜Jšœœœœ˜+Jšœœœ˜!J˜LJšœAœ˜aJšœ œœ ˜?Jšœœœ œ˜&Jšœœ ˜Jšœ˜ Jšœ2œP˜…J˜:J˜JšœAœ¬˜ðJšœB˜BJ˜:J˜QJ˜JJ˜PJ˜9Jšœœ-œ˜SJ˜?Jšœ œ œœ ˜CJšœœ˜ Jšœœ˜ Jšœ#˜#Jšœ˜Jšœ˜ J˜JšœAœ˜GJ˜ J˜ J˜JšœAœ˜GJ˜ J˜!JšœGœ˜MJ˜ J˜ Jšœœ˜Jšœœ˜J˜J˜Jšœ˜J˜*Jšœ@œ˜EJ˜(J˜J˜!Jšœ œ˜J˜Jšœ œ ˜J˜-JšœL˜LJš œœœœœ˜2J˜(J˜@JšœKœ ˜YJšœGœ ˜UJšœ9œL˜ˆJ˜.J˜(J˜4J˜-J˜(J˜+Jšœœ4˜FJšœHœ˜[J˜Jšœ˜ Jšœœ˜J˜Jšœ^œ˜dJ˜ J˜ J˜J˜Jšœ%˜%JšœA˜AJšœtœ˜yJ˜(J˜J˜!Jšœœ˜J˜Jšœ œ ˜J˜-JšœF˜FJš œœ œœœ˜/JšœGœ ˜UJšœ>˜>J˜@Jšœ7œœ ˜eJ˜+J˜IJšœWœˆ˜ãJšœ"œˆ˜­Jšœ#˜#Jšœ:˜:Jšœ-œM˜}J˜DJ˜4J˜-J˜(J˜+Jšœœ4˜FJš œœœ!œœSœ˜›J˜Jšœ˜ Jšœœ˜J˜Jšœaœ˜gJ˜ JšœTœ˜qJ˜J˜JšœQ™QJšœV™VJšœ ™ J˜J˜*JšœDœ˜IJ˜Jšœ˜ J˜J˜*J˜GJ˜DJšœœc˜iJšœœQ˜WJšœœ8˜>Jšœœ˜Jšœœ˜&JšœRœ ˜^Jšœœ˜Jšœ_˜_Jšœœ<œ ˜NJšœœ˜Jšœœ˜Jšœœbœ?˜©J™Jšœcœ ˜oJšœœ˜"JšœV˜VJšœœBœ ˜TJšœœ˜ Jšœœh˜nJ˜Jšœ˜ Jšœ˜JšœRœ˜XJšœ ˜ Jšœ ˜ Jšœ˜JšœLœ˜RJ˜ Jšœ*˜*JšœEœ˜KJšœ ˜ Jšœ ˜ Jšœ˜JšœZœ˜`J˜ J˜ J˜Jšœœ˜J˜J˜J˜-JšœDœ˜IJ˜Jšœ˜ J˜J˜*J˜GJ˜DJšœ_˜_Jšœœ<œ ˜NJšœœ ˜Jšœœƒ˜‰Jšœœ˜&JšœR˜RJšœœ œ˜(Jšœ˜JšœœY˜_Jšœ˜JšœV˜VJšœœBœ ˜TJšœœz˜†Jšœœ œ˜/JšœœQ˜WJ˜Jšœ˜ Jšœ˜Jšœdœ˜jJ˜ J˜ Jšœ˜Jšœyœ˜J˜ J˜ J˜!JšœWœ˜]J˜ J˜ J˜Jšœœ˜ J˜J˜Jš ž œœ œœ œ˜:J˜#J˜J˜Jš ž œœ œœ œ˜Jšœœ˜ J˜J˜Jšœœœ ˜Jšœœœ ˜QJšœœ˜ Jšœœœ˜Jšœœœœ˜dJšœœ˜ Jšœœœ˜MJšœœ˜*J˜J˜Jšž œœœ œ˜œ!˜dJ˜J˜Jšœœ˜ J˜J˜Jšž œœœ œ˜@J˜J˜ Jšœœœœ ˜J˜LJ˜KJšœœ˜ J˜J˜Jšž œœ œœ˜BJ˜Jšœœœœ ˜Jšœ œ7˜NJšœ˜Jšœœ˜ J˜J˜Jš œœœ œœœ˜>J˜Jšžœœœœœœœœœœœœ˜yJ˜Jš œœœœœ˜DJšœ˜ Jšœ˜ Jš œ œœœœ ˜1Jš œ œœœœ ˜1Jš œ œœœœ ˜1Jšœœ˜/Jšœœ˜/Jšœœ˜/Jšœœ˜/J˜!Jšœœ˜J˜J˜šžœœ#˜0Jšœ+œ+˜\J˜Jšœ"˜"J˜:J˜Jšœ3˜3Jšœ&˜&J˜J˜J˜—J˜J˜aJ˜J˜oJ˜J˜ J˜Jšœ˜—…—g¶}7