const ushort Magic16_responderToRequester = 0x60C1; // is used to validate decrypted data /// <summary> /// when sending ACK1 /// </summary> public byte[] Encrypt_ack1_ToResponderTxParametersEncrypted_AtResponder_DeriveSharedDhSecret(Logger logger, RegisterRequestPacket req, RegisterAck1Packet ack1, ConnectionToNeighbor neighbor) { IPEndPoint localResponderEndpoint; if (neighbor != null) { localResponderEndpoint = neighbor.LocalEndpoint; } else { localResponderEndpoint = req.EpEndpoint; } if (localResponderEndpoint.Address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) { throw new NotImplementedException(); } SharedDhSecret = _engine.CryptoLibrary.DeriveEcdh25519SharedSecret(LocalEcdhe25519PrivateKey, req.RequesterEcdhePublicKey.Ecdh25519PublicKey); #region key, iv BinaryProcedures.CreateBinaryWriter(out var ms, out var writer); req.GetSharedSignedFields(writer, true); ack1.GetSharedSignedFields(writer, false, false); var iv = _engine.CryptoLibrary.GetHashSHA256(ms.ToArray()).Take(16).ToArray();; ms.Write(SharedDhSecret, 0, SharedDhSecret.Length); var aesKey = _engine.CryptoLibrary.GetHashSHA256(ms.ToArray()); // here SHA256 is used as KDF, together with common fields from packets, including both ECDH public keys and timestamp #endregion // encode localRxParameters BinaryProcedures.CreateBinaryWriter(out var msRxParameters, out var wRxParameters); BinaryProcedures.EncodeIPEndPoint(wRxParameters, localResponderEndpoint); // max 19 LocalNeighborToken32.Encode(wRxParameters); // +4 max 23 _engine.LocalNatBehaviour.Encode(wRxParameters); // +2 max 25 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"encrypting local responder endpoint={localResponderEndpoint}, localNeighborToken={LocalNeighborToken32} into ACK1"); } wRxParameters.Write(Magic16_responderToRequester); // +2 max 27 var bytesRemaining = RegisterAck1Packet.ToResponderTxParametersEncryptedLength - (int)msRxParameters.Length; wRxParameters.Write(_engine.CryptoLibrary.GetRandomBytes(bytesRemaining)); var localRxParametersDecrypted = msRxParameters.ToArray(); // total 32 bytes = RegisterAck1Packet.ToResponderTxParametersEncryptedLength var localRxParametersEncrypted = new byte[localRxParametersDecrypted.Length]; _engine.CryptoLibrary.ProcessAesCbcBlocks(true, aesKey, iv, localRxParametersDecrypted, localRxParametersEncrypted); if (localRxParametersEncrypted.Length != RegisterAck1Packet.ToResponderTxParametersEncryptedLength) { throw new Exception(); } return(localRxParametersEncrypted); }
/// <summary> /// when sending ACK /// </summary> public byte[] Encrypt_ack2_ToRequesterTxParametersEncrypted_AtRequester(Logger logger, RegisterRequestPacket req, RegisterAck1Packet ack1, RegisterAck2Packet ack2) { if (SharedDhSecret == null) { throw new InvalidOperationException(); } #region aes key, iv PacketProcedures.CreateBinaryWriter(out var ms, out var writer); req.GetSharedSignedFields(writer, true); ack1.GetSharedSignedFields(writer, true, true); ack2.GetSharedSignedFields(writer, false, false); var iv = _engine.CryptoLibrary.GetHashSHA256(ms.ToArray()).Take(16).ToArray(); ms.Write(SharedDhSecret, 0, SharedDhSecret.Length); var aesKey = _engine.CryptoLibrary.GetHashSHA256(ms.ToArray()); // here SHA256 is used as KDF, together with common fields from packets, including both ECDH public keys and timestamp #endregion // encode localRxParameters PacketProcedures.CreateBinaryWriter(out var msRxParameters, out var wRxParameters); PacketProcedures.EncodeIPEndPoint(wRxParameters, LocalEndpoint); // max 19 LocalNeighborToken32.Encode(wRxParameters); // +4 max 23 if (logger.WriteToLog_detail_enabled) { logger.WriteToLog_detail($"encrypting local requester endpoint={LocalEndpoint}, localNeighborToken={LocalNeighborToken32} into ACK2"); } wRxParameters.Write(Magic16_ipv4_requesterToResponder); // +2 max 25 var bytesRemaining = RegisterAck2Packet.ToRequesterTxParametersEncryptedLength - (int)msRxParameters.Length; wRxParameters.Write(_engine.CryptoLibrary.GetRandomBytes(bytesRemaining)); var localRxParametersDecrypted = msRxParameters.ToArray(); var localRxParametersEncrypted = new byte[localRxParametersDecrypted.Length]; _engine.CryptoLibrary.ProcessAesCbcBlocks(true, aesKey, iv, localRxParametersDecrypted, localRxParametersEncrypted); if (localRxParametersEncrypted.Length != RegisterAck2Packet.ToRequesterTxParametersEncryptedLength) { throw new Exception(); } return(localRxParametersEncrypted); }