初始化系统融合软件代码仓库

This commit is contained in:
2026-10-20 08:34:12 +08:00
commit 587ae6f088
96 changed files with 10230 additions and 0 deletions
+89
View File
@@ -0,0 +1,89 @@
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
function Initialize-XtrhImageVerification {
param([Parameter(Mandatory)][string]$CredentialFile)
# 私有文件只读入内存。日志、stdout、命令参数和联调记录都不携带凭证正文。
$privateText = [IO.File]::ReadAllText([IO.Path]::GetFullPath($CredentialFile))
$match = [regex]::Match($privateText, '(?m)^fusion\.token\s*=\s*([^\r\n]+)')
if (-not $match.Success -or $match.Groups[1].Value.Trim().Length -lt 16) { throw '集群私有凭证文件无效。' }
$script:imageVerificationCredential = $match.Groups[1].Value.Trim()
$handler = [Net.Http.HttpClientHandler]::new()
$handler.AllowAutoRedirect = $false
$handler.UseCookies = $false
$script:imageVerificationClient = [Net.Http.HttpClient]::new($handler, $true)
$script:imageVerificationClient.Timeout = [TimeSpan]::FromSeconds(5)
$script:imageVerificationClient.MaxResponseContentBufferSize = 8 * 1024 * 1024
}
function Get-XtrhImageDigest {
param([Parameter(Mandatory)][AllowEmptyCollection()][byte[]]$Bytes)
$sha = [Security.Cryptography.SHA256]::Create()
try { return [BitConverter]::ToString($sha.ComputeHash($Bytes)).Replace('-', '').ToLowerInvariant() }
finally { $sha.Dispose() }
}
function Get-XtrhImageSignature {
param([Parameter(Mandatory)][string]$Canonical)
$mac = [Security.Cryptography.HMACSHA256]::new([Text.Encoding]::UTF8.GetBytes($script:imageVerificationCredential))
try { return [BitConverter]::ToString($mac.ComputeHash([Text.Encoding]::UTF8.GetBytes($Canonical))).Replace('-', '').ToLowerInvariant() }
finally { $mac.Dispose() }
}
function Invoke-XtrhImageSigned {
param([Parameter(Mandatory)][string]$BaseUrl, [Parameter(Mandatory)][string]$Path,
[ValidateSet('GET','POST','PATCH')][string]$Method = 'GET', $Payload = $null)
$base = [Uri]$BaseUrl
if ($base.Scheme -notin @('http','https') -or $base.UserInfo -or $base.Query -or $base.Fragment -or $base.AbsolutePath -ne '/') {
throw '服务根地址格式无效。'
}
if ($Path -notmatch '^/' -or $Path -match '[\r\n]') { throw '签名接口路径无效。' }
# 空数组不能通过 PowerShell 表达式管道返回,否则会被展开为 null。
[byte[]]$body = [byte[]]::new(0)
if ($null -ne $Payload) { $body = [Text.Encoding]::UTF8.GetBytes(($Payload | ConvertTo-Json -Depth 12 -Compress)) }
$nonce = [Guid]::NewGuid().ToString()
$time = [DateTimeOffset]::UtcNow.ToUnixTimeMilliseconds().ToString()
$digest = Get-XtrhImageDigest -Bytes $body
$signature = Get-XtrhImageSignature -Canonical "request`n$Method`n$Path`n$time`n$nonce`n$digest"
$request = [Net.Http.HttpRequestMessage]::new([Net.Http.HttpMethod]::new($Method), $BaseUrl.TrimEnd('/') + $Path)
try {
[void]$request.Headers.TryAddWithoutValidation('X-Xtrh-Time', $time)
[void]$request.Headers.TryAddWithoutValidation('X-Xtrh-Nonce', $nonce)
[void]$request.Headers.TryAddWithoutValidation('X-Xtrh-Signature', $signature)
$request.Content = [Net.Http.ByteArrayContent]::new($body)
$request.Content.Headers.ContentType = [Net.Http.Headers.MediaTypeHeaderValue]::new('application/json')
try { $response = $script:imageVerificationClient.SendAsync($request).GetAwaiter().GetResult() }
catch { throw '服务请求失败,未输出私有请求头或响应内容。' }
try {
[byte[]]$bytes = $response.Content.ReadAsByteArrayAsync().GetAwaiter().GetResult()
$status = [int]$response.StatusCode
$expected = Get-XtrhImageSignature -Canonical ("response`n$nonce`n$status`n" + (Get-XtrhImageDigest -Bytes $bytes))
$headers = [Collections.Generic.IEnumerable[string]]$null
if (-not $response.Headers.TryGetValues('X-Xtrh-Response-Signature', [ref]$headers)) { throw '响应缺少服务身份签名。' }
$actual = @($headers)[0]
# 固定长度与逐字节累计比较;JPEG 必须签其原始字节,不能按文本转换。
$different = $actual.Length -bxor $expected.Length
if ($actual.Length -eq $expected.Length) {
for ($i = 0; $i -lt $expected.Length; $i++) { $different = $different -bor ([int]$actual[$i] -bxor [int]$expected[$i]) }
}
if ($different -ne 0) { throw '响应服务身份认证失败。' }
return [pscustomobject]@{ Status = $status; Bytes = $bytes; ContentType = [string]$response.Content.Headers.ContentType }
} finally { $response.Dispose() }
} finally { $request.Dispose() }
}
function ConvertFrom-XtrhImageJson {
param([Parameter(Mandatory)]$Response)
if ($Response.Status -ne 200) { throw ('签名服务接口返回 HTTP ' + $Response.Status) }
return [Text.Encoding]::UTF8.GetString($Response.Bytes) | ConvertFrom-Json
}
function Get-XtrhImageSnapshot {
param([Parameter(Mandatory)][string]$CoordinatorUrl)
return ConvertFrom-XtrhImageJson (Invoke-XtrhImageSigned -BaseUrl $CoordinatorUrl -Path '/control/snapshot')
}
function Close-XtrhImageVerification {
if ($null -ne $script:imageVerificationClient) { $script:imageVerificationClient.Dispose() }
$script:imageVerificationCredential = ''
}