<> <> <> <> <> <> <> 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]]]; }; <<******** Conversation management ********>> 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 ]; }; <<******** Packet encryption and decryption ********>> 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; }; <<******** Requests-For-Authenticator's (RFA's) ********>> 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, connectionID, callCount, nonceID+1}CK,>> <> <> 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; }. <> <> <> <>