Skip to content

Commit

Permalink
Added code signing attributes to ConfigFactory and QtIfwPackage. Inte…
Browse files Browse the repository at this point in the history
…grated code signing mechanism into high level processes.
  • Loading branch information
BuvinJ committed Feb 16, 2021
1 parent 9ca5626 commit bff31a9
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 50 deletions.
2 changes: 1 addition & 1 deletion distbuilder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@

from distbuilder.code_sign import \
SelfSignedCertConfig \
, SignToolConfig \
, CodeSignConfig \
, TrustInstallerBuilderProcess \
, trustCertInstallerConfigFactory \
, generateTrustCerts \
Expand Down
49 changes: 25 additions & 24 deletions distbuilder/code_sign.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,9 @@
# WINDOWS CODE SIGNING
#------------------------------------------------------------------------------
SIGNTOOL_PATH_ENV_VAR = "SIGNTOOL_PATH"

class SignToolConfig:

# TODO: Refactor, so as to not be quite so MS SignTool specific
class CodeSignConfig:

DEFAULT_DIGEST = "sha256"
DEFAULT_TIMESTAMP_SERVER = "http://timestamp.digicert.com"
Expand All @@ -27,21 +28,21 @@ class SignToolConfig:
@staticmethod
def _builtInInstallerPath():
return joinPath( _RES_DIR_PATH,
SignToolConfig.__RES_DIR_NAME, SignToolConfig.__MSI_NAME )
CodeSignConfig.__RES_DIR_NAME, CodeSignConfig.__MSI_NAME )

@staticmethod
def _defaultSignToolPath( isVerified=False ):
if IS_ARM_CPU:
subDirName =( SignToolConfig.__ARM_32BIT_DIR
subDirName =( CodeSignConfig.__ARM_32BIT_DIR
if IS_32_BIT_CONTEXT else
SignToolConfig.__ARM_64BIT_DIR )
CodeSignConfig.__ARM_64BIT_DIR )
else:
subDirName =( SignToolConfig.__INTEL_32BIT_DIR
subDirName =( CodeSignConfig.__INTEL_32BIT_DIR
if IS_32_BIT_CONTEXT else
SignToolConfig.__INTEL_64BIT_DIR )
CodeSignConfig.__INTEL_64BIT_DIR )
path = joinPath( util._winProgs86DirPath(),
SignToolConfig.__WINDOWS_KITS_DIR, subDirName,
SignToolConfig.__SIGNTOOL_NAME )
CodeSignConfig.__WINDOWS_KITS_DIR, subDirName,
CodeSignConfig.__SIGNTOOL_NAME )
if isVerified and not isFile( path ): path = None
return path

Expand All @@ -52,17 +53,17 @@ def __init__( self, pfxFilePath=None, keyPassword=None ):

self.signToolPath = None # if None, this will be auto resolved

self.fileDigest = SignToolConfig.DEFAULT_DIGEST
self.timeStampDigest = SignToolConfig.DEFAULT_DIGEST
self.timeStampServerUrl = SignToolConfig.DEFAULT_TIMESTAMP_SERVER
self.fileDigest = CodeSignConfig.DEFAULT_DIGEST
self.timeStampDigest = CodeSignConfig.DEFAULT_DIGEST
self.timeStampServerUrl = CodeSignConfig.DEFAULT_TIMESTAMP_SERVER
self.otherSignToolArgs = ""

self.isDebugMode = True

def __str__( self ) :
if not isFile( self.pfxFilePath ):
raise Exception(
"Missing or invalid pfx path in SignToolConfig: %s" %
"Missing or invalid pfx path in CodeSignConfig: %s" %
(self.pfxFilePath,) )
operation = "sign"
verbose = '/v' if self.isDebugMode else ''
Expand All @@ -77,34 +78,34 @@ def __str__( self ) :
self.otherSignToolArgs)
return ' '.join( (('%s ' * len(tokens)) % tokens).split() )

def __useSignTool( exePath, signToolConfig ):
__validateSignToolConfig( signToolConfig )
cmd = '"%s" %s "%s"' % ( signToolConfig.signToolPath,
str(signToolConfig), exePath )
def __useSignTool( exePath, codeSignConfig ):
__validateCodeSignConfig( codeSignConfig )
cmd = '"%s" %s "%s"' % ( codeSignConfig.signToolPath,
str(codeSignConfig), exePath )
if not util._isSystemSuccess( cmd ):
raise Exception( 'FAILED to code sign "%s"' % (exePath,) )
print( "Signed successfully!" )
return exePath

def __validateSignToolConfig( cfg ):
def __validateCodeSignConfig( cfg ):
if not isFile( cfg.pfxFilePath ):
raise Exception( "Missing or invalid PFX file path: %s" %
(cfg.pfxFilePath,) )
if cfg.signToolPath is None:
cfg.signToolPath = getenv( SIGNTOOL_PATH_ENV_VAR )
if cfg.signToolPath is None:
cfg.signToolPath = (
SignToolConfig._defaultSignToolPath( isVerified=True ) )
CodeSignConfig._defaultSignToolPath( isVerified=True ) )
if cfg.signToolPath is None:
cfg.signToolPath = __installSignTool()
if cfg.signToolPath is None:
raise Exception( "Valid SignTool path required" )

def __installSignTool():
print( "Installing SignTool utility...\n" )
if not util._isSystemSuccess( SignToolConfig._builtInInstallerPath() ):
if not util._isSystemSuccess( CodeSignConfig._builtInInstallerPath() ):
raise Exception( "SignTool installation FAILED" )
return SignToolConfig._defaultSignToolPath( isVerified=True )
return CodeSignConfig._defaultSignToolPath( isVerified=True )

#------------------------------------------------------------------------------
MAKECERT_PATH_ENV_VAR = "MAKECERT_PATH"
Expand Down Expand Up @@ -512,15 +513,15 @@ def onPyInstConfig( self, cfg ):
def onFinalize( self ):
self.configFactory._trustCertScript.remove()
# sign the installer itself
signConfig = SignToolConfig(
signConfig = CodeSignConfig(
pfxFilePath=absPath( self.configFactory._keyFilePath ),
keyPassword=self.configFactory._keyPassword )
signExe( self.binPath, signConfig )

def signExe( exePath, signToolConfig ):
def signExe( exePath, codeSignConfig ):
exePath = normBinaryName( exePath, isPathPreserved=True )
print( "Code signing %s...\n" % (exePath,) )
if IS_WINDOWS: return __useSignTool( exePath, signToolConfig )
if IS_WINDOWS: return __useSignTool( exePath, codeSignConfig )
#TODO: SUPPORT OTHER PLATFORMS!!!
util._onPlatformErr()

56 changes: 46 additions & 10 deletions distbuilder/master.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,15 @@
, QT_IFW_TARGET_DIR \
, _SILENT_FORCED_ARGS \
, _LOUD_FORCED_ARGS

# work around for cross dependency
__signExe = None
def signExe( exePath, codeSignConfig ):
global __signExe
if __signExe is None:
from distbuilder.code_sign import signExe
__signExe = signExe
return __signExe( exePath, codeSignConfig )

# -----------------------------------------------------------------------------
class ConfigFactory:
Expand Down Expand Up @@ -108,14 +117,18 @@ def __init__( self, cfgId=None ) :
self.ifwPkgScriptPath = None
self.ifwPkgScriptName = DEFAULT_QT_IFW_SCRIPT_NAME

self.pkgType = None
self.pkgSubDirName = None
self.pkgSrcDirPath = None
self.pkgSrcExePath = None
self.pkgExeWrapper = None
self.pkgType = None
self.pkgSubDirName = None
self.pkgSrcDirPath = None
self.pkgSrcExePath = None
self.pkgExeWrapper = None
self.pkgCodeSignTargets = None

self.startOnBoot = False

# code signing
self.codeSignConfig = None

# Configurations for specific package types
self.__pkgPyInstConfig = None
self.qtCppConfig = None
Expand Down Expand Up @@ -223,7 +236,7 @@ def qtIfwPackage( self, pyInstConfig=None, isTempSrc=False ):

pkg.exeName = self.__pkgExeName()
pkg.isGui = self.isGui

pkg.exeWrapper = self.pkgExeWrapper
if( IS_WINDOWS and
isinstance( pkg.exeWrapper, QtIfwExeWrapper ) and
Expand Down Expand Up @@ -254,8 +267,19 @@ def qtIfwPackage( self, pyInstConfig=None, isTempSrc=False ):
pkg.distResources = list( set().union(
pkg.distResources if pkg.distResources else [],
self.distResources if self.distResources else [] ) )

pkg.qtCppConfig = self.qtCppConfig

if self.codeSignConfig:
if pkgType != QtIfwPackage.Type.PY_INSTALLER:
try: self.pkgCodeSignTargets.append( pkg.exeName )
except: self.pkgCodeSignTargets=[ pkg.exeName ]
if( isinstance( pkg.exeWrapper, QtIfwExeWrapper ) and
pkg.exeWrapper.isExe ):
exeName = pkg.exeWrapper.wrapperExeName
try: self.pkgCodeSignTargets.append( exeName )
except: self.pkgCodeSignTargets=[ exeName ]
pkg.codeSignTargets = self.pkgCodeSignTargets

pkg.qtCppConfig = self.qtCppConfig
return pkg

def qtIfwPackageXml( self ) :
Expand Down Expand Up @@ -507,6 +531,9 @@ def _body( self ):
buildExecutable( pyInstConfig=self._pyInstConfig,
opyConfig=opyConfig ) )

if self.configFactory.codeSignConfig :
signExe( self.binPath, self.configFactory.codeSignConfig )

destDirPath =( util._userDesktopDirPath() if self.isDesktopTarget else
util._userHomeDirPath() if self.isHomeDirTarget else
None )
Expand All @@ -522,7 +549,7 @@ def _body( self ):
if destDirPath and self.binDir != destDirPath:
binName = baseFileName( self.binPath )
isOtherContent = len( [item for item in listdir( self.binDir )
if item != binName ] ) > 0
if item != binName] ) > 0
if isOtherContent:
self.binDir = moveToDir( self.binDir, destDirPath )
self.binPath = joinPath( self.binDir, binName )
Expand Down Expand Up @@ -568,7 +595,6 @@ def __init__( self, configFactory,
self.setupPath = None

def _body( self ):

for p in self.pyToBinPkgProcesses :
p.run()
self.ifwPackages.append(
Expand All @@ -582,10 +608,20 @@ def _body( self ):

_stageInstallerPackages( ifwConfig, self.configFactory.isSilentSetup )
self.onPackagesStaged( ifwConfig, ifwConfig.packages )

if self.configFactory.codeSignConfig:
for pkg in self.ifwPackages:
if pkg.codeSignTargets:
for relPath in pkg.codeSignTargets:
exePath = absPath( relPath, pkg.contentDirPath )
signExe( exePath, self.configFactory.codeSignConfig )

self.setupPath = _buildInstaller(
ifwConfig, self.configFactory.isSilentSetup )

if self.configFactory.codeSignConfig :
signExe( self.setupPath, self.configFactory.codeSignConfig )

if self.isDesktopTarget :
self.setupPath = moveToDesktop( self.setupPath )
elif self.isHomeDirTarget :
Expand Down
19 changes: 12 additions & 7 deletions distbuilder/qt_installer.py
Original file line number Diff line number Diff line change
Expand Up @@ -606,12 +606,13 @@ def __init__( self, pkgId=None, pkgType=None, name=None,
self.distResources = None
self.isTempSrc = isTempSrc
# extended content detail
self.subDirName = subDirName
self.exeName = None
self.isGui = False
self.exeWrapper = None # class QtIfwExeWrapper
self.qtCppConfig = None

self.subDirName = subDirName
self.exeName = None
self.isGui = False
self.exeWrapper = None # class QtIfwExeWrapper
self.codeSignTargets = None # list of relative paths within package
self.qtCppConfig = None

self._isMergeProduct = False

def dirPath( self ) :
Expand Down Expand Up @@ -7410,7 +7411,11 @@ def __mergePackageObjects( srcPkg, destPkg, subDirName=None ):
for i, _ in enumerate( srcShortcuts ):
srcShortcuts[i].exeDir = joinPathQtIfw(
QT_IFW_TARGET_DIR, subDirName )
except: srcShortcuts = []
except: srcShortcuts = []
if srcPkg.pkgCodeSignTargets:
try: destPkg.pkgCodeSignTargets.extend( srcPkg.pkgCodeSignTargets )
except: destPkg.pkgCodeSignTargets = srcPkg.pkgCodeSignTargets
destPkg.pkgCodeSignTargets = list(set( destPkg.pkgCodeSignTargets ))
if destPkg.pkgScript:
if srcShortcuts:
try: destPkg.pkgScript.shortcuts.extend( srcShortcuts )
Expand Down
26 changes: 26 additions & 0 deletions examples/hello_code_signing/signedInstaller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from distbuilder import( PyToBinInstallerProcess, ConfigFactory,
CodeSignConfig, getPassword)

f = configFactory = ConfigFactory()
f.productName = "Hello World Tk Example"
f.description = "A Distribution Builder Example"
f.companyTradeName = "Some Company"
f.companyLegalName = "Some Company Inc."
f.binaryName = "HelloWorldTk"
f.isGui = True
f.entryPointPy = "../hello_world_tk/hello.py"
f.iconFilePath = "../hello_world_tk/demo.ico"
f.version = (1,0,0,0)
f.setupName = "HelloSignedSetup"

# You must run the generateTrustCerts example first, to have the PFX file
# referenced below.
# Supply the PFX password using one of the following options shown.
f.codeSignConfig = CodeSignConfig( pfxFilePath="./certs/SomeCompany.pfx",
keyPassword=getPassword( isGuiPrompt=True ) )
#keyPassword=None )
#keyPassword="my-secure-password" )

p = PyToBinInstallerProcess( configFactory, isDesktopTarget=True )
p.isAutoInstallTest = True
p.run()
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from distbuilder import PyToBinPackageProcess, ConfigFactory, \
SignToolConfig, signExe, getPassword
from distbuilder import( PyToBinPackageProcess, ConfigFactory,
CodeSignConfig, getPassword )

f = configFactory = ConfigFactory()
f.productName = "Hello World Example"
Expand All @@ -8,15 +8,13 @@
f.binaryName = "HelloWorld"
f.version = (1,0,0,0)
f.entryPointPy = "../hello_world/hello.py"

p = PyToBinPackageProcess( configFactory )
p.run()

# You must run the generateTrustCerts example first, to have the PFX file
# referenced below.
# Supply the PFX password using one of the following options shown.
signConfig = SignToolConfig( pfxFilePath="./certs/SomeCompany.pfx",
f.codeSignConfig = CodeSignConfig( pfxFilePath="./certs/SomeCompany.pfx",
keyPassword=getPassword( isGuiPrompt=True ) )
#keyPassword=None )
#keyPassword="my-secure-password" )
signExe( p.binPath, signConfig )

p = PyToBinPackageProcess( configFactory )
p.run()

0 comments on commit bff31a9

Please sign in to comment.