AzureValidation/AzureADConfiguration.psm1

<#############################################################
# #
# Copyright (C) Microsoft Corporation. All rights reserved. #
# #
#############################################################>


$ErrorActionPreference = 'Stop'

Import-LocalizedData LocalizedData -BaseDirectory $PSScriptRoot -Filename AzureADConfiguration.Strings.psd1

$AzurePowerShellClientID = "1950a258-227b-4e31-a9cf-717495945fc2"
$AzurePowerShellReturnUri = "urn:ietf:wg:oauth:2.0:oob"

function Get-Endpoints([string] $CloudARMEndpoint) {
    <#
    .Synopsis
    Builds graph and login endpoints for a given CloudARMEndpoint
    #>


    $fullUri = $CloudARMEndpoint.TrimEnd('/')+"/metadata/endpoints?api-version=2015-01-01"
    Write-AzsReadinessLog -message "Getting Azure Endpoints from $fullUri" -type Info
    $response = Invoke-RestMethod -Uri $fullUri -ErrorAction Stop -UseBasicParsing -TimeoutSec 30

    $EndpointProperties = @{
        GraphUri = $response.graphEndpoint
        LoginUri = $response.authentication.loginEndpoint
        ManagementServiceUri = $response.authentication.audiences[0]
        ARMUri = $CloudARMEndpoint
    }

    return $EndpointProperties
}

function Get-AzToken {
    [CmdletBinding(DefaultParameterSetName = 'default')]
    param
    (
        # The Azure PowerShell context representing the context of a token to be resolved.
        [Parameter()]
        [ValidateNotNull()]
        [Microsoft.Azure.Commands.Common.Authentication.Abstractions.IAzureContext] $Context = (Get-AzContext -ErrorAction Stop),

        # The target resource for which a token should be resolved.
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $Resource = ($Context.Environment.ActiveDirectoryServiceEndpointResourceId),

        # The target tenantId in which a token should be resolved.
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $TenantId = ($t = if ($Context.Tenant) { $Context.Tenant } else { $Context.Subscription.TenantId }),

        # The account for which a token should be resolved.
        [Parameter()]
        [ValidateNotNullOrEmpty()]
        [string] $AccountId = ($Context.Account.Id),

        # Indicates that target token should be resolved from existing cache data (including a refresh token, if one is available).
        [Parameter(Mandatory = $true, ParameterSetName = 'FromCache')]
        [switch] $FromCache,

        # Indicates that all token cache data should be returned.
        [Parameter(ParameterSetName = 'FromCache')]
        [switch] $Raw
    )

    $originalErrorActionPreference = $ErrorActionPreference
    try {
        $ErrorActionPreference = 'Stop'

        Write-AzsReadinessLog -message "Attempting to retrieve a token for account '$AccountId' in tenant '$TenantId' for resource '$Resource'..." -type Info

        if (-not $FromCache) {
            $token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(
                ($account = $Context.Account),
                ($environment = $Context.Environment),
                ($tenant = $TenantId),
                ($password = $null),
                ($promptBehavior = 'Never'),
                ($promptAction = $null),
                ($tokenCache = $null),
                ($resourceIdEndpoint = $Resource))

            return [pscustomobject]@{ AccessToken = ConvertTo-SecureString $token.AccessToken -AsPlainText -Force } |
            Add-Member -MemberType ScriptMethod -Name 'GetAccessToken' -Value { return [System.Net.NetworkCredential]::new('$tokenType', $this.AccessToken).Password } -PassThru
        }
        else {
            Write-AzsReadinessLog -message "Attempting to find a refresh token and an access token from the existing token cache data..." -type Info
        }

        #
        # Resolve token cache data
        #

        [Microsoft.Azure.Commands.Common.Authentication.Authentication.Clients.AuthenticationClientFactory]$authenticationClientFactory = $null
        if (-not ([Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.TryGetComponent(
                    [Microsoft.Azure.Commands.Common.Authentication.Authentication.Clients.AuthenticationClientFactory]::AuthenticationClientFactoryKey,
                    [ref]$authenticationClientFactory))) {
            $m = 'Please ensure you have authenticated with Az Accounts module!'
            $m += ' Unable to resolve authentication client factory from Az Accounts module runtime'
            $m += ' ([Microsoft.Azure.Commands.Common.Authentication.Authentication.Clients.AuthenticationClientFactory])'
            Write-AzsReadinessLog -message $m -type Error -toscreen
            return
        }

        $client = $authenticationClientFactory.CreatePublicClient(
            ($clientId = '1950a258-227b-4e31-a9cf-717495945fc2'),
            ($TenantId),
            ($authority = if ($Context.Environment.EnableAdfsAuthentication) { $Context.Environment.ActiveDirectoryAuthority } else { '{0}/{1}' -f $Context.Environment.ActiveDirectoryAuthority.TrimEnd('/'), $TenantId }),
            ($redirectUri = $null),
            ($useAdfs = $Context.Environment.EnableAdfsAuthentication))

        $authenticationClientFactory.RegisterCache($client)

        $accounts = $client.GetAccountsAsync().ConfigureAwait($true).GetAwaiter().GetResult()

        $bytes = ([Microsoft.Identity.Client.ITokenCacheSerializer]$client.UserTokenCache).SerializeMsalV3()
        $json = [System.Text.Encoding]::UTF8.GetString($bytes)
        $data = ConvertFrom-Json $json

        Write-AzsReadinessLog -message "MSAL token cache deserialized ($($bytes.Length) bytes); Looking for target tokens..." -type Info

        foreach ($name in 'AccessToken', 'Account', 'AppMetadata', 'IdToken', 'RefreshToken') {
            $data | Add-Member -NotePropertyName "${name}s" -NotePropertyValue ((Get-Member -MemberType NoteProperty -InputObject $data."$name").Name | ForEach-Object { $data."$name"."$_" })
        }

        if ($Raw) {
            Write-AzsReadinessLog -message "Returning raw token cache data!" -type Warning
            Write-Output $data
            return
        }

        #
        # Resolve target account
        #

        $targetAccount = $accounts | Where-Object Username -EQ $AccountId

        if (-not $targetAccount -or $targetAccount.Count -gt 1) {
            Write-AzsReadinessLog -message "Unable to resolve acccount for identity '$identityId'; available accounts: $(ConvertTo-Json $accounts.Username -Compress)" -type Error -ToScreen
            return
        }
        
        Write-AzsReadinessLog -message "Target account resolved to: $(ConvertTo-Json $targetAccount -Compress)" -type Info

        #
        # Resolve target token(s)
        #

        $resolvedRefreshToken = $data.RefreshToken."$(Get-Member -InputObject $data.RefreshToken -MemberType NoteProperty |
            Where-Object { "$($_.Name)".StartsWith($targetAccount.HomeAccountId.Identifier, [System.StringComparison]::OrdinalIgnoreCase) } |
            Select-Object -ExpandProperty Name)"
.secret

        $resolvedAccessToken = Get-Member -InputObject $data.AccessToken -MemberType NoteProperty |
        ForEach-Object { $data.AccessToken."$($_.Name)" } | 
        Where-Object home_account_id -EQ $targetAccount.HomeAccountId.Identifier |
        Where-Object { (-not $_.realm) -or ($_.realm -eq $TenantId) } |
        Where-Object target -Like "*$Resource*" |
        Sort-Object expires_on -Descending |
        Select-Object -First 1 -ExpandProperty secret

        if (-not $resolvedAccessToken -and -not $resolvedRefreshToken) {
            Write-AzsReadinessLog -message "Unable to resolve an access token or refresh token for identity '$identityId' with the specified properties..." -type Error -ToScreen
            return
        }
        elseif (-not $resolvedAccessToken) {
            Write-AzsReadinessLog -message "Unable to resolve an access token for identity '$identityId' with the specified properties..." -type Warning
        }
        elseif (-not $resolvedRefreshToken) {
            Write-AzsReadinessLog -message "Unable to resolve a refresh token for identity '$identityId' with the specified properties..." -type Warning
        }

        $result = [pscustomobject]@{
            AccessToken  = if ($resolvedAccessToken) { ConvertTo-SecureString $resolvedAccessToken  -AsPlainText -Force } else { $null }
            RefreshToken = if ($resolvedRefreshToken) { ConvertTo-SecureString $resolvedRefreshToken -AsPlainText -Force } else { $null }
        }
    
        return $result |
        Add-Member -MemberType ScriptMethod -Name 'GetAccessToken' -Value { return [System.Net.NetworkCredential]::new('$tokenType', $this.AccessToken).Password } -PassThru |
        Add-Member -MemberType ScriptMethod -Name 'GetRefreshToken' -Value { return [System.Net.NetworkCredential]::new('$tokenType', $this.RefreshToken).Password } -PassThru
    }
    catch [Microsoft.Identity.Client.MsalUiRequiredException] {
        $msg = "Failed to retrieve a token. Login using Connect-AzAccount and specify the target tenant. Exception: $($_.exception.message)"
        Write-AzsReadinessLog -message $msg -type Error
        throw $msg
    }
    catch {
        $msg = "Failed to retrieve a token. Exception: $($_.exception.message)"
        Write-AzsReadinessLog -message $msg -type Error
        throw $msg
    }
    finally {
        $ErrorActionPreference = $originalErrorActionPreference
    }
}

function Get-AADTenantIds
{
    [OutputType([Hashtable])]
    [CmdletBinding()]
    param()

    $token = Get-AzToken
    $cToken = ConvertFrom-JwtToken $token.GetAccessToken()

    $UserInfo = @{
        FirstName = $cToken.claims.given_name
        LastName = $cToken.claims.family_name
        Account = $cToken.claims.unique_name
    }

    Write-AzsReadinessLog -message "Retrieving tenantIds..." -type Info
    $tenantsResponse = Invoke-RestMethod -Method Get -Uri "$($AzureURIs.ARMUri.TrimEnd('/'))/tenants?api-version=2016-02-01" -Headers @{Authorization = "Bearer $($token.GetAccessToken())"} -UseBasicParsing -TimeoutSec 30
    $tenantIds = [Array]$tenantsResponse.Value.tenantId

    return @{
                UserInfo = $UserInfo
                TenantIds = $tenantIds
            }
}

function Invoke-Graph($method, $uri, $authorization, $body)
{

    $params = @{
        Method  = $method
        Uri     = "$($uri)?api-version=1.6"
        Headers = @{ Authorization = $authorization }
        UseBasicParsing = $true
        TimeoutSec = 30
        ContentType = 'application/json'
    }
    if ($body) { $params += @{ Body = $body } }

    try
    {
        return (Invoke-WebRequest @params -ErrorAction Stop).Content | ConvertFrom-Json
    } 
    catch
    {
        if ($_.Exception.Response.StatusCode -eq 'Internal Server Error')
        {
            Write-AzsReadinessLog -message ("$LocalizedData.GraphEndpointError" -f $graphUri) -type Error
            throw ($LocalizedData.GraphEndpointError -f $graphUri)
        }
        if ($_.Exception.Response.StatusCode -eq 'Forbidden')
        {
            Write-AzsReadinessLog -message ("{0} StatusCode: Forbidden" -f $graphUri) -type Error
            throw "Forbidden: Login using Connect-AzAccount and specify the target tenant."
        }
        else
        {
            Write-AzsReadinessLog -message ("{0} throw an exception: {1}" -f $graphUri, $_) -type Error
            throw $_
        }  
    }

}

function Get-AADTenantDetail
{
    [OutputType([Hashtable])]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true)]
        [string]
        $TenantId,

        [Parameter(Mandatory=$true)]
        [string]
        $UserId,

        [Parameter(Mandatory=$true)]
        [Hashtable]
        $AzureURIs
    )

    try
    {
        $token = Get-AzToken -resource (Get-AzContext).Environment.GraphEndpointResourceId -TenantId $TenantId
    }
    catch {
            throw $_
    }
    
    # Validating if the user has required permission
    $authorization = "Bearer $($token.GetAccessToken())"
    
    $accessTokenObj = ConvertFrom-JwtToken -JwtToken $token.GetAccessToken()
    $postUri = $azureURIs.GraphUri.TrimEnd('/')+"/$($TenantId)/directoryObjects/$($accessTokenObj.Claims.oid)/getMemberObjects"
    Write-AzsReadinessLog -message "Retrieving member objects for user." -type Info
    $roles = Invoke-Graph -method Post -uri $postUri -authorization $authorization -body ( ConvertTo-Json -Depth 1 @{ securityEnabledOnly = $false } ) | Select-Object -ExpandProperty value
    if( $roles -eq $null ){ return $null }

    $getUri = $azureURIs.GraphUri.TrimEnd('/')+"/$($TenantId)/directoryRoles"
    Write-AzsReadinessLog -message "Retrieving administrator role in directory." -type Info
    $roleOid = Invoke-Graph -method Get -uri $getUri -authorization $authorization | Select-Object -ExpandProperty Value | Where displayName -EQ 'Company Administrator' | Select-Object -ExpandProperty objectId
    
    if ($roleOid -notin $roles) { 
        Write-AzsReadinessLog -message "Administrator role not present in user roles." -type Error
        return $null 
    }
    Write-AzsReadinessLog -message "Success. Administrator role present in user roles." -type Info
    $tenantResponse = Invoke-RestMethod -Method Get -Uri "$($AzureURIs.GraphUri.TrimEnd('/'))/myOrganization/tenantDetails?api-version=1.5" -Headers @{Authorization = "Bearer $($token.GetAccessToken())"} -UseBasicParsing -TimeoutSec 30

    return @{
                Id = $tenantResponse.Value.objectId
                DisplayName = $tenantResponse.Value.DisplayName
                DomainName = ($tenantResponse.Value.VerifiedDomains | Where {$_.default}).name
                Token = $token.GetAccessToken()
            }
}

function Get-AADTenantDetails
{
    <#
    .SYNOPSIS
        Resolve Azure Tenant Details
    .DESCRIPTION
        Resolve Azure Tenant Details
    .EXAMPLE
        Get-AADTenantDetails -AADDirectoryTenantName azurestack.contoso.com
    .OUTPUTS
        Hashtable of tenant details
    .NOTES
        General notes
    #>

    [OutputType([Hashtable])]
    [CmdletBinding()]
    param(

        [Parameter(Mandatory=$false)]
        [string] $AADDirectoryTenantName

    )

    Write-Progress -Activity $LocalizedData.InfoSigningIn

    $azureURIs = Get-Endpoints (Get-AzContext).Environment.ResourceManagerUrl
    $TenantsInfo = Get-AADTenantIds
    $TenantIds = [Array] $TenantsInfo.TenantIds
    $tenantDetails = @()
    # Workaround to add the target tenant Id if specified to the list of "resolved" tenant memberships
    if ($AADDirectoryTenantName)
    {
        Write-AzsReadinessLog -message "Retrieving tenant detail from target tenant $AADDirectoryTenantName" -type Info
        $targetTenantId = Get-TenantIdFromName -tenantName $AADDirectoryTenantName
        $tenantDetail =  Get-AADTenantDetail -TenantId $targetTenantId -UserId $TenantsInfo.UserInfo.Account -AzureURIs $azureURIs
        if ($tenantDetail -ne $null)
        {
            Write-AzsReadinessLog -message ("Success. Target directory ({0}...{1}) returned result." -f $targetTenantId.split('-')[0],$targetTenantId.split('-')[-1]) -type Info
            $tenantDetails = $tenantDetail
        }
        else {      
            for ($i = 1; $i -le $TenantIds.Count; $i++)
            {
                Write-Progress -Activity $LocalizedData.InfoSigningIn -PercentComplete ($i * 100 / ($TenantIds.Count + 1))
                $tid = $TenantIds[$i-1]
                Write-AzsReadinessLog -message ("Retrieving tenant detail from tenant {0}...{1}" -f $tid.split('-')[0],$tid.split('-')[-1]) -type Info
                $tenantDetail =  Get-AADTenantDetail -TenantId $tid -UserId $TenantsInfo.UserInfo.Account -AzureURIs $azureURIs
                if ($tenantDetail -ne $null)
                {
                    Write-AzsReadinessLog -message ("Success. Directory ({0}...{1}) returned result." -f $tid.split('-')[0],$tid.split('-')[-1]) -type Info
                    $tenantDetails += $tenantDetail
                }
            }
        }
    }

    Write-Progress -Activity $LocalizedData.InfoSigningIn -Completed

    return @{
                UserInfo = $TenantsInfo.UserInfo
                TenantDetails = $tenantDetails
            }
}

<#
.Synopsis
Decodes a base64-encoded string.
#>

function ConvertFrom-Base64String
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [ValidateNotNull()]
        [string] $Base64String
    )
    process
    {
        $data = $Base64String.Replace('-','+').Replace('_','/')
        switch ($data.Length % 4)
        {
            0 { break }
            2 { $data += '==' }
            3 { $data += '=' }
            default { Write-Error "Invalid data: '$data'" }
    }
        $bytes = [System.Convert]::FromBase64String($data)
        Write-Output $bytes
    }
}

<#
.Synopsis
Converts a Jwt Token into an object.
#>

function ConvertFrom-JwtToken
{
    [CmdletBinding()]
    Param
    (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string] $JwtToken
    )

    function ConvertFrom-RawData([String]$Data)
    {
        [System.Text.Encoding]::UTF8.GetString((ConvertFrom-Base64String $Data)) | ConvertFrom-Json | Write-Output
    }

    $parts = $JwtToken.Split('.')
    [pscustomobject]@{
        Headers = ConvertFrom-RawData $parts[0]
        Claims = ConvertFrom-RawData $parts[1]
        Signature = $parts[2]
    } | Write-Output
}

function Get-AzureADTenantDetails
{
    <#
    .SYNOPSIS
        Resolve Azure AD Tenant Details
    .DESCRIPTION
        Resolve Azure AD Tenant Details
    .EXAMPLE
        Get-AzureADTenantDetails -AADDirectoryTenantName $AADDirectoryTenantName
    .INPUTS
        AzureEnvironment - string - should be AzureCloud, AzureChinaCloud, AzureGermanCloud or CustomCloud
        AADAdminCredential - PSCredential - AAD Admin Credential
        AADDirectoryTenantName - string - AAD tenant Name
    .OUTPUTS
        Hashtable of Azure AD tenant details
    .NOTES
        General notes
    #>

    [OutputType([Hashtable])]
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$false)]
        [string] $AADDirectoryTenantName
    )

    $claimsProvider = (Get-AzContext).Environment.Name

    $azureResult = Get-AADTenantDetails -AADDirectoryTenantName $AADDirectoryTenantName
    $azureToken = $null
    #$azureRefreshToken = $null
    $tenantId = $null
    $tenantName = $null

    if ($azureResult.TenantDetails -eq $null -or @($azureResult.TenantDetails).Count -eq 0)
    {
        Write-AzsReadinessLog -message ($LocalizedData.AADAccountNotAdmin -f $($azureResult.UserInfo.Account)) -type Error -toScreen
    }
    elseif (@($azureResult.TenantDetails).Count -gt 1 -and -not $AADDirectoryTenantName)
    {
        Write-AzsReadinessLog -message ($LocalizedData.MoreThanOneTenant -f @($($azureResult.UserInfo.Account), $($azureResult.TenantDetails.DomainName -join ', '))) -type Error -toScreen
    }
    elseif (@($azureResult.TenantDetails).Count -ige 1 -and $AADDirectoryTenantName)
    {
        $tenantDetail = $azureResult.TenantDetails | Where-Object DomainName -eq $AADDirectoryTenantName
        if ($tenantDetail)
        {
            $azureToken = $tenantDetail.Token
            #$azureRefreshToken = $tenantDetail.RefreshToken
            $tenantName = $tenantDetail.DomainName
            $tenantID = $tenantDetail.ID
        }
        else
        {
            Write-AzsReadinessLog -message ($LocalizedData.NotAdminOfTenant -f @($($azureResult.UserInfo.Account), $AADDirectoryTenantName, $($azureResult.TenantDetails.DomainName -join ', '))) -type Error -toScreen
        }
    }
    else
    {
        $azureToken = $azureResult.TenantDetails.Token
        #$azureRefreshToken = $azureResult.TenantDetails.RefreshToken
        $tenantName = $azureResult.TenantDetails.DomainName
        $tenantID = $azureResult.TenantDetails.ID
    }

    $adminSubscriptionOwner = (ConvertFrom-JwtToken $azureToken).Claims.unique_name

    return @{
                UserName = $azureResult.UserInfo.Account
                Token = $azureToken
                #RefreshToken = $azureRefreshToken
                AdminSubscriptionOwner = $adminSubscriptionOwner
                TenantDirectoryName = $tenantName
                TenantDirectoryID = $tenantID
                ClaimsProvider = $claimsProvider
            }
}

<#
.SYNOPSIS
    Returns Azure AD directory tenant ID given the login endpoint and the directory tenant name
.DESCRIPTION
    Makes an unauthenticated REST call to the given Azure environment's login endpoint to retrieve directory tenant id
.EXAMPLE
$tenantId = Get-TenantIdFromName -azureEnvironment "Public Azure" -tenantName "msazurestack.onmicrosoft.com"
#>

function Get-TenantIdFromName
{
    [CmdletBinding()]
    param
    (
        [Parameter(Mandatory=$true)]
        [ValidateNotNull()]
        [string] $tenantName
     )

    $azureURIs = Get-Endpoints (Get-AzContext).Environment.ResourceManagerUrl
    $uri = "{0}/{1}/.well-known/openid-configuration" -f ($azureURIs.LoginUri).TrimEnd('/'), $tenantName

    $response = Invoke-RestMethod -Uri $uri -Method Get -UseBasicParsing -TimeoutSec 30

    Write-AzsReadinessLog -message "using token_endpoint $($response.token_endpoint) to parse tenant id" -type Info
    $tenantId = $response.token_endpoint.Split('/')[3]

    $tenantIdGuid = [guid]::NewGuid()
    $result = [guid]::TryParse($tenantId, [ref] $tenantIdGuid)

    if(-not $result)
    {
        Write-AzsReadinessLog -message "Error obtaining tenant id from tenant name" -type Error
    }
    else
    {
        Write-AzsReadinessLog -message "Success. Tenant Name: $tenantName Tenant id: $tenantId" -type Info
        return $tenantId
    }
}

Export-ModuleMember -Function Get-AzureADTenantDetails
Export-ModuleMember -Function Get-Endpoints
Export-ModuleMember -Function Get-TenantIdFromName
Export-ModuleMember -Function Get-AzToken
Export-ModuleMember -Function ConvertFrom-JwtToken
# SIG # Begin signature block
# MIIjjwYJKoZIhvcNAQcCoIIjgDCCI3wCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDT9ThdDokaV3lX
# v/SFPs8ySNwd6jEX2fDpJNCmL3qqDaCCDYEwggX/MIID56ADAgECAhMzAAABh3IX
# chVZQMcJAAAAAAGHMA0GCSqGSIb3DQEBCwUAMH4xCzAJBgNVBAYTAlVTMRMwEQYD
# VQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMTH01pY3Jvc29mdCBDb2RlIFNpZ25p
# bmcgUENBIDIwMTEwHhcNMjAwMzA0MTgzOTQ3WhcNMjEwMzAzMTgzOTQ3WjB0MQsw
# CQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9u
# ZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMR4wHAYDVQQDExVNaWNy
# b3NvZnQgQ29ycG9yYXRpb24wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIB
# AQDOt8kLc7P3T7MKIhouYHewMFmnq8Ayu7FOhZCQabVwBp2VS4WyB2Qe4TQBT8aB
# znANDEPjHKNdPT8Xz5cNali6XHefS8i/WXtF0vSsP8NEv6mBHuA2p1fw2wB/F0dH
# sJ3GfZ5c0sPJjklsiYqPw59xJ54kM91IOgiO2OUzjNAljPibjCWfH7UzQ1TPHc4d
# weils8GEIrbBRb7IWwiObL12jWT4Yh71NQgvJ9Fn6+UhD9x2uk3dLj84vwt1NuFQ
# itKJxIV0fVsRNR3abQVOLqpDugbr0SzNL6o8xzOHL5OXiGGwg6ekiXA1/2XXY7yV
# Fc39tledDtZjSjNbex1zzwSXAgMBAAGjggF+MIIBejAfBgNVHSUEGDAWBgorBgEE
# AYI3TAgBBggrBgEFBQcDAzAdBgNVHQ4EFgQUhov4ZyO96axkJdMjpzu2zVXOJcsw
# UAYDVR0RBEkwR6RFMEMxKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVyYXRpb25zIFB1
# ZXJ0byBSaWNvMRYwFAYDVQQFEw0yMzAwMTIrNDU4Mzg1MB8GA1UdIwQYMBaAFEhu
# ZOVQBdOCqhc3NyK1bajKdQKVMFQGA1UdHwRNMEswSaBHoEWGQ2h0dHA6Ly93d3cu
# bWljcm9zb2Z0LmNvbS9wa2lvcHMvY3JsL01pY0NvZFNpZ1BDQTIwMTFfMjAxMS0w
# Ny0wOC5jcmwwYQYIKwYBBQUHAQEEVTBTMFEGCCsGAQUFBzAChkVodHRwOi8vd3d3
# Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL01pY0NvZFNpZ1BDQTIwMTFfMjAx
# MS0wNy0wOC5jcnQwDAYDVR0TAQH/BAIwADANBgkqhkiG9w0BAQsFAAOCAgEAixmy
# S6E6vprWD9KFNIB9G5zyMuIjZAOuUJ1EK/Vlg6Fb3ZHXjjUwATKIcXbFuFC6Wr4K
# NrU4DY/sBVqmab5AC/je3bpUpjtxpEyqUqtPc30wEg/rO9vmKmqKoLPT37svc2NV
# BmGNl+85qO4fV/w7Cx7J0Bbqk19KcRNdjt6eKoTnTPHBHlVHQIHZpMxacbFOAkJr
# qAVkYZdz7ikNXTxV+GRb36tC4ByMNxE2DF7vFdvaiZP0CVZ5ByJ2gAhXMdK9+usx
# zVk913qKde1OAuWdv+rndqkAIm8fUlRnr4saSCg7cIbUwCCf116wUJ7EuJDg0vHe
# yhnCeHnBbyH3RZkHEi2ofmfgnFISJZDdMAeVZGVOh20Jp50XBzqokpPzeZ6zc1/g
# yILNyiVgE+RPkjnUQshd1f1PMgn3tns2Cz7bJiVUaqEO3n9qRFgy5JuLae6UweGf
# AeOo3dgLZxikKzYs3hDMaEtJq8IP71cX7QXe6lnMmXU/Hdfz2p897Zd+kU+vZvKI
# 3cwLfuVQgK2RZ2z+Kc3K3dRPz2rXycK5XCuRZmvGab/WbrZiC7wJQapgBodltMI5
# GMdFrBg9IeF7/rP4EqVQXeKtevTlZXjpuNhhjuR+2DMt/dWufjXpiW91bo3aH6Ea
# jOALXmoxgltCp1K7hrS6gmsvj94cLRf50QQ4U8Qwggd6MIIFYqADAgECAgphDpDS
# AAAAAAADMA0GCSqGSIb3DQEBCwUAMIGIMQswCQYDVQQGEwJVUzETMBEGA1UECBMK
# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0
# IENvcnBvcmF0aW9uMTIwMAYDVQQDEylNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0
# ZSBBdXRob3JpdHkgMjAxMTAeFw0xMTA3MDgyMDU5MDlaFw0yNjA3MDgyMTA5MDla
# MH4xCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdS
# ZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKDAmBgNVBAMT
# H01pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBIDIwMTEwggIiMA0GCSqGSIb3DQEB
# AQUAA4ICDwAwggIKAoICAQCr8PpyEBwurdhuqoIQTTS68rZYIZ9CGypr6VpQqrgG
# OBoESbp/wwwe3TdrxhLYC/A4wpkGsMg51QEUMULTiQ15ZId+lGAkbK+eSZzpaF7S
# 35tTsgosw6/ZqSuuegmv15ZZymAaBelmdugyUiYSL+erCFDPs0S3XdjELgN1q2jz
# y23zOlyhFvRGuuA4ZKxuZDV4pqBjDy3TQJP4494HDdVceaVJKecNvqATd76UPe/7
# 4ytaEB9NViiienLgEjq3SV7Y7e1DkYPZe7J7hhvZPrGMXeiJT4Qa8qEvWeSQOy2u
# M1jFtz7+MtOzAz2xsq+SOH7SnYAs9U5WkSE1JcM5bmR/U7qcD60ZI4TL9LoDho33
# X/DQUr+MlIe8wCF0JV8YKLbMJyg4JZg5SjbPfLGSrhwjp6lm7GEfauEoSZ1fiOIl
# XdMhSz5SxLVXPyQD8NF6Wy/VI+NwXQ9RRnez+ADhvKwCgl/bwBWzvRvUVUvnOaEP
# 6SNJvBi4RHxF5MHDcnrgcuck379GmcXvwhxX24ON7E1JMKerjt/sW5+v/N2wZuLB
# l4F77dbtS+dJKacTKKanfWeA5opieF+yL4TXV5xcv3coKPHtbcMojyyPQDdPweGF
# RInECUzF1KVDL3SV9274eCBYLBNdYJWaPk8zhNqwiBfenk70lrC8RqBsmNLg1oiM
# CwIDAQABo4IB7TCCAekwEAYJKwYBBAGCNxUBBAMCAQAwHQYDVR0OBBYEFEhuZOVQ
# BdOCqhc3NyK1bajKdQKVMBkGCSsGAQQBgjcUAgQMHgoAUwB1AGIAQwBBMAsGA1Ud
# DwQEAwIBhjAPBgNVHRMBAf8EBTADAQH/MB8GA1UdIwQYMBaAFHItOgIxkEO5FAVO
# 4eqnxzHRI4k0MFoGA1UdHwRTMFEwT6BNoEuGSWh0dHA6Ly9jcmwubWljcm9zb2Z0
# LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcmwwXgYIKwYBBQUHAQEEUjBQME4GCCsGAQUFBzAChkJodHRwOi8vd3d3Lm1p
# Y3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dDIwMTFfMjAxMV8wM18y
# Mi5jcnQwgZ8GA1UdIASBlzCBlDCBkQYJKwYBBAGCNy4DMIGDMD8GCCsGAQUFBwIB
# FjNodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2RvY3MvcHJpbWFyeWNw
# cy5odG0wQAYIKwYBBQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AcABvAGwAaQBjAHkA
# XwBzAHQAYQB0AGUAbQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAGfyhqWY
# 4FR5Gi7T2HRnIpsLlhHhY5KZQpZ90nkMkMFlXy4sPvjDctFtg/6+P+gKyju/R6mj
# 82nbY78iNaWXXWWEkH2LRlBV2AySfNIaSxzzPEKLUtCw/WvjPgcuKZvmPRul1LUd
# d5Q54ulkyUQ9eHoj8xN9ppB0g430yyYCRirCihC7pKkFDJvtaPpoLpWgKj8qa1hJ
# Yx8JaW5amJbkg/TAj/NGK978O9C9Ne9uJa7lryft0N3zDq+ZKJeYTQ49C/IIidYf
# wzIY4vDFLc5bnrRJOQrGCsLGra7lstnbFYhRRVg4MnEnGn+x9Cf43iw6IGmYslmJ
# aG5vp7d0w0AFBqYBKig+gj8TTWYLwLNN9eGPfxxvFX1Fp3blQCplo8NdUmKGwx1j
# NpeG39rz+PIWoZon4c2ll9DuXWNB41sHnIc+BncG0QaxdR8UvmFhtfDcxhsEvt9B
# xw4o7t5lL+yX9qFcltgA1qFGvVnzl6UJS0gQmYAf0AApxbGbpT9Fdx41xtKiop96
# eiL6SJUfq/tHI4D1nvi/a7dLl+LrdXga7Oo3mXkYS//WsyNodeav+vyL6wuA6mk7
# r/ww7QRMjt/fdW1jkT3RnVZOT7+AVyKheBEyIXrvQQqxP/uozKRdwaGIm1dxVk5I
# RcBCyZt2WwqASGv9eZ/BvW1taslScxMNelDNMYIVZDCCFWACAQEwgZUwfjELMAkG
# A1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQx
# HjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEoMCYGA1UEAxMfTWljcm9z
# b2Z0IENvZGUgU2lnbmluZyBQQ0EgMjAxMQITMwAAAYdyF3IVWUDHCQAAAAABhzAN
# BglghkgBZQMEAgEFAKCBrjAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgor
# BgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAvBgkqhkiG9w0BCQQxIgQg5wG2CpaH
# 9teEfo5pOzEv8uS0V9KW/0GjzTWG8fiKx18wQgYKKwYBBAGCNwIBDDE0MDKgFIAS
# AE0AaQBjAHIAbwBzAG8AZgB0oRqAGGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbTAN
# BgkqhkiG9w0BAQEFAASCAQCsNXX2zJEDILVe9I+HrB5J7xOuQhF5HZhWVzLwze1g
# V7xkOPH78pfRAEaAVfdJ2+m+FSBLReQin14y8/vuuRBUyrcrSGBiN9bS/HVRGaFV
# k7mVoyJeAXgDo617QxKPtyifE+EwQ6+0HVy7h6j9xJ0uhILUwg0CECsbxNScLejm
# GL5mV18xTc3ePzu7KUfOfnt2ZnjvV2ctv0RWpUJlRfGmNymF8l2/tg+yZ4Q6oQzN
# HXtZOKCPZGN4ywLe44tUNExM7ass8EZvyHYWMEBpa9Yg+uGfLf1EF57sdEhiA6P4
# xLDVzU2R+jbzYmSOM8Cshf+2as0Cb+YUAIwEP7A4zqMloYIS7jCCEuoGCisGAQQB
# gjcDAwExghLaMIIS1gYJKoZIhvcNAQcCoIISxzCCEsMCAQMxDzANBglghkgBZQME
# AgEFADCCAVUGCyqGSIb3DQEJEAEEoIIBRASCAUAwggE8AgEBBgorBgEEAYRZCgMB
# MDEwDQYJYIZIAWUDBAIBBQAEIAEdwK1c/QU9XZm2lxCFeavtPD0IchjbCTxAlGCM
# 34D+AgZfdI8iyWsYEzIwMjAxMDA3MTQwNTU3LjUyOFowBIACAfSggdSkgdEwgc4x
# CzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRt
# b25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1p
# Y3Jvc29mdCBPcGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMg
# VFNTIEVTTjpGNzdGLUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgU2VydmljZaCCDkEwggT1MIID3aADAgECAhMzAAABKugXlviGp++jAAAA
# AAEqMA0GCSqGSIb3DQEBCwUAMHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNo
# aW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29y
# cG9yYXRpb24xJjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEw
# MB4XDTE5MTIxOTAxMTUwMloXDTIxMDMxNzAxMTUwMlowgc4xCzAJBgNVBAYTAlVT
# MRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQK
# ExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBPcGVy
# YXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpGNzdG
# LUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vydmlj
# ZTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAJ/flYGkhdJtxSsHBu9l
# mXF/UXxPF7L45nEhmtd01KDosWbY8y54BN7+k9DMvzqToP39v8/Z+NtEzKj8Bf5E
# QoG1/pJfpzCJe80HZqyqMo0oQ9EugVY6YNVNa2T1u51d96q1hFmu1dgxt8uD2g7I
# pBQdhS2tpc3j3HEzKvV/vwEr7/BcTuwqUHqrrBgHc971epVR4o5bNKsjikawmMw9
# D/tyrTciy3F9Gq9pEgk8EqJfOdAabkanuAWTjlmBhZtRiO9W1qFpwnu9G5qVvdNK
# RKxQdtxMC04pWGfnxzDac7+jIql532IEC5QSnvY84szEpxw31QW/LafSiDmAtYWH
# pm8CAwEAAaOCARswggEXMB0GA1UdDgQWBBRw9MUtdCs/rhN2y9EkE6ZI9O8TaTAf
# BgNVHSMEGDAWgBTVYzpcijGQ80N7fEYbxTNoWoVtVTBWBgNVHR8ETzBNMEugSaBH
# hkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtpL2NybC9wcm9kdWN0cy9NaWNU
# aW1TdGFQQ0FfMjAxMC0wNy0wMS5jcmwwWgYIKwYBBQUHAQEETjBMMEoGCCsGAQUF
# BzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY1RpbVN0
# YVBDQV8yMDEwLTA3LTAxLmNydDAMBgNVHRMBAf8EAjAAMBMGA1UdJQQMMAoGCCsG
# AQUFBwMIMA0GCSqGSIb3DQEBCwUAA4IBAQCKwDT0CnHVo46OWyUbrPIj8QIcf+PT
# jBVYpKg1K2D15Z6xEuvmf+is6N8gj9f1nkFIALvh+iGkx8GgGa/oA9IhXNEFYPNF
# aHwHan/UEw1P6Tjdaqy3cvLC8f8zE1CR1LhXNofq6xfoT9HLGFSg9skPLM1TQ+RA
# QX9MigEm8FFlhhsQ1iGB1399x8d92h9KspqGDnO96Z9Aj7ObDtdU6RoZrsZkiRQN
# nXmnX1I+RuwtLu8MN8XhJLSl5wqqHM3rqaaMvSAISVtKySpzJC5Zh+5kJlqFdSiI
# HW8Q+8R6EWG8ILb9Pf+w/PydyK3ZTkVXUpFA+JhWjcyzphVGw9ffj0YKMIIGcTCC
# BFmgAwIBAgIKYQmBKgAAAAAAAjANBgkqhkiG9w0BAQsFADCBiDELMAkGA1UEBhMC
# VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
# BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEyMDAGA1UEAxMpTWljcm9zb2Z0IFJv
# b3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTAwHhcNMTAwNzAxMjEzNjU1WhcN
# MjUwNzAxMjE0NjU1WjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3Rv
# bjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0
# aW9uMSYwJAYDVQQDEx1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDCCASIw
# DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKkdDbx3EYo6IOz8E5f1+n9plGt0
# VBDVpQoAgoX77XxoSyxfxcPlYcJ2tz5mK1vwFVMnBDEfQRsalR3OCROOfGEwWbEw
# RA/xYIiEVEMM1024OAizQt2TrNZzMFcmgqNFDdDq9UeBzb8kYDJYYEbyWEeGMoQe
# dGFnkV+BVLHPk0ySwcSmXdFhE24oxhr5hoC732H8RsEnHSRnEnIaIYqvS2SJUGKx
# Xf13Hz3wV3WsvYpCTUBR0Q+cBj5nf/VmwAOWRH7v0Ev9buWayrGo8noqCjHw2k4G
# kbaICDXoeByw6ZnNPOcvRLqn9NxkvaQBwSAJk3jN/LzAyURdXhacAQVPIk0CAwEA
# AaOCAeYwggHiMBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTVYzpcijGQ80N7
# fEYbxTNoWoVtVTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMC
# AYYwDwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTV9lbLj+iiXGJo0T2UkFvX
# zpoYxDBWBgNVHR8ETzBNMEugSaBHhkVodHRwOi8vY3JsLm1pY3Jvc29mdC5jb20v
# cGtpL2NybC9wcm9kdWN0cy9NaWNSb29DZXJBdXRfMjAxMC0wNi0yMy5jcmwwWgYI
# KwYBBQUHAQEETjBMMEoGCCsGAQUFBzAChj5odHRwOi8vd3d3Lm1pY3Jvc29mdC5j
# b20vcGtpL2NlcnRzL01pY1Jvb0NlckF1dF8yMDEwLTA2LTIzLmNydDCBoAYDVR0g
# AQH/BIGVMIGSMIGPBgkrBgEEAYI3LgMwgYEwPQYIKwYBBQUHAgEWMWh0dHA6Ly93
# d3cubWljcm9zb2Z0LmNvbS9QS0kvZG9jcy9DUFMvZGVmYXVsdC5odG0wQAYIKwYB
# BQUHAgIwNB4yIB0ATABlAGcAYQBsAF8AUABvAGwAaQBjAHkAXwBTAHQAYQB0AGUA
# bQBlAG4AdAAuIB0wDQYJKoZIhvcNAQELBQADggIBAAfmiFEN4sbgmD+BcQM9naOh
# IW+z66bM9TG+zwXiqf76V20ZMLPCxWbJat/15/B4vceoniXj+bzta1RXCCtRgkQS
# +7lTjMz0YBKKdsxAQEGb3FwX/1z5Xhc1mCRWS3TvQhDIr79/xn/yN31aPxzymXlK
# kVIArzgPF/UveYFl2am1a+THzvbKegBvSzBEJCI8z+0DpZaPWSm8tv0E4XCfMkon
# /VWvL/625Y4zu2JfmttXQOnxzplmkIz/amJ/3cVKC5Em4jnsGUpxY517IW3DnKOi
# PPp/fZZqkHimbdLhnPkd/DjYlPTGpQqWhqS9nhquBEKDuLWAmyI4ILUl5WTs9/S/
# fmNZJQ96LjlXdqJxqgaKD4kWumGnEcua2A5HmoDF0M2n0O99g/DhO3EJ3110mCII
# YdqwUB5vvfHhAN/nMQekkzr3ZUd46PioSKv33nJ+YWtvd6mBy6cJrDm77MbL2IK0
# cs0d9LiFAR6A+xuJKlQ5slvayA1VmXqHczsI5pgt6o3gMy4SKfXAL1QnIffIrE7a
# KLixqduWsqdCosnPGUFN4Ib5KpqjEWYw07t0MkvfY3v1mYovG8chr1m1rtxEPJdQ
# cdeh0sVV42neV8HR3jDA/czmTfsNv11P6Z0eGTgvvM9YBS7vDaBQNdrvCScc1bN+
# NR4Iuto229Nfj950iEkSoYICzzCCAjgCAQEwgfyhgdSkgdEwgc4xCzAJBgNVBAYT
# AlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYD
# VQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xKTAnBgNVBAsTIE1pY3Jvc29mdCBP
# cGVyYXRpb25zIFB1ZXJ0byBSaWNvMSYwJAYDVQQLEx1UaGFsZXMgVFNTIEVTTjpG
# NzdGLUUzNTYtNUJBRTElMCMGA1UEAxMcTWljcm9zb2Z0IFRpbWUtU3RhbXAgU2Vy
# dmljZaIjCgEBMAcGBSsOAwIaAxUA6rLmrKHyIMP76ePl321xKUJ3YX+ggYMwgYCk
# fjB8MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMH
# UmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSYwJAYDVQQD
# Ex1NaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EgMjAxMDANBgkqhkiG9w0BAQUFAAIF
# AOMoR+0wIhgPMjAyMDEwMDcxNzU4MDVaGA8yMDIwMTAwODE3NTgwNVowdDA6Bgor
# BgEEAYRZCgQBMSwwKjAKAgUA4yhH7QIBADAHAgEAAgIO1jAHAgEAAgISvjAKAgUA
# 4ymZbQIBADA2BgorBgEEAYRZCgQCMSgwJjAMBgorBgEEAYRZCgMCoAowCAIBAAID
# B6EgoQowCAIBAAIDAYagMA0GCSqGSIb3DQEBBQUAA4GBAGdOI1pVfo9C7r1pzi4d
# QKaORfxzrcNhOEQ9A5E82bBjcFdV+rq7dLnUFnmCz6Gc3mKLoOW8XREyNfvznFLR
# TZqH3kAk/bSdMIwfGroxn6VB3sO9fFm8VEfUUd8JLGZsBNWxBMlWncnu9Wuz1PnD
# IKsLoxfxFPufzlkWdPkdqrO0MYIDDTCCAwkCAQEwgZMwfDELMAkGA1UEBhMCVVMx
# EzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoT
# FU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEmMCQGA1UEAxMdTWljcm9zb2Z0IFRpbWUt
# U3RhbXAgUENBIDIwMTACEzMAAAEq6BeW+Ian76MAAAAAASowDQYJYIZIAWUDBAIB
# BQCgggFKMBoGCSqGSIb3DQEJAzENBgsqhkiG9w0BCRABBDAvBgkqhkiG9w0BCQQx
# IgQgNKDVUxR8C75ZWt2OmUTa5TEmg3zNY8eWrLzRBG5UeScwgfoGCyqGSIb3DQEJ
# EAIvMYHqMIHnMIHkMIG9BCBDmDWEWvc6fhs5t4Woo5Q+FMFCcaIgV4yUP4CpuBmL
# mTCBmDCBgKR+MHwxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw
# DgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24x
# JjAkBgNVBAMTHU1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQSAyMDEwAhMzAAABKugX
# lviGp++jAAAAAAEqMCIEII+JwmaucF+hPPWsqi5XfoihYnFKOyOv9jNnHwiSG/So
# MA0GCSqGSIb3DQEBCwUABIIBAGBNbrOOGuD3ZWtPuN4YCFQUPxmeekUawPF0xwo3
# XdKULw3mbMnbsnh2UhUiWkKhCgEARutCsibM3ugvQyD7priwrRD6IK0h5oZDo17L
# LB2sAOY+8Q06D9+/NZJiNWjdDjoC4tldmaJPO/YwPWHMup+/mYJvqLZpJsq2Rlxc
# MxCp+PBxm0TWlVcaxJUXWe/3pwf8eZZusVKiyiH5iXX02J01SewQGuyZIR67LGAB
# bFmvmHUT+9I4g+TovESjeNNpJRTfl5pVYhGftjh8h1HgLWQ4vzkz4oYqFS/YLrmg
# 1e5i0U8zfXjAQ3rh7pN9kVzvLX8+1Pbwtigj64Fh0JN+NWo=
# SIG # End signature block