<#
.SYNOPSIS
    
Provides operations and functions for the retrieval of KeyVault Keys and Passwords as
well as some other utilities for manipulating the data that is returned.

.DESCRIPTION

Provides a common module for Unified Operations Setup to retrieve and manipulate Azure Secret/Keys.

.PARAMETER VaultName

The Name of the KeyVault you want to retrieve the key from. Azure specifies that a 
KeyVault name must match this regex "^[a-zA-Z0-9-]{3,24}$".

.PARAMETER ApplicationID

The ApplicationID of the ServicePrincipal we are going to use to authenticate with.

.PARAMETER TenantID

The TenantID guid that Application is under.

.PARAMETER CertificateThumprint

The Thumprint of the certificate that is associated with the Application.

.EXAMPLE

C:\PS> Import-Module ".\KeyVault.psm1" -ArgumentList ("<KeyVaultName>", "<ApplicationID>", "<TenantID>", "<Thumbprint>")

#>
[CmdletBinding()]
param
(
    [Parameter(Mandatory=$False, Position = 0)]
    [System.String]
    $VaultName,

    [Parameter(Mandatory=$False, Position = 1)]
    [System.String]
    $ApplicationID,

    [Parameter(Mandatory=$False, Position = 2)]
    [System.String]
    $CertificateThumprint
)

Import-Module "$PSScriptRoot\AosCommon.psm1" -Force -DisableNameChecking

if (![System.String]::IsNullOrWhiteSpace($VaultName))
{
    $Script:VaultName = $VaultName
}
else
{
    $Script:VaultName = "default"
}

if (![System.String]::IsNullOrWhiteSpace($ApplicationID) -and 
    ![System.String]::IsNullOrWhiteSpace($CertificateThumprint))
{
    $Script:ApplicationID = $ApplicationID
    $Script:CertificateThumprint = $CertificateThumprint 
    Import-Module "$PSScriptRoot\DynamicsVault.psm1" -Force -DisableNameChecking
}

function Get-KeyVaultKey
{
    <#
    .SYNOPSIS
    
    Retrieves a specified Key from a KeyVault.

    .DESCRIPTION

    Retrieves a specified Key from KeyVault using the Azure PowerShell Modules and wraps
    the function up to provide an easier call. 

    .PARAMETER Name

    The Name of the Key in the selected vault we want to retrieve.

    .PARAMETER Version

    The Version of the Key we want, this is an optional field. The default Version
    is the latest one in KeyVault.

    .PARAMETER VaultName

    The Name of the KeyVault you want to retrieve the key from. This parameter is optional,
    it will default to the $Script:VaultName parameter. Azure specifies that a KeyVault name
    must match this regex "^[a-zA-Z0-9-]{3,24}$".

    .EXAMPLE

    Retrieves the latest version of the Key.
    
    C:\PS> Get-KeyVaultKey "KeyName"

    .EXAMPLE
    
    Retrieves the specific version of the Key.

    C:\PS> Get-KeyVaultKey "KeyName" "KeyVersion"

    .EXAMPLE

    Retrieves the latest version of the Key from a different KeyVault than what was passed
    from the script parameters.

    C:\PS> Get-KeyVaultKey -Name "KeyName" -VaultName "VaultName"
    #>
    param
    (
        [Parameter(Mandatory=$True, Position = 0)]
        [ValidateLength(1,127)]
        [System.String]
        $Name,

        [Parameter(ParameterSetName = "Version", Mandatory=$False, Position = 1)]
        [System.String]
        $Version,

        [Parameter(Mandatory=$False, Position = 2)]
        [ValidatePattern("^[a-zA-Z0-9-]{3,24}$")]
        [System.String]
        $VaultName = $Script:VaultName
    )


    switch ($PsCmdlet.ParameterSetName)
    {
        "Version" 
        { 
            $keyInformation = Get-AzureKeyVaultKey -VaultName $VaultName -Name $Name -Version $Version
        }
        default
        {
            $keyInformation = Get-AzureKeyVaultKey -VaultName $VaultName -Name $Name
        }
    }
    return $keyInformation
}

function Get-KeyVaultSecret
{
    <#
    .SYNOPSIS
    
    Retrieves a specified secret from a KeyVault.

    .DESCRIPTION

    Retrieves a specified secret from KeyVault using the Azure PowerShell Modules and wraps
    the function up to provide an easier call. 

    .PARAMETER VaultUri

    The Name of the secret in the selected vault we want to retrieve. The assumption is the
    URI is formatted as such "Vault://SecretName/SecretVersion", if it does not match this
    pattern we assume the secret is the VaultUri parameter.

    .PARAMETER VaultName

    The Name of the KeyVault you want to retrieve the secret from. This parameter is optional,
    it will default to the $Script:VaultName parameter. Azure specifies that a KeyVault name
    must match this regex "^[a-zA-Z0-9-]{3,24}$".

    .EXAMPLE

    Retrieves the latest version of the secret.
    
    C:\PS> Get-KeyVaultSecret "VaultUri"

    .EXAMPLE

    Retrieves the latest version of the secret from a different KeyVault than what was passed
    from the script parameters.

    C:\PS> Get-KeyVaultSecret -VaultUri "Vault://SecretName/SecretVersion" -VaultName "VaultName"
    #>
    param
    (
        [Parameter(Mandatory=$True, Position = 0)]
        [System.String]
        $VaultUri,

        [Parameter(Mandatory=$False, Position = 1)]
        [ValidatePattern("^[a-zA-Z0-9-]{3,24}$")]
        [System.String]
        $VaultName = $Script:VaultName
    )

    if (!(Test-ValidKeyVaultUri -VaultUri $VaultUri))
    {
        return $VaultUri
    }
    else
    {
        [System.Uri]$Uri = [System.Uri]::new($VaultUri)

        $secretName = $Uri.Host

        if ($Uri.Segments.Count -ge 2)
        {
            $secret = Get-VaultCredential -ClientId $Script:ApplicationID -CertificateThumbprint $Script:CertificateThumprint -VaultName $VaultName -SecretName $secretName -Version $Uri.Segments[1]
        }
        else
        {
            $secret = Get-VaultCredential -ClientId $Script:ApplicationID -CertificateThumbprint $Script:CertificateThumprint -VaultName $VaultName -SecretName $secretName
        }

        return $secret
    }
}

function Get-CertificatefromBase64String
{
    <#
    .SYNOPSIS

    Takes in a Base64 String representation of a Private Certificate (PFX) and the
    password associated with it and loads the certificate into a X509Certificate2 object.

    .DESCRIPTION

    Upon receiving the certificate information from KeyVault this allows us to convert
    the Base64 string and the password and load a certificate into a X509Certificate2 object.

    .PARAMETER Base64String

    The Base64 string representation of the PFX file.

    .PARAMETER CertificatePassword

    The password of the PFX Certificate.

    .EXAMPLE

    C:\PS> Create-CertificatefromBase64String "<Base64String>" "<Password>"

    #>
    param
    (
        [Parameter(Mandatory=$True, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $Base64String,

        [Parameter(Mandatory=$False, Position = 1)]
        [System.Security.SecureString]
        $CertificatePassword
    )

    $certificateBytes = [System.Convert]::FromBase64String($Base64String)

    if ($CertificatePassword -ne $null)
    { 
        return [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertificateBytes, $CertificatePassword)       
    }
    else
    {
        return [System.Security.Cryptography.X509Certificates.X509Certificate2]::new($CertificateBytes)
    }
}

function Get-CertificateFromJsonString
{
    <#
    .SYNOPSIS

    Takes in a JSON String representation of a Certificate and attributes 
    and loads the certificate in memory.

    .DESCRIPTION

    This functions makes the assumption that the JSON file is formatted like this.

    {
        "data": "<Base64String>",
        "dataType" :"pfx",
        "password": "<CertificatePassword>"
    }

    This is following the same format that the ARM Templates expect Certificates for 
    VM Deployments.

    .PARAMETER JsonString

    The Json String string representation of the Certificate file and its attributes.

    .PARAMETER CertificateType

    This is a reference object so that the caller of the function can determine the
    type of the certificate for example (Public or Private key).

    .EXAMPLE

    C:\PS> [System.String]$certificateType = [System.String]::Empty
    C:\PS> $jsonObject = @"
    C:\PS> {
    C:\PS> "data": "<Base64EncodedCert>",
    C:\PS> "dataType" :"pfx",
    C:\PS> "password": "<CertificatePassword>"
    C:\PS> }
    C:\PS> Get-CertificateFromJsonString $jsonObject ([ref]$certificateType)

    #>
    param
    (
        [Parameter(Mandatory=$True, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $JsonString,

        [Parameter(Mandatory=$True, Position = 1)]
        [Ref]
        $CertificateType
    )

    $jsonObject = $JsonString | ConvertFrom-Json

    $CertificateType.Value = $jsonObject.dataType
    [System.Security.SecureString]$certificatePassword = $null

    if (![System.String]::IsNullOrWhiteSpace($jsonObject.password))
    {
        $certificatePassword = ConvertTo-SecureString $jsonObject.password -AsPlainText -Force
    }

    $certificate = Get-CertificatefromBase64String -Base64String $jsonObject.data -CertificatePassword $certificatePassword

    return $certificate
}

function Get-CertificateFromVault
{
    <#
    .SYNOPSIS

    Takes in two Vault Uri's. One is for the Certificate file (base64 encoded) and the
    second the password for the Certificate (Optional).

    .DESCRIPTION

    Once the information for the certificate has been retrieved we take the information and 
    create a x509Certificate2 object to be consumed.

    .PARAMETER CertUri

    The Uri of the certificate file we want.

    .PARAMETER CertPasswordUri

    The Uri of the certificate password that corresponds to the Certificate

    .PARAMETER VaultName

    The name of the Vault to retrieve the secret information.

    .EXAMPLE

    # Uses default VaultName
    C:\PS> Get-CertificateFromVault "vault://certSecretName" "vault://certSecretPasswordName"

    # overrides default VaultName
    C:\PS> Get-CertificateFromVault "vault://certSecretName" "vault://certSecretPasswordName" "VaultName"

    #>
    param
    (
        [Parameter(Mandatory=$True, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $CertUri,

        [Parameter(Mandatory=$false, Position = 1)]
        [System.String]
        $CertPasswordUri,

        [Parameter(Mandatory=$False, Position = 2)]
        [System.String]
        $VaultName = $Script:VaultName
    )

    $base64Cert = Get-KeyVaultSecret -VaultUri $CertUri -VaultName $VaultName

    if (![System.String]::IsNullOrWhiteSpace($CertPasswordUri))
    {
        $certificatePassword = ConvertTo-SecureString -String $(Get-KeyVaultSecret -VaultUri $CertPasswordUri -VaultName $VaultName) -AsPlainText -Force
    }

    return Get-CertificatefromBase64String -Base64String $base64Cert -CertificatePassword $certificatePassword
}

function Get-Base64UTF8String
{
    <#
    .SYNOPSIS

    Takes in a UTF8 Encoded Base64 String and converts it to native string.

    .DESCRIPTION

    This is a helper function to unwrap the Private Key that is stored for the
    ARM Template deployment.

    Azure requires this JSON Structure to be wrapped like this 

    $JsonBlob = @{
        "data": "<Base64String>",
        "dataType" :"pfx",
        "password": "<CertificatePassword>"
    }@

    $ContentBytes = [System.Text.Encoding]::UTF8.GetBytes($JSONBlob)
    $Content = [System.Convert]::ToBase64String($ContentBytes)

    This will unwrap the JSON File.

    .PARAMETER Base64String

    The UTF8 Base64String Object to unwrap.

    .EXAMPLE

    C:\PS> Read-Base64UTF8String <UTF8 Encoded Base64String>

    #>
    param
    (
        [Parameter(Mandatory=$True, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $Base64String
    )

    $ReceivedBytes = [System.Convert]::FromBase64String($Base64String)

    return [System.Text.Encoding]::UTF8.GetString($ReceivedBytes)
}

function Create-FilefromBase64String
{
    <#
    .SYNOPSIS
    
    Takes in a Base64 String representation of a File and
    Creates a file to the specified path.
    
    .DESCRIPTION

    Takes in a Base64 String representation of a File and
    Creates a file to the specified path.

    .PARAMETER Base64String

    The Base64 string representation of the PFX file. 

    .PARAMETER DestinationPath

    The Path of the file we want to save too. This includes the path and 
    file name i.e. (C:\Temp\Cert.pfx)

    .EXAMPLE

    C:\PS> Create-FilefromBase64String "<Base64String>" "C:\Temp\Cert.pfx"

    #>
    param
    (
        [Parameter(Mandatory=$True, Position = 0)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $Base64String,

        [Parameter(Mandatory=$True, Position = 1)]
        [ValidateNotNullOrEmpty()]
        [System.String]
        $DestinationPath
    )
    
    $pfxBytes = [System.Convert]::FromBase64String($Base64String)
    
    [System.IO.File]::WriteAllBytes($DestinationPath, $pfxBytes)
}

function Test-ValidKeyVaultUri
{
    param
    (
        [Parameter(Mandatory=$True, Position = 0)]
        [System.String]
        $VaultUri
    )

    if (![System.Uri]::IsWellFormedUriString($VaultUri, [System.UriKind]::Absolute))
    {
        return $false
    }

    [System.Uri]$Uri = [System.Uri]::new($VaultUri)

    if ($Uri.Scheme -ne "vault")
    {
        return $false
    }

    return $true
}
# SIG # Begin signature block
# MIIoRgYJKoZIhvcNAQcCoIIoNzCCKDMCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCCYIEPfj2Yy48kJ
# gWEKNntz2Qhws7AgUPUErSkZHt+v0qCCDXYwggX0MIID3KADAgECAhMzAAAEBGx0
# Bv9XKydyAAAAAAQEMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjQwOTEyMjAxMTE0WhcNMjUwOTExMjAxMTE0WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQC0KDfaY50MDqsEGdlIzDHBd6CqIMRQWW9Af1LHDDTuFjfDsvna0nEuDSYJmNyz
# NB10jpbg0lhvkT1AzfX2TLITSXwS8D+mBzGCWMM/wTpciWBV/pbjSazbzoKvRrNo
# DV/u9omOM2Eawyo5JJJdNkM2d8qzkQ0bRuRd4HarmGunSouyb9NY7egWN5E5lUc3
# a2AROzAdHdYpObpCOdeAY2P5XqtJkk79aROpzw16wCjdSn8qMzCBzR7rvH2WVkvF
# HLIxZQET1yhPb6lRmpgBQNnzidHV2Ocxjc8wNiIDzgbDkmlx54QPfw7RwQi8p1fy
# 4byhBrTjv568x8NGv3gwb0RbAgMBAAGjggFzMIIBbzAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQU8huhNbETDU+ZWllL4DNMPCijEU4w
# RQYDVR0RBD4wPKQ6MDgxHjAcBgNVBAsTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEW
# MBQGA1UEBRMNMjMwMDEyKzUwMjkyMzAfBgNVHSMEGDAWgBRIbmTlUAXTgqoXNzci
# tW2oynUClTBUBgNVHR8ETTBLMEmgR6BFhkNodHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpb3BzL2NybC9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3JsMGEG
# CCsGAQUFBwEBBFUwUzBRBggrBgEFBQcwAoZFaHR0cDovL3d3dy5taWNyb3NvZnQu
# Y29tL3BraW9wcy9jZXJ0cy9NaWNDb2RTaWdQQ0EyMDExXzIwMTEtMDctMDguY3J0
# MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggIBAIjmD9IpQVvfB1QehvpC
# Ge7QeTQkKQ7j3bmDMjwSqFL4ri6ae9IFTdpywn5smmtSIyKYDn3/nHtaEn0X1NBj
# L5oP0BjAy1sqxD+uy35B+V8wv5GrxhMDJP8l2QjLtH/UglSTIhLqyt8bUAqVfyfp
# h4COMRvwwjTvChtCnUXXACuCXYHWalOoc0OU2oGN+mPJIJJxaNQc1sjBsMbGIWv3
# cmgSHkCEmrMv7yaidpePt6V+yPMik+eXw3IfZ5eNOiNgL1rZzgSJfTnvUqiaEQ0X
# dG1HbkDv9fv6CTq6m4Ty3IzLiwGSXYxRIXTxT4TYs5VxHy2uFjFXWVSL0J2ARTYL
# E4Oyl1wXDF1PX4bxg1yDMfKPHcE1Ijic5lx1KdK1SkaEJdto4hd++05J9Bf9TAmi
# u6EK6C9Oe5vRadroJCK26uCUI4zIjL/qG7mswW+qT0CW0gnR9JHkXCWNbo8ccMk1
# sJatmRoSAifbgzaYbUz8+lv+IXy5GFuAmLnNbGjacB3IMGpa+lbFgih57/fIhamq
# 5VhxgaEmn/UjWyr+cPiAFWuTVIpfsOjbEAww75wURNM1Imp9NJKye1O24EspEHmb
# DmqCUcq7NqkOKIG4PVm3hDDED/WQpzJDkvu4FrIbvyTGVU01vKsg4UfcdiZ0fQ+/
# V0hf8yrtq9CkB8iIuk5bBxuPMIIHejCCBWKgAwIBAgIKYQ6Q0gAAAAAAAzANBgkq
# hkiG9w0BAQsFADCBiDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24x
# EDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlv
# bjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
# IDIwMTEwHhcNMTEwNzA4MjA1OTA5WhcNMjYwNzA4MjEwOTA5WjB+MQswCQYDVQQG
# EwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwG
# A1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSgwJgYDVQQDEx9NaWNyb3NvZnQg
# Q29kZSBTaWduaW5nIFBDQSAyMDExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC
# CgKCAgEAq/D6chAcLq3YbqqCEE00uvK2WCGfQhsqa+laUKq4BjgaBEm6f8MMHt03
# a8YS2AvwOMKZBrDIOdUBFDFC04kNeWSHfpRgJGyvnkmc6Whe0t+bU7IKLMOv2akr
# rnoJr9eWWcpgGgXpZnboMlImEi/nqwhQz7NEt13YxC4Ddato88tt8zpcoRb0Rrrg
# OGSsbmQ1eKagYw8t00CT+OPeBw3VXHmlSSnnDb6gE3e+lD3v++MrWhAfTVYoonpy
# 4BI6t0le2O3tQ5GD2Xuye4Yb2T6xjF3oiU+EGvKhL1nkkDstrjNYxbc+/jLTswM9
# sbKvkjh+0p2ALPVOVpEhNSXDOW5kf1O6nA+tGSOEy/S6A4aN91/w0FK/jJSHvMAh
# dCVfGCi2zCcoOCWYOUo2z3yxkq4cI6epZuxhH2rhKEmdX4jiJV3TIUs+UsS1Vz8k
# A/DRelsv1SPjcF0PUUZ3s/gA4bysAoJf28AVs70b1FVL5zmhD+kjSbwYuER8ReTB
# w3J64HLnJN+/RpnF78IcV9uDjexNSTCnq47f7Fufr/zdsGbiwZeBe+3W7UvnSSmn
# Eyimp31ngOaKYnhfsi+E11ecXL93KCjx7W3DKI8sj0A3T8HhhUSJxAlMxdSlQy90
# lfdu+HggWCwTXWCVmj5PM4TasIgX3p5O9JawvEagbJjS4NaIjAsCAwEAAaOCAe0w
# ggHpMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBRIbmTlUAXTgqoXNzcitW2o
# ynUClTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYwDwYD
# VR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBRyLToCMZBDuRQFTuHqp8cx0SOJNDBa
# BgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2Ny
# bC9wcm9kdWN0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3JsMF4GCCsG
# AQUFBwEBBFIwUDBOBggrBgEFBQcwAoZCaHR0cDovL3d3dy5taWNyb3NvZnQuY29t
# L3BraS9jZXJ0cy9NaWNSb29DZXJBdXQyMDExXzIwMTFfMDNfMjIuY3J0MIGfBgNV
# HSAEgZcwgZQwgZEGCSsGAQQBgjcuAzCBgzA/BggrBgEFBQcCARYzaHR0cDovL3d3
# dy5taWNyb3NvZnQuY29tL3BraW9wcy9kb2NzL3ByaW1hcnljcHMuaHRtMEAGCCsG
# AQUFBwICMDQeMiAdAEwAZQBnAGEAbABfAHAAbwBsAGkAYwB5AF8AcwB0AGEAdABl
# AG0AZQBuAHQALiAdMA0GCSqGSIb3DQEBCwUAA4ICAQBn8oalmOBUeRou09h0ZyKb
# C5YR4WOSmUKWfdJ5DJDBZV8uLD74w3LRbYP+vj/oCso7v0epo/Np22O/IjWll11l
# hJB9i0ZQVdgMknzSGksc8zxCi1LQsP1r4z4HLimb5j0bpdS1HXeUOeLpZMlEPXh6
# I/MTfaaQdION9MsmAkYqwooQu6SpBQyb7Wj6aC6VoCo/KmtYSWMfCWluWpiW5IP0
# wI/zRive/DvQvTXvbiWu5a8n7dDd8w6vmSiXmE0OPQvyCInWH8MyGOLwxS3OW560
# STkKxgrCxq2u5bLZ2xWIUUVYODJxJxp/sfQn+N4sOiBpmLJZiWhub6e3dMNABQam
# ASooPoI/E01mC8CzTfXhj38cbxV9Rad25UAqZaPDXVJihsMdYzaXht/a8/jyFqGa
# J+HNpZfQ7l1jQeNbB5yHPgZ3BtEGsXUfFL5hYbXw3MYbBL7fQccOKO7eZS/sl/ah
# XJbYANahRr1Z85elCUtIEJmAH9AAKcWxm6U/RXceNcbSoqKfenoi+kiVH6v7RyOA
# 9Z74v2u3S5fi63V4GuzqN5l5GEv/1rMjaHXmr/r8i+sLgOppO6/8MO0ETI7f33Vt
# Y5E90Z1WTk+/gFcioXgRMiF670EKsT/7qMykXcGhiJtXcVZOSEXAQsmbdlsKgEhr
# /Xmfwb1tbWrJUnMTDXpQzTGCGiYwghoiAgEBMIGVMH4xCzAJBgNVBAYTAlVTMRMw
# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN
# aWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNp
# Z25pbmcgUENBIDIwMTECEzMAAAQEbHQG/1crJ3IAAAAABAQwDQYJYIZIAWUDBAIB
# BQCgga4wGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEO
# MAwGCisGAQQBgjcCARUwLwYJKoZIhvcNAQkEMSIEILxvlgb5ANeIjvZsgvMjlnNE
# fIzVfFWaawyJ/VKEMb0gMEIGCisGAQQBgjcCAQwxNDAyoBSAEgBNAGkAYwByAG8A
# cwBvAGYAdKEagBhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20wDQYJKoZIhvcNAQEB
# BQAEggEAq2urKR1vbe1qAc9XLC5PMBIT3DZYIarzXJjUbhtfnxEH5sNu5HJkXZX0
# yw0a41zT3XHDGYKYQd+73wltO+6a4bMY5CbFpv/gNZuEPK+adV6eyxX9FgI0AVZu
# KZCVsQGH6PNWEbzLS3X/TNYdNdqudyU3qeireoGGihvzErNBRU99VMzWETgYH6h9
# jNOONq7wckmL4G5k4Uuk26/tAjWCdlNgqYuGgstYkLi2ARV7HXFO7XYU3dm1YcFq
# VN7a7rmFniL/D2h6+EHKcDySkcInUsHqocA77fxebSJ8k/0j3HMuBTl7UuMzNZOy
# zTUe37UmoOSGX+7eL2YB3Zz38DEwZqGCF7AwghesBgorBgEEAYI3AwMBMYIXnDCC
# F5gGCSqGSIb3DQEHAqCCF4kwgheFAgEDMQ8wDQYJYIZIAWUDBAIBBQAwggFaBgsq
# hkiG9w0BCRABBKCCAUkEggFFMIIBQQIBAQYKKwYBBAGEWQoDATAxMA0GCWCGSAFl
# AwQCAQUABCBuNy/Cwi4nfFrem71J2i2mf+HChUIp6dO5GUUaHMGUgQIGZ0n8YjsH
# GBMyMDI0MTIxMjA3MjEwNS41NzZaMASAAgH0oIHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# TjoyQTFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaCCEf4wggcoMIIFEKADAgECAhMzAAAB+R9njXWrpPGxAAEAAAH5MA0G
# CSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9u
# MRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRp
# b24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMB4XDTI0
# MDcyNTE4MzEwOVoXDTI1MTAyMjE4MzEwOVowgdMxCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xLTArBgNVBAsTJE1pY3Jvc29mdCBJcmVsYW5kIE9w
# ZXJhdGlvbnMgTGltaXRlZDEnMCUGA1UECxMeblNoaWVsZCBUU1MgRVNOOjJBMUEt
# MDVFMC1EOTQ3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBTZXJ2aWNl
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtD1MH3yAHWHNVslC+CBT
# j/Mpd55LDPtQrhN7WeqFhReC9xKXSjobW1ZHzHU8V2BOJUiYg7fDJ2AxGVGyovUt
# gGZg2+GauFKk3ZjjsLSsqehYIsUQrgX+r/VATaW8/ONWy6lOyGZwZpxfV2EX4qAh
# 6mb2hadAuvdbRl1QK1tfBlR3fdeCBQG+ybz9JFZ45LN2ps8Nc1xr41N8Qi3KVJLY
# X0ibEbAkksR4bbszCzvY+vdSrjWyKAjR6YgYhaBaDxE2KDJ2sQRFFF/egCxKgogd
# F3VIJoCE/Wuy9MuEgypea1Hei7lFGvdLQZH5Jo2QR5uN8hiMc8Z47RRJuIWCOeyI
# J1YnRiiibpUZ72+wpv8LTov0yH6C5HR/D8+AT4vqtP57ITXsD9DPOob8tjtsefPc
# QJebUNiqyfyTL5j5/J+2d+GPCcXEYoeWZ+nrsZSfrd5DHM4ovCmD3lifgYnzjOry
# 4ghQT/cvmdHwFr6yJGphW/HG8GQd+cB4w7wGpOhHVJby44kGVK8MzY9s32Dy1THn
# Jg8p7y1sEGz/A1y84Zt6gIsITYaccHhBKp4cOVNrfoRVUx2G/0Tr7Dk3fpCU8u+5
# olqPPwKgZs57jl+lOrRVsX1AYEmAnyCyGrqRAzpGXyk1HvNIBpSNNuTBQk7FBvu+
# Ypi6A7S2V2Tj6lzYWVBvuGECAwEAAaOCAUkwggFFMB0GA1UdDgQWBBSJ7aO6nJXJ
# I9eijzS5QkR2RlngADAfBgNVHSMEGDAWgBSfpxVdAF5iXYP05dJlpxtTNRnpcjBf
# BgNVHR8EWDBWMFSgUqBQhk5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3Bz
# L2NybC9NaWNyb3NvZnQlMjBUaW1lLVN0YW1wJTIwUENBJTIwMjAxMCgxKS5jcmww
# bAYIKwYBBQUHAQEEYDBeMFwGCCsGAQUFBzAChlBodHRwOi8vd3d3Lm1pY3Jvc29m
# dC5jb20vcGtpb3BzL2NlcnRzL01pY3Jvc29mdCUyMFRpbWUtU3RhbXAlMjBQQ0El
# MjAyMDEwKDEpLmNydDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUF
# BwMIMA4GA1UdDwEB/wQEAwIHgDANBgkqhkiG9w0BAQsFAAOCAgEAZiAJgFbkf7jf
# hx/mmZlnGZrpae+HGpxWxs8I79vUb8GQou50M1ns7iwG2CcdoXaq7VgpVkNf1uvI
# hrGYpKCBXQ+SaJ2O0BvwuJR7UsgTaKN0j/yf3fpHD0ktH+EkEuGXs9DBLyt71iut
# Vkwow9iQmSk4oIK8S8ArNGpSOzeuu9TdJjBjsasmuJ+2q5TjmrgEKyPe3TApAio8
# cdw/b1cBAmjtI7tpNYV5PyRI3K1NhuDgfEj5kynGF/uizP1NuHSxF/V1ks/2tCEo
# riicM4k1PJTTA0TCjNbkpmBcsAMlxTzBnWsqnBCt9d+Ud9Va3Iw9Bs4ccrkgBjLt
# g3vYGYar615ofYtU+dup+LuU0d2wBDEG1nhSWHaO+u2y6Si3AaNINt/pOMKU6l4A
# W0uDWUH39OHH3EqFHtTssZXaDOjtyRgbqMGmkf8KI3qIVBZJ2XQpnhEuRbh+Agpm
# Rn/a410Dk7VtPg2uC422WLC8H8IVk/FeoiSS4vFodhncFetJ0ZK36wxAa3FiPgBe
# bRWyVtZ763qDDzxDb0mB6HL9HEfTbN+4oHCkZa1HKl8B0s8RiFBMf/W7+O7EPZ+w
# MH8wdkjZ7SbsddtdRgRARqR8IFPWurQ+sn7ftEifaojzuCEahSAcq86yjwQeTPN9
# YG9b34RTurnkpD+wPGTB1WccMpsLlM0wggdxMIIFWaADAgECAhMzAAAAFcXna54C
# m0mZAAAAAAAVMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UE
# CBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9z
# b2Z0IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZp
# Y2F0ZSBBdXRob3JpdHkgMjAxMDAeFw0yMTA5MzAxODIyMjVaFw0zMDA5MzAxODMy
# MjVaMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQH
# EwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNV
# BAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwMIICIjANBgkqhkiG9w0B
# AQEFAAOCAg8AMIICCgKCAgEA5OGmTOe0ciELeaLL1yR5vQ7VgtP97pwHB9KpbE51
# yMo1V/YBf2xK4OK9uT4XYDP/XE/HZveVU3Fa4n5KWv64NmeFRiMMtY0Tz3cywBAY
# 6GB9alKDRLemjkZrBxTzxXb1hlDcwUTIcVxRMTegCjhuje3XD9gmU3w5YQJ6xKr9
# cmmvHaus9ja+NSZk2pg7uhp7M62AW36MEBydUv626GIl3GoPz130/o5Tz9bshVZN
# 7928jaTjkY+yOSxRnOlwaQ3KNi1wjjHINSi947SHJMPgyY9+tVSP3PoFVZhtaDua
# Rr3tpK56KTesy+uDRedGbsoy1cCGMFxPLOJiss254o2I5JasAUq7vnGpF1tnYN74
# kpEeHT39IM9zfUGaRnXNxF803RKJ1v2lIH1+/NmeRd+2ci/bfV+AutuqfjbsNkz2
# K26oElHovwUDo9Fzpk03dJQcNIIP8BDyt0cY7afomXw/TNuvXsLz1dhzPUNOwTM5
# TI4CvEJoLhDqhFFG4tG9ahhaYQFzymeiXtcodgLiMxhy16cg8ML6EgrXY28MyTZk
# i1ugpoMhXV8wdJGUlNi5UPkLiWHzNgY1GIRH29wb0f2y1BzFa/ZcUlFdEtsluq9Q
# BXpsxREdcu+N+VLEhReTwDwV2xo3xwgVGD94q0W29R6HXtqPnhZyacaue7e3Pmri
# Lq0CAwEAAaOCAd0wggHZMBIGCSsGAQQBgjcVAQQFAgMBAAEwIwYJKwYBBAGCNxUC
# BBYEFCqnUv5kxJq+gpE8RjUpzxD/LwTuMB0GA1UdDgQWBBSfpxVdAF5iXYP05dJl
# pxtTNRnpcjBcBgNVHSAEVTBTMFEGDCsGAQQBgjdMg30BATBBMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL0RvY3MvUmVwb3NpdG9y
# eS5odG0wEwYDVR0lBAwwCgYIKwYBBQUHAwgwGQYJKwYBBAGCNxQCBAweCgBTAHUA
# YgBDAEEwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAU
# 1fZWy4/oolxiaNE9lJBb186aGMQwVgYDVR0fBE8wTTBLoEmgR4ZFaHR0cDovL2Ny
# bC5taWNyb3NvZnQuY29tL3BraS9jcmwvcHJvZHVjdHMvTWljUm9vQ2VyQXV0XzIw
# MTAtMDYtMjMuY3JsMFoGCCsGAQUFBwEBBE4wTDBKBggrBgEFBQcwAoY+aHR0cDov
# L3d3dy5taWNyb3NvZnQuY29tL3BraS9jZXJ0cy9NaWNSb29DZXJBdXRfMjAxMC0w
# Ni0yMy5jcnQwDQYJKoZIhvcNAQELBQADggIBAJ1VffwqreEsH2cBMSRb4Z5yS/yp
# b+pcFLY+TkdkeLEGk5c9MTO1OdfCcTY/2mRsfNB1OW27DzHkwo/7bNGhlBgi7ulm
# ZzpTTd2YurYeeNg2LpypglYAA7AFvonoaeC6Ce5732pvvinLbtg/SHUB2RjebYIM
# 9W0jVOR4U3UkV7ndn/OOPcbzaN9l9qRWqveVtihVJ9AkvUCgvxm2EhIRXT0n4ECW
# OKz3+SmJw7wXsFSFQrP8DJ6LGYnn8AtqgcKBGUIZUnWKNsIdw2FzLixre24/LAl4
# FOmRsqlb30mjdAy87JGA0j3mSj5mO0+7hvoyGtmW9I/2kQH2zsZ0/fZMcm8Qq3Uw
# xTSwethQ/gpY3UA8x1RtnWN0SCyxTkctwRQEcb9k+SS+c23Kjgm9swFXSVRk2XPX
# fx5bRAGOWhmRaw2fpCjcZxkoJLo4S5pu+yFUa2pFEUep8beuyOiJXk+d0tBMdrVX
# VAmxaQFEfnyhYWxz/gq77EFmPWn9y8FBSX5+k77L+DvktxW/tM4+pTFRhLy/AsGC
# onsXHRWJjXD+57XQKBqJC4822rpM+Zv/Cuk0+CQ1ZyvgDbjmjJnW4SLq8CdCPSWU
# 5nR0W2rRnj7tfqAxM328y+l7vzhwRNGQ8cirOoo6CGJ/2XBjU02N7oJtpQUQwXEG
# ahC0HVUzWLOhcGbyoYIDWTCCAkECAQEwggEBoYHZpIHWMIHTMQswCQYDVQQGEwJV
# UzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UE
# ChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMS0wKwYDVQQLEyRNaWNyb3NvZnQgSXJl
# bGFuZCBPcGVyYXRpb25zIExpbWl0ZWQxJzAlBgNVBAsTHm5TaGllbGQgVFNTIEVT
# TjoyQTFBLTA1RTAtRDk0NzElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAg
# U2VydmljZaIjCgEBMAcGBSsOAwIaAxUAqs5WjWO7zVAKmIcdwhqgZvyp6UaggYMw
# gYCkfjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UE
# BxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYD
# VQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQsF
# AAIFAOsE9OswIhgPMjAyNDEyMTIwNTM2NDNaGA8yMDI0MTIxMzA1MzY0M1owdzA9
# BgorBgEEAYRZCgQBMS8wLTAKAgUA6wT06wIBADAKAgEAAgIG/AIB/zAHAgEAAgIS
# RjAKAgUA6wZGawIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAow
# CAIBAAIDB6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBCwUAA4IBAQAa1NqHRz6d
# OpCpd7btuGVaMgjua4WRO29qadVkxt8q4HuJsM8hPrizRKb94e++Ywb54ros3A9m
# btwJBWz2VM7R63vgNaJR3iucIQGURFxv+fb6/Tk66HFZRavbFHJEMsgV//Fo+jkG
# 2OPEsMO5WvIGa4SuBB6wm5V93Gf8VYAtP7st5DuMioJrX5gubVctb4xSW/DtEqqe
# +xRKAhGhKBYdITvbyq7OHtWuLxp+EWIeWGxd+eXkjS+ZoLPFQw8KZyIkPV7PyANH
# apMOVEtnvHs7vODVPj9jXk7yGM3sGR6HiQIIXnH3B3ZzKrQRA/f6c5Ad82r9OC+s
# mDcCMGCn/ZWaMYIEDTCCBAkCAQEwgZMwfDELMAkGA1UEBhMCVVMxEzARBgNVBAgT
# Cldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29m
# dCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUtU3RhbXAgUENB
# IDIwMTACEzMAAAH5H2eNdauk8bEAAQAAAfkwDQYJYIZIAWUDBAIBBQCgggFKMBoG
# CSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQxIgQg865tEdlP
# lr5lqveBp3zGi2yP8+rGaXePLF3a8OOh6gswgfoGCyqGSIb3DQEJEAIvMYHqMIHn
# MIHkMIG9BCA5I4zIHvCN+2T66RUOLCZrUEVdoKlKl8VeCO5SbGLYEDCBmDCBgKR+
# MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xJjAkBgNVBAMT
# HU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAAB+R9njXWrpPGxAAEA
# AAH5MCIEIIFAGBTlynmFbxRr3l34h1GY7Tz+TMdYfZm+L3x1nRX7MA0GCSqGSIb3
# DQEBCwUABIICAD9HnADhfg2dPnfXXQqGa2l+WcNjUHlwDHSx3A6b0QyNcxze64Rl
# SVeoqvBk4G5XF7KmVFHunaV0AUO6c/dgPUV/XTgh2O4buz/se77IQGVpwCU2sOqC
# ymxAkbmhDA38qOZIQWIW+4uBliGFixFTrKuF+Kk97QcahjKqLk+6W6dvCNyhm5D6
# snX7ktZzFZDd2S76EfefaLHXPPwoEI7SKa8kx9uYGI20TEHjc2yDysjwKU9HKzS+
# INIkSz7TrNOBFtmMar5Iqros6Cw7y7ZHpAXnHveGeC96F9sK2yxfChbx3xRHUu9a
# BZp/k1yU3t91pG3Q1yH+ZA7CO3UlFa0HwpYSEWzu84oWOBpQAZhj9HK4TCFcYDfV
# hHARbdN7QprhAhu3gzoGhDsJXgsGrpm7fMOrWI7SofaGi/63ELZSaNACJjcdDrSm
# eEBW5hdkAlB+KdgSqrI6Q9PMB76OEamljOC8UjFu6REfjGCLl8wYnOt5nperS7iN
# x7/jZ6BaE68yF/dayLari4zVJ0S4PdqUJivmf2ftiV7lxcjEskRE8QjluyLoELKZ
# SUrjd3bz46Yq5t8g5ChrskMfb3BjH145s6htkh0YSi9p5EqJWc0Dhaeo1RSmdgxD
# u5rIIpXQFlsWBeqSLkPdl5tvGQMQK3zVhp0+j6o1ayjtrNrQq0FLc3Ap
# SIG # End signature block
