initialStrongVerifier: Auth.StrongVerifier ~ [BasicTime.ToNSTime[BasicTime.earliestGMT], 0];
Key and Password Administration
ChangeMyPasswords:
PUBLIC PROC [myIdentity: Identity, newPassword:
ROPE,
changeStrong:
BOOL ←
TRUE, changeSimple:
BOOL ←
TRUE] ~ {
IF changeStrong
THEN {
newKey: Key ← StrongKeyFromPassword[newPassword];
ChangeMyStrongKey[myIdentity, newKey] };
IF changeSimple
THEN {
newKey: HashedPassword ← SimpleKeyFromPassword[newPassword];
ChangeMySimpleKey[myIdentity, newKey] };
};
CreateStrongKey:
PUBLIC
PROC [myIdentity: Identity, name: Name, newKey: Key] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.CreateStrongKey[h, GetCredentials[c], GetNextVerifier[c], name, newKey] };
IF myIdentity = NIL THEN ERROR;
c ← Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
ChangeMyStrongKey:
PUBLIC
PROC [myIdentity: Identity, newKey: Key] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.ChangeStrongKey[h, GetCredentials[c], GetNextVerifier[c], newKey] };
IF myIdentity = NIL THEN ERROR;
c ← Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
DeleteStrongKey:
PUBLIC
PROC [myIdentity: Identity, name: Name] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.DeleteStrongKey[h, GetCredentials[c], GetNextVerifier[c], name] };
IF myIdentity = NIL THEN ERROR;
c ← Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
CreateSimpleKey:
PUBLIC
PROC [myIdentity: Identity, name: Name, newKey: HashedPassword] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.CreateSimpleKey[h, GetCredentials[c], GetNextVerifier[c], name, newKey] };
IF myIdentity = NIL THEN ERROR;
c ← Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
ChangeMySimpleKey:
PUBLIC PROC [myIdentity: Identity, newKey: HashedPassword] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.ChangeSimpleKey[h, GetCredentials[c], GetNextVerifier[c], newKey] };
IF myIdentity = NIL THEN ERROR;
c ← Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
DeleteSimpleKey:
PUBLIC PROC [myIdentity: Identity, name: Name] ~ {
c: Conversation;
DoIt:
PROC [h: CrRPC.Handle, host: HostNumber] ~ {
SetRecipientHostNumber[c, host];
Auth.DeleteSimpleKey[h, GetCredentials[c], GetNextVerifier[c], name] };
IF myIdentity = NIL THEN ERROR;
c ← Initiate[myIdentity, authSvcName, oneMinuteOfSeconds];
{ ENABLE UNWIND => Terminate[c]; CallRemote[DoIt] };
Terminate[c];
};
Private Utilities
EqualNames:
PROC [a: Name, b: Name]
RETURNS [
BOOL] ~ {
RETURN[Rope.Equal[a.organization, b.organization, FALSE]
AND Rope.Equal[a.domain, b.domain, FALSE]
AND Rope.Equal[a.object, b.object, FALSE]] };
EqualSeqWords:
PROC [a, b: SeqWords]
RETURNS [
BOOL] ~ {
IF a.length # b.length THEN RETURN [FALSE];
FOR i:
CARDINAL
IN [0..a.length)
DO
IF a.body[i] # b.body[i] THEN RETURN [FALSE];
ENDLOOP;
RETURN [TRUE] };
IncrementStrongVerifier:
PROC [v: Auth.StrongVerifier]
RETURNS [result: Auth.StrongVerifier] ~ {
result.timeStamp ← v.timeStamp;
IF (result.ticks ← v.ticks + 1) = 0
THEN result.timeStamp ← result.timeStamp + 1;
};
StrongVerifierGT:
PROC [new, old: Auth.StrongVerifier]
RETURNS [gt:
BOOL] ~ {
deltaT: INT ~ BasicTime.Period[from~BasicTime.FromNSTime[old.timeStamp], to~BasicTime.FromNSTime[new.timeStamp]];
SELECT deltaT
FROM
> 0 => RETURN [TRUE];
< 0 => RETURN [FALSE];
0 => RETURN [new.ticks > old.ticks];
ENDCASE;
};
StrongVerifierExpired:
PROC [v: Auth.StrongVerifier]
RETURNS [expired:
BOOL] ~ {
deltaT: INT ~ BasicTime.Period[from~BasicTime.FromNSTime[v.timeStamp], to~Now[]];
RETURN [ deltaT > strongVerifierTimeout ] };
FetchRopeFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, r:
ROPE] ~ {
i, count, seqLen: CARDINAL;
w: Basics.ShortNumber;
buf: REF TEXT;
seqLen ← s.length;
IF where >= seqLen THEN ERROR MarshallError;
count ← s.body[where]; where ← where + 1;
buf ← RefText.ObtainScratch[count];
i ← 0;
WHILE i < count
DO
IF where >= seqLen THEN ERROR MarshallError;
w.lc ← s.body[where]; where ← where + 1;
buf ← RefText.InlineAppendChar[buf, VAL[w.hi]]; i ← i + 1;
IF i < count THEN { buf ← RefText.InlineAppendChar[buf, VAL[w.lo]]; i ← i + 1 };
ENDLOOP;
newWhere ← where; r ← Rope.FromRefText[buf];
RefText.ReleaseScratch[buf] };
StoreRopeInSeqWords:
PROC [r:
ROPE, s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL] ~ {
i, length, seqLen: CARDINAL;
w: Basics.ShortNumber;
length ← Rope.Length[r];
seqLen ← s.length;
IF where >= seqLen THEN ERROR MarshallError;
s.body[where] ← length; where ← where + 1;
i ← 0;
WHILE i < length
DO
w.hi ← ORD[Rope.InlineFetch[r,i]]; i ← i + 1;
w.lo ← (IF i < length THEN ORD[Rope.InlineFetch[r,i]] ELSE 0); i ← i + 1;
IF where >= seqLen THEN ERROR MarshallError;
s.body[where] ← LOOPHOLE[w]; where ← where + 1;
ENDLOOP;
RETURN [where] };
FetchSeqWordsFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, result: SeqWords] ~ {
n, seqLen: CARDINAL;
seqLen ← s.length;
IF where >= seqLen THEN ERROR MarshallError;
n ← s.body[where]; where ← where + 1;
IF (n+where) >= seqLen THEN ERROR MarshallError;
result ← NEW[SeqWordsObject[n]];
FOR i:
CARDINAL
IN [0..n)
DO
result.body[i] ← s.body[where]; where ← where + 1;
ENDLOOP;
newWhere ← where;
};
StoreSeqWordsInSeqWords:
PROC [s: SeqWords, arg: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL] ~ {
n: CARDINAL ~ arg.length;
seqLen: CARDINAL ~ s.length;
IF (where+n+1) >= seqLen THEN ERROR MarshallError;
s.body[where] ← n; where ← where + 1;
FOR i:
CARDINAL
IN [0..n)
DO
s.body[where] ← arg.body[i]; where ← where + 1;
ENDLOOP;
newWhere ← where;
};
FetchBlockFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, block: Block] ~ {
IF (newWhere ← where+4) >= s.length THEN ERROR MarshallError;
block[0] ← s.body[where];
block[1] ← s.body[where+1];
block[2] ← s.body[where+2];
block[3] ← s.body[where+3];
};
StoreBlockInSeqWords:
PROC [s: SeqWords, block: Block, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL] ~ {
IF (newWhere ← where+4) >= s.length THEN ERROR MarshallError;
s.body[where] ← block[0];
s.body[where+1] ← block[1];
s.body[where+2] ← block[2];
s.body[where+3] ← block[3];
};
FetchCredentialsFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, c: Credentials] ~ {
type: CARDINAL;
IF where >= s.length THEN ERROR MarshallError;
type ← s.body[where]; where ← where + 1;
SELECT type
FROM
ORD[CredentialsType.strong] => c.type ← strong;
ORD[CredentialsType.simple] => c.type ← simple;
ENDCASE => ERROR MarshallError;
[newWhere~newWhere, result~c.value] ← FetchSeqWordsFromSeqWords[s, where];
};
FetchCARDFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, result:
CARD] ~ {
n: Basics.LongNumber;
IF(newWhere ← where+2) >= s.length THEN ERROR MarshallError;
n.hi ← s.body[where];
n.lo ← s.body[where+1];
result ← n.lc };
FetchCredentialsPackageFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, cp: Auth.CredentialsPackage] ~ {
[where, cp.credentials] ← FetchCredentialsFromSeqWords[s, where];
IF cp.credentials.type # strong THEN ERROR AuthenticationError[inappropriateCredentials];
[where, cp.nonce] ← FetchCARDFromSeqWords[s, where];
[where, cp.recipient] ← FetchNameFromSeqWords[s, where];
[where, cp.conversationKey] ← FetchBlockFromSeqWords[s, where];
newWhere ← where };
FetchStrongCredentialsFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, sc: Auth.StrongCredentials] ~ {
[where, sc.conversationKey] ← FetchBlockFromSeqWords[s, where];
[where, sc.expirationTime] ← FetchCARDFromSeqWords[s, where];
[where, sc.initiator] ← FetchNameFromSeqWords[s, where];
newWhere ← where };
StoreSubropeInBlock:
PROC [r:
ROPE, index, len:
INT,
to:
LONG
POINTER
TO Block, mapCase:
BOOL ←
TRUE] ~ {
Store len chars in block using 16-bit encoding.
c: CHAR;
blockIndex: INT ← 0;
WHILE blockIndex < len
DO
c ← Rope.InlineFetch[r, index];
IF mapCase
THEN
SELECT c
FROM
IN ['A .. 'Z] => c ← 'a + (c - 'A);
ENDCASE;
TRUSTED { to[blockIndex] ← ORD[c] };
index ← index + 1;
blockIndex ← blockIndex + 1;
ENDLOOP;
WHILE blockIndex < 4
DO
TRUSTED { to[blockIndex] ← 0 };
blockIndex ← blockIndex + 1;
ENDLOOP;
};
FetchNameFromSeqWords:
PROC [s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL, name: Name] ~ {
[where, name.organization] ← FetchRopeFromSeqWords[s, where];
[where, name.domain] ← FetchRopeFromSeqWords[s, where];
[where, name.object] ← FetchRopeFromSeqWords[s, where];
newWhere ← where };
StoreNameInSeqWords:
PROC [name: Name, s: SeqWords, where:
CARDINAL]
RETURNS [newWhere:
CARDINAL] ~ {
where ← StoreRopeInSeqWords[name.organization, s, where];
where ← StoreRopeInSeqWords[name.domain, s, where];
where ← StoreRopeInSeqWords[name.object, s, where];
RETURN [where] };
MarshalledNameHWORDs:
PROC [name: Name]
RETURNS [hWords:
CARDINAL] ~ {
RETURN [CrRPC.MarshalledROPEHWords[name.organization] + CrRPC.MarshalledROPEHWords[name.domain] + CrRPC.MarshalledROPEHWords[name.object]] };
SeqWordsFromName:
PROC [name: Name]
RETURNS [SeqWords] ~ {
s: SeqWords ← NEW[SeqWordsObject[MarshalledNameHWORDs[name]]];
[] ← StoreNameInSeqWords[name, s, 0];
RETURN [s] };
CredentialsFromName:
PROC [name: Name]
RETURNS [credentials: Credentials] ~ {
RETURN[ [type~simple, value~SeqWordsFromName[name]] ] };
VerifierFromHashedPassword:
PROC [hashedPassword: HashedPassword]
RETURNS [verifier: Verifier] ~ {
???? WORD SIZE DEPENDENT ????
verifier ← NEW[SeqWordsObject[1]];
verifier.body[0] ← hashedPassword };
EncryptSeqWords:
PROC [key: Key, seqWords: SeqWords]
RETURNS [result: SeqWords] ~ {
???? Word size dependent. Should use BYTES in here instead of SIZE ????
nBlocks: CARDINAL ← (seqWords.length + SIZE[Block] - 1) / SIZE[Block];
roundedLength: CARDINAL ← nBlocks * SIZE[Block];
result ← NEW[SeqWordsObject[roundedLength]];
IF roundedLength # seqWords.length
THEN {
FOR i:
CARDINAL
IN [0..seqWords.length)
DO result.body[i] ← seqWords.body[i] ENDLOOP;
FOR i:
CARDINAL
IN [seqWords.length..roundedLength)
DO result.body[i] ← 0 ENDLOOP;
seqWords ← result };
TRUSTED {
blocksFrom: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[seqWords, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
blocksTo: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[result, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
DESFace.CBCCheckEncrypt[LOOPHOLE[key], nBlocks, blocksFrom, blocksTo, ALL[0]];
};
};
DecryptSeqWords:
PROC [key: Key, seqWords: SeqWords]
RETURNS [result: SeqWords] ~ {
???? Word size dependent. Should use BYTES in here instead of SIZE ????
nBlocks: CARDINAL ~ seqWords.length / SIZE[Block];
roundedLength: CARDINAL ← nBlocks * SIZE[Block];
IF seqWords.length # roundedLength THEN ERROR MarshallError;
result ← NEW[SeqWordsObject[roundedLength]];
TRUSTED {
blocksFrom: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[seqWords, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
blocksTo: DESFace.Blocks ~ LOOPHOLE[
LOOPHOLE[result, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
DESFace.CBCCheckDecrypt[LOOPHOLE[key], nBlocks, blocksFrom, blocksTo, ALL[0]];
};
};
EncryptedVerifierFromStrongVerifier:
PROC [conversationKey: Key,
hostNumber:
MACHINE
DEPENDENT
RECORD [a, b, c:
WORD],
strongVerifier: Auth.StrongVerifier]
RETURNS [verifier: Verifier] ~ {
seqWords: SeqWords ~ NEW[SeqWordsObject[SIZE[Auth.StrongVerifier]]];
TRUSTED {
p: LONG POINTER TO Auth.StrongVerifier ← LOOPHOLE[
LOOPHOLE[seqWords, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
p^ ← strongVerifier };
seqWords.body[0] ← Basics.BITXOR[seqWords.body[0], hostNumber.a];
seqWords.body[1] ← Basics.BITXOR[seqWords.body[1], hostNumber.b];
seqWords.body[2] ← Basics.BITXOR[seqWords.body[2], hostNumber.c];
verifier ← EncryptSeqWords[key~LOOPHOLE[conversationKey], seqWords~seqWords];
};
StrongVerifierFromEncryptedVerifier:
PROC [conversationKey: Key,
hostNumber:
MACHINE
DEPENDENT
RECORD [a, b, c:
WORD],
verifier: Verifier]
RETURNS [strongVerifier: Auth.StrongVerifier] ~ {
IF verifier.length # SIZE[Auth.StrongVerifier] THEN ERROR MarshallError;
verifier ← DecryptSeqWords[key~LOOPHOLE[conversationKey], seqWords~verifier];
verifier.body[0] ← Basics.BITXOR[verifier.body[0], hostNumber.a];
verifier.body[1] ← Basics.BITXOR[verifier.body[1], hostNumber.b];
verifier.body[2] ← Basics.BITXOR[verifier.body[2], hostNumber.c];
TRUSTED {
p: LONG POINTER TO Auth.StrongVerifier ← LOOPHOLE[
LOOPHOLE[verifier, LONG POINTER] + SIZE[SeqWordsObject[0]] ];
strongVerifier ← p^ };
};
Locating Authentication Servers and Performing Remote Calls
ServerInfo: TYPE ~ REF ServerInfoObject;
ServerInfoObject:
TYPE ~
RECORD [
next: ServerInfo,
address: XNS.Address,
hops: XNSRouter.Hops
];
serverInfoLock: Lock ← NEW[LockObject ← []];
serverBroadcastLock: Lock ← NEW[LockObject ← []];
serverInfo: ServerInfo ← NIL;
serverInfoTimestamp: GMT;
defaultServerHops: XNSRouter.Hops ~ 3;
maxServerHops: XNSRouter.Hops ~ 6;
secondsBetweenBroadcasts: INT ~ 600;
DeleteServer:
ENTRY
PROC [lock: Lock ← serverInfoLock, it: ServerInfo] ~ {
ENABLE UNWIND => NULL;
IF it = serverInfo THEN { serverInfo ← it.next; RETURN };
FOR p: ServerInfo ← serverInfo, p.next
WHILE p #
NIL
DO
IF p.next = it THEN { p.next ← it.next; RETURN };
ENDLOOP;
};
AddServerAddress:
ENTRY
PROC [lock: Lock ← serverInfoLock, addr:
XNS.Address, hops: XNSRouter.Hops] ~ {
ENABLE UNWIND => NULL;
p, prev, new: ServerInfo;
Delete old entry for this address (hops may have changed)
prev ← NIL; p ← serverInfo;
WHILE (p # NIL) AND (p.address # addr) DO { prev ← p; p ← p.next } ENDLOOP;
IF p =
NIL
THEN {
new ← NEW[ServerInfoObject ← [next~NIL, address~addr, hops~hops]] }
ELSE {
IF prev # NIL THEN prev.next ← p.next ELSE serverInfo ← p.next;
new ← p; new.hops ← hops };
Add new entry in correct position (sorted by hops)
prev ← NIL; p ← serverInfo;
WHILE (p # NIL) AND (p.hops < hops) DO { prev ← p; p ← p.next } ENDLOOP;
new.next ← p;
IF prev # NIL THEN prev.next ← new ELSE serverInfo ← new;
};
BroadcastForServers:
ENTRY
PROC [lock: Lock ← serverBroadcastLock,
maxHops:
CARDINAL, nWanted:
CARDINAL ← 0, tryLimit:
CARDINAL ← 0] ~ {
ENABLE UNWIND => NULL;
nGot: CARDINAL ← 0;
EachAddress: XNSServerLocation.EachAddressProc
-- [addr: XNS.Address] -- ~ {
hops: XNSRouter.Hops;
addr.socket ← XNS.unknownSocket;
hops ← XNSRouter.GetHops[addr.net];
AddServerAddress[addr~addr, hops~hops];
IF (nGot ← nGot.SUCC) = nWanted THEN ERROR XNSServerLocation.StopBroadcast[] };
XNSServerLocation.LocateServers[eachAddress~EachAddress, socket~XNSWKS.authenticationInfo, remotePgm~authPgmNum, remotePgmVersion~authVersionNum, maxHops~maxHops, tryLimit~tryLimit];
serverInfoTimestamp ← Now[];
};
GetBestServer:
PROC
RETURNS [ServerInfo] ~ {
DO
p: ServerInfo ← serverInfo; -- ATOMIC
currentHops: XNSRouter.Hops;
SELECT
TRUE
FROM
(p =
NIL) => {
BroadcastForServers[maxHops~maxServerHops, nWanted~1];
p ← serverInfo;
TRUSTED { Process.Detach[ FORK BroadcastForServers[maxHops~defaultServerHops, tryLimit~1] ] };
};
((currentHops ← XNSRouter.GetHops[p.address.net]) # p.hops) => {
DeleteServer[it~p];
AddServerAddress[addr~p.address, hops~currentHops];
LOOP };
((p.hops > 0)
AND (BasicTime.Period[from~serverInfoTimestamp, to~Now[]] > secondsBetweenBroadcasts)) =>
TRUSTED { Process.Detach[ FORK BroadcastForServers[maxHops~MIN[(p.hops-1), defaultServerHops], tryLimit~1] ] };
ENDCASE;
RETURN [p];
ENDLOOP;
};
CallRemote:
PROC [proc:
PROC [CrRPC.Handle, HostNumber]] ~ {
h: CrRPC.Handle ← NIL;
p: ServerInfo;
maxTries: CARDINAL ~ 10;
CleanUp: PROC ~ { IF h # NIL THEN { CrRPC.DestroyClientHandle[h]; h ← NIL }};
FOR try:
CARDINAL
IN [1..maxTries]
DO
BEGIN
ENABLE {
UNWIND => CleanUp[];
CrRPC.Error => { CleanUp[]; DeleteServer[it~p]; LOOP }};
IF (p ← GetBestServer[]) = NIL THEN EXIT;
h ← CrRPC.CreateClientHandle[class~$SPP, remote~p.address];
proc[h, p.address.host]; CleanUp[]; RETURN;
END;
ENDLOOP;
CleanUp[]; CantContactAuthServer[];
};
}.