SpfAnalyzer.psm1
#region Enums enum SpfAction { Pass Fail SoftFail Neutral } #endregion Enums #region Classes class Dns { hidden static [DnsClient.LookupClient]$client = [DnsClient.LookupClient]::new() static [object[]] GetRecord([string]$Name, [DnsClient.QueryType]$recordType) { $retVal = @() switch($recordType) { {$_ -in ([DnsClient.QueryType]::A, [DnsClient.QueryType]::AAAA)} { $data = [Dns]::Client.Query($Name, $_) ` | select-object -ExpandProperty Answers ` | where-object{$_.RecordType -eq $recordType} ` | select-object -ExpandProperty Address $data | foreach-object { $retVal += [SpfIpAddress]::new($recordName, $_) } break; } {$_ -eq [DnsClient.QueryType]::MX} { $data = [Dns]::Client.Query($Name, $_) ` | select-object -ExpandProperty Answers ` | where-object{$_.RecordType -eq $recordType} ` | select-object -expand Exchange ` | select-object -expand Value $data | foreach-object { $retVal += $_ } break; } {$_ -eq [DnsClient.QueryType]::TXT} { [Dns]::Client.Query($Name, $_) ` | select-object -ExpandProperty Answers ` | where-object{$_.RecordType -eq $recordType} ` | foreach-object { #TXT records may be split into multiple strings if($_.Text.Count -gt 1) { $retVal += ($_.Text -join '') } else { $retVal += $_.Text } } break; } default { throw "Unsupported record type $recordType" } } if($retVal.Count -eq 0) {return $null} else {return $retVal} } static [object[]] GetSpfRecord([string]$Name) { $retVal = @() [Dns]::GetRecord($Name, [DnsClient.QueryType]::TXT) | foreach-object { if($_ -match "^v=spf1") { $retVal += $_ } } if($retVal.Count -eq 0) {return $null} else {return $retVal} } } class SpfEntry { [string]$Source [string]$Prefix [string]$Value SpfEntry([string]$Source, [string]$prefix, [string]$value) { $this.Prefix = $prefix $this.Value = $value $this.Source = $Source } [string] ToString() { return "$($this.Prefix) $($this.Value)" } } class SpfIpAddress { [string]$Source [System.Net.IPAddress]$Address SpfIpAddress([string]$source, [System.Net.IPAddress]$address) { $this.Source = $source $this.Address = $address } [string] ToString() { return $this.Address.ToString() } [System.Net.IPNetwork] ToNetwork([int]$prefixLength) { return [SpfIpNetwork]::new($this.source, [IpHelper.IPAddressExtensions]::Mask($this.address,$prefixLength,$true)) } static [SpfIpAddress] Parse([string]$source, [string]$address) { try { $ip = [System.Net.IPAddress]::Parse($address) return [SpfIpAddress]::new($source, $ip) } catch { Write-Warning "Invalid IP address $address" return $null } } } class SpfIpNetwork { hidden [System.Net.IPNetwork] $network [string]$Source [System.Net.IPAddress]$BaseAddress [int]$PrefixLength static [hashtable[]] $MemberDefinitions = @( @{ MemberType = 'ScriptProperty' MemberName = 'BaseAddress' Value = { $this.network.BaseAddress } } @{ MemberType = 'ScriptProperty' MemberName = 'PrefixLength' Value = { $this.network.PrefixLength } } ) static SpfIpNetwork() { $TypeName = [SpfIpNetwork].Name foreach ($Definition in [SpfIpNetwork]::MemberDefinitions) { Update-TypeData -TypeName $TypeName -Force @Definition } } SpfIpNetwork() {} SpfIpNetwork([string]$source, [System.Net.IPNetwork]$network) { $this.Source = $source $this.network = $network } SpfIpNetwork([string]$source, [System.Net.IPAddress]$address, [int]$prefixLength) { $this.Source = $source #need compiled helper here to overcome powershell language limitations $this.network = [IpHelper.IPAddressExtensions]::Mask($address,$prefixLength,$true) } [bool] Contains([System.Net.IPAddress]$address) { return $this.network.Contains($address) } static [SpfIpNetwork] Parse([string]$source, [string]$address) { $parts = $address.Split('/') $ip = [System.Net.IPAddress]::Parse($parts[0]) $mask = [int]$parts[1] return [SpfIpNetwork]::new($source, $ip, $mask) } [string] ToString() { return "$($this.BaseAddress)/$($this.PrefixLength)" } } class SpfRecord { hidden [string] $rawRecord [string] $Version [SpfAction] $FinalAction [string] $Source [object[]] $Entries SpfRecord([string]$source, [string]$rawRecord) { $this.rawRecord = $rawRecord $this.Version = 'spf1' $this.FinalAction = [SpfAction]::Neutral $this.Source = $source $this.Entries = @() } [string] ToString() { return "Source: $($this.Source) Record: $($this.rawRecord)" } static [SpfRecord[]] Parse([string]$source, [string]$rawRecord) { $retVal = @() $record = [SpfRecord]::new($source, $rawRecord) $retVal += $record $parts = $rawRecord.Split(' ') $continueParsing = $true foreach($part in $parts) { if($part.StartsWith('v=')) { $record.Version = $part.Substring(2) } #methods elseif ($continueParsing -and ($part.StartsWith('ip4:') -or $part.StartsWith('ip6:'))) { $ip = $part.Substring(4) $prefix = $part.Substring(0, 3) $record.Entries += [SpfEntry]::new($source, $prefix, $ip) if($ip -match '/') { $record.Entries += [SpfIpNetwork]::Parse($source, $ip) } else { $record.Entries += [SpfIpAddress]::Parse($source, $ip) } } elseif($continueParsing -and $part.StartsWith('include:')) { $domainName = $part.Substring(8) $record.Entries += [SpfEntry]::new($source, 'include', $domainName) if($retval.source -notcontains $domainName) { $additionalRecords = [Dns]::GetSpfRecord($domainName) foreach($additionalRecord in $additionalRecords) { $retVal += [SpfRecord]::Parse($domainName, $additionalRecord) } } else { Write-Warning "Cyclic reference: $domainName" } } elseif($continueParsing -and $part.StartsWith('exists:') -or $part.StartsWith('ptr:') -or $part -eq 'ptr') { $splits = $part.Split(':') if($splits.Length -gt 1) { $record.Entries += [SpfEntry]::new($source, $splits[0], $splits[1]) } else { $record.Entries += [SpfEntry]::new($source, $part, $null) } } elseif($continueParsing -and ($part.StartsWith('a:') -or $part.StartsWith('a/') -or $part -eq 'a' -or $part.StartsWith('+a:') -or $part.StartsWith('+a/') -or $part -eq '+a')) { $mask = -1 $splits = $part.Split('/') if($splits.Length -gt 1) { if(-not [int]::TryParse($splits[1], [ref]$mask)) { Write-Warning "Invalid mask value in $part" } } $splits = $splits[0].Split(':') $domainName = $source if($splits.Length -gt 1) { $domainName = $splits[1] } $start = 1 if($part[0] -eq '+') { $start++ } $record.Entries += [SpfEntry]::new($source, 'a', $part.Substring($start).Replace(':','')) if($mask -eq -1) { [SpfRecord]::ParseAMechanism($domainName, $part, [ref]$record) } else { [SpfRecord]::ParseAWithMaskMechanism($domainName, $mask, $part, [ref]$record) } } elseif($continueParsing -and ($part.StartsWith('mx') -or $part.startsWith('+mx'))) { $mask = -1 $splits = $part.Split('/') if($splits.Length -gt 1) { if(-not [int]::TryParse($splits[1], [ref]$mask)) { Write-Warning "Invalid mask value in $part" } } $splits = $splits[0].Split(':') $domainName = $source if($splits.Length -gt 1) { $domainName = $splits[1] } $start = 2 if($part[0] -eq '+') { $start++ } $record.Entries += [SpfEntry]::new($source, 'mx', $part.Substring($start).Replace(':','')) $mx = [Dns]::GetRecord($domainName, [DnsClient.QueryType]::MX) foreach($rec in $mx) { if($null -eq $rec) {continue} $domainName = $rec -as [string] if($null -eq $domainName) {continue} if($mask -eq -1) { [SpfRecord]::ParseAMechanism($domainName, $part, [ref]$record) } else { [SpfRecord]::ParseAWithMaskMechanism($domainName, $mask, $part, [ref]$record) } } } elseif($part -eq 'all' -or $part -eq '+all') { $record.FinalAction = [SpfAction]::Pass $continueParsing = $false } elseif($part -eq '-all') { $record.FinalAction = [SpfAction]::Fail $continueParsing = $false } elseif($part -eq '~all') { $record.FinalAction = [SpfAction]::SoftFail $continueParsing = $false } elseif($part -eq '?all') { $record.FinalAction = [SpfAction]::Neutral $continueParsing = $false } #Modifiers elseif($part.StartsWith('redirect=')) { $domainName = $part.Substring(9) $record.Entries += [SpfEntry]::new($source, 'redirect', $domainName) $additionalRecords = [Dns]::GetSpfRecord($domainName) foreach($additionalRecord in $additionalRecords) { $retVal += [SpfRecord]::Parse($domainName, $additionalRecord) } } elseif($part.StartsWith('exp=')) { $domainName = $part.Substring(4) $record.Entries += [SpfEntry]::new($source, 'exp', $domainName) } } return $retVal } static [void] ParseAMechanism([string]$domain, [string]$rawEntry, [ref]$record) { $records = [Dns]::GetRecord($domain, [DnsClient.QueryType]::A) $records += [Dns]::GetRecord($domain, [DnsClient.QueryType]::AAAA) foreach($rec in $records) { if($null -eq $rec) {continue} $ip = $rec -as [System.Net.IPAddress] if($null -eq $ip) {continue} $record.Entries += [SpfIpAddress]::new("$domain $rawEntry", $ip) } } static [void] ParseAWithMaskMechanism([string]$domain, [int]$mask, [string]$rawEntry, [ref]$record) { $records = [Dns]::GetRecord($domain, [DnsClient.QueryType]::A) $records += [Dns]::GetRecord($domain, [DnsClient.QueryType]::AAAA) foreach($rec in $records) { if($null -eq $rec) {continue} $ip = $rec -as [System.Net.IPAddress] if($null -eq $ip) {continue} $record.Entries += [SpfIpNetwork]::new("$domain $rawEntry", $ip, $mask) } } } #endregion Classes #region Public commands function Get-SPFRecord { <# .SYNOPSIS Retrieves and parses SPF record for domain .DESCRIPTION This command takes TXT records from provided domain, selects record representing SPF and parses it. Multi-string TXT records are concatenated into single string before parsing. In case record contains include method, additional records are retrieved and parsed as well, so output of this command is array of parsed SPF records. .OUTPUTS SpfRecord[] .EXAMPLE Get-SpfRecord -Domain 'microsoft.com' Description ----------- Retrieves and parses SPF record for microsoft.com domain .LINK More about SPF, see http://www.openspf.org/ and https://tools.ietf.org/html/rfc7208 #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline)] [string]$Domain ) process { $spfRecords = [Dns]::GetSpfRecord($domain) foreach($spfRecord in $spfRecords) { [SpfRecord]::Parse($domain, $spfRecord) } } } function Get-SpfRecordEntries { <# .SYNOPSIS Retrieves SPF record for domain, or takes parsed SPF record and parses it .DESCRIPTION This command retrieves SPF record for domain, or takes raw SPF record and parses it. Returns only entries of type SpfEntry from parsed record. SpfEntry represents parsed token from SPF record, like ip4, ip6, mx, a, include, redirect, exp, etc. It also contains information about SPF record it was parsed from. .OUTPUTS SpfEntry[] .EXAMPLE Get-SpfRecordEntries -Domain 'microsoft.com' Description ----------- Retrieves and parses SPF record for microsoft.com domain .EXAMPLE Test-SpfRecord -RawRecord 'v=spf1 include:spf.protection.outlook.com -all' -Domain 'mydomain.com' | Get-SpfRecordEntries Description ----------- Retrieves and parses raw SPF record for domain mydomain.com and returs parsed entries .LINK More about SPF, see http://www.openspf.org/ and https://tools.ietf.org/html/rfc7208 #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Record')] [SpfRecord]$SpfRecord, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DomainName')] [string]$Domain ) process { if ($PSCmdlet.ParameterSetName -eq 'DomainName') { Write-Verbose "Processing $Domain" [SpfRecord[]]$record = Get-SpfRecord -Domain $Domain ` } else { $record = $SpfRecord } Write-Verbose "Processing $record" $record.Entries | Where-Object{$_ -is [SpfEntry]} } } function Get-SpfRecordIpAddress { <# .SYNOPSIS Retrieves SPF record for domain, or takes parsed SPF record and returns only IP addresses from it .DESCRIPTION This command retrieves SPF record for domain, or takes raw SPF record and parses it. Returns IPv6 or IPv6 addresses from parsed record. SpfIpAddress represents parsed IP address from SPF record from ip4, ip6, mx, a, include and redirect record entries. It also contains information about SPF record it was parsed from. .OUTPUTS SpfIpAddress[] .EXAMPLE Get-SpfRecord -Domain 'microsoft.com' | Get-SpfRecordIpAddress Description ----------- Retrieves IP addresses authorized for use with microsoft.com domain .LINK More about SPF, see http://www.openspf.org/ and https://tools.ietf.org/html/rfc7208 #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Record')] [SpfRecord]$SpfRecord, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DomainName')] [string]$Domain ) process { if ($PSCmdlet.ParameterSetName -eq 'DomainName') { Write-Verbose "Processing $Domain" [SpfRecord[]]$record = Get-SpfRecord -Domain $Domain } else { $record = $SpfRecord } Write-Verbose "Processing $record" $record.Entries | Where-Object { $_ -is [SpfIpAddress] } } } function Get-SpfRecordIpNetwork { <# .SYNOPSIS Retrieves SPF record for domain, or takes parsed SPF record and returns only IP networks from it .DESCRIPTION This command retrieves SPF record for domain, or takes raw SPF record and parses it. Returns IPv6 or IPv6 networks from parsed record. SpfIpNetwork represents parsed IP network from SPF record from ip4, ip6, include and redirect record entries. It also contains information about SPF record it was parsed from. .OUTPUTS SpfIpNetwork[] .EXAMPLE Get-SpfRecord -Domain 'microsoft.com' | Get-SpfRecordIpNetwork Description ----------- Retrieves IP networks authorized for use with microsoft.com domain .LINK More about SPF, see http://www.openspf.org/ and https://tools.ietf.org/html/rfc7208 #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Record')] [SpfRecord]$SpfRecord, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DomainName')] [string]$Domain ) process { if ($PSCmdlet.ParameterSetName -eq 'DomainName') { Write-Verbose "Processing $Domain" [SpfRecord[]]$record = Get-SpfRecord -Domain $Domain } else { $record = $SpfRecord } Write-Verbose "Processing $record" $record.Entries | Where-Object { $_ -is [SpfIpNetwork] } } } function Test-SpfHost { <# .SYNOPSIS Tests IP address and sender against policy defined by SPF record .DESCRIPTION This command tests IP address and optional sender to test them with SPF policy defined for domain or defined by SPF record. Command returns entries from SPF record that authorize or deny given IP address and sender. Sender information is only used if SPF record contains macros in exists entry that require it. Command basically provides the same functionality as SPF test tools like https://www.kitterman.com/spf/validate.html .OUTPUTS SpfIpAddress[] SpfIpNetwork[] SpfEntry[] .EXAMPLE Get-SpfRecord -Domain 'microsoft.com' | Test-SpfHost -IpAddress '20.88.157.184' Description ----------- CHecks if IP address 20.88.157.184 is authorized to send email on behalf of microsoft.com .LINK More about SPF, see http://www.openspf.org/ and https://tools.ietf.org/html/rfc7208 #> [CmdletBinding()] param ( [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'Record')] [SpfRecord]$SpfRecord, [Parameter(Mandatory, ValueFromPipeline, ParameterSetName = 'DomainName')] [string]$Domain, [Parameter(Mandatory)] [string]$IpAddress, [Parameter()] [string]$SenderAddress ) process { $ip = [System.Net.IPAddress]::Parse($IpAddress) if ($PSCmdlet.ParameterSetName -eq 'DomainName') { Write-Verbose "Processing $Domain" [SpfRecord[]]$spfRecords = Get-SpfRecord -Domain $Domain ` } else { $spfRecords = @($SpfRecord) } Write-Verbose "Processing $record" foreach($record in $spfRecords) { $record ` | Get-SpfRecordIpAddress ` | Where-Object { $_.Address -eq $ip } $record ` | Get-SpfRecordIpNetwork ` | Where-Object { $_.Contains($ip) } $record.Entries ` | Where-Object { $_.Prefix -eq 'exists' } ` | ForEach-Object { $macro = Expand-SpfMacro -Macro $_.Value -Domain $spfRecords[0].Source -IpAddress $ip -SenderAddress $SenderAddress if($macro -match '%{.' ) { throw "Unsupported macro $macro after expansion of $( $_.Value )" } try { [Dns]::GetRecord($macro, [DnsClient.QueryType]::A) } catch { #silently ignore not found expanded macro } } $record.Entries ` | Where-Object { $_.Prefix -eq 'include' } ` | Where-Object { $_.Value -match '%{.' } ` | ForEach-Object { $macro = Expand-SpfMacro -Macro $_.Value -Domain $spfRecords[0].Source -IpAddress $ip -SenderAddress $SenderAddress if($macro -match '%{.' ) { throw "Unsupported macro $macro after expansion of $( $_.Value )" } try { $rawRecord = [Dns]::GetRecord($macro, [DnsClient.QueryType]::TXT) if($null -ne $rawRecord) { $additionalRecord = [SpfRecord]::Parse($_.Source, $rawRecord) $additionalRecord ` | Test-SpfHost -IpAddress $IpAddress -SenderAddress $SenderAddress } } catch { #silently ignore not found expanded macro } } } } } function Test-SpfRecord { <# .SYNOPSIS Parses raw SPF record .DESCRIPTION This command parses raw SPF record and returns parsed SPF record object. This is useful when constructing SPF record from scratch to test it. .OUTPUTS SpfRecord[] .EXAMPLE Get-SpfRecord -Domain 'mydomain.com' -RawRecord 'v=spf1 include:spf.protection.outlook.com -all' Description ----------- CHecks if SPF record can be parsed correctly. .LINK More about SPF, see http://www.openspf.org/ and https://tools.ietf.org/html/rfc7208 #> param ( [Parameter(Mandatory, ValueFromPipeline)] [string]$RawRecord, [Parameter(Mandatory)] [string]$Domain ) process { [SpfRecord]::Parse($Domain, $RawRecord) } } #endregion Public commands #region Internal commands function Expand-SpfMacro { param ( [Parameter(Mandatory = $true)] [string]$Macro, [Parameter(Mandatory)] [string]$Domain, [Parameter(Mandatory)] [System.Net.IPAddress]$IpAddress, [Parameter()] [string]$SenderAddress ) process { $senderValid = [string]::IsNullOrEmpty($SenderAddress) -eq $false if($senderValid) { $senderParts = $SenderAddress.Split('@') $senderValid = $senderParts.Count -eq 2 } if($macro -match '%{i}') { $dottedIp = [IpHelper.IPAddressExtensions]::ToDotted($IpAddress) $macro = $macro -replace '%{i}', $dottedIp } if($macro -match '%{ir}') { $dottedIp = [IpHelper.IPAddressExtensions]::ToReverseDotted($IpAddress) $macro = $macro -replace '%{ir}', $dottedIp } if($macro -match '%{c}') { $macro = $macro -replace '%{c}', $IpAddress.ToString() } if($macro -match '%{d}') { $macro = $macro -replace '%{d}', $Domain } if($macro -match '%{h}') { #we assume here that domain is a HELO domain $macro = $macro -replace '%{h}', $Domain } if($macro -match '%{s}' -and $senderValid) { $macro = $macro -replace '%{s}', $SenderAddress } if($macro -match '%{l}' -and $senderValid) { $macro = $macro -replace '%{l}', $senderParts[0] } if($macro -match '%{o}' -and $senderValid) { $macro = $macro -replace '%{o}', $senderParts[1] } if($macro -match '%{v}') { if($IpAddress.AddressFamily -eq 'InterNetwork') { $macro = $macro -replace '%{v}', 'in-addr' } else { $macro = $macro -replace '%{v}', 'ipv6' } } return $macro } } #endregion Internal commands # SIG # Begin signature block # MIIt9gYJKoZIhvcNAQcCoIIt5zCCLeMCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCDoB7ip82Tme3SL # yEWubIYm4JxbZt+WdN0GBLUHl0NGYqCCE2AwggWQMIIDeKADAgECAhAFmxtXno4h # 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/XOSIrps4SXzInjHPEYuBETzMYIZ7DCC # GegCAQEwfTBpMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQsIEluYy4x # QTA/BgNVBAMTOERpZ2lDZXJ0IFRydXN0ZWQgRzQgQ29kZSBTaWduaW5nIFJTQTQw # OTYgU0hBMzg0IDIwMjEgQ0ExAhAP9xCe9qf4ax3LBs7uih/sMA0GCWCGSAFlAwQC # AQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI # hvcNAQkEMSIEIBIZQFE9AkKIIQQHqLCd7+lWRhSigzwcIm5I+zhtdLvmMA0GCSqG # SIb3DQEBAQUABIIBgI5ko0Kc7dfeDzDd+N9C74Sx2Jq1T7ruCokPLAwIwUgi46x8 # bALkoCCWoe1p0laLbIgwhSOSyW6ipFOpRrWVugKy3ESvImf+i/PwzpDWiPZJ4zY5 # L0BNAUmaEn4EjJqIKSG55C7kIUA9MDZqwxXxdyhiVV3ytJEV0JMJitRLapj9i1p2 # x4lNa/ixtoyomZ35EQF6iIT9cK6BdXTIwsiLch3VGbbu/mq5D+M6aNMZY1LCsL6M # cd2dc+xNDLhZsjuv7oRlziESM/d9UuoQ/JvEOEaQVXjkkJM31juB/IxXttHr9W1A # SWNxUDlF+73QxL9Uy0bAqwhuquqwfusYX88+qxM6c7D3DM2AIQQC6e44CTYijJ46 # m0v1Ez8tS12xIPfFgIQBZJa5jOWpPqy4Z0cSk0P4FsMCicCOrtVXOIT1tQIjRUvY # 2wy3GWUuIfZ++U+4buF6mKl/zK9zqullQHPu2kXV7rJPoB6fLNxGaE8jzbFb0Meu # 4esna+H6Oova6EiPTaGCFzkwghc1BgorBgEEAYI3AwMBMYIXJTCCFyEGCSqGSIb3 # DQEHAqCCFxIwghcOAgEDMQ8wDQYJYIZIAWUDBAIBBQAwdwYLKoZIhvcNAQkQAQSg # aARmMGQCAQEGCWCGSAGG/WwHATAxMA0GCWCGSAFlAwQCAQUABCArf5+h4DUo0XQN # JlWr5gVBfPV7CLFNb6NBbiYoY1TPeQIQLi5H3BvGvr+92jQaFvinHRgPMjAyNTAx # MTAwNzI3NTVaoIITAzCCBrwwggSkoAMCAQICEAuuZrxaun+Vh8b56QTjMwQwDQYJ # KoZIhvcNAQELBQAwYzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJ # bmMuMTswOQYDVQQDEzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2 # IFRpbWVTdGFtcGluZyBDQTAeFw0yNDA5MjYwMDAwMDBaFw0zNTExMjUyMzU5NTla # MEIxCzAJBgNVBAYTAlVTMREwDwYDVQQKEwhEaWdpQ2VydDEgMB4GA1UEAxMXRGln # aUNlcnQgVGltZXN0YW1wIDIwMjQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIK # AoICAQC+anOf9pUhq5Ywultt5lmjtej9kR8YxIg7apnjpcH9CjAgQxK+CMR0Rne/ # i+utMeV5bUlYYSuuM4vQngvQepVHVzNLO9RDnEXvPghCaft0djvKKO+hDu6ObS7r # JcXa/UKvNminKQPTv/1+kBPgHGlP28mgmoCw/xi6FG9+Un1h4eN6zh926SxMe6We # 2r1Z6VFZj75MU/HNmtsgtFjKfITLutLWUdAoWle+jYZ49+wxGE1/UXjWfISDmHuI # 5e/6+NfQrxGFSKx+rDdNMsePW6FLrphfYtk/FLihp/feun0eV+pIF496OVh4R1Tv # jQYpAztJpVIfdNsEvxHofBf1BWkadc+Up0Th8EifkEEWdX4rA/FE1Q0rqViTbLVZ # Iqi6viEk3RIySho1XyHLIAOJfXG5PEppc3XYeBH7xa6VTZ3rOHNeiYnY+V4j1XbJ # +Z9dI8ZhqcaDHOoj5KGg4YuiYx3eYm33aebsyF6eD9MF5IDbPgjvwmnAalNEeJPv # IeoGJXaeBQjIK13SlnzODdLtuThALhGtyconcVuPI8AaiCaiJnfdzUcb3dWnqUnj # XkRFwLtsVAxFvGqsxUA2Jq/WTjbnNjIUzIs3ITVC6VBKAOlb2u29Vwgfta8b2ypi # 6n2PzP0nVepsFk8nlcuWfyZLzBaZ0MucEdeBiXL+nUOGhCjl+QIDAQABo4IBizCC # AYcwDgYDVR0PAQH/BAQDAgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYI # KwYBBQUHAwgwIAYDVR0gBBkwFzAIBgZngQwBBAIwCwYJYIZIAYb9bAcBMB8GA1Ud # IwQYMBaAFLoW2W1NhS9zKXaaL3WMaiCPnshvMB0GA1UdDgQWBBSfVywDdw4oFZBm # pWNe7k+SH3agWzBaBgNVHR8EUzBRME+gTaBLhklodHRwOi8vY3JsMy5kaWdpY2Vy # dC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1waW5n # Q0EuY3JsMIGQBggrBgEFBQcBAQSBgzCBgDAkBggrBgEFBQcwAYYYaHR0cDovL29j # c3AuZGlnaWNlcnQuY29tMFgGCCsGAQUFBzAChkxodHRwOi8vY2FjZXJ0cy5kaWdp # Y2VydC5jb20vRGlnaUNlcnRUcnVzdGVkRzRSU0E0MDk2U0hBMjU2VGltZVN0YW1w # aW5nQ0EuY3J0MA0GCSqGSIb3DQEBCwUAA4ICAQA9rR4fdplb4ziEEkfZQ5H2Edub # Tggd0ShPz9Pce4FLJl6reNKLkZd5Y/vEIqFWKt4oKcKz7wZmXa5VgW9B76k9NJxU # l4JlKwyjUkKhk3aYx7D8vi2mpU1tKlY71AYXB8wTLrQeh83pXnWwwsxc1Mt+FWqz # 57yFq6laICtKjPICYYf/qgxACHTvypGHrC8k1TqCeHk6u4I/VBQC9VK7iSpU5wlW # jNlHlFFv/M93748YTeoXU/fFa9hWJQkuzG2+B7+bMDvmgF8VlJt1qQcl7YFUMYgZ # U1WM6nyw23vT6QSgwX5Pq2m0xQ2V6FJHu8z4LXe/371k5QrN9FQBhLLISZi2yemW # 0P8ZZfx4zvSWzVXpAb9k4Hpvpi6bUe8iK6WonUSV6yPlMwerwJZP/Gtbu3CKldMn # n+LmmRTkTXpFIEB06nXZrDwhCGED+8RsWQSIXZpuG4WLFQOhtloDRWGoCwwc6ZpP # ddOFkM2LlTbMcqFSzm4cd0boGhBq7vkqI1uHRz6Fq1IX7TaRQuR+0BGOzISkcqwX # u7nMpFu3mgrlgbAW+BzikRVQ3K2YHcGkiKjA4gi4OA/kz1YCsdhIBHXqBzR0/Zd2 # QwQ/l4Gxftt/8wY3grcc/nS//TVkej9nmUYu83BDtccHHXKibMs/yXHhDXNkoPId # ynhVAku7aRZOwqw6pDCCBq4wggSWoAMCAQICEAc2N7ckVHzYR6z9KGYqXlswDQYJ # KoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IElu # YzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8GA1UEAxMYRGlnaUNlcnQg # VHJ1c3RlZCBSb290IEc0MB4XDTIyMDMyMzAwMDAwMFoXDTM3MDMyMjIzNTk1OVow # YzELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMTswOQYDVQQD # EzJEaWdpQ2VydCBUcnVzdGVkIEc0IFJTQTQwOTYgU0hBMjU2IFRpbWVTdGFtcGlu # ZyBDQTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAMaGNQZJs8E9cklR # VcclA8TykTepl1Gh1tKD0Z5Mom2gsMyD+Vr2EaFEFUJfpIjzaPp985yJC3+dH54P # Mx9QEwsmc5Zt+FeoAn39Q7SE2hHxc7Gz7iuAhIoiGN/r2j3EF3+rGSs+QtxnjupR # PfDWVtTnKC3r07G1decfBmWNlCnT2exp39mQh0YAe9tEQYncfGpXevA3eZ9drMvo # hGS0UvJ2R/dhgxndX7RUCyFobjchu0CsX7LeSn3O9TkSZ+8OpWNs5KbFHc02DVzV # 5huowWR0QKfAcsW6Th+xtVhNef7Xj3OTrCw54qVI1vCwMROpVymWJy71h6aPTnYV # VSZwmCZ/oBpHIEPjQ2OAe3VuJyWQmDo4EbP29p7mO1vsgd4iFNmCKseSv6De4z6i # c/rnH1pslPJSlRErWHRAKKtzQ87fSqEcazjFKfPKqpZzQmiftkaznTqj1QPgv/Ci # PMpC3BhIfxQ0z9JMq++bPf4OuGQq+nUoJEHtQr8FnGZJUlD0UfM2SU2LINIsVzV5 # K6jzRWC8I41Y99xh3pP+OcD5sjClTNfpmEpYPtMDiP6zj9NeS3YSUZPJjAw7W4oi # qMEmCPkUEBIDfV8ju2TjY+Cm4T72wnSyPx4JduyrXUZ14mCjWAkBKAAOhFTuzuld # yF4wEr1GnrXTdrnSDmuZDNIztM2xAgMBAAGjggFdMIIBWTASBgNVHRMBAf8ECDAG # AQH/AgEAMB0GA1UdDgQWBBS6FtltTYUvcyl2mi91jGogj57IbzAfBgNVHSMEGDAW # gBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNVHQ8BAf8EBAMCAYYwEwYDVR0lBAww # CgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBpMCQGCCsGAQUFBzABhhhodHRwOi8v # b2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUHMAKGNWh0dHA6Ly9jYWNlcnRzLmRp # Z2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3J0MEMGA1UdHwQ8MDow # OKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRS # b290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EMAQQCMAsGCWCGSAGG/WwHATANBgkq # hkiG9w0BAQsFAAOCAgEAfVmOwJO2b5ipRCIBfmbW2CFC4bAYLhBNE88wU86/GPvH # UF3iSyn7cIoNqilp/GnBzx0H6T5gyNgL5Vxb122H+oQgJTQxZ822EpZvxFBMYh0M # CIKoFr2pVs8Vc40BIiXOlWk/R3f7cnQU1/+rT4osequFzUNf7WC2qk+RZp4snuCK # rOX9jLxkJodskr2dfNBwCnzvqLx1T7pa96kQsl3p/yhUifDVinF2ZdrM8HKjI/rA # J4JErpknG6skHibBt94q6/aesXmZgaNWhqsKRcnfxI2g55j7+6adcq/Ex8HBanHZ # xhOACcS2n82HhyS7T6NJuXdmkfFynOlLAlKnN36TU6w7HQhJD5TNOXrd/yVjmScs # PT9rp/Fmw0HNT7ZAmyEhQNC3EyTN3B14OuSereU0cZLXJmvkOHOrpgFPvT87eK1M # rfvElXvtCl8zOYdBeHo46Zzh3SP9HSjTx/no8Zhf+yvYfvJGnXUsHicsJttvFXse # GYs2uJPU5vIXmVnKcPA3v5gA3yAWTyf7YGcWoWa63VXAOimGsJigK+2VQbc61RWY # MbRiCQ8KvYHZE/6/pNHzV9m8BPqC3jLfBInwAM1dwvnQI38AC+R2AibZ8GV2QqYp # hwlHK+Z/GqSFD/yYlvZVVCsfgPrA8g4r5db7qS9EFUrnEw4d2zc4GqEr9u3WfPww # ggWNMIIEdaADAgECAhAOmxiO+dAt5+/bUOIIQBhaMA0GCSqGSIb3DQEBDAUAMGUx # CzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3 # dy5kaWdpY2VydC5jb20xJDAiBgNVBAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9v # dCBDQTAeFw0yMjA4MDEwMDAwMDBaFw0zMTExMDkyMzU5NTlaMGIxCzAJBgNVBAYT # AlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2Vy # dC5jb20xITAfBgNVBAMTGERpZ2lDZXJ0IFRydXN0ZWQgUm9vdCBHNDCCAiIwDQYJ # KoZIhvcNAQEBBQADggIPADCCAgoCggIBAL/mkHNo3rvkXUo8MCIwaTPswqclLskh # PfKK2FnC4SmnPVirdprNrnsbhA3EMB/zG6Q4FutWxpdtHauyefLKEdLkX9YFPFIP # Uh/GnhWlfr6fqVcWWVVyr2iTcMKyunWZanMylNEQRBAu34LzB4TmdDttceItDBvu # INXJIB1jKS3O7F5OyJP4IWGbNOsFxl7sWxq868nPzaw0QF+xembud8hIqGZXV59U # WI4MK7dPpzDZVu7Ke13jrclPXuU15zHL2pNe3I6PgNq2kZhAkHnDeMe2scS1ahg4 # AxCN2NQ3pC4FfYj1gj4QkXCrVYJBMtfbBHMqbpEBfCFM1LyuGwN1XXhm2ToxRJoz # QL8I11pJpMLmqaBn3aQnvKFPObURWBf3JFxGj2T3wWmIdph2PVldQnaHiZdpekjw # 4KISG2aadMreSx7nDmOu5tTvkpI6nj3cAORFJYm2mkQZK37AlLTSYW3rM9nF30sE # AMx9HJXDj/chsrIRt7t/8tWMcCxBYKqxYxhElRp2Yn72gLD76GSmM9GJB+G9t+ZD # pBi4pncB4Q+UDCEdslQpJYls5Q5SUUd0viastkF13nqsX40/ybzTQRESW+UQUOsx # xcpyFiIJ33xMdT9j7CFfxCBRa2+xq4aLT8LWRV+dIPyhHsXAj6KxfgommfXkaS+Y # HS312amyHeUbAgMBAAGjggE6MIIBNjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW # BBTs1+OC0nFdZEzfLmc/57qYrhwPTzAfBgNVHSMEGDAWgBRF66Kv9JLLgjEtUYun # pyGd823IDzAOBgNVHQ8BAf8EBAMCAYYweQYIKwYBBQUHAQEEbTBrMCQGCCsGAQUF # BzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQwYIKwYBBQUHMAKGN2h0dHA6 # Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEFzc3VyZWRJRFJvb3RDQS5j # cnQwRQYDVR0fBD4wPDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp # Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDARBgNVHSAECjAIMAYGBFUdIAAwDQYJ # KoZIhvcNAQEMBQADggEBAHCgv0NcVec4X6CjdBs9thbX979XB72arKGHLOyFXqka # uyL4hxppVCLtpIh3bb0aFPQTSnovLbc47/T/gLn4offyct4kvFIDyE7QKt76LVbP # +fT3rDB6mouyXtTP0UNEm0Mh65ZyoUi0mcudT6cGAxN3J0TU53/oWajwvy8Lpuny # NDzs9wPHh6jSTEAZNUZqaVSwuKFWjuyk1T3osdz9HNj0d1pcVIxv76FQPfx2CWiE # n2/K2yCNNWAcAgPLILCsWKAOQGPFmCLBsln1VWvPJ6tsds5vIy30fnFqI2si/xK4 # VC0nftg62fC2h5b9W9FcrBjDTZ9ztwGpn1eqXijiuZQxggN2MIIDcgIBATB3MGMx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjE7MDkGA1UEAxMy # RGlnaUNlcnQgVHJ1c3RlZCBHNCBSU0E0MDk2IFNIQTI1NiBUaW1lU3RhbXBpbmcg # Q0ECEAuuZrxaun+Vh8b56QTjMwQwDQYJYIZIAWUDBAIBBQCggdEwGgYJKoZIhvcN # AQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTAxMTAwNzI3NTVa # MCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFNvThe5i29I+e+T2cUhQhyTVhltFMC8G # CSqGSIb3DQEJBDEiBCCTm5yEBQDUtXx1e9Mrk6pB4P1QnE++W4RvfBDYet+bRzA3 # BgsqhkiG9w0BCRACLzEoMCYwJDAiBCB2dp+o8mMvH0MLOiMwrtZWdf7Xc9sF1mW5 # BZOYQ4+a2zANBgkqhkiG9w0BAQEFAASCAgCy1x+6Q27VOyGBKqwquqLCBRLbSm50 # /7Oe2rO7kLyic5EYJpAyyG4rclIC5TYvDCFTAqsBxO9Ub0fRDuvgqseRAzkLb/M1 # tDBVvHVBo1XOP7pW4MWWxyQBfJNX8YDR84Xu9HpS+AHoYfgfoPLc8xR2PQqzv99A # WU2tNVCeMB61Una0QqyS1H87fqyxe5iSmTp+jAg1qcvphmguZEY01Kq5dsTPkJms # +MIlYv9acyIp1iB5LfMCdQH4X7JjL1xoAyVAqrWhqMiEpdWOfy1SwQkcwCq6k0d8 # 5hHIEz+S48HR07tsYNsedFsgbvMHVjdLnHrilvcn/VUq9kRRli4XGIVtfIjKzq2E # f9TDXwRNOeovP6u5ze7PtG+aHL21K648cWThh74DGCwPWUViGWqqi4uBduH5ytUp # FLxH7IfZ5+cg8Tem5eFZmncLM0hYF38Vau4vRV11mluCiPmUJv8k4vyWfGXwfepc # 1S3i4681/veROlxezbNHDgm/TEfaD1g3VKRnlPiTkn4zwaCGrfMEJLp6ZvmsXrIT # rLUcGBlMu30mMndFCj8WUfhU7o4XynfSSbC3QkHbMPOOKK6zxNYQhL0TBScW145+ # gXs7DZz3y4Avsip8QwR6/imEHBiiEVW+lL/YfxJqGMYFtNKH6XPnboZs8RdnrWTm # ZJdsx33yw5pYHA== # SIG # End signature block |