# Verae signing key handling for the installer (runs elevated, acts for the LOGGED-IN user). # -Mode status : detect target user + existing key → INI [token] # -Mode validate : check key in -TokenFile against Verae (free) → INI [validate] # -Mode save : validate-format + write \.verae\xverae-token with user-only ACL → INI [save] # The key is never printed; only a masked form (xVerae-...abcd) is logged. param( [ValidateSet("status","validate","save")][string]$Mode = "status", [string]$TokenFile, [Parameter(Mandatory=$true)][string]$IniOut ) $ErrorActionPreference = "Continue" . (Join-Path $PSScriptRoot "common.ps1") [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 $ValidateUrl = "https://zap.verae.ai/v1/status" # documented as free; 401 = bad key # Prefer the Go validator (verae-print.exe validate-key): PowerShell 5.1's # Invoke-WebRequest was seen timing out on nimo (proxy auto-detect) while Go and # curl answered in <1 s. The key is read from a file and never on a command line. function Find-VeraeExe { foreach ($c in @((Join-Path $PSScriptRoot "verae-print.exe"), (Join-Path (Split-Path -Parent $PSScriptRoot) "bin\verae-print.exe"), (Join-Path (Split-Path -Parent $PSScriptRoot) "verae-print.exe"))) { if (Test-Path $c) { return $c } } return $null } function Test-VeraeKey([string]$tok, [string]$file) { if ($tok -notmatch '^(xVerae-|key-)\S{8,}$') { return @{ result = "badformat"; detail = "must start with xVerae- (or legacy key-)" } } $exe = Find-VeraeExe if ($exe -and $file -and (Test-Path $file)) { $ini = Join-Path $env:TEMP ("verae-val-" + [guid]::NewGuid().ToString("N") + ".ini") try { & $exe validate-key --token-file $file --out $ini 2>&1 | Out-Null $kv = @{} if (Test-Path $ini) { Get-Content $ini | ForEach-Object { if ($_ -match '^([^=\[]+)=(.*)$') { $kv[$Matches[1]] = $Matches[2] } } } if ($kv.result) { return @{ result = $kv.result; detail = $kv.detail } } } finally { Remove-Item $ini -ErrorAction SilentlyContinue } } try { $r = Invoke-WebRequest -UseBasicParsing -TimeoutSec 10 -Uri $ValidateUrl -Headers @{ "x-api-key" = $tok } return @{ result = "valid"; detail = "HTTP $($r.StatusCode)" } } catch { $code = $null if ($_.Exception.Response) { $code = [int]$_.Exception.Response.StatusCode } if ($code -eq 401 -or $code -eq 403) { return @{ result = "invalid"; detail = "HTTP $code from Verae" } } if ($code) { return @{ result = "unknown"; detail = "HTTP $code from Verae" } } return @{ result = "unreachable"; detail = $_.Exception.Message } } } $u = Get-VeraeTargetUser $tokPath = Join-Path $u.Profile ".verae\xverae-token" Write-Host "user-token.ps1 mode=$Mode target=$($u.Account) sid=$($u.Sid) profile=$($u.Profile) via=$($u.Source) runningAs=$([Security.Principal.WindowsIdentity]::GetCurrent().Name)" switch ($Mode) { "status" { $existing = $null if (Test-Path $tokPath) { $existing = (Get-Content $tokPath -Raw -ErrorAction SilentlyContinue) } if ($existing) { $existing = $existing.Trim() } $v = @{ result = "none"; detail = "" } if ($existing) { $v = Test-VeraeKey $existing $tokPath } Write-Host "existing key: $([bool]$existing) $(Get-MaskedToken $existing) check=$($v.result) $($v.detail)" Write-VeraeIni $IniOut "token" ([ordered]@{ account = $u.Account; sid = $u.Sid; profile = $u.Profile; source = $u.Source; path = $tokPath present = $(if ($existing) { 1 } else { 0 }); masked = (Get-MaskedToken $existing) check = $v.result; detail = $v.detail }) } "validate" { $tok = (Get-Content $TokenFile -Raw).Trim() $v = Test-VeraeKey $tok $TokenFile Write-Host "validate $(Get-MaskedToken $tok): $($v.result) $($v.detail)" Write-VeraeIni $IniOut "validate" ([ordered]@{ result = $v.result; detail = $v.detail; masked = (Get-MaskedToken $tok) }) } "save" { $tok = (Get-Content $TokenFile -Raw).Trim() $ok = $false; $err = "" try { if ($tok -notmatch '^(xVerae-|key-)\S{8,}$') { throw "bad token format" } $dir = Split-Path $tokPath -Parent New-Item -ItemType Directory -Force -Path $dir | Out-Null # same format as internal/auth.SaveXVeraeToken: token + "\n", no BOM [IO.File]::WriteAllText($tokPath, $tok + "`n", (New-Object Text.UTF8Encoding($false))) # ACL: the logged-in user owns it; only user, SYSTEM, Administrators have access icacls $dir /grant "*$($u.Sid):(OI)(CI)F" /C | Out-Host icacls $dir /setowner "*$($u.Sid)" /C | Out-Host icacls $tokPath /inheritance:r /grant:r "*$($u.Sid):F" "*S-1-5-18:F" "*S-1-5-32-544:F" /C | Out-Host icacls $tokPath /setowner "*$($u.Sid)" /C | Out-Host $back = (Get-Content $tokPath -Raw).Trim() $ok = ($back -eq $tok) if (-not $ok) { $err = "read-back mismatch" } icacls $tokPath | Out-Host } catch { $err = $_.Exception.Message } Write-Host "save $(Get-MaskedToken $tok) -> $tokPath : $(if ($ok) {'OK'} else {"FAIL $err"})" Write-VeraeIni $IniOut "save" ([ordered]@{ result = $(if ($ok) { "OK" } else { "FAIL" }); detail = $err; path = $tokPath; masked = (Get-MaskedToken $tok); account = $u.Account }) } } exit 0