AzureCodeSigning.psm1

using namespace System.Collections.Generic
using namespace System.IO

function Invoke-AzureCodeSigning {
    param (
        [Parameter(Mandatory)]
        [string]$Endpoint,

        [Parameter(Mandatory)]
        [string]$CodeSigningAccountName,

        [Parameter(Mandatory)]
        [string]$CertificateProfileName,

        [Parameter()]
        [AllowEmptyString()]
        [string]$FilesFolder,

        [Parameter()]
        [AllowEmptyString()]
        [string]$FilesFolderFilter,

        [Parameter()]
        [switch]$FilesFolderRecurse = $false,

        [Parameter()]
        [AllowNull()]
        [int]$FilesFolderDepth,

        [Parameter()]
        [AllowEmptyString()]
        [string]$FilesCatalog,

        [Parameter(Mandatory)]
        [string]$FileDigest,

        [Parameter()]
        [AllowEmptyString()]
        [string]$TimestampRfc3161,

        [Parameter()]
        [AllowEmptyString()]
        [string]$TimestampDigest,

        [Parameter()]
        [switch]$AppendSignature = $false,

        [Parameter()]
        [AllowEmptyString()]
        [string]$Description,

        [Parameter()]
        [AllowEmptyString()]
        [string]$DescriptionUrl,

        [Parameter()]
        [AllowEmptyString()]
        [string]$GenerateDigestPath,

        [Parameter()]
        [switch]$GenerateDigestXml = $false,

        [Parameter()]
        [AllowEmptyString()]
        [string]$IngestDigestPath,

        [Parameter()]
        [switch]$SignDigest = $false,

        [Parameter()]
        [switch]$GeneratePageHashes = $false,

        [Parameter()]
        [switch]$SuppressPageHashes = $false,

        [Parameter()]
        [switch]$GeneratePkcs7 = $false,

        [Parameter()]
        [AllowEmptyString()]
        [string]$Pkcs7Options,

        [Parameter()]
        [AllowEmptyString()]
        [string]$Pkcs7Oid,

        [Parameter()]
        [AllowEmptyString()]
        [string]$EnhancedKeyUsage,

        [Parameter()]
        [switch]$ExcludeEnvironmentCredential = $false,

        [Parameter()]
        [switch]$ExcludeManagedIdentityCredential = $false,

        [Parameter()]
        [switch]$ExcludeSharedTokenCacheCredential = $false,

        [Parameter()]
        [switch]$ExcludeVisualStudioCredential = $false,

        [Parameter()]
        [switch]$ExcludeVisualStudioCodeCredential = $false,

        [Parameter()]
        [switch]$ExcludeAzureCliCredential = $false,

        [Parameter()]
        [switch]$ExcludeAzurePowerShellCredential = $false,

        [Parameter()]
        [switch]$ExcludeInteractiveBrowserCredential = $false,

        [Parameter()]
        [int]$Timeout = 300,

        [Parameter()]
        [ValidateRange(0, 30000)]
        [int]$BatchSize = 10000
    )

    Write-Warning "This module is being deprecated in favor of TrustedSigning (https://www.powershellgallery.com/packages/TrustedSigning). Please migrate to the new module as soon as possible. All future bug fixes and enhancements will be exclusively released for the new module."

    # Install signtool.exe and the Azure Code Signing binaries.
    $packageSource = Get-NugetV2PackageSource
    $signToolFolderPath = Install-BuildToolsPackage -PackageSource $packageSource
    $dlibFolderPath = Install-AzureCodeSigningPackage -PackageSource $packageSource

    # Create the Azure Code Signing metadata.json file that is passed to signtool.exe.
    $metadataFilePath = Join-Path -Path $dlibFolderPath -ChildPath "metadata.json"
    $convertToMetadataJsonParams = @{
        Endpoint = $Endpoint
        CodeSigningAccountName = $CodeSigningAccountName
        CertificateProfileName = $CertificateProfileName
        ExcludeEnvironmentCredential = $ExcludeEnvironmentCredential
        ExcludeManagedIdentityCredential = $ExcludeManagedIdentityCredential
        ExcludeSharedTokenCacheCredential = $ExcludeSharedTokenCacheCredential
        ExcludeVisualStudioCredential = $ExcludeVisualStudioCredential
        ExcludeVisualStudioCodeCredential = $ExcludeVisualStudioCodeCredential
        ExcludeAzureCliCredential = $ExcludeAzureCliCredential
        ExcludeAzurePowerShellCredential = $ExcludeAzurePowerShellCredential
        ExcludeInteractiveBrowserCredential = $ExcludeInteractiveBrowserCredential
    }
    $metadataJson = ConvertTo-MetadataJson @convertToMetadataJsonParams
    [File]::WriteAllLines($metadataFilePath, $metadataJson)

    # Get the list of files to be signed from the files folder and the catalog file.
    if ([string]::IsNullOrWhiteSpace($FilesFolder)) {
        $filteredFiles = [List[string]]::new()
    } else {
        $getFilteredFileListParams = @{
            Path = $FilesFolder
            Filter = $FilesFolderFilter
            Recurse = $FilesFolderRecurse
            Depth = $FilesFolderDepth
        }
        $filteredFiles = Get-FilteredFileList @getFilteredFileListParams
    }

    if ([string]::IsNullOrWhiteSpace($FilesCatalog)) {
        $catalogFiles = [List[string]]::new()
    } else {
        $catalogFiles = Get-CatalogFileList -Path $FilesCatalog
    }

    $formattedFileList = Format-FileList -FilteredFiles $filteredFiles -CatalogFiles $catalogFiles

    $batchedFileLists = Split-FileList -FormattedFiles $formattedFileList -BatchSize $BatchSize
    if ($batchedFileLists.Count -eq 0) {
        throw "No files were found to sign"
    }

    foreach ($batchedFileList in $batchedFileLists) {
        # Format the arguments that will be passed to signtool.exe.
        $dlibFilePath = Join-Path -Path $dlibFolderPath -ChildPath "Azure.CodeSigning.Dlib.dll"
        $formatSignToolArgumentListParams = @{
            FileList = $batchedFileList
            FileDigest = $FileDigest
            TimestampRfc3161 = $TimestampRfc3161
            TimestampDigest = $TimestampDigest
            DlibFilePath = $dlibFilePath
            MetadataFilePath = $metadataFilePath
            AppendSignature = $AppendSignature
            Description = $Description
            DescriptionUrl = $DescriptionUrl
            GenerateDigestPath = $GenerateDigestPath
            GenerateDigestXml = $GenerateDigestXml
            IngestDigestPath = $IngestDigestPath
            SignDigest = $SignDigest
            GeneratePageHashes = $GeneratePageHashes
            SuppressPageHashes = $SuppressPageHashes
            GeneratePkcs7 = $GeneratePkcs7
            Pkcs7Options = $Pkcs7Options
            Pkcs7Oid = $Pkcs7Oid
            EnhancedKeyUsage = $EnhancedKeyUsage
        }
        $signToolArguments = Format-SignToolArgumentList @formatSignToolArgumentListParams

        # Run signtool.exe.
        $invokeSignToolParams = @{
            SignToolFolderPath = $signToolFolderPath
            SignToolArguments = $signToolArguments
            Timeout = $Timeout
        }
        $result = Invoke-SignTool @invokeSignToolParams

        # Throw an error if signtool.exe failed.
        # Exit code 0 means that signtool.exe completed successfully.
        # Exit code 2 means that signtool.exe completed with warnings.
        if ($result -ne 0 -and $result -ne 2) {
            throw "SignTool failed with exit code $($result)"
        }
    }

    return "Azure Code Signing completed successfully"
}