Make the Console<->Launcher system source-built and modern now that the console is under our control and the WinXP-era pods are gone. Contract extraction (Contract/Tesla.Contract.csproj): - One multi-targeted (net48;net8.0-windows) source project for the RPC contract, replacing the vendored TeslaConsoleLaunchLib.dll and the hand-synced Tesla.Net replica in Launcher/LaunchModels_Shared.cs. Emits assembly TeslaConsoleLaunchLib. SecureConfig extraction (SecureConfig/Tesla.SecureConfig.csproj): - net48 source of the first-boot provisioning protocol (UDP beacons, OFB crypto, RSA key exchange), replacing the vendored TeslaSecureConfiguration.dll. Remove BinaryFormatter from the wire (RCE sink + the reason net6 was pinned): - Console<->Launcher RPC is now length-prefixed System.Text.Json frames (Contract/PodRpcProtocol.cs) over the unchanged OFB transport; dispatch by method name. Deleted the SerializationBinder / MethodInfoProxy machinery. - Console-local BinaryFormatter (Site config, mission replays) intentionally retained: local net48 file I/O, not the network surface. Runtime modernization: - Launcher Service + Agent: net6 -> net8, win-x86 -> win-x64 (all pods are 64-bit Win10). Kept the SHA1-default PBKDF2 (Console key-derivation compat) with SYSLIB0041 suppressed and documented. Tests: differential suite now 73 green. Added SecureConfigCompatTests (OFB ciphertext byte-identical to the vendored DLL) and PodRpcProtocolTests (JSON round-trip of every request/response shape); removed the now-obsolete BinaryFormatter byte-identity guard. Build hygiene: per-project obj dirs (Launcher/Directory.Build.props) fix a NuGet restore collision between the two Launcher projects sharing one folder. NOT runtime-verified against a live pod. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1080 lines
31 KiB
C#
1080 lines
31 KiB
C#
// =============================================================================
|
|
// Tesla.SecureConfig — First-boot secure provisioning (assembly: TeslaSecureConfiguration)
|
|
// =============================================================================
|
|
// Reconstructed verbatim (ilspycmd) from the original proprietary
|
|
// TeslaSecureConfiguration.dll. Provides the Console-side pod provisioning
|
|
// protocol: UDP RQST/RPLY beacons, PBKDF2-from-passphrase key derivation, the
|
|
// OFB crypto-stream handshake (NegotiateCryptoStreams), and RSA session-key
|
|
// exchange. Previously consumed by the Console (and the Tesla.Contract client)
|
|
// as a vendored binary; now built from source as the single definition.
|
|
//
|
|
// The wire/crypto behaviour is load-bearing: it must stay byte-compatible with
|
|
// the pod-side counterpart (the Launcher's hand-written OFBDuplexStream /
|
|
// CryptoHelper in Launcher/SecureConfig.cs). Byte-identity against the original
|
|
// binary is asserted by SecureConfigCompatTests in the differential suite.
|
|
//
|
|
// NOTE: this is a faithful decompilation — local names and some control flow
|
|
// differ from the lost original source (e.g. the dead GetInterface(byte[])
|
|
// comparison loop is preserved as-is for behavioural fidelity).
|
|
// =============================================================================
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Net;
|
|
using System.Net.NetworkInformation;
|
|
using System.Net.Sockets;
|
|
using System.Reflection;
|
|
using System.Security.Cryptography;
|
|
using System.Text;
|
|
using System.Threading;
|
|
using System.Timers;
|
|
using Microsoft.Win32;
|
|
|
|
namespace Tesla
|
|
{
|
|
public class PodConfigurationClient : IDisposable
|
|
{
|
|
private static readonly Random sRandom = new Random();
|
|
|
|
private static readonly char[] sPassphraseAlphabet = "23456789ABCDEFGHJKLMNPQRSTUVWXYZ".ToCharArray();
|
|
|
|
private Process mPasscodeDisplay;
|
|
|
|
private string mPassphrase;
|
|
|
|
private string mRequestId;
|
|
|
|
private static TextWriter mLogger;
|
|
|
|
public string Passphrase => mPassphrase;
|
|
|
|
public string RequestId => mRequestId;
|
|
|
|
public static TextWriter Logger
|
|
{
|
|
get
|
|
{
|
|
return mLogger;
|
|
}
|
|
set
|
|
{
|
|
mLogger = value;
|
|
}
|
|
}
|
|
|
|
public PodConfigurationClient(uint passpharaseLength, uint requestIdLength)
|
|
{
|
|
mPassphrase = GenerateRandomString(passpharaseLength);
|
|
mRequestId = GenerateRandomString(requestIdLength);
|
|
mPasscodeDisplay = new Process();
|
|
mPasscodeDisplay.StartInfo = new ProcessStartInfo("TeslaPasscodeDisplay.exe", $"{mRequestId} {mPassphrase}");
|
|
mPasscodeDisplay.StartInfo.CreateNoWindow = false;
|
|
mPasscodeDisplay.StartInfo.UseShellExecute = true;
|
|
mPasscodeDisplay.StartInfo.WorkingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
|
mPasscodeDisplay.Start();
|
|
}
|
|
|
|
private string GenerateRandomString(uint length)
|
|
{
|
|
char[] array = new char[length];
|
|
for (int i = 0; i < array.Length; i++)
|
|
{
|
|
array[i] = sPassphraseAlphabet[sRandom.Next(sPassphraseAlphabet.Length)];
|
|
}
|
|
return new string(array);
|
|
}
|
|
|
|
public byte[] WaitForEncryptionKey(ushort localPort, ushort remotePort, IPAddress identifyingAddress)
|
|
{
|
|
mLogger.WriteLine("Waiting for configuration. Request ID: {0}\tPassphrase: {1}", mRequestId, mPassphrase);
|
|
mLogger.WriteLine("Configuring temporary ip address.");
|
|
mLogger.Flush();
|
|
string adapterName;
|
|
byte[] @interface = GetInterface(identifyingAddress, out adapterName);
|
|
try
|
|
{
|
|
ConfigureTempIp(adapterName);
|
|
}
|
|
catch (Exception innerException)
|
|
{
|
|
throw new Exception("Could not initalize temporary IP address.", innerException);
|
|
}
|
|
mLogger.WriteLine("Temp IP configured.");
|
|
mLogger.Flush();
|
|
byte[] array = PodConfigurationServer.GenerateKeyFromPassphrase(mPassphrase);
|
|
mLogger.WriteLine("Setting up broadcast.");
|
|
mLogger.Flush();
|
|
byte[] bytes = Encoding.ASCII.GetBytes(mRequestId);
|
|
byte[] array2 = new byte[@interface.Length + bytes.Length];
|
|
@interface.CopyTo(array2, 0);
|
|
bytes.CopyTo(array2, @interface.Length);
|
|
UdpBeacon udpBeacon = new UdpBeacon(PodConfigurationServer.sMessageRequestID, array2, 10000.0, remotePort, null);
|
|
udpBeacon.Start();
|
|
mLogger.WriteLine("Broadcasting... waiting for packets.");
|
|
mLogger.Flush();
|
|
BasicConfigResponse basicConfigResponse = ReceiveUdpResponse(localPort, array);
|
|
mLogger.WriteLine("Packets received.");
|
|
mLogger.Flush();
|
|
udpBeacon.Stop();
|
|
mLogger.WriteLine("Attempting to configure final IP.");
|
|
mLogger.Flush();
|
|
try
|
|
{
|
|
SetNetworkProperties(adapterName, basicConfigResponse.Address, basicConfigResponse.Mask, basicConfigResponse.Gateway, basicConfigResponse.Dns, basicConfigResponse.HostName);
|
|
}
|
|
catch (Exception innerException2)
|
|
{
|
|
throw new Exception("Could not initalize final IP address.", innerException2);
|
|
}
|
|
mLogger.WriteLine("Waiting for console to connect.");
|
|
mLogger.Flush();
|
|
TcpListener tcpListener = new TcpListener(IPAddress.Any, localPort);
|
|
tcpListener.Start();
|
|
while (true)
|
|
{
|
|
TcpClient tcpClient = null;
|
|
try
|
|
{
|
|
mLogger.WriteLine("Console connection received. Negotiating...");
|
|
mLogger.Flush();
|
|
tcpClient = tcpListener.AcceptTcpClient();
|
|
if (!PodConfigurationServer.NegotiateCryptoStreams(tcpClient.GetStream(), array, out var outStream, out var inStream))
|
|
{
|
|
continue;
|
|
}
|
|
BinaryWriter binaryWriter = new BinaryWriter(outStream);
|
|
BinaryReader binaryReader = new BinaryReader(inStream);
|
|
mLogger.WriteLine("Secure connection to console negotiated.");
|
|
mLogger.Flush();
|
|
RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider();
|
|
int dwKeySize = rSACryptoServiceProvider.KeySize;
|
|
KeySizes[] legalKeySizes = rSACryptoServiceProvider.LegalKeySizes;
|
|
foreach (KeySizes keySizes in legalKeySizes)
|
|
{
|
|
if (keySizes.MaxSize > rSACryptoServiceProvider.KeySize)
|
|
{
|
|
if (keySizes.MaxSize > 2048)
|
|
{
|
|
dwKeySize = 2048;
|
|
break;
|
|
}
|
|
rSACryptoServiceProvider.KeySize = keySizes.MaxSize;
|
|
}
|
|
}
|
|
mLogger.WriteLine("Sending console final key.");
|
|
mLogger.Flush();
|
|
rSACryptoServiceProvider = new RSACryptoServiceProvider(dwKeySize);
|
|
binaryWriter.Write(rSACryptoServiceProvider.ToXmlString(includePrivateParameters: false));
|
|
mLogger.WriteLine("Receiving console key.");
|
|
mLogger.Flush();
|
|
byte[] rgb = binaryReader.ReadBytes(binaryReader.ReadInt32());
|
|
byte[] result = rSACryptoServiceProvider.Decrypt(rgb, fOAEP: false);
|
|
mLogger.WriteLine("Console key received.");
|
|
mLogger.Flush();
|
|
return result;
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
tcpClient.Close();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
try
|
|
{
|
|
tcpListener.Stop();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static byte[] GetInterface(IPAddress identifyingAddress, out string adapterName)
|
|
{
|
|
NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
|
foreach (NetworkInterface networkInterface in allNetworkInterfaces)
|
|
{
|
|
foreach (UnicastIPAddressInformation unicastAddress in networkInterface.GetIPProperties().UnicastAddresses)
|
|
{
|
|
if (unicastAddress.Address == identifyingAddress)
|
|
{
|
|
adapterName = networkInterface.Name;
|
|
return networkInterface.GetPhysicalAddress().GetAddressBytes();
|
|
}
|
|
}
|
|
}
|
|
NetworkInterface[] allNetworkInterfaces2 = NetworkInterface.GetAllNetworkInterfaces();
|
|
foreach (NetworkInterface networkInterface2 in allNetworkInterfaces2)
|
|
{
|
|
if (networkInterface2.OperationalStatus == OperationalStatus.Up && networkInterface2.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
|
{
|
|
adapterName = networkInterface2.Name;
|
|
return networkInterface2.GetPhysicalAddress().GetAddressBytes();
|
|
}
|
|
}
|
|
throw new Exception("Could not find a valid network adapter.");
|
|
}
|
|
|
|
private static string GetInterface(byte[] identifyingMacAddress)
|
|
{
|
|
NetworkInterface[] allNetworkInterfaces = NetworkInterface.GetAllNetworkInterfaces();
|
|
int num = 0;
|
|
if (num < allNetworkInterfaces.Length)
|
|
{
|
|
NetworkInterface networkInterface = allNetworkInterfaces[num];
|
|
byte[] addressBytes = networkInterface.GetPhysicalAddress().GetAddressBytes();
|
|
if (addressBytes.Length == identifyingMacAddress.Length)
|
|
{
|
|
for (int i = 0; i < addressBytes.Length; i++)
|
|
{
|
|
_ = addressBytes[i];
|
|
_ = identifyingMacAddress[i];
|
|
}
|
|
}
|
|
return networkInterface.Name;
|
|
}
|
|
NetworkInterface[] allNetworkInterfaces2 = NetworkInterface.GetAllNetworkInterfaces();
|
|
foreach (NetworkInterface networkInterface2 in allNetworkInterfaces2)
|
|
{
|
|
if (networkInterface2.OperationalStatus == OperationalStatus.Up && networkInterface2.NetworkInterfaceType != NetworkInterfaceType.Loopback)
|
|
{
|
|
return networkInterface2.Name;
|
|
}
|
|
}
|
|
throw new Exception("Could not find a valid network adapter.");
|
|
}
|
|
|
|
public static void SetNetworkProperties(byte[] macAddress, IPAddress address, IPAddress mask, IPAddress gateway, IPAddress dns, string hostName)
|
|
{
|
|
SetNetworkProperties(GetInterface(macAddress), address, mask, gateway, dns, hostName);
|
|
}
|
|
|
|
public static void SetNetworkProperties(string adapterName, IPAddress address, IPAddress mask, IPAddress gateway, IPAddress dns, string hostName)
|
|
{
|
|
if (adapterName == null)
|
|
{
|
|
throw new ArgumentNullException("adapterName");
|
|
}
|
|
if (adapterName.Length == 0)
|
|
{
|
|
throw new ArgumentOutOfRangeException("adapterName", "Adapter name can not be empty.");
|
|
}
|
|
if (address.AddressFamily != AddressFamily.InterNetwork)
|
|
{
|
|
throw new ArgumentOutOfRangeException("address", "Address must be an IPv4 address.");
|
|
}
|
|
if (mask.AddressFamily != AddressFamily.InterNetwork)
|
|
{
|
|
throw new ArgumentOutOfRangeException("mask", "Mask must be an IPv4 address.");
|
|
}
|
|
if (gateway.AddressFamily != AddressFamily.InterNetwork)
|
|
{
|
|
throw new ArgumentOutOfRangeException("gateway", "Default gateway must be an IPv4 address.");
|
|
}
|
|
if (dns.AddressFamily != AddressFamily.InterNetwork)
|
|
{
|
|
throw new ArgumentOutOfRangeException("dns", "DNS server address must be an IPv4 address.");
|
|
}
|
|
mLogger.WriteLine(string.Format("Setting data for adapter \"{0}\": IP {1}, Mask {2}, Gateway {3}, DNS {4}, Host {5}.", adapterName, address.ToString(), mask.ToString(), gateway.Equals(IPAddress.Any) ? "none" : gateway.ToString(), dns.Equals(IPAddress.Any) ? "none" : dns.ToString(), string.IsNullOrEmpty(hostName) ? "no change" : hostName));
|
|
mLogger.Flush();
|
|
using (Process process = new Process())
|
|
{
|
|
process.StartInfo.FileName = "netsh";
|
|
process.StartInfo.Arguments = string.Format("interface ip set address \"{0}\" static {1} {2} {3}", adapterName, address, mask, gateway.Equals(IPAddress.Any) ? "none" : (gateway.ToString() + " 1"));
|
|
process.Start();
|
|
process.WaitForExit();
|
|
if (process.ExitCode != 0)
|
|
{
|
|
throw new Exception("Could not initalize IP address.");
|
|
}
|
|
}
|
|
mLogger.WriteLine("IP Set");
|
|
mLogger.Flush();
|
|
using (Process process2 = new Process())
|
|
{
|
|
process2.StartInfo.FileName = "netsh";
|
|
process2.StartInfo.Arguments = string.Format("interface ip set dns \"{0}\" static {1}", adapterName, dns.Equals(IPAddress.Any) ? "none" : dns.ToString());
|
|
process2.Start();
|
|
process2.WaitForExit();
|
|
if (process2.ExitCode != 0)
|
|
{
|
|
throw new Exception("Could not set DNS server.");
|
|
}
|
|
}
|
|
mLogger.WriteLine("DNS set.");
|
|
mLogger.Flush();
|
|
if (string.IsNullOrEmpty(hostName))
|
|
{
|
|
return;
|
|
}
|
|
using (RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\Control\\ComputerName\\ComputerName", writable: true))
|
|
{
|
|
try
|
|
{
|
|
registryKey.SetValue("ComputerName", hostName, RegistryValueKind.String);
|
|
}
|
|
catch (Exception innerException)
|
|
{
|
|
throw new Exception("Could not set host name.", innerException);
|
|
}
|
|
finally
|
|
{
|
|
registryKey.Close();
|
|
}
|
|
}
|
|
using (RegistryKey registryKey2 = Registry.LocalMachine.OpenSubKey("SYSTEM\\CurrentControlSet\\services\\Tcpip\\Parameters", writable: true))
|
|
{
|
|
try
|
|
{
|
|
registryKey2.SetValue("NV Hostname", hostName, RegistryValueKind.String);
|
|
}
|
|
catch (Exception innerException2)
|
|
{
|
|
throw new Exception("Could not set host name.", innerException2);
|
|
}
|
|
finally
|
|
{
|
|
registryKey2.Close();
|
|
}
|
|
}
|
|
mLogger.WriteLine("Host set.");
|
|
mLogger.Flush();
|
|
}
|
|
|
|
private static void ConfigureTempIp(string adapterName)
|
|
{
|
|
byte[] array = new byte[4];
|
|
sRandom.NextBytes(array);
|
|
array[0] = 172;
|
|
array[1] = (byte)((array[1] & 0xFu) | 0x10u);
|
|
IPAddress address = new IPAddress(array);
|
|
IPAddress mask = IPAddress.Parse("255.240.0.0");
|
|
SetNetworkProperties(adapterName, address, mask, IPAddress.Any, IPAddress.Any, null);
|
|
}
|
|
|
|
private static BasicConfigResponse ReceiveUdpResponse(ushort localPort, byte[] weakAesKey)
|
|
{
|
|
UdpBeaconListener udpBeaconListener = new UdpBeaconListener(PodConfigurationServer.sMessageReplyID, localPort, weakAesKey);
|
|
BasicConfigResponse basicConfigResponse;
|
|
do
|
|
{
|
|
basicConfigResponse = new BasicConfigResponse(udpBeaconListener.Receive());
|
|
}
|
|
while (basicConfigResponse == null);
|
|
return basicConfigResponse;
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
Dispose(disposing: true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing && mPasscodeDisplay != null && mPasscodeDisplay.Responding)
|
|
{
|
|
mPasscodeDisplay.Kill();
|
|
}
|
|
}
|
|
}
|
|
public delegate void ConfigurationRequestReceivedDelegate(byte[] macAddress, string reqeustId);
|
|
public class PodConfigurationServer
|
|
{
|
|
internal static readonly byte[] sMessageRequestID = Encoding.UTF8.GetBytes("RQST");
|
|
|
|
internal static readonly byte[] sMessageReplyID = Encoding.UTF8.GetBytes("RPLY");
|
|
|
|
private static readonly byte[] sMessageConfID = Encoding.UTF8.GetBytes("CONF");
|
|
|
|
private static readonly byte[] mPassphraseSalt = new byte[32]
|
|
{
|
|
23, 171, 81, 217, 236, 209, 212, 116, 169, 9,
|
|
74, 52, 39, 251, 31, 242, 222, 196, 249, 241,
|
|
166, 216, 158, 218, 21, 17, 71, 101, 50, 231,
|
|
231, 239
|
|
};
|
|
|
|
private readonly ushort mLocalPort;
|
|
|
|
private readonly ushort mRemotePort;
|
|
|
|
private readonly ConfigurationRequestReceivedDelegate mRequestDelegate;
|
|
|
|
private UdpBeaconListener mBeaconListener;
|
|
|
|
private List<long> mActiveConfigPods = new List<long>();
|
|
|
|
internal static byte[] GenerateKeyFromPassphrase(string passphrase)
|
|
{
|
|
return new Rfc2898DeriveBytes(passphrase, mPassphraseSalt, 1000).GetBytes(32);
|
|
}
|
|
|
|
public static bool NegotiateCryptoStreams(NetworkStream clientStream, byte[] key, out Stream outStream, out Stream inStream)
|
|
{
|
|
outStream = null;
|
|
inStream = null;
|
|
Rijndael rijndael = Rijndael.Create();
|
|
rijndael.Mode = CipherMode.ECB;
|
|
rijndael.Key = key;
|
|
rijndael.GenerateIV();
|
|
clientStream.Write(rijndael.IV, 0, rijndael.IV.Length);
|
|
inStream = new OFBCryptoStream(clientStream, rijndael.CreateEncryptor(), CryptoStreamMode.Read, rijndael.IV);
|
|
byte[] array = new byte[rijndael.IV.Length];
|
|
for (int i = 0; i < array.Length; i += clientStream.Read(array, i, array.Length - i))
|
|
{
|
|
}
|
|
rijndael.IV = array;
|
|
outStream = new OFBCryptoStream(clientStream, rijndael.CreateEncryptor(), CryptoStreamMode.Write, array);
|
|
outStream.Write(sMessageConfID, 0, sMessageConfID.Length);
|
|
byte[] array2 = new byte[sMessageConfID.Length];
|
|
for (int j = 0; j < array2.Length; j += inStream.Read(array2, j, array2.Length - j))
|
|
{
|
|
}
|
|
for (int k = 0; k < array2.Length; k++)
|
|
{
|
|
if (array2[k] != sMessageConfID[k])
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
public PodConfigurationServer(ushort localPort, ushort remotePort, ConfigurationRequestReceivedDelegate requestDelegate)
|
|
{
|
|
if (requestDelegate == null)
|
|
{
|
|
throw new ArgumentNullException("requestDelegate");
|
|
}
|
|
mLocalPort = localPort;
|
|
mRemotePort = remotePort;
|
|
mRequestDelegate = requestDelegate;
|
|
mBeaconListener = new UdpBeaconListener(sMessageRequestID, mLocalPort, null);
|
|
mBeaconListener.BeginReceive(BeaconReceived, null);
|
|
}
|
|
|
|
private void BeaconReceived(IAsyncResult ar)
|
|
{
|
|
byte[] array;
|
|
try
|
|
{
|
|
array = mBeaconListener.EndReceive(ar);
|
|
}
|
|
catch
|
|
{
|
|
mBeaconListener = new UdpBeaconListener(sMessageRequestID, mLocalPort, null);
|
|
mBeaconListener.BeginReceive(BeaconReceived, null);
|
|
return;
|
|
}
|
|
mBeaconListener.BeginReceive(BeaconReceived, null);
|
|
byte[] array2 = new byte[6];
|
|
byte[] array3 = new byte[array.Length - array2.Length];
|
|
Buffer.BlockCopy(array, 0, array2, 0, array2.Length);
|
|
Buffer.BlockCopy(array, array2.Length, array3, 0, array3.Length);
|
|
byte[] array4 = new byte[8];
|
|
Array.Copy(array2, array4, array2.Length);
|
|
long item = BitConverter.ToInt64(array4, 0);
|
|
lock (mActiveConfigPods)
|
|
{
|
|
if (mActiveConfigPods.Contains(item))
|
|
{
|
|
return;
|
|
}
|
|
mActiveConfigPods.Add(item);
|
|
}
|
|
try
|
|
{
|
|
mRequestDelegate(array2, Encoding.ASCII.GetString(array3));
|
|
}
|
|
finally
|
|
{
|
|
lock (mActiveConfigPods)
|
|
{
|
|
mActiveConfigPods.Remove(item);
|
|
}
|
|
}
|
|
}
|
|
|
|
public byte[] SendEncryptionKey(string passphrase, IPAddress ipAddress, IPAddress mask, IPAddress gateway, IPAddress dns, string hostName, TimeSpan timeout)
|
|
{
|
|
DateTime dateTime = DateTime.Now + timeout;
|
|
byte[] array = GenerateKeyFromPassphrase(passphrase);
|
|
UdpBeacon udpBeacon = new UdpBeacon(sMessageReplyID, new BasicConfigResponse(ipAddress, mask, gateway, dns, hostName).ToBytes(), 10000.0, mRemotePort, array);
|
|
udpBeacon.Start();
|
|
try
|
|
{
|
|
do
|
|
{
|
|
TcpClient tcpClient = null;
|
|
try
|
|
{
|
|
tcpClient = new TcpClient(AddressFamily.InterNetwork);
|
|
tcpClient.Connect(new IPEndPoint(ipAddress, mRemotePort));
|
|
if (NegotiateCryptoStreams(tcpClient.GetStream(), array, out var outStream, out var inStream))
|
|
{
|
|
BinaryWriter binaryWriter = new BinaryWriter(outStream);
|
|
BinaryReader binaryReader = new BinaryReader(inStream);
|
|
RSACryptoServiceProvider rSACryptoServiceProvider = new RSACryptoServiceProvider();
|
|
rSACryptoServiceProvider.FromXmlString(binaryReader.ReadString());
|
|
Rijndael rijndael = Rijndael.Create();
|
|
rijndael.KeySize = 256;
|
|
rijndael.GenerateKey();
|
|
byte[] key = rijndael.Key;
|
|
byte[] array2 = rSACryptoServiceProvider.Encrypt(key, fOAEP: false);
|
|
binaryWriter.Write(array2.Length);
|
|
binaryWriter.Write(array2);
|
|
return key;
|
|
}
|
|
}
|
|
catch (SocketException)
|
|
{
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
tcpClient.Close();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
while (DateTime.Now < dateTime);
|
|
throw new TimeoutException();
|
|
}
|
|
finally
|
|
{
|
|
udpBeacon.Stop();
|
|
}
|
|
}
|
|
}
|
|
public class UdpBeaconListener
|
|
{
|
|
private class InternalAsyncResult : IAsyncResult
|
|
{
|
|
public AsyncCallback UserCallback;
|
|
|
|
public IAsyncResult UdpAsyncResult;
|
|
|
|
public Exception ThrownException;
|
|
|
|
public byte[] ReceivedMessage;
|
|
|
|
private object mState;
|
|
|
|
public EventWaitHandle WaitHandle = new EventWaitHandle(initialState: false, EventResetMode.ManualReset);
|
|
|
|
private bool mIsCompleted;
|
|
|
|
public object AsyncState => mState;
|
|
|
|
public WaitHandle AsyncWaitHandle => WaitHandle;
|
|
|
|
public bool CompletedSynchronously => false;
|
|
|
|
public bool IsCompleted
|
|
{
|
|
get
|
|
{
|
|
return mIsCompleted;
|
|
}
|
|
set
|
|
{
|
|
mIsCompleted = value;
|
|
}
|
|
}
|
|
|
|
public InternalAsyncResult(AsyncCallback userCallback, object userState)
|
|
{
|
|
UserCallback = userCallback;
|
|
mState = userState;
|
|
}
|
|
}
|
|
|
|
private readonly byte[] mHeader;
|
|
|
|
private readonly Rijndael mAes;
|
|
|
|
private readonly SHA1 mSha1;
|
|
|
|
private UdpClient mClient;
|
|
|
|
public UdpBeaconListener(byte[] header, ushort port, byte[] encryptionKey)
|
|
{
|
|
mHeader = (byte[])header.Clone();
|
|
mClient = new UdpClient(port);
|
|
mClient.EnableBroadcast = true;
|
|
if (encryptionKey != null)
|
|
{
|
|
mAes = UdpBeacon.CreateRijndael(encryptionKey);
|
|
mSha1 = SHA1.Create();
|
|
}
|
|
}
|
|
|
|
public IAsyncResult BeginReceive(AsyncCallback requestCallback, object state)
|
|
{
|
|
InternalAsyncResult internalAsyncResult = new InternalAsyncResult(requestCallback, state);
|
|
internalAsyncResult.UdpAsyncResult = mClient.BeginReceive(InternalEndReceive, internalAsyncResult);
|
|
return internalAsyncResult;
|
|
}
|
|
|
|
public byte[] EndReceive(IAsyncResult ar)
|
|
{
|
|
InternalAsyncResult internalAsyncResult = (InternalAsyncResult)ar;
|
|
internalAsyncResult.AsyncWaitHandle.WaitOne();
|
|
if (internalAsyncResult.ThrownException != null)
|
|
{
|
|
throw internalAsyncResult.ThrownException;
|
|
}
|
|
return internalAsyncResult.ReceivedMessage;
|
|
}
|
|
|
|
public byte[] Receive()
|
|
{
|
|
return EndReceive(BeginReceive(null, null));
|
|
}
|
|
|
|
private void InternalEndReceive(IAsyncResult udpAr)
|
|
{
|
|
InternalAsyncResult internalAsyncResult = (InternalAsyncResult)udpAr.AsyncState;
|
|
ReceiveAndTransform(internalAsyncResult);
|
|
if (internalAsyncResult.ThrownException != null || internalAsyncResult.ReceivedMessage != null)
|
|
{
|
|
internalAsyncResult.IsCompleted = true;
|
|
internalAsyncResult.WaitHandle.Set();
|
|
if (internalAsyncResult.UserCallback != null)
|
|
{
|
|
internalAsyncResult.UserCallback(internalAsyncResult);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
internalAsyncResult.UdpAsyncResult = mClient.BeginReceive(InternalEndReceive, internalAsyncResult);
|
|
}
|
|
}
|
|
|
|
private void ReceiveAndTransform(InternalAsyncResult ar)
|
|
{
|
|
IPEndPoint remoteEP = new IPEndPoint(0L, 0);
|
|
byte[] array;
|
|
try
|
|
{
|
|
array = mClient.EndReceive(ar.UdpAsyncResult, ref remoteEP);
|
|
}
|
|
catch (Exception thrownException)
|
|
{
|
|
Exception ex = (ar.ThrownException = thrownException);
|
|
return;
|
|
}
|
|
finally
|
|
{
|
|
ar.UdpAsyncResult = null;
|
|
}
|
|
try
|
|
{
|
|
if (array.Length < mHeader.Length)
|
|
{
|
|
return;
|
|
}
|
|
for (int i = 0; i < mHeader.Length; i++)
|
|
{
|
|
if (array[i] != mHeader[i])
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if (mAes == null)
|
|
{
|
|
ar.ReceivedMessage = new byte[array.Length - mHeader.Length];
|
|
Buffer.BlockCopy(array, mHeader.Length, ar.ReceivedMessage, 0, ar.ReceivedMessage.Length);
|
|
return;
|
|
}
|
|
byte[] array2 = new byte[mAes.IV.Length];
|
|
if (array.Length < mHeader.Length + array2.Length)
|
|
{
|
|
return;
|
|
}
|
|
Buffer.BlockCopy(array, mHeader.Length, array2, 0, array2.Length);
|
|
ICryptoTransform transform = mAes.CreateDecryptor(mAes.Key, array2);
|
|
MemoryStream memoryStream = new MemoryStream();
|
|
CryptoStream cryptoStream = new CryptoStream(memoryStream, transform, CryptoStreamMode.Write);
|
|
cryptoStream.Write(array, mHeader.Length + array2.Length, array.Length - (mHeader.Length + array2.Length));
|
|
cryptoStream.FlushFinalBlock();
|
|
byte[] array3 = memoryStream.ToArray();
|
|
int num = array3.Length - mSha1.HashSize / 8;
|
|
if (num <= 0)
|
|
{
|
|
return;
|
|
}
|
|
byte[] array4 = mSha1.ComputeHash(array3, 0, num);
|
|
for (int j = 0; j < array4.Length; j++)
|
|
{
|
|
if (array3[j + num] != array4[j])
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
ar.ReceivedMessage = new byte[num];
|
|
Buffer.BlockCopy(array3, 0, ar.ReceivedMessage, 0, num);
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
}
|
|
}
|
|
public class UdpBeacon
|
|
{
|
|
private readonly byte[] mHeader;
|
|
|
|
private readonly byte[] mMessage;
|
|
|
|
private readonly double mMilliseconds;
|
|
|
|
private readonly IPEndPoint mBroadcast;
|
|
|
|
private readonly object mSynchronizer;
|
|
|
|
private readonly Rijndael mAes;
|
|
|
|
private readonly SHA1 mSha1;
|
|
|
|
private UdpClient mClient;
|
|
|
|
private System.Timers.Timer mTimer;
|
|
|
|
internal static Rijndael CreateRijndael(byte[] encryptionKey)
|
|
{
|
|
Rijndael rijndael = Rijndael.Create();
|
|
rijndael.Key = (byte[])encryptionKey.Clone();
|
|
return rijndael;
|
|
}
|
|
|
|
public UdpBeacon(byte[] header, byte[] message, double milliseconds, ushort port, byte[] encryptionKey)
|
|
{
|
|
mHeader = (byte[])header.Clone();
|
|
mMessage = (byte[])message.Clone();
|
|
if (encryptionKey != null)
|
|
{
|
|
mAes = CreateRijndael(encryptionKey);
|
|
mSha1 = SHA1.Create();
|
|
}
|
|
mMilliseconds = milliseconds;
|
|
mBroadcast = new IPEndPoint(IPAddress.Broadcast, port);
|
|
mSynchronizer = new object();
|
|
}
|
|
|
|
public void Start()
|
|
{
|
|
lock (mSynchronizer)
|
|
{
|
|
Initalize();
|
|
BroadcastBeacon(allowReset: false);
|
|
mTimer.Start();
|
|
}
|
|
}
|
|
|
|
public void Stop()
|
|
{
|
|
lock (mSynchronizer)
|
|
{
|
|
if (mTimer != null)
|
|
{
|
|
mTimer.Stop();
|
|
}
|
|
mTimer = null;
|
|
if (mClient != null)
|
|
{
|
|
mClient.Close();
|
|
}
|
|
mClient = null;
|
|
}
|
|
}
|
|
|
|
private void Initalize()
|
|
{
|
|
try
|
|
{
|
|
Stop();
|
|
}
|
|
catch
|
|
{
|
|
}
|
|
mClient = new UdpClient();
|
|
mClient.EnableBroadcast = true;
|
|
mTimer = new System.Timers.Timer(mMilliseconds);
|
|
mTimer.Elapsed += mTimer_Elapsed;
|
|
mTimer.AutoReset = true;
|
|
}
|
|
|
|
private void mTimer_Elapsed(object sender, ElapsedEventArgs e)
|
|
{
|
|
lock (mSynchronizer)
|
|
{
|
|
BroadcastBeacon(allowReset: true);
|
|
}
|
|
}
|
|
|
|
private void BroadcastBeacon(bool allowReset)
|
|
{
|
|
try
|
|
{
|
|
List<byte> list = new List<byte>(mHeader);
|
|
if (mAes == null)
|
|
{
|
|
list.AddRange(mMessage);
|
|
}
|
|
else
|
|
{
|
|
mAes.GenerateIV();
|
|
list.AddRange(mAes.IV);
|
|
byte[] array = mSha1.ComputeHash(mMessage);
|
|
byte[] array2 = new byte[mMessage.Length + array.Length];
|
|
mMessage.CopyTo(array2, 0);
|
|
array.CopyTo(array2, mMessage.Length);
|
|
MemoryStream memoryStream = new MemoryStream();
|
|
CryptoStream cryptoStream = new CryptoStream(memoryStream, mAes.CreateEncryptor(), CryptoStreamMode.Write);
|
|
cryptoStream.Write(array2, 0, array2.Length);
|
|
cryptoStream.FlushFinalBlock();
|
|
list.AddRange(memoryStream.ToArray());
|
|
}
|
|
byte[] array3 = list.ToArray();
|
|
mClient.Send(array3, array3.Length, mBroadcast);
|
|
}
|
|
catch
|
|
{
|
|
if (allowReset)
|
|
{
|
|
Stop();
|
|
Initalize();
|
|
Start();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
namespace System.Security.Cryptography
|
|
{
|
|
public class OFBCryptoStream : Stream
|
|
{
|
|
private Stream mStream;
|
|
|
|
private ICryptoTransform mTransform;
|
|
|
|
private CryptoStreamMode mStreamMode;
|
|
|
|
private byte[] mOutputBuffer;
|
|
|
|
private byte[] mOutputBufferAlternate;
|
|
|
|
private int mOutputBufferPosition;
|
|
|
|
public override bool CanRead => mStreamMode == CryptoStreamMode.Read;
|
|
|
|
public override bool CanSeek => false;
|
|
|
|
public override bool CanWrite => mStreamMode == CryptoStreamMode.Write;
|
|
|
|
public override long Length
|
|
{
|
|
get
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
public override long Position
|
|
{
|
|
get
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
set
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
}
|
|
|
|
public OFBCryptoStream(Stream stream, ICryptoTransform transform, CryptoStreamMode mode, byte[] IV)
|
|
{
|
|
if (transform.InputBlockSize != transform.OutputBlockSize)
|
|
{
|
|
throw new ArgumentException("transform");
|
|
}
|
|
if (!transform.CanTransformMultipleBlocks)
|
|
{
|
|
throw new ArgumentException("transform");
|
|
}
|
|
if (mode != 0 && mode != CryptoStreamMode.Write)
|
|
{
|
|
throw new ArgumentOutOfRangeException("mode");
|
|
}
|
|
if ((mode == CryptoStreamMode.Read && !stream.CanRead) || (mode != CryptoStreamMode.Write && !stream.CanWrite))
|
|
{
|
|
throw new ArgumentException("mode");
|
|
}
|
|
mStream = stream;
|
|
mTransform = transform;
|
|
mStreamMode = mode;
|
|
mOutputBuffer = IV;
|
|
mOutputBufferAlternate = new byte[mTransform.OutputBlockSize];
|
|
NextBuffer();
|
|
}
|
|
|
|
private void NextBuffer()
|
|
{
|
|
mTransform.TransformBlock(mOutputBuffer, 0, mOutputBuffer.Length, mOutputBufferAlternate, 0);
|
|
byte[] array = mOutputBuffer;
|
|
mOutputBuffer = mOutputBufferAlternate;
|
|
mOutputBufferAlternate = array;
|
|
mOutputBufferPosition = 0;
|
|
}
|
|
|
|
public override void Flush()
|
|
{
|
|
mStream.Flush();
|
|
}
|
|
|
|
public override long Seek(long offset, SeekOrigin origin)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
public override void SetLength(long value)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
|
|
public override int Read(byte[] buffer, int offset, int count)
|
|
{
|
|
if (!CanRead)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
byte[] array = new byte[count];
|
|
int num = mStream.Read(array, 0, count);
|
|
int num2 = 0;
|
|
while (num2 < num)
|
|
{
|
|
if (mOutputBufferPosition >= mOutputBuffer.Length)
|
|
{
|
|
NextBuffer();
|
|
}
|
|
while (mOutputBufferPosition < mOutputBuffer.Length && num2 < num)
|
|
{
|
|
buffer[offset++] = (byte)(mOutputBuffer[mOutputBufferPosition++] ^ array[num2++]);
|
|
}
|
|
}
|
|
return num;
|
|
}
|
|
|
|
public override void Write(byte[] buffer, int offset, int count)
|
|
{
|
|
if (!CanWrite)
|
|
{
|
|
throw new NotSupportedException();
|
|
}
|
|
byte[] array = new byte[count];
|
|
int num = 0;
|
|
while (num < count)
|
|
{
|
|
if (mOutputBufferPosition >= mOutputBuffer.Length)
|
|
{
|
|
NextBuffer();
|
|
}
|
|
while (mOutputBufferPosition < mOutputBuffer.Length && num < count)
|
|
{
|
|
array[num++] = (byte)(mOutputBuffer[mOutputBufferPosition++] ^ buffer[offset++]);
|
|
}
|
|
}
|
|
mStream.Write(array, 0, count);
|
|
}
|
|
}
|
|
}
|
|
namespace Tesla
|
|
{
|
|
public class BasicConfigResponse
|
|
{
|
|
private IPAddress mAddress;
|
|
|
|
private IPAddress mMask;
|
|
|
|
private IPAddress mGateway;
|
|
|
|
private IPAddress mDns;
|
|
|
|
private string mHostName;
|
|
|
|
public IPAddress Address => mAddress;
|
|
|
|
public IPAddress Mask => mMask;
|
|
|
|
public IPAddress Gateway => mGateway;
|
|
|
|
public IPAddress Dns => mDns;
|
|
|
|
public string HostName => mHostName;
|
|
|
|
public BasicConfigResponse(IPAddress address, IPAddress mask, IPAddress gateway, IPAddress dns, string hostName)
|
|
{
|
|
if (address.AddressFamily != AddressFamily.InterNetwork)
|
|
{
|
|
throw new ArgumentOutOfRangeException("address", "Address must be an IPv4 address.");
|
|
}
|
|
if (mask.AddressFamily != AddressFamily.InterNetwork)
|
|
{
|
|
throw new ArgumentOutOfRangeException("mask", "Address mask must be an IPv4 address.");
|
|
}
|
|
if (gateway.AddressFamily != AddressFamily.InterNetwork)
|
|
{
|
|
throw new ArgumentOutOfRangeException("gateway", "Default gateway must be an IPv4 address.");
|
|
}
|
|
if (dns.AddressFamily != AddressFamily.InterNetwork)
|
|
{
|
|
throw new ArgumentOutOfRangeException("dns", "DNS server must be an IPv4 address.");
|
|
}
|
|
if (hostName == null)
|
|
{
|
|
throw new ArgumentNullException("hostName");
|
|
}
|
|
mAddress = address;
|
|
mMask = mask;
|
|
mGateway = gateway;
|
|
mDns = dns;
|
|
mHostName = hostName;
|
|
}
|
|
|
|
public byte[] ToBytes()
|
|
{
|
|
List<byte> list = new List<byte>(8);
|
|
list.AddRange(mAddress.GetAddressBytes());
|
|
list.AddRange(mMask.GetAddressBytes());
|
|
list.AddRange(mGateway.GetAddressBytes());
|
|
list.AddRange(mDns.GetAddressBytes());
|
|
list.AddRange(Encoding.UTF8.GetBytes(mHostName));
|
|
return list.ToArray();
|
|
}
|
|
|
|
public BasicConfigResponse(byte[] data)
|
|
{
|
|
byte[] array = new byte[4];
|
|
Buffer.BlockCopy(data, 0, array, 0, array.Length);
|
|
mAddress = new IPAddress(array);
|
|
Buffer.BlockCopy(data, 4, array, 0, array.Length);
|
|
mMask = new IPAddress(array);
|
|
Buffer.BlockCopy(data, 8, array, 0, array.Length);
|
|
mGateway = new IPAddress(array);
|
|
Buffer.BlockCopy(data, 12, array, 0, array.Length);
|
|
mDns = new IPAddress(array);
|
|
mHostName = Encoding.UTF8.GetString(data, 16, data.Length - 16);
|
|
}
|
|
}
|
|
}
|