Files
Joseph Doherty fe2a6db786
rust / build / test / clippy / fmt (push) Has been cancelled
Initial project state: .NET reference, design, Rust port (M0+M1), evidence
Layout:
- src/                    .NET 10 x64 reference: MxNativeCodec, MxNativeClient,
                          MxAsbClient, probes, tests, harnesses. Executable spec.
- design/                 Architectural plan for the Rust port (M0–M6), error
                          model, protocol invariants, risks (R1–R16), adversarial
                          review log (review.md).
- rust/                   Rust workspace. M0 skeleton + M1 codec parity.
                          mxaccess-codec: 215 unit tests + 2 cross-implementation
                          parity tests (byte-identical against .NET reference).
                          Other crates are M0 stubs awaiting M2+.
- captures/               Frida + netsh + pcap evidence per CLAUDE.md
                          ("captures are evidence, not throwaway logs").
- analysis/               Decompiled C# (frida/proxy/decompiled-*),
                          Ghidra exports for native DLLs (`exports/` only —
                          working state at `projects/` and AVEVA's input
                          binaries at `input/` are gitignored).
- docs/                   Reverse-engineering reference docs.
- tools/                  Setup-LiveProbeEnv.ps1 (Infisical credential fetcher),
                          Compute-Crc.ps1 (.NET parity helper).
- .github/workflows/      Rust CI: fmt + build + test + clippy on Windows.
- LICENSE                 MIT (Joseph Doherty, 2026).

Verified:
- cargo test --workspace → 217 passed (215 unit + 2 .NET parity), 0 failed
- cargo clippy --workspace -- -D warnings → clean
- cargo fmt --all -- --check → clean
- cargo publish --dry-run -p mxaccess-codec → packages cleanly

Excluded from history (see .gitignore):
- **/bin, **/obj, **/target — build artifacts
- analysis/ghidra/projects/ — Ghidra working state (regenerable)
- analysis/ghidra/input/ — AVEVA proprietary DLLs (vendor IP)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-05 06:21:00 -04:00

493 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Threading;
using ArchestrAServices.Common;
using Asb.Base.V2.ContractTypes;
namespace Asb.Base.V2;
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]
public abstract class BaseV2Shim : IAuthenticateAsb
{
private sealed class ConnectionContext : IDisposable
{
private readonly TimeSpan keepAliveTimeout;
private readonly Timer timer;
private bool disposed;
public IContextChannel ConnectionChannel { get; private set; }
public IBaseV2 ConnectionImplementation { get; }
public ConnectionContext(IBaseV2 implementation, TimerCallback keepAliveTimeoutCallback, Guid connectionId, TimeSpan keepAliveTimeout)
{
ConnectionChannel = ((OperationContext.Current == null) ? null : OperationContext.Current.Channel);
ConnectionImplementation = implementation;
timer = new Timer(keepAliveTimeoutCallback, connectionId, (int)keepAliveTimeout.TotalMilliseconds, -1);
this.keepAliveTimeout = keepAliveTimeout;
}
~ConnectionContext()
{
Dispose(disposing: false);
}
public void RestartTimer()
{
timer.Change((int)keepAliveTimeout.TotalMilliseconds, -1);
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (disposed)
{
return;
}
disposed = true;
if (disposing)
{
if (ConnectionChannel != null)
{
ConnectionChannel.Close();
ConnectionChannel = null;
}
if (timer != null)
{
timer.Dispose();
}
}
}
}
private static readonly ReaderWriterLockSlim implementationLock = new ReaderWriterLockSlim();
private static readonly Dictionary<Guid, ConnectionContext> implementations = new Dictionary<Guid, ConnectionContext>();
private static readonly TimeSpan defaultKeepAliveTimeout = TimeSpan.FromSeconds(30.0);
public virtual ConnectionState DownstreamConnectionState { get; set; }
protected virtual TimeSpan KeepAliveTimeout => defaultKeepAliveTimeout;
public ConnectResponse Connect(ConnectRequest request)
{
if (request == null)
{
return null;
}
ServiceTrace.LogCsv("Connect entry [ConId]", request.ConnectionId);
ServiceTrace.LogResume("Connect entry, connection Id {0} ", request.ConnectionId);
ConnectResponse connectResponse = SystemAuthenticationServiceAuthentication.ProcessClientConnection(request);
ServiceTrace.LogVerbose("BaseV2Shim.Connect started new SysAuthenticator for connection Id {0}", connectResponse.ConnectionValidator.ConnectionId);
ServiceTrace.LogSuspend("Connect exit, connection Id {0}, success = {1}", request.ConnectionId, connectResponse.Result.Success);
ServiceTrace.LogCsv("Connect exit [ConId Success]", request.ConnectionId, connectResponse.Result.Success);
return connectResponse;
}
public AuthenticateMeResponse AuthenticateMe(AuthenticateMeRequest request)
{
if (request == null)
{
return null;
}
ServiceTrace.LogCsv("AuthenticateMe entry [ConnId]", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogResume("AuthenticateMe entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
ArchestrAResult archestrAResult = ArchestrAResult.MakeResult(ArchestrAError.OperationFailed, 0);
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId);
if (serviceAuthenticator != null)
{
if (serviceAuthenticator.ProcessClientAuthenticateMe(request))
{
ServiceTrace.LogVerbose("AuthenticateMe found authenticator and validated request message, establishing serivce implementation for connect Id {0})", serviceAuthenticator.ConnectionId);
ConsumerMetadata metadata = ConsumerMetadata.Create(request.ConsumerMetaData);
archestrAResult = CheckConsumerMetadata(metadata, serviceAuthenticator.ConnectionId);
if (archestrAResult.Success)
{
archestrAResult = CreateServiceImplmentation(serviceAuthenticator, metadata);
}
}
else
{
archestrAResult = ArchestrAResult.MakeResult(ArchestrAError.ApplicationAuthenticationError, 0);
string message = string.Format(CultureInfo.CurrentCulture, "AuthenticateMe unable to authenticate client for ConnectionId {0}", new object[1] { serviceAuthenticator.ConnectionId });
ServiceTrace.LogWarning(message);
archestrAResult.AddErrorMessage(message);
}
}
if (!archestrAResult.Success)
{
OnDisconnect(request.ConnectionValidator.ConnectionId);
}
ServiceTrace.LogSuspend("AuthenticateMe exit, connection Id {0}, success = {1}", request.ConnectionValidator.ConnectionId, archestrAResult.Success);
ServiceTrace.LogCsv("AuthenticateMe exit [ConId Success]", request.ConnectionValidator.ConnectionId, archestrAResult.Success);
return new AuthenticateMeResponse(archestrAResult, DownstreamConnectionState);
}
public KeepAliveResponse KeepAlive(KeepAliveRequest request)
{
if (request == null)
{
return null;
}
ServiceTrace.LogCsv("KeepAlive entry [ConnId]", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogResume("KeepAlive entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
ArchestrAResult result = ArchestrAResult.MakeResult(ArchestrAError.OperationFailed, 0);
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId);
if (serviceAuthenticator != null && serviceAuthenticator.ValidRequest(request, forceHmac: false))
{
implementationLock.EnterReadLock();
bool flag;
ConnectionContext value;
try
{
flag = implementations.TryGetValue(request.ConnectionValidator.ConnectionId, out value);
}
finally
{
implementationLock.ExitReadLock();
}
if (flag && value != null)
{
value.RestartTimer();
IBaseV2 connectionImplementation = value.ConnectionImplementation;
try
{
result = EnsureValidResult(connectionImplementation.KeepAlive());
}
catch (Exception ex)
{
ServiceTrace.LogWarning("KeepAlive caught exception from implementation for connection Id {0}: {1}", request.ConnectionValidator.ConnectionId, ex.Message);
if (ex.InnerException != null)
{
ServiceTrace.LogWarning(" {0}", ex.InnerException.Message);
}
}
}
}
ServiceTrace.LogResume("KeepAlive exit, connection Id {0}", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogResume("KeepAlive exit [ConId]", request.ConnectionValidator.ConnectionId);
return new KeepAliveResponse(result, DownstreamConnectionState);
}
public GetStatusItemsResponse GetStatusItems(GetStatusItemsRequest request)
{
if (request == null)
{
return null;
}
ServiceTrace.LogCsv("GetStatusItems entry [ConnId]", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogResume("GetStatusItems entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
GetStatusItemsResponse getStatusItemsResponse = new GetStatusItemsResponse(ArchestrAResult.MakeResult(ArchestrAError.InvalidConnectionId, 0), null, DownstreamConnectionState);
IBaseV2 implementation = GetImplementation<IBaseV2>(request);
if (implementation != null)
{
try
{
getStatusItemsResponse.Result = EnsureValidResult(implementation.GetStatusItems(out var items));
List<string> list = items.ToList();
list.Add("ServiceLoadTime");
getStatusItemsResponse.Items = list.ToArray();
}
catch (Exception ex)
{
ServiceTrace.LogWarning("GetStatusItems caught exception from implementation for ConnectionId {0}: {1}", request.ConnectionValidator.ConnectionId, ex.Message);
getStatusItemsResponse.Result.AddErrorMessage(ex.Message);
if (ex.InnerException != null)
{
ServiceTrace.LogWarning(" {0}", ex.InnerException.Message);
getStatusItemsResponse.Result.AddErrorMessage(ex.InnerException.Message);
}
getStatusItemsResponse.Result.ResultCodeAsError = ArchestrAError.OperationFailed;
}
}
ServiceTrace.LogSuspend("GetStatusItems exit, connection Id {0}, success = {1}", request.ConnectionValidator.ConnectionId, getStatusItemsResponse.Result.Success);
ServiceTrace.LogCsv("GetStatusItems exit [ConId Success]", request.ConnectionValidator.ConnectionId, getStatusItemsResponse.Result.Success);
return getStatusItemsResponse;
}
public GetStatusResponse GetStatus(GetStatusRequest request)
{
if (request == null)
{
return null;
}
ServiceTrace.LogCsv("GetStatus entry [ConnId]", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogResume("GetStatus entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
GetStatusResponse getStatusResponse = new GetStatusResponse(ArchestrAResult.MakeResult(ArchestrAError.InvalidConnectionId, 0), null, DownstreamConnectionState);
IBaseV2 implementation = GetImplementation<IBaseV2>(request);
if (implementation != null)
{
try
{
getStatusResponse.Result = EnsureValidResult(implementation.GetStatus(request.ItemsToReturn, out var items));
List<NamedValue> list = items.ToList();
list.Add(new NamedValue
{
Name = "ServiceLoadTime",
Value = Value.Create(implementation.ServiceLoadTime)
});
getStatusResponse.Items = list.ToArray();
}
catch (Exception ex)
{
ServiceTrace.LogWarning("GetStatus caught exception from implementation for ConnectionId {0}: {1}", request.ConnectionValidator.ConnectionId, ex.Message);
getStatusResponse.Result.AddErrorMessage(ex.Message);
if (ex.InnerException != null)
{
ServiceTrace.LogWarning(" {0}", ex.InnerException.Message);
getStatusResponse.Result.AddErrorMessage(ex.InnerException.Message);
}
getStatusResponse.Result.ResultCodeAsError = ArchestrAError.OperationFailed;
}
}
ServiceTrace.LogSuspend("GetStatus exit, connection Id {0}, success = {1}", request.ConnectionValidator.ConnectionId, getStatusResponse.Result.Success);
ServiceTrace.LogCsv("GetStatus exit [ConId Success]", request.ConnectionValidator.ConnectionId, getStatusResponse.Result.Success);
return getStatusResponse;
}
public RenewResponse Renew(RenewRequest request)
{
if (request == null)
{
return null;
}
ServiceTrace.LogCsv("Renew entry [ConnId]", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogResume("Renew entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
RenewResponse renewResponse = SystemAuthenticationServiceAuthentication.ProcessClientRenew(request);
ServiceTrace.LogSuspend("Renew exit, connection Id {0}, success = {1}", request.ConnectionValidator.ConnectionId, renewResponse.Result.Success);
ServiceTrace.LogCsv("Renew exit [ConId Success]", request.ConnectionValidator.ConnectionId, renewResponse.Result.Success);
return renewResponse;
}
public void UpdateSystemAuthenticationConfiguration(UpdateSystemAuthenticationConfigurationRequest request)
{
if (request != null)
{
ServiceTrace.LogCsv("UpdateSystemAuthenticationConfiguration entry [ConnId]", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogResume("UpdateSystemAuthenticationConfiguration entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
SystemAuthenticationServiceAuthentication.ProcessClientUpdateSystemAuthenticationConfiguration(request);
ServiceTrace.LogSuspend("UpdateSystemAuthenticationConfiguration exit, connection Id {0}", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogCsv("UpdateSystemAuthenticationConfiguration exit [ConId]", request.ConnectionValidator.ConnectionId);
}
}
public void Disconnect(DisconnectRequest request)
{
if (request != null)
{
ServiceTrace.LogCsv("Disconnect entry [ConnId]", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogResume("Disconnect entry, connection Id {0} ", request.ConnectionValidator.ConnectionId);
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId);
if (serviceAuthenticator != null && serviceAuthenticator.ValidRequest(request, forceHmac: false))
{
OnDisconnect(request.ConnectionValidator.ConnectionId);
serviceAuthenticator.ProcessClientDisconnect(request);
}
ServiceTrace.LogSuspend("Disconnect exit, connection Id {0}", request.ConnectionValidator.ConnectionId);
ServiceTrace.LogCsv("Disconnect exit [ConId]", request.ConnectionValidator.ConnectionId);
}
}
protected UserToken DecryptUserToken(Guid connectionId, UserTokenContract wireToken)
{
if (wireToken == null)
{
return null;
}
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(connectionId);
if (serviceAuthenticator == null || !serviceAuthenticator.SecureSessionEstablished)
{
throw new InvalidOperationException("Cannot decrypt user token due to session in wrong state");
}
UserToken userToken = new UserToken
{
Encryption = wireToken.Encryption,
HostName = wireToken.HostName,
IdType = wireToken.IdType,
LocationId = wireToken.LocationId,
UserName = wireToken.UserName,
Validity = wireToken.Validity
};
if (wireToken.Password != null)
{
byte[] bytes = serviceAuthenticator.Decypher(wireToken.Password, serviceAuthenticator.EncryptionKey);
userToken.Password = Encoding.UTF8.GetString(bytes);
}
if (wireToken.SamlToken != null)
{
userToken.SamlToken = serviceAuthenticator.Decypher(wireToken.SamlToken, serviceAuthenticator.EncryptionKey);
}
if (wireToken.JwtToken != null)
{
byte[] bytes2 = serviceAuthenticator.Decypher(wireToken.JwtToken, serviceAuthenticator.EncryptionKey);
userToken.JwtToken = Encoding.UTF8.GetString(bytes2);
}
if (wireToken.X509Certificate != null)
{
userToken.X509Certificate = serviceAuthenticator.Decypher(wireToken.X509Certificate, serviceAuthenticator.EncryptionKey);
}
return userToken;
}
protected static ArchestrAResult EnsureValidResult(ArchestrAResult result)
{
if (result == null)
{
return ArchestrAResult.MakeErrorResult("Service returned no result");
}
return result;
}
private ArchestrAResult CreateServiceImplmentation(SystemAuthenticationServiceAuthentication authenticator, ConsumerMetadata metadata)
{
ArchestrAResult result = ArchestrAResult.MakeGoodResult();
IBaseV2 implementation = GetImplementation();
if (implementation == null)
{
result = ArchestrAResult.MakeResult(ArchestrAError.Unknown, 0);
string message = string.Format(CultureInfo.CurrentCulture, "AuthenticateMe could not create a service implementation object for ConnectionId {0}", new object[1] { authenticator.ConnectionId });
ServiceTrace.LogWarning(message);
result.AddErrorMessage(message);
return result;
}
try
{
ServiceTrace.LogCsv("AuthenticateMe found authenticator and validated request message, calling implementation OnConnect [ConId]", authenticator.ConnectionId);
ServiceTrace.LogVerbose("AuthenticateMe found authenticator and validated request message, calling implementation OnConnect for connection Id {0}", authenticator.ConnectionId);
implementation.ServiceLoadTime = DateTime.UtcNow;
implementation.ConnectionId = authenticator.ConnectionId;
implementation.OnConnect(authenticator.Lifetime, metadata);
CreateConnectionContext(authenticator.ConnectionId, implementation);
}
catch (Exception ex)
{
result = ArchestrAResult.MakeResult(ArchestrAError.Unknown, 0);
ServiceTrace.LogWarning("AuthenticateMe caught exception from implementation for ConnectionId {0}: {1}", authenticator.ConnectionId, ex.Message);
result.AddErrorMessage(ex.Message);
if (ex.InnerException != null)
{
ServiceTrace.LogWarning(" {0}", ex.InnerException.Message);
result.AddErrorMessage(ex.InnerException.Message);
}
}
return result;
}
private void CreateConnectionContext(Guid connectionId, IBaseV2 implementation)
{
implementationLock.EnterWriteLock();
try
{
implementations.Add(connectionId, new ConnectionContext(implementation, KeepaliveHandler, connectionId, KeepAliveTimeout));
ServiceTrace.LogVerbose("AuthenticateMe added implementation for connection Id {0}", connectionId);
}
finally
{
implementationLock.ExitWriteLock();
}
}
private static void OnDisconnect(Guid connectionId)
{
IBaseV2 baseV = null;
bool flag = false;
implementationLock.EnterWriteLock();
try
{
flag = implementations.TryGetValue(connectionId, out var value);
if (flag)
{
implementations.Remove(connectionId);
ServiceTrace.LogVerbose("OnDisconnect removed implementation for ConnectionId {0}", connectionId);
baseV = value.ConnectionImplementation;
value.Dispose();
}
}
catch (Exception ex)
{
ServiceTrace.LogVerbose("OnDisconnect handled an exception: {0}", ex);
}
finally
{
implementationLock.ExitWriteLock();
}
if (flag && baseV != null)
{
try
{
ServiceTrace.LogCsv("OnDisconnect found authenticator and validated request message, calling implementation OnDisconnect Id =", connectionId);
baseV.OnDisconnect();
}
catch (Exception ex2)
{
ServiceTrace.LogWarning("OnDisconnect caught exception from implementation for ConnectionId {0}: {1}", connectionId, ex2.Message);
if (ex2.InnerException != null)
{
ServiceTrace.LogWarning(" {0}", ex2.InnerException.Message);
}
}
}
SysAuthenticatorServiceCache.RemoveServiceAuthenticator(connectionId);
}
private static void KeepaliveHandler(object connectionId)
{
try
{
OnDisconnect((Guid)connectionId);
}
catch (Exception ex)
{
ServiceTrace.LogVerbose("KeepaliveHandler handled an exception {0}", ex);
}
}
protected virtual ArchestrAResult CheckConsumerMetadata(ConsumerMetadata metadata, Guid connectionId)
{
return ArchestrAResult.MakeGoodResult();
}
protected abstract IBaseV2 GetImplementation();
protected static T GetImplementation<T>(ConnectedRequest request) where T : class, IBaseV2
{
if (request == null)
{
return null;
}
SystemAuthenticationServiceAuthentication serviceAuthenticator = SysAuthenticatorServiceCache.GetServiceAuthenticator(request.ConnectionValidator.ConnectionId);
if (serviceAuthenticator != null && serviceAuthenticator.ValidRequest(request, forceHmac: false))
{
implementationLock.EnterReadLock();
bool flag;
ConnectionContext value;
try
{
flag = implementations.TryGetValue(request.ConnectionValidator.ConnectionId, out value);
}
finally
{
implementationLock.ExitReadLock();
}
if (flag && value != null)
{
value.RestartTimer();
return value.ConnectionImplementation as T;
}
}
return null;
}
}