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 []; SimpleIdentity: TYPE ~ REF SimpleIdentityObject; SimpleIdentityObject: TYPE ~ RECORD [ name: Name, password: ROPE, valid: BOOL _ TRUE, 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: BOOL _ TRUE, key: Key ]; Identity: TYPE ~ REF; -- SimpleIdentityObject U 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: BOOL _ TRUE] 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: BOOL _ TRUE] 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] }; 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: BOOL _ TRUE, recipientName: Name, recipientHostNumber: HostNumber, credentials: Credentials, conversationKey: Key, lastStrongVerifier: Auth.StrongVerifier ]; Conversation: TYPE ~ REF; -- SimpleConversationObject U StrongConversationObject 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; }; 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; }; RSimpleConversation: TYPE ~ REF RSimpleConversationObject; RSimpleConversationObject: TYPE ~ RECORD [ next: RSimpleConversation, referenced: BOOL _ TRUE, 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; }; RStrongConversation: TYPE ~ REF RStrongConversationObject; RStrongConversationObject: TYPE ~ RECORD [ next: RStrongConversation, referenced: BOOL _ TRUE, 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; }; InvalidateRecipientCaches: Booting.RollbackProc ~ { rSimpleConversationCache _ NIL; rStrongConversationCache _ NIL; }; 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; }; 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: BOOL _ FALSE, computeReplyVerifier: BOOL _ FALSE] 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] ~ { okay: BOOL _ FALSE; 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]; }; 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]; }; 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~@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: PROC ~ { DO Process.Pause[Process.SecondsToTicks[secondsBetweenSweeps]]; SweepStrongConversationCache[]; SweepRStrongConversationCache[]; ENDLOOP; }; 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] ~ { 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 + 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] ~ { 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^ }; }; 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; 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 }; 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[]; }; GetStrongCredentialsLifetime: PROC [name: Name, password: ROPE] RETURNS [ok: BOOL _ FALSE, 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]; }; StartStrongIdentityFinalizer[]; Booting.RegisterProcs[r~InvalidateRecipientCaches]; TRUSTED { Process.Detach[ FORK Daemon[]] }; TRUSTED { Process.Detach[ FORK BroadcastForServers[maxHops~defaultServerHops, nWanted~2]] }; }. ΠXNSAuthImpl.mesa Demers, December 8, 1986 10:48:20 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. Initiator Identities Conversations Note there is no simple conversation cache on the initiator's side, since creating a simple conversation does not require contacting the authentication service. Public Procedures Recipient Simple Conversations Strong Conversations Cache Invalidation on Rollback Public Procedures Private Utilities Just return if okay, else raise CallError or AuthenticationError. Key and Password Administration Public Utilities DESFace.EncryptBlock[key~ki, from~@bi, to~LOOPHOLE[LONG[@ki]]] }; Daemon Private Utilities Store len chars in block using 16-bit encoding. ???? WORD SIZE DEPENDENT ???? ???? Word size dependent. Should use BYTES in here instead of SIZE ???? ???? Word size dependent. Should use BYTES in here instead of SIZE ???? Locating Authentication Servers and Performing Remote Calls Delete old entry for this address (hops may have changed) Add new entry in correct position (sorted by hops) Debugging Stuff Main Line Code Κ+μ˜™J™(J™J™ΙJ™—codešΟk ˜ Kšœœ΄˜ΝKšœœœ˜/Kšœ œ+œX˜•Kšœœ˜,KšœœP˜[Kšœœ^˜kKšœœB˜OKšœœ3˜@Kšœœ+œ˜;Kšœ œƒ˜”Kšœ œ˜Kšœœ:˜CKšœœE˜RK˜ Kšœœ1˜HKšœœ˜!Kšœ˜K˜—šΟn œœ˜Kšœœ ˜Kšœgœ˜Kšœ˜K˜Kšœžœžœ ˜0K˜Kšœ œ˜Kšœœ˜K˜Kšœ œ˜Kšœœ˜+Kšœœ œ˜Kšœœ˜Kšœœœ˜Kšœœ ˜Kšœ œ˜(Kšœœ˜0Kšœœ˜.Kšœœ˜Kšœœ˜Kšœ œ˜"Kšœ œœ˜K˜K˜Kšœœ˜-K˜K˜IK˜Kšœ\˜\K˜Kšœœ˜Kšœœ˜.Kšœœœ˜1Kšœœœ˜0K˜KšœœΟc˜HKšœœ˜1Kšœœ˜0K˜Kšœ œ=˜MK˜š žœœœœœ˜'šœ˜Kšœ1œ˜@——K˜šžœ œ˜9Kšœ˜—šž œ œ1˜HK˜—K˜Kšž œ œœ˜$K˜šžœœ˜Kšœ˜—K˜Kšœœœ ˜Kšœ œ œœ˜'K˜head™ ™ Kšœœœ˜0šœœœ˜%K˜ Kšœ œ˜Kšœœœ˜K˜K˜K˜K˜—Kšœœœ˜0šœœœ˜%Kšœ˜K˜"K˜ Kšœ œŸ+˜;Kšœœœ˜Kšœ˜K˜K˜—Kšœ œœŸΠcmŸ˜DK˜Kšœœ˜0Kšœ#œ˜'K˜šžœ œ=˜]K˜K˜K˜—šžœœœ=˜`K˜Kšœœ˜"Kš œœœ œœ˜@šœœœ˜Kšœœœœ˜G—K˜K˜—Kšœ<˜˜iKšœ;œœ˜N—šœœ œ˜Kšœœ˜šœ)œ>˜kKšœ;œœ˜N—K˜—Kšœœ œœ˜Kšœœ˜K˜Kšœ˜—šœœœ˜Kšœœœœ˜EKšœ œ˜—Kšœ˜ K˜—šžœœœL˜rK˜Kšœœœ˜K˜Kšœ˜K˜—šžœœœ%˜MKšœœœ˜K˜Kšœœ ˜šœ<œ œ˜VKšœ#œ˜'šœœ˜šœœ ˜šœŸœ˜Kšœ ˜ Kšœœœœ˜B—šœŸœ˜"Kšœœ˜Kšœ ˜ Kšœ ˜ ——Kšœ˜—Kšœ˜—K˜——™š žœœœ4œœ˜sKšœ˜Kšœ œœœ˜šœ œ˜˜KšœœQ˜mK˜—˜K˜Kšœ@˜@šœœ˜Kšœœ”œ@˜ή—Kšœ˜Kšœœ˜Kšœ˜K˜—Kšœœ˜—K˜K˜—šžœ œ'œ˜@Kšœ˜Kšœœœœ˜!šœœ˜šœ ˜ Kšœ˜—šœ!˜!K˜K˜,Kšœœ˜ šžœœ(˜DK˜b—Kšœ7œ(œœ˜pKšœ#˜#Kšœ#˜#Kšœ>˜>˜IKšœœ˜—Kšœ"œœ˜2Kšœ5˜5Kšœ5˜5Kšœ=˜=Kšœ3˜3K˜š˜Kšœœ)˜5—K˜—Kšœœ˜—K˜K˜—šž œ œ˜3Kšœ˜Kšœœœœ˜!šœœ˜šœ ˜ Jšœ˜—šœ!˜!Kšœ/˜/Kšœœ˜Kšœ5˜5—Kšœœ˜—K˜K˜—šžœ œœ˜[Kšœ˜Kšœœœœ˜!šœœ˜šœ ˜ Kšœ0˜0—šœ!˜!Kšœ$˜$—Kšœœ˜—K˜K˜—šžœœœ>˜aK˜Kšœœœœ˜!šœœ˜šœ ˜ Kšœ˜—šœ!˜!Kšœ4˜4—Kšœœ˜—K˜K˜—šžœœœœ˜VKšœ˜Kšœœœœ˜!šœœ˜šœ ˜ Kšœ*˜*—šœ ˜ Kšœœ ˜šœb˜bKšœJ˜NKšœR˜V—KšœHœ;˜‹Kšœ˜—Kšœœ˜—K˜K˜—šžœ œ2œœ˜dKšœ˜Kšœœœœ˜!šœœ˜šœ ˜ Kšœ1˜6—šœ ˜ Kšœœœ˜1KšœXœ)˜‰Kšœ@˜@Kšœœ#˜-—Kšœœ˜—K˜———™ ™Kšœœœ˜:šœœœ˜*K˜Jšœ œœ˜K˜K˜K˜K˜K˜—Kšœ%œ˜:Kšœ0œ˜4K˜šžœ œHœ˜Kšœœœ˜Kšœ˜Kšœœœ˜(Kšœœ ˜*š œœœœ8˜RK˜Kšœ˜—šœœœ˜Kšœœœœ#˜MKšœ œ˜ Kšœœ˜—Kšœ˜ K˜—šžœœœS˜zKšœœœ˜Kšœ-˜-Kšœ*˜*K˜—šžœœœ/˜XKšœœœ˜K˜Kšœ%œ˜)šœœ˜šœ ˜šœ˜Kšœœ˜Kšœ ˜ Kšœ ˜ —šœ˜Kšœ ˜ Kšœœœœ˜E——Kšœ˜—K˜——™Kšœœœ˜:šœœœ˜*Kšœ˜Jšœ œœ˜K˜J˜3K˜'K˜K˜—Kšœ%œ˜:Kšœ0œ˜4K˜šžœ œHœ˜Kšœœœ˜Kšœ˜Kšœœœ˜(Kšœœ ˜*š œœœœ8˜RK˜Kšœ˜—šœœœ˜Kšœœœœ#˜MKšœ œ˜ Kšœœ˜—Kšœ˜ K˜—šžœœœS˜zKšœœœ˜Kšœ-˜-Kšœ*˜*K˜—šžœœœ/˜XKšœœœ˜Kšœ˜Kšœ%œ˜)šœœ˜šœ ˜šœ˜Kšœœ˜Kšœ ˜ Kšœ ˜ —šœ˜Kšœ ˜ Kšœœœœ˜E——Kšœ˜—K˜K˜——™šžœ˜3Kšœœ˜Kšœœ˜K˜——™š ž œ œdœœœ˜·K˜Kšœœœœ˜šœ˜˜ Kšœœœœ/˜WKšœ<˜<—˜ KšœΈœ˜Α—Kšœœ˜—K˜K˜—š žœœœcœœ-˜ΎKšœœœœ˜šœ˜˜ Jšœ/˜4K˜—˜ KšœΏœ˜ΕK˜—Kšœœ˜—K˜K˜——™šžœœ6œ˜eK˜K˜K˜Cš œ œœœ0œ˜QKšœ)˜)Kšœ œ”˜’K˜—Kšœ!˜!Kšœ3˜3Kšœ˜K˜—Kšœœ˜K˜šžœœcœœœœœ+œ˜ηKšœ˜šœ œ˜˜K˜Cšœ œœ˜Kšœœ˜!KšœT˜TKšœ œ―˜½K˜—Kšœ œœœ)˜Ešœœœ˜#šœ œ˜"Kšœ ˜ KšœJ˜JKšœ ˜ —Kšœ œœ+˜@—šœœ(œ˜9Kšœvœ˜›Kšœœ= œ%˜rKšœœœ&˜Ošœ˜šœ?˜CKšœjœ.˜ —šœ˜Kšœ%˜%——š˜Kšœœ&˜2—K˜—Kšœ8˜8Kšœ4˜4K˜—˜Kšœ1˜6—Kšœœ˜—K˜K˜—šž œœ2˜CKšœA™AKšœœœ˜šž œœ$˜7KšœC˜C—Kšœ˜šœœŸœ˜'Kšœ1˜6—K˜———™šžœ œ%œœœœœ˜‚šœœ˜Kšœ1˜1Kšœ(˜(—šœœ˜Kšœ<˜˜>Kšœœ˜ K˜—˜ Kšœœ˜!Kšœ˜K˜K˜K˜Cšœ ˜šœ˜Kšœ)˜)Kšœ5˜5—šœ˜KšœK˜KKšœ7˜7——Kšœ%˜%Kšœ9˜9Kšœ˜Kšœœ˜ Kšœ˜—Kšœœ˜—K˜——™šžœœ˜š˜K˜Kšœ%˜%Kšœ˜ K˜—šžœœœ˜MKšœ2˜8K˜—šžœœ"œ˜bKšœ™Kšœ œ˜"Kšœ$˜$K˜—šžœœ œ˜SK™HKšœ œœœ˜FKšœœ œ˜0Kšœ œ ˜,šœ!œ˜)šœœœ˜'Kšœ#œ˜-—šœœœ!˜3Kšœœ˜—Kšœ˜—šœ˜ Kš œœœ œœœ˜dKš œœœ œœœ˜`Kšœœ&œ˜NK˜—Kšœ˜K˜—šžœœ œ˜SK™HKšœ œœ˜2Kšœœ œ˜0Kšœ!œœ˜˜\K˜—J˜—…—£ ΣΖ