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
Willie-Sue, July 12, 1985 11:48:31 am PDT
Swinehart, July 11, 1985 3:42:11 pm PDT
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],
WalnutSendOps USING [AddToSendMenu, BuildSendViewer, ClearSendViewer, GetFieldBody,
 GetRecipients, RemoveFromSendMenu, StuffForm, Send],
WalnutSendInternal USING [SenderInfo],
WalnutRegistry USING [CurrentWalnutState],
WalnutWindow USING [AddToMsgMenu, RemoveFromMsgMenu];
EncryptMailImpl: CEDAR PROGRAM
IMPORTS
Basics, BigCardinals, Commander, DESFace, Encrypt, FS, IO, EncryptionKeys, MessageWindow, PrincOpsUtils, Rope, RSA, UserCredentials, ViewerOps, ViewerTools, WalnutSendOps, WalnutRegistry, WalnutWindow =
BEGIN
Startup: PROC[] ~ {
userName, publicKeyFileName, localPrivateKeyFileName, remotePrivateKeyFileName: Rope.ROPE;
publicExists, localPrivateExists, remotePrivateExists: BOOLTRUE;
size: INT;
figLog: IO.STREAM;
BEGIN
These button names are very short for now, because the window is narrow.
IF WalnutRegistry.CurrentWalnutState[] = unknown THEN
BEGIN
MessageWindow.Append["Walnut has not been loaded", TRUE];
MessageWindow.Append[" Run Walnut first."];
RETURN;
END;
IF ~PrincOpsUtils.IsBound[LOOPHOLE[WalnutSendOps.AddToSendMenu]] THEN
BEGIN
MessageWindow.Append["WalnutSendOps.AddToSendMenu Unbound", TRUE];
MessageWindow.Append[" Run Walnut first."];
RETURN;
END;
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]<Cryptography>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;
};
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 ← 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: BOOLEANTRUE;
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 ← 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: BOOLEANTRUE;
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 ← 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};
};
In the CommandProcs StorePrivateKey and RetrievePrivateKey, you should be able to
suppress password echoing using EditedStream.SetMode, once EditedStream is implemented
properly.
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"]];
Now Store Master Public Key.
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 [User].\n"];
Commander.Register["RetrievePrivateKey", RetrievePrivateKey, "Retrieves private key from [User] and decrypts.\n"];
Startup[];
END.