XNSAuthImpl.mesa
Demers, January 6, 1987 11:58:38 pm PST
NOTE: The observed behavior of authentication service is that new credentials expire in one day (!). The refresh heuristics are designed around this, and will need to be fixed if the behvaior changes.
DIRECTORY
AuthenticationP14V2 USING [AuthenticationError, CallError, CallProblem, ChangeSimpleKey, ChangeStrongKey, CheckSimpleCredentials, CreateSimpleKey, CreateStrongKey, CredentialsPackage, DeleteSimpleKey, DeleteStrongKey, GetStrongCredentials, HashedPassword, Problem, SeqWords, SeqWordsObject, StrongCredentials, StrongVerifier, Which],
Basics USING [BITXOR, LongNumber, ShortNumber],
BasicTime USING [earliestGMT, FromNSTime, GetClockPulses, GMT, latestGMT, Now, OutOfRange, Period, TimeNotKnown, ToNSTime, Unpack, Unpacked, Update],
Booting USING [RegisterProcs, RollbackProc],
CrRPC USING [CreateClientHandle, DestroyClientHandle, Error, Handle, MarshalledRopeHWords],
DESFace USING [Block, Blocks, CBCCheckDecrypt, CBCCheckEncrypt, CorrectParity, EncryptBlock, Key, nullKey],
Process USING [Detach, Pause, priorityBackground, SecondsToTicks, SetPriority],
RefText USING [InlineAppendChar, ObtainScratch, ReleaseScratch],
Rope USING [Equal, FromRefText, InlineFetch, Length, ROPE],
SafeStorage USING [CantEstablishFinalization, EnableFinalization, EstablishFinalization, FinalizationQueue, FQNext, NewFQ, ReEstablishFinalization],
TimeP15V2 USING [Time],
XNS USING [Address, GetThisHost, Host, unknownHost, unknownSocket],
XNSAuth USING [Credentials, CredentialsType, HashedPassword, Key, Name, Verifier],
XNSRouter USING [GetHops, Hops],
XNSServerLocation USING [EachAddressProc, LocateServers, StopBroadcast],
XNSWKS USING [authenticationInfo]
;
XNSAuthImpl: CEDAR MONITOR
LOCKS lock USING lock: Lock
IMPORTS AuthenticationP14V2, Basics, BasicTime, Booting, CrRPC, DESFace, Process, RefText, Rope, SafeStorage, XNS, XNSRouter, XNSServerLocation
EXPORTS XNSAuth
~ {
OPEN Auth: AuthenticationP14V2, Time: TimeP15V2;
authPgmNum: CARD ~ 14;
authVersionNum: CARDINAL ~ 2;
SeqWords: TYPE ~ Auth.SeqWords;
SeqWordsObject: TYPE ~ Auth.SeqWordsObject;
GMT: TYPE ~ BasicTime.GMT;
Block: TYPE ~ DESFace.Block;
ROPE: TYPE ~ Rope.ROPE;
NSTime: TYPE ~ Time.Time;
Credentials: TYPE ~ XNSAuth.Credentials;
CredentialsType: TYPE ~ XNSAuth.CredentialsType;
HashedPassword: TYPE ~ XNSAuth.HashedPassword;
Key: TYPE ~ XNSAuth.Key;
Name: TYPE ~ XNSAuth.Name;
Verifier: TYPE ~ XNSAuth.Verifier;
HostNumber: TYPE ~ XNS.Host;
myHostNumber: HostNumber ← XNS.GetThisHost[];
authSvcName: Name ← ["CHServers", "CHServers", "Authentication Service"];
initialStrongVerifier: Auth.StrongVerifier ~ [BasicTime.ToNSTime[BasicTime.earliestGMT], 0];
oneMinuteOfSeconds: INT ~ 60;
oneHourOfSeconds: INT ~ 30*oneMinuteOfSeconds;
oneDayOfSeconds: INT ~ LONG[24]*oneHourOfSeconds;
oneWeekOfSeconds: INT ~ LONG[7]*oneDayOfSeconds;
strongCredentialsLifetime: INT ~ oneDayOfSeconds; -- one day of seconds
secondsBetweenSweeps: INT ~ 3*oneMinuteOfSeconds;
strongVerifierTimeout: INT ~ oneMinuteOfSeconds;
unknownTime: GMT ← BasicTime.Update[BasicTime.earliestGMT, oneWeekOfSeconds];
Now: PROC RETURNS [now: GMT] ~ INLINE {
now ← BasicTime.Now[
! BasicTime.TimeNotKnown => { now ← unknownTime; CONTINUE } ] };
AuthenticationError: PUBLIC ERROR [problem: Auth.Problem]
← Auth.AuthenticationError;
CallError: PUBLIC ERROR[problem: Auth.CallProblem, whichArg: Auth.Which]
← Auth.CallError;
MarshallError: PRIVATE ERROR ~ CODE;
CantContactAuthServer: PROC ~ {
ERROR };
Lock: TYPE ~ REF LockObject;
LockObject: TYPE ~ MONITORED RECORD [];
Initiator
Identities
SimpleIdentity: TYPE ~ REF SimpleIdentityObject;
SimpleIdentityObject: TYPE ~ RECORD [
name: Name,
password: ROPE,
valid: BOOLTRUE,
credentials: Credentials,
verifier: Verifier
];
StrongIdentity: TYPE ~ REF StrongIdentityObject;
StrongIdentityObject: TYPE ~ RECORD [
next: StrongIdentity,
conversations: StrongConversation,
name: Name,
password: ROPE, -- optional, if present must agree with key
valid: BOOLTRUE,
key: Key
];
Identity: TYPE ~ REF; -- SimpleIdentityObject * StrongIdentityObject
initiatorCacheLock: Lock ~ NEW[LockObject ← []];
strongIdentities: StrongIdentity ← NIL;
AddNewStrongIdentity: ENTRY PROC [lock: Lock ← initiatorCacheLock, newID: StrongIdentity] ~ {
newID.next ← strongIdentities;
strongIdentities ← newID };
RemoveOldStrongIdentity: ENTRY PROC [lock: Lock ← initiatorCacheLock, oldID: StrongIdentity] ~ {
p, prev: StrongIdentity;
p ← strongIdentities; prev ← NIL;
WHILE (p # NIL) AND (p # oldID) DO prev ← p; p ← p.next ENDLOOP;
IF p # NIL THEN {
IF prev # NIL THEN prev.next ← p.next ELSE strongIdentities ← p.next };
};
strongIdentityFinalizerQueue: SafeStorage.FinalizationQueue;
droppedStrongIdentities: CARD ← 0;
StrongIdentityFinalizer: PROC ~ {
Process.SetPriority[Process.priorityBackground];
DO
strongID: StrongIdentity ← NARROW[SafeStorage.FQNext[strongIdentityFinalizerQueue]];
RemoveOldStrongIdentity[oldID~strongID];
droppedStrongIdentities ← droppedStrongIdentities.SUCC;
ENDLOOP;
};
StartStrongIdentityFinalizer: PROC ~ {
established: BOOL;
strongIdentityFinalizerQueue ← SafeStorage.NewFQ[20];
established ← TRUE;
SafeStorage.EstablishFinalization[type~CODE[StrongIdentityObject], npr~1, fq~strongIdentityFinalizerQueue
! SafeStorage.CantEstablishFinalization => { established ← FALSE; CONTINUE }];
IF NOT established THEN {
established ← TRUE;
SafeStorage.ReEstablishFinalization[type~CODE[StrongIdentityObject], npr~1, fq~strongIdentityFinalizerQueue
! SafeStorage.CantEstablishFinalization => { established ← FALSE; CONTINUE }];
};
IF NOT established THEN ERROR;
TRUSTED { Process.Detach[FORK StrongIdentityFinalizer[]]; };
};
MakeIdentity: PUBLIC PROC[name: Name, password: ROPE,
credentialsType: CredentialsType ← strong, check: BOOLTRUE]
RETURNS [identity: Identity]
~ {
SELECT credentialsType FROM
simple => {
simpleID: SimpleIdentity;
simpleID ← NEW[SimpleIdentityObject ← [name~name, password~password, credentials~CredentialsFromName[name], verifier~VerifierFromHashedPassword[SimpleKeyFromPassword[password]]]];
identity ← simpleID;
IF check THEN CheckSimple[simpleID.credentials, simpleID.verifier];
};
strong => {
strongID: StrongIdentity ← NEW[StrongIdentityObject ← [name~name, password~password, key~StrongKeyFromPassword[password]]];
AddNewStrongIdentity[newID~strongID];
SafeStorage.EnableFinalization[strongID];
identity ← strongID;
IF check THEN Terminate[Initiate[identity, authSvcName, 60]];
};
ENDCASE => ERROR;
};
MakeStrongIdentityUsingKey: PUBLIC PROC[name: Name, key: Key, check: BOOLTRUE]
RETURNS [identity: Identity]
~ {
strongID: StrongIdentity;
TRUSTED { DESFace.CorrectParity[LOOPHOLE[LONG[@key]]] };
strongID ← NEW[StrongIdentityObject ← [name~name, password~NIL, key~key]];
AddNewStrongIdentity[newID~strongID];
SafeStorage.EnableFinalization[strongID];
identity ← strongID;
IF check THEN Terminate[Initiate[identity, authSvcName, 60]];
};
DestroyIdentity: PUBLIC PROC [identity: Identity] ~ {
IF identity = NIL THEN RETURN;
WITH identity SELECT FROM
simpleID: SimpleIdentity => {
simpleID.valid ← FALSE;
simpleID.password ← NIL;
simpleID.verifier ← VerifierFromHashedPassword[SimpleKeyFromPassword[NIL]];
};
strongID: StrongIdentity => {
strongID.valid ← FALSE;
strongID.conversations ← NIL;
strongID.password ← NIL;
strongID.key ← StrongKeyFromPassword[NIL];
};
ENDCASE => ERROR;
};
nullIdentity: Identity ← NIL;
nullIdentityLock: Lock ~ NEW[LockObject ← []];
GetNullIdentity: PUBLIC PROC RETURNS [Identity] ~ {
RETURN [EntryGetNullIdentity[]] };
EntryGetNullIdentity: ENTRY PROC [lock: Lock ← nullIdentityLock]
RETURNS [Identity] ~ {
IF nullIdentity = NIL THEN nullIdentity ← MakeIdentity[name~["", "", ""], password~"", credentialsType~simple, check~FALSE];
RETURN [nullIdentity] };
Conversations
SimpleConversation: TYPE ~ REF SimpleConversationObject;
SimpleConversationObject: TYPE ~ RECORD [
initiatorID: SimpleIdentity,
recipientName: Name
];
StrongConversation: TYPE ~ REF StrongConversationObject;
StrongConversationObject: TYPE ~ RECORD [
next: StrongConversation,
initiatorID: StrongIdentity,
timeStamp: GMT,
referenced: BOOLTRUE,
recipientName: Name,
recipientHostNumber: HostNumber,
credentials: Credentials,
conversationKey: Key,
lastStrongVerifier: Auth.StrongVerifier
];
Conversation: TYPE ~ REF; -- SimpleConversationObject * StrongConversationObject
Note there is no simple conversation cache on the initiator's side, since creating a simple conversation does not require contacting the authentication service.
StrongConversationCacheGet: ENTRY PROC[lock: Lock ← initiatorCacheLock,
id: StrongIdentity, name: Name] RETURNS[StrongConversation]
~ {
ENABLE UNWIND => NULL;
p, prev: StrongConversation;
prev ← NIL; p ← id.conversations;
WHILE (p # NIL) AND (NOT EqualNames[name, p.recipientName]) DO
prev ← p; p ← p.next;
ENDLOOP;
IF p # NIL THEN {
IF prev # NIL THEN prev.next ← p.next ELSE id.conversations ← p.next;
p.next ← NIL };
RETURN [p] };
StrongConversationCachePut: ENTRY PROC[lock: Lock ← initiatorCacheLock,
c: StrongConversation, id: StrongIdentity]
~ {
ENABLE UNWIND => NULL;
c.next ← id.conversations;
id.conversations ← c };
SweepStrongConversationCache: ENTRY PROC[lock: Lock ← initiatorCacheLock] ~ {
ENABLE UNWIND => NULL;
p, prev: StrongConversation;
now: GMT ~ Now[];
FOR strongID: StrongIdentity ← strongIdentities, strongID.next WHILE strongID # NIL DO
p ← strongID.conversations; prev ← NIL;
WHILE p # NIL DO
IF NOT p.referenced
THEN -- delete the entry -- {
p ← p.next;
IF prev = NIL THEN strongID.conversations ← p ELSE prev.next ← p }
ELSE -- leave the entry alone -- {
p.referenced ← FALSE;
prev ← p;
p ← p.next };
ENDLOOP;
ENDLOOP;
};
Public Procedures
Initiate: PUBLIC PROC [identity: Identity, recipientName: Name, seconds: CARD]
RETURNS [conversation: Conversation]
~ {
IF identity = NIL THEN ERROR;
WITH identity SELECT FROM
simpleID: SimpleIdentity => {
c: SimpleConversation ← NEW[SimpleConversationObject ← [initiatorID~simpleID, recipientName~recipientName]];
conversation ← c };
strongID: StrongIdentity => {
c: StrongConversation;
c ← StrongConversationCacheGet[id~strongID, name~recipientName];
IF c = NIL THEN
c ← NEW[StrongConversationObject ← [timeStamp~BasicTime.earliestGMT, recipientName~recipientName, recipientHostNumber~XNS.unknownHost, credentials~[strong,NIL], conversationKey~, lastStrongVerifier~initialStrongVerifier]];
c.initiatorID ← strongID;
c.referenced ← TRUE;
Refresh[c, seconds];
conversation ← c };
ENDCASE => ERROR;
};
Refresh: PUBLIC PROC [conversation: Conversation, seconds: CARD]
~ {
IF conversation = NIL THEN ERROR;
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
NULL };
strongC: StrongConversation => { 
seqWords: SeqWords;
credentialsPackage: Auth.CredentialsPackage;
nonce: CARD;
DoGetStrongCredentials: PROC [h: CrRPC.Handle, host: HostNumber] ~ {
seqWords ← Auth.GetStrongCredentials[h, strongC.initiatorID.name, strongC.recipientName, nonce] };
IF (BasicTime.Period[from~strongC.timeStamp, to~Now[]] + INT[seconds]) <= strongCredentialsLifetime THEN RETURN;
nonce ← BasicTime.GetClockPulses[];
CallRemote[DoGetStrongCredentials];
seqWords ← DecryptSeqWords[strongC.initiatorID.key, seqWords];
[cp~credentialsPackage] ← FetchCredentialsPackageFromSeqWords[seqWords, 0
! MarshallError => GOTO Bad];
IF credentialsPackage.nonce # nonce THEN GOTO Bad;
strongC.recipientName ← credentialsPackage.recipient;
strongC.credentials ← credentialsPackage.credentials;
strongC.conversationKey ← credentialsPackage.conversationKey;
strongC.lastStrongVerifier ← initialStrongVerifier;
strongC.timeStamp ← Now[];
EXITS
Bad => ERROR AuthenticationError[credentialsInvalid];
};
ENDCASE => ERROR;
};
Terminate: PUBLIC PROC [conversation: Conversation]
~ {
IF conversation = NIL THEN ERROR;
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
NULL };
strongC: StrongConversation => { 
strongID: StrongIdentity ~ strongC.initiatorID;
strongC.initiatorID ← NIL;
StrongConversationCachePut[c~strongC, id~strongID] };
ENDCASE => ERROR;
};
GetCredentials: PUBLIC PROC [conversation: Conversation]
RETURNS [credentials: Credentials]
~ {
IF conversation = NIL THEN ERROR;
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
credentials ← simpleC.initiatorID.credentials };
strongC: StrongConversation => { 
credentials ← strongC.credentials };
ENDCASE => ERROR;
};
SetRecipientHostNumber: PUBLIC PROC [conversation: Conversation,
recipientHostNumber: HostNumber]
~ {
IF conversation = NIL THEN ERROR;
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
NULL };
strongC: StrongConversation => { 
strongC.recipientHostNumber ← recipientHostNumber };
ENDCASE => ERROR;
};
GetNextVerifier: PUBLIC PROC [conversation: Conversation]
RETURNS [verifier: Verifier]
~ {
IF conversation = NIL THEN ERROR;
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
verifier ← simpleC.initiatorID.verifier };
strongC: StrongConversation => {
now: GMT ~ Now[];
IF BasicTime.Period[
from~BasicTime.FromNSTime[strongC.lastStrongVerifier.timeStamp],
to~now ] > 0
THEN strongC.lastStrongVerifier ← [timeStamp~BasicTime.ToNSTime[now], ticks~0]
ELSE strongC.lastStrongVerifier ← IncrementStrongVerifier[strongC.lastStrongVerifier];
verifier ← EncryptedVerifierFromStrongVerifier[strongC.conversationKey, LOOPHOLE[strongC.recipientHostNumber], strongC.lastStrongVerifier];
};
ENDCASE => ERROR;
};
ReplyVerifierChecks: PUBLIC PROC [conversation: Conversation, verifier: Verifier]
RETURNS [ok: BOOL]
~ {
IF conversation = NIL THEN ERROR;
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
ERROR AuthenticationError[inappropriateCredentials] };
strongC: StrongConversation => {
ENABLE MarshallError => { ok ← FALSE; CONTINUE };
sV: Auth.StrongVerifier ~ StrongVerifierFromEncryptedVerifier[ strongC.conversationKey, LOOPHOLE[strongC.recipientHostNumber], verifier];
ok ← (sV = IncrementStrongVerifier[strongC.lastStrongVerifier]);
IF ok THEN strongC.lastStrongVerifier ← sV };
ENDCASE => ERROR;
};
Recipient
Simple Conversations
RSimpleConversation: TYPE ~ REF RSimpleConversationObject;
RSimpleConversationObject: TYPE ~ RECORD [
next: RSimpleConversation,
referenced: BOOLTRUE,
initiatorName: Name,
credentials: Credentials,
verifier: Verifier
];
rSimpleConversationCacheLock: Lock ← NEW[LockObject ← []];
rSimpleConversationCache: RSimpleConversation ← NIL;
RSimpleConversationCacheGet: ENTRY PROC [
lock: Lock ← rSimpleConversationCacheLock,
credentials: Credentials] RETURNS [RSimpleConversation] ~ {
ENABLE UNWIND => NULL;
p, prev: RSimpleConversation;
IF credentials.type # simple THEN ERROR;
prev ← NIL; p ← rSimpleConversationCache;
WHILE (p # NIL) AND (NOT EqualSeqWords[credentials.value, p.credentials.value]) DO
prev ← p; p ← p.next;
ENDLOOP;
IF p # NIL THEN {
IF prev # NIL THEN prev.next ← p.next ELSE rSimpleConversationCache ← p.next;
p.next ← NIL;
p.referenced ← TRUE };
RETURN [p] };
RSimpleConversationCachePut: ENTRY PROC[
lock: Lock ← rSimpleConversationCacheLock,
conversation: RSimpleConversation] ~ {
ENABLE UNWIND => NULL;
conversation.next ← rSimpleConversationCache;
rSimpleConversationCache ← conversation };
SweepRSimpleConversationCache: ENTRY PROC[lock: Lock ← rSimpleConversationCacheLock] ~ {
ENABLE UNWIND => NULL;
p, prev: RSimpleConversation;
p ← rSimpleConversationCache; prev ← NIL;
WHILE p # NIL DO
IF p.referenced
THEN {
p.referenced ← FALSE;
prev ← p;
p ← p.next }
ELSE {
p ← p.next;
IF prev = NIL THEN rSimpleConversationCache ← p ELSE prev.next ← p };
ENDLOOP;
};
Strong Conversations
RStrongConversation: TYPE ~ REF RStrongConversationObject;
RStrongConversationObject: TYPE ~ RECORD [
next: RStrongConversation,
referenced: BOOLTRUE,
credentials: Credentials,
decryptedStrongCredentials: Auth.StrongCredentials,
lastStrongVerifier: Auth.StrongVerifier
];
rStrongConversationCacheLock: Lock ← NEW[LockObject ← []];
rStrongConversationCache: RStrongConversation ← NIL;
RStrongConversationCacheGet: ENTRY PROC [
lock: Lock ← rStrongConversationCacheLock,
credentials: Credentials] RETURNS [RStrongConversation] ~ {
ENABLE UNWIND => NULL;
p, prev: RStrongConversation;
IF credentials.type # strong THEN ERROR;
prev ← NIL; p ← rStrongConversationCache;
WHILE (p # NIL) AND (NOT EqualSeqWords[credentials.value, p.credentials.value]) DO
prev ← p; p ← p.next;
ENDLOOP;
IF p # NIL THEN {
IF prev # NIL THEN prev.next ← p.next ELSE rStrongConversationCache ← p.next;
p.next ← NIL;
p.referenced ← TRUE };
RETURN [p] };
RStrongConversationCachePut: ENTRY PROC[
lock: Lock ← rStrongConversationCacheLock,
conversation: RStrongConversation] ~ {
ENABLE UNWIND => NULL;
conversation.next ← rStrongConversationCache;
rStrongConversationCache ← conversation };
SweepRStrongConversationCache: ENTRY PROC[lock: Lock ← rStrongConversationCacheLock] ~ {
ENABLE UNWIND => NULL;
p, prev: RStrongConversation;
p ← rStrongConversationCache; prev ← NIL;
WHILE p # NIL DO
IF p.referenced
THEN {
p.referenced ← FALSE;
prev ← p;
p ← p.next }
ELSE {
p ← p.next;
IF prev = NIL THEN rStrongConversationCache ← p ELSE prev.next ← p };
ENDLOOP;
};
Cache Invalidation on Rollback
InvalidateRecipientCaches: Booting.RollbackProc ~ {
rSimpleConversationCache ← NIL;
rStrongConversationCache ← NIL;
};
Public Procedures
Authenticate: PUBLIC PROC [myIdentity: Identity,
hisCredentials: Credentials, hisVerifier: Verifier,
allowSimpleCredentials: BOOL,
useExpiredCredentials: BOOL]
RETURNS [hisName: Name]
~ {
IF myIdentity = NIL THEN ERROR;
SELECT hisCredentials.type FROM
simple => {
IF NOT allowSimpleCredentials THEN ERROR AuthenticationError[inappropriateCredentials];
hisName ← AuthenticateSimple[hisCredentials, hisVerifier] };
strong => {
[hisName~hisName] ← AuthenticateStrong[myIdentity~myIdentity, hisCredentials~hisCredentials, hisVerifier~hisVerifier, useExpiredCredentials~useExpiredCredentials, computeReplyVerifier~FALSE] };
ENDCASE => ERROR;
};
AuthenticateAndReply: PUBLIC PROC [myIdentity: Identity,
hisCredentials: Credentials, hisVerifier: Verifier,
useExpiredCredentials: BOOL]
RETURNS [hisName: Name, replyVerifier: Verifier] ~ {
IF myIdentity = NIL THEN ERROR;
SELECT hisCredentials.type FROM
simple => {
ERROR AuthenticationError[inappropriateCredentials];
};
strong => {
[hisName, replyVerifier] ← AuthenticateStrong[myIdentity~myIdentity, hisCredentials~hisCredentials, hisVerifier~hisVerifier, useExpiredCredentials~useExpiredCredentials, computeReplyVerifier~TRUE];
};
ENDCASE => ERROR;
};
Private Utilities
AuthenticateSimple: PROC [hisCredentials: Credentials, hisVerifier: Verifier]
RETURNS [hisName: Name]
~ {
rSimpleC: RSimpleConversation;
rSimpleC ← RSimpleConversationCacheGet[credentials~hisCredentials];
IF (rSimpleC = NIL) OR (NOT EqualSeqWords[hisVerifier, rSimpleC.verifier]) THEN {
CheckSimple[hisCredentials, hisVerifier];
rSimpleC ← NEW[RSimpleConversationObject ← [initiatorName~FetchNameFromSeqWords[hisCredentials.value, 0].name, credentials~hisCredentials, verifier~hisVerifier]];
};
hisName ← rSimpleC.initiatorName;
RSimpleConversationCachePut[conversation~rSimpleC];
};
verifierTimeout: INT ~ 60;
AuthenticateStrong: PROC [myIdentity: Identity,
hisCredentials: Credentials, hisVerifier: Verifier,
useExpiredCredentials: BOOLFALSE, computeReplyVerifier: BOOLFALSE]
RETURNS [hisName: Name, replyVerifier: Verifier ← NIL] ~ {
rStrongC: RStrongConversation;
WITH myIdentity SELECT FROM
myStrongID: StrongIdentity => {
rStrongC ← RStrongConversationCacheGet[credentials~hisCredentials];
IF rStrongC = NIL THEN {
ENABLE MarshallError => CONTINUE;
temp: SeqWords ← DecryptSeqWords[key~myStrongID.key, seqWords~hisCredentials.value];
rStrongC ← NEW[RStrongConversationObject ← [credentials~hisCredentials, decryptedStrongCredentials~FetchStrongCredentialsFromSeqWords[temp,0].sc, lastStrongVerifier~initialStrongVerifier]];
};
IF rStrongC = NIL THEN ERROR AuthenticationError[credentialsInvalid];
IF NOT useExpiredCredentials THEN {
expired: BOOL ~ (BasicTime.Period[
from~Now[],
to~BasicTime.FromNSTime[rStrongC.decryptedStrongCredentials.expirationTime
]] <= 0);
IF expired THEN ERROR AuthenticationError[credentialsExpired] };
{ ENABLE MarshallError, BasicTime.OutOfRange => GOTO Bad;
temp: Auth.StrongVerifier ← StrongVerifierFromEncryptedVerifier[
rStrongC.decryptedStrongCredentials.conversationKey, LOOPHOLE[myHostNumber], hisVerifier];
IF NOT StrongVerifierGT[new~temp, old~rStrongC.lastStrongVerifier] THEN ERROR AuthenticationError[verifierReused];
IF StrongVerifierExpired[temp] THEN ERROR AuthenticationError[verifierExpired];
IF computeReplyVerifier
THEN { rStrongC.lastStrongVerifier ← IncrementStrongVerifier[temp];
replyVerifier ← EncryptedVerifierFromStrongVerifier[
rStrongC.decryptedStrongCredentials.conversationKey, LOOPHOLE[myHostNumber], rStrongC.lastStrongVerifier] }
ELSE {
rStrongC.lastStrongVerifier ← temp };
EXITS
Bad => ERROR AuthenticationError[verifierInvalid];
};
hisName ← rStrongC.decryptedStrongCredentials.initiator;
RStrongConversationCachePut[conversation: rStrongC];
};
mySimpleID: SimpleIdentity => {
ERROR AuthenticationError[inappropriateCredentials] };
ENDCASE => ERROR;
};
CheckSimple: PROC[credentials: Credentials, verifier: Verifier] ~ {
Just return if okay, else raise CallError or AuthenticationError.
okay: BOOLFALSE;
DoCheckSimple: PROC [h: CrRPC.Handle, host: HostNumber]
~ { okay ← Auth.CheckSimpleCredentials[h, credentials, verifier] };
CallRemote[DoCheckSimple];
IF NOT okay -- can this happen? -- THEN
ERROR AuthenticationError[problem~credentialsInvalid];
};
Key and Password Administration
ChangeMyPasswords: PUBLIC PROC [myIdentity: Identity, newPassword: ROPE,
changeStrong: BOOLTRUE, changeSimple: BOOLTRUE] ~ {
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];
};
Public Utilities
StrongKeyFromPassword: PUBLIC PROC [password: ROPE] RETURNS [key: Key] ~ {
index: INT;
passwordLen: INT ~ Rope.Length[password];
ki: DESFace.Key ← DESFace.nullKey;
bi: Block;
biPlus1: Block;
FOR index ← 0, index + 4 WHILE index < passwordLen DO
len: INT ~ MIN[4, passwordLen-index];
TRUSTED {
StoreSubropeInBlock[password, index, len, @bi, TRUE];
DESFace.CorrectParity[@ki];
DESFace.EncryptBlock[key~ki, from~@bi, to~LOOPHOLE[LONG[@ki]]] };
DESFace.EncryptBlock[key~ki, from~@bi, to~@biPlus1];
ki ← LOOPHOLE[biPlus1] };
ENDLOOP;
TRUSTED { DESFace.CorrectParity[@ki] };
RETURN [ LOOPHOLE[ki] ];
};
SimpleKeyFromPassword: PUBLIC PROC [password: ROPE]
RETURNS [hashVal: HashedPassword ← 0] ~ {
c: CHAR;
acc: Basics.LongNumber;
FOR i: INT IN [0 .. Rope.Length[password]) DO
c ← Rope.InlineFetch[password, i];
SELECT c FROM
IN ['A .. 'Z] => c ← 'a + (c - 'A);
ENDCASE;
acc.hi ← hashVal; acc.lo ← ORD[c];
hashVal ← acc.lc MOD 65357;
ENDLOOP;
};
GetCredentialsType: PUBLIC PROC [credentials: Credentials]
RETURNS [CredentialsType] ~ {
RETURN [credentials.type] };
GetConversationDetails: PUBLIC PROC [conversation: Conversation] RETURNS [
recipientName: Name, recipientHostNumber: HostNumber, credentials: Credentials, conversationKey: Key, owner: Identity] ~ {
IF conversation = NIL THEN ERROR;
WITH conversation SELECT FROM
simpleC: SimpleConversation => {
recipientName ← simpleC.recipientName;
recipientHostNumber ← XNS.unknownHost;
credentials ← simpleC.initiatorID.credentials;
conversationKey ← ALL[0];
owner ← simpleC.initiatorID };
strongC: StrongConversation => {
recipientName ← strongC.recipientName;
recipientHostNumber ← strongC.recipientHostNumber;
credentials ← strongC.credentials;
conversationKey ← strongC.conversationKey;
owner ← strongC.initiatorID };
ENDCASE => ERROR;
};
GetIdentityDetails: PUBLIC PROC [identity: Identity] RETURNS [
name: Name, password: ROPE, credentialsType: CredentialsType] ~ {
IF identity = NIL THEN ERROR;
WITH identity SELECT FROM
simpleI: SimpleIdentity => {
name ← simpleI.name;
password ← simpleI.password;
credentialsType ← simple };
strongI: StrongIdentity => {
name ← strongI.name;
password ← strongI.password;
credentialsType ← strong };
ENDCASE => ERROR;
};
GetCredentialsDetails: PUBLIC PROC [myKey: Key, hisCredentials: Credentials]
RETURNS [ok: BOOL, credentialsType: CredentialsType, conversationKey: Key,
expirationTime: GMT, hisName: Name] ~ {
ok ← FALSE;
SELECT hisCredentials.type FROM
simple => {
ENABLE MarshallError => CONTINUE;
credentialsType ← simple;
conversationKey ← ALL[0];
expirationTime ← BasicTime.latestGMT;
hisName ← FetchNameFromSeqWords[hisCredentials.value, 0].name;
ok ← TRUE;
};
strong => {
ENABLE MarshallError => CONTINUE;
rStrongC: RStrongConversation;
sC: Auth.StrongCredentials;
credentialsType ← strong;
rStrongC ← RStrongConversationCacheGet[credentials~hisCredentials];
IF rStrongC # NIL
THEN {
sC ← rStrongC.decryptedStrongCredentials;
RStrongConversationCachePut[conversation: rStrongC] }
ELSE {
temp: SeqWords ← DecryptSeqWords[key~myKey, seqWords~hisCredentials.value];
[sc~sC] ← FetchStrongCredentialsFromSeqWords[temp,0] };
conversationKey ← sC.conversationKey;
expirationTime ← BasicTime.FromNSTime[sC.expirationTime];
hisName ← sC.initiator;
ok ← TRUE;
};
ENDCASE => ERROR;
};
Daemon
Daemon: PROC ~ {
DO
Process.Pause[Process.SecondsToTicks[secondsBetweenSweeps]];
SweepStrongConversationCache[];
SweepRStrongConversationCache[];
ENDLOOP;
};
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.sc ← 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] ← w.sc; where ← where + 1; -- USED TO BE LOOPHOLE[w] ????
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];
};
FetchCard32FromSeqWords: PROC [s: SeqWords, where: CARDINAL]
RETURNS [newWhere: CARDINAL, result: CARD32] ~ {
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] ← FetchCard32FromSeqWords[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] ← FetchCard32FromSeqWords[s, where];
[where, sc.initiator] ← FetchNameFromSeqWords[s, where];
newWhere ← where };
StoreSubropeInBlock: PROC [r: ROPE, index, len: INT,
to: LONG POINTER TO Block, mapCase: BOOLTRUE] ~ {
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] ~ {
verifier ← NEW[SeqWordsObject[1]];
verifier.body[0] ← hashedPassword };
EncryptSeqWords: PROC [key: Key, seqWords: SeqWords] RETURNS [result: SeqWords] ~ {
nBlocks: CARDINAL ← (seqWords.length*BITS[CARD16] + BITS[Block] - 1) / BITS[Block];
roundedLength: CARDINAL ← nBlocks * (BITS[Block]/BITS[CARD16]);
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. ????
nBlocks: CARDINAL ~ (seqWords.length*BITS[CARD16]) / BITS[Block];
roundedLength: CARDINAL ← nBlocks * (BITS[Block]/BITS[CARD16]);
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: CARD16],
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: CARD16],
verifier: Verifier] RETURNS [strongVerifier: Auth.StrongVerifier] ~ {
IF verifier.length # (BITS[Auth.StrongVerifier]/BITS[CARD16]) 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,
refAddress: REF XNS.Address, -- REF conforms to CrRPC.RefAddress
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.refAddress^ # addr) DO { prev ← p; p ← p.next } ENDLOOP;
IF p = NIL
THEN {
new ← NEW[ServerInfoObject ← [
next~NIL,
refAddress~NEW[XNS.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.refAddress.net]) # p.hops) => {
DeleteServer[it~p];
AddServerAddress[addr~p.refAddress^, 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.refAddress];
proc[h, p.refAddress.host]; CleanUp[]; RETURN;
END;
ENDLOOP;
CleanUp[]; CantContactAuthServer[];
};
Debugging Stuff
GetStrongCredentialsLifetime: PROC [name: Name, password: ROPE]
RETURNS [ok: BOOLFALSE, seconds: INT ← 0, from, to: BasicTime.Unpacked] ~ {
ENABLE CallError, AuthenticationError => CONTINUE;
id: Identity;
c: Conversation;
t1, t2, tExp: GMT;
myKey: Key;
hisCredentials: Credentials;
id ← MakeIdentity[name, password, strong, FALSE];
t1 ← Now[];
c ← Initiate[id, name, 3600];
t2 ← Now[];
myKey ← StrongKeyFromPassword[password];
[credentials~hisCredentials] ← GetConversationDetails[c];
[ok~ok, expirationTime~tExp] ← GetCredentialsDetails[myKey~myKey, hisCredentials~hisCredentials];
IF ok THEN {
seconds ← (BasicTime.Period[from~t1, to~tExp] + BasicTime.Period[from~t2, to~tExp] + 1) / 2;
from ← BasicTime.Unpack[t1];
to ← BasicTime.Unpack[tExp];
};
Terminate[c];
};
AuthenticateToSelf: PROC [name: Name, password: ROPE, howMany: CARDINAL] ~ {
count: CARDINAL ← 0;
id: Identity;
c: Conversation;
credentials: Credentials;
verifier: Verifier;
replyVerifier: Verifier;
myHostNumber: HostNumber;
id ← MakeIdentity[name, password, strong, FALSE];
c ← Initiate[id, name, 3600];
myHostNumber ← XNS.GetThisHost[];
SetRecipientHostNumber[c, myHostNumber];
WHILE count < howMany DO
credentials ← GetCredentials[c];
verifier ← GetNextVerifier[c];
[replyVerifier~replyVerifier] ← AuthenticateAndReply[id, credentials, verifier, FALSE];
IF NOT ReplyVerifierChecks[c, replyVerifier] THEN ERROR AuthenticationError[verifierInvalid];
count ← count + 1;
ENDLOOP;
Terminate[c];
};
Main Line Code
InitialBroadcastForServers: PROC ~ TRUSTED {
Process.Detach[ FORK BroadcastForServers[maxHops~defaultServerHops, nWanted~2]] };
StartStrongIdentityFinalizer[];
Booting.RegisterProcs[r~InvalidateRecipientCaches];
TRUSTED { Process.Detach[ FORK Daemon[]] };
InitialBroadcastForServers[];
}.