Runtime/HardwareReadiness.ps1
|
#============================================================================================================================= # # Script Name: HardwareReadiness.ps1 # Description: Verifies the hardware compliance. Return code 0 for success. # In case of failure, returns non zero error code along with error message. # This script is not supported under any Microsoft standard support program or service and is distributed under the MIT license # Copyright (C) 2021 Microsoft Corporation # Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation # files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, # modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software # is furnished to do so, subject to the following conditions: # The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR # COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #============================================================================================================================= $exitCode = 0 [int]$MinOSDiskSizeGB = 64 [int]$MinMemoryGB = 4 [Uint32]$MinClockSpeedMHz = 1000 [Uint32]$MinLogicalCores = 2 [Uint16]$RequiredAddressWidth = 64 $PASS_STRING = "PASS" $FAIL_STRING = "FAIL" $FAILED_TO_RUN_STRING = "FAILED TO RUN" $UNDETERMINED_CAPS_STRING = "UNDETERMINED" $UNDETERMINED_STRING = "Undetermined" $CAPABLE_STRING = "Capable" $NOT_CAPABLE_STRING = "Not capable" $CAPABLE_CAPS_STRING = "CAPABLE" $NOT_CAPABLE_CAPS_STRING = "NOT CAPABLE" $STORAGE_STRING = "Storage" $OS_DISK_SIZE_STRING = "OSDiskSize" $MEMORY_STRING = "Memory" $SYSTEM_MEMORY_STRING = "System_Memory" $GB_UNIT_STRING = "GB" $TPM_STRING = "TPM" $TPM_VERSION_STRING = "TPMVersion" $PROCESSOR_STRING = "Processor" $SECUREBOOT_STRING = "SecureBoot" $I7_7820HQ_CPU_STRING = "i7-7820hq CPU" # 0=name of check, 1=attribute checked, 2=value, 3=PASS/FAIL/UNDETERMINED $logFormat = '{0}: {1}={2}. {3}; ' # 0=name of check, 1=attribute checked, 2=value, 3=unit of the value, 4=PASS/FAIL/UNDETERMINED $logFormatWithUnit = '{0}: {1}={2}{3}. {4}; ' # 0=name of check. $logFormatReturnReason = '{0}, ' # 0=exception. $logFormatException = '{0}; ' # 0=name of check, 1= attribute checked and its value, 2=PASS/FAIL/UNDETERMINED $logFormatWithBlob = '{0}: {1}. {2}; ' # return returnCode is -1 when an exception is thrown. 1 if the value does not meet requirements. 0 if successful. -2 default, script didn't run. $outObject = @{ returnCode = -2; returnResult = $FAILED_TO_RUN_STRING; returnReason = ""; logging = "" } # NOT CAPABLE(1) state takes precedence over UNDETERMINED(-1) state function Private:UpdateReturnCode { param( [Parameter(Mandatory = $true)] [ValidateRange(-2, 1)] [int] $ReturnCode ) Switch ($ReturnCode) { 0 { if ($outObject.returnCode -eq -2) { $outObject.returnCode = $ReturnCode } } 1 { $outObject.returnCode = $ReturnCode } -1 { if ($outObject.returnCode -ne 1) { $outObject.returnCode = $ReturnCode } } } } $Source = @" using Microsoft.Win32; using System; using System.Runtime.InteropServices; public class CpuFamilyResult { public bool IsValid { get; set; } public string Message { get; set; } } public class CpuFamily { [StructLayout(LayoutKind.Sequential)] public struct SYSTEM_INFO { public ushort ProcessorArchitecture; ushort Reserved; public uint PageSize; public IntPtr MinimumApplicationAddress; public IntPtr MaximumApplicationAddress; public IntPtr ActiveProcessorMask; public uint NumberOfProcessors; public uint ProcessorType; public uint AllocationGranularity; public ushort ProcessorLevel; public ushort ProcessorRevision; } [DllImport("kernel32.dll")] internal static extern void GetNativeSystemInfo(ref SYSTEM_INFO lpSystemInfo); public enum ProcessorFeature : uint { ARM_SUPPORTED_INSTRUCTIONS = 34 } [DllImport("kernel32.dll")] [return: MarshalAs(UnmanagedType.Bool)] static extern bool IsProcessorFeaturePresent(ProcessorFeature processorFeature); private const ushort PROCESSOR_ARCHITECTURE_X86 = 0; private const ushort PROCESSOR_ARCHITECTURE_ARM64 = 12; private const ushort PROCESSOR_ARCHITECTURE_X64 = 9; private const string INTEL_MANUFACTURER = "GenuineIntel"; private const string AMD_MANUFACTURER = "AuthenticAMD"; private const string QUALCOMM_MANUFACTURER = "Qualcomm Technologies Inc"; public static CpuFamilyResult Validate(string manufacturer, ushort processorArchitecture) { CpuFamilyResult cpuFamilyResult = new CpuFamilyResult(); if (string.IsNullOrWhiteSpace(manufacturer)) { cpuFamilyResult.IsValid = false; cpuFamilyResult.Message = "Manufacturer is null or empty"; return cpuFamilyResult; } string registryPath = "HKEY_LOCAL_MACHINE\\Hardware\\Description\\System\\CentralProcessor\\0"; SYSTEM_INFO sysInfo = new SYSTEM_INFO(); GetNativeSystemInfo(ref sysInfo); switch (processorArchitecture) { case PROCESSOR_ARCHITECTURE_ARM64: if (manufacturer.Equals(QUALCOMM_MANUFACTURER, StringComparison.OrdinalIgnoreCase)) { bool isArmv81Supported = IsProcessorFeaturePresent(ProcessorFeature.ARM_SUPPORTED_INSTRUCTIONS); if (!isArmv81Supported) { string registryName = "CP 4030"; long registryValue = (long)Registry.GetValue(registryPath, registryName, -1); long atomicResult = (registryValue >> 20) & 0xF; if (atomicResult >= 2) { isArmv81Supported = true; } } cpuFamilyResult.IsValid = isArmv81Supported; cpuFamilyResult.Message = isArmv81Supported ? "" : "Processor does not implement ARM v8.1 atomic instruction"; } else { cpuFamilyResult.IsValid = false; cpuFamilyResult.Message = "The processor isn't currently supported for Windows 11"; } break; case PROCESSOR_ARCHITECTURE_X64: case PROCESSOR_ARCHITECTURE_X86: int cpuFamily = sysInfo.ProcessorLevel; int cpuModel = (sysInfo.ProcessorRevision >> 8) & 0xFF; int cpuStepping = sysInfo.ProcessorRevision & 0xFF; if (manufacturer.Equals(INTEL_MANUFACTURER, StringComparison.OrdinalIgnoreCase)) { try { cpuFamilyResult.IsValid = true; cpuFamilyResult.Message = ""; if (cpuFamily >= 6 && cpuModel <= 95 && !(cpuFamily == 6 && cpuModel == 85)) { cpuFamilyResult.IsValid = false; cpuFamilyResult.Message = ""; } else if (cpuFamily == 6 && (cpuModel == 142 || cpuModel == 158) && cpuStepping == 9) { string registryName = "Platform Specific Field 1"; int registryValue = (int)Registry.GetValue(registryPath, registryName, -1); if ((cpuModel == 142 && registryValue != 16) || (cpuModel == 158 && registryValue != 8)) { cpuFamilyResult.IsValid = false; } cpuFamilyResult.Message = "PlatformId " + registryValue; } } catch (Exception ex) { cpuFamilyResult.IsValid = false; cpuFamilyResult.Message = "Exception:" + ex.GetType().Name; } } else if (manufacturer.Equals(AMD_MANUFACTURER, StringComparison.OrdinalIgnoreCase)) { cpuFamilyResult.IsValid = true; cpuFamilyResult.Message = ""; if (cpuFamily < 23 || (cpuFamily == 23 && (cpuModel == 1 || cpuModel == 17))) { cpuFamilyResult.IsValid = false; } } else { cpuFamilyResult.IsValid = false; cpuFamilyResult.Message = "Unsupported Manufacturer: " + manufacturer + ", Architecture: " + processorArchitecture + ", CPUFamily: " + sysInfo.ProcessorLevel + ", ProcessorRevision: " + sysInfo.ProcessorRevision; } break; default: cpuFamilyResult.IsValid = false; cpuFamilyResult.Message = "Unsupported CPU category. Manufacturer: " + manufacturer + ", Architecture: " + processorArchitecture + ", CPUFamily: " + sysInfo.ProcessorLevel + ", ProcessorRevision: " + sysInfo.ProcessorRevision; break; } return cpuFamilyResult; } } "@ # Storage try { $osDrive = Get-WmiObject -Class Win32_OperatingSystem | Select-Object -Property SystemDrive $osDriveSize = Get-WmiObject -Class Win32_LogicalDisk -filter "DeviceID='$($osDrive.SystemDrive)'" | Select-Object @{Name = "SizeGB"; Expression = { $_.Size / 1GB -as [int] } } if ($null -eq $osDriveSize) { UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $STORAGE_STRING $outObject.logging += $logFormatWithBlob -f $STORAGE_STRING, "Storage is null", $FAIL_STRING $exitCode = 1 } elseif ($osDriveSize.SizeGB -lt $MinOSDiskSizeGB) { UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $STORAGE_STRING $outObject.logging += $logFormatWithUnit -f $STORAGE_STRING, $OS_DISK_SIZE_STRING, ($osDriveSize.SizeGB), $GB_UNIT_STRING, $FAIL_STRING $exitCode = 1 } else { $outObject.logging += $logFormatWithUnit -f $STORAGE_STRING, $OS_DISK_SIZE_STRING, ($osDriveSize.SizeGB), $GB_UNIT_STRING, $PASS_STRING UpdateReturnCode -ReturnCode 0 } } catch { UpdateReturnCode -ReturnCode -1 $outObject.logging += $logFormat -f $STORAGE_STRING, $OS_DISK_SIZE_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING $outObject.logging += $logFormatException -f "$($_.Exception.GetType().Name) $($_.Exception.Message)" $exitCode = 1 } # Memory (bytes) try { $memory = Get-WmiObject Win32_PhysicalMemory | Measure-Object -Property Capacity -Sum | Select-Object @{Name = "SizeGB"; Expression = { $_.Sum / 1GB -as [int] } } if ($null -eq $memory) { UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $MEMORY_STRING $outObject.logging += $logFormatWithBlob -f $MEMORY_STRING, "Memory is null", $FAIL_STRING $exitCode = 1 } elseif ($memory.SizeGB -lt $MinMemoryGB) { UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $MEMORY_STRING $outObject.logging += $logFormatWithUnit -f $MEMORY_STRING, $SYSTEM_MEMORY_STRING, ($memory.SizeGB), $GB_UNIT_STRING, $FAIL_STRING $exitCode = 1 } else { $outObject.logging += $logFormatWithUnit -f $MEMORY_STRING, $SYSTEM_MEMORY_STRING, ($memory.SizeGB), $GB_UNIT_STRING, $PASS_STRING UpdateReturnCode -ReturnCode 0 } } catch { UpdateReturnCode -ReturnCode -1 $outObject.logging += $logFormat -f $MEMORY_STRING, $SYSTEM_MEMORY_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING $outObject.logging += $logFormatException -f "$($_.Exception.GetType().Name) $($_.Exception.Message)" $exitCode = 1 } # TPM try { $tpm = Get-Tpm if ($null -eq $tpm) { UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $TPM_STRING $outObject.logging += $logFormatWithBlob -f $TPM_STRING, "TPM is null", $FAIL_STRING $exitCode = 1 } elseif ($tpm.TpmPresent) { $tpmVersion = Get-WmiObject -Class Win32_Tpm -Namespace root\CIMV2\Security\MicrosoftTpm | Select-Object -Property SpecVersion if ($null -eq $tpmVersion.SpecVersion) { UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $TPM_STRING $outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, "null", $FAIL_STRING $exitCode = 1 } $majorVersion = $tpmVersion.SpecVersion.Split(",")[0] -as [int] if ($majorVersion -lt 2) { UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $TPM_STRING $outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, ($tpmVersion.SpecVersion), $FAIL_STRING $exitCode = 1 } else { $outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, ($tpmVersion.SpecVersion), $PASS_STRING UpdateReturnCode -ReturnCode 0 } } else { if ($tpm.GetType().Name -eq "String") { UpdateReturnCode -ReturnCode -1 $outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING $outObject.logging += $logFormatException -f $tpm } else { UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $TPM_STRING $outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, ($tpm.TpmPresent), $FAIL_STRING } $exitCode = 1 } } catch { UpdateReturnCode -ReturnCode -1 $outObject.logging += $logFormat -f $TPM_STRING, $TPM_VERSION_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING $outObject.logging += $logFormatException -f "$($_.Exception.GetType().Name) $($_.Exception.Message)" $exitCode = 1 } # CPU Details $cpuDetails; try { $cpuDetails = @(Get-WmiObject -Class Win32_Processor)[0] if ($null -eq $cpuDetails) { UpdateReturnCode -ReturnCode 1 $exitCode = 1 $outObject.returnReason += $logFormatReturnReason -f $PROCESSOR_STRING $outObject.logging += $logFormatWithBlob -f $PROCESSOR_STRING, "CpuDetails is null", $FAIL_STRING } else { $processorCheckFailed = $false # AddressWidth if ($null -eq $cpuDetails.AddressWidth -or $cpuDetails.AddressWidth -ne $RequiredAddressWidth) { UpdateReturnCode -ReturnCode 1 $processorCheckFailed = $true $exitCode = 1 } # ClockSpeed is in MHz if ($null -eq $cpuDetails.MaxClockSpeed -or $cpuDetails.MaxClockSpeed -le $MinClockSpeedMHz) { UpdateReturnCode -ReturnCode 1; $processorCheckFailed = $true $exitCode = 1 } # Number of Logical Cores if ($null -eq $cpuDetails.NumberOfLogicalProcessors -or $cpuDetails.NumberOfLogicalProcessors -lt $MinLogicalCores) { UpdateReturnCode -ReturnCode 1 $processorCheckFailed = $true $exitCode = 1 } # CPU Family Add-Type -TypeDefinition $Source $cpuFamilyResult = [CpuFamily]::Validate([String]$cpuDetails.Manufacturer, [uint16]$cpuDetails.Architecture) $cpuDetailsLog = "{AddressWidth=$($cpuDetails.AddressWidth); MaxClockSpeed=$($cpuDetails.MaxClockSpeed); NumberOfLogicalCores=$($cpuDetails.NumberOfLogicalProcessors); Manufacturer=$($cpuDetails.Manufacturer); Caption=$($cpuDetails.Caption); $($cpuFamilyResult.Message)}" if (!$cpuFamilyResult.IsValid) { UpdateReturnCode -ReturnCode 1 $processorCheckFailed = $true $exitCode = 1 } if ($processorCheckFailed) { $outObject.returnReason += $logFormatReturnReason -f $PROCESSOR_STRING $outObject.logging += $logFormatWithBlob -f $PROCESSOR_STRING, ($cpuDetailsLog), $FAIL_STRING } else { $outObject.logging += $logFormatWithBlob -f $PROCESSOR_STRING, ($cpuDetailsLog), $PASS_STRING UpdateReturnCode -ReturnCode 0 } } } catch { UpdateReturnCode -ReturnCode -1 $outObject.logging += $logFormat -f $PROCESSOR_STRING, $PROCESSOR_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING $outObject.logging += $logFormatException -f "$($_.Exception.GetType().Name) $($_.Exception.Message)" $exitCode = 1 } # SecureBooot try { $isSecureBootEnabled = Confirm-SecureBootUEFI $outObject.logging += $logFormatWithBlob -f $SECUREBOOT_STRING, $CAPABLE_STRING, $PASS_STRING UpdateReturnCode -ReturnCode 0 } catch [System.PlatformNotSupportedException] { # PlatformNotSupportedException "Cmdlet not supported on this platform." - SecureBoot is not supported or is non-UEFI computer. UpdateReturnCode -ReturnCode 1 $outObject.returnReason += $logFormatReturnReason -f $SECUREBOOT_STRING $outObject.logging += $logFormatWithBlob -f $SECUREBOOT_STRING, $NOT_CAPABLE_STRING, $FAIL_STRING $exitCode = 1 } catch [System.UnauthorizedAccessException] { UpdateReturnCode -ReturnCode -1 $outObject.logging += $logFormatWithBlob -f $SECUREBOOT_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING $outObject.logging += $logFormatException -f "$($_.Exception.GetType().Name) $($_.Exception.Message)" $exitCode = 1 } catch { UpdateReturnCode -ReturnCode -1 $outObject.logging += $logFormatWithBlob -f $SECUREBOOT_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING $outObject.logging += $logFormatException -f "$($_.Exception.GetType().Name) $($_.Exception.Message)" $exitCode = 1 } # i7-7820hq CPU try { $supportedDevices = @('surface studio 2', 'precision 5520') $systemInfo = @(Get-WmiObject -Class Win32_ComputerSystem)[0] if ($null -ne $cpuDetails) { if ($cpuDetails.Name -match 'i7-7820hq cpu @ 2.90ghz'){ $modelOrSKUCheckLog = $systemInfo.Model.Trim() if ($supportedDevices -contains $modelOrSKUCheckLog){ $outObject.logging += $logFormatWithBlob -f $I7_7820HQ_CPU_STRING, $modelOrSKUCheckLog, $PASS_STRING $outObject.returnCode = 0 $exitCode = 0 } } } } catch { if ($outObject.returnCode -ne 0){ UpdateReturnCode -ReturnCode -1 $outObject.logging += $logFormatWithBlob -f $I7_7820HQ_CPU_STRING, $UNDETERMINED_STRING, $UNDETERMINED_CAPS_STRING $outObject.logging += $logFormatException -f "$($_.Exception.GetType().Name) $($_.Exception.Message)" $exitCode = 1 } } Switch ($outObject.returnCode) { 0 { $outObject.returnResult = $CAPABLE_CAPS_STRING } 1 { $outObject.returnResult = $NOT_CAPABLE_CAPS_STRING } -1 { $outObject.returnResult = $UNDETERMINED_CAPS_STRING } -2 { $outObject.returnResult = $FAILED_TO_RUN_STRING } } $outObject | ConvertTo-Json -Compress # SIG # Begin signature block # MIIeYwYJKoZIhvcNAQcCoIIeVDCCHlACAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAB29gIatM9AduU # 0wAT9t/RelQp5NrCmrRfGDey08MR56CCF/swggS9MIIDJaADAgECAhAebu87xzjh # s0Q4yPEDH+JoMA0GCSqGSIb3DQEBCwUAME4xHjAcBgNVBAMMFXdvcmtwbGFjZWNs # b3VkaHViLmNvbTEsMCoGCSqGSIb3DQEJARYdY29udGFjdEB3b3JrcGxhY2VjbG91 # ZGh1Yi5jb20wHhcNMjYwNzEzMDgyMjM1WhcNMjkwNzEzMDgzMjI5WjBOMR4wHAYD # VQQDDBV3b3JrcGxhY2VjbG91ZGh1Yi5jb20xLDAqBgkqhkiG9w0BCQEWHWNvbnRh # Y3RAd29ya3BsYWNlY2xvdWRodWIuY29tMIIBojANBgkqhkiG9w0BAQEFAAOCAY8A # MIIBigKCAYEAse6XztERSyHn9DVqj8Rdv0qjc5owqvgAIGaYxBmfiQuoM48Fo4Xt # 1ovi9brLUtf55G4XgthNPCoanxfCRRg30IVRxaDfdPXJzYmgsM5tXlsuNU49lE7E # PJk3+jEOgSCt8NKzmVPKpNRG0NmK0a8wm12cceYZOZlSYE0+ZtT6wy5PQQjMUqIx # XnGjt4H0nfgZZa7D4FyARKOVg/Xr9sUq5jIn3zszvg4jjeb4b0DKJtfbHukhWc2Y # oVFgswxVBXCWIaBnfF/cjqMfK/CaToT2trVb4hG4qcQ31s1nR4keoRaOw/vyd6ap # rEtCsT22N/Jx0dz7fIo1tVyvIaVcHdN9LW3chn0en0OKZ6Ke1OH9wf2prl4KA6Ww # VzrAZrOlXTAItdK7D9kKO/HeJd4PZvO53oy1LdmMGLSz3OLB9e5q7yo8rfqi5Ka9 # KzM2CrSzz1yphn/H90wz7Q2pm4FIlWdcj86A/0kmhYg+5Wqqbg1drrPXu4nEBwWN # /dzoGtKZKHTdAgMBAAGjgZYwgZMwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoG # CCsGAQUFBwMDMD8GA1UdEQQ4MDaBHWNvbnRhY3RAd29ya3BsYWNlY2xvdWRodWIu # Y29tghV3b3JrcGxhY2VjbG91ZGh1Yi5jb20wDAYDVR0TAQH/BAIwADAdBgNVHQ4E # FgQUXIOOADQM78XfPAncirgCECedg9gwDQYJKoZIhvcNAQELBQADggGBADhZUB2R # 5J/Jw030xodhEWeCQ0vnJRaiEsjOxuArQREKH3lCrQ3UsUVl292d6LnQUSTH/jF7 # rovEZ+JN2GQ/LCrXRaCuwCEGZKzlSEbtYWhfwDyj6GpIPq8Y4SeXyjdq4/rrI1bm # iTK4Sq7EoBlGJuX6l2nfvx1tTioSr11FoDfllJR7EYawRj9hBFJ0gG0b2SuYZMgW # gaDKefcnJDmOwcRNAZUII0ss8EeyANukWSkNN5ILZ+iKDpQgZxgDLPTiRguCyx45 # PI5wrVTjV/pR7IrtSIfq8UladlrSZJyyDn3NV2ATvIZ6wNxbTmPFcE0uMg/EYzwd # Tek+CgXL3TxUKeldJM4YDWPimNBRhOPXzBDiOQIj6WNswt/KM1oDLnA00CNtciPN # dn+dXlneMvTEUah9wyt8o8tkLpoBw+KN+Bq/K0O1qPtS7umi70l45pPiej+mwbwq # ztcaoVD7a8ggHP1Vdp/rnafM4GtyCAE6b7U9Yzgvp1/a1kh7XffmqVhRRjCCBY0w # ggR1oAMCAQICEA6bGI750C3n79tQ4ghAGFowDQYJKoZIhvcNAQEMBQAwZTELMAkG # A1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRp # Z2ljZXJ0LmNvbTEkMCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENB # MB4XDTIyMDgwMTAwMDAwMFoXDTMxMTEwOTIzNTk1OVowYjELMAkGA1UEBhMCVVMx # FTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNv # bTEhMB8GA1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MIICIjANBgkqhkiG # 9w0BAQEFAAOCAg8AMIICCgKCAgEAv+aQc2jeu+RdSjwwIjBpM+zCpyUuySE98orY # WcLhKac9WKt2ms2uexuEDcQwH/MbpDgW61bGl20dq7J58soR0uRf1gU8Ug9SH8ae # FaV+vp+pVxZZVXKvaJNwwrK6dZlqczKU0RBEEC7fgvMHhOZ0O21x4i0MG+4g1ckg # HWMpLc7sXk7Ik/ghYZs06wXGXuxbGrzryc/NrDRAX7F6Zu53yEioZldXn1RYjgwr # t0+nMNlW7sp7XeOtyU9e5TXnMcvak17cjo+A2raRmECQecN4x7axxLVqGDgDEI3Y # 1DekLgV9iPWCPhCRcKtVgkEy19sEcypukQF8IUzUvK4bA3VdeGbZOjFEmjNAvwjX # WkmkwuapoGfdpCe8oU85tRFYF/ckXEaPZPfBaYh2mHY9WV1CdoeJl2l6SPDgohIb # Zpp0yt5LHucOY67m1O+SkjqePdwA5EUlibaaRBkrfsCUtNJhbesz2cXfSwQAzH0c # lcOP9yGyshG3u3/y1YxwLEFgqrFjGESVGnZifvaAsPvoZKYz0YkH4b235kOkGLim # dwHhD5QMIR2yVCkliWzlDlJRR3S+Jqy2QXXeeqxfjT/JvNNBERJb5RBQ6zHFynIW # IgnffEx1P2PsIV/EIFFrb7GrhotPwtZFX50g/KEexcCPorF+CiaZ9eRpL5gdLfXZ # qbId5RsCAwEAAaOCATowggE2MA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFOzX # 44LScV1kTN8uZz/nupiuHA9PMB8GA1UdIwQYMBaAFEXroq/0ksuCMS1Ri6enIZ3z # bcgPMA4GA1UdDwEB/wQEAwIBhjB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGG # GGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2Nh # Y2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDBF # BgNVHR8EPjA8MDqgOKA2hjRodHRwOi8vY3JsMy5kaWdpY2VydC5jb20vRGlnaUNl # cnRBc3N1cmVkSURSb290Q0EuY3JsMBEGA1UdIAQKMAgwBgYEVR0gADANBgkqhkiG # 9w0BAQwFAAOCAQEAcKC/Q1xV5zhfoKN0Gz22Ftf3v1cHvZqsoYcs7IVeqRq7IviH # GmlUIu2kiHdtvRoU9BNKei8ttzjv9P+Aufih9/Jy3iS8UgPITtAq3votVs/59Pes # MHqai7Je1M/RQ0SbQyHrlnKhSLSZy51PpwYDE3cnRNTnf+hZqPC/Lwum6fI0POz3 # A8eHqNJMQBk1RmppVLC4oVaO7KTVPeix3P0c2PR3WlxUjG/voVA9/HYJaISfb8rb # II01YBwCA8sgsKxYoA5AY8WYIsGyWfVVa88nq2x2zm8jLfR+cWojayL/ErhULSd+ # 2DrZ8LaHlv1b0VysGMNNn3O3AamfV6peKOK5lDCCBrQwggScoAMCAQICEA3HrFcF # /yGZLkBDIgw6SYYwDQYJKoZIhvcNAQELBQAwYjELMAkGA1UEBhMCVVMxFTATBgNV # BAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEhMB8G # A1UEAxMYRGlnaUNlcnQgVHJ1c3RlZCBSb290IEc0MB4XDTI1MDUwNzAwMDAwMFoX # DTM4MDExNDIzNTk1OVowaTELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0 # LCBJbmMuMUEwPwYDVQQDEzhEaWdpQ2VydCBUcnVzdGVkIEc0IFRpbWVTdGFtcGlu # ZyBSU0E0MDk2IFNIQTI1NiAyMDI1IENBMTCCAiIwDQYJKoZIhvcNAQEBBQADggIP # ADCCAgoCggIBALR4MdMKmEFyvjxGwBysddujRmh0tFEXnU2tjQ2UtZmWgyxU7UNq # EY81FzJsQqr5G7A6c+Gh/qm8Xi4aPCOo2N8S9SLrC6Kbltqn7SWCWgzbNfiR+2fk # HUiljNOqnIVD/gG3SYDEAd4dg2dDGpeZGKe+42DFUF0mR/vtLa4+gKPsYfwEu7EE # bkC9+0F2w4QJLVSTEG8yAR2CQWIM1iI5PHg62IVwxKSpO0XaF9DPfNBKS7Zazch8 # NF5vp7eaZ2CVNxpqumzTCNSOxm+SAWSuIr21Qomb+zzQWKhxKTVVgtmUPAW35xUU # FREmDrMxSNlr/NsJyUXzdtFUUt4aS4CEeIY8y9IaaGBpPNXKFifinT7zL2gdFpBP # 9qh8SdLnEut/GcalNeJQ55IuwnKCgs+nrpuQNfVmUB5KlCX3ZA4x5HHKS+rqBvKW # xdCyQEEGcbLe1b8Aw4wJkhU1JrPsFfxW1gaou30yZ46t4Y9F20HHfIY4/6vHespY # MQmUiote8ladjS/nJ0+k6MvqzfpzPDOy5y6gqztiT96Fv/9bH7mQyogxG9QEPHrP # V6/7umw052AkyiLA6tQbZl1KhBtTasySkuJDpsZGKdlsjg4u70EwgWbVRSX1Wd4+ # zoFpp4Ra+MlKM2baoD6x0VR4RjSpWM8o5a6D8bpfm4CLKczsG7ZrIGNTAgMBAAGj # ggFdMIIBWTASBgNVHRMBAf8ECDAGAQH/AgEAMB0GA1UdDgQWBBTvb1NK6eQGfHrK # 4pBW9i/USezLTjAfBgNVHSMEGDAWgBTs1+OC0nFdZEzfLmc/57qYrhwPTzAOBgNV # HQ8BAf8EBAMCAYYwEwYDVR0lBAwwCgYIKwYBBQUHAwgwdwYIKwYBBQUHAQEEazBp # MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC5kaWdpY2VydC5jb20wQQYIKwYBBQUH # MAKGNWh0dHA6Ly9jYWNlcnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRS # b290RzQuY3J0MEMGA1UdHwQ8MDowOKA2oDSGMmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0 # LmNvbS9EaWdpQ2VydFRydXN0ZWRSb290RzQuY3JsMCAGA1UdIAQZMBcwCAYGZ4EM # AQQCMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAgEAF877FoAc/gc9EXZx # ML2+C8i1NKZ/zdCHxYgaMH9Pw5tcBnPw6O6FTGNpoV2V4wzSUGvI9NAzaoQk97fr # PBtIj+ZLzdp+yXdhOP4hCFATuNT+ReOPK0mCefSG+tXqGpYZ3essBS3q8nL2UwM+ # NMvEuBd/2vmdYxDCvwzJv2sRUoKEfJ+nN57mQfQXwcAEGCvRR2qKtntujB71WPYA # gwPyWLKu6RnaID/B0ba2H3LUiwDRAXx1Neq9ydOal95CHfmTnM4I+ZI2rVQfjXQA # 1WSjjf4J2a7jLzWGNqNX+DF0SQzHU0pTi4dBwp9nEC8EAqoxW6q17r0z0noDjs6+ # BFo+z7bKSBwZXTRNivYuve3L2oiKNqetRHdqfMTCW/NmKLJ9M+MtucVGyOxiDf06 # VXxyKkOirv6o02OoXN4bFzK0vlNMsvhlqgF2puE6FndlENSmE+9JGYxOGLS/D284 # NHNboDGcmWXfwXRy4kbu4QFhOm0xJuF2EZAOk5eCkhSxZON3rGlHqhpB/8MluDez # ooIs8CVnrpHMiD2wL40mm53+/j7tFaxYKIqL0Q4ssd8xHZnIn/7GELH3IdvG2XlM # 9q7WP/UwgOkw/HQtyRN62JK4S1C8uw3PdBunvAZapsiI5YKdvlarEvf8EA+8hcpS # M9LHJmyrxaFtoza2zNaQ9k+5t1wwggbtMIIE1aADAgECAhAKgO8YS43xBYLRxHan # lXRoMA0GCSqGSIb3DQEBCwUAMGkxCzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdp # Q2VydCwgSW5jLjFBMD8GA1UEAxM4RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3Rh # bXBpbmcgUlNBNDA5NiBTSEEyNTYgMjAyNSBDQTEwHhcNMjUwNjA0MDAwMDAwWhcN # MzYwOTAzMjM1OTU5WjBjMQswCQYDVQQGEwJVUzEXMBUGA1UEChMORGlnaUNlcnQs # IEluYy4xOzA5BgNVBAMTMkRpZ2lDZXJ0IFNIQTI1NiBSU0E0MDk2IFRpbWVzdGFt # cCBSZXNwb25kZXIgMjAyNSAxMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKC # AgEA0EasLRLGntDqrmBWsytXum9R/4ZwCgHfyjfMGUIwYzKomd8U1nH7C8Dr0cVM # F3BsfAFI54um8+dnxk36+jx0Tb+k+87H9WPxNyFPJIDZHhAqlUPt281mHrBbZHqR # K71Em3/hCGC5KyyneqiZ7syvFXJ9A72wzHpkBaMUNg7MOLxI6E9RaUueHTQKWXym # OtRwJXcrcTTPPT2V1D/+cFllESviH8YjoPFvZSjKs3SKO1QNUdFd2adw44wDcKgH # +JRJE5Qg0NP3yiSyi5MxgU6cehGHr7zou1znOM8odbkqoK+lJ25LCHBSai25CFyD # 23DZgPfDrJJJK77epTwMP6eKA0kWa3osAe8fcpK40uhktzUd/Yk0xUvhDU6lvJuk # x7jphx40DQt82yepyekl4i0r8OEps/FNO4ahfvAk12hE5FVs9HVVWcO5J4dVmVzi # x4A77p3awLbr89A90/nWGjXMGn7FQhmSlIUDy9Z2hSgctaepZTd0ILIUbWuhKuAe # NIeWrzHKYueMJtItnj2Q+aTyLLKLM0MheP/9w6CtjuuVHJOVoIJ/DtpJRE7Ce7vM # RHoRon4CWIvuiNN1Lk9Y+xZ66lazs2kKFSTnnkrT3pXWETTJkhd76CIDBbTRofOs # NyEhzZtCGmnQigpFHti58CSmvEyJcAlDVcKacJ+A9/z7eacCAwEAAaOCAZUwggGR # MAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOQ7/PIx7f391/ORcWMZUEPPYYzoMB8G # A1UdIwQYMBaAFO9vU0rp5AZ8esrikFb2L9RJ7MtOMA4GA1UdDwEB/wQEAwIHgDAW # BgNVHSUBAf8EDDAKBggrBgEFBQcDCDCBlQYIKwYBBQUHAQEEgYgwgYUwJAYIKwYB # BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBdBggrBgEFBQcwAoZRaHR0 # cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0VHJ1c3RlZEc0VGltZVN0 # YW1waW5nUlNBNDA5NlNIQTI1NjIwMjVDQTEuY3J0MF8GA1UdHwRYMFYwVKBSoFCG # Tmh0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFRydXN0ZWRHNFRpbWVT # dGFtcGluZ1JTQTQwOTZTSEEyNTYyMDI1Q0ExLmNybDAgBgNVHSAEGTAXMAgGBmeB # DAEEAjALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQADggIBAGUqrfEcJwS5rmBB # 7NEIRJ5jQHIh+OT2Ik/bNYulCrVvhREafBYF0RkP2AGr181o2YWPoSHz9iZEN/FP # sLSTwVQWo2H62yGBvg7ouCODwrx6ULj6hYKqdT8wv2UV+Kbz/3ImZlJ7YXwBD9R0 # oU62PtgxOao872bOySCILdBghQ/ZLcdC8cbUUO75ZSpbh1oipOhcUT8lD8QAGB9l # ctZTTOJM3pHfKBAEcxQFoHlt2s9sXoxFizTeHihsQyfFg5fxUFEp7W42fNBVN4ue # LaceRf9Cq9ec1v5iQMWTFQa0xNqItH3CPFTG7aEQJmmrJTV3Qhtfparz+BW60OiM # EgV5GWoBy4RVPRwqxv7Mk0Sy4QHs7v9y69NBqycz0BZwhB9WOfOu/CIJnzkQTwtS # SpGGhLdjnQ4eBpjtP+XB3pQCtv4E5UCSDag6+iX8MmB10nfldPF9SVD7weCC3yXZ # i/uuhqdwkgVxuiMFzGVFwYbQsiGnoa9F5AaAyBjFBtXVLcKtapnMG3VH3EmAp/js # J3FVF3+d1SVDTmjFjLbNFZUWMXuZyvgLfgyPehwJVxwC+UpX2MSey2ueIu9THFVk # T+um1vshETaWyQo8gmBto/m3acaP9QsuLj3FNwFlTxq25+T4QwX9xa6ILs84ZPvm # povq90K8eWyG2N01c4IhSOxqt81nMYIFvjCCBboCAQEwYjBOMR4wHAYDVQQDDBV3 # b3JrcGxhY2VjbG91ZGh1Yi5jb20xLDAqBgkqhkiG9w0BCQEWHWNvbnRhY3RAd29y # a3BsYWNlY2xvdWRodWIuY29tAhAebu87xzjhs0Q4yPEDH+JoMA0GCWCGSAFlAwQC # AQUAoIGEMBgGCisGAQQBgjcCAQwxCjAIoAKAAKECgAAwGQYJKoZIhvcNAQkDMQwG # CisGAQQBgjcCAQQwHAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwLwYJKoZI # hvcNAQkEMSIEIDwwnwoAAkuzpcNOEvc7r57GACKW4NqVxDIYU3rd25cVMA0GCSqG # SIb3DQEBAQUABIIBgANazkxTo/xxTwUG95fGJtb+57QmSMRxATuxq/Y6zX5PqvjR # IojxsZ4TcaG8vaVqfd5/3L0ZX9IjFHoy5f84CtVixeyl5jb0VuVn4vfYimDMfYIX # fN+/g56RRhYPycSjbv41lLEzr34jLH6TO3TXyIwfQn4r3Ixv7bMQ76DP2XEB8e3m # X7Rhyn2xhjCzzG7U8SF4ANweCfLX7yz0ybh997k4i9R5mj0uOtvFjoMDX5p+hBr6 # w+F+7f4EmS2J9UvjcS4fxpUWA1G2t33YPWQZH9jwb5x5W28D9M3uZX/+5QCNJh/J # fXavXD7R08T1d3EyWiU6KkPnlzrs70/dLLgMH5qsSHNAkOdahLuK2CLT47CU7I+m # JfnqT7ncJlWUuhq26/MRGzh0BpVUndpgXssz4GW3VqbFf/pqlOqTGQ7KTwQOE+nX # Z5RwCukAhxRLKUBBjWgSU6h53kAEjXTV22AZjrzBpaBHHhJYJS5EFwfWoMk8ziiz # joma+96bW0xwVpjGVqGCAyYwggMiBgkqhkiG9w0BCQYxggMTMIIDDwIBATB9MGkx # CzAJBgNVBAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjFBMD8GA1UEAxM4 # RGlnaUNlcnQgVHJ1c3RlZCBHNCBUaW1lU3RhbXBpbmcgUlNBNDA5NiBTSEEyNTYg # MjAyNSBDQTECEAqA7xhLjfEFgtHEdqeVdGgwDQYJYIZIAWUDBAIBBQCgaTAYBgkq # hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yNjA4MTIxMDUy # NTZaMC8GCSqGSIb3DQEJBDEiBCCIlfnMcTz8KFhLvZJQZwJI7p+S6eIakdNCVEmw # uA4aTjANBgkqhkiG9w0BAQEFAASCAgCvJgy3jwZY71oX/SDHfGunAVUzX3/KIfG0 # to81vRUM09qX6zbH0/IuQTZc09VXmvRKMRCFw+UFAuj4uSqyCB+MBUSSbDpt+sen # ELdkzQe4LYC/aQsR+q3ZeYZY699yVS/cmSCjFCJybnAjDyw+tJ6Qox8zXqTHa2C1 # SnTmKsLkqMqEfPfE3QYnQOXZ61vZqJqIjaBzw7+Zh+hV0B4biZLVHca5nl3WuLnX # Rh8EciyD5hzpGY+uc0WI23dlsXhPxUOSNdP0sR4TJ471QHBMp1c7TiY+mYbOtzyO # SLWY+8Y11n/YQRahSwk1yxJOd1Jo39Uaca5c5oa+G0QLW26S2kUmX0QTpFSkypB7 # Hiovb8GaSk3P4v0VpjiMi6qUpxSM84KTRzMm/K41R1jGbaF9ODbL0zMoiUkXgQe5 # 87JZpKCwmYOpcFiBKJ0j8svaQMLzGpDdwe59PNejPWMGvkDtOaa2Cw8JO1G1sgsD # xEW2FRwMbcZc+WEImPC+XlstEhsXzjj/ZegM/vx6vrItvr+18xuKW30rVa3Or6Ja # g9qKvRB+R8KhfZGq7p50K1/qXlzGnBZcF9v7q6WrQagMRFWCgIvH7caF5dzCVnNz # hg8Bn1lO9yLTGjuz6I+iR7vRjtyfscr+sY+vzp7F/TZtxb2soRIUy9ZPnyEqmlSC # J79FgeB4Pw== # SIG # End signature block |