diff --git a/MLAPI/Data/Channel.cs b/MLAPI/Data/Channel.cs new file mode 100644 index 0000000..b0b0826 --- /dev/null +++ b/MLAPI/Data/Channel.cs @@ -0,0 +1,15 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using UnityEngine.Networking; + +namespace MLAPI.Data +{ + [Serializable] + public class Channel + { + public string Name; + public QosType Type; + } +} diff --git a/MLAPI/Data/NetworkingConfiguration.cs b/MLAPI/Data/NetworkConfig.cs similarity index 86% rename from MLAPI/Data/NetworkingConfiguration.cs rename to MLAPI/Data/NetworkConfig.cs index b6d00ed..2b0221e 100644 --- a/MLAPI/Data/NetworkingConfiguration.cs +++ b/MLAPI/Data/NetworkConfig.cs @@ -1,7 +1,10 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Security.Cryptography; +using System.Text; +using UnityEngine; using UnityEngine.Networking; namespace MLAPI.Data @@ -9,7 +12,8 @@ namespace MLAPI.Data /// /// The configuration object used to start server, client and hosts /// - public class NetworkingConfiguration + [Serializable] + public class NetworkConfig { /// /// The protocol version. Different versions doesn't talk to each other. @@ -18,7 +22,7 @@ namespace MLAPI.Data /// /// Channels used by the NetworkedTransport /// - public SortedDictionary Channels = new SortedDictionary(); + public List Channels = new List(); /// /// Registered MessageTypes /// @@ -27,14 +31,12 @@ namespace MLAPI.Data /// List of MessageTypes that can be passed through by Server. MessageTypes in this list should thus not be trusted to as great of an extent as normal messages. /// public List PassthroughMessageTypes = new List(); - /// - /// Internal collection of Passthrough MessageTypes - /// - internal HashSet RegisteredPassthroughMessageTypes = new HashSet(); + internal HashSet PassthroughMessageHashSet = new HashSet(); /// /// Set of channels that will have all message contents encrypted when used /// - public HashSet EncryptedChannels = new HashSet(); + public List EncryptedChannels = new List(); + internal HashSet EncryptedChannelsHashSet = new HashSet(); /// /// A list of SceneNames that can be used during networked games. /// @@ -80,10 +82,6 @@ namespace MLAPI.Data /// public bool ConnectionApproval = false; /// - /// The callback to invoke when a connection has to be decided if it should get approved - /// - public Action> ConnectionApprovalCallback = null; - /// /// The data to send during connection which can be used to decide on if a client should get accepted /// public byte[] ConnectionData = new byte[0]; @@ -106,10 +104,12 @@ namespace MLAPI.Data /// /// Private RSA XML key to use for signing key exchange /// + [TextArea] public string RSAPrivateKey = "vBEvOQki/EftWOgwh4G8/nFRvcDJLylc8P7Dhz5m/hpkkNtAMzizNKYUrGbs7sYWlEuMYBOWrzkIDGOMoOsYc9uCi+8EcmNoHDlIhK5yNfZUexYBF551VbvZ625LSBR7kmBxkyo4IPuA09fYCHeUFm3prt4h6aTD0Hjc7ZsJHUU=EQ==

ydgcrq5qLJOdDQibD3m9+o3/dkKoFeCC110dnMgdpEteCruyBdL0zjGKKvjjgy3XTSSp43EN591NiXaBp0JtDw==

7obHrUnUCsSHUsIJ7+JOrupcGrQ0XaYcQ+Uwb2v7d2YUzwZ46U4gI9snfD2J0tc3DGEh3v3G0Q8q7bxEe3H4aw==L34k3c6vkgSdbHp+1nb/hj+HZx6+I0PijQbZyolwYuSOmR0a1DGjA1bzVWe9D86NAxevgM9OkOjG8yrxVIgZqQ==OB+2gyBuIKa2bdNNodrlVlVC2RtXnZB/HwjAGjeGdnJfP8VJoE6eJo3rLEq3BG7fxq1xYaUfuLhGVg4uOyngGQ==o97PimYu58qH5eFmySRCIsyhBr/tK2GM17Zd9QQPJZRSorrhIJn1m6gwQ/G5aJLIM/3Yl04CoyqmQGsPXMzW2w==CxAR1i22w4vCquB7U0Pd8Nl9R2Wxez6rHTwpnoszPB+rkAzlqKj7e5FMgpykhoQfciKPyWqQZKkAeTMIRbN56JinvpAt5POId/28HDd5xjGymHE81k3RzoHqzQXFIOF1TSYKUWzjPPF/TU4nn7auD4i6lOODATsMqtLr5DRBN/0=
"; //CHANGE THESE FOR PRODUCTION! /// /// Public RSA XML key to use for signing key exchange /// + [TextArea] public string RSAPublicKey = "vBEvOQki/EftWOgwh4G8/nFRvcDJLylc8P7Dhz5m/hpkkNtAMzizNKYUrGbs7sYWlEuMYBOWrzkIDGOMoOsYc9uCi+8EcmNoHDlIhK5yNfZUexYBF551VbvZ625LSBR7kmBxkyo4IPuA09fYCHeUFm3prt4h6aTD0Hjc7ZsJHUU=EQ=="; //CHANGE THESE FOR PRODUCTION! /// /// Wheter or not to allow any type of passthrough messages @@ -119,10 +119,12 @@ namespace MLAPI.Data /// Wheter or not to enable scene switching /// public bool EnableSceneSwitching = false; + /// + /// The RSA Keysize to use + /// + public int RSAKeySize = 2048; - //Cached config hash private byte[] ConfigHash = null; - /// /// Gets a SHA256 hash of parts of the NetworkingConfiguration instance /// @@ -133,41 +135,48 @@ namespace MLAPI.Data if (ConfigHash != null && cache) return ConfigHash; - using(MemoryStream writeStream = new MemoryStream()) + using (MemoryStream writeStream = new MemoryStream()) { - using(BinaryWriter writer = new BinaryWriter(writeStream)) + using (BinaryWriter writer = new BinaryWriter(writeStream)) { writer.Write(ProtocolVersion); - foreach (KeyValuePair pair in Channels) + for (int i = 0; i < Channels.Count; i++) { - writer.Write(pair.Key); - writer.Write((int)pair.Value); + writer.Write(Channels[i].Name); + writer.Write((byte)Channels[i].Type); } for (int i = 0; i < MessageTypes.Count; i++) { writer.Write(MessageTypes[i]); } - if(AllowPassthroughMessages) + if (AllowPassthroughMessages) { for (int i = 0; i < PassthroughMessageTypes.Count; i++) { writer.Write(PassthroughMessageTypes[i]); } } - if(EnableSceneSwitching) + if (EnableSceneSwitching) { for (int i = 0; i < RegisteredScenes.Count; i++) { writer.Write(RegisteredScenes[i]); } } + if(EnableEncryption) + { + for (int i = 0; i < EncryptedChannels.Count; i++) + { + writer.Write(EncryptedChannels[i]); + } + } writer.Write(HandleObjectSpawning); writer.Write(EnableEncryption); writer.Write(AllowPassthroughMessages); writer.Write(EnableSceneSwitching); writer.Write(SignKeyExchange); } - using(SHA256Managed sha256 = new SHA256Managed()) + using (SHA256Managed sha256 = new SHA256Managed()) { //Returns a 256 bit / 32 byte long checksum of the config if (cache) diff --git a/MLAPI/MLAPI.csproj b/MLAPI/MLAPI.csproj index 7cb2cfc..7b0bc3b 100644 --- a/MLAPI/MLAPI.csproj +++ b/MLAPI/MLAPI.csproj @@ -67,8 +67,10 @@ + + @@ -81,7 +83,6 @@ - diff --git a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs index af98611..bcaf43b 100644 --- a/MLAPI/MonoBehaviours/Core/NetworkingManager.cs +++ b/MLAPI/MonoBehaviours/Core/NetworkingManager.cs @@ -142,14 +142,20 @@ namespace MLAPI.MonoBehaviours.Core /// public Action OnServerStarted = null; /// + /// The callback to invoke during connection approval + /// + public Action> ConnectionApprovalCallback = null; + /// /// The current NetworkingConfiguration /// - public NetworkingConfiguration NetworkConfig; + public NetworkConfig NetworkConfig; private EllipticDiffieHellman clientDiffieHellman; private Dictionary diffieHellmanPublicKeys; private byte[] clientAesKey; + public bool RegenerateRSAKeys = false; + private void OnValidate() { if (SpawnablePrefabs != null) @@ -175,11 +181,26 @@ namespace MLAPI.MonoBehaviours.Core Debug.LogWarning("MLAPI: The player object needs a NetworkedObject component."); } } + + if (!NetworkConfig.EnableEncryption) + RegenerateRSAKeys = false; + else + { + if(RegenerateRSAKeys) + { + using (RSACryptoServiceProvider rsa = new RSACryptoServiceProvider()) + { + rsa.PersistKeyInCsp = false; + NetworkConfig.RSAPrivateKey = rsa.ToXmlString(true); + NetworkConfig.RSAPublicKey = rsa.ToXmlString(false); + } + RegenerateRSAKeys = false; + } + } } - private ConnectionConfig Init(NetworkingConfiguration netConfig) + private ConnectionConfig Init() { - NetworkConfig = netConfig; networkTime = 0f; lastSendTickTime = 0; lastEventTickTime = 0; @@ -226,11 +247,32 @@ namespace MLAPI.MonoBehaviours.Core }; //MLAPI channels and messageTypes - NetworkConfig.Channels.Add("MLAPI_INTERNAL", QosType.ReliableFragmentedSequenced); - NetworkConfig.Channels.Add("MLAPI_POSITION_UPDATE", QosType.StateUpdate); - NetworkConfig.Channels.Add("MLAPI_ANIMATION_UPDATE", QosType.ReliableSequenced); - NetworkConfig.Channels.Add("MLAPI_NAV_AGENT_STATE", QosType.ReliableSequenced); - NetworkConfig.Channels.Add("MLAPI_NAV_AGENT_CORRECTION", QosType.StateUpdate); + NetworkConfig.Channels.Add(new Channel() + { + Name = "MLAPI_INTERNAL", + Type = QosType.ReliableFragmentedSequenced + }); + NetworkConfig.Channels.Add(new Channel() + { + Name = "MLAPI_POSITION_UPDATE", + Type = QosType.StateUpdate + }); + NetworkConfig.Channels.Add(new Channel() + { + Name = "MLAPI_ANIMATION_UPDATE", + Type = QosType.ReliableSequenced + }); + NetworkConfig.Channels.Add(new Channel() + { + Name = "MLAPI_NAV_AGENT_STATE", + Type = QosType.ReliableSequenced + }); + NetworkConfig.Channels.Add(new Channel() + { + Name = "MLAPI_NAV_AGENT_CORRECTION", + Type = QosType.StateUpdate + }); + MessageManager.messageTypes.Add("MLAPI_CONNECTION_REQUEST", 0); MessageManager.messageTypes.Add("MLAPI_CONNECTION_APPROVED", 1); MessageManager.messageTypes.Add("MLAPI_ADD_OBJECT", 2); @@ -260,21 +302,52 @@ namespace MLAPI.MonoBehaviours.Core NetworkSceneManager.SetCurrentSceneIndex(); } - + + if(NetworkConfig.EnableEncryption) + { + List channelNameList = NetworkConfig.Channels.Select(x => x.Name).ToList(); + for (int i = 0; i < NetworkConfig.EncryptedChannels.Count; i++) + { + if (NetworkConfig.EncryptedChannelsHashSet.Contains(NetworkConfig.EncryptedChannels[i])) + { + Debug.LogWarning("MLAPI: Duplicate encrypted channel: " + NetworkConfig.EncryptedChannels[i]); + continue; + } + else if (!channelNameList.Contains(NetworkConfig.EncryptedChannels[i])) + { + Debug.LogWarning("MLAPI: Channel " + NetworkConfig.EncryptedChannels[i] + " doesn't exist"); + } + NetworkConfig.EncryptedChannelsHashSet.Add(NetworkConfig.EncryptedChannels[i]); + } + } + + if (NetworkConfig.AllowPassthroughMessages) + { + for (int i = 0; i < NetworkConfig.PassthroughMessageTypes.Count; i++) + { + if(!NetworkConfig.MessageTypes.Contains(NetworkConfig.PassthroughMessageTypes[i])) + { + Debug.LogWarning("MLAPI: The messageType " + NetworkConfig.PassthroughMessageTypes[i] + " doesn't exist"); + continue; + } + NetworkConfig.PassthroughMessageHashSet.Add(MessageManager.messageTypes[NetworkConfig.PassthroughMessageTypes[i]]); + } + } HashSet channelNames = new HashSet(); - foreach (KeyValuePair pair in NetworkConfig.Channels) + for (int i = 0; i < NetworkConfig.Channels.Count; i++) { - if(channelNames.Contains(pair.Key)) + if(channelNames.Contains(NetworkConfig.Channels[i].Name)) { - Debug.LogWarning("MLAPI: Duplicate channel name: " + pair.Key); + Debug.LogWarning("MLAPI: Duplicate channel name: " + NetworkConfig.Channels[i].Name); continue; } - int channelId = cConfig.AddChannel(pair.Value); - MessageManager.channels.Add(pair.Key, channelId); - channelNames.Add(pair.Key); - MessageManager.reverseChannels.Add(channelId, pair.Key); + int channelId = cConfig.AddChannel(NetworkConfig.Channels[i].Type); + MessageManager.channels.Add(NetworkConfig.Channels[i].Name, channelId); + channelNames.Add(NetworkConfig.Channels[i].Name); + MessageManager.reverseChannels.Add(channelId, NetworkConfig.Channels[i].Name); } + //0-32 are reserved for MLAPI messages ushort messageId = 32; for (ushort i = 0; i < NetworkConfig.MessageTypes.Count; i++) @@ -283,15 +356,6 @@ namespace MLAPI.MonoBehaviours.Core MessageManager.reverseMessageTypes.Add(messageId, NetworkConfig.MessageTypes[i]); messageId++; } - - if (NetworkConfig.AllowPassthroughMessages) - { - for (int i = 0; i < NetworkConfig.PassthroughMessageTypes.Count; i++) - { - NetworkConfig.RegisteredPassthroughMessageTypes.Add(MessageManager.messageTypes[NetworkConfig.PassthroughMessageTypes[i]]); - } - } - return cConfig; } @@ -299,12 +363,12 @@ namespace MLAPI.MonoBehaviours.Core /// Starts a server with a given NetworkingConfiguration /// /// The NetworkingConfiguration to use - public void StartServer(NetworkingConfiguration netConfig) + public void StartServer() { - ConnectionConfig cConfig = Init(netConfig); + ConnectionConfig cConfig = Init(); if (NetworkConfig.ConnectionApproval) { - if (NetworkConfig.ConnectionApprovalCallback == null) + if (ConnectionApprovalCallback == null) { Debug.LogWarning("MLAPI: No ConnectionApproval callback defined. Connection approval will timeout"); } @@ -323,9 +387,9 @@ namespace MLAPI.MonoBehaviours.Core /// Starts a client with a given NetworkingConfiguration /// /// The NetworkingConfiguration to use - public void StartClient(NetworkingConfiguration netConfig) + public void StartClient() { - ConnectionConfig cConfig = Init(netConfig); + ConnectionConfig cConfig = Init(); HostTopology hostTopology = new HostTopology(cConfig, NetworkConfig.MaxConnections); hostId = NetworkTransport.AddHost(hostTopology, 0, null); @@ -387,12 +451,12 @@ namespace MLAPI.MonoBehaviours.Core /// Starts a Host with a given NetworkingConfiguration /// /// The NetworkingConfiguration to use - public void StartHost(NetworkingConfiguration netConfig) + public void StartHost() { - ConnectionConfig cConfig = Init(netConfig); + ConnectionConfig cConfig = Init(); if (NetworkConfig.ConnectionApproval) { - if (NetworkConfig.ConnectionApprovalCallback == null) + if (ConnectionApprovalCallback == null) { Debug.LogWarning("MLAPI: No ConnectionApproval callback defined. Connection approval will timeout"); } @@ -612,7 +676,7 @@ namespace MLAPI.MonoBehaviours.Core ushort bytesToRead = reader.ReadUInt16(); byte[] incommingData = reader.ReadBytes(bytesToRead); - if(NetworkConfig.EncryptedChannels.Contains(channelId)) + if(NetworkConfig.EncryptedChannelsHashSet.Contains(MessageManager.reverseChannels[channelId])) { //Encrypted message if (isServer) @@ -621,12 +685,12 @@ namespace MLAPI.MonoBehaviours.Core incommingData = CryptographyHelper.Decrypt(incommingData, clientAesKey); } - if (isServer && isPassthrough && !NetworkConfig.RegisteredPassthroughMessageTypes.Contains(messageType)) + if (isServer && isPassthrough && !NetworkConfig.PassthroughMessageHashSet.Contains(messageType)) { Debug.LogWarning("MLAPI: Client " + clientId + " tried to send a passthrough message for a messageType not registered as passthrough"); return; } - else if(isClient && isPassthrough && !NetworkConfig.RegisteredPassthroughMessageTypes.Contains(messageType)) + else if(isClient && isPassthrough && !NetworkConfig.PassthroughMessageHashSet.Contains(messageType)) { Debug.LogWarning("MLAPI: Server tried to send a passthrough message for a messageType not registered as passthrough"); return; @@ -716,7 +780,7 @@ namespace MLAPI.MonoBehaviours.Core { ushort bufferSize = messageReader.ReadUInt16(); byte[] connectionBuffer = messageReader.ReadBytes(bufferSize); - NetworkConfig.ConnectionApprovalCallback(connectionBuffer, clientId, HandleApproval); + ConnectionApprovalCallback(connectionBuffer, clientId, HandleApproval); } else { @@ -1104,7 +1168,7 @@ namespace MLAPI.MonoBehaviours.Core writer.Write(orderId.Value); writer.Write(true); writer.Write(sourceId); - if(NetworkConfig.EncryptedChannels.Contains(channelId)) + if(NetworkConfig.EncryptedChannelsHashSet.Contains(MessageManager.reverseChannels[channelId])) { //Encrypted message byte[] encrypted = CryptographyHelper.Encrypt(data, connectedClients[targetId].AesKey); @@ -1136,7 +1200,7 @@ namespace MLAPI.MonoBehaviours.Core } bool isPassthrough = (!isServer && clientId != serverClientId && NetworkConfig.AllowPassthroughMessages); - if (isPassthrough && !NetworkConfig.RegisteredPassthroughMessageTypes.Contains(MessageManager.messageTypes[messageType])) + if (isPassthrough && !NetworkConfig.PassthroughMessageHashSet.Contains(MessageManager.messageTypes[messageType])) { Debug.LogWarning("MLAPI: The The MessageType " + messageType + " is not registered as an allowed passthrough message type."); return; @@ -1165,7 +1229,7 @@ namespace MLAPI.MonoBehaviours.Core if (isPassthrough) writer.Write(clientId); - if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + if (NetworkConfig.EncryptedChannelsHashSet.Contains(channelName)) { //This is an encrypted message. byte[] encrypted; @@ -1195,7 +1259,7 @@ namespace MLAPI.MonoBehaviours.Core internal void Send(int[] clientIds, string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null) { - if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + if (NetworkConfig.EncryptedChannelsHashSet.Contains(channelName)) { Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients."); return; @@ -1242,7 +1306,7 @@ namespace MLAPI.MonoBehaviours.Core internal void Send(List clientIds, string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null) { - if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + if (NetworkConfig.EncryptedChannelsHashSet.Contains(channelName)) { Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients."); return; @@ -1291,7 +1355,7 @@ namespace MLAPI.MonoBehaviours.Core internal void Send(string messageType, string channelName, byte[] data, uint? networkId = null, ushort? orderId = null) { - if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + if (NetworkConfig.EncryptedChannels.Contains(channelName)) { Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients."); return; @@ -1341,7 +1405,7 @@ namespace MLAPI.MonoBehaviours.Core internal void Send(string messageType, string channelName, byte[] data, int clientIdToIgnore, uint? networkId = null, ushort? orderId = null) { - if (NetworkConfig.EncryptedChannels.Contains(MessageManager.channels[channelName])) + if (NetworkConfig.EncryptedChannels.Contains(channelName)) { Debug.LogWarning("MLAPI: Cannot send messages over encrypted channel to multiple clients."); return;