GoogleAuthenticationFactory.psm1

#region Public commands
<#
.SYNOPSIS
Gets an OAuth access token from a Google authentication factory.
 
.DESCRIPTION
Retrieves an access token from the supplied factory instance, the named factory,
or the most recently created factory stored in the module scope. Use -AsHashTable
to return an Authorization header that can be passed directly to Invoke-RestMethod
or Invoke-WebRequest.
 
.PARAMETER AsHashTable
Returns a hashtable containing the Authorization header instead of the raw token object.
 
.PARAMETER Factory
The authentication factory instance to use. You can pass a factory object or the
name of a registered factory. If omitted, the current module-level default factory
is used.
 
.PARAMETER ForceRefresh
Forces acquisition of a new token instead of reusing a cached valid token.
 
.EXAMPLE
PS> Get-GoogleAccessToken
 
Gets the access token from the most recently created factory.
 
.EXAMPLE
PS> Get-GoogleAccessToken -Factory 'googleAdminApi' -AsHashTable
 
Gets the access token from the named factory and returns it as an Authorization header hashtable.
 
.EXAMPLE
PS> Get-GoogleAccessToken -Factory 'googleAdminApi' -ForceRefresh
 
Forces a fresh token retrieval from the named factory.
 
.OUTPUTS
System.Collections.Hashtable
Google access token object
#>

function Get-GoogleAccessToken
{
    param
    (
        [switch]$AsHashTable,
        [Parameter()]
        $Factory = $script:GoogleTokenProvider,
        [switch]$ForceRefresh
    )

    process
    {
        if($Factory -is [string])
        {
            #name of factory has been passed
            $Factory = Get-GoogleAuthenticationFactory -Name $Factory
        }
        $token = $Factory.GetAccessToken($ForceRefresh)
        if($AsHashTable)
        {
            @{
                Authorization = "$($token.token_type) $($token.access_token)"
            }
        }
        else
        {
            $token
        }
    }
}
<#
.SYNOPSIS
Gets one or more registered Google authentication factories.
 
.DESCRIPTION
Returns a registered factory by name, all registered factories, or the current
default factory when no parameters are specified.
 
.PARAMETER Name
The name of a registered factory to retrieve.
 
.PARAMETER All
Returns all registered factories instead of only the current default factory.
 
.EXAMPLE
PS> Get-GoogleAuthenticationFactory
 
Returns the current default Google authentication factory.
 
.EXAMPLE
PS> Get-GoogleAuthenticationFactory -Name 'chatAdminApi'
 
Returns the registered factory with the specified name.
 
.EXAMPLE
PS> Get-GoogleAuthenticationFactory -All
 
Returns all registered Google authentication factories.
 
.OUTPUTS
GoogleTokenProvider
System.Object[]
#>


function Get-GoogleAuthenticationFactory
{
    param
    (
        [Parameter()]
        [string]$Name,
        [switch]$All
    )

    process
    {
        if(-not [string]::IsNullOrEmpty($Name))
        {
            if($script:GoogleAuthenticationProviders.ContainsKey($Name))
            {
                return $script:GoogleAuthenticationProviders[$Name]
            }
            else
            {
                Write-Warning "No Google authentication provider registered with name '$Name'"
                return $null
            }
        }
        else
        {
            if($all)
            {
                return $script:GoogleAuthenticationProviders.Values
            }
            else
            {
                return $script:GoogleTokenProvider
            }
        }
    }
}
<#
.SYNOPSIS
Creates a Google authentication factory for acquiring access tokens.
 
.DESCRIPTION
Creates a new GoogleTokenProvider instance using either service account JSON
credentials or Azure AD federated credentials, then stores it as the current
module-level default provider. Optionally registers the provider by name and
enables Application Insights logging. In the federated flow, you can optionally
exchange the federated token for a service-account access token by supplying
`-ServiceAccountEmail`.
 
.PARAMETER GoogleAccessJson
The raw JSON content of the Google service account credentials.
 
Used by parameter set: ClientSecret.
 
.PARAMETER AadAuthenticationFactory
An Azure AD authentication factory configured for workload identity federation.
 
Used by parameter set: AadFederated.
 
.PARAMETER WorkloadIdentityProviderResourceId
The full Google workload identity provider resource identifier configured for
federated identity.
 
Used by parameter set: AadFederated.
 
.PARAMETER ServiceAccountEmail
The Google service account email used to exchange the federated token for a
native Google service-account access token. If omitted, the factory uses the
federated token directly.
 
Used by parameter set: AadFederated.
 
.PARAMETER Scopes
One or more Google API scopes to request when acquiring access tokens.
 
.PARAMETER TargetUserEmail
The email address of the user to impersonate. If omitted, no impersonation is used.
 
Used by parameter set: ClientSecret.
 
.PARAMETER Name
An optional name used to register the factory in the module-level factory dictionary.
 
.PARAMETER AiLogger
Optional logger instance used for Application Insights logging.
 
.EXAMPLE
PS> $jsonData = Get-Content -Path 'C:\service-account.json' -Raw
PS> New-GoogleAuthenticationFactory -GoogleAccessJson $jsonData -Scopes 'https://www.googleapis.com/auth/admin.directory.user.readonly'
 
Creates a new factory and makes it the current default factory.
 
.EXAMPLE
PS> New-GoogleAuthenticationFactory -GoogleAccessJson $jsonData -Scopes 'https://www.googleapis.com/auth/chat.admin.spaces.readonly' -TargetUserEmail 'user@contoso.com' -Name 'chatAdminApi'
 
Creates a named factory that uses user impersonation.
 
.EXAMPLE
PS> New-GoogleAuthenticationFactory -AadAuthenticationFactory $aadFactory -WorkloadIdentityProviderResourceId '//iam.googleapis.com/projects/132546827814/locations/global/workloadIdentityPools/my-pool/providers/entra-id-mytenant-com' -ServiceAccountEmail 'service-account@project-id.iam.gserviceaccount.com' -Scopes 'https://www.googleapis.com/auth/cloud-platform' -Name 'federatedApi'
 
Creates a named factory using Azure AD federated credentials.
 
.EXAMPLE
PS> New-GoogleAuthenticationFactory -AadAuthenticationFactory $aadFactory -WorkloadIdentityProviderResourceId '//iam.googleapis.com/projects/132546827814/locations/global/workloadIdentityPools/my-pool/providers/entra-id-mytenant-com' -Scopes 'https://www.googleapis.com/auth/cloud-platform' -Name 'federatedApi'
 
Creates a named factory that uses the federated token directly without service-account exchange.
 
.OUTPUTS
GoogleTokenProvider
#>

function New-GoogleAuthenticationFactory
{
    param
    (
        [Parameter(Mandatory, ParameterSetName='ClientSecret')]
        [string]
            #Google access JSON file content
        $GoogleAccessJson,
        
        [Parameter(ParameterSetName='ClientSecret')]
        [string]
            #Impersonated user email address
            # If not specified, impoersonation will not be used
        $TargetUserEmail,

        [Parameter(Mandatory, ParameterSetName='AadFederated')]
        [object]
            #instance of AAD Authentication Factory configured for federated credentials
        $AadAuthenticationFactory,

        [Parameter(Mandatory, ParameterSetName='AadFederated')]
        [string]
            #Resource ID of the Google workload identity provider configured in Azure AD
            #Example: //iam.googleapis.com/projects/132546827814/locations/global/workloadIdentityPools/my-pool/providers/entra-id-mytenant-com
        $WorkloadIdentityProviderResourceId,

        [Parameter(ParameterSetName='AadFederated')]
        [string]
            #email address of the service account to impersonate. If not specified, the factory will use the federated token directly without exchanging for a service account token.
            #Important: to be able to impersonate, the federated identity must have "Service Account Token Creator" role on the target service account
        $ServiceAccountEmail,
        [Parameter(Mandatory)]
        [string[]]
            #Scopes requested to be granted
        $Scopes,
        
        
        [Parameter()]
        [string]
            #Name of the factory instance
            # If specified, the factory will be registered in the global dictionary of Google authentication providers
        $Name,
        [Parameter()]
            #AI logger to use for logging to Application insights
            #Instance of this logger can be obtained via module AiLogging
        $AiLogger
    )

    process
    {
        switch($PSCmdlet.ParameterSetName)
        {
            'AadFederated'
            {
                Write-Verbose "Creating Google authentication factory using Azure AD federated credentials"
                $script:GoogleTokenProvider = [GoogleTokenProvider]::new($AadAuthenticationFactory, $WorkloadIdentityProviderResourceId, $ServiceAccountEmail, $Scopes, $Name, $AiLogger)
                break;
            }
            'ClientSecret'
            {
                if(-not [string]::IsNullOrEmpty($TargetUserEmail))
                {
                    Write-Verbose "Using impersonation for user $TargetUserEmail"
                }
                Write-Verbose "Creating Google authentication factory using JSON credentials"
                $script:GoogleTokenProvider = [GoogleTokenProvider]::new($GoogleAccessJson, $Scopes, $TargetUserEmail, $Name, $AiLogger)
                break;
            }
            default
            {
                throw "Invalid parameter set: $($PSCmdlet.ParameterSetName)"
            }
        }
        if(-not [string]::IsNullOrEmpty($Name))
        {
            $script:GoogleAuthenticationProviders[$Name] = $script:GoogleTokenProvider
            Write-Verbose "Registered Google authentication provider with name '$Name'"
        }
        $script:GoogleTokenProvider
    }
}
<#
.SYNOPSIS
Displays diagnostic information about a Google access token.
 
.DESCRIPTION
Calls the factory's token test routine to inspect the current access token. The
factory can be provided directly, looked up by name, or taken from the current
module-level default factory. Direct federated tokens are not supported by the
Google token inspection endpoint used by this command; in that case the command
returns `$null` and writes a warning.
 
.PARAMETER Factory
The authentication factory instance to test. You can pass a factory object or the
name of a registered factory. If omitted, the current module-level default factory
is used.
 
.EXAMPLE
PS> Test-GoogleAccessToken
 
Tests the access token for the current default factory.
 
.EXAMPLE
PS> Test-GoogleAccessToken -Factory 'googleAdminApi'
 
Tests the access token for the named factory.
 
.EXAMPLE
PS> Test-GoogleAccessToken -Factory 'federatedApi'
 
Tests the token for the named factory when the factory is configured to exchange
the federated token for a service-account token.
 
.OUTPUTS
System.Object
System.Management.Automation.WarningRecord
#>

function Test-GoogleAccessToken {
    [CmdletBinding()]
    param
    (
        [Parameter()]
        $Factory = $script:GoogleTokenProvider
    )

    process
    {
        if($Factory -is [string])
        {
            #name of factory has been passed
            $Factory = Get-GoogleAuthenticationFactory -Name $GoogleTokenProvider
        }
        $Factory.TestAccessToken()
    }
}
#endregion Public commands
#region Internal commands
<#
.SYNOPSIS
Internal token provider used by the module public commands.
 
.DESCRIPTION
`GoogleTokenProvider` encapsulates token acquisition, caching, refresh logic,
and token diagnostics for Google APIs. It supports two authentication flows:
 
- `ClientSecret`: Service account JSON private key flow (optionally with user impersonation).
- `AadFederated`: Azure AD token exchange with Google STS (optionally followed by
    service account access token generation).
 
The class is internal to the module and is created by `New-GoogleAuthenticationFactory`.
#>

class GoogleTokenProvider
{
    [string] $Name
    [string[]] $Scopes
    [string] $FlowType
    hidden $token
    hidden [PSCustomObject]$Configuration

    # Initializes provider configuration for service-account JSON authentication.
    # The JSON must contain `client_email` and `private_key` fields.
    GoogleTokenProvider([string]$googleAccessJson , [string[]]$scopes, $TargetUserEmail, $Name, $AiLogger = $null)
    {
        $this.FlowType = 'ClientSecret'
        $this.Name = $Name
        $this.Scopes = $scopes
        $Credential = ConvertFrom-Json -InputObject $GoogleAccessJson -Depth 10
        $this.Configuration = [PSCustomObject]@{
            AiLogger = $AiLogger
            ServiceAccountEmail = $Credential.client_email
            TargetUserEmail = $TargetUserEmail
            PrivateKey = $Credential.private_key -replace '-----BEGIN PRIVATE KEY-----\n' -replace '\n-----END PRIVATE KEY-----\n' -replace '\n'
        }
    }
    
    # Initializes provider configuration for Azure AD federated authentication.
    # The provider can optionally exchange the federated token for a native
    # service-account token when ServiceAccountEmail is supplied.
    GoogleTokenProvider([object]$aadFactory , [string]$workloadIdentityProviderResourceId, [string]$saEmail,  [string[]]$scopes, $Name, $AiLogger = $null)
    {
        $this.FlowType = 'AadFederated'
        $this.Name = $Name
        $this.Scopes = $scopes
        $this.Configuration = [PSCustomObject]@{
            AiLogger = $AiLogger
            ServiceAccountEmail = $saEmail
            AadFactory = $aadFactory
            WorkloadIdentityProviderResourceId = $workloadIdentityProviderResourceId
        }
    }

    # Returns a cached token when valid, otherwise acquires a new one.
    # Set ForceRefresh to bypass cache and always request a new token.
    [PSCustomObject]GetAccessToken([bool]$ForceRefresh)
    {
        if($null -eq $this.token -or $this.token.expiration_time -lt ([DateTime]::UtcNow) -or $ForceRefresh)
        {
            switch($this.FlowType)
            {
                'AadFederated'
                {
                    $tokenUri = "https://sts.googleapis.com/v1/token"
                    Write-Verbose "Getting Google access token using Azure AD federated credentials"
                    $aadToken = Get-AadToken -factory $this.Configuration.AadFactory
                    $payload = @{
                        grant_type         = "urn:ietf:params:oauth:grant-type:token-exchange"
                        audience           = $this.Configuration.WorkloadIdentityProviderResourceId
                        subject_token_type = "urn:ietf:params:oauth:token-type:jwt"
                        subject_token      = $aadToken.AccessToken
                        scope              = ($this.Scopes -join " ")
                        requested_token_type = 'urn:ietf:params:oauth:token-type:access_token'
                    }
                    Write-Verbose "Calling Google API to get access token: $tokenUri"
                    $this.token = $this.CallGoogleTokenApi($tokenUri, 'POST', $null, ($payload | ConvertTo-Json), "application/json")

                    if(-not [string]::IsNullOrEmpty($this.Configuration.ServiceAccountEmail))
                    {
                        #try to exchange federated token for native google token
                        Write-Verbose "Exchanging federated token for native Google access token with service account email $($this.Configuration.ServiceAccountEmail)"
                        $tokenUri = "https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/$($this.Configuration.ServiceAccountEmail)`:generateAccessToken"
                        $payload = @{
                            "scope"= $this.Scopes
                            "lifetime"= "3600s"
                        }
                        $headers = @{
                            Authorization = "$($this.token.token_type) $($this.token.access_token)"
                        }
                        $finaltoken = $this.CallGoogleTokenApi($tokenUri, 'POST', $headers, ($payload | ConvertTo-Json)    , "application/json")
                        #token comes in different shape from this endpoint, we need to map it back to the same shape as the original token for caching and later use
                        $this.token.access_token = $finaltoken.accessToken
                        $this.token.expiration_time = $finaltoken.expireTime
                    }
                    break;
                }
                'ClientSecret'
                {
                    Write-Verbose "Getting Google access token using client secret flow"
                    Write-Verbose "Fetching new access token for Google API"
                    $tokenUri = "https://oauth2.googleapis.com/token"
                    $header = @{
                        alg = "RS256"
                        typ = "JWT"
                    }
                    $headerBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($header | ConvertTo-Json)))
                    $timestamp = [Math]::Round((Get-Date -UFormat %s))
                    
                    $claimSet = @{
                        iss   = $this.Configuration.ServiceAccountEmail
                        scope = ($this.Scopes -join " ")
                        aud   = "https://oauth2.googleapis.com/token"
                        exp   = $timestamp + 3600
                        iat   = $timestamp
                    }
                    if(-not [string]::IsNullOrEmpty($this.Configuration.TargetUserEmail))
                    {
                        $claimSet.sub =$this.Configuration.TargetUserEmail
                    }
                    $claimSetBase64 = [System.Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($claimSet | ConvertTo-Json)))
                    $signatureInput = $headerBase64 + "." + $claimSetBase64
                    $signatureBytes = [System.Text.Encoding]::UTF8.GetBytes($signatureInput)
                    $privateKeyBytes = [System.Convert]::FromBase64String($this.Configuration.PrivateKey)
                    $rsaProvider = [System.Security.Cryptography.RSA]::Create()
                    $bytesRead = $null
                    $rsaProvider.ImportPkcs8PrivateKey($privateKeyBytes, [ref]$bytesRead)
                    $signature = $rsaProvider.SignData($signatureBytes, [System.Security.Cryptography.HashAlgorithmName]::SHA256, [System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
                    $signatureBase64 = [System.Convert]::ToBase64String($signature)
                    $jwt = $headerBase64 + "." + $claimSetBase64 + "." + $signatureBase64
                    $body = @{
                        grant_type = "urn:ietf:params:oauth:grant-type:jwt-bearer"
                        assertion  = $jwt
                    }
                    Write-Verbose "Calling Google API to get access token: $tokenUri"
                    $this.token = $this.CallGoogleTokenApi($tokenUri, 'POST', $null, $body, "application/x-www-form-urlencoded")
                    
                    break;
                }
                default
                {
                    throw "Unsupported flow type: $($this.FlowType)"
                }
            }
        }
        return $this.Token
    }

    # Validates the current token by calling Google's tokeninfo endpoint.
    # Returns token metadata when validation succeeds.
    [PSCustomObject]TestAccessToken()
    {
        if($this.FlowType -eq 'AadFederated' -and [string]::IsNullOrEmpty($this.Configuration.ServiceAccountEmail))
        {
            Write-Warning "Test-GoogleAccessToken is not supported for Federated access tokens"
            return $null
        }
        $t = $this.GetAccessToken($false)
        $headers = @{
            Authorization = "$($t.token_type) $($t.access_token)"
        }
        $tokenUri = 'https://www.googleapis.com/oauth2/v3/tokeninfo'
        Write-Verbose "Calling Google API to test access token: $tokenUri"
        $requestStart = Get-Date -AsUTC
        $response = Invoke-WebRequest -Uri $tokenUri -Headers $headers -SkipHttpErrorCheck
        if($this.AiLogger)
        {
            Write-AiDependency -Target 'GoogleAuth' -DependencyType 'HTTP' -Name 'TestAccessToken' `
                -Data $tokenUri -Start $requestStart `
                -ResultCode $response.StatusCode.ToString() `
                -Success ($response.StatusCode -eq [System.Net.HttpStatusCode]::OK) `
                -Connection $this.AiLogger
        }
        if($response.StatusCode -ne [System.Net.HttpStatusCode]::OK)
        {
            $ex = new-object System.Net.Http.HttpRequestException( $response.Content, $null, $response.StatusCode )
            throw $ex
        }
        return ($response.Content | ConvertFrom-Json)
    }

    # Executes token-related HTTP calls and normalizes successful responses
    # into the module's `Google.AccessToken` shape.
    hidden [PSCustomObject] CallGoogleTokenApi($uri, $method,  $headers, $body, $contentType)
    {
        Write-Verbose "Calling Google API: $uri"
        $requestStart = Get-Date -AsUTC
        $response = Invoke-WebRequest `
                        -Uri $uri `
                        -Method $method `
                        -Body $body `
                        -Headers $headers `
                        -ContentType $contentType `
                        -SkipHttpErrorCheck
        if($response.StatusCode -eq [System.Net.HttpStatusCode]::OK)
        {
            if($this.AiLogger)
            {
                Write-AiDependency -Target 'GoogleAuth' -DependencyType 'HTTP' -Name 'GetAccessToken' -Data $uri -Start $requestStart -ResultCode 'Ok' -Success $true -Connection $this.AiLogger
            }
            #when succeeded, response is the token object, we add the expiration_time property to it for easier caching and check later
            $responseToken = $response.Content | ConvertFrom-Json 
            $responseToken | Add-Member -MemberType NoteProperty -Name expiration_time -Value ([DateTime]::UtcNow.AddSeconds($responseToken.expires_in))
            $responseToken.psobject.TypeNames.Insert(0,"Google.AccessToken")
            return $responseToken
        }
        else
        {
            if($this.AiLogger)
            {
                Write-AiDependency -Target 'GoogleAuth' -DependencyType 'HTTP' -Name 'GetAccessToken' -Data $uri -Start $requestStart -ResultCode $response.StatusCode.ToString() -Success $false -Connection $this.AiLogger
            }
            
            $ex = new-object System.Net.Http.HttpRequestException( $response.Content, $null, $response.StatusCode )
            throw $ex
        }
    }
}
#endregion Internal commands
#region Module initialization
$script:GoogleAuthenticationProviders = @{}
#endregion Module initialization

# SIG # Begin signature block
# MIIuNAYJKoZIhvcNAQcCoIIuJTCCLiECAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAqVQPav0kfGxfj
# cB1lPEgbNhTasvl0QXzclKJQzWy+I6CCE2AwggWQMIIDeKADAgECAhAFmxtXno4h
# MuI5B72nd3VcMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNV
# BAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDAeFw0xMzA4MDExMjAwMDBaFw0z
# ODAxMTUxMjAwMDBaMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIB
# AL/mkHNo3rvkXUo8MCIwaTPswqclLskhPfKK2FnC4SmnPVirdprNrnsbhA3EMB/z
# G6Q4FutWxpdtHauyefLKEdLkX9YFPFIPUh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZ
# anMylNEQRBAu34LzB4TmdDttceItDBvuINXJIB1jKS3O7F5OyJP4IWGbNOsFxl7s
# Wxq868nPzaw0QF+xembud8hIqGZXV59UWI4MK7dPpzDZVu7Ke13jrclPXuU15zHL
# 2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfb
# BHMqbpEBfCFM1LyuGwN1XXhm2ToxRJozQL8I11pJpMLmqaBn3aQnvKFPObURWBf3
# JFxGj2T3wWmIdph2PVldQnaHiZdpekjw4KISG2aadMreSx7nDmOu5tTvkpI6nj3c
# AORFJYm2mkQZK37AlLTSYW3rM9nF30sEAMx9HJXDj/chsrIRt7t/8tWMcCxBYKqx
# YxhElRp2Yn72gLD76GSmM9GJB+G9t+ZDpBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0
# viastkF13nqsX40/ybzTQRESW+UQUOsxxcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aL
# T8LWRV+dIPyhHsXAj6KxfgommfXkaS+YHS312amyHeUbAgMBAAGjQjBAMA8GA1Ud
# EwEB/wQFMAMBAf8wDgYDVR0PAQH/BAQDAgGGMB0GA1UdDgQWBBTs1+OC0nFdZEzf
# Lmc/57qYrhwPTzANBgkqhkiG9w0BAQwFAAOCAgEAu2HZfalsvhfEkRvDoaIAjeNk
# aA9Wz3eucPn9mkqZucl4XAwMX+TmFClWCzZJXURj4K2clhhmGyMNPXnpbWvWVPjS
# PMFDQK4dUPVS/JA7u5iZaWvHwaeoaKQn3J35J64whbn2Z006Po9ZOSJTROvIXQPK
# 7VB6fWIhCoDIc2bRoAVgX+iltKevqPdtNZx8WorWojiZ83iL9E3SIAveBO6Mm0eB
# cg3AFDLvMFkuruBx8lbkapdvklBtlo1oepqyNhR6BvIkuQkRUNcIsbiJeoQjYUIp
# 5aPNoiBB19GcZNnqJqGLFNdMGbJQQXE9P01wI4YMStyB0swylIQNCAmXHE/A7msg
# dDDS4Dk0EIUhFQEI6FUy3nFJ2SgXUE3mvk3RdazQyvtBuEOlqtPDBURPLDab4vri
# RbgjU2wGb2dVf0a1TD9uKFp5JtKkqGKX0h7i7UqLvBv9R0oN32dmfrJbQdA75PQ7
# 9ARj6e/CVABRoIoqyc54zNXqhwQYs86vSYiv85KZtrPmYQ/ShQDnUBrkG5WdGaG5
# nLGbsQAe79APT0JsyQq87kP6OnGlyE0mpTX9iV28hWIdMtKgK1TtmlfB2/oQzxm3
# i0objwG2J5VT6LaJbVu8aNQj6ItRolb58KaAoNYes7wPD1N1KarqE3fk3oyBIa0H
# EEcRrYc9B9F1vM/zZn4wggawMIIEmKADAgECAhAIrUCyYNKcTJ9ezam9k67ZMA0G
# CSqGSIb3DQEBDAUAMGIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0
# IFRydXN0ZWQgUm9vdCBHNDAeFw0yMTA0MjkwMDAwMDBaFw0zNjA0MjgyMzU5NTla
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDVtC9C
# 0CiteLdd1TlZG7GIQvUzjOs9gZdwxbvEhSYwn6SOaNhc9es0JAfhS0/TeEP0F9ce
# 2vnS1WcaUk8OoVf8iJnBkcyBAz5NcCRks43iCH00fUyAVxJrQ5qZ8sU7H/Lvy0da
# E6ZMswEgJfMQ04uy+wjwiuCdCcBlp/qYgEk1hz1RGeiQIXhFLqGfLOEYwhrMxe6T
# SXBCMo/7xuoc82VokaJNTIIRSFJo3hC9FFdd6BgTZcV/sk+FLEikVoQ11vkunKoA
# FdE3/hoGlMJ8yOobMubKwvSnowMOdKWvObarYBLj6Na59zHh3K3kGKDYwSNHR7Oh
# D26jq22YBoMbt2pnLdK9RBqSEIGPsDsJ18ebMlrC/2pgVItJwZPt4bRc4G/rJvmM
# 1bL5OBDm6s6R9b7T+2+TYTRcvJNFKIM2KmYoX7BzzosmJQayg9Rc9hUZTO1i4F4z
# 8ujo7AqnsAMrkbI2eb73rQgedaZlzLvjSFDzd5Ea/ttQokbIYViY9XwCFjyDKK05
# huzUtw1T0PhH5nUwjewwk3YUpltLXXRhTT8SkXbev1jLchApQfDVxW0mdmgRQRNY
# mtwmKwH0iU1Z23jPgUo+QEdfyYFQc4UQIyFZYIpkVMHMIRroOBl8ZhzNeDhFMJlP
# /2NPTLuqDQhTQXxYPUez+rbsjDIJAsxsPAxWEQIDAQABo4IBWTCCAVUwEgYDVR0T
# AQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHwYD
# VR0jBBgwFoAU7NfjgtJxXWRM3y5nP+e6mK4cD08wDgYDVR0PAQH/BAQDAgGGMBMG
# A1UdJQQMMAoGCCsGAQUFBwMDMHcGCCsGAQUFBwEBBGswaTAkBggrBgEFBQcwAYYY
# aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEEGCCsGAQUFBzAChjVodHRwOi8vY2Fj
# ZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkUm9vdEc0LmNydDBDBgNV
# HR8EPDA6MDigNqA0hjJodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRU
# cnVzdGVkUm9vdEc0LmNybDAcBgNVHSAEFTATMAcGBWeBDAEDMAgGBmeBDAEEATAN
# BgkqhkiG9w0BAQwFAAOCAgEAOiNEPY0Idu6PvDqZ01bgAhql+Eg08yy25nRm95Ry
# sQDKr2wwJxMSnpBEn0v9nqN8JtU3vDpdSG2V1T9J9Ce7FoFFUP2cvbaF4HZ+N3HL
# IvdaqpDP9ZNq4+sg0dVQeYiaiorBtr2hSBh+3NiAGhEZGM1hmYFW9snjdufE5Btf
# Q/g+lP92OT2e1JnPSt0o618moZVYSNUa/tcnP/2Q0XaG3RywYFzzDaju4ImhvTnh
# OE7abrs2nfvlIVNaw8rpavGiPttDuDPITzgUkpn13c5UbdldAhQfQDN8A+KVssIh
# dXNSy0bYxDQcoqVLjc1vdjcshT8azibpGL6QB7BDf5WIIIJw8MzK7/0pNVwfiThV
# 9zeKiwmhywvpMRr/LhlcOXHhvpynCgbWJme3kuZOX956rEnPLqR0kq3bPKSchh/j
# wVYbKyP/j7XqiHtwa+aguv06P0WmxOgWkVKLQcBIhEuWTatEQOON8BUozu3xGFYH
# Ki8QxAwIZDwzj64ojDzLj4gLDb879M4ee47vtevLt/B3E+bnKD+sEq6lLyJsQfmC
# XBVmzGwOysWGw/YmMwwHS6DTBwJqakAwSEs0qFEgu60bhQjiWQ1tygVQK+pKHJ6l
# /aCnHwZ05/LWUpD9r4VIIflXO7ScA+2GRfS0YW6/aOImYIbqyK+p/pQd52MbOoZW
# eE4wggcUMIIE/KADAgECAhAP9xCe9qf4ax3LBs7uih/sMA0GCSqGSIb3DQEBCwUA
# MGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UE
# AxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNpZ25pbmcgUlNBNDA5NiBTSEEz
# ODQgMjAyMSBDQTEwHhcNMjMxMTA4MDAwMDAwWhcNMjYxMDAxMjM1OTU5WjCBnDET
# MBEGCysGAQQBgjc8AgEDEwJDWjEdMBsGA1UEDwwUUHJpdmF0ZSBPcmdhbml6YXRp
# b24xETAPBgNVBAUTCDA0OTIzNjkzMQswCQYDVQQGEwJDWjEOMAwGA1UEBxMFUHJh
# aGExGjAYBgNVBAoTEUdyZXlDb3JiZWwgcy5yLm8uMRowGAYDVQQDExFHcmV5Q29y
# YmVsIHMuci5vLjCCAaIwDQYJKoZIhvcNAQEBBQADggGPADCCAYoCggGBAJ8t/Qga
# dJKtGC7EqH4pmIU73fInH+j1scmVnrJtXL8tGlKzWZ7qlWDWOJBR3owF9CVqL4IX
# BGImH8Miowj6RKKqhEe9UtxiH5ipV6msnzAjTFkwqR9vjfEm9vrU1JuXWvAWAfYx
# qYg92oyCEBDQxpURpZmqAVSBy9U/ScDwE4NykZGzb0oYSPtzStd8RJvtUkc4126w
# YKMbVe/kdY1mDbKO9DLfpbSIj3vghrH6XeHwEb7/jAVYI7Vl+jUyyqfmYHD7FldQ
# X2fZfwvoGSibY1uWvvP0/vm0yd6uDbDjCDOTQW8Lxl5wvlXEf5ewn2oaPSoa6ov3
# 1XmnxL5iT8c1LM06JFCwfHS9e0NSyNr86IiKaxQO9/MANrYciTicObtD3cBcSRDO
# pEUfhc4TvA5DQZaakSduVJWPdMhxQs9iWeYMOzh5NDTB3xAx8eLBn7Uj++hjI3FQ
# WGEPw4Ew6WoDsJShU0HemlDJGTPW9EZSWHGdNFr1BxXEPb4F7DbjJZn33QIDAQAB
# o4ICAjCCAf4wHwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0O
# BBYEFP2yViJvcgO05qXIH6aJSXB/QcEhMD0GA1UdIAQ2MDQwMgYFZ4EMAQMwKTAn
# BggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5jb20vQ1BTMA4GA1UdDwEB
# /wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzCBtQYDVR0fBIGtMIGqMFOgUaBP
# hk1odHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2Rl
# U2lnbmluZ1JTQTQwOTZTSEEzODQyMDIxQ0ExLmNybDBToFGgT4ZNaHR0cDovL2Ny
# bDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdSU0E0
# MDk2U0hBMzg0MjAyMUNBMS5jcmwwgZQGCCsGAQUFBwEBBIGHMIGEMCQGCCsGAQUF
# BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wXAYIKwYBBQUHMAKGUGh0dHA6
# Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWdu
# aW5nUlNBNDA5NlNIQTM4NDIwMjFDQTEuY3J0MAkGA1UdEwQCMAAwDQYJKoZIhvcN
# AQELBQADggIBADCe9Fh40HN9RneCehz5MrBy4O9WYsYCMJ7qJ9DsBT+Hed98UOKB
# k/XjgSLfsj5eZRHRmz3HzhGDK1PaRI+yIUVQx96a4qL7adktmrHex3fW39Iq+tPB
# rHtiEIp9rwunATeZpk+876u0AXYD1VDRWCtkL8zwZU0oqL6U/mWEIXzkryCB5N3x
# xtE54jMmW7MKi1+To4yQcrK3zQ394e2dr50L+aF2fgJ5mo1/YJvzyLLhigbqpoYG
# U/gjZonhNJXUaYogpHSTgUaBRlIKZ5xCnrFfJlOsbkhex4QAcdkU6XC+XyYfEQka
# 7ERwgxmEoRT3NlZ8/EbrQxJP4S1H8Z29M4D3L6rXNXXmv0IbfA9FQcqEco3Y3tRW
# dgdcFEwJmYTo0mCZrYTJHgkKW8xDvQ5BJISAp/ydOX5tSa71ojx1/Kp7qizqjBN/
# W77jdqJ89N1y+N/SOiHOCH9NO5pDLsHpTWW/arvjZT0I8dVYkqK0V39rh95XELI+
# NwBZvV4AsKLirjrkZU3pwCz6O99VmPkBqp9TA5wl13NdTpDHuQ6QyVT7hbC8LF5p
# z6x/xO/+tEGxG+1A31UTJPmkxhhUlR+NE3ZXiXhcG72CFHYUUvqwlThPkFYe4Ygf
# j9ADmss08k0JhVU5rkbrC2h+549HPlFu/XOSIrps4SXzInjHPEYuBETzMYIaKjCC
# GiYCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x
# QTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQw
# OTYgU0hBMzg0IDIwMjEgQ0ExAhAP9xCe9qf4ax3LBs7uih/sMA0GCWCGSAFlAwQC
# AQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwG
# CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI
# hvcNAQkEMSIEIAwDeV/QMOZdpgZmX0TtYKuIbUAujhj0L9kc73dGtHdvMA0GCSqG
# SIb3DQEBAQUABIIBgAqgVlWEI9PsHfD2d+k7f9ibNUvG4vWe02N431NMiFqn503T
# iQ5DxP8oE7KoGNqTdX6XjP9yQt1t/fNksB46zGYqmME+wOMyUWXs1kt1xHvguWjx
# PHlaTPG05QtbYfOa/KxsRVrCzERmXaoGH2iKI5Xc6DiJDLkWqPpdlHQdyXIvGpQ3
# PeeeRxgahmHUnHd6F8xghz9FVVCHzEjoo+RUo/CqJ6iEPKyAKutoEUujkanFUhwc
# P5YOZS3QCKb59aJQ79viTOc7ygwAjFXyt7699OhCT0KSeTnSWcufyFjQeaFB/xXW
# +ARI8wfbV1GNvgkW3AijdbCE/EcRRr9OafW8ZU/Dl9Bm0xJdb35dnejvzMJWOiiN
# gU5nrLP6MVdD3rF8Bz8p+BQMDQBSQtjqEwltol8S65FnzUVQOL2T82srCmThfiJ4
# 7eM9US9GHqaYd2c7X8alVCUQUfkgPJnGzz4DOrW1PvjcUIKa7VYnHsUeHtbVR98I
# 712SgXaujcR8nm8iLaGCF3cwghdzBgorBgEEAYI3AwMBMYIXYzCCF18GCSqGSIb3
# DQEHAqCCF1AwghdMAgEDMQ8wDQYJYIZIAWUDBAIBBQAweAYLKoZIhvcNAQkQAQSg
# aQRnMGUCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCD2fZXoKeAwfCse
# 1Igs+AFzfDK54MXf57Iq3XewtZE+gwIRAKDmFR/O6ilosHVRmOVGYvAYDzIwMjYw
# NDA1MjMwMDI3WqCCEzowggbtMIIE1aADAgECAhAKgO8YS43xBYLRxHanlXRoMA0G
# CSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcg
# UlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwHhcNMjUwNjA0MDAwMDAwWhcNMzYwOTAz
# MjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x
# OzA5BgNVBAMTMkRpZ2lDZXJ0IFNIQTI1NiBSU0E0MDk2IFRpbWVzdGFtcCBSZXNw
# b25kZXIgMjAyNSAxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0Eas
# LRLGntDqrmBWsytXum9R/4ZwCgHfyjfMGUIwYzKomd8U1nH7C8Dr0cVMF3BsfAFI
# 54um8+dnxk36+jx0Tb+k+87H9WPxNyFPJIDZHhAqlUPt281mHrBbZHqRK71Em3/h
# CGC5KyyneqiZ7syvFXJ9A72wzHpkBaMUNg7MOLxI6E9RaUueHTQKWXymOtRwJXcr
# cTTPPT2V1D/+cFllESviH8YjoPFvZSjKs3SKO1QNUdFd2adw44wDcKgH+JRJE5Qg
# 0NP3yiSyi5MxgU6cehGHr7zou1znOM8odbkqoK+lJ25LCHBSai25CFyD23DZgPfD
# rJJJK77epTwMP6eKA0kWa3osAe8fcpK40uhktzUd/Yk0xUvhDU6lvJukx7jphx40
# DQt82yepyekl4i0r8OEps/FNO4ahfvAk12hE5FVs9HVVWcO5J4dVmVzix4A77p3a
# wLbr89A90/nWGjXMGn7FQhmSlIUDy9Z2hSgctaepZTd0ILIUbWuhKuAeNIeWrzHK
# YueMJtItnj2Q+aTyLLKLM0MheP/9w6CtjuuVHJOVoIJ/DtpJRE7Ce7vMRHoRon4C
# WIvuiNN1Lk9Y+xZ66lazs2kKFSTnnkrT3pXWETTJkhd76CIDBbTRofOsNyEhzZtC
# GmnQigpFHti58CSmvEyJcAlDVcKacJ+A9/z7eacCAwEAAaOCAZUwggGRMAwGA1Ud
# EwEB/wQCMAAwHQYDVR0OBBYEFOQ7/PIx7f391/ORcWMZUEPPYYzoMB8GA1UdIwQY
# MBaAFO9vU0rp5AZ8esrikFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIHgDAWBgNVHSUB
# Af8EDDAKBggrBgEFBQcDCDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYIKwYBBQUHMAGG
# GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZRaHR0cDovL2Nh
# Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0YW1waW5n
# UlNBNDA5NlNIQTI1NjIwMjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBSoFCGTmh0dHA6
# Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVTdGFtcGlu
# Z1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgGBmeBDAEEAjAL
# BglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAGUqrfEcJwS5rmBB7NEIRJ5j
# QHIh+OT2Ik/bNYulCrVvhREafBYF0RkP2AGr181o2YWPoSHz9iZEN/FPsLSTwVQW
# o2H62yGBvg7ouCODwrx6ULj6hYKqdT8wv2UV+Kbz/3ImZlJ7YXwBD9R0oU62Ptgx
# Oao872bOySCILdBghQ/ZLcdC8cbUUO75ZSpbh1oipOhcUT8lD8QAGB9lctZTTOJM
# 3pHfKBAEcxQFoHlt2s9sXoxFizTeHihsQyfFg5fxUFEp7W42fNBVN4ueLaceRf9C
# q9ec1v5iQMWTFQa0xNqItH3CPFTG7aEQJmmrJTV3Qhtfparz+BW60OiMEgV5GWoB
# y4RVPRwqxv7Mk0Sy4QHs7v9y69NBqycz0BZwhB9WOfOu/CIJnzkQTwtSSpGGhLdj
# nQ4eBpjtP+XB3pQCtv4E5UCSDag6+iX8MmB10nfldPF9SVD7weCC3yXZi/uuhqdw
# kgVxuiMFzGVFwYbQsiGnoa9F5AaAyBjFBtXVLcKtapnMG3VH3EmAp/jsJ3FVF3+d
# 1SVDTmjFjLbNFZUWMXuZyvgLfgyPehwJVxwC+UpX2MSey2ueIu9THFVkT+um1vsh
# ETaWyQo8gmBto/m3acaP9QsuLj3FNwFlTxq25+T4QwX9xa6ILs84ZPvmpovq90K8
# eWyG2N01c4IhSOxqt81nMIIGtDCCBJygAwIBAgIQDcesVwX/IZkuQEMiDDpJhjAN
# BgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQg
# SW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2Vy
# dCBUcnVzdGVkIFJvb3QgRzQwHhcNMjUwNTA3MDAwMDAwWhcNMzgwMTE0MjM1OTU5
# WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/BgNV
# BAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYgU0hB
# MjU2IDIwMjUgQ0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAtHgx
# 0wqYQXK+PEbAHKx126NGaHS0URedTa2NDZS1mZaDLFTtQ2oRjzUXMmxCqvkbsDpz
# 4aH+qbxeLho8I6jY3xL1IusLopuW2qftJYJaDNs1+JH7Z+QdSKWM06qchUP+AbdJ
# gMQB3h2DZ0Mal5kYp77jYMVQXSZH++0trj6Ao+xh/AS7sQRuQL37QXbDhAktVJMQ
# bzIBHYJBYgzWIjk8eDrYhXDEpKk7RdoX0M980EpLtlrNyHw0Xm+nt5pnYJU3Gmq6
# bNMI1I7Gb5IBZK4ivbVCiZv7PNBYqHEpNVWC2ZQ8BbfnFRQVESYOszFI2Wv82wnJ
# RfN20VRS3hpLgIR4hjzL0hpoYGk81coWJ+KdPvMvaB0WkE/2qHxJ0ucS638ZxqU1
# 4lDnki7CcoKCz6eum5A19WZQHkqUJfdkDjHkccpL6uoG8pbF0LJAQQZxst7VvwDD
# jAmSFTUms+wV/FbWBqi7fTJnjq3hj0XbQcd8hjj/q8d6ylgxCZSKi17yVp2NL+cn
# T6Toy+rN+nM8M7LnLqCrO2JP3oW//1sfuZDKiDEb1AQ8es9Xr/u6bDTnYCTKIsDq
# 1BtmXUqEG1NqzJKS4kOmxkYp2WyODi7vQTCBZtVFJfVZ3j7OgWmnhFr4yUozZtqg
# PrHRVHhGNKlYzyjlroPxul+bgIspzOwbtmsgY1MCAwEAAaOCAV0wggFZMBIGA1Ud
# EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO9vU0rp5AZ8esrikFb2L9RJ7MtOMB8G
# A1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1UdDwEB/wQEAwIBhjAT
# BgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGG
# GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcwAoY1aHR0cDovL2Nh
# Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYD
# VR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0
# VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9
# bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQAXzvsWgBz+Bz0RdnEwvb4LyLU0pn/N0IfF
# iBowf0/Dm1wGc/Do7oVMY2mhXZXjDNJQa8j00DNqhCT3t+s8G0iP5kvN2n7Jd2E4
# /iEIUBO41P5F448rSYJ59Ib61eoalhnd6ywFLerycvZTAz40y8S4F3/a+Z1jEMK/
# DMm/axFSgoR8n6c3nuZB9BfBwAQYK9FHaoq2e26MHvVY9gCDA/JYsq7pGdogP8HR
# trYfctSLANEBfHU16r3J05qX3kId+ZOczgj5kjatVB+NdADVZKON/gnZruMvNYY2
# o1f4MXRJDMdTSlOLh0HCn2cQLwQCqjFbqrXuvTPSegOOzr4EWj7PtspIHBldNE2K
# 9i697cvaiIo2p61Ed2p8xMJb82Yosn0z4y25xUbI7GIN/TpVfHIqQ6Ku/qjTY6hc
# 3hsXMrS+U0yy+GWqAXam4ToWd2UQ1KYT70kZjE4YtL8Pbzg0c1ugMZyZZd/BdHLi
# Ru7hAWE6bTEm4XYRkA6Tl4KSFLFk43esaUeqGkH/wyW4N7OigizwJWeukcyIPbAv
# jSabnf7+Pu0VrFgoiovRDiyx3zEdmcif/sYQsfch28bZeUz2rtY/9TCA6TD8dC3J
# E3rYkrhLULy7Dc90G6e8BlqmyIjlgp2+VqsS9/wQD7yFylIz0scmbKvFoW2jNrbM
# 1pD2T7m3XDCCBY0wggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcN
# AQEMBQAwZTELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcG
# A1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJl
# ZCBJRCBSb290IENBMB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjEL
# MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
# LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0
# MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBp
# M+zCpyUuySE98orYWcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR
# 0uRf1gU8Ug9SH8aeFaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0
# O21x4i0MG+4g1ckgHWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53
# yEioZldXn1RYjgwrt0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4
# x7axxLVqGDgDEI3Y1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3Vd
# eGbZOjFEmjNAvwjXWkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1C
# doeJl2l6SPDgohIbZpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJh
# besz2cXfSwQAzH0clcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz
# 0YkH4b235kOkGLimdwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNB
# ERJb5RBQ6zHFynIWIgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+
# CiaZ9eRpL5gdLfXZqbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8w
# HQYDVR0OBBYEFOzX44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0
# ksuCMS1Ri6enIZ3zbcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGsw
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcw
# AoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElE
# Um9vdENBLmNydDBFBgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2Vy
# dC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYE
# VR0gADANBgkqhkiG9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqs
# oYcs7IVeqRq7IviHGmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPI
# TtAq3votVs/59PesMHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZ
# qPC/Lwum6fI0POz3A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/v
# oVA9/HYJaISfb8rbII01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+
# cWojayL/ErhULSd+2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDGCA3wwggN4
# AgEBMH0waTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEw
# PwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGluZyBSU0E0MDk2
# IFNIQTI1NiAyMDI1IENBMQIQCoDvGEuN8QWC0cR2p5V0aDANBglghkgBZQMEAgEF
# AKCB0TAaBgkqhkiG9w0BCQMxDQYLKoZIhvcNAQkQAQQwHAYJKoZIhvcNAQkFMQ8X
# DTI2MDQwNTIzMDAyN1owKwYLKoZIhvcNAQkQAgwxHDAaMBgwFgQU3WIwrIYKLTBr
# 2jixaHlSMAf7QX4wLwYJKoZIhvcNAQkEMSIEIPFXwYjoRLxfDoc3wX9nh+uy+8Cp
# urjqVPND31InTLNJMDcGCyqGSIb3DQEJEAIvMSgwJjAkMCIEIEqgP6Is11yExVyT
# j4KOZ2ucrsqzP+NtJpqjNPFGEQozMA0GCSqGSIb3DQEBAQUABIICAEaQfSXv1H1F
# XwOqPF5CI5uPI4rCniaaLj/Ax/ViIpOXBgLJOsdruqQ78EfzdjN/oXxc46bL1gSs
# HEvEuLeXK9Fdjg/iTPY7Mkwkhg22MXH1Y8VZK8ohpJHWnCCgUJ9igh+tcEY603p9
# bX+1K3DI0/sOh1wjftZlnb4gbiytPOdeA+5VzzyuQ9jPQcgOPwLEYnlka4/SUpLt
# LuZlssSGp91jsPZ5VggR0Tm25gV0oj6QrGtAn+uYF9zcTT34iWfuEPUO4bu5GtyQ
# DxvTQxqvLTvONxzSiD5AH7rvr0ZaVHxGuZZlZ7JUHZzk4A7XAcEQdXZZFy/gwmLy
# virhmpA/J30LlvTC4fSDXZPXywLS7u3QOyz+V35QGISoqc/AJeSF9XuxrPtArHoE
# c6u0f05Q2ymxPw0Ou+NViNe+daLqOfrbJM6rNEIWlOryaOMtUFST84lnJwIRzsy/
# cuYac/T9BlmxsUVYK/Xx+622uIAZrX7ZME4HcCbm3DcpHI3INinJF6A/nVOwQjmi
# wBJXC9AfV1KXr4/yZxIRUaS5gP/DYBnJp3BfazIqA+Aqm3tSbq/H3ouzo2iW5zM9
# rsYtbWH6hdzBOaCklAGgyJo4twGBeJnl0Yumg1rkgy19xh6wa6lT/PSRnRiusCrV
# Z4vP2hFkMBf7O8CS7lSDf0GW2LoAbAR5
# SIG # End signature block