c95824a65d
Full read-only SDK (src/AVEVA.Historian.Client) implementing the CLAUDE.md required
surface against AVEVA Historian's binary WCF protocol — no native AVEVA runtime
dependency. All operations live-verified against a local Historian:
- ProbeAsync, ReadRawAsync, ReadAggregateAsync, ReadAtTimeAsync, ReadEventsAsync
- BrowseTagNamesAsync, GetTagMetadataAsync (17 native data-type codes mapped)
- GetConnectionStatusAsync, GetStoreForwardStatusAsync, GetSystemParameterAsync
- 108/108 unit + integration tests pass
Includes the reverse-engineering toolkit (tools/AVEVA.Historian.ReverseEngineering)
used to decode the protocol: WCF probes, IL inspection via dnlib, and IL-rewrite
instrumentation (instrument-wcf-{write,read}message etc.) plus the .NET Framework
trace harness (tools/AVEVA.Historian.NativeTraceHarness) for parity testing.
Sanitized handoff evidence under docs/reverse-engineering/. Native AVEVA binaries
(current/, aveva-install-x64/, aveva-install-x86/) are gitignored — fetch separately
from the AVEVA installer.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
98 lines
3.1 KiB
C#
98 lines
3.1 KiB
C#
using AVEVA.Historian.Client.Wcf;
|
|
|
|
namespace AVEVA.Historian.Client.Tests;
|
|
|
|
public sealed class WcfAuthenticationProtocolTests
|
|
{
|
|
[Fact]
|
|
public void WrapValidateClientCredentialToken_UsesNativeRoundAndLengthEnvelope()
|
|
{
|
|
byte[] actual = HistorianWcfAuthenticationProtocol.WrapValidateClientCredentialToken(
|
|
isFirstRound: true,
|
|
[0x4E, 0x54, 0x4C, 0x4D]);
|
|
|
|
Assert.Equal([0x01, 0x04, 0x00, 0x00, 0x00, 0x4E, 0x54, 0x4C, 0x4D], actual);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReadWrappedValidateClientCredentialToken_ReadsNativeEnvelope()
|
|
{
|
|
ValidateClientCredentialToken? actual =
|
|
HistorianWcfAuthenticationProtocol.TryReadWrappedValidateClientCredentialToken(
|
|
[0x00, 0x03, 0x00, 0x00, 0x00, 0xAA, 0xBB, 0xCC]);
|
|
|
|
Assert.NotNull(actual);
|
|
Assert.False(actual.IsFirstRound);
|
|
Assert.Equal([0xAA, 0xBB, 0xCC], actual.Token);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReadWrappedValidateClientCredentialToken_RejectsLengthMismatch()
|
|
{
|
|
Assert.Null(HistorianWcfAuthenticationProtocol.TryReadWrappedValidateClientCredentialToken(
|
|
[0x01, 0x04, 0x00, 0x00, 0x00, 0xAA]));
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReadValidateClientCredentialResponse_ReadsContinueFlagAndServerToken()
|
|
{
|
|
ValidateClientCredentialResponse? actual =
|
|
HistorianWcfAuthenticationProtocol.TryReadValidateClientCredentialResponse(
|
|
[0x01, 0x11, 0x22, 0x33]);
|
|
|
|
Assert.NotNull(actual);
|
|
Assert.True(actual.Continue);
|
|
Assert.Equal([0x11, 0x22, 0x33], actual.Token);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReadValidateClientCredentialResponse_ReadsTerminalOneByteResponse()
|
|
{
|
|
ValidateClientCredentialResponse? actual =
|
|
HistorianWcfAuthenticationProtocol.TryReadValidateClientCredentialResponse([0x00]);
|
|
|
|
Assert.NotNull(actual);
|
|
Assert.False(actual.Continue);
|
|
Assert.Empty(actual.Token);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryReadValidateClientCredentialResponse_RejectsEmptyResponse()
|
|
{
|
|
Assert.Null(HistorianWcfAuthenticationProtocol.TryReadValidateClientCredentialResponse([]));
|
|
}
|
|
|
|
[Fact]
|
|
public void TryApplyNativeNtlmNegotiateVersionFlag_MatchesObservedNativeFirstTokenFlag()
|
|
{
|
|
byte[] token =
|
|
[
|
|
0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50, 0x00,
|
|
0x01, 0x00, 0x00, 0x00,
|
|
0xB7, 0xB2, 0x08, 0xE2
|
|
];
|
|
|
|
bool changed = HistorianWcfAuthenticationProtocol.TryApplyNativeNtlmNegotiateVersionFlag(token);
|
|
|
|
Assert.True(changed);
|
|
Assert.Equal(
|
|
[
|
|
0x4E, 0x54, 0x4C, 0x4D, 0x53, 0x53, 0x50, 0x00,
|
|
0x01, 0x00, 0x00, 0x00,
|
|
0xB7, 0xB2, 0x18, 0xE2
|
|
],
|
|
token);
|
|
}
|
|
|
|
[Fact]
|
|
public void TryApplyNativeNtlmNegotiateVersionFlag_IgnoresNonNtlmNegotiateTokens()
|
|
{
|
|
byte[] token = [0x4B, 0x52, 0x42, 0x35];
|
|
|
|
bool changed = HistorianWcfAuthenticationProtocol.TryApplyNativeNtlmNegotiateVersionFlag(token);
|
|
|
|
Assert.False(changed);
|
|
Assert.Equal([0x4B, 0x52, 0x42, 0x35], token);
|
|
}
|
|
}
|