DIRECTORY BasicTime USING [ earliestGMT, GetClockPulses, Now, Period ], BufferDefs USING [ PupBuffer ], ConvertUnsafe USING [ ToRope ], DESFace, GVNames USING [ AuthenticateKey, CheckStamp ], PrincOpsUtils USING [ IsBound, LongCopy ], PrincOps USING [ PsbNull ], PupDefs USING [ AnyLocalPupAddress, PupRouterSendThis, ReturnFreePupBuffer], PupTypes USING [ PupAddress ], Rope USING [ Flatten, Length, Text ], RPC USING [ AuthenticateFailed, Conversation, ConversationLevel, EncryptionKey, maxPrincipalLength, Principal, SecurityLevel, unencrypted ], RPCInternal USING [ Authenticator, AuthenticatorObject, ConversationObject ], RPCInternalExtras, RPCLupine USING [ DataLength, Dispatcher, GetStubPkt, Header, pktOverhead, RPCPkt, StubPkt ], RPCPkt USING [ CallCount, ConnectionID, ConversationID, DispatcherDetails, Header, Machine, PktConversationID, PktExchange, PktID, pktLengthOverhead, SetupResponse ], RPCPrivate USING [ rpcSocket, ReturnBuffer ]; RPCSecurity: MONITOR IMPORTS BasicTime, ConvertUnsafe, DESFace, GVNames, PrincOpsUtils, PupDefs, Rope, RPC, RPCLupine, RPCPkt, RPCPrivate EXPORTS RPC--encryption stuff--, RPCInternal--GetAuthenticator--, RPCInternalExtras, RPCLupine SHARES RPCLupine = { Header: PUBLIC TYPE = RPCPkt.Header; ConcreteHeader: PROC [abstract: LONG POINTER TO RPCLupine.Header] RETURNS [LONG POINTER TO Header] = INLINE {RETURN [ abstract ] }; GiveBackBuffer: PROC [b: BufferDefs.PupBuffer] = IF PrincOpsUtils.IsBound[LOOPHOLE[RPCPrivate.ReturnBuffer]] THEN RPCPrivate.ReturnBuffer ELSE PupDefs.ReturnFreePupBuffer; CopyPrincipal: PUBLIC PROC [from: RPC.Principal, to: LONG POINTER] = { flat: Rope.Text = Rope.Flatten[from]; text: LONG POINTER TO TEXT = LOOPHOLE[flat]; PrincOpsUtils.LongCopy[from: LOOPHOLE[text], to: to, nwords: SIZE[StringBody[text.length]]]; }; ConversationID: PUBLIC TYPE = RPCPkt.ConversationID; ConversationObject: PUBLIC TYPE = RPCInternal.ConversationObject; Conversation: TYPE = REF ConversationObject; lastConversation: ConversationID; firstConversation: PUBLIC RPCPkt.PktConversationID; GenerateConversation: PUBLIC SAFE PROC RETURNS [conversation: Conversation] = TRUSTED { RETURN[EntryGenerate[none, , NIL, NIL, DESFace.nullKey, NIL]]; }; EntryGenerate: PUBLIC ENTRY PROC [ level: RPC.SecurityLevel, iv: DESFace.IV, originator, responder: RPC.Principal, convKey: DESFace.Key, auth: RPCInternal.Authenticator] RETURNS [ conversation: Conversation ] = { lastConversation.count.ls _ lastConversation.count.ls+1; IF lastConversation.count.ls = 0 THEN lastConversation.count.ms _ lastConversation.count.ms+1; conversation _ AddConversation[lastConversation, level, iv, originator, responder, convKey, auth]; }; AuthenticatorLayout: TYPE = MACHINE DEPENDENT RECORD[ ky(0): DESFace.Key, kySpare(4): DESFace.Key, -- space for larger keys! ck(8): DESFace.Key, ckSpare(12): DESFace.Key, -- space for larger keys! time(16): LONG CARDINAL, a(18): StringBody ]; BadConversationLevel: ERROR = CODE; StartConversation: PUBLIC SAFE PROC [caller: RPC.Principal, key: RPC.EncryptionKey, callee: RPC.Principal, level: RPC.ConversationLevel] RETURNS [conversation: Conversation] = TRUSTED { authenticator: RPCInternal.Authenticator; convKey: DESFace.Key; iv: DESFace.IV; IF level NOT IN RPC.ConversationLevel THEN ERROR BadConversationLevel[]; [authenticator, convKey, iv] _ Authenticate[caller, key, callee]; RETURN[ EntryGenerate[level, iv, caller, callee, convKey, authenticator] ] }; Authenticate: PROC [caller: RPC.Principal, key: RPC.EncryptionKey, callee: RPC.Principal] RETURNS [ authenticator: RPCInternal.Authenticator, convKey: DESFace.Key, iv: DESFace.IV] = { nBlks: CARDINAL = ( SIZE[AuthenticatorLayout] - SIZE[StringBody[0]] + SIZE[StringBody[caller.Length[]]] + SIZE[DESFace.Block]-1 --rounding-- ) / SIZE[DESFace.Block]; IF caller.Length[] > RPC.maxPrincipalLength THEN ERROR RPC.AuthenticateFailed[badCaller]; IF callee.Length[] > RPC.maxPrincipalLength THEN ERROR RPC.AuthenticateFailed[badCallee]; SELECT GVNames.AuthenticateKey[caller, key] FROM group, notFound => ERROR RPC.AuthenticateFailed[badCaller]; allDown => ERROR RPC.AuthenticateFailed[communications]; badPwd => ERROR RPC.AuthenticateFailed[badKey]; ENDCASE => NULL; SELECT GVNames.CheckStamp[callee] FROM group, notFound => ERROR RPC.AuthenticateFailed[badCallee]; allDown => ERROR RPC.AuthenticateFailed[communications]; ENDCASE => NULL; iv _ DESFace.GetRandomIV[]; authenticator _ NEW[RPCInternal.AuthenticatorObject[nBlks]]; { kb: DESFace.Key _ DESFace.nullKey -- !!! --; authRec: LONG POINTER TO AuthenticatorLayout = LOOPHOLE[@authenticator[0]]; authRec.ky _ DESFace.GetRandomKey[]; authRec.ck _ convKey _ DESFace.GetRandomKey[]; authRec.time _ 0; CopyPrincipal[from: caller, to: @(authRec.a)]; DESFace.CorrectParity[@kb]; DESFace.EncryptBlock[key: kb, from: LOOPHOLE[@authRec.ck], to: LOOPHOLE[@authRec.ck] ]; DESFace.CBCCheckEncrypt[key: authRec.ky, nBlks: nBlks-2, from: LOOPHOLE[@authRec.ck], to: LOOPHOLE[@authRec.ck], seed: [0,0,0,0] ]; DESFace.EncryptBlock[key: kb, from: LOOPHOLE[@authRec.ky], to: LOOPHOLE[@authRec.ky] ]; }; }; ConvHashKey: TYPE = [0..127]; conversations: REF ARRAY ConvHashKey OF Conversation = NEW[ARRAY ConvHashKey OF Conversation _ ALL[NIL]]; InconsistentHashTable: ERROR = CODE; EntryAddConversation: ENTRY PROC [ id: ConversationID, level: RPC.SecurityLevel, iv: DESFace.IV, originator, responder: RPC.Principal, convKey: DESFace.Key, auth: RPCInternal.Authenticator] RETURNS [ conversation: Conversation ] = INLINE { RETURN[AddConversation[id, level, iv, originator, responder, convKey, auth]]; }; AddConversation: INTERNAL PROC [ id: ConversationID, level: RPC.SecurityLevel, iv: DESFace.IV, originator, responder: RPC.Principal, convKey: DESFace.Key, auth: RPCInternal.Authenticator] RETURNS [ conversation: Conversation ] = { hash: ConvHashKey = id.count.ls MOD SUCC[LAST[ConvHashKey]]; prev: Conversation _ NIL; conversation _ conversations[hash]; DO SELECT TRUE FROM conversation = NIL => { conversation _ NEW[ConversationObject _ [ NIL, id, level, convKey, iv, originator, responder, auth] ]; IF prev = NIL THEN conversations[hash] _ conversation ELSE prev.next _ conversation; EXIT; }; conversation.id = id => { EXIT -- already there }; ENDCASE => { prev _ conversation; conversation _ conversation.next }; ENDLOOP; }; UnknownConversation: ERROR = CODE; EndConversation: PUBLIC ENTRY SAFE PROC [conversation: Conversation] = TRUSTED { hash: ConvHashKey = conversation.id.count.ls MOD SUCC[LAST[ConvHashKey]]; prev: Conversation _ NIL; old: Conversation _ conversations[hash]; IF conversation.id.originator # myHost THEN RETURN WITH ERROR UnknownConversation[]; DO SELECT TRUE FROM old = NIL => RETURN WITH ERROR UnknownConversation[]; old = conversation => { IF prev = NIL THEN conversations[hash] _ conversation.next ELSE prev.next _ conversation.next; EXIT; }; ENDCASE => { prev _ old; old _ old.next }; ENDLOOP; }; InvalidateConversations: INTERNAL PROC = { FOR hash: ConvHashKey IN ConvHashKey DO FOR conversation: Conversation _ conversations[hash], conversation.next UNTIL conversation = NIL DO IF conversation.id.originator = myHost THEN conversation.id.originator _ [[0],[0]]; ENDLOOP; ENDLOOP; }; GetConversationID: PUBLIC ENTRY SAFE PROC [conversation: Conversation] RETURNS [id: ConversationID] = TRUSTED { RETURN[ IF conversation = NIL THEN [[[0],[0]],[0,0]] ELSE conversation.id ]; }; GetCaller: PUBLIC ENTRY SAFE PROC [conversation: Conversation] RETURNS [ RPC.Principal ] = TRUSTED { RETURN[ IF conversation = NIL THEN NIL ELSE conversation.originator ]; }; GetLevel: PUBLIC ENTRY SAFE PROC [conversation: Conversation] RETURNS [ level: RPC.SecurityLevel ] = TRUSTED { RETURN[ IF conversation = NIL THEN none ELSE conversation.level ]; }; encryptedHeaderLength: CARDINAL = SIZE[RPCPkt.PktID] + SIZE[RPCPkt.DispatcherDetails]; Check: TYPE = LONG CARDINAL; EncryptPkt: PUBLIC PROC [pkt: RPCLupine.RPCPkt, l: RPCLupine.DataLength] RETURNS [CARDINAL] = { header: LONG POINTER TO RPCPkt.Header = @pkt.header; conv: Conversation = pkt.convHandle; IF conv # NIL AND conv.level # none AND conv.level # authOnly THEN { words: CARDINAL = l -- stub data words -- + encryptedHeaderLength + SIZE[Check]; nBlks: CARDINAL = (words + SIZE[DESFace.Block]-1 -- round up --) / SIZE[DESFace.Block]; blocks: DESFace.Blocks = LOOPHOLE[@header.pktID]; checkPtr: LONG POINTER TO Check = LOOPHOLE[@blocks[nBlks] - SIZE[Check]]; checkPtr^ _ nBlks * SIZE[DESFace.Block] - words; SELECT conv.level FROM ECB => DESFace.ECBEncrypt[conv.key, nBlks, blocks, blocks]; CBC => DESFace.CBCEncrypt[conv.key, nBlks, blocks, blocks, conv.iv]; CBCCheck => DESFace.CBCCheckEncrypt[conv.key, nBlks, blocks, blocks, conv.iv]; ENDCASE => ERROR; RETURN [nBlks * SIZE[DESFace.Block] - encryptedHeaderLength + RPCPkt.pktLengthOverhead] } ELSE RETURN [l + RPCPkt.pktLengthOverhead] }; DecryptPkt: PUBLIC PROC [header: LONG POINTER TO RPCPkt.Header, convHandle: Conversation] RETURNS [ok: BOOL _ TRUE, l: RPCLupine.DataLength_0] = { IF convHandle # NIL AND convHandle.level # none AND convHandle.level # authOnly THEN { ENABLE DESFace.BadKey => GO TO badReturn; nBlks: CARDINAL = (header.length - RPCPkt.pktLengthOverhead + encryptedHeaderLength) / SIZE[DESFace.Block]; blocks: DESFace.Blocks = LOOPHOLE[@header.pktID]; checkPtr: LONG POINTER TO Check = LOOPHOLE[@blocks[nBlks] - SIZE[Check]]; SELECT convHandle.level FROM ECB => DESFace.ECBDecrypt[convHandle.key, nBlks, blocks, blocks]; CBC => DESFace.CBCDecrypt[convHandle.key, nBlks, blocks, blocks, convHandle.iv]; CBCCheck => DESFace.CBCCheckDecrypt[convHandle.key, nBlks, blocks, blocks, convHandle.iv]; ENDCASE => ERROR; IF checkPtr^ IN [0..SIZE[DESFace.Block]) THEN l _ nBlks * SIZE[DESFace.Block] - checkPtr^ - SIZE[Check] - encryptedHeaderLength ELSE GO TO badReturn; } ELSE l _ header.length - RPCPkt.pktLengthOverhead; EXITS badReturn => ok _ FALSE; }; RFARequest: TYPE = MACHINE DEPENDENT RECORD[ callerConv(0): RPCPkt.PktConversationID, callerPktID(2): RPCPkt.PktID -- still encrypted --, nonceID(6): LONG CARDINAL -- nominated by requester --]; RFAResponse: TYPE = MACHINE DEPENDENT RECORD[ iv(0): DESFace.IV, connection(4): RPCPkt.ConnectionID, call(8): RPCPkt.CallCount, nonceID(10): LONG CARDINAL, level(12): RPC.SecurityLevel, authLength(13): CARDINAL, -- length of authenticator (words) authenticator(14): AuthenticatorLayout ]; responseCKBlocks: CARDINAL = 12 / SIZE[DESFace.Block]; rfaDataLength: RPCLupine.DataLength = SIZE[RFAResponse] - SIZE[StringBody[0]] + 2 * SIZE[StringBody[RPC.maxPrincipalLength]]; myHost: RPCPkt.Machine; GetConnectionState: PUBLIC PROC [ decrypted: BOOL, callPkt: RPCLupine.RPCPkt] RETURNS [ ok: BOOL _ FALSE, id: RPCPkt.ConnectionID, call: RPCPkt.CallCount, conv: Conversation _ NIL, l: RPCLupine.DataLength] = { callHeader: LONG POINTER TO Header = @callPkt.header; rfaPktSpace: ARRAY [1..rfaDataLength + RPCLupine.pktOverhead] OF WORD; rfaPkt: RPCLupine.StubPkt = RPCLupine.GetStubPkt[@rfaPktSpace]; myNonceID: LONG CARDINAL = BasicTime.GetClockPulses[]; request: LONG POINTER TO RFARequest = LOOPHOLE[LONG[@(rfaPkt[0])]]; response: LONG POINTER TO RFAResponse = LOOPHOLE[LONG[@(rfaPkt[0])]]; rfaHeader: LONG POINTER TO Header = @rfaPkt.header; request.callerConv _ callHeader.conv; request.callerPktID _ callHeader.pktID; --still encrypted-- request.nonceID _ myNonceID; rfaPkt.convHandle _ RPC.unencrypted; rfaHeader^ _ ConcreteHeader[@callPkt.header]^; RPCPkt.SetupResponse[rfaHeader]; rfaHeader.conv _ firstConversation; rfaHeader.pktID.activity _ 0; rfaHeader.pktID.pktSeq _ 0; { responseLength: RPCLupine.DataLength _ RPCPkt.PktExchange[rfaPkt, SIZE[RFARequest], rfaDataLength, authReq].newLength; IF responseLength < SIZE[RFAResponse] - SIZE[AuthenticatorLayout] THEN GO TO badReturn; }; IF response.level = none THEN { l _ callHeader.length - RPCPkt.pktLengthOverhead; conv _ NIL } ELSE { ENABLE DESFace.BadKey => GO TO badReturn; --TEMP-- kb: DESFace.Key _ DESFace.nullKey -- !!! --; DESFace.CorrectParity[@kb]; DESFace.DecryptBlock[key: kb, from: LOOPHOLE[@response.authenticator.ky], to: LOOPHOLE[@response.authenticator.ky] ]; DESFace.CBCCheckDecrypt[key: response.authenticator.ky, nBlks: response.authLength/SIZE[DESFace.Block] - 2, from: LOOPHOLE[@response.authenticator.ck], to: LOOPHOLE[@response.authenticator.ck], seed: [0,0,0,0] ]; DESFace.DecryptBlock[key: kb, from: LOOPHOLE[@response.authenticator.ck], to: LOOPHOLE[@response.authenticator.ck] ]; DESFace.CBCCheckDecrypt[key: response.authenticator.ck, nBlks: responseCKBlocks, from: LOOPHOLE[response], to: LOOPHOLE[response], seed: [0,0,0,0] ]; conv _ EntryAddConversation[ [IF response.connection.conv.originator = caller THEN response.connection.caller ELSE myHost, [response.connection.conv.ls, response.connection.conv.ms]], response.level, response.iv, ConvertUnsafe.ToRope[@response.authenticator.a], NIL, response.authenticator.ck, NIL ]; callPkt.convHandle _ conv; IF NOT decrypted THEN { [ok, l] _ DecryptPkt[callHeader, conv]; IF NOT ok THEN RETURN; }; }; IF response.nonceID # myNonceID+1 THEN GO TO badReturn; IF response.connection # [callHeader.conv, callHeader.srceHost, callHeader.pktID.activity] THEN GO TO badReturn; IF response.call # callHeader.pktID.callSeq THEN GO TO badReturn; RETURN[TRUE, response.connection, response.call, conv, l]; EXITS badReturn => ok _ FALSE; }; ReplyToRFA: PUBLIC PROC [b: BufferDefs.PupBuffer, callHeader: LONG POINTER TO RPCPkt.Header, callPktID: RPCPkt.PktID, convHandle: Conversation] RETURNS [BOOL] = { recvdHeader: LONG POINTER TO RPCPkt.Header = LOOPHOLE[@b.pupLength]; request: RFARequest = LOOPHOLE[recvdHeader+SIZE[RPCPkt.Header], LONG POINTER TO RFARequest]^; response: LONG POINTER TO RFAResponse = LOOPHOLE[recvdHeader+SIZE[RPCPkt.Header]]; used: CARDINAL _ SIZE[RFAResponse] - SIZE[AuthenticatorLayout]; IF request.callerConv # callHeader.conv OR request.callerPktID # callHeader.pktID THEN GO TO badReturn; response.connection _ [callHeader.conv, callHeader.srceHost, callPktID.activity]; response.call _ callPktID.callSeq; response.nonceID _ request.nonceID+1; IF callHeader.conv = firstConversation THEN response.level _ none ELSE { IF callHeader.conv.originator = callee THEN --TEMP--ERROR; response.level _ convHandle.level; response.iv _ convHandle.iv; DESFace.CBCCheckEncrypt[key: convHandle.key, nBlks: responseCKBlocks, from: LOOPHOLE[response], to: LOOPHOLE[response], seed: [0,0,0,0] ! DESFace.BadKey => GO TO badReturn]; response.authLength _ convHandle.authenticator.nBlks * SIZE[DESFace.Block]; PrincOpsUtils.LongCopy[from: @convHandle.authenticator[0], to: @response.authenticator, nwords: response.authLength]; used _ used + response.authLength; CopyPrincipal[from: convHandle.originator, to: @response.authenticator+response.authLength]; used _ used + SIZE[StringBody[convHandle.originator.Length[]]]; }; { header: Header _ recvdHeader^; RPCPkt.SetupResponse[@header]; header.length _ RPCPkt.pktLengthOverhead + used; header.oddByte _ no; header.type _ [0,rpc,end,dontAck,data]; header.pktID.pktSeq _ header.pktID.pktSeq+1; header.srceHost _ myHost; header.srceSoc _ RPCPrivate.rpcSocket; header.srcePSB _ PrincOps.PsbNull; recvdHeader^ _ header; }; PupDefs.PupRouterSendThis[b]; RETURN[TRUE]; EXITS badReturn => {GiveBackBuffer[b]; RETURN[FALSE]}; }; Initialize: ENTRY PROC = { myAddr: PupTypes.PupAddress _ PupDefs.AnyLocalPupAddress[RPCPrivate.rpcSocket]; now: MACHINE DEPENDENT RECORD[ls: CARDINAL, spare: [0..1], ms: [0..77777B]] = LOOPHOLE[BasicTime.Period[from: BasicTime.earliestGMT, to: BasicTime.Now[]]]; firstHandle: Conversation; myHost _ [myAddr.net, myAddr.host]; lastConversation _ [originator: myHost, count:[ls: now.ls, ms: now.ms] ]; firstHandle _ AddConversation[lastConversation, none, , NIL, NIL, DESFace.nullKey, NIL]; firstConversation _ [ls: firstHandle.id.count.ls, originator: caller, ms: firstHandle.id.count.ms]; }; Restart: ENTRY PROC = { InvalidateConversations[]; }; DO Initialize[]; STOP; Restart[]; ENDLOOP; }. ΐRPCSecurity.mesa - Authentication and security facilities Copyright c 1985 by Xerox Corporation. All rights reserved. Andrew Birrell September 7, 1983 4:33 pm Bob Hagmann February 11, 1985 4:36:01 pm PST Russ Atkinson (RRA) February 20, 1985 7:44:34 pm PST Polle Zellweger (PTZ) July 31, 1985 11:05:18 pm PDT Swinehart, August 1, 1985 11:04:26 am PDT NOTE: calls of this must be made outside our monitor, because RPCPrivate.ReturnBuffer acquires the EthernetDriver monitor, and the EthernetDriver may call EnqueueRecvd which acquires our monitor! ******** Conversation management ******** Contents of an authenticator when it's not encrypted TEMP: This entire procedure should use a secure R-Server protocol! TEMP: construct authenticator in clear ourselves! Note that we cannot recover from DESFace.BadKey (because it had better not happen!) Invalidate conversations for which we are the originator, such that client will get an error if he uses them. ******** Packet encryption and decryption ******** cipher checksum at end of packet bottom 2 bits indicate block rounding; remaining 30 bits should be 0 ******** Requests-For-Authenticator's (RFA's) ******** conversationID, {pktID}CK, nonceID {iv, connectionID, callCount, nonceID+1}CK, null, authScheme, authLength, authenticator, callee. The initial part is encrypted with CBC using a zero IV. ends here if callee originated the conversation ends here if level = none followed by Callee principal name Size of {iv, connectionID, callCount, nonceID+1} The RFA packet looks a lot like the first packet of a new call. The response, a data packet, will look a lot like the result of this call. rfaHeader.pktID.callSeq will be set by PktExchange response = {IV,cond,call,nonce}CK,,{KY}KB,{{CK}KB,time,A}KY Construct a ConversationID from a PktConversationID correctness of nonceID means he knows CK. Note: callHeader is encrypted, callPktID is clear Remember BufferDefs.PupBuffer's don't look like RPCLupine.RPCPkt's! copy request out of buffer to avoid overwriting our args! Initialization Use first available conversationID for unencrypted conversations. Bob Hagmann February 11, 1985 4:36:01 pm PST changes to: GiveBackBuffer Polle Zellweger (PTZ) August 2, 1985 1:40:03 pm PDT changes to: DecryptPkt -- initialize return param l so RPCPktIO.PktExchange won't blow up for incorrectly decrypted packets (seems to happen for ACK pkts -- connection info has gone away? May only happen when client and server are on the same machine.) Κ‡˜codešœ9™9Kšœ Οmœ1™Kšœ˜K˜—š  œž œžœ žœžœžœBžœ#˜ΤK˜8Kšžœ˜ Kšžœ9˜=˜0K˜!K˜—Kšœ˜K˜—š œžœžœž œžœ˜5Kšœ4™4K˜KšœŸ˜2K˜KšœŸ˜3Kšœ žœžœ˜Kšœ˜K˜K˜—Kšœžœžœ˜#K˜š œžœžœžœ žœžœžœžœžœ žœ˜ΉK˜)K˜Kšœ žœ˜Kš žœžœžœžœžœžœ˜HK˜AKšžœD˜JKšœ˜K˜—š  œžœ žœžœžœ žœOžœ˜·KšœB™Bšœžœžœ˜/Kšžœ˜Kšžœ˜#KšžœŸ œžœ˜;—Kš žœžœžœžœžœ˜YKš žœžœžœžœžœ˜Yšžœ&ž˜0Kšœžœžœ˜;Kšœ žœžœ$˜8Kšœ žœžœ˜/Kšžœžœ˜—šžœž˜&Kšœžœžœ˜;Kšœ žœžœ$˜8Kšžœžœ˜—K˜Kšœžœ)˜<šœ˜Kšžœ‚™†Kšœ"Ÿ œ˜,Kš œ žœžœžœžœ˜KK˜$K˜.K˜Kšœ.˜.K˜˜Kšœžœ˜Kšœžœ˜—˜*K˜Kšœžœ˜Kšœžœ˜K˜—˜Kšœžœ˜Kšœžœ˜—Kšœ˜—Kšœ˜K˜—Kšœ žœ ˜K˜šœžœžœ žœ˜6Kš žœžœ žœžœžœ˜2K˜—Kšœžœžœ˜$K˜š œžœžœžœžœžœCžœ"žœ˜οKšžœG˜MKšœ˜K˜—š œžœžœžœžœžœCžœ#˜ζKšœ žœžœžœ˜˜E—Kšžœ˜—Kšœ˜K˜—Kšœžœžœ˜"K˜š  œžœžœžœžœ žœ˜PKšœ-žœžœžœ˜IKšœžœ˜Kšœ(˜(Kš žœ%žœžœžœžœ˜Tšž˜šžœžœž˜Kš œžœžœžœžœ˜5šœ˜šžœž˜ Kšžœ(˜,Kšžœ˜#—Kšžœ˜Kšœ˜—Kšžœ#˜*—Kšžœ˜—Kšœ˜K˜—š œžœžœ˜*Kšœm™mšžœžœ ž˜'šžœDžœž˜cKšžœ%žœ(˜SKšžœ˜—Kšžœ˜—Kšœ˜K˜—š œžœžœžœžœžœžœ˜oKš žœžœžœžœžœ˜LKšœ˜K˜—š  œžœžœžœžœžœžœžœ˜dKš žœžœžœžœžœžœ˜FKšœ˜K˜—š œžœžœžœžœžœ žœžœ˜nKš žœžœžœžœžœ˜BKšœ˜K˜—Kšœ2™2K˜šœžœ˜!Kšžœžœ˜4K˜—šœžœžœžœ˜Kšœ ™ KšœD™DK˜—š   œžœžœ2žœžœ˜_Kšœžœžœžœ˜4K˜$šžœžœžœžœ˜=šžœ˜šœžœŸœ˜+K˜Kšžœ˜ —šœžœ žœŸœ˜@Kšœžœ˜—Kšœžœ˜1Kš œ žœžœžœ žœžœ ˜IKšœžœ˜0šžœ ž˜šžœ'˜*K˜K˜—šžœ'˜*K˜K˜K˜ —˜4K˜K˜K˜ —Kšžœžœ˜—Kšžœ žœC˜WKšœ˜—Kšžœžœ˜*—Kšœ˜K˜—š  œžœžœ žœžœžœ*žœžœžœ ˜’šžœžœžœžœ˜Ošžœ˜Kšžœžœžœ ˜)šœžœ.˜=K˜Kšœžœ˜—Kšœžœ˜1Kš œ žœžœžœ žœžœ ˜Kšžœž˜šžœ-˜0K˜K˜—šžœ-˜0K˜K˜K˜—˜:K˜K˜K˜—Kšžœžœ˜—šžœ žœžœ˜(šžœ žœ˜&K˜ Kšžœ ˜ K˜—Kšžœžœžœ ˜—Kšœ˜—Kšžœ.˜2—Kšžœžœ˜Kšœ˜K˜K˜—Kšœ6™6K˜š œ žœžœž œžœ˜,Kšœ"™"K˜+KšœŸœ˜5KšœžœžœŸœ˜>K˜—š œ žœžœž œžœ˜-Kšœ+™+Kšœ4™4šœ7™7Kšœžœ˜K˜'K˜$Kšœžœžœ˜!—šœ/™/Kšœžœ˜%—šœ™KšœžœŸ"˜?K˜&—Kšœ!™!K˜K˜—šœžœžœ˜6Kšœ0™0K˜—šœ&žœžœ˜OKšœžœ žœ˜-K˜—K˜K˜š œžœžœžœžœžœžœHžœ˜ΡKšœ‹™‹Kšœ žœžœžœ˜5Kšœ žœ,žœžœ˜FK˜?Kšœ žœžœ˜6Kš œ žœžœžœžœžœ˜EKš œ žœžœžœžœžœ˜EKšœ žœžœžœ˜3K˜%Kšœ(Ÿ˜;K˜Kšœžœ ˜$K˜.K˜ K˜#K˜Kšœ2™2K˜šœ˜˜&Kšœžœ0˜O—šžœžœžœž˜FKšžœžœ ˜—Kšœ˜—šžœ˜Kšžœ<žœ˜Ešžœ˜Kšžœžœžœ ˜)Kšœ;™;KšŸœ#Ÿ œ˜5K˜˜Kšœžœ˜+Kšœžœ˜+—˜9Kšœžœ˜3Kšœžœ˜,Kšœžœ˜,K˜—˜Kšœžœ˜+Kšœžœ˜-—˜9K˜Kšœžœ ˜Kšœžœ ˜K˜—˜Kšœ3™3šœžœ-˜0Kšžœ˜Kšžœ˜ K˜<—K˜K˜ K˜0Kšžœ˜K˜Kšžœ˜—K˜šžœžœ žœ˜K˜'Kšžœžœžœžœ˜Kšœ˜—Kšœ˜——Kšœ)™)Kšžœ žœžœžœ ˜7KšžœXžœžœžœ ˜pKšžœ*žœžœžœ ˜AKšžœžœ/˜:Kšžœžœ˜Kšœ˜K˜—š  œžœžœ'žœžœžœBžœžœ˜’Kšœ1™1KšœC™CKš œ žœžœžœžœ˜DKšœ9™9Kš œžœ žœžœžœžœ˜^Kš œ žœžœžœžœ žœ˜RKšœžœžœžœ˜?šžœ&žœ'ž˜VKšžœžœ ˜—K˜QK˜"K˜%šžœ$˜&Kšžœ˜šžœ˜Kšžœ%žœŸžœ˜:K˜"K˜˜.K˜Kšœžœ ˜Kšœžœ ˜K˜Kšœžœžœ ˜%—Kšœ7žœ˜Kšœ:˜:Kšœ˜Kšœ˜—Kšœ#˜#˜*Kšœ1˜1—Kšœžœ-˜?Kšœ˜——šœ˜K˜K˜K˜0K˜K˜'K˜,K˜K˜&Kšœ"˜"K˜—Kšœ˜K˜Kšžœžœ˜ Kšžœ"žœžœ˜6Kšœ˜K˜—Kšœ™K˜š  œžœžœ˜KšœO˜Oš œžœž œžœžœ#˜MKšžœE˜M—K˜K˜#K˜IKšœA™AKšœ8žœžœžœ˜XK˜cKšœ˜K˜—š œžœžœ˜K˜Kšœ˜K˜—Kšžœžœ žœ˜*K˜Kšœ˜™,Kšœ Οr™—™3Kšœ ‘ œη™ύ——…—>ά[#