public static byte[] CreateTransportPacket(TransportPacket packet) { // [TNW]:[TYPE]:[VERSION]:[DATA_SIZE]:[DATA]:[EOP] : MIN SIZE 12 Bytes // TNW : 'T', 'N', 'W' : 3 BYTES // TYPE : 1 BYTE // VERSION : 1 BYTE // DATA_SZ : 4 BYTES [Little Endian Byte Order] // DATA : DATA_SZ BYTES // EOP : 'E', 'O', 'P' : 3 BYTES MemoryStream ms = new MemoryStream(); ms.WriteByte((byte)'T'); ms.WriteByte((byte)'N'); ms.WriteByte((byte)'W'); ms.WriteByte((byte)packet.Type); ms.WriteByte((byte)packet.Version); ms.Write(Utils.GetLengthAsBytes(packet.Data.Length), 0, 4); ms.Write(packet.Data, 0, packet.Data.Length); ms.WriteByte((byte)'E'); ms.WriteByte((byte)'O'); ms.WriteByte((byte)'P'); return ms.ToArray(); }
public static async Task SendTransportPacket(NetworkStream sw, TransportPacketType type, byte[] Data) { TransportPacket tp = new TransportPacket(Data, type, Constants.TransportVersion); byte[] msg = PacketCodec.CreateTransportPacket(tp); sw.Write(msg, 0, msg.Length); await sw.FlushAsync(); }
//System.Diagnostics.Stopwatch sw_timer = new System.Diagnostics.Stopwatch(); async Task ProcessIncomingUserInternal(IncomingClient iClient, TransportPacket p) { BinaryReader reader = new BinaryReader(iClient.client.GetStream()); //BinaryWriter writer = new BinaryWriter(iClient.client.GetStream()); NetworkStream writer = iClient.client.GetStream(); //DisplayUtils.Display("Packet Received: " + p.Type); switch (p.Type) { case TransportPacketType.Initialize: iClient.ConnTimeStart = DateTime.UtcNow.Ticks; //DisplayUtils.Display("Init Received", DisplayType.Info); if ((!iClient.WorkProven) && (!iClient.KeyExchanged)) { await PacketSender.SendTransportPacket(writer, TransportPacketType.WorkProofRequest, iClient.WorkTask); } break; case TransportPacketType.WorkProofKeyResponse: //DisplayUtils.Display("WorkProofResponse Received : " + p.Data.Length, DisplayType.Info); if ((!iClient.WorkProven) && (!iClient.KeyExchanged)) { if (p.Data.Length == 60) { Constants.SERVER_GLOBAL_AUTH_PACKETS++; byte[] Proof = new byte[4]; byte[] DHClientPublic = new byte[32]; byte[] AuthRandom = new byte[24]; Array.Copy(p.Data, 0, Proof, 0, 4); Array.Copy(p.Data, 4, DHClientPublic, 0, 32); Array.Copy(p.Data, 36, AuthRandom, 0, 24); iClient.WorkProven = WorkProof.VerifyProof(iClient.WorkTask, Proof, Constants.Difficulty); if (iClient.WorkProven) { //DisplayUtils.Display("Work Proved", DisplayType.Info); Common.rngCsp.GetBytes(iClient.DHRandomBytes); iClient.DHPrivateKey = Curve25519.ClampPrivateKey(iClient.DHRandomBytes); iClient.DHPublicKey = Curve25519.GetPublicKey(iClient.DHPrivateKey); // Generate the shared-secret using the provided client Public Key byte[] sharedSecret = (new SHA512Managed()).ComputeHash(Curve25519.GetSharedSecret(iClient.DHPrivateKey, DHClientPublic)); Array.Copy(sharedSecret, iClient.TransportKey, 32); Array.Copy(sharedSecret, 32, iClient.AuthenticationKey, 0, 32); // Sign the data using the Node-Private key, so that the client can know that the connection is secure. // This thwarts MITM attacks. byte[] Client_ServerAuthSignature = nodeConfig.SignDataWithPrivateKey(AuthRandom); // 64 bytes Signature byte[] signPlain = Client_ServerAuthSignature; if (signPlain.Length != 64) throw new Exception("Improbable Assertion failed : 1"); // Encrypt the Signature and Identifier using Salsa20 byte[] signCrypted = Salsa20.ProcessSalsa20(signPlain, iClient.TransportKey, new byte[8], 0); // EtM -> Encrypt then MAC byte[] signMAC = (new HMACSHA256(iClient.AuthenticationKey)).ComputeHash(signCrypted); // SERVER_PUBLIC[32] || signMAC[32] || signCrypted[64] => 128 bytes byte[] KeysSignature = iClient.DHPublicKey.Concat(signMAC).Concat(signCrypted).ToArray(); await PacketSender.SendTransportPacket(writer, TransportPacketType.ServerPublicTransfer, KeysSignature); } else { DisplayUtils.Display("Work Proof invalid : " + p.Data.Length, DisplayType.Exception); await PacketSender.SendTransportPacket(writer, TransportPacketType.InvalidAuthDisconnect, new byte[0]); } } else { DisplayUtils.Display("Invalid Packet Length : " + p.Data.Length, DisplayType.Exception); await PacketSender.SendTransportPacket(writer, TransportPacketType.InvalidAuthDisconnect, new byte[0]); } } break; case TransportPacketType.KeyExComplete_1: if ((iClient.WorkProven) && (!iClient.KeyExchanged)) { if (p.Data.Length == 128) { // cryptedSigPK_MAC[32] || cryptedSigPK[96] = 128 bytes byte[] cryptedSigPK_MAC = new byte[32]; byte[] cryptedSigPK = new byte[96]; Array.Copy(p.Data, 0, cryptedSigPK_MAC, 0, 32); Array.Copy(p.Data, 32, cryptedSigPK, 0, 96); byte[] cryptedSigPK_MAC_Expected = (new HMACSHA256(iClient.AuthenticationKey)).ComputeHash(cryptedSigPK); if (CryptoBytes.ConstantTimeEquals(cryptedSigPK_MAC_Expected, cryptedSigPK_MAC)) { byte[] workSignPK = Salsa20.ProcessSalsa20(cryptedSigPK, iClient.TransportKey, new byte[8], 0); // workSignPK[96] = workSign[64] || nodeConfig.PublicKey[32] byte[] workSign = new byte[64]; byte[] remotePK = new byte[32]; Array.Copy(workSignPK, 0, workSign, 0, 64); Array.Copy(workSignPK, 64, remotePK, 0, 32); bool serverVerified = Ed25519.Verify(workSign, iClient.WorkTask, remotePK); if (serverVerified) { iClient.PublicKey = new Hash(remotePK); if (Constants.NetworkVerbosity >= Verbosity.Info) { /*DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : Key Exchanged 1: Shared KEY [TransportKey] : {" + (HexUtil.ToString(TransportKey)) + "}", DisplayType.Info);*/ DisplayUtils.Display(iClient.PublicKey.ToString() + " Client Authenticated", DisplayType.Info); } await PacketSender.SendTransportPacket(writer, TransportPacketType.KeyExComplete_2, new byte[0]); iClient.KeyExchanged = true; IncomingConnections.Add(iClient.PublicKey, iClient); DisplayUtils.Display("Exchange Complete 2, Shared KEY [TransportKey] : " + HexUtil.ToString(iClient.TransportKey), DisplayType.Info); } } } } break; case TransportPacketType.DataCrypted: if (iClient.KeyExchanged && p.Data.Length > 28) { Constants.SERVER_GLOBAL_DATA_PACKETS++; byte[] nonce = new byte[8]; byte[] hmac = new byte[16]; byte[] crypted_data = new byte[p.Data.Length - 24]; Array.Copy(p.Data, 0, nonce, 0, 8); Array.Copy(p.Data, 8, hmac, 0, 16); Array.Copy(p.Data, 24, crypted_data, 0, crypted_data.Length); byte[] MAC_EXPECTED = (new HMACSHA256(iClient.AuthenticationKey)).ComputeHash(crypted_data).Take(16).ToArray(); if (CryptoBytes.ConstantTimeEquals(MAC_EXPECTED, hmac)) { byte[] DeCryptedData = Salsa20.ProcessSalsa20(crypted_data, iClient.TransportKey, nonce, 0); byte[] counter = new byte[4]; byte[] rec_data = new byte[p.Data.Length - 28]; Array.Copy(DeCryptedData, 0, counter, 0, 4); Array.Copy(DeCryptedData, 4, rec_data, 0, rec_data.Length); Array.Copy(DeCryptedData, 0, counter, 0, 4); Array.Copy(DeCryptedData, 4, rec_data, 0, rec_data.Length); NetworkPacket np = new NetworkPacket(); np.Deserialize(rec_data); if (iClient.PublicKey == np.PublicKeySource) { if (PacketReceived != null) PacketReceived(np); } else { DisplayUtils.Display("Packet Source Incoming Mismatch", DisplayType.Warning); } } else { DisplayUtils.Display("HMAC FAILED : "); } } break; } }
async private Task ProcessOutgoingConnectionInternal(TcpClient client, TransportPacket p) { BinaryReader reader = new BinaryReader(client.GetStream()); NetworkStream writer = client.GetStream(); switch (p.Type) // Decode received messages from Server. { case TransportPacketType.WorkProofRequest: // INPUT : 24 Bytes of (random[16] + timestamp[8]) // OUTPUT : 4 bytes Proof, 32 Bytes of KeypairPublic, 16 Bytes Random => 52 bytes if (p.Data.Length == 24) { //DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : WorkProofRequest Received : " + p.Data.Length, DisplayType.Info); byte[] Proof = WorkProof.CalculateProof(p.Data, Constants.Difficulty); WorkTask = p.Data; byte[] rv = new byte[Proof.Length + DH_PublicKey.Length + AuthRandom.Length /*+ Sig_WorkTask.Length*/]; Buffer.BlockCopy(Proof, 0, rv, 0, Proof.Length); Buffer.BlockCopy(DH_PublicKey, 0, rv, Proof.Length, DH_PublicKey.Length); Buffer.BlockCopy(AuthRandom, 0, rv, Proof.Length + DH_PublicKey.Length, AuthRandom.Length); await PacketSender.SendTransportPacket(writer, TransportPacketType.WorkProofKeyResponse, rv); } break; case TransportPacketType.ServerPublicTransfer: // SERVER_PUBLIC[32] || SIGN_MAC[32] || Sign_Crypted[64] => 128 bytes if (p.Data.Length == 128) { byte[] serverPublic = new byte[32]; byte[] signMAC = new byte[32]; byte[] signCrypted = new byte[64]; Array.Copy(p.Data, 0, serverPublic, 0, 32); Array.Copy(p.Data, 32, signMAC, 0, 32); Array.Copy(p.Data, 64, signCrypted, 0, 64); byte[] sharedSecret = (new SHA512Managed()).ComputeHash(Curve25519.GetSharedSecret(DH_PrivateKey, serverPublic)); Array.Copy(sharedSecret, TransportKey, 32); Array.Copy(sharedSecret, 32, AuthenticationKey, 0, 32); byte[] SIGN_ID_MAC_EXPECTED = (new HMACSHA256(AuthenticationKey)).ComputeHash(signCrypted); if (CryptoBytes.ConstantTimeEquals(SIGN_ID_MAC_EXPECTED, signMAC)) { byte[] Sign_Plain = Salsa20.ProcessSalsa20(signCrypted, TransportKey, new byte[8], 0); // The remote node should be able to identify itself properly. bool serverVerified = Ed25519.Verify(Sign_Plain, AuthRandom, nodeSocketData.PublicKey.Hex); if (serverVerified) { if (Constants.NetworkVerbosity >= Verbosity.Info) { DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : Key Exchanged 1: Shared KEY [TransportKey] : {" + (HexUtil.ToString(TransportKey)) + "}", DisplayType.Info); DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : Server Authenticated 1", DisplayType.Info); } // workSignPK[96] = workSign[64] || nodeConfig.PublicKey[32] // Sign the WorkTask with private key. byte[] workSign = nodeConfig.SignDataWithPrivateKey(WorkTask); byte[] workSignPK = new byte[96]; // 64 + 32 Array.Copy(workSign, workSignPK, 64); Array.Copy(nodeConfig.PublicKey.Hex, 0, workSignPK, 64, 32); // cryptedSigPK_MAC[32] || cryptedSigPK[96] byte[] cryptedSigPK = Salsa20.ProcessSalsa20(workSignPK, TransportKey, new byte[8], 0); byte[] cryptedSigPK_MAC = (new HMACSHA256(AuthenticationKey)).ComputeHash(cryptedSigPK); byte[] macSigPK = new byte[128]; // 32 + 96 Array.Copy(cryptedSigPK_MAC, macSigPK, 32); Array.Copy(cryptedSigPK, 0, macSigPK, 32, 96); await PacketSender.SendTransportPacket(writer, TransportPacketType.KeyExComplete_1, macSigPK); InterKeyExchanged = true; } else { if (Constants.NetworkVerbosity >= Verbosity.Errors) DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : Remote Authentication Failed : Remote Node Could not authenticate itself", DisplayType.AuthFailure); } } else { if (Constants.NetworkVerbosity >= Verbosity.Errors) DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : Server Authentication Failed : Server Could not authenticate itself : Invalid MAC ", DisplayType.AuthFailure); } } break; case TransportPacketType.KeyExComplete_2: if (InterKeyExchanged) { KeyExchanged = true; if (Constants.NetworkVerbosity >= Verbosity.Info) { DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : Key Exchanged 3 (Sync)", DisplayType.Info); } } break; case TransportPacketType.KeepAlive: if (p.Data.Length == 4) { uint pCounter = BitConverter.ToUInt32(p.Data, 0); if (KeepAlives != null) KeepAlives(pCounter); } break; case TransportPacketType.DataCrypted: if (KeyExchanged) { if (p.Data.Length >= 28) { byte[] nonce = new byte[8]; byte[] hmac = new byte[16]; byte[] crypted_data = new byte[p.Data.Length - 24]; Array.Copy(p.Data, 0, nonce, 0, 8); Array.Copy(p.Data, 8, hmac, 0, 16); Array.Copy(p.Data, 24, crypted_data, 0, crypted_data.Length); byte[] MAC_EXPECTED = (new HMACSHA256(AuthenticationKey)).ComputeHash(crypted_data).Take(16).ToArray(); if (CryptoBytes.ConstantTimeEquals(MAC_EXPECTED, hmac)) { byte[] DeCryptedData = new byte[p.Data.Length - 24]; DeCryptedData = Salsa20.ProcessSalsa20(crypted_data, TransportKey, nonce, 0); byte[] counter = new byte[4]; byte[] rec_data = new byte[p.Data.Length - 28]; Array.Copy(DeCryptedData, 0, counter, 0, 4); Array.Copy(DeCryptedData, 4, rec_data, 0, rec_data.Length); NetworkPacket np = new NetworkPacket(); np.Deserialize(rec_data); if (nodeSocketData.PublicKey == np.PublicKeySource) { if (PacketReceived != null) PacketReceived(np); } else { DisplayUtils.Display("Packet Source Outgoing Mismatch", DisplayType.Warning); } } else { if (Constants.NetworkVerbosity >= Verbosity.Errors) DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : HMAC FAILED : "); } } } break; default: if (Constants.NetworkVerbosity >= Verbosity.Errors) DisplayUtils.Display(NodeSocketData.GetString(nodeSocketData) + " : Unknown Packet received : " + p.Type.ToString()); break; } }