Add verified WSS transport for legacy boards
Configure the default WSS endpoint and ISRG Root X2 trust anchor, preserve device authentication, enforce TLS and WebSocket handshake validation, and handle partial I/O and connection cleanup. Add host tests, server probes, package verification and integration documentation. Backend 443 /ws still returns HTTP 404; on-device validation remains pending.
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
param([string]$OutputDir = "$PSScriptRoot/../artifacts/wss", [ValidateSet(443, 8080)][int]$ServerPort = 443)
|
||||
$ErrorActionPreference = 'Stop'
|
||||
$serverHost = 'banban.api.talkingq.com'
|
||||
New-Item -ItemType Directory -Force -Path $OutputDir | Out-Null
|
||||
$OutputDir = (Resolve-Path -LiteralPath $OutputDir).Path
|
||||
$tcp = New-Object System.Net.Sockets.TcpClient
|
||||
$tls = $null
|
||||
try {
|
||||
$pending = $tcp.BeginConnect($serverHost, $serverPort, $null, $null)
|
||||
if (-not $pending.AsyncWaitHandle.WaitOne(15000)) { throw 'TCP connect timeout' }
|
||||
$tcp.EndConnect($pending)
|
||||
$tcp.ReceiveTimeout = 15000
|
||||
$tcp.SendTimeout = 15000
|
||||
# Default SslStream validation checks the trusted chain and server hostname.
|
||||
$tls = New-Object System.Net.Security.SslStream($tcp.GetStream(), $false)
|
||||
$tls.ReadTimeout = 15000
|
||||
$tls.WriteTimeout = 15000
|
||||
$tls.AuthenticateAsClient($serverHost, $null, [System.Security.Authentication.SslProtocols]::Tls12, $false)
|
||||
$leaf = New-Object System.Security.Cryptography.X509Certificates.X509Certificate2($tls.RemoteCertificate)
|
||||
$chain = New-Object System.Security.Cryptography.X509Certificates.X509Chain
|
||||
$chain.ChainPolicy.RevocationMode = [System.Security.Cryptography.X509Certificates.X509RevocationMode]::NoCheck
|
||||
if (-not $chain.Build($leaf)) { throw 'Trusted certificate chain could not be built' }
|
||||
$certificates = @()
|
||||
for ($i = 0; $i -lt $chain.ChainElements.Count; $i++) {
|
||||
$cert = $chain.ChainElements[$i].Certificate
|
||||
$pem = "-----BEGIN CERTIFICATE-----`n" + [Convert]::ToBase64String($cert.RawData, [Base64FormattingOptions]::InsertLineBreaks).Replace("`r`n", "`n") + "`n-----END CERTIFICATE-----`n"
|
||||
[IO.File]::WriteAllText((Join-Path $OutputDir "chain-$i.pem"), $pem, [Text.Encoding]::ASCII)
|
||||
$certificates += [ordered]@{subject=$cert.Subject; issuer=$cert.Issuer; not_before=$cert.NotBefore.ToUniversalTime().ToString('o'); not_after=$cert.NotAfter.ToUniversalTime().ToString('o'); thumbprint=$cert.Thumbprint}
|
||||
if ($i -eq $chain.ChainElements.Count - 1) {
|
||||
if ($cert.Subject -ne $cert.Issuer) { throw 'Chain did not terminate at a root CA' }
|
||||
[IO.File]::WriteAllText((Join-Path $OutputDir 'root-ca.pem'), $pem, [Text.Encoding]::ASCII)
|
||||
}
|
||||
}
|
||||
$nonce = New-Object byte[] 16
|
||||
$rng = [Security.Cryptography.RandomNumberGenerator]::Create()
|
||||
$rng.GetBytes($nonce)
|
||||
$rng.Dispose()
|
||||
$key = [Convert]::ToBase64String($nonce)
|
||||
$request = "GET /ws HTTP/1.1`r`nHost: ${serverHost}:${serverPort}`r`nUpgrade: websocket`r`nConnection: Upgrade`r`nSec-WebSocket-Key: $key`r`nSec-WebSocket-Version: 13`r`n`r`n"
|
||||
$bytes = [Text.Encoding]::ASCII.GetBytes($request)
|
||||
$tls.Write($bytes, 0, $bytes.Length)
|
||||
$response = ''
|
||||
while (-not $response.EndsWith("`r`n`r`n") -and $response.Length -lt 16384) {
|
||||
$next = $tls.ReadByte()
|
||||
if ($next -lt 0) { break }
|
||||
$response += [char]$next
|
||||
}
|
||||
$sha1 = [Security.Cryptography.SHA1]::Create()
|
||||
$expected = [Convert]::ToBase64String($sha1.ComputeHash([Text.Encoding]::ASCII.GetBytes($key + '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')))
|
||||
$sha1.Dispose()
|
||||
$accepted = $response -match '^HTTP/1\.1 101 ' -and $response -match ('(?im)^Sec-WebSocket-Accept:\s*' + [regex]::Escape($expected) + '\s*$')
|
||||
$report = [ordered]@{url="wss://${serverHost}:${serverPort}/ws"; tested_utc=[DateTime]::UtcNow.ToString('o'); tls_protocol=$tls.SslProtocol.ToString(); cipher=$tls.CipherAlgorithm.ToString(); cipher_bits=$tls.CipherStrength; certificate_validation='System trust and hostname validation passed'; certificates=$certificates; websocket_upgrade=$accepted; response_headers=$response; device_auth_sent=$false}
|
||||
$report | ConvertTo-Json -Depth 6 | Set-Content -Encoding UTF8 -LiteralPath (Join-Path $OutputDir 'server-probe.json')
|
||||
$report | ConvertTo-Json -Depth 6
|
||||
if (-not $accepted) { throw 'WebSocket upgrade did not return a valid HTTP 101 response' }
|
||||
} finally {
|
||||
if ($tls) { $tls.Dispose() }
|
||||
$tcp.Close()
|
||||
}
|
||||
Reference in New Issue
Block a user