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], 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], WalnutSendOps USING [AddToSendMenu, BuildSendViewer, ClearSendViewer, GetFieldBody, GetRecipients, RemoveFromSendMenu, StuffForm, Send], WalnutSendInternal USING [SenderInfo], WalnutWindow USING [AddToMsgMenu, RemoveFromMsgMenu]; EncryptMailImpl: CEDAR PROGRAM IMPORTS Basics, BigCardinals, Commander, DESFace, Encrypt, FS, IO, EncryptionKeys, MessageWindow, Rope, RSA, UserCredentials, ViewerOps, ViewerTools, WalnutSendOps, WalnutWindow = BEGIN Startup: PROC[] ~ { userName, publicKeyFileName, localPrivateKeyFileName, remotePrivateKeyFileName: Rope.ROPE; publicExists, localPrivateExists, remotePrivateExists: BOOL _ TRUE; size: INT; figLog: IO.STREAM; { WalnutWindow.RemoveFromMsgMenu["D"]; WalnutWindow.AddToMsgMenu["D", DecryptRead, NIL, TRUE, FALSE]; WalnutWindow.RemoveFromMsgMenu["V"]; WalnutWindow.AddToMsgMenu["V", VerifyDecryptRead, NIL, TRUE, FALSE]; WalnutSendOps.RemoveFromSendMenu["ES"]; WalnutSendOps.AddToSendMenu["ES", EncryptMail]; WalnutSendOps.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["[User]<", 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; }; }; }; 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; viewer _ NARROW[parent]; viewerContents _ ViewerTools.GetTiogaContents[viewer]; plain _ Rope.Concat[viewerContents.contents, viewerContents.formatting]; IF plain.Fetch[0] = '\n THEN subject _ WalnutSendOps.GetFieldBody[Rope.Substr[plain, 1, Rope.Length[plain]-1], "Subject"] ELSE subject _ WalnutSendOps.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] _ WalnutSendOps.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 { 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; }; 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; }; 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; viewer _ NARROW[parent]; viewerContents _ ViewerTools.GetTiogaContents[viewer]; plain _ Rope.Concat[viewerContents.contents, viewerContents.formatting]; IF plain.Fetch[0] = '\n THEN subject _ WalnutSendOps.GetFieldBody[Rope.Substr[plain, 1, Rope.Length[plain]-1], "Subject"] ELSE subject _ WalnutSendOps.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] _ WalnutSendOps.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 { 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; }; 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; }; 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 _ WalnutSendOps.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 _ WalnutSendOps.GetFieldBody[cipher, "Signature"]; IF signature = NIL THEN GOTO WrongMenuProc; private _ EncryptionKeys.GetPrivateKey[ !EncryptionKeys.KeyFail => GOTO Return;]; sender _ WalnutSendOps.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; { 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; }; }; }; RetrievePrivateKey: Commander.CommandProc ~ { passWord, privateFileName, userName, encryptedKeyFileName: Rope.ROPE; { 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; }; }; }; 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 { 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]}; }; 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]]; { 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]; }; }; 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 [User].\n"]; Commander.Register["RetrievePrivateKey", RetrievePrivateKey, "Retrieves private key from [User] and decrypts.\n"]; Startup[]; END. ,EncryptMailImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last Edited by: Feigenbaum, June 7, 1984 8:02 pm PDT Willie-Sue, July 12, 1985 11:48:31 am PDT Swinehart, July 11, 1985 3:42:11 pm PDT Last Edited by: Greene, July 1, 1986 7:08:23 pm PDT Rick Beach, November 3, 1986 6:08:08 pm PST PrincOpsUtils USING [IsBound], WalnutRegistry USING [CurrentWalnutState], These button names are very short for now, because the window is narrow. --IF WalnutRegistry.CurrentWalnutState[] = unknown THEN { -- MessageWindow.Append["Walnut has not been loaded", TRUE]; -- MessageWindow.Append[" Run Walnut first."]; --RETURN; --}; --IF ~PrincOpsUtils.IsBound[LOOPHOLE[WalnutSendOps.AddToSendMenu]] THEN { --MessageWindow.Append["WalnutSendOps.AddToSendMenu Unbound", TRUE]; --MessageWindow.Append[" Run Walnut first."]; --RETURN; --}; 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. ΚQ˜codešœ™Kšœ Οmœ1™˜NKšžœžœ˜Kšœžœ˜Kšœžœ ˜Kšœ žœ˜+Kšœ žœx˜‰Kšœžœu˜ˆKšœžœ˜&Kšœžœ™*Kšœ žœ#˜5K˜—K˜KšΠlnœžœž˜Kšžœ4žœžœ'žœF˜±Kšœž˜K˜šΟnœžœ˜KšœUžœ˜ZKšœ7žœžœ˜CKšœžœ˜ Kšœžœžœ˜K˜˜KšœH™Hšžœ/žœ™9Kšœ7žœ™=Kšœ/™/Kšœžœ™ Kšžœ™—šžœžœžœ™IKšœ>žœ™DKšœ.™.Kšžœ™ Kšžœ™—K˜$Kšœ,žœžœžœ˜>Kšœ$˜$Kšœ2žœžœžœ˜DKšœ(˜(Kšœ/˜/K˜'Kšœ3˜3K˜K˜&K˜BK˜TK˜HK˜`šœžœ˜&Kš žœ žœžœžœžœ ˜X—šœžœ#˜,Kš žœ žœžœžœžœ ˜^—šœžœ$˜-Kš žœ žœžœžœžœ ˜^—Kš žœžœžœžœžœ˜SKšžœžœžœžœ˜IKšžœžœžœžœ˜IKšžœžœžœ˜/Kšžœžœžœžœ˜JKšžœžœžœ ˜*K˜šž˜˜ KšœHžœ˜NK˜Kšœ5žœ˜?K˜K˜——˜˜Kšœ3žœ˜9K˜Kšœ žœ!žœ žœ˜LKšžœ8˜:Kšžœ‡˜‰KšžœX˜ZK˜šž˜˜Kšœ/žœ˜5K˜K˜——K˜——˜šœ˜Kšœ3žœ˜9K˜Kšœ žœ!žœ žœ˜LKšžœ8˜:Kšžœ†˜ˆKšžœX˜ZK˜šž˜˜Kšœ/žœ˜5K˜K˜——K˜——˜˜Kšœ3žœ˜9K˜Kšœ žœ!žœ žœ˜LKšžœ8˜:Kšžœͺ˜¬KšžœX˜ZK˜šž˜˜Kšœ/žœ˜5K˜K˜——K˜——˜˜Kšœ3žœ˜9K˜Kšœ žœ!žœ žœ˜LKšžœ/˜1Kšžœ€˜‚KšžœX˜ZK˜šž˜˜Kšœ/žœ˜5K˜K˜——K˜——˜˜ KšœNžœ˜TK˜K˜——K˜—K˜—K˜š  œ˜Kšœ žœ˜K˜K˜KšœSžœ˜XKšœ žœžœžœ˜K˜(K˜*K™Kšœ žœ ˜K˜6K˜Hšžœž˜Kšœ\˜\—Kšžœ8˜<šžœ˜ Kšœ˜Kšžœžœž˜K˜JK˜-Kšœžœ-žœ˜GK˜3Kšžœ žœžœ ˜7Kšœ˜—Kšžœ˜—Kšœ#˜#K˜K˜šž˜˜Kšœ5žœ˜;K˜K˜—˜Kšœ5žœ˜;˜K˜——˜Kšœ;žœ˜AK˜K˜—Kšœ žœ˜—K˜—K˜š œ˜#Kšœ žœ˜Kšœžœ˜K˜=K˜Kšœxžœ˜}Kšœ žœžœžœ˜K˜(Kšœ*˜*K™Kšœ žœ ˜K˜6K˜Hšžœž˜Kšœ\˜\—Kšžœ8˜<šžœ˜ Kšœ˜Kšžœžœž˜K˜DK˜-Kšœžœ-žœ˜GK˜3Kšžœ žœžœ ˜7Kšœ˜—Kšžœ˜—Kšœ#˜#Kšœ˜šž˜˜Kšœ5žœ˜;K˜K˜—˜Kšœ5žœ˜;˜K˜——˜Kšœ;žœ˜AK˜K˜—Kšœ žœ˜—K˜—K˜š  œ˜K˜&Kšœ<žœ˜AK˜$K˜K˜Kšœžœ˜ K˜Kšœ žœ ˜K˜)KšœB˜BKšžœžœžœžœ˜.K˜$K˜Kšœžœ˜K˜K˜šžœžœ ž˜Kšœžœžœ ˜IKšžœ˜ —šžœžœž˜Kšœžœžœžœ˜\Kšžœ˜—Kšœžœžœ˜IKšžœ˜&K˜—K˜š  œžœžœ žœ˜K˜š œžœžœžœžœžœžœžœžœžœžœžœ˜yKš œžœžœžœžœ˜@šœ˜šž˜Kš œžœžœžœžœ ˜)Kš œžœžœžœžœ ˜)Kš œžœžœžœžœ ˜)—Kšœžœ˜'Kšœžœ˜'Kšœžœ˜'Kšœžœ˜'K˜Kšœ˜—K˜—K˜š œžœ#˜0Kšœ+žœ+˜\K˜Kšœ"˜"K˜:K˜Kšœ&Οc ˜3Kšœ&˜&K˜K˜K˜—K˜K˜dK˜K˜rK˜K˜ K˜Kšžœ˜—…—X<oΉ