Test-SmtpSaslAuthBlob.psm1

<#
 .Synopsis
  Diagnostic module for SMTP SASL Auth Blob
 
 .Description
  Utility module for testing SMTP SASL Auth Blob for common issues and misconfigurations.
  Validates SASL XOAUTH2 authentication blobs and their embedded OAuth tokens.
 
 .Parameter EncodedAuthBlob
  Base64 encoded SASL XOAUTH2 auth blob to test.
  Expected format when decoded: user=username^Aauth=Bearer token^A^A
  Where ^A represents the ASCII control character 0x01
 
  .Parameter RbacEnabledApplication
  Switch to indicate if RBAC enforcement is enabled for application authentication.
 
 .Example
   # Test an auth blob
   Test-SmtpSaslAuthBlob -EncodedAuthBlob 'dXNlcj1zb21ldXNlckBleGFtcGxlLmNvbQFhdXRoPUJlYXJlciB5YTI5LnZGOWRmdDRxbVRjMk52YjNSbGNrQmhkSFJoZG1semRHRXVZMjl0Q2cBAQ==' -Verbose
 
    AuthBlobUserName : someuser@example.com
    AuthBlobToken : eyJ0eXAiOiJKV1QiLCJub25jZSI6IlItWW...
    OAuthTokenAudience : https://outlook.office.com
    OAuthTokenScopes : SMTP.Send
    OAuthTokenRoles :
    OAuthTokenUpn : someuser@example.com
    OAuthTokenExpirationUtc : 01/01/1970 12:00:00 AM
    ApplicationId : 9954180a-16f4-4683-aaaaaaaaaaaa
    AppDisplayName : My OAuth SMTP Application
    IsAuthBlobValid : True
    IsAuthTokenValid : True
 
 .Notes
  The SASL XOAUTH2 mechanism format specification:
  base64("user=" + userName + "^Aauth=Bearer " + accessToken + "^A^A")
  Where ^A is the ASCII control character 0x01 (SOH - Start of Heading)
#>


using module .\Utils.psm1

# Constants for SASL blob validation
$script:SASL_CONTROL_CHAR = [char]0x01
$script:SASL_USER_PREFIX = "user="
$script:SASL_AUTH_PREFIX = "auth="
$script:SASL_BEARER_PREFIX = "Bearer "
$script:SASL_USER_PREFIX_LENGTH = $script:SASL_USER_PREFIX.Length
$script:SASL_AUTH_PREFIX_LENGTH = $script:SASL_AUTH_PREFIX.Length
$script:SASL_BEARER_PREFIX_LENGTH = $script:SASL_BEARER_PREFIX.Length

# Flag if RBAC enforcement is enabled for application authentication
$script:RbacEnabled = $false

function Test-SmtpSaslAuthBlob {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $false, Position = 0)]
        [string]$EncodedAuthBlob,
        [Parameter(Mandatory = $false)]
        [switch]$RbacEnabledApplication
    )

    [bool]$script:RbacEnabled = $RbacEnabledApplication

    if ($script:RbacEnabled) {
        Write-Verbose "RBAC enforcement for application authentication is enabled."
    }
    # Initialize result object
    $result = [PSCustomObject]@{
        AuthBlobUserName        = $null
        AuthBlobToken           = $null
        OAuthTokenAudience      = $null
        OAuthTokenScopes        = $null
        OAuthTokenRoles         = $null
        OAuthTokenUpn           = $null
        OAuthTokenExpirationUtc = $null
        ApplicationId           = $null
        AppDisplayName          = $null
        IsAuthBlobValid         = $false
        IsAuthTokenValid        = $false
    }

    # Validate input parameter
    if ([string]::IsNullOrEmpty($EncodedAuthBlob)) {
        Write-Error "AuthBlob is null or empty. Please supply a value to test." -ErrorAction Stop
    }

    try {
        # Decode and validate the SASL blob structure
        $decodedAuthBlob = ConvertFrom-EncodedSaslBlob -EncodedAuthBlob $EncodedAuthBlob
        $parsedBlob = ConvertFrom-SaslAuthBlob -DecodedBlob $decodedAuthBlob

        # Extract user and token information
        $result.AuthBlobUserName = $parsedBlob.UserName
        $result.AuthBlobToken = $parsedBlob.AccessToken

        # Validate the OAuth token
        Test-AccessTokenStructure -AccessToken $result.AuthBlobToken -Result $result -UserName $result.AuthBlobUserName

        # Mark blob as valid if we reach here
        $result.IsAuthBlobValid = $true

        Write-Verbose "SASL auth blob validation completed successfully"
        return $result
    }
    catch {
        Write-Error $_.Exception.Message -ErrorAction Stop
    }
}

function ConvertFrom-EncodedSaslBlob {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$EncodedAuthBlob
    )

    try {
        $decodedBlob = DecodeBase64Value($EncodedAuthBlob)
        Write-Verbose "Successfully decoded SASL auth blob"
        return $decodedBlob
    }
    catch {
        Write-Verbose "Failed to decode auth blob: $EncodedAuthBlob"
        throw "AuthBlob is not a valid base64 encoded string. Check your input and try again."
    }
}

function ConvertFrom-SaslAuthBlob {
    [CmdletBinding()]
    [OutputType([System.Collections.Hashtable])]
    param(
        [Parameter(Mandatory = $true)]
        [string]$DecodedBlob
    )

    Write-Verbose "Parsing SASL auth blob structure"

    # Get key indices in the blob
    $userIndex = $DecodedBlob.IndexOf($script:SASL_USER_PREFIX)
    $authIndex = $DecodedBlob.IndexOf($script:SASL_AUTH_PREFIX)
    $bearerIndex = $DecodedBlob.IndexOf($script:SASL_BEARER_PREFIX)

    # Validate SASL blob structure
    $charArray = $DecodedBlob.ToCharArray()

    # Validate 'user=' is at the beginning
    if ($userIndex -ne 0) {
        Write-Verbose "Invalid SASL blob. Expected 'user=' at index 0. Found at index: $userIndex"
        throw "SASL auth blob is incorrectly formatted. 'user=' parameter not found in correct position."
    }

    # Validate 'auth=' is present
    if ($authIndex -eq -1) {
        Write-Verbose "Invalid SASL blob. 'auth=' not found in blob."
        throw "SASL auth blob is incorrectly formatted. 'auth=' parameter not found in blob."
    }

    # Validate 'Bearer ' is present after 'auth='
    if ($bearerIndex -eq -1 -or $bearerIndex -lt $authIndex) {
        Write-Verbose "Invalid SASL blob. 'Bearer ' not found or not in correct position. Bearer index: $bearerIndex"
        throw "SASL auth blob is incorrectly formatted. 'Bearer ' not found in expected position."
    }

    # Validate control character before 'auth='
    if ($authIndex -eq 0 -or $charArray[$authIndex - 1] -ne $script:SASL_CONTROL_CHAR) {
        if ($authIndex -gt 0) {
            $actualChar = GetCharHexValue($charArray[$authIndex - 1])
            Write-Verbose "Invalid SASL blob. Expected control character 0x01 before 'auth=' but found: 0x$actualChar"
        }
        else {
            Write-Verbose "Invalid SASL blob. Control character 0x01 not found before 'auth='."
        }
        throw "SASL auth blob is incorrectly formatted. Missing control character (0x01) before 'auth='."
    }

    # Validate control characters at the end (should be ^A^A)
    if ($charArray.Length -lt 2 -or
        $charArray[-1] -ne $script:SASL_CONTROL_CHAR -or
        $charArray[-2] -ne $script:SASL_CONTROL_CHAR) {
        $endChars = if ($charArray.Length -ge 2) {
            "0x$(GetCharHexValue($charArray[-2]))0x$(GetCharHexValue($charArray[-1]))"
        } else {
            "insufficient length"
        }
        Write-Verbose "Invalid SASL blob. Expected control characters 0x01 0x01 at end but found: $endChars"
        throw "SASL auth blob is incorrectly formatted. Missing control characters (0x01 0x01) at the end."
    }

    # Extract username: from after "user=" to before the control character preceding "auth="
    try {
        $startPos = $userIndex + $script:SASL_USER_PREFIX_LENGTH
        $length = $authIndex - 1 - $startPos  # -1 to exclude the control character

        if ($length -le 0) {
            throw "Invalid username length calculated: $length"
        }

        $userName = $DecodedBlob.Substring($startPos, $length)

        if ([string]::IsNullOrEmpty($userName)) {
            throw "Username is empty"
        }

        Write-Verbose "Extracted username: $userName"
    }
    catch {
        Write-Verbose "Failed to extract username from SASL blob"
        throw "AuthBlob does not contain a valid username"
    }

    # Extract token: from after "auth=Bearer " to before the final control characters
    try {
        $startPos = $bearerIndex + $script:SASL_BEARER_PREFIX_LENGTH
        $length = $DecodedBlob.Length - 2 - $startPos  # -2 to exclude the final ^A^A

        if ($length -le 0) {
            throw "Invalid token length calculated: $length"
        }

        $accessToken = $DecodedBlob.Substring($startPos, $length)

        if ([string]::IsNullOrEmpty($accessToken)) {
            throw "Access token is empty"
        }

        Write-Verbose "Extracted access token (length: $($accessToken.Length))"
    }
    catch {
        Write-Verbose "Failed to extract access token from SASL blob"
        throw "AuthBlob does not contain a valid access token"
    }

    return @{
        UserName = $userName
        AccessToken = $accessToken
    }
}

# OAuth token validation constants
$script:EXPECTED_AUDIENCES = @(
    "https://outlook.office365.com",
    "https://outlook.office.com",
    "https://outlook.office365.us"
)
$script:REQUIRED_DELEGATED_SCOPE = "SMTP.Send"
$script:REQUIRED_APPLICATION_ROLE = "SMTP.SendAsApp"

function Test-AccessTokenStructure {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [string]$AccessToken,
        [Parameter(Mandatory = $true)]
        [PSCustomObject]$Result,
        [Parameter(Mandatory = $true)]
        [string]$UserName
    )

    if ([string]::IsNullOrEmpty($AccessToken)) {
        Write-Verbose "Access token is null or empty. Skipping token verification."
        $Result.IsAuthTokenValid = $false
        return
    }

    try {
        # Parse and decode the JWT token
        $tokenParts = $AccessToken.Split(".")

        # Validate JWT structure (header.payload.signature)
        if ($tokenParts.Count -ne 3 -or
            [string]::IsNullOrEmpty($tokenParts[0]) -or
            [string]::IsNullOrEmpty($tokenParts[1])) {
            throw "Invalid JWT token structure. Expected 3 parts (header.payload.signature)"
        }

        # Decode and parse token claims
        try {
            # Validate header can be decoded
            DecodeBase64Value($tokenParts[0]) | Out-Null

            # Decode payload and convert from JSON
            $decodedPayload = DecodeBase64Value($tokenParts[1])
            $tokenClaims = ConvertFrom-Json $decodedPayload

            Write-Verbose "Successfully decoded JWT token claims"
        }
        catch {
            throw "Failed to decode JWT token or token payload is not valid JSON: $($_.Exception.Message)"
        }

        # Populate result with token information
        $Result.OAuthTokenAudience = $tokenClaims.aud
        $Result.OAuthTokenUpn = $tokenClaims.upn
        $Result.OAuthTokenScopes = $tokenClaims.scp
        $Result.OAuthTokenRoles = $tokenClaims.roles
        $Result.ApplicationId = $tokenClaims.appid
        $Result.AppDisplayName = $tokenClaims.app_displayname

        # Convert Unix timestamp to UTC DateTime
        if ($tokenClaims.exp) {
            try {
                $Result.OAuthTokenExpirationUtc = [System.DateTimeOffset]::FromUnixTimeSeconds($tokenClaims.exp).UtcDateTime
            }
            catch {
                Write-Verbose "Failed to parse token expiration time: $($tokenClaims.exp)"
                $Result.OAuthTokenExpirationUtc = $null
            }
        }

        # Validate token claims
        $isTokenValid = Test-TokenClaim -TokenClaims $tokenClaims -UserName $UserName

        $Result.IsAuthTokenValid = $isTokenValid
    }
    catch {
        Write-Verbose "OAuth token validation failed: $($_.Exception.Message)"
        throw "Token in auth blob is invalid: $($_.Exception.Message)"
    }
}

function Test-TokenClaim {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory = $true)]
        [PSCustomObject]$TokenClaims,
        [Parameter(Mandatory = $true)]
        [string]$UserName
    )

    $validationResults = @()

    # Test audience claim
    if ($TokenClaims.aud -notin $script:EXPECTED_AUDIENCES) {
        Write-Verbose "Invalid audience claim. Expected one of: '$($script:EXPECTED_AUDIENCES -join "', '")' but found: '$($TokenClaims.aud)'"
        Write-Warning "Authentication token contains an invalid audience claim for SMTP Client Submission."
        $validationResults += $false
    }
    else {
        Write-Verbose "Token audience validation passed: $($TokenClaims.aud)"
        $validationResults += $true
    }

    # Test expiration
    if (-not $TokenClaims.exp) {
        Write-Verbose "Token does not contain expiration claim"
        Write-Warning "Authentication token does not contain expiration information."
        $validationResults += $false
    }
    else {
        try {
            $expirationTime = [System.DateTimeOffset]::FromUnixTimeSeconds($TokenClaims.exp).UtcDateTime
            $currentTime = (Get-Date).ToUniversalTime()

            if ($currentTime -gt $expirationTime) {
                Write-Verbose "Token has expired. Expiration: '$expirationTime', Current: '$currentTime'"
                Write-Warning "Authentication token has expired."
                $validationResults += $false
            }
            else {
                Write-Verbose "Token expiration validation passed. Expires: $expirationTime"
                $validationResults += $true
            }
        }
        catch {
            Write-Verbose "Failed to validate token expiration: $($_.Exception.Message)"
            Write-Warning "Unable to validate token expiration time."
            $validationResults += $false
        }
    }

    # Determine authentication type and test permissions
    $isApplicationAuth = [string]::IsNullOrEmpty($TokenClaims.upn)

    if ($isApplicationAuth) {
        Write-Verbose "Application authentication detected. UPN claim not found in token."
        Write-Output "Application authentication detected."

        # Only time empty roles are allowed is in RBAC enabled applications
        if (-not $script:RbacEnabled -and [string]::IsNullOrEmpty($TokenClaims.roles)) {
            Write-Verbose "Roles claim is null or empty"
            Write-Warning "Required permission for SMTP Client Submission not found in token."
            $validationResults += $false
        }
        else {
            # Check if roles is null before splitting
            if ([string]::IsNullOrEmpty($TokenClaims.roles)) {
                $roles = @()
            } else {
                $roles = $TokenClaims.roles.Split(' ')
            }

            # Check that no SMTP roles are present in RBAC enabled applications
            if ($script:RbacEnabled -and $script:REQUIRED_APPLICATION_ROLE -in $roles) {
                Write-Verbose "Unexpected SMTP roles found in RBAC enabled application."
                Write-Warning "RBAC enabled application should not have direct SMTP permissions assigned."
                $validationResults += $false
            }
            elseif ($script:RbacEnabled -eq $false -and $script:REQUIRED_APPLICATION_ROLE -notin $roles) {
                Write-Verbose "Required role missing. Expected: '$script:REQUIRED_APPLICATION_ROLE', Found: '$($TokenClaims.roles)'"
                Write-Warning "Required permission for SMTP Client Submission not found in token."
                $validationResults += $false
            }
            else {
                Write-Verbose "Application permissions validation passed"
                $validationResults += $true
            }
        }
    }
    else {
        Write-Output "Delegated authentication detected"
        $isValid = $true

        # Validate UPN matches auth blob username
        if ($TokenClaims.upn -ne $UserName) {
            Write-Verbose "UPN mismatch. Token UPN: '$($TokenClaims.upn)', Auth blob username: '$UserName'"
            Write-Warning "UPN in authentication token and AuthBlob do not match."
            $isValid = $false
        }

        # Validate required scope
        if ([string]::IsNullOrEmpty($TokenClaims.scp)) {
            Write-Verbose "Scopes claim is null or empty"
            Write-Warning "Required permission for SMTP Client Submission not found in token."
            $isValid = $false
        }
        else {
            $scopes = $TokenClaims.scp.Split(' ')
            if ($script:REQUIRED_DELEGATED_SCOPE -notin $scopes) {
                Write-Verbose "Required scope missing. Expected: '$script:REQUIRED_DELEGATED_SCOPE', Found: '$($TokenClaims.scp)'"
                Write-Warning "Required permission for SMTP Client Submission not found in token."
                $isValid = $false
            }
            else {
                Write-Verbose "Delegated permissions validation passed"
            }
        }

        $validationResults += $isValid
    }

    # Return true only if all validations passed
    return ($validationResults -notcontains $false)
}

# SIG # Begin signature block
# MIIoKgYJKoZIhvcNAQcCoIIoGzCCKBcCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB
# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR
# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQUvLt3CyWVFi98zGotiQ8KE1ZU
# RWuggiFMMIIFjTCCBHWgAwIBAgIQDpsYjvnQLefv21DiCEAYWjANBgkqhkiG9w0B
# AQwFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYD
# VQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYDVQQDExtEaWdpQ2VydCBBc3N1cmVk
# IElEIFJvb3QgQ0EwHhcNMjIwODAxMDAwMDAwWhcNMzExMTA5MjM1OTU5WjBiMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQw
# ggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQC/5pBzaN675F1KPDAiMGkz
# 7MKnJS7JIT3yithZwuEppz1Yq3aaza57G4QNxDAf8xukOBbrVsaXbR2rsnnyyhHS
# 5F/WBTxSD1Ifxp4VpX6+n6lXFllVcq9ok3DCsrp1mWpzMpTREEQQLt+C8weE5nQ7
# bXHiLQwb7iDVySAdYyktzuxeTsiT+CFhmzTrBcZe7FsavOvJz82sNEBfsXpm7nfI
# SKhmV1efVFiODCu3T6cw2Vbuyntd463JT17lNecxy9qTXtyOj4DatpGYQJB5w3jH
# trHEtWoYOAMQjdjUN6QuBX2I9YI+EJFwq1WCQTLX2wRzKm6RAXwhTNS8rhsDdV14
# Ztk6MUSaM0C/CNdaSaTC5qmgZ92kJ7yhTzm1EVgX9yRcRo9k98FpiHaYdj1ZXUJ2
# h4mXaXpI8OCiEhtmmnTK3kse5w5jrubU75KSOp493ADkRSWJtppEGSt+wJS00mFt
# 6zPZxd9LBADMfRyVw4/3IbKyEbe7f/LVjHAsQWCqsWMYRJUadmJ+9oCw++hkpjPR
# iQfhvbfmQ6QYuKZ3AeEPlAwhHbJUKSWJbOUOUlFHdL4mrLZBdd56rF+NP8m800ER
# ElvlEFDrMcXKchYiCd98THU/Y+whX8QgUWtvsauGi0/C1kVfnSD8oR7FwI+isX4K
# Jpn15GkvmB0t9dmpsh3lGwIDAQABo4IBOjCCATYwDwYDVR0TAQH/BAUwAwEB/zAd
# BgNVHQ4EFgQU7NfjgtJxXWRM3y5nP+e6mK4cD08wHwYDVR0jBBgwFoAUReuir/SS
# y4IxLVGLp6chnfNtyA8wDgYDVR0PAQH/BAQDAgGGMHkGCCsGAQUFBwEBBG0wazAk
# BggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAC
# hjdodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURS
# b290Q0EuY3J0MEUGA1UdHwQ+MDwwOqA4oDaGNGh0dHA6Ly9jcmwzLmRpZ2ljZXJ0
# LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5jcmwwEQYDVR0gBAowCDAGBgRV
# HSAAMA0GCSqGSIb3DQEBDAUAA4IBAQBwoL9DXFXnOF+go3QbPbYW1/e/Vwe9mqyh
# hyzshV6pGrsi+IcaaVQi7aSId229GhT0E0p6Ly23OO/0/4C5+KH38nLeJLxSA8hO
# 0Cre+i1Wz/n096wwepqLsl7Uz9FDRJtDIeuWcqFItJnLnU+nBgMTdydE1Od/6Fmo
# 8L8vC6bp8jQ87PcDx4eo0kxAGTVGamlUsLihVo7spNU96LHc/RzY9HdaXFSMb++h
# UD38dglohJ9vytsgjTVgHAIDyyCwrFigDkBjxZgiwbJZ9VVrzyerbHbObyMt9H5x
# aiNrIv8SuFQtJ37YOtnwtoeW/VvRXKwYw02fc7cBqZ9Xql4o4rmUMIIGsDCCBJig
# AwIBAgIQCK1AsmDSnEyfXs2pvZOu2TANBgkqhkiG9w0BAQwFADBiMQswCQYDVQQG
# EwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNl
# cnQuY29tMSEwHwYDVQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjEw
# NDI5MDAwMDAwWhcNMzYwNDI4MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UE
# ChMORGlnaUNlcnQsIEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQg
# Q29kZSBTaWduaW5nIFJTQTQwOTYgU0hBMzg0IDIwMjEgQ0ExMIICIjANBgkqhkiG
# 9w0BAQEFAAOCAg8AMIICCgKCAgEA1bQvQtAorXi3XdU5WRuxiEL1M4zrPYGXcMW7
# xIUmMJ+kjmjYXPXrNCQH4UtP03hD9BfXHtr50tVnGlJPDqFX/IiZwZHMgQM+TXAk
# ZLON4gh9NH1MgFcSa0OamfLFOx/y78tHWhOmTLMBICXzENOLsvsI8IrgnQnAZaf6
# mIBJNYc9URnokCF4RS6hnyzhGMIazMXuk0lwQjKP+8bqHPNlaJGiTUyCEUhSaN4Q
# vRRXXegYE2XFf7JPhSxIpFaENdb5LpyqABXRN/4aBpTCfMjqGzLmysL0p6MDDnSl
# rzm2q2AS4+jWufcx4dyt5Big2MEjR0ezoQ9uo6ttmAaDG7dqZy3SvUQakhCBj7A7
# CdfHmzJawv9qYFSLScGT7eG0XOBv6yb5jNWy+TgQ5urOkfW+0/tvk2E0XLyTRSiD
# NipmKF+wc86LJiUGsoPUXPYVGUztYuBeM/Lo6OwKp7ADK5GyNnm+960IHnWmZcy7
# 40hQ83eRGv7bUKJGyGFYmPV8AhY8gyitOYbs1LcNU9D4R+Z1MI3sMJN2FKZbS110
# YU0/EpF23r9Yy3IQKUHw1cVtJnZoEUETWJrcJisB9IlNWdt4z4FKPkBHX8mBUHOF
# ECMhWWCKZFTBzCEa6DgZfGYczXg4RTCZT/9jT0y7qg0IU0F8WD1Hs/q27IwyCQLM
# bDwMVhECAwEAAaOCAVkwggFVMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYE
# FGg34Ou2O/hfEYb7/mF7CIhl9E5CMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/n
# upiuHA9PMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzB3Bggr
# BgEFBQcBAQRrMGkwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNv
# bTBBBggrBgEFBQcwAoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lD
# ZXJ0VHJ1c3RlZFJvb3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2Ny
# bDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwHAYDVR0g
# BBUwEzAHBgVngQwBAzAIBgZngQwBBAEwDQYJKoZIhvcNAQEMBQADggIBADojRD2N
# CHbuj7w6mdNW4AIapfhINPMstuZ0ZveUcrEAyq9sMCcTEp6QRJ9L/Z6jfCbVN7w6
# XUhtldU/SfQnuxaBRVD9nL22heB2fjdxyyL3WqqQz/WTauPrINHVUHmImoqKwba9
# oUgYftzYgBoRGRjNYZmBVvbJ43bnxOQbX0P4PpT/djk9ntSZz0rdKOtfJqGVWEjV
# Gv7XJz/9kNF2ht0csGBc8w2o7uCJob054ThO2m67Np375SFTWsPK6Wrxoj7bQ7gz
# yE84FJKZ9d3OVG3ZXQIUH0AzfAPilbLCIXVzUstG2MQ0HKKlS43Nb3Y3LIU/Gs4m
# 6Ri+kAewQ3+ViCCCcPDMyu/9KTVcH4k4Vfc3iosJocsL6TEa/y4ZXDlx4b6cpwoG
# 1iZnt5LmTl/eeqxJzy6kdJKt2zyknIYf48FWGysj/4+16oh7cGvmoLr9Oj9FpsTo
# FpFSi0HASIRLlk2rREDjjfAVKM7t8RhWByovEMQMCGQ8M4+uKIw8y4+ICw2/O/TO
# HnuO77Xry7fwdxPm5yg/rBKupS8ibEH5glwVZsxsDsrFhsP2JjMMB0ug0wcCampA
# MEhLNKhRILutG4UI4lkNbcoFUCvqShyepf2gpx8GdOfy1lKQ/a+FSCH5Vzu0nAPt
# hkX0tGFuv2jiJmCG6sivqf6UHedjGzqGVnhOMIIGtDCCBJygAwIBAgIQDcesVwX/
# IZkuQEMiDDpJhjANBgkqhkiG9w0BAQsFADBiMQswCQYDVQQGEwJVUzEVMBMGA1UE
# ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSEwHwYD
# VQQDExhEaWdpQ2VydCBUcnVzdGVkIFJvb3QgRzQwHhcNMjUwNTA3MDAwMDAwWhcN
# MzgwMTE0MjM1OTU5WjBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs
# IEluYy4xQTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5n
# IFJTQTQwOTYgU0hBMjU2IDIwMjUgQ0ExMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A
# MIICCgKCAgEAtHgx0wqYQXK+PEbAHKx126NGaHS0URedTa2NDZS1mZaDLFTtQ2oR
# jzUXMmxCqvkbsDpz4aH+qbxeLho8I6jY3xL1IusLopuW2qftJYJaDNs1+JH7Z+Qd
# SKWM06qchUP+AbdJgMQB3h2DZ0Mal5kYp77jYMVQXSZH++0trj6Ao+xh/AS7sQRu
# QL37QXbDhAktVJMQbzIBHYJBYgzWIjk8eDrYhXDEpKk7RdoX0M980EpLtlrNyHw0
# Xm+nt5pnYJU3Gmq6bNMI1I7Gb5IBZK4ivbVCiZv7PNBYqHEpNVWC2ZQ8BbfnFRQV
# ESYOszFI2Wv82wnJRfN20VRS3hpLgIR4hjzL0hpoYGk81coWJ+KdPvMvaB0WkE/2
# qHxJ0ucS638ZxqU14lDnki7CcoKCz6eum5A19WZQHkqUJfdkDjHkccpL6uoG8pbF
# 0LJAQQZxst7VvwDDjAmSFTUms+wV/FbWBqi7fTJnjq3hj0XbQcd8hjj/q8d6ylgx
# CZSKi17yVp2NL+cnT6Toy+rN+nM8M7LnLqCrO2JP3oW//1sfuZDKiDEb1AQ8es9X
# r/u6bDTnYCTKIsDq1BtmXUqEG1NqzJKS4kOmxkYp2WyODi7vQTCBZtVFJfVZ3j7O
# gWmnhFr4yUozZtqgPrHRVHhGNKlYzyjlroPxul+bgIspzOwbtmsgY1MCAwEAAaOC
# AV0wggFZMBIGA1UdEwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFO9vU0rp5AZ8esri
# kFb2L9RJ7MtOMB8GA1UdIwQYMBaAFOzX44LScV1kTN8uZz/nupiuHA9PMA4GA1Ud
# DwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB3BggrBgEFBQcBAQRrMGkw
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBBBggrBgEFBQcw
# AoY1aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZFJv
# b3RHNC5jcnQwQwYDVR0fBDwwOjA4oDagNIYyaHR0cDovL2NybDMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0VHJ1c3RlZFJvb3RHNC5jcmwwIAYDVR0gBBkwFzAIBgZngQwB
# BAIwCwYJYIZIAYb9bAcBMA0GCSqGSIb3DQEBCwUAA4ICAQAXzvsWgBz+Bz0RdnEw
# vb4LyLU0pn/N0IfFiBowf0/Dm1wGc/Do7oVMY2mhXZXjDNJQa8j00DNqhCT3t+s8
# G0iP5kvN2n7Jd2E4/iEIUBO41P5F448rSYJ59Ib61eoalhnd6ywFLerycvZTAz40
# y8S4F3/a+Z1jEMK/DMm/axFSgoR8n6c3nuZB9BfBwAQYK9FHaoq2e26MHvVY9gCD
# A/JYsq7pGdogP8HRtrYfctSLANEBfHU16r3J05qX3kId+ZOczgj5kjatVB+NdADV
# ZKON/gnZruMvNYY2o1f4MXRJDMdTSlOLh0HCn2cQLwQCqjFbqrXuvTPSegOOzr4E
# Wj7PtspIHBldNE2K9i697cvaiIo2p61Ed2p8xMJb82Yosn0z4y25xUbI7GIN/TpV
# fHIqQ6Ku/qjTY6hc3hsXMrS+U0yy+GWqAXam4ToWd2UQ1KYT70kZjE4YtL8Pbzg0
# c1ugMZyZZd/BdHLiRu7hAWE6bTEm4XYRkA6Tl4KSFLFk43esaUeqGkH/wyW4N7Oi
# gizwJWeukcyIPbAvjSabnf7+Pu0VrFgoiovRDiyx3zEdmcif/sYQsfch28bZeUz2
# rtY/9TCA6TD8dC3JE3rYkrhLULy7Dc90G6e8BlqmyIjlgp2+VqsS9/wQD7yFylIz
# 0scmbKvFoW2jNrbM1pD2T7m3XDCCBu0wggTVoAMCAQICEAqA7xhLjfEFgtHEdqeV
# dGgwDQYJKoZIhvcNAQELBQAwaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lD
# ZXJ0LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFt
# cGluZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMTAeFw0yNTA2MDQwMDAwMDBaFw0z
# NjA5MDMyMzU5NTlaMGMxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwg
# SW5jLjE7MDkGA1UEAxMyRGlnaUNlcnQgU0hBMjU2IFJTQTQwOTYgVGltZXN0YW1w
# IFJlc3BvbmRlciAyMDI1IDEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDQRqwtEsae0OquYFazK1e6b1H/hnAKAd/KN8wZQjBjMqiZ3xTWcfsLwOvRxUwX
# cGx8AUjni6bz52fGTfr6PHRNv6T7zsf1Y/E3IU8kgNkeECqVQ+3bzWYesFtkepEr
# vUSbf+EIYLkrLKd6qJnuzK8Vcn0DvbDMemQFoxQ2Dsw4vEjoT1FpS54dNApZfKY6
# 1HAldytxNM89PZXUP/5wWWURK+IfxiOg8W9lKMqzdIo7VA1R0V3Zp3DjjANwqAf4
# lEkTlCDQ0/fKJLKLkzGBTpx6EYevvOi7XOc4zyh1uSqgr6UnbksIcFJqLbkIXIPb
# cNmA98Oskkkrvt6lPAw/p4oDSRZreiwB7x9ykrjS6GS3NR39iTTFS+ENTqW8m6TH
# uOmHHjQNC3zbJ6nJ6SXiLSvw4Smz8U07hqF+8CTXaETkVWz0dVVZw7knh1WZXOLH
# gDvundrAtuvz0D3T+dYaNcwafsVCGZKUhQPL1naFKBy1p6llN3QgshRta6Eq4B40
# h5avMcpi54wm0i2ePZD5pPIssoszQyF4//3DoK2O65Uck5Wggn8O2klETsJ7u8xE
# ehGifgJYi+6I03UuT1j7FnrqVrOzaQoVJOeeStPeldYRNMmSF3voIgMFtNGh86w3
# ISHNm0IaadCKCkUe2LnwJKa8TIlwCUNVwppwn4D3/Pt5pwIDAQABo4IBlTCCAZEw
# DAYDVR0TAQH/BAIwADAdBgNVHQ4EFgQU5Dv88jHt/f3X85FxYxlQQ89hjOgwHwYD
# VR0jBBgwFoAU729TSunkBnx6yuKQVvYv1Ensy04wDgYDVR0PAQH/BAQDAgeAMBYG
# A1UdJQEB/wQMMAoGCCsGAQUFBwMIMIGVBggrBgEFBQcBAQSBiDCBhTAkBggrBgEF
# BQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMF0GCCsGAQUFBzAChlFodHRw
# Oi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRUaW1lU3Rh
# bXBpbmdSU0E0MDk2U0hBMjU2MjAyNUNBMS5jcnQwXwYDVR0fBFgwVjBUoFKgUIZO
# aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0
# YW1waW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3JsMCAGA1UdIAQZMBcwCAYGZ4EM
# AQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAZSqt8RwnBLmuYEHs
# 0QhEnmNAciH45PYiT9s1i6UKtW+FERp8FgXRGQ/YAavXzWjZhY+hIfP2JkQ38U+w
# tJPBVBajYfrbIYG+Dui4I4PCvHpQuPqFgqp1PzC/ZRX4pvP/ciZmUnthfAEP1HSh
# TrY+2DE5qjzvZs7JIIgt0GCFD9ktx0LxxtRQ7vllKluHWiKk6FxRPyUPxAAYH2Vy
# 1lNM4kzekd8oEARzFAWgeW3az2xejEWLNN4eKGxDJ8WDl/FQUSntbjZ80FU3i54t
# px5F/0Kr15zW/mJAxZMVBrTE2oi0fcI8VMbtoRAmaaslNXdCG1+lqvP4FbrQ6IwS
# BXkZagHLhFU9HCrG/syTRLLhAezu/3Lr00GrJzPQFnCEH1Y58678IgmfORBPC1JK
# kYaEt2OdDh4GmO0/5cHelAK2/gTlQJINqDr6JfwyYHXSd+V08X1JUPvB4ILfJdmL
# +66Gp3CSBXG6IwXMZUXBhtCyIaehr0XkBoDIGMUG1dUtwq1qmcwbdUfcSYCn+Own
# cVUXf53VJUNOaMWMts0VlRYxe5nK+At+DI96HAlXHAL5SlfYxJ7La54i71McVWRP
# 66bW+yERNpbJCjyCYG2j+bdpxo/1Cy4uPcU3AWVPGrbn5PhDBf3Froguzzhk++am
# i+r3Qrx5bIbY3TVzgiFI7Gq3zWcwggdaMIIFQqADAgECAhANw2Fva20K+rkvK+/+
# Bxk+MA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp
# Q2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBDb2RlIFNp
# Z25pbmcgUlNBNDA5NiBTSEEzODQgMjAyMSBDQTEwHhcNMjUwNzE1MDAwMDAwWhcN
# MjgwNzE2MjM1OTU5WjBiMQswCQYDVQQGEwJVUzEOMAwGA1UECBMFVGV4YXMxDzAN
# BgNVBAcTBklydmluZzEYMBYGA1UEChMPUmljaGFyZCBGYWphcmRvMRgwFgYDVQQD
# Ew9SaWNoYXJkIEZhamFyZG8wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoIC
# AQDsnVRBsMdpPCQMc05ysOVukiJC6uzFiamN/MuOMMicR/oTyBZa9o1+l0PV8uBJ
# 8v5yu/eDmZkwCuJRhL1QA/T5FrJ/gwwI3YtHOWBG1QDOXsg8qiekMsaFgdYWAjCe
# nEriC25YZE3I6mYNbWinJ/9/gsmP8EZCRGFbFtXfZO70c+83P2Q7TlS6R/Jt9ZgM
# d7ML6F6jvvvxSR7lJ2D+7tyiNE45r/eWjL0tYAXaKaKpnnZbMW+At/vYjRqN10CE
# OSnhQeuvTZTTiy4E/gjS6dN8gvregoO/9bAVofkDuRh6s+UvZtsp+yYyFpHTPbd6
# xEzwSwplT4/CYIVvaFEZ7mD0m3iIqR6RrdQYsdwB1dRKzJDgpxisUuAnpXRFUF8U
# NHM17/mXQPf8wwJHnleVJbnecQ8ltiVvsMKi1lveru7T8b5EeSdt1vwMzUxe6tS2
# qWeYPvBvToOc0SyrmKPvLYq3Ut6fveK5g/XRXdqj6F2JRNvelnP+gg+DIgctoKTU
# 7U8G07YIaJtO/lCIpvJXgNd0VD4iIZkhKMAbW95k4ovBhNJ9LZJ1mKF21i1MetFU
# 6vk4T2kE8oSCzAH6gFyZ+SXhGzyzcOwhv/tBhYAiwwA7keSotpqgbnhnQST0XEFP
# 9eQP1zMrpuGDNZMIxZFImD2dgNdP+X7e8zH8PJ6HxTwt/QIDAQABo4ICAzCCAf8w
# HwYDVR0jBBgwFoAUaDfg67Y7+F8Rhvv+YXsIiGX0TkIwHQYDVR0OBBYEFJflaBtr
# VOU8mYho6v2pIvOs2kN9MD4GA1UdIAQ3MDUwMwYGZ4EMAQQBMCkwJwYIKwYBBQUH
# AgEWG2h0dHA6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAOBgNVHQ8BAf8EBAMCB4Aw
# EwYDVR0lBAwwCgYIKwYBBQUHAwMwgbUGA1UdHwSBrTCBqjBToFGgT4ZNaHR0cDov
# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0Q29kZVNpZ25pbmdS
# U0E0MDk2U0hBMzg0MjAyMUNBMS5jcmwwU6BRoE+GTWh0dHA6Ly9jcmw0LmRpZ2lj
# ZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNENvZGVTaWduaW5nUlNBNDA5NlNIQTM4
# NDIwMjFDQTEuY3JsMIGUBggrBgEFBQcBAQSBhzCBhDAkBggrBgEFBQcwAYYYaHR0
# cDovL29jc3AuZGlnaWNlcnQuY29tMFwGCCsGAQUFBzAChlBodHRwOi8vY2FjZXJ0
# cy5kaWdpY2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRDb2RlU2lnbmluZ1JTQTQw
# OTZTSEEzODQyMDIxQ0ExLmNydDAJBgNVHRMEAjAAMA0GCSqGSIb3DQEBCwUAA4IC
# AQBcJngbL8FgxIfCimjUkh+HAPlxzbWJW3brq7D3RHe9wfV5ItOrui6Op64vKVg5
# T5lyzZrOAzQM6GPXBBoWWVLfdSscLsI/NvLMx6eW81My+xFUgRnFSp/IQ/DwQNUf
# bthZJNN8us+urwPCoE7pinMHVOx4K5OtV9tBuu9qrqvti1IlAueOkkPZB9sKlN+a
# Q2e7o7IaEDeQ8cf9NXNlwUU5Npt4A15AyY/Rs4UZ0VA+V5+Jg12fxcP7GzonxUoB
# kNdeX6o9MnOSu0uyoahFqU6xdqhYt6rp9Jm4DCyTnOH0PhCOc2o4Yw+kyLCT89G9
# eh141zP3S3enNXUqZX1Y/oYHAk0yv1o5ANKmpLD6oHMYmD0MurprwX9Fs36mIllp
# ElxPpgnVZK6RvJjucrM9+Ohd1wTaQWLFJ/P1ma2gwZqucHebrd5m+8aABh//jtAs
# bU6cu5hQm/+Ip5jKD8BJE5xiZNr++fOokE6/x38MMgqtPSj29KNOwcAUAV73aY+x
# CKC6uRBoMaKHXVdN8r2f4/8a6dA26PaEHLY0v/PeWYUxCfiyCgMcqhFUkhP7HvsX
# 3/XxfRLB+FM2n1MkfECTc2C17mAb1+Gmt0yW6gd141JLzJAfogQxYt3FoJEcht7x
# /yvg+sWOkZtILz1ndlgt2eo/aQL/Z17OYxdPdo4rSXD8GTGCBkgwggZEAgEBMH0w
# aTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMUEwPwYDVQQD
# EzhEaWdpQ2VydCBUcnVzdGVkIEc0IENvZGUgU2lnbmluZyBSU0E0MDk2IFNIQTM4
# NCAyMDIxIENBMQIQDcNhb2ttCvq5Lyvv/gcZPjAJBgUrDgMCGgUAoHgwGAYKKwYB
# BAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG9w0BCQMxDAYKKwYBBAGCNwIBBDAc
# BgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIBFTAjBgkqhkiG9w0BCQQxFgQUc3aB
# VqHST3bmd1kOI1nMqHG+AlkwDQYJKoZIhvcNAQEBBQAEggIAjJjx0lEhmd6IzTe9
# 3y9n3r0K7U1VmWX/dEGZE8ZnaTXbgYT89Ld3hcsYijum1CgckQttWmLvsyoWFzis
# pGGcS/9d38/GKhdVDl+d5MxX/yveG0imyu/jGwc37VoSHnCovrSKhZZg3q6If1k/
# QiUjcF8842K0dB3lOnptpuU3JQH5nzyLI6IxsHZVtrTzBxHLA1U7N+e920HwhSL2
# y++IRNBRTyqA4dLi/v+HVglcUwPoehEXx/Mh934ECJmFQQ3XHvlsDdChMyELzhWx
# 60TeXYTksxQyLZltarEnI6C4fSwi0GYW8P6d2hVMpoHK6yW7NzMCR04ie9PqUYEE
# qxBKxVqb6Ai7IkEGWWp23bAmP4zs/FCgEGeMkNivtUGr/akt5zDp9urcQyJO3AiM
# xIHtrXODlIgGd+HzZfoYupJwjfEfawG5T09lJdIoYfhKoXM75odFsIxgmvKMytCq
# gS69jQcXrkwxFzpuencyYb0PtwiCzWl0HWuOh/Y3ZNDmhRiwcJ2rS99UQPtCydh1
# 5YNNdwxuApH6tVxCXqiaJ0rHg1923hHYac/e+1wzVNeurJqiE+T2b5+yYGMU+KwS
# LMCLU4wnoTZzXd/q9v0oKB1nbFsYr+NYPwUVqUUYsy3tSKEGY7ohMe/uQ3yFygPK
# w+7lecal0OVXC18nNe1rMP5ic5ChggMmMIIDIgYJKoZIhvcNAQkGMYIDEzCCAw8C
# AQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4xQTA/
# BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgVGltZVN0YW1waW5nIFJTQTQwOTYg
# U0hBMjU2IDIwMjUgQ0ExAhAKgO8YS43xBYLRxHanlXRoMA0GCWCGSAFlAwQCAQUA
# oGkwGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMjUx
# MTE4MjAxMjUxWjAvBgkqhkiG9w0BCQQxIgQg5ENX/7bdkQFJtljZax3CSYHtxZcy
# 99wI+H3BZYSoy4YwDQYJKoZIhvcNAQEBBQAEggIAelD/C6sz0L47hl+R5/I1qGaM
# jeWjCeibDcaeE8SubITSwA1bzTdA1LhJPbhhe4KAmVYOPWzWxL+GcKf+p/Ul4dtR
# 3PtyUQ98Rg4vtg3BsWRR/2wb0UJB60E3UdoRgBrey+0z52pVNU1FtE3LV5jmjIb7
# D+xJW5+Ua8KjOp/mix41yP2pYNSuwyPrEmhgK1lDqsfzsxU6UcqvBWBx4bPaqsQh
# q5zQW5WA61KocGWmJCIkcVjr7eQ4QVhXNvnjsNQ7DIja+LZjAzTf7keSLdPjO8Yz
# cc3b1gVKCWbX/QrTPB9y6ZHrPjEt8VSjwKJi1ivtkHYC47WjYFBUES4FETRR/yKz
# KmZmXKVqHpf0x0mgo5hM11Iwoz45UqvdoF9G4MWH5cyelyX2P6ZZXk2f4rnTHhW0
# n/PfaryeWYN+XDOuIz0qkz+jJafr7KuZWrQk4IC1Gp/PxxFp0p83iPVmtcJkN/50
# BVvHNoERqxWC3zgJwUFEk/+Zg/dq4JOhtvDeIxE7CKGunqXqODFhzH/dc5zvpPB8
# eCJurXddm69pjHQhHG/Ld9FUAh42dCz7+Ojo3M+6zT6Gdv3dmCKtm0/QnH/3HsYy
# RcbrpX/GqDnXjdubSqIqazPl3cVXdGjU0GIibIx8cbqwciIDM869sqVuVmrlENhg
# 5pJIUZg+KP/KXW4Gj5U=
# SIG # End signature block