Files
TeslaSuite/SecureConfig/SecureConfig.cs
T
CydandClaude Opus 4.8 b9d8027cf6 Extract shared contract, drop BinaryFormatter wire, modernize to net8/x64
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>
2026-06-30 08:15:17 -05:00

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);
}
}
}