|
| 1 | +name: Build C++ example |
| 2 | + |
| 3 | +on: |
| 4 | + push: |
| 5 | + branches: [main] |
| 6 | + pull_request: |
| 7 | + branches: [main] |
| 8 | + workflow_dispatch: |
| 9 | + inputs: |
| 10 | + app_name: |
| 11 | + description: Optional public KeyAuth application name |
| 12 | + required: false |
| 13 | + type: string |
| 14 | + owner_id: |
| 15 | + description: Optional public KeyAuth owner ID |
| 16 | + required: false |
| 17 | + type: string |
| 18 | + app_version: |
| 19 | + description: Optional application version |
| 20 | + required: false |
| 21 | + type: string |
| 22 | + |
| 23 | +permissions: |
| 24 | + contents: read |
| 25 | + |
| 26 | +env: |
| 27 | + KEYAUTH_LIBRARY_COMMIT: e345db830576b7a14cf1a818ffd4a3b368fb228b |
| 28 | + KEYAUTH_LIBRARY_SHA256: 93583a96377956b0092b6c037c5c258fc73881e694f57cff305ec658d62a5ab4 |
| 29 | + |
| 30 | +jobs: |
| 31 | + build: |
| 32 | + runs-on: windows-2022 |
| 33 | + steps: |
| 34 | + - uses: actions/checkout@v4 |
| 35 | + - uses: actions/checkout@v4 |
| 36 | + with: |
| 37 | + repository: ELF-Nigel/keyauth-cpp-library-1.3API |
| 38 | + ref: ${{ env.KEYAUTH_LIBRARY_COMMIT }} |
| 39 | + path: .keyauth-source |
| 40 | + persist-credentials: false |
| 41 | + |
| 42 | + - name: Verify paired library |
| 43 | + shell: pwsh |
| 44 | + run: | |
| 45 | + $expected = [IO.File]::ReadAllText('.keyauth-source/auth.hpp').Replace("`r`n", "`n") |
| 46 | + $actual = [IO.File]::ReadAllText('x64/lib/auth.hpp').Replace("`r`n", "`n") |
| 47 | + if ($actual -cne $expected) { throw 'Bundled header does not match the selected library source.' } |
| 48 | + $digest = (Get-FileHash -LiteralPath 'x64/library_x64.lib' -Algorithm SHA256).Hash.ToLowerInvariant() |
| 49 | + if ($digest -cne $env:KEYAUTH_LIBRARY_SHA256) { throw 'Bundled library does not match the verified build.' } |
| 50 | +
|
| 51 | + - uses: microsoft/setup-msbuild@v2 |
| 52 | + |
| 53 | + - name: Apply optional public application settings |
| 54 | + shell: pwsh |
| 55 | + env: |
| 56 | + APP_NAME: ${{ inputs.app_name }} |
| 57 | + OWNER_ID: ${{ inputs.owner_id }} |
| 58 | + APP_VERSION: ${{ inputs.app_version }} |
| 59 | + run: | |
| 60 | + if ([bool]$env:APP_NAME -ne [bool]$env:OWNER_ID) { |
| 61 | + throw 'Provide both application name and owner ID, or leave both blank.' |
| 62 | + } |
| 63 | + if ($env:OWNER_ID -and $env:OWNER_ID.Length -ne 10) { |
| 64 | + throw 'Owner ID must contain 10 characters.' |
| 65 | + } |
| 66 | + $source = Get-Content -LiteralPath 'x64/main.cpp' -Raw |
| 67 | + $settings = @{name = $env:APP_NAME; ownerid = $env:OWNER_ID; version = $env:APP_VERSION} |
| 68 | + foreach ($entry in $settings.GetEnumerator()) { |
| 69 | + if ([string]::IsNullOrEmpty($entry.Value)) { continue } |
| 70 | + $encoded = ([Text.Encoding]::UTF8.GetBytes($entry.Value) | ForEach-Object { '\x{0:x2}' -f $_ }) -join '' |
| 71 | + $replacement = 'std::string ' + $entry.Key + ' = skCrypt("' + $encoded + '").decrypt();' |
| 72 | + $pattern = '(?m)^std::string ' + $entry.Key + ' = skCrypt\("[^"\r\n]*"\)\.decrypt\(\);' |
| 73 | + if (-not [regex]::IsMatch($source, $pattern)) { throw 'Application setting was not found.' } |
| 74 | + $source = [regex]::Replace($source, $pattern, [Text.RegularExpressions.MatchEvaluator]{param($match) $replacement}) |
| 75 | + } |
| 76 | + Set-Content -LiteralPath 'x64/main.cpp' -Value $source -NoNewline |
| 77 | +
|
| 78 | + - name: Build Release x64 |
| 79 | + working-directory: x64 |
| 80 | + run: msbuild example.vcxproj /t:Build /p:Configuration=Release /p:Platform=x64 /m |
| 81 | + |
| 82 | + - name: Verify executable mitigations |
| 83 | + shell: pwsh |
| 84 | + run: | |
| 85 | + $installation = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath |
| 86 | + $dumpbin = Get-ChildItem -Path (Join-Path $installation 'VC/Tools/MSVC/*/bin/Hostx64/x64/dumpbin.exe') | Sort-Object FullName -Descending | Select-Object -First 1 |
| 87 | + if (-not $dumpbin) { throw 'Executable inspection tool was not found.' } |
| 88 | + $headers = (& $dumpbin.FullName /headers /loadconfig 'x64/x64/Release/example.exe') -join "`n" |
| 89 | + if ($LASTEXITCODE -ne 0) { throw 'Executable inspection failed.' } |
| 90 | + foreach ($required in @('CF instrumented', 'FID table present', 'Dynamic base', 'NX compatible', 'High Entropy Virtual Addresses')) { |
| 91 | + if ($headers -notmatch [regex]::Escape($required)) { throw "Missing executable mitigation: $required" } |
| 92 | + } |
| 93 | +
|
| 94 | + - name: Package runnable example |
| 95 | + shell: pwsh |
| 96 | + run: | |
| 97 | + New-Item -ItemType Directory -Path build-output -Force | Out-Null |
| 98 | + Copy-Item -LiteralPath 'x64/x64/Release/example.exe' -Destination build-output |
| 99 | + $installation = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere.exe" -latest -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath |
| 100 | + $crt = Get-ChildItem -Path (Join-Path $installation 'VC/Redist/MSVC/*/x64/Microsoft.VC*.CRT') -Directory | Sort-Object FullName -Descending | Select-Object -First 1 |
| 101 | + if (-not $crt) { throw 'Visual C++ runtime files were not found.' } |
| 102 | + Get-ChildItem -LiteralPath $crt.FullName -Filter '*.dll' | Copy-Item -Destination build-output |
| 103 | + Get-FileHash -LiteralPath 'build-output/example.exe' -Algorithm SHA256 | ForEach-Object { $_.Hash.ToLowerInvariant() + ' example.exe' } | Set-Content -LiteralPath 'build-output/SHA256SUMS.txt' |
| 104 | +
|
| 105 | + - uses: actions/upload-artifact@v4 |
| 106 | + with: |
| 107 | + name: KeyAuth-CPP-Example-x64 |
| 108 | + path: build-output/ |
| 109 | + if-no-files-found: error |
| 110 | + retention-days: 30 |
0 commit comments