You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
o3de/Tools/build/JenkinsScripts/distribution/Installer/SignTool.py

213 lines
7.7 KiB
Python

#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
import shutil
import subprocess
from BuildInstallerUtils import *
# Signing cmd example
# signtool.exe sign /f c:\codesigning\lumberyard.pfx /tr http://tsa.starfieldtech.com /td SHA256 /p thePasswordSeeNotesBelow %f
# Verify cmd example.
# signtool.exe verify /v /pa %f
# SignTool COMMANDLINE TEMPLATES
signtoolCommandBase = "signtool.exe {commandType} {verbose} {commandTypeParams} {filename}"
signtoolKeySignParams = "/fd SHA256 /f {certificatePath} /p {password}"
signtoolNameSignParams = '/fd SHA256 /n "{certificateName}"'
signtoolTimestampParams = "/tr {timestampServerURL} /td SHA256"
signtoolVerifyParams = "/pa /tw /hash SHA256"
class SignType:
def __init__(self, signParamsString):
self.signtoolParamsTemplate = signParamsString
def getSigntoolParamsTemplate(self):
return self.signtoolParamsTemplate
def getSigntoolParams(self):
raise NotImplementedError(self.getSigntoolParams.__name__)
class KeySigning(SignType):
def __init__(self, key, password):
SignType.__init__(self, signtoolKeySignParams)
self.privateKey = key
self.password = password
def getSigntoolParams(self):
return self.getSigntoolParamsTemplate().format(certificatePath=self.privateKey, password=self.password)
class NameSigning(SignType):
def __init__(self, name):
SignType.__init__(self, signtoolNameSignParams)
self.certName = name
def getSigntoolParams(self):
return self.getSigntoolParamsTemplate().format(certificateName=self.certName)
def getSignToolVerboseCommand(verboseMode):
if verboseMode:
return " /v"
else:
return ""
def buildSignCommand(filename, signingType, verbose):
verboseCmd = getSignToolVerboseCommand(verbose)
signtoolParams = signingType.getSigntoolParams()
signtoolCommand = signtoolCommandBase.format(commandType="sign",
verbose=verboseCmd, commandTypeParams=signtoolParams, filename = filename)
return signtoolCommand
def signtoolTestCredentials(signingType, timestampServer, verbose):
testFileName = "testFile"
if os.path.exists(testFileName):
os.remove(testFileName)
with open(testFileName, "wb") as testSign:
testSign.seek(1023)
testSign.write("0")
# error codes for sign tool are only 0 or 1. No way to get a success without
# a valid .exe to sign, so the only way to test is to try to sign a file
# that will be considered an unrecognized format, and parse the output to
# see if the error was with the password.
signtoolCommand = buildSignCommand(testFileName, signingType, verbose)
sp = subprocess.Popen(signtoolCommand, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
output, error = sp.communicate()
validPassword = False
if error:
# if find returns -1, it means it couldnt find password incorrect text,
# meaning it is a valid password.
validPassword = error.splitlines()[0].lower().find("password is not correct") == -1
os.remove(testFileName)
return validPassword
def signtoolSignFile(filename, signingType, verbose):
signtoolCommand = buildSignCommand(filename, signingType, verbose)
verbose_print(verbose, '\n{}\n'.format(signtoolCommand))
return os.system(signtoolCommand) == 0
def signtoolTimestamp(filename, timestampServer, verbose):
verboseCmd = getSignToolVerboseCommand(verbose)
signtoolParams = signtoolTimestampParams.format(timestampServerURL=timestampServer)
signtoolCommand = signtoolCommandBase.format(commandType="timestamp",
verbose=verboseCmd,
commandTypeParams=signtoolParams,
filename=filename)
verbose_print(verbose, '\n{}\n'.format(signtoolCommand))
success = False
while not success:
success = (os.system(signtoolCommand) == 0)
return success
def signtoolVerifySign(filename, verbose):
verboseCmd = getSignToolVerboseCommand(verbose)
signtoolCommand = signtoolCommandBase.format(commandType="verify",
verbose=verboseCmd,
commandTypeParams=signtoolVerifyParams,
filename=filename)
verbose_print(verbose, '\n{}\n'.format(signtoolCommand))
return_result = os.system(signtoolCommand) == 0
return return_result
def signtoolSignAndVerifyFile(filename,
workingDir,
sourceDir,
signingType,
timestampServer,
verbose):
shouldCopy = workingDir is not sourceDir
filePath = os.path.join(workingDir, filename)
# most of the time that signing fails, it is due to the signing server not responding.
# keep retrying until the signing is successful.
# NOTE: might want to change this to a for loop to limit number of retries per file.
success = False
attemptsMade = 0
while not success:
# SIGN THE FILE
success = signtoolSignFile(filePath, signingType, verbose)
assert success, 'Failed to sign file {}. Most likely the password entered is incorrect.'.format(filename)
# TIMESTAMP THE FILE
success = signtoolTimestamp(filePath, timestampServer, verbose)
assert success, "Failed to contact the timestamp server."
# VERIFY SUCCESSFUL SIGNING
success = signtoolVerifySign(filePath, verbose)
attemptsMade += 1
if not success:
verbose_print(verbose, 'Failed to sign file {}'.format(filename))
if shouldCopy:
# delete the original file, copy back from source, and re-sign
os.remove(filePath)
shutil.copy(os.path.join(sourceDir, filename), filePath)
verbose_print(verbose, 'Attempts made to sign file {}: {}\n'.format(filename, attemptsMade))
return success
def signtoolSignAndVerifyFiles(fileList,
workingDir,
sourceDir,
signingType,
timestampServer,
verbose):
for filename in fileList:
signtoolSignAndVerifyFile(filename,
workingDir,
sourceDir,
signingType,
timestampServer,
verbose)
def signtoolSignAndVerifyType(fileExtension,
workingDir,
sourceDir,
signingType,
timestampServer,
verbose):
# Sign each file in a directory that has the given file extension
for file in os.listdir(workingDir):
if file.endswith(fileExtension):
signtoolSignAndVerifyFile(os.path.basename(file),
workingDir,
sourceDir,
signingType,
timestampServer,
verbose)