void SendNeighborPeerAckResponseToAck1(InviteAck1Packet ack1, ConnectionToNeighbor neighbor) { var npAck = new NeighborPeerAckPacket { ReqP2pSeq16 = ack1.ReqP2pSeq16, ResponseCode = ResponseOrFailureCode.accepted }; npAck.NeighborToken32 = neighbor.RemoteNeighborToken32; npAck.NeighborHMAC = neighbor.GetNeighborHMAC(w => npAck.GetSignedFieldsForNeighborHMAC(w, ack1.GetSignedFieldsForNeighborHMAC)); var npAckUdpData = npAck.Encode(false); Engine.RespondToRequestAndRetransmissions(ack1.DecodedUdpPayloadData, npAckUdpData, neighbor.RemoteEndpoint); }
internal void DeriveSharedPingPongHmacKey(InviteRequestPacket req, InviteAck1Packet ack1, InviteAck2Packet ack2, InviteConfirmationPacket cfm) { if (SharedInviteAckDhSecret == null) { throw new NotImplementedException(); } PacketProcedures.CreateBinaryWriter(out var ms, out var w); req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); cfm.GetSharedSignedFields(w); w.Write(SharedInviteAckDhSecret); SharedPingPongHmacKey = _localDrpPeer.CryptoLibrary.GetHashSHA256(ms.ToArray()); }
/// <param name="receivedFromUser">comes from local contact book. is null if it is contact invitation</param> internal static InviteSessionDescription Decrypt_Verify(ICryptoLibrary cryptoLibrary, byte[] encryptedSdData, InviteRequestPacket req, InviteAck1Packet ack1, bool ack1SdIsReady, InviteSession session, UserId receivedFromUserNullable, DateTime localTimeNowUtc ) { #region key, iv BinaryProcedures.CreateBinaryWriter(out var ms2, out var w2); req.GetSharedSignedFields(w2); ack1.GetSharedSignedFields(w2, ack1SdIsReady); var iv = cryptoLibrary.GetHashSHA256(ms2.ToArray()).Take(16).ToArray(); ms2.Write(session.SharedInviteAckDhSecret, 0, session.SharedInviteAckDhSecret.Length); var aesKey = cryptoLibrary.GetHashSHA256(ms2.ToArray()); // here SHA256 is used as KDF, together with common fields from packets, including both ECDH public keys and timestamp #endregion // decrypt var plainTextSdData = new byte[encryptedSdData.Length]; cryptoLibrary.ProcessAesCbcBlocks(false, aesKey, iv, encryptedSdData, plainTextSdData); var r = new InviteSessionDescription(); var reader = BinaryProcedures.CreateBinaryReader(plainTextSdData, 0); r.Flags = reader.ReadByte(); if ((r.Flags & FlagsMask_MustBeZero) != 0) { throw new NotImplementedException(); } r.UserCertificate = UserCertificate.Decode_AssertIsValidNow(reader, cryptoLibrary, receivedFromUserNullable, localTimeNowUtc); r.DirectChannelEndPoint = BinaryProcedures.DecodeIPEndPoint(reader); r.NatBehaviour = NatBehaviourModel.Decode(reader); r.DirectChannelToken32 = DirectChannelToken32.Decode(reader); r.SessionType = (SessionType)reader.ReadByte(); r.UserCertificateSignature = UserCertificateSignature.DecodeAndVerify(reader, cryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, ack1SdIsReady); r.WriteSignedFields(w); }, r.UserCertificate); return(r); }
/// <summary> /// Timestamp32S, NeighborToken32 and NeighborHMAC are verified at this time /// </summary> internal async Task AcceptInviteRequestAsync(RoutedRequest routedRequest) { if (routedRequest.ReceivedFromNeighborNullable == null) { throw new ArgumentException(); } var req = routedRequest.InviteReq; if (!req.ResponderRegistrationId.Equals(this.Configuration.LocalPeerRegistrationId)) { throw new ArgumentException(); } var logger = routedRequest.Logger; logger.ModuleName = DrpPeerEngine.VisionChannelModuleName_inv_responderSide; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"accepting {req} from sourcePeer={routedRequest.ReceivedFromNeighborNullable}"); } // check if regID exists in contact book, get userID from the local contact book // ignore the REQ packet if no such user in contacts this._drpPeerApp.OnReceivedInvite(req.RequesterRegistrationId, req.ContactInvitationTokenNullable, out var remoteRequesterUserIdFromLocalContactBookNullable, out var localUserCertificateWithPrivateKey, out var autoReply); if (autoReply == false) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_needsAttention($"ignored invite: autoReply = false"); } return; } localUserCertificateWithPrivateKey?.AssertHasPrivateKey(); if (remoteRequesterUserIdFromLocalContactBookNullable != null) { if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"resolved user {remoteRequesterUserIdFromLocalContactBookNullable} by requester regID={req.RequesterRegistrationId}"); } } if (!Engine.RecentUniquePublicEcdhKeys.Filter(req.RequesterEcdhePublicKey.Ecdh25519PublicKey)) { logger.WriteToLog_mediumPain($"RequesterEcdhePublicKey {req.RequesterEcdhePublicKey} is not unique, it has been recently processed"); return; } if (!Engine.RecentUniqueInviteRequests.Filter(req.GetUniqueRequestIdFields)) { logger.WriteToLog_mediumPain($"{req} fields are not unique, the request has been recently processed"); return; } // verify requester reg. signature if (!req.RequesterRegistrationSignature.Verify(Engine.CryptoLibrary, req.GetSharedSignedFields, req.RequesterRegistrationId)) { throw new BadSignatureException("invalid INVITE REQ RequesterRegistrationSignature 2349"); } _pendingInviteRequests.Add(req.RequesterRegistrationId); try { // send NPACK to REQ if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to REQ source peer"); } routedRequest.SendNeighborPeerAck_accepted_IfNotAlreadyReplied(); var session = new InviteSession(this) { Logger = logger }; try { session.DeriveSharedInviteAckDhSecret(Engine.CryptoLibrary, req.RequesterEcdhePublicKey.Ecdh25519PublicKey); session.LocalSessionDescription = new InviteSessionDescription { DirectChannelEndPoint = routedRequest.ReceivedFromNeighborNullable.LocalEndpoint, NatBehaviour = Engine.LocalNatBehaviour, DirectChannelToken32 = session.LocalDirectChannelToken32 }; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"responding with local session {session.LocalSessionDescription}"); } session.LocalSessionDescription.UserCertificate = localUserCertificateWithPrivateKey; #region send ACK1. sign local SD by local user var ack1 = new InviteAck1Packet { ReqTimestamp32S = req.ReqTimestamp32S, RequesterRegistrationId = req.RequesterRegistrationId, ResponderRegistrationId = req.ResponderRegistrationId, ResponderEcdhePublicKey = new EcdhPublicKey(session.LocalInviteAckEcdhePublicKey), }; session.LocalSessionDescription.UserCertificateSignature = UserCertificateSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, false); session.LocalSessionDescription.WriteSignedFields(w); }, localUserCertificateWithPrivateKey); ack1.ToResponderSessionDescriptionEncrypted = session.LocalSessionDescription.Encrypt(Engine.CryptoLibrary, req, ack1, session, false); ack1.ResponderRegistrationSignature = RegistrationSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); }, this.Configuration.LocalPeerRegistrationPrivateKey ); var ack1UdpData = ack1.Encode_SetP2pFields(routedRequest.ReceivedFromNeighborNullable); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK1 to source peer, awaiting for NPACK"); } _ = routedRequest.ReceivedFromNeighborNullable.SendUdpRequestAsync_Retransmit_WaitForNPACK("ack1 1450", ack1UdpData, ack1.ReqP2pSeq16, ack1.GetSignedFieldsForNeighborHMAC); // not waiting for NPACK, wait for ACK2 #endregion // wait for ACK2 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for ACK2"); } var ack2UdpData = await Engine.OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("ack2 23467789", routedRequest.ReceivedFromNeighborNullable.ToString(), null, routedRequest.ReceivedFromNeighborNullable.RemoteEndpoint, InviteAck2Packet.GetScanner(logger, req, routedRequest.ReceivedFromNeighborNullable)); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received ACK2"); } var ack2 = InviteAck2Packet.Decode(ack2UdpData); if (!ack2.RequesterRegistrationSignature.Verify(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); }, req.RequesterRegistrationId)) { throw new BadSignatureException("invalid INVITE ACK2 RequesterRegistrationSignature 2348"); } // decrypt, verify SD remote user's certificate and signature session.RemoteSessionDescription = InviteSessionDescription.Decrypt_Verify(Engine.CryptoLibrary, ack2.ToRequesterSessionDescriptionEncrypted, req, ack1, true, session, remoteRequesterUserIdFromLocalContactBookNullable, Engine.DateTimeNowUtc); session.LocalSessionDescription.SessionType = session.RemoteSessionDescription.SessionType; switch (session.RemoteSessionDescription.SessionType) { case SessionType.asyncShortSingleMessage: break; case SessionType.ike1: if (_drpPeerApp.OnReceivedInvite_GetLocalIke1Data(req.ContactInvitationTokenNullable) == null) { throw new BadSignatureException("bad ContactInvitationToken 21379"); } break; default: throw new NotImplementedException(); } // send NPACK to ACK2 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to ACK2 to source peer"); } SendNeighborPeerAckResponseToAck2(ack2, routedRequest.ReceivedFromNeighborNullable); // send CFM with signature var cfm = new InviteConfirmationPacket { ReqTimestamp32S = req.ReqTimestamp32S, RequesterRegistrationId = req.RequesterRegistrationId, ResponderRegistrationId = req.ResponderRegistrationId, }; cfm.ResponderRegistrationSignature = RegistrationSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); }, this.Configuration.LocalPeerRegistrationPrivateKey); var cfmUdpData = cfm.Encode_SetP2pFields(routedRequest.ReceivedFromNeighborNullable); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending CFM to source peer, waiting for NPACK"); } await routedRequest.ReceivedFromNeighborNullable.SendUdpRequestAsync_Retransmit_WaitForNPACK("cfm 49146", cfmUdpData, cfm.ReqP2pSeq16, cfm.GetSignedFieldsForNeighborHMAC); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received NPACK to CFM"); } session.DeriveSharedPingPongHmacKey(req, ack1, ack2, cfm); await session.SetupAEkeysAsync(); } catch { session.Dispose(); throw; } if (autoReply) { switch (session.RemoteSessionDescription.SessionType) { case SessionType.asyncShortSingleMessage: if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"autoReply=true: receiving message"); } _ = ReceiveShortSingleMessageAsync(session, req); break; case SessionType.ike1: if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"autoReply=true: starting IKE1"); } _ = Ike1Async_AtInviteResponder(session, req); break; default: throw new NotImplementedException(); } } else { session.Dispose(); // todo implement other cases } } catch (DrpTimeoutException exc) { logger.WriteToLog_lightPain($"could not accept INVITE request: {exc}"); } catch (Exception exc) { logger.WriteToLog_mediumPain($"could not accept INVITE request: {exc}"); } finally { _pendingInviteRequests.Remove(req.RequesterRegistrationId); } }
/// <summary> /// Timestamp32S, NeighborToken32 and NeighborHMAC are verified at this time /// </summary> /// <returns> /// true to retry the request with another neighbor (if the request needs to be "rerouted") /// </returns> internal async Task <bool> ProxyInviteRequestAsync(RoutedRequest routedRequest, ConnectionToNeighbor destinationPeer) { var req = routedRequest.InviteReq; var logger = routedRequest.Logger; logger.ModuleName = DrpPeerEngine.VisionChannelModuleName_inv_proxySide; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"proxying {req} to {destinationPeer}"); } if (!routedRequest.CheckedRecentUniqueProxiedRequests) { if (!Engine.RecentUniqueInviteRequests.Filter(req.GetUniqueRequestIdFields)) { logger.WriteToLog_higherLevelDetail($"rejecting non-unique {req}: requesterEndpoint={routedRequest.ReceivedFromEndpoint}"); await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return(false); } routedRequest.CheckedRecentUniqueProxiedRequests = true; } if (req.NumberOfHopsRemaining > InviteRequestPacket.MaxNumberOfHopsRemaining) { await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_routeIsUnavailable); return(false); } if (req.NumberOfHopsRemaining <= 1) { await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero); return(false); } _pendingInviteRequests.Add(req.RequesterRegistrationId); try { // send NPACK to REQ if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to REQ source peer"); } routedRequest.SendNeighborPeerAck_accepted_IfNotAlreadyReplied(); req.NumberOfHopsRemaining--; if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"decremented number of hops in {req}: NumberOfHopsRemaining={req.NumberOfHopsRemaining}"); } // send (proxy) REQ to responder. wait for NPACK, verify NPACK.senderHMAC, retransmit REQ var reqUdpData = req.Encode_SetP2pFields(destinationPeer); #region wait for ACK1 from responder verify NeighborHMAC byte[] ack1UdpData; try { var sentRequest = new SentRequest(Engine, logger, destinationPeer.RemoteEndpoint, destinationPeer, reqUdpData, req.ReqP2pSeq16, InviteAck1Packet.GetScanner(logger, req, destinationPeer)); ack1UdpData = await sentRequest.SendRequestAsync("inv proxy ack1 3457"); } catch (RequestRejectedException reqExc) { logger.WriteToLog_higherLevelDetail($"got response={reqExc.ResponseCode} from destination {destinationPeer}. will try another neighbor.."); if (reqExc.ResponseCode == ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero) { await routedRequest.SendErrorResponse(ResponseOrFailureCode.failure_numberOfHopsRemainingReachedZero); return(false); } if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 75675"); return(false); } if (reqExc.ResponseCode == ResponseOrFailureCode.failure_routeIsUnavailable) { req.NumberOfHopsRemaining++; // roll back previous decrement for a new trial } return(true); // will retry } catch (DrpTimeoutException exc) { logger.WriteToLog_higherLevelDetail($"got timeout error when requesting {destinationPeer}: {exc.Message}"); req.NumberOfHopsRemaining++; // roll back previous decrement for a new trial return(true); // will retry } catch (Exception reqExc) { logger.WriteToLog_mediumPain($"could not proxy INVITE request: {reqExc}"); if (routedRequest.ReceivedFromNeighborNullable?.IsDisposed == true) { logger.WriteToLog_needsAttention($"sourcePeer={routedRequest.ReceivedFromNeighborNullable} is disposed during proxying 76897805"); return(false); } return(true); // will retry } var ack1 = InviteAck1Packet.Decode(ack1UdpData); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified ACK1 from responder"); } // respond with NPACK to ACk1 SendNeighborPeerAckResponseToAck1(ack1, destinationPeer); #endregion #region send ACK1 to requester, wait for NPACK and ACK2 var ack1UdpDataTx = ack1.Encode_SetP2pFields(routedRequest.ReceivedFromNeighborNullable); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK1, awaiting for NPACK"); } _ = Engine.OptionallySendUdpRequestAsync_Retransmit_WaitForNeighborPeerAck("ack1 13536", ack1UdpDataTx, routedRequest.ReceivedFromEndpoint, ack1.ReqP2pSeq16, routedRequest.ReceivedFromNeighborNullable, ack1.GetSignedFieldsForNeighborHMAC); // not waiting for NPACK, wait for ACK1 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for ACK2"); } var ack2UdpData = await Engine.OptionallySendUdpRequestAsync_Retransmit_WaitForResponse("ack2 2346892", routedRequest.ReceivedFromNeighborNullable?.ToString() ?? routedRequest.ReceivedFromEndpoint.ToString(), null, routedRequest.ReceivedFromEndpoint, InviteAck2Packet.GetScanner(logger, req, routedRequest.ReceivedFromNeighborNullable)); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received ACK2"); } var ack2 = InviteAck2Packet.Decode(ack2UdpData); #endregion // send NPACK to ACK2 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending NPACK to ACK2 to source peer"); } SendNeighborPeerAckResponseToAck2(ack2, routedRequest.ReceivedFromNeighborNullable); // send ACK2 to responder // put ACK2.ReqP2pSeq16, sendertoken32, senderHMAC // wait for NPACK var ack2UdpDataTx = ack2.Encode_SetP2pFields(destinationPeer); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK2 to responder"); } await destinationPeer.SendUdpRequestAsync_Retransmit_WaitForNPACK("ack2 5344530", ack2UdpDataTx, ack2.ReqP2pSeq16, ack2.GetSignedFieldsForNeighborHMAC); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received NPACK to ACK2 from destination peer"); } // wait for CFM from responder if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for CFM from responder"); } var cfmUdpData = await Engine.WaitForUdpResponseAsync(new PendingLowLevelUdpRequest("cfm 12358", destinationPeer.RemoteEndpoint, InviteConfirmationPacket.GetScanner(logger, req, destinationPeer), Engine.DateTimeNowUtc, Engine.Configuration.CfmTimoutS )); if (cfmUdpData == null) { throw new DrpTimeoutException($"inv. proxy CFM response from destination peer {destinationPeer} (timeout={Engine.Configuration.CfmTimoutS}s)"); } var cfm = InviteConfirmationPacket.Decode(cfmUdpData); // todo verify signature, update RDRs and QoS if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified CFM from responder"); } // respond NPACK to CFM to destination peer SendNeighborPeerAckResponseToCfm(cfm, destinationPeer); // send CFM to requester var cfmUdpDataTx = cfm.Encode_SetP2pFields(routedRequest.ReceivedFromNeighborNullable); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending CFM to requester, waiting for NPACK"); } await Engine.OptionallySendUdpRequestAsync_Retransmit_WaitForNeighborPeerAck("cfm 23468", cfmUdpDataTx, routedRequest.ReceivedFromEndpoint, cfm.ReqP2pSeq16, routedRequest.ReceivedFromNeighborNullable, cfm.GetSignedFieldsForNeighborHMAC); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received NPACK to CFM from source peer"); } } catch (DrpTimeoutException exc) { logger.WriteToLog_lightPain($"could not proxy INVITE: {exc.Message}"); } catch (Exception exc) { logger.WriteToLog_mediumPain($"could not proxy INVITE request: {exc}"); } finally { _pendingInviteRequests.Remove(req.RequesterRegistrationId); } return(false); }
/// <param name="ack1SdIsReady"> /// =true for SD in ACK2 /// =false for SD in ACK1 (since the SessionDescription is not initialized yet) /// </param> internal byte[] Encrypt(ICryptoLibrary cryptoLibrary, InviteRequestPacket req, InviteAck1Packet ack1, InviteSession session, bool ack1SdIsReady ) { BinaryProcedures.CreateBinaryWriter(out var ms, out var w); w.Write(Flags); UserCertificate.Encode(w, false); BinaryProcedures.EncodeIPEndPoint(w, DirectChannelEndPoint); NatBehaviour.Encode(w); DirectChannelToken32.Encode(w); w.Write((byte)SessionType); UserCertificateSignature.Encode(w); var bytesInLastBlock = (int)ms.Position % CryptoLibraries.AesBlockSize; if (bytesInLastBlock != 0) { var bytesRemainingTillFullAesBlock = CryptoLibraries.AesBlockSize - bytesInLastBlock; w.Write(cryptoLibrary.GetRandomBytes(bytesRemainingTillFullAesBlock)); } var plainTextSdData = ms.ToArray(); var encryptedSdData = new byte[plainTextSdData.Length]; #region key, iv BinaryProcedures.CreateBinaryWriter(out var ms2, out var w2); req.GetSharedSignedFields(w2); ack1.GetSharedSignedFields(w2, ack1SdIsReady); var iv = cryptoLibrary.GetHashSHA256(ms2.ToArray()).Take(16).ToArray(); ms2.Write(session.SharedInviteAckDhSecret, 0, session.SharedInviteAckDhSecret.Length); var aesKey = cryptoLibrary.GetHashSHA256(ms2.ToArray()); // here SHA256 is used as KDF, together with common fields from packets, including both ECDH public keys and timestamp #endregion cryptoLibrary.ProcessAesCbcBlocks(true, aesKey, iv, plainTextSdData, encryptedSdData); return(encryptedSdData); }
/// <summary> /// sends INVITE, autenticates users, returns Session to be used to create direct cannel /// </summary> /// <param name="responderUserId"> /// comes from local contact book /// </param> /// <param name="responderRegId"> /// comes from local contact book /// </param> /// <param name="loggerCb">may be invoked more than one time (in case of retrying)</param> public async Task <InviteSession> SendInviteAsync(UserCertificate requesterUserCertificate, RegistrationId responderRegistrationId, UserId responderUserId, SessionType sessionType, Action <Logger> loggerCb = null) { InviteSession session = null; try { var sw = Stopwatch.StartNew(); RoutedRequest routedRequest = null; int trialsCount = 0; Exception latestTriedNeighborException = null; _retry: trialsCount++; session = new InviteSession(this); var req = new InviteRequestPacket { NumberOfHopsRemaining = InviteRequestPacket.MaxNumberOfHopsRemaining, RequesterEcdhePublicKey = new EcdhPublicKey(session.LocalInviteAckEcdhePublicKey), RequesterRegistrationId = this.Configuration.LocalPeerRegistrationId, ResponderRegistrationId = responderRegistrationId, ReqTimestamp32S = Engine.Timestamp32S, }; var logger = new Logger(Engine, this, req, DrpPeerEngine.VisionChannelModuleName_inv_requesterSide); if (!Engine.RecentUniqueInviteRequests.Filter(req.GetUniqueRequestIdFields)) { if (trialsCount > 50) { throw new NonUniquePacketFieldsException($"could not find unique fields to send INVITE request"); } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting a second to generate ne wunique INVITE request"); } await Engine.EngineThreadQueue.WaitAsync(TimeSpan.FromSeconds(1), "inv_wait_1236"); goto _retry; } session.Logger = logger; loggerCb?.Invoke(logger); Engine.RecentUniquePublicEcdhKeys.AssertIsUnique(req.RequesterEcdhePublicKey.Ecdh25519PublicKey, "req.RequesterEcdhePublicKey"); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"generated unique ECDH key {req.RequesterEcdhePublicKey}"); } req.RequesterRegistrationSignature = RegistrationSignature.Sign(Engine.CryptoLibrary, req.GetSharedSignedFields, this.Configuration.LocalPeerRegistrationPrivateKey); this.TestDirection(logger, req.ResponderRegistrationId); routedRequest = new RoutedRequest(logger, null, null, null, req, null, routedRequest); // find best connected peer to send the request var destinationPeer = Engine.RouteInviteRequest(this, routedRequest); if (destinationPeer == null) { if (latestTriedNeighborException == null) { throw new NoNeighborsToSendInviteException(); } else { throw latestTriedNeighborException; } } InviteAck1Packet ack1; try { var reqUdpData = req.Encode_SetP2pFields(destinationPeer); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending {req} (ReqTimestamp32S={MiscProcedures.Uint32secondsToDateTime(req.ReqTimestamp32S)}), waiting for NPACK"); } var sentRequest = new SentRequest(Engine, logger, destinationPeer.RemoteEndpoint, destinationPeer, reqUdpData, req.ReqP2pSeq16, InviteAck1Packet.GetScanner(logger, req, destinationPeer)); var ack1UdpData = await sentRequest.SendRequestAsync("ack1 4146"); #region process ACK1 // NeighborHMAC and NeighborToken32 are already verified by scanner ack1 = InviteAck1Packet.Decode(ack1UdpData); Engine.RecentUniquePublicEcdhKeys.AssertIsUnique(ack1.ResponderEcdhePublicKey.Ecdh25519PublicKey, $"ack1.ResponderEcdhePublicKey"); if (!ack1.ResponderRegistrationSignature.Verify(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); }, responderRegistrationId)) { throw new BadSignatureException("invalid REGISTER ACK1 ResponderRegistrationSignature 2349"); } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified ACK1"); } session.DeriveSharedInviteAckDhSecret(Engine.CryptoLibrary, ack1.ResponderEcdhePublicKey.Ecdh25519PublicKey); // send NPACK to ACK1 SendNeighborPeerAckResponseToAck1(ack1, destinationPeer); #endregion } catch (RequestFailedException exc2) { latestTriedNeighborException = exc2; if (trialsCount > 50) { throw; } logger.WriteToLog_higherLevelDetail($"trying again on error {exc2.Message}... alreadyTriedProxyingToDestinationPeers.Count={routedRequest.TriedNeighbors.Count}"); routedRequest.TriedNeighbors.Add(destinationPeer); goto _retry; } // decode and verify SD session.RemoteSessionDescription = InviteSessionDescription.Decrypt_Verify(Engine.CryptoLibrary, ack1.ToResponderSessionDescriptionEncrypted, req, ack1, false, session, responderUserId, Engine.DateTimeNowUtc); // sign and encode local SD session.LocalSessionDescription = new InviteSessionDescription { DirectChannelEndPoint = destinationPeer.LocalEndpoint, SessionType = sessionType, DirectChannelToken32 = session.LocalDirectChannelToken32 }; session.LocalSessionDescription.UserCertificate = requesterUserCertificate; session.LocalSessionDescription.UserCertificateSignature = UserCertificateSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); session.LocalSessionDescription.WriteSignedFields(w); }, requesterUserCertificate ); #region send ack2 var ack2 = new InviteAck2Packet { RequesterRegistrationId = req.RequesterRegistrationId, ResponderRegistrationId = req.ResponderRegistrationId, ReqTimestamp32S = req.ReqTimestamp32S, ToRequesterSessionDescriptionEncrypted = session.LocalSessionDescription.Encrypt(Engine.CryptoLibrary, req, ack1, session, true) }; ack2.RequesterRegistrationSignature = RegistrationSignature.Sign(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); }, this.Configuration.LocalPeerRegistrationPrivateKey); var ack2UdpData = ack2.Encode_SetP2pFields(destinationPeer); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"sending ACK2, waiting for NPACK"); } await destinationPeer.SendUdpRequestAsync_Retransmit_WaitForNPACK("ack2 234575672", ack2UdpData, ack2.ReqP2pSeq16, ack2.GetSignedFieldsForNeighborHMAC); if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"received NPACK"); } #endregion #region wait for CFM if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"waiting for CFM"); } var cfmUdpData = await Engine.WaitForUdpResponseAsync(new PendingLowLevelUdpRequest("cfm 1235695", destinationPeer.RemoteEndpoint, InviteConfirmationPacket.GetScanner(logger, req, destinationPeer), Engine.DateTimeNowUtc, Engine.Configuration.CfmTimoutS )); if (cfmUdpData == null) { throw new DrpTimeoutException($"did not get CFM at invite requester from destination peer {destinationPeer} (timeout={Engine.Configuration.CfmTimoutS}s)"); } // NeighborHMAC and NeighborToken32 are already verified by scanner var cfm = InviteConfirmationPacket.Decode(cfmUdpData); if (!cfm.ResponderRegistrationSignature.Verify(Engine.CryptoLibrary, w => { req.GetSharedSignedFields(w); ack1.GetSharedSignedFields(w, true); ack2.GetSharedSignedFields(w); }, responderRegistrationId)) { throw new BadSignatureException("invalid REGISTER CFM ResponderRegistrationSignature 6398"); } if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"verified CFM"); } // send NPACK to CFM SendNeighborPeerAckResponseToCfm(cfm, destinationPeer); #endregion session.DeriveSharedPingPongHmacKey(req, ack1, ack2, cfm); return(session); } catch { session?.Dispose(); throw; } }