Skip to content

Commit c50fe0e

Browse files
Merge e30e00e into dff28c7
2 parents dff28c7 + e30e00e commit c50fe0e

10 files changed

Lines changed: 70 additions & 27 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,3 +51,4 @@ source/locale/*/cldr.dic
5151
.venv
5252
nvdaHelper/docs/
5353
*.pfx
54+
appveyor-tools/

appveyor.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,13 @@ branches:
1515

1616
environment:
1717
PY_PYTHON: 3.11-32
18+
# TODO: remove key
1819
secure_authenticode_pass:
1920
secure: Way+hJyhbiLG/cmCo4+dHHzS5DiSvk/45o6frnIQ27GBX6nVDsh7jwQ7fSnqxBRP
21+
# TODO use appveyor encrypt YAML to change this from test cert to prod cert
22+
apiSigningToken:
23+
secure: pTgq98Ewdmg+NgPNoaskkeG6UoPNw1BvIKPAHP/v1PqvD7ihPaVc0TbvZ3qpTgRE
24+
# TODO: check where this is used and remove key
2025
secure_ssh_pass:
2126
secure: Iql/RhSathGacONacsyr6gis+rjL75UFZ/R+nPAJpo3asAzQSQQd8hfxq0iv8+Th
2227
mozillaSymsAuthToken:

appveyor/scripts/decryptFilesForSigning.ps1

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
# TODO update this for apiSigningToken - is any of this still required?
12
if(!$env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:feature_signing) {
23
openssl enc -d -md sha256 -aes-256-cbc -pbkdf2 -salt -pass pass:$env:secure_authenticode_pass -in appveyor\authenticode.pfx.enc -out appveyor\authenticode.pfx
34
if($LastExitCode -ne 0) {
45
$errorCode=$LastExitCode
56
Add-AppveyorMessage "Unable to decrypt authenticode certificate"
67
}
8+
# TODO why was a ssh key used?
79
openssl enc -d -md sha256 -aes-256-cbc -pbkdf2 -salt -pass pass:$env:secure_ssh_pass -in appveyor\ssh_id_rsa.enc -out appveyor\ssh_id_rsa
810
if($LastExitCode -ne 0) {
911
$errorCode=$LastExitCode

appveyor/scripts/setSconsArgs.ps1

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ if ($env:versionType) {
1212
}
1313
$sconsArgs += " publisher=`"$env:scons_publisher`""
1414
if (!$env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:feature_signing) {
15-
$sconsArgs += " certFile=appveyor\authenticode.pfx certTimestampServer=http://timestamp.digicert.com"
15+
# TODO make sure this doesn't expose the decrypted API key in the appveyor log
16+
# TODO Is this script run in local builds? if so we need to add back the certFile and certPassword since people still need to be able to make self-signed builds.
17+
$sconsArgs += " apiSigningToken=$env:apiSigningToken"
1618
}
1719
$sconsArgs += " version_build=$env:APPVEYOR_BUILD_NUMBER"
1820
# We use cmd to run scons because PowerShell throws exceptions if warnings get dumped to stderr.

appveyor/scripts/sign.ps1

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# A part of NonVisual Desktop Access (NVDA)
2+
# Copyright (C) 2010-2024 NV Access Limited
3+
# This file may be used under the terms of the GNU General Public License, version 2 or later.
4+
# For more details see: https://www.gnu.org/licenses/gpl-2.0.html
5+
6+
param(
7+
[string]$ApiToken,
8+
[string]$FileToSign
9+
)
10+
11+
# Check if the Submit-SigningRequest command is available
12+
if (-not (Get-Command -Name Submit-SigningRequest -ErrorAction SilentlyContinue)) {
13+
# If the command is not available, install the SignPath module
14+
Install-Module -Name SignPath -Scope CurrentUser -Force
15+
}
16+
17+
# Execute Submit-SigningRequest command from the SignPath module
18+
# TODO replace test_signing_policy with prod policy slug
19+
Submit-SigningRequest -ApiToken $ApiToken -InputArtifactPath $FileToSign -OutputArtifactPath $FileToSign -OrganizationId "12147e94-bba9-4fef-b29b-300398e90c5a" -ProjectSlug "NVDA" -SigningPolicySlug "test_signing_policy" -WaitForCompletion -Force

appx/sconscript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ else: # not for submission, just side-loadable
5858
packagePublisherDisplayName=env['publisher']
5959
productName="NVDA Screen Reader (Windows Desktop Bridge Edition)"
6060

61-
signExec=env['signExec'] if env['certFile'] else None
61+
signExec=env['signExec'] if (bool(env['certFile']) ^ bool(env['apiSigningToken'])) else None
6262

6363
# Files from NVDA's distribution that cannot be included in the appx due to policy or security restrictions
6464
excludedDistFiles = [

nvdaHelper/archBuild_sconscript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ if not env.get('MSVC_VERSION') or tuple(map(int, env.get('MSVC_VERSION').split("
7070
TARGET_ARCH=env['TARGET_ARCH']
7171
debug=env['nvdaHelperDebugFlags']
7272
release=env['release']
73-
signExec=env['signExec'] if env['certFile'] else None
73+
signExec=env['signExec'] if (bool(env['certFile']) ^ bool(env['apiSigningToken'])) else None
7474

7575
#Some defines and includes for the environment
7676
env.Append(

nvdaHelper/liblouis/sconscript

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ louisSourceDir = louisRootDir.Dir("liblouis")
3333
louisTableDir = louisRootDir.Dir("tables")
3434
outDir = sourceDir.Dir("louis")
3535
unitTestTablesDir = env.Dir("#tests/unit/brailleTables")
36-
signExec=env['signExec'] if env['certFile'] else None
36+
signExec=env['signExec'] if (bool(env['certFile']) ^ bool(env['apiSigningToken'])) else None
3737

3838
RE_AC_INIT = re.compile(r"^AC_INIT\(\[(?P<package>.*)\], \[(?P<version>.*)\], \[(?P<bugReport>.*)\], \[(?P<tarName>.*)\], \[(?P<url>.*)\]\)")
3939
def getLouisVersion():

projectDocs/dev/buildingNVDA.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ This is useful when [creating a self signed build](./selfSignedBuild.md).
7676
* This enables various C++ compiler optimizations such as /O2 and whole-program optimization.
7777
* It also instructs Python to generate optimized byte code.
7878
* publisher: The publisher of this build.
79+
# TODO update documentation for apiSigningToken
7980
* certFile: The certificate file with which to sign executables. The certificate must be in pfx format and contain the private key.
8081
* certPassword: The password for the private key in the signing certificate. If omitted, no password will be assumed.
8182
* certTimestampServer: The URL of the timestamping server to use to timestamp authenticode signatures. If omitted, signatures will not be timestamped.

sconstruct

Lines changed: 36 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# A part of NonVisual Desktop Access (NVDA)
2-
# Copyright (C) 2010-2023 NV Access Limited, James Teh, Michael Curran, Peter Vágner, Joseph Lee,
2+
# Copyright (C) 2010-2024 NV Access Limited, James Teh, Michael Curran, Peter Vágner, Joseph Lee,
33
# Reef Turner, Babbage B.V., Leonard de Ruijter, Łukasz Golonka, Accessolutions, Julien Cochuyt,
44
# Cyrille Bougot
55
# This file may be used under the terms of the GNU General Public License, version 2 or later.
@@ -95,6 +95,7 @@ vars.Add(PathVariable("certFile", "The certificate file with which to sign execu
9595
lambda key, val, env: not val or PathVariable.PathIsFile(key, val, env)))
9696
vars.Add("certPassword", "The password for the private key in the signing certificate", "")
9797
vars.Add("certTimestampServer", "The URL of the timestamping server to use to timestamp authenticode signatures", "")
98+
vars.Add("apiSigningToken", "The API key for the signing service", "")
9899
vars.Add(PathVariable("outputDir", "The directory where the final built archives and such will be placed", "output",PathVariable.PathIsDirCreate))
99100
vars.Add(ListVariable("nvdaHelperDebugFlags", "a list of debugging features you require", 'none', ["debugCRT","RTC","analyze"]))
100101
vars.Add(EnumVariable('nvdaHelperLogLevel','The level of logging you wish to see, lower is more verbose','15',allowed_values=[str(x) for x in range(60)]))
@@ -146,6 +147,9 @@ publisher = env["publisher"]
146147
certFile = env["certFile"]
147148
certPassword = env["certPassword"]
148149
certTimestampServer = env["certTimestampServer"]
150+
# TODO remove local testing code when using secure Appveyor store
151+
# TODO make sure that the unencrypted API key isn't logged anywhere eg AppVeyor
152+
apiSigningToken = env["apiSigningToken"] or os.environ.get("apiSigningToken") # workaround for local testing
149153
userDocsDir=Dir('user_docs')
150154
sourceDir = env.Dir("source")
151155
Export('sourceDir')
@@ -165,24 +169,33 @@ Export('outFilePrefix')
165169
outputDir=Dir(env['outputDir'])
166170
Export('outputDir')
167171

168-
# An action to sign an executable with certFile.
169-
# we encrypt with SHA256 as this is the minimum required by the Windows Store for appx packages
170-
signExecCmd = ["signtool", "sign", "/fd", "SHA256", "/f", certFile]
171-
if certPassword:
172-
signExecCmd.extend(("/p", certPassword))
173-
if certTimestampServer:
174-
signExecCmd.extend(("/tr", certTimestampServer, "/td", "SHA256"))
175-
def signExec(target,source,env):
176-
print([str(x) for x in target])
177-
#sys.exit(1)
178-
# #3795: signtool can quite commonly fail with timestamping, so allow it to try up to 3 times with a 1 second delay between each try.
179-
res=0
180-
for count in range(3):
181-
res=env.Execute([signExecCmd+[target[0].abspath]])
182-
if not res:
183-
return 0 # success
184-
time.sleep(1)
185-
return res # failed
172+
assert not (apiSigningToken and certFile), "Cannot specify signing with both API token (cloud) and local certificate"
173+
if apiSigningToken:
174+
# Code signing with SignPath HSM
175+
def signExec(target, source, env):
176+
# TODO see if we have to pass the apiSigningToken or whether the sign.ps1 shell can find the variable
177+
ps_cmd = f"powershell -File appveyor\scripts\sign.ps1 -ApiToken {apiSigningToken} -FileToSign {target[0].abspath}"
178+
return env.Execute(ps_cmd, shell=True)
179+
if certFile:
180+
# Local code signing with a certFile
181+
def signExec(target,source,env):
182+
# we encrypt with SHA256 as this is the minimum required by the Windows Store for appx packages
183+
signExecCmd = ["signtool", "sign", "/fd", "SHA256", "/f", certFile]
184+
if certPassword:
185+
signExecCmd.extend(("/p", certPassword))
186+
if certTimestampServer:
187+
signExecCmd.extend(("/tr", certTimestampServer, "/td", "SHA256"))
188+
def signExec(target,source,env):
189+
print([str(x) for x in target])
190+
#sys.exit(1)
191+
# #3795: signtool can quite commonly fail with timestamping, so allow it to try up to 3 times with a 1 second delay between each try.
192+
res=0
193+
for count in range(3):
194+
res=env.Execute([signExecCmd+[target[0].abspath]])
195+
if not res:
196+
return 0 # success
197+
time.sleep(1)
198+
return res # failed
186199
#Export via scons environment so other libraries can be signed
187200
env['signExec']=signExec
188201

@@ -313,7 +326,7 @@ def NVDADistGenerator(target, source, env, for_signature):
313326
if file.name.endswith(".dll") and file.is_file():
314327
action.append(Copy(target[0], file.path))
315328

316-
if certFile:
329+
if certFile or apiSigningToken:
317330
for prog in "nvda_noUIAccess.exe", "nvda_uiAccess.exe", "nvda_slave.exe":
318331
action.append(lambda target,source,env, progByVal=prog: signExec([target[0].File(progByVal)],source,env))
319332

@@ -376,10 +389,10 @@ uninstGen = env.Command(File("uninstaller/uninstGen.exe"), "uninstaller/uninst.n
376389
"/DINSTEXE=${TARGET.abspath}",
377390
"$SOURCE"]])
378391
uninstaller=env.Command(uninstFile,uninstGen,[uninstGen])
379-
if certFile:
392+
if certFile or apiSigningToken:
380393
env.AddPostAction(uninstaller, [signExec])
381394

382-
dist = env.NVDADist("dist", [sourceDir,userDocsDir], uiAccess=bool(certFile))
395+
dist = env.NVDADist("dist", [sourceDir,userDocsDir], uiAccess=bool(certFile) or bool(apiSigningToken))
383396
env.Depends(dist,uninstaller)
384397
# dist will always be considered obsolete
385398
AlwaysBuild(dist)
@@ -393,7 +406,7 @@ launcher = env.Command(outputDir.File("%s.exe" % outFilePrefix), ["launcher/nvda
393406
"/DVERSION=$version", '/DPUBLISHER="$publisher"','/DCOPYRIGHT="$copyright"','/DVERSION_YEAR="$version_year"','/DVERSION_MAJOR="$version_major"','/DVERSION_MINOR="$version_minor"','/DVERSION_BUILD="$version_build"',
394407
"/DNVDADistDir=${SOURCES[1].abspath}", "/DLAUNCHEREXE=${TARGET.abspath}",
395408
"$SOURCE"]])
396-
if certFile:
409+
if certFile or apiSigningToken:
397410
env.AddPostAction(launcher, [signExec])
398411
env.Alias("launcher", launcher)
399412

0 commit comments

Comments
 (0)