# Generate a Gitea GPG key-verification signature (official UI challenge). # Token comes from /user/settings/keys -> Verify. Never commit the token. # Matches Gitea template: echo "TOKEN" | gpg -a --default-key KEYID --detach-sig # (trailing newline included - unlike SSH verification). param( [Parameter(Mandatory = $true)] [string]$Token, [Parameter(Mandatory = $true)] [string]$KeyId, [string]$GpgPath ) $ErrorActionPreference = 'Stop' . (Join-Path $PSScriptRoot 'common.ps1') function Resolve-GpgPath { param([string]$Preferred) if ($Preferred -and (Test-Path -LiteralPath $Preferred)) { return $Preferred } $cmd = Get-Command gpg -ErrorAction SilentlyContinue if ($cmd) { return $cmd.Source } $candidates = @( (Join-Path $env:ProgramFiles 'Git\usr\bin\gpg.exe'), (Join-Path ${env:ProgramFiles(x86)} 'GnuPG\bin\gpg.exe'), (Join-Path $env:ProgramFiles 'GnuPG\bin\gpg.exe') ) foreach ($p in $candidates) { if ($p -and (Test-Path -LiteralPath $p)) { return $p } } return $null } $gpg = Resolve-GpgPath -Preferred $GpgPath if ($null -eq $gpg) { Write-StatusLine -Key 'status' -Value 'failed' Write-StatusLine -Key 'reason' -Value 'gpg_missing' exit 1 } $token = $Token.Trim() $keyId = $KeyId.Trim() if ([string]::IsNullOrWhiteSpace($token) -or [string]::IsNullOrWhiteSpace($keyId)) { Write-StatusLine -Key 'status' -Value 'failed' Write-StatusLine -Key 'reason' -Value 'token_or_key_empty' exit 1 } $bash = Join-Path $env:ProgramFiles 'Git\bin\bash.exe' $sigText = $null # gpg prints "using ... as default secret key" to stderr; do not treat as terminating error. $prevEap = $ErrorActionPreference $ErrorActionPreference = 'Continue' try { if (Test-Path -LiteralPath $bash) { # Unix echo "TOKEN" includes a single trailing LF - matches Gitea UI help. $sigText = Invoke-GitSkillsNativeWithInput -FilePath $bash -ArgumentList @('-lc', "gpg -a --default-key '$keyId' --detach-sig") -InputText ($token + "`n") | Out-String } else { $tmp = [System.IO.Path]::GetTempFileName() try { # UTF-8 token + LF, no BOM $bytes = [System.Text.Encoding]::UTF8.GetBytes($token + "`n") [System.IO.File]::WriteAllBytes($tmp, $bytes) $null = Invoke-GitSkillsNative -FilePath $gpg -ArgumentList @('-a', '--default-key', $keyId, '--detach-sig', $tmp) $asc = $tmp + '.asc' if (Test-Path -LiteralPath $asc) { $sigText = Get-Content -LiteralPath $asc -Raw -Encoding UTF8 Remove-Item -LiteralPath $asc -Force -ErrorAction SilentlyContinue } } finally { Remove-Item -LiteralPath $tmp -Force -ErrorAction SilentlyContinue } } } finally { $ErrorActionPreference = $prevEap } if ($sigText -notmatch 'BEGIN PGP SIGNATURE') { Write-StatusLine -Key 'status' -Value 'failed' Write-StatusLine -Key 'reason' -Value 'sign_failed' Write-StatusLine -Key 'gpg' -Value $gpg exit 1 } Write-StatusLine -Key 'status' -Value 'ok' Write-StatusLine -Key 'key_id' -Value $keyId Write-StatusLine -Key 'gpg' -Value $gpg Write-Output '-----BEGIN_SIGNATURE_BODY-----' # Print only the armored block lines for easy copy $capture = $false foreach ($line in ($sigText -split "`r?`n")) { if ($line -match 'BEGIN PGP SIGNATURE') { $capture = $true } if ($capture) { Write-Output $line } if ($line -match 'END PGP SIGNATURE') { break } } Write-Output '-----END_SIGNATURE_BODY-----' Write-Output 'Paste the block between BEGIN/END PGP SIGNATURE into Gitea Verify, then click verify.'