HP.Private.psm1

#
# Copyright 2018-2021 HP Development Company, L.P.
# All Rights Reserved.
#
# NOTICE: All information contained herein is, and remains the property of HP Development Company, L.P.
#
# The intellectual and technical concepts contained herein are proprietary to HP Development Company, L.P
# and may be covered by U.S. and Foreign Patents, patents in process, and are protected by
# trade secret or copyright law. Dissemination of this information or reproduction of this material
# is strictly forbidden unless prior written permission is obtained from HP Development Company, L.P.


$ErrorActionPreference = 'Stop'
Set-StrictMode -Version 5.1

Add-Type -TypeDefinition @'
   using System;
   using System.Runtime.InteropServices;
   using System.Collections.Generic;
 
   public struct SureAdminSetting
   {
       public string Name;
       public string Value;
       public string AuthString;
   }
 
  [StructLayout(LayoutKind.Sequential, Pack = 1)]
  public struct arr4k_t
  {
      [MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.I1, SizeConst = 4096)] public byte[] raw;
  };
 
  public static class X509Utilities
  {
    [DllImport("dfmbios32.dll", EntryPoint = "get_public_key_from_pem", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern int get_public_key_from_pem32([In] string file, [In,Out] ref arr4k_t modulus, [In,Out] ref UInt32 modulus_size, [In,Out] ref UInt32 exponent);
    [DllImport("dfmbios64.dll", EntryPoint = "get_public_key_from_pem", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
    public static extern int get_public_key_from_pem64([In] string file, [In,Out] ref arr4k_t modulus, [In,Out] ref UInt32 modulus_size, [In,Out] ref UInt32 exponent);
   };
'@



function Get-HPPrivateReadINI {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $True)] $file,
    [Parameter(Mandatory = $False)] [int]$maxRetries = 0
  )

  Write-Verbose "Reading INI style file '$file'"
  [System.IO.StreamReader]$streamReader = $null
  $CommentCount = 0
  $name = $null
  $memoryStream = $null
  $webClient = $null


  try {
    if ($file.StartsWith("http://") -or
      $file.StartsWith("https://") -or
      $file.StartsWith("file://") -or
      $file.StartsWith("ftp://")) {
      Write-Verbose ("Reading network file: $file")
      $webClient = New-Object System.Net.WebClient

      if ($file.StartsWith("ftp://")) {
        $webClient.Credentials = New-Object -Type System.Net.NetworkCredential -ArgumentList "anonymous","anonymous"
      }
      else {
        $webClient.UseDefaultCredentials = $true;
      }

      $webClient.Proxy = [System.Net.WebRequest]::GetSystemWebProxy()


      [int]$retries = $maxRetries
      do {
        try {
          Write-Verbose "Downloading CVA file $file, try $($maxRetries-$retries) / $maxRetries"
          $data = $webClient.DownloadData($file)
          $retries = 0
        }
        catch {
          $retries = $retries - 1
          Write-Verbose ("Download failed: $($_.Exception)")
          if ($retries -le 0) { throw $_ }
          Start-Sleep 5

        }
      } while ($retries -gt 0)


      $memoryStream = New-Object System.IO.MemoryStream ($data,[System.Text.Encoding]::Default)
      $streamReader = New-Object System.IO.StreamReader ($memoryStream)
    }
    else {
      Write-Verbose ("Reading filesystem file: $file")
      $streamReader = New-Object -TypeName System.IO.StreamReader -ArgumentList $file
    }

    $ini = @{}
    while (($line = $streamReader.ReadLine()) -ne $null) {
      switch -regex ($line) {
        "^\[(.+)\]$" {
          # Section
          $section = $matches[1]
          $ini[$section] = @{}
          $CommentCount = 0
        }
        "^(;.*)$" {
          # Comment
          if (!(Test-Path variable:\section)) {
            $section = "No-Section"
            $ini[$section] = @{}
          }
          $value = $matches[1]
          $CommentCount = $CommentCount + 1
          $name = "Comment" + $CommentCount
          $ini[$section][$name] = $value
        }
        "(.+?)\s*=\s*(.*)" {
          # Key
          if (!($section)) {
            $section = "No-Section"
            $ini[$section] = @{}
          }
          $name,$value = $matches[1..2]
          if ($ini[$section][$name]) {
            if ($ini[$section][$name] -is [string]) {
              $ini[$section][$name] = @($ini[$section][$name])
            }
            $ini[$section][$name] += $value
          }
          else {
            $ini[$section][$name] = $value
          }
          continue
        }
        "^(?!(.*[=])).*" {
          # section text block
          if (!($section)) {
            $section = "No-Section"
            $ini[$section] = @{}
          }

          if ($ini[$section]["_body"] -eq $null) {
            $ini[$section]["_body"] = @()
          }

          $ini[$section]["_body"] += ($matches.Values | Where-Object { $_.StartsWith("[") -eq $false })
        }
      }
    }
  }
  finally {
    if ($streamReader) {
      $streamReader.Close()
      $streamReader.Dispose()
      $streamReader = $null
    }

    if ($memoryStream) {
      $memoryStream.Close()
      $memoryStream.Dispose()
      $memoryStream = $null
    }

    if ($webClient) {
      $webClient.Dispose()
      $webClient = $null
    }

  }
  return $ini
}




# this is what the downloaded filename will be
function Get-HPPrivateTemporaryFileName ($filename,[System.IO.DirectoryInfo]$cacheDir = [System.IO.Path]::GetTempPath() + "hp") {
  $cacheDir = Join-Path -Path $cacheDir -ChildPath $filename
  $cacheDir.FullName
}




function GetLockedStreamForWrite {
  [CmdletBinding()]
  param([string]$target,[int]$maxRetries = 10)

  Write-Verbose "Opening exclusive access to file $target with maximum retries of $maxRetries"
  $lock_wait = $false

  do {
    try {
      $lock_wait = $false
      $result = New-Object -TypeName System.IO.FileStream -ArgumentList $target,Create,Write,None
    }
    catch {
      Write-Verbose ("******* $($_ | fl)")
      $lock_wait = $true
      if ($maxRetries -gt 0) {
        Start-Sleep -Seconds 30
        $maxRetries = $maxRetries - 1
      }
      else {
        throw "Could not obtain exclusive access to file '$target' and all retries were exhausted."
      }

    }
  }
  while ($lock_wait -eq $true)
  $result
}

# check for collision with other processes
function GetSharedFileInformation {
  [CmdletBinding()]
  param($file,[string]$mode,[switch]$wait,[int]$maxRetries,[switch]$progress,[switch]$skipSignatureCheck)

  $return = $true
  $length = 0
  $sig = $false

  Write-Verbose ("Getting file information for file $file with access rights=$mode, wait = $wait, maxRetries=$maxRetries, skipAuthenticode=$($skipSignatureCheck.IsPresent)")
  if (-not $wait.IsPresent) {
    Write-Verbose ("This operation will not be retried.")
    $maxRetries = 0
  }

  do {
    # file length
    try {
      $length = (Get-ChildItem -File $file -ErrorAction Stop).Length
    }
    catch {
      return (-1,$true,$skipSignatureCheck.IsPresent)
    }

    Write-Verbose ("Target file length on disk is $length bytes")
    try {
      $fs = [System.IO.File]::Open($file,"Open",$mode)
      $return = $true
      $fs.Close()
      $fs.Dispose()
      Write-Verbose "Able to read from file '$file', it doesn't seem locked."


      if ($skipSignatureCheck.IsPresent) {
        Write-Verbose "Not checking Authenticode signature for file $file"
        $sig = $true
      }
      else {
        $sig = Get-HPPrivateCheckSignature -File $file -Progress:$progress
      }
      break
    }
    catch [System.IO.FileNotFoundException]{
      return (-1,$true,$skipSignatureCheck.IsPresent)
    }
    catch {
      Write-Verbose "Internal error: $_.Message"
      $return = $false
      if ($maxRetries -gt 0) {
        if ($progress) {
          Write-Progress -Activity "Blocked by another process, will retry for ($maxRetries) tries"
        }

        Write-Verbose ("Sleeping for 30 seconds since someone else has '$file' locked")
        Start-Sleep -Seconds 30
        Write-Verbose ("Woke up")
      }
      $maxRetries = $maxRetries - 1
    }
  } while ($maxRetries -gt 0)
  ($length,$return,$sig)
}


# download a file
function Invoke-HPPrivateDownloadFile {
  [CmdletBinding()]
  param
  (
    [string]$url,
    [string]$target,
    [bool]$progress,
    [string]$noclobber,
    [switch]$panic,
    [int]$maxRetries = 0,
    [switch]$skipSignatureCheck
  )
  Write-Verbose ("Requesting to download $url to $target with progress: $progress and signatureCheckSkip: $skipSignatureCheck")
  [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols
  $targetStream = $null
  $responseStream = $null
  $response = $null

  try {
    if (Test-Path $target -PathType Leaf) {
      # target file exists
      switch ($noclobber) {
        "no" {
          if ($panic.IsPresent) { throw "File $target already exists, will not overwrite." }

          if ($progress) { Write-Host -ForegroundColor Magenta "File $target already exists, will not overwrite." }
          return
        }
        "yes" {
          if ($progress -eq $true) { Write-Verbose "Overwriting existing file $target" }
        }
        "skip" {}
      }

    }
    else {
      #create lead directory if needed
      $lead = Split-Path $target
      if (!(Test-Path $lead)) {
        Write-Verbose "Creating directory '$lead'"
        $tmp = New-Item -ItemType Directory -Force -Path $lead
      }
    }

    $uri = New-Object "System.Uri" "$url"

    $retries = $maxRetries



    do {
      $request = [System.Net.HttpWebRequest]::Create($uri)
      $request.set_Timeout(60000)

      if (-not $request -is [System.Net.FtpWebRequest]) {
        $request.set_UserAgent($LIBRARY_UA);
      }
      try {
        Write-Verbose "Executing query on $uri, try $($maxRetries-$retries) / $maxRetries"
        $response = $request.GetResponse()
        $retries = 0
      }
      catch {
        $retries = $retries - 1
        Write-Verbose ("Query failed $($_.Exception)")

        if ($retries -le 0) {
          throw
        }
        Start-Sleep 5
      }

    } while ($retries -gt 0)

    $responseContentLength = $response.get_ContentLength()
    if ($responseContentLength -ge 1024) {
      $totalLength = [System.Math]::Floor($responseContentLength / 1024)
    }
    else {
      $totalLength = 1
    }

    # Someone else may be downloading this file at this time, so we'll wait until they release the
    # lock and then we check the size
    Write-Verbose ("Target file is $target")

    $r = GetSharedFileInformation -File $target -Mode "Read" -Wait -maxRetries $maxRetries -Progress:$progress -skipSignatureCheck:$skipSignatureCheck
    if ($noclobber -eq "skip") {
      if (($r[0] -eq $response.get_ContentLength()) -and ($r[2] -eq $true)) {
        Write-Verbose "File already exists or another process has finished downloading this file for us."
        return
      }
      else {
        Write-Verbose ("Existing file $target doesn't seem correct (size=$($r[0]) vs expected $($response.get_ContentLength()), signature_check=$($r[2]), deleting it.")
      }
    }

    $responseStream = $response.GetResponseStream()
    $targetStream = GetLockedStreamForWrite -maxRetries $maxRetries -Target $target
    #try {
    $buffer = New-Object byte[] 10KB
    $count = $responseStream.Read($buffer,0,$buffer.Length)
    $downloadedBytes = $count



    #being too verbose with Write-Progress slows down the process
    $maxChunks = 20
    $chunkSize = $totalLength / $maxChunks
    while ($chunkSize -gt 1024) {
      $maxChunks = $maxChunks * 2
      $chunkSize = $totalLength / $maxChunks
    }

    if ($chunkSize -lt 16384) {
      $chunkSize = 16384
      $maxChunks = 1
    }

    $lastChunk = 0
    while ($count -gt 0) {
      $targetStream.Write($buffer,0,$count)
      $count = $responseStream.Read($buffer,0,$buffer.Length)
      $downloadedBytes = $downloadedBytes + $count
      $thisChunk = [System.Math]::Floor(($downloadedBytes / 100) / $totalLength)

      if (($progress -eq $true) -and ($thisChunk -gt $lastChunk)) {
        $lastChunk = $thisChunk
        Write-Progress -Activity "Downloading file '$($url.split('/') | Select -Last 1)'" -Status "Downloaded ($([System.Math]::Floor($downloadedBytes/1024))K of $($totalLength)K): " -PercentComplete ((([System.Math]::Floor($downloadedBytes / 1024)) / $totalLength) * 100)
      }
    }

    if ($progress -eq $true) {
      Write-Verbose ("Finished downloading '$($url.split('/') | Select -Last 1)'")
      Write-Progress -Activity "Finished downloading file '$($url.split('/') | Select -Last 1)'" -Completed
    }

  }


  finally {
    if ($targetStream) {
      $targetStream.Flush()
      $targetStream.Close()
      $targetStream.Dispose()
    }

    if ($responseStream) {
      $responseStream.Close()
      $responseStream.Dispose()
    }

    if ($response) {
      $response.Close()
    }
  }

}

# perform an action after a SoftPaq download completed
function Invoke-PostDownloadSoftpaqAction
{
  [CmdletBinding()]
  param([string]$downloadedFile,[string]$action,[string]$number,$info,[string]$Destination)

  Write-Verbose 'Processing post-download action'
  $PostDownloadCmd = $null

  switch ($action) {
    "extract" {
      if (!$Destination) {
        $Destination = (Get-Item $downloadedFile).DirectoryName
        $Destination = Join-Path -Path $Destination -ChildPath (Get-Item $downloadedFile).BaseName
      }
      Write-Verbose -Message "Extracting $downloadedFile to: $Destination"
      Start-Process -Wait "$downloadedFile" -ArgumentList "-e -f `"$Destination`"","-s"
    }
    "install" {
      #$PostDownloadCmd = descendNodesAndGet $info -field "install"
      Start-Process -Wait "$downloadedFile"
    }
    "silentinstall" {
      # Get the silent install command from the metadata
      if (!$info) { $info = Get-SoftpaqMetadata $number }

      $PostDownloadCmd = $info | Out-SoftpaqField -Field "silentinstall"
      Start-Process -Wait "$downloadedFile" -ArgumentList "-s","-e cmd.exe","-a","/c $PostDownloadCmd"
    }
  }
  Write-Verbose 'Post-download action processing complete'
}

function Get-HPPrivateAllowedHttpsProtocols {
  [CmdletBinding()]
  param()

  $c = [System.Net.SecurityProtocolType]([System.Net.SecurityProtocolType].GetEnumNames() | Where-Object { $_ -ne "Ssl3" -and $_ -ne "Tls" -and $_ -ne "Tls11" })
  Write-Verbose "Removing obsolete protocols SSL 3.0, TLS 1.0 and TLS 1.1, now supporting: $c"
  $c
}


function Get-HPPrivateCacheDirPath {

  [CmdletBinding()]
  param([System.IO.DirectoryInfo]$seed)

  if (-not $seed) {
    $seed = [System.IO.Path]::GetTempPath() + "hp"
  }
  Join-Path -Path $seed -ChildPath "cache"
  Write-Verbose "Local caching path is: $seed"
}

# check authenticode signature
# check CVA and SoftPaq hash to determine download of the SoftPaq
#
# tests (remove these comments once we are happy with the function)
#
# PASS: Get-HPPrivateCheckSignature -file C:\windows\System32\notepad.exe -signedBy "Microsoft Windows" -Verbose
# PASS: Get-HPPrivateCheckSignature -file C:\windows\System32\notepad.exe -Verbose
# PASS: Get-HPPrivateCheckSignature -file .\sp99062.exe -CVAfile .\sp99062.cva -Verbose
# PASS: Get-HPPrivateCheckSignature -file .\sp99062.exe -CVAfile .\sp99062.cva -Verbose -signedBy "HP Inc."
function Get-HPPrivateCheckSignature {
  [CmdletBinding()]
  param(
    [Parameter(Position = 0,Mandatory = $true)]
    [string]$file,

    [Parameter(Mandatory = $false,Position = 1)]
    [string]$CVAfile = $null,

    [Parameter(Mandatory = $false,Position = 2)]
    [string]$signedBy = $null,

    [Parameter(Mandatory = $false,Position = 3)]
    [switch]$Progress

  )

  if ($Progress.IsPresent) {
    Write-Progress -Activity "Checking integrity of $file"
  }

  try {

    $c = Get-AuthenticodeSignature -FilePath $file

    if ($c.Status -ne "Valid") {
      Write-Verbose ("$file is not signed or certificate is invalid.")
      return $false
    }
    if ($signedBy) {
      $signer = $c.SignerCertificate.Subject.Split(",")[0].trim().Split("=")[1]

      if ($signer -ne $signedBy) {
        Write-Verbose ("$file is not signed by $signedBy, it is signed by $signer, failing.")
        return $false
      }
      else {
        Write-Verbose ("$file is signed by $signedBy.")
        # return $true
      }
    }

    if ($CVAfile) {
      Write-Verbose "Verifying '$file' using '$CVAFile'"

      $targetFile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($file)
      $targetCVA = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($CVAFile)

      # Getting hash value of CVA
      $read_file = Get-HPPrivateReadINI -File $targetCVA
      $CVA_SHA256 = $read_file | Out-SoftpaqField -Field SoftPaqSHA256
      $CVA_MD5 = $read_file | Out-SoftpaqField -Field SoftPaqMD5

      Write-Verbose "CVA has MD5 hash '$CVA_MD5' and sha256 hash '$CVA_SHA256'"
      if ($CVA_SHA256 -and $CVA_SHA256.Length -eq 64) {
        Write-Verbose 'Checking SHA256 hash'
        $sha256match = return $CVA_SHA256 -eq (Get-FileHash -Path $targetFile -Algorithm SHA256).Hash
        Write-Verbose "SHA256 matched: $sha256match"
        return $sha256match
      }
      elseif ($CVA_MD5) {
        Write-Verbose 'Checking MD5 hash'
        $md5match = $CVA_MD5 -eq (Get-FileHash -Path $targetFile -Algorithm MD5).Hash
        Write-Verbose "MD5 matched: $md5match"
        return $md5match
      }
      else {
        Write-Verbose 'This CVA file has no checksum value'
        return $false
      }
    }

    # When only file is passed and it has valid signature
    return $true
  }

  catch {
    Write-Verbose "During signature check, had exception $_.Exception"
    return $false
  }
  finally {
    if ($Progress.IsPresent) {
      Write-Progress -Activity "Finished checking integrity of $file" -Completed
    }
  }
}

function Invoke-HPPrivateDeleteCachedItem {
  [CmdletBinding()]
  param([Parameter(Mandatory = $true)] $cab)

  Invoke-HPPrivateSafeRemove -Path $cab
  Invoke-HPPrivateSafeRemove -Path "$cab.dir" -Recurse
}


function Invoke-HPPrivateSafeRemove {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true,ValueFromPipelineByPropertyName = $true)] [string[]]$path,
    [Parameter(Mandatory = $false)] [switch]$recurse
  )
  foreach ($p in $path) {
    if (Test-Path $p) {
      Write-Verbose "Removing $p"
      Remove-Item $p -Recurse:$recurse
    }
  }
}

function Invoke-HPPrivateExpandCAB {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)] $cab,
    [Parameter(Mandatory = $true)] $expectedFile
  )
  Write-Verbose "Expanding CAB $cab to $cab.dir"

  $target = "$cab.dir"
  Invoke-HPPrivateSafeRemove -Path $target -Recurse
  Write-Verbose "Expanding $cab to $target"
  $result = New-Item -Force $target -ItemType Directory
  Write-Verbose "Created folder $result"

  $shell = New-Object -ComObject "Shell.Application"
  try {
    if (!$?) { $(throw "unable to create $comObject object") }

    $sourceCab = $shell.Namespace($cab).items()
    $DestinationFolder = $shell.Namespace($target)
    $DestinationFolder.CopyHere($sourceCab)
  }
  finally {
    [System.Runtime.InteropServices.Marshal]::ReleaseComObject([System.__ComObject]$shell) | Out-Null
    [System.GC]::Collect()
    [System.GC]::WaitForPendingFinalizers()

  }
  $downloadedOk = Test-Path $expectedFile
  if ($downloadedOk -eq $false) {
    throw "Invalid cab file, did not find $expectedFile in contents"
  }
  return $expectedFile
}


# check if a download is needed, based on file existence and the remote last-modified time
function Test-HPPrivateIsDownloadNeeded {
  [CmdletBinding()]
  param([Parameter(Mandatory = $true)] $url,[Parameter(Mandatory = $true)] $file)

  Write-Verbose "Checking if we need a new copy of $file"

  # $c = [System.Net.ServicePointManager]::SecurityProtocol
  # Write-Verbose ("Allowed HTTPS protocols: $c")
  [System.Net.ServicePointManager]::SecurityProtocol = Get-HPPrivateAllowedHttpsProtocols

  # need to validate if $header can be generated, in other words if $url is legitimate
  try {
    $headers = (Invoke-WebRequest -Uri $url -Method HEAD -UseBasicParsing).Headers
    [datetime]$offered = [string]$headers["Last-Modified"]
    Write-Verbose "File on server has timestamp $offered"
  }
  catch {
    Write-Verbose "data file not found on $url"
    throw
  }

  $exists = Test-Path -Path $file -PathType leaf
  if ($exists -eq $false) {
    Write-Verbose "Cached file $file does not exist, will need to download new file"
    $offered
    $true
  }
  else {
    [datetime]$have = (Get-Item $file).CreationTime
    $r = ($have -lt $offered)
    Write-Verbose "Cached file exist and it has timestamp $offered. Need to download: $r"

    $offered
    $r
  }
}


# check if the downloaded xml file is corrupted.
function Test-HPPrivateIsValidXmlFile {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true)] $file
  )

  if (-not (Test-Path -Path $file)) {
    Write-Verbose "File $file does not exist."
    return $false
  }

  # Check for Load or Parse errors when loading the XML file.
  $xml = New-Object System.Xml.XmlDocument
  try {
    $xml.Load($file)
    return $true
  }
  catch [System.Xml.XmlException]{
    Write-Verbose "Invalid Xml file $file"
    return $false
  }
}


function Get-HPPrivateUserAgent () {
  "hpcmsl ($($MyInvocation.MyCommand.Module.Name); $($MyInvocation.MyCommand.Module.Version))"
}

# calculates CurrentOSVer
function GetCurrentOSVer
{
  [CmdletBinding()]
  param()

  try {
    $result = [string](Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name DisplayVersion | Select-Object DisplayVersion).DisplayVersion

    if ($result -match '[0-9]{2}[hH][0-9]') {
      $bits = $result.substring(0,2)
      if ($bits -ge 21) {
        # for DisplayVersion >= 21XX, use the DisplayVersion as OSVer
        # convert OSVer to lower since the reference files have "21h1" in file name
        return $result.ToLower()
      }
    }
  }
  catch {
    Write-Verbose "Display Version not found. Fallback to ReleaseId."
  }

  # If DisplayVersion isn't found or DisplayVersion < 21XX, use ReleaseId instead
  $result = [string](Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion' -Name ReleaseID | Select-Object ReleaseID).ReleaseId
  return $result
}

function validateWmiResult {
  [CmdletBinding()]
  param([int]$code,[int]$category = 0xff)

  Write-Verbose "Validating error code $code for facility $category"
  switch ($code) {
    0 {}
    0xea {}
    6 { throw [NotSupportedException]"Operation could not be completed. Please ensure this is a supported HP system." }
    5 { throw [ArgumentException]"Method called with invalid parameters." }
    4 { throw [UnauthorizedAccessException]"The caller does not have permissions to perform this operation." }
    default { validateWmiResultInCategory -Category $category -code $code }
  }
}


function validateWmiResultInCategory {
  [CmdletBinding()]
  param([int]$category,[int]$code)

  switch ($category) {
    1 {
      switch ($code) {
        0x40 { throw [NotSupportedException]"This system does not support firmware logs." }
        0x41 { throw [System.TimeoutException]"Call has timed out." }
        default { throw [SystemException]"An unknown error $code has occured." }
      }
    }
    2 {
      switch ($code) {
        0x0b { throw [UnauthorizedAccessException]"The caller does not have permissions to perform this operation." }
        0x0e { throw [UnauthorizedAccessException]"The operation could not be completed, possibly due to a bios password mismatch?" }
        0x0010 { throw [SystemException]"Invalid flash offset." }
        0x0012 { throw [SystemException]"Invalid flash checksum" }
        0x0013 { throw [InvalidOperationException]"Flash-in-progress error" }
        0x0014 { throw [InvalidOperationException]"Flash-in-progress not set" }
        default { throw [SystemException]"An unknown error $code has occured." }
      }
    }
    3 {
      switch ($code) {
        # this facility doesn't define specific codes
        default { throw [SystemException]"An unknown error $code has occured." }
      }
    }
    4 {
      switch ($code) {
        0x0b { throw [UnauthorizedAccessException]"The caller does not have permissions to perform this operation." }
        0x03 { throw [NotSupportedException]"This system does not support HP Secure Platform or a hardware option is missing." }
        0x1c { throw [SystemException]"The request was not accepted by the BIOS." }
        0x1000 { throw [SystemException]"HP Secure Platform is not provisioned." }
        0x1001 { throw [SystemException]"HP Secure Platform is already provisioned." }
        0x1002 { throw [SystemException]"HP Secure Platform is in use. Deprovision all features that use the HP Secure Platform first." }
        default { throw [SystemException]"An unknown error $code has occured." }
      }
    }
    default {
      throw [SystemException]"An unknown error $code has occured."
    }
  }
}

function Test-HPPrivateCustomResult {
  [CmdletBinding()]
  param([int]$result,[int]$mi_result,[int]$category)
  Write-Verbose ("Checking result={0:x8}, mi_result={1:x8}, category={2:x4}" -f $result,$mi_result,$category)
  switch ($result) {
    0 { Write-Verbose ("Operation succeeded.") }
    0x80000711 { validateWmiResult -code $mi_result -Category $category } # E_DFM_FAILED_WITH_EXTENDED_ERROR
    0x80000710 { throw [NotSupportedException]"Current platform does not support this operation." } # E_DFM_FEATURE_NOT_SUPPORTED
    0x8000070b { throw [System.IO.IOException]"Firmware file could not be read." } # E_DFM_FILE_ACCESS_FAILURE
    0x8000070e { throw [InvalidOperationException]"Firmware file is too long for expected flash type." } # E_DFM_FLASH_BUR_INPUT_DATA_TOO_LARGE
    0x80000712 { throw [InvalidOperationException]"The firmware does not mach the target platform." } # E_DFM_WRONG_FLASH_FILE
    0x80000714 { throw [OutOfMemoryException]"A memory allocation failed. The system may be out of memory." } # E_DFM_ALLOC_FAILED
    0x80000715 { throw [InvalidOperationException]"Password length is not valid." } # E_DFM_PASSWORD_SIZE_INVALID
    0x8000071a { throw [System.ArgumentException]"Invalid parameter for HP Sure View API" }
    1392 { throw [System.IO.IOException]"Could not copy the file to the system partition." } # ERROR_FILE_CORRUPT
    234 { Write-Verbose ("Operation succeeded.") } # MORE_DATA
    default { throw [ComponentModel.Win32Exception]$result }
  }

}




function Convert-HPPrivateObjectToBytes {
  [CmdletBinding()]
  param($obj)

  $mem = $null
  $length = 0
  $bytes = $()


  Write-Verbose "Converting object of type $($obj.Gettype()) to byte array"
  try {
    $length = [System.Runtime.InteropServices.Marshal]::SizeOf($obj)
    $bytes = New-Object byte[] $length
    Write-Verbose "Converting object of type $($obj.Gettype()) is $length bytes"
    $mem = [System.Runtime.InteropServices.Marshal]::AllocHGlobal($length)

    [System.Runtime.InteropServices.Marshal]::StructureToPtr($obj,$mem,$true)
    [System.Runtime.InteropServices.Marshal]::Copy($mem,$bytes,0,$length)
    ($bytes,$length)
  }
  finally {
    # Free the memory we allocated for the struct value
    if ($mem) {
      Write-Verbose "Freeing allocated memory"
      [System.Runtime.InteropServices.Marshal]::FreeHGlobal($mem)
    }
  }
  Write-Verbose "Conversion complete."

}


#region Cryptography

function Get-HPPrivatePublicKeyCoalesce {
  [CmdletBinding()]
  param(
    [System.IO.FileInfo]$file,
    [psobject]$key
  )

  if ($file) {
    $efile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($file)
    Write-Verbose "Coalescing to FILE PEM $efile"
    $modulus = New-Object arr4k_t
    $modulus_size = 4096
    $exponent = 0
    $mi_result = 0

    $c = '[X509Utilities]::get_public_key_from_pem' + (Test-OSBitness) + '($efile,[ref]$modulus, [ref]$modulus_size, [ref]$exponent);'
    $result = Invoke-Expression -Command $c
    Test-HPPrivateCustomResult -result $result -mi_result $mi_result -Category 0x04
    New-Object -TypeName PSObject -Property @{
      Modulus = $modulus.raw[0..($modulus_size - 1)]
      Exponent = $exponent
    }

  }
  else {
    Write-Verbose "Coalescing to binary PEM"
    $key
  }

}


function Get-HPPrivateX509CertCoalesce {
  [CmdletBinding()]
  param(
    [System.IO.FileInfo]$file,
    [System.Security.Cryptography.X509Certificates.X509Certificate2]$cert,
    [string]$password
  )
  $param = @{}
  if ($password) {
    $param.Add("Password",(ConvertTo-SecureString -AsPlainText -Force $password))
  }
  if ($file) {
    $efile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($file)
    Write-Verbose "Coalescing to FILE certificate $efile"
    $param.Add("FileName",$efile)
    Get-HPPrivatePublicKeyCertificateFromPFX @param -Verbose:$VerbosePreference
  }
  else {
    Write-Verbose "Coalescing to binary certificate"
    $key = $cert.PublicKey.key
    $parameters = $key.ExportParameters($false);
    $mod_reversed = $parameters.Modulus
    [array]::Reverse($mod_reversed)
    New-Object -TypeName PSObject -Property @{
      Full = $Cert
      Certificate = $cert.Export('Cert')
      Modulus = $mod_reversed
      Exponent = $parameters.Exponent
    }
  }
}


# get the PK from a PFX file
function Get-HPPrivatePublicKeyCertificateFromPFX {
  [CmdletBinding(DefaultParameterSetName = "FF")]
  param(
    [Parameter(Mandatory = $true,Position = 0)]
    [string]$FileName,

    [Parameter(Mandatory = $false,Position = 1)]
    [securestring]$Password
  )

  $certfile = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($FileName)
  if (-not (Test-Path -PathType leaf -Path $certfile)) {
    throw [System.IO.FileNotFoundException]"Certificate file '$certfile' could not be found"
  }
  Write-Verbose "Extracting public key from '$certfile'."

  try {
    $cert = New-Object -TypeName System.Security.Cryptography.X509Certificates.X509Certificate2 -ArgumentList ($certfile,$Password,'Exportable')
    $key = $cert.PublicKey.Key
    $parameters = $key.ExportParameters($false);

    $mod_reversed = $parameters.Modulus
    [array]::Reverse($mod_reversed)
    New-Object -TypeName PSObject -Property @{
      Full = $cert
      Certificate = $cert.Export('Cert')
      Modulus = $mod_reversed
      Exponent = $parameters.Exponent
    }
  }
  finally {
    #$cert.Dispose();
    #$cert = $null
  }

}

# sign a byte array with a certificate provided in $Filename
function Invoke-HPPrivateSignData {
  [CmdletBinding()]
  param(
    [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate,
    [byte[]]$Data
  )
  $privKey = $Certificate.PrivateKey
  if ($null -eq $privKey) {
    Write-Error "Please provide an exportable key in the PFX file"
  }
  $params = $privKey.ExportParameters($true)
  $cspParams = New-Object System.Security.Cryptography.CspParameters (24,"Microsoft Enhanced RSA and AES Cryptographic Provider")
  $enhancedSignCsp = New-Object System.Security.Cryptography.RSACryptoServiceProvider ($cspParams)
  $enhancedSignCsp.ImportParameters($params)

  $result = $enhancedSignCsp.SignData($Data,[System.Security.Cryptography.HashAlgorithmName]::SHA256,[System.Security.Cryptography.RSASignaturePadding]::Pkcs1)
  [array]::Reverse($result)
  return $result
}

function Get-HPPrivateHash {
  [CmdletBinding()]
  param(
    [Parameter(Mandatory = $true,Position = 0)] [byte[]]$data,
    [Parameter(Mandatory = $false,Position = 1)] [string]$algo = "SHA256"
  )
  $cp = [System.Security.Cryptography.HashAlgorithm]::Create($algo)
  try {
    $result = $cp.ComputeHash($data)
  }
  finally {
    $cp.Dispose()
  }
  $result

}
#endregion

# Downloads files for when OfflineCacheMode is Enable
# If -platform is present : Downloads Advisory Data Files (XXXX_cds.cab) where XXXX is platform ID.
# also downloads the platform List
function Get-HPPrivateOfflineCacheFiles {
  [CmdletBinding()]
  param(
    [string]$url,
    [string]$filename,
    [System.IO.DirectoryInfo]$cacheDirOffline = [System.IO.Path]::GetTempPath() + "hp",
    [switch]$expand
  )

  $file = Get-HPPrivateTemporaryFileName -FileName $filename -cacheDir $cacheDirOffline
  $filename = $filename.Replace("cab","xml")
  $downloadedFile = "$file.dir\$filename"

  Write-Verbose "Checking if $url is available locally."
  try {
    $result = Test-HPPrivateIsDownloadNeeded -url $url -File $file -Verbose:$VerbosePreference
  }
  catch {
    throw [System.Net.WebException]"Could not find a data file for this platform."
  }

  if ($result[1] -eq $true) {
    Write-Verbose "$url is not local, or is out of date, will download."
    Write-Verbose "Cleaning cached data and downloading the data file."
    Invoke-HPPrivateDeleteCachedItem -cab $file
    Invoke-HPPrivateDownloadFile -url $url -Target $file -Verbose:$VerbosePreference
    (Get-Item $file).CreationTime = ($result[0])
    (Get-Item $file).LastWriteTime = ($result[0])
  }

  if ($expand.IsPresent) {
    # Need to make sure that the expanded data file exists and is not corrupted.
    # Otherwise, expand the cab file.
    if (-not (Test-Path $downloadedFile) -or (-not (Test-HPPrivateIsValidXmlFile -File $downloadedFile))) {
      Write-Verbose "Extracting the data file, looking for $downloadedFile."
      $file = Invoke-HPPrivateExpandCAB -cab $file -expectedFile $downloadedFile
    }
  }
  return $downloadedFile
}

# build URL for a remote item
function Get-HPPrivateItemUrl (
  [CmdletBinding()]
  [int]$Number,
  [string]$Ext,
  [string]$Url) {
  if ($url) {
    return "$url/sp$number.$ext"
  }

  [string]$baseNumber = $number.ToString()
  [int]$last3Value = [int]($baseNumber.substring($baseNumber.Length - 3))
  [int]$blockStart = [int]($baseNumber.substring(0,$baseNumber.Length - 3))

  [string]$block = ""
  [int]$blockEnd = $blockStart

  if ($last3Value -gt 500) {
    $blockEnd += 1
    $block = "$($blockStart)501-$($blockEnd)000"
  }
  else {
    if ($last3Value -eq 0) {
      $blockStart -= 1
      $block = "$($blockStart)501-$($blockEnd)000"
    }
    else {
      $block = "$($blockStart)001-$($blockStart)500"
    }
  }

  return "https://ftp.hp.com/pub/softpaq/sp$block/sp$number.$ext"
}

function biosErrorCodesToString ($code) {
  switch ($code) {
    0 { return "OK" }
    1 { return "Not Supported" }
    2 { return "Unspecified error" }
    3 { return "Operation timed out" }
    4 { return "Operation failed or setting name is invalid" }
    5 { return "Invalid parameter" }
    6 { return "Access denied or incorrect password" }
    7 { return "Bios user already exists" }
    8 { return "Bios user not present" }
    9 { return "Bios user name too long" }
    10 { return "Password policy not met" }
    11 { return "Invalid keyboard layout" }
    12 { return "Too many users" }
    32768 { return "Security or password policy not met" }
    default { return "Unknown error: $code" }
  }
}

function getBiosSettingInterface {
  [CmdletBinding(DefaultParameterSetName = 'nNwSession')]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $false)]
    [string]$Target = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $true)]
    [CimSession]$CimSession
  )
  $defaultAction = $ErrorActionPreference
  $ns = getNamespace
  $ErrorActionPreference = "Stop";

  try {
    Write-Verbose "Getting BIOS interface from '$target' for namespace '$ns'"
    $params = @{
      Namespace = $ns
      Class = "HPBIOS_BIOSSettingInterface"
    }

    if ($CimSession) {
      $params.Add("CimSession",$CimSession)
    }

    if ($Target -and ($target -ne ".") -and -not $CimSession) {
      $params.Add("ComputerName",$Target)
    }


    $result = Get-CimInstance @params -ErrorAction stop
    if (-not $result) { throw [System.EntryPointNotFoundException]"Setting interface not found" }
  }
  catch {
    Write-Error "Method failed: $($_.Exception.Message)" -ErrorAction stop
  }
  finally {
    $ErrorActionPreference = $defaultAction
  }
  $result
}

function getNamespace {
  [CmdletBinding()]
  param()
  [string]$c = [environment]::GetEnvironmentVariable("HP_BIOS_NAMESPACE","User")
  if (-not $c) {
    return "root\HP\InstrumentedBIOS"
  }
  Write-Verbose ("Default BIOS namespace is overwritten via HP_BIOS_NAMESPACE Variable, to $c. This should only happen during development.")
  return $c
}

function Get-HPPrivateFileContent {
  [CmdletBinding()]
  param([System.IO.FileInfo]$File)

  $f = $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($File)
  [string]$content = Get-Content -Raw -Path $f -ErrorAction Stop
  return $content
}

function Get-HPPrivateSettingsFromPayload {
  [CmdletBinding()]
  param([string]$Content)
  $payload = $Content | ConvertFrom-Json

  if ($payload.purpose -ne "hp:sureadmin:biossettingslist") {
    throw "The payload should be generated by New-HPSureAdminBIOSSettingValuePayload function"
  }

  $data = [System.Text.Encoding]::UTF8.GetString($payload.Data)
  $settingsList = (Get-HPPrivateSettingsFromJson $data)

  return $settingsList
}

function Get-HPPrivateSettingsFromPayloadFile {
  [CmdletBinding()]
  param([System.IO.FileInfo]$File)
  $content = Get-HPPrivateFileContent $File
  return Get-HPPrivateSettingsFromPayload $content
}

function Get-HPPrivateSettingsFromJsonFile {
  [CmdletBinding()]
  param([System.IO.FileInfo]$File)
  [string]$content = Get-HPPrivateFileContent $File
  return (Get-HPPrivateSettingsFromJson $content)
}

function Get-HPPrivateSettingsFromJson {
  [CmdletBinding()]
  param([string]$Content)
  $list = $Content | ConvertFrom-Json

  $settingsList = New-Object System.Collections.Generic.List[SureAdminSetting]
  foreach ($item in $list) {
    $setting = New-Object -TypeName SureAdminSetting
    $setting.Name = $item.Name
    $setting.Value = $item.Value
    if ("AuthString" -in $item.PSObject.Properties.Name) {
      $setting.AuthString = $item.AuthString
    }
    $settingsList.Add($setting)
  }

  return $settingsList
}

function Get-HPPrivateSettingsFromBcuFile {
  [CmdletBinding()]
  param([System.IO.FileInfo]$File)
  $list = [ordered]@{}
  $auth = @{}
  $currset = ""

  Write-Verbose "Reading from file: $File"
  switch -regex -File $File {

    '^;Signature=<BEAM/>.*' {
      # keeping compatibility with BCU tool
      $c = $matches[0].trim()
      $auth[$currset] = $c.substring(11)
    }

    '^;AuthString=<BEAM/>.*' {
      $c = $matches[0].trim()
      $auth[$currset] = $c.substring(12)
    }

    '^\S.*$' {
      $currset = $matches[0].trim()
      if ($currset -ne "BIOSConfig 1.0" -and -not $currset.StartsWith(";")) {
        $list[$currset] = New-Object System.Collections.Generic.List[System.String]
      }
    }

    '^\s.*$' {
      # value (indented)
      $c = $matches[0].trim()
      $list[$currset].Add($c)
    }
  }

  $settingsList = New-Object System.Collections.Generic.List[SureAdminSetting]
  foreach ($s in $list.keys) {
    $setting = New-Object -TypeName SureAdminSetting
    $setting.Name = $s
    $setting.Value = Get-HPPrivateDesiredValue -Value $list[$s]
    if ($auth.ContainsKey($s)) {
      $setting.AuthString = $auth[$s]
    }
    $settingsList.Add($setting)
  }

  return $settingsList
}

function Get-HPPrivateDesiredValue {
  [CmdletBinding()]
  param($Value)

  $desired = $null

  $list = $Value -split ','
  foreach ($v in $list) {

    if ($v.StartsWith("*")) {
      # enum
      $desired = $v.substring(1)
      break
    }
  }

  if (-not $desired) {
    # not an enum
    if ($list.Count -eq 1) {
      $desired = $list # a string or int
    }
    else {
      $desired = $list -join ',' # an ordered list
    }
  }

  return $desired
}

function Get-HPPrivateSettingsFromCsvFile {
  [CmdletBinding()]
  param([System.IO.FileInfo]$File)
  Write-Verbose "Reading CSV"
  $content = Get-HPPrivateFileContent $File
  $items = $content | ConvertFrom-Csv

  $settingsList = New-Object System.Collections.Generic.List[SureAdminSetting]

  foreach ($item in $items) {
    $setting = New-Object -TypeName SureAdminSetting
    $setting.Name = $item.Name
    $setting.Value = (Get-HPPrivateDesiredValue $item.CURRENT_VALUE)
    if ("AUTHSTRING" -in $item.PSObject.Properties.Name) {
      $setting.AuthString = $item.AuthString
    }
    $settingsList.Add($setting)
  }

  return $settingsList
}

function Get-HPPrivateSettingsFromXmlFile {
  [CmdletBinding()]
  param([System.IO.FileInfo]$File)
  Write-Verbose "Reading XML"
  $content = Get-HPPrivateFileContent $File

  try {
    $entries = ([xml]$content).ImagePal.BIOSSettings.BIOSSetting
    $settingsList = New-Object System.Collections.Generic.List[SureAdminSetting]

    foreach ($item in $entries) {
      $setting = New-Object -TypeName SureAdminSetting
      $setting.Name = $item.Name
      $setting.Value = $item.Value
      if ("AuthString" -in $item.PSObject.Properties.Name) {
        # The XML parser adds an unwanted space in the tag BEAM
        $setting.AuthString = $item.AuthString.InnerXml -replace "<BEAM />","<BEAM/>"
      }
      $settingsList.Add($setting)
    }
  }
  catch [System.Management.Automation.PropertyNotFoundException]{
    throw [System.FormatException]'Invalid XML file.'
  }

  return $settingsList
}

function Get-HPPrivateSettingsFromFile {
  [CmdletBinding()]
  param(
    [System.IO.FileInfo]$FileName,
    [string]$Format
  )
  [System.Collections.Generic.List[SureAdminSetting]]$settingsList = $null

  switch ($Format) {
    { $_ -eq 'csv' } { $settingsList = Get-HPPrivateSettingsFromCsvFile $FileName }
    { $_ -eq 'xml' } { $settingsList = Get-HPPrivateSettingsFromXmlFile $FileName }
    { $_ -eq 'json' } { $settingsList = Get-HPPrivateSettingsFromJsonFile $FileName }
    { $_ -eq 'bcu' } { $settingsList = Get-HPPrivateSettingsFromBcuFile $FileName }
    { $_ -eq 'payload' } { $settingsList = Get-HPPrivateSettingsFromPayloadFile $FileName }
    default { throw [System.FormatException]"Format specifier not provided, and could not determine format from file extension" }
  }

  return $settingsList
}

function Invoke-HPPrivateSetSetting {
  [CmdletBinding(DefaultParameterSetName = 'NewSession')]
  param(
    [Parameter(ParameterSetName = 'NewSession',Position = 0,Mandatory = $true)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 0,Mandatory = $true)]
    [SureAdminSetting]$Setting,
    [Parameter(ParameterSetName = 'NewSession',Position = 1,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 1,Mandatory = $false)]
    [string]$Password,
    [Parameter(ParameterSetName = 'NewSession',Position = 2,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 2,Mandatory = $false)]
    $ErrorHandling = 0,
    [Parameter(ParameterSetName = 'NewSession',Position = 3,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 3,Mandatory = $false)]
    [Alias('Target')]
    [string]$ComputerName = ".",
    [Parameter(ParameterSetName = 'ReuseSession',Position = 4,Mandatory = $true)]
    [CimSession]$CimSession,
    [Parameter(ParameterSetName = 'NewSession',Position = 5,Mandatory = $false)]
    [Parameter(ParameterSetName = 'ReuseSession',Position = 5,Mandatory = $false)]
    [ref]$SingleSettingFailCounter
  )
  $readOnly = 0
  $notFound = 0
  $alreadySet = 0
  $localCounter = 0

  $s = $null
  if (-not $CimSession) { $CimSession = newCimSession -Target $ComputerName }

  try {
    $s = Get-HPBIOSSetting -Name $Setting.Name -CimSession $CimSession -ErrorAction stop
  }
  catch {
    $notFound = 1
    $SingleSettingFailCounter.Value = 0 #matching BCU, even if setting not found exit with 0
    $err = $PSItem.ToString()

    Write-Verbose "'$Setting.Name': $err"
    switch ($ErrorHandling) {
      0 { throw $err }
      1 { Write-Warning -Message "$err" }
      2 { Write-Verbose "Setting '$Setting.Name' could not be set, but ErrorHandling was set to 2 so error is quietly ignored" }
    }
    return $readOnly,$notFound,$alreadySet,$SingleSettingFailCounter.Value
  }

  if ($s) {
    switch ($s.CimClass.CimClassName) {
      "HPBIOS_BIOSEnumeration" {
        if ($s.CurrentValue -eq $Setting.Value) {
          $alreadySet = 1
          Write-Host "Setting $($Setting.Name) is already set to $($Setting.Value)"
        }
      }
      default {
        if ($s.Value -eq $Setting.Value) {
          $alreadySet = 1
          Write-Host "Setting $($Setting.Name) is already set to $($Setting.Value)"
        }
      }
    }

    if ($alreadySet -eq $false) {
      if ($s.IsReadOnly -eq 1) { $readOnly = 1 }
      else {
        if ($ErrorHandling -ne 1) {
          Set-HPPrivateBIOSSetting -Setting $setting -password $Password -CimSession $CimSession -SkipPrecheck $true -ErrorHandling $ErrorHandling -actualSetFailCounter ([ref]$localCounter) -Verbose:$VerbosePreference
          $SingleSettingFailCounter.Value = $localCounter
        }
        else {
          try {
            Set-HPPrivateBIOSSetting -Setting $setting -password $Password -CimSession $CimSession -SkipPrecheck $true -ErrorHandling $ErrorHandling -actualSetFailCounter ([ref]$localCounter) -Verbose:$VerbosePreference
          }
          catch {
            $SingleSettingFailCounter.Value = $localCounter
            $err = $PSItem.ToString()
          }
        }
      }
    }
  }

  return $readOnly,$notFound,$alreadySet,$SingleSettingFailCounter.Value
}

function Get-HPPrivateUseAuthString {
  [CmdletBinding()]
  param(
    [string]$SettingName
  )

  if ((Get-HPPrivateIsSureAdminEnabled) -eq $true -or $SettingName -eq "Enhanced BIOS Authentication Mode") {
    return $true
  }

  return $false
}

function Set-HPPrivateBIOSSetting {
  [CmdletBinding()]
  param(
    $Setting,
    [string]$ComputerName = ".",
    [CimSession]$CimSession,
    [switch]$SkipPrecheck,
    [AllowEmptyString()]
    [string]$Password,
    $ErrorHandling,
    [Parameter(Mandatory = $false)]
    [ref]$actualSetFailCounter
  )

  $localCounterForSet = 0

  if ($CimSession -eq $null) {
    $CimSession = newCimSession -Target $ComputerName
  }

  $Name = $Setting.Name
  $Value = $Setting.Value
  if ($Setting.AuthString -and (Get-HPPrivateUseAuthString -SettingName $Name) -eq $true) {
    $authorization = $Setting.AuthString
    Write-Verbose "Using authorization string"
  }
  else {
    $authorization = "<utf-16/>" + $Password
    Write-Verbose "Using BIOS Setup password"
  }

  if ($SkipPrecheck.IsPresent) {
    Write-Verbose "Skipping pre-check"

    if ($Name -eq "Setup Password" -or $Name -eq "Power-On Password") {
      $type = 'HPBIOS_BIOSPassword'
    }
    else {
      $type = 'HPBIOS_Setting'
    }
  }
  else {
    $obj = Get-HPBIOSSetting -Name $name -CimSession $CimSession -ErrorAction stop
    $type = $obj.CimClass.CimClassName
  }

  $c = getBiosSettingInterface -CimSession $CimSession
  switch ($type) {
    { $_ -eq 'HPBIOS_BIOSPassword' } {
      Write-Verbose "Setting Password setting '$Name' on '$ComputerName'"
      $Arguments = @{
        Name = $Name
        Value = "<utf-16/>" + [string]$Value
        Password = $authorization
      }
      $r = Invoke-CimMethod -InputObject $c -MethodName SetBiosSetting -Arguments $Arguments
    }

    default {
      Write-Verbose "Setting HP BIOS Setting '$Name' to value '$Value' on '$ComputerName'"
      $Arguments = @{
        Name = $Name
        Value = [string]$Value
        Password = $authorization;
      }
      $r = Invoke-CimMethod -InputObject $c -MethodName SetBiosSetting -Arguments $Arguments
    }
  }

  if ($r.Return -eq 0) {
    $message = "HP BIOS Setting $Name successfully set"
    if ($Name -ne "Setup Password" -and $Name -ne "Power-On Password") {
      $message += " to $Value"
    }
    Write-Host -ForegroundColor Green $message
  }
  if ($r.Return -ne 0) {

    $localCounterForSet++

    if ($r.Return -eq 5) {
      Write-Host -ForegroundColor Magenta "Operation failed. Please make sure that you are passing a valid value."
      Write-Host -ForegroundColor Magenta "Some variable names or values may be case sensitive."
    }
    $Err = "$(biosErrorCodesToString($r.Return))"
    if ($ErrorHandling -eq 1) {
      Write-Host -ForegroundColor Red "$($setting.Name) failed to set due to $Err"
      $actualSetFailCounter.Value = $localCounterForSet
    }
    throw $Err
  }
}

function Get-HPPrivateIsSureAdminEnabled {
  [CmdletBinding()]
  param()

  [boolean]$status = $false

  if ((Get-HPPrivateIsSureAdminSupported) -eq $true) {
    try {
      $mode = (Get-HPBIOSSettingValue -Name "Enhanced BIOS Authentication Mode")
      if ($mode -eq "Enable") {
        $status = $true
      }
    }
    catch {}
  }

  return $status
}

function Get-HPPrivateIsSureAdminSupported {
  [CmdletBinding()]
  param()

  [boolean]$status = $false
  try {
    $mode = (Get-HPBIOSSettingValue -Name "Enhanced BIOS Authentication Mode")
    $status = $true
  }
  catch {}

  return $status
}

function newCimSession () {
  [CmdletBinding()]
  param
  (
    [Parameter(Position = 0)] $SkipTestConnection = $true,
    [Parameter(Position = 1)] $Protocol = 'DCOM',
    [Parameter(Position = 2)] $target = '.',
    [Parameter(Position = 3)] $SessionName = 'CMSLCimSession'
  )

  Write-Verbose "Creating new CimSession (Protocol= $Protocol, Computer=$Target)"
  $opts = New-CimSessionOption -Protocol $Protocol

  $params = @{
    Name = $SessionName
    SkipTestConnection = $SkipTestConnection
    SessionOption = $opts
  }
  if ($Target -and ($Target -ne ".")) {
    $params.Add("ComputerName",$target)
  }
  New-CimSession @params

}

function Set-HPPrivateBIOSSettingsList {
  [CmdletBinding()]
  param(
    [System.Collections.Generic.List[SureAdminSetting]]$settingsList,
    [string]$Password,
    [string]$ComputerName = ".",
    $ErrorHandling = 2,
    [CimSession]$CimSession,
    [switch]$NoSummary
  )

  $failedToSet = 0

  if (-not $CimSession) {
    $CimSession = newCimSession -Target $ComputerName
  }

  $counter = @(0,0,0,0,0)
  foreach ($setting in $SettingsList) {
    $counter[0]++
    $refParameter = 0

    $params = @{
      Setting = $setting
      ErrorHandling = $ErrorHandling
      CimSession = $CimSession
      Password = $Password
      SingleSettingFailCounter = [ref]$refParameter
    }

    $c = Invoke-HPPrivateSetSetting @params -Verbose:$VerbosePreference
    $failedToSet += $refParameter

    $counter[1] += $c[0]
    $counter[2] += $c[1]
    $counter[3] += $c[2]
  }


  if ($counter -and (-not $NoSummary.IsPresent)) {
    $summary = "Total: $($counter[0]), not found: $($counter[2]), different but read-only: $($counter[1]), already set: $($counter[3])"
    Write-Output $summary
  }

  if ($ErrorHandling -eq 1) {
    if ($failedToSet -eq 0) {
      return 0
    }
    else {
      return 13
    }
  }
}

function Set-HPPrivateBIOSSettingsListPayload {
  [CmdletBinding()]
  param(
    [Parameter(ParameterSetName = 'Payload',Position = 0,Mandatory = $true,ValueFromPipeline = $true)]
    [string]$Payload,
    $ErrorHandling = 2
  )

  [System.Collections.Generic.List[SureAdminSetting]]$settingsList = Get-HPPrivateSettingsFromPayload -Content $Payload

  $params = @{
    SettingsList = $settingsList
    ErrorHandling = $ErrorHandling
  }
  Set-HPPrivateBIOSSettingsList @params -Verbose:$VerbosePreference
}



# SIG # Begin signature block
# MIIaygYJKoZIhvcNAQcCoIIauzCCGrcCAQExDzANBglghkgBZQMEAgEFADB5Bgor
# BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG
# KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAIAVV5HEqdNbsK
# y2TALaGrfLBvnJSnig7L+i8A6/8cm6CCCm8wggUwMIIEGKADAgECAhAECRgbX9W7
# ZnVTQ7VvlVAIMA0GCSqGSIb3DQEBCwUAMGUxCzAJBgNVBAYTAlVTMRUwEwYDVQQK
# EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xJDAiBgNV
# BAMTG0RpZ2lDZXJ0IEFzc3VyZWQgSUQgUm9vdCBDQTAeFw0xMzEwMjIxMjAwMDBa
# Fw0yODEwMjIxMjAwMDBaMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2Vy
# dCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lD
# ZXJ0IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwggEiMA0GCSqGSIb3
# DQEBAQUAA4IBDwAwggEKAoIBAQD407Mcfw4Rr2d3B9MLMUkZz9D7RZmxOttE9X/l
# qJ3bMtdx6nadBS63j/qSQ8Cl+YnUNxnXtqrwnIal2CWsDnkoOn7p0WfTxvspJ8fT
# eyOU5JEjlpB3gvmhhCNmElQzUHSxKCa7JGnCwlLyFGeKiUXULaGj6YgsIJWuHEqH
# CN8M9eJNYBi+qsSyrnAxZjNxPqxwoqvOf+l8y5Kh5TsxHM/q8grkV7tKtel05iv+
# bMt+dDk2DZDv5LVOpKnqagqrhPOsZ061xPeM0SAlI+sIZD5SlsHyDxL0xY4PwaLo
# LFH3c7y9hbFig3NBggfkOItqcyDQD2RzPJ6fpjOp/RnfJZPRAgMBAAGjggHNMIIB
# yTASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAK
# BggrBgEFBQcDAzB5BggrBgEFBQcBAQRtMGswJAYIKwYBBQUHMAGGGGh0dHA6Ly9v
# Y3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcwAoY3aHR0cDovL2NhY2VydHMuZGln
# aWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNydDCBgQYDVR0fBHow
# eDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJl
# ZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDovL2NybDMuZGlnaWNlcnQuY29tL0Rp
# Z2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBPBgNVHSAESDBGMDgGCmCGSAGG/WwA
# AgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93d3cuZGlnaWNlcnQuY29tL0NQUzAK
# BghghkgBhv1sAzAdBgNVHQ4EFgQUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHwYDVR0j
# BBgwFoAUReuir/SSy4IxLVGLp6chnfNtyA8wDQYJKoZIhvcNAQELBQADggEBAD7s
# DVoks/Mi0RXILHwlKXaoHV0cLToaxO8wYdd+C2D9wz0PxK+L/e8q3yBVN7Dh9tGS
# dQ9RtG6ljlriXiSBThCk7j9xjmMOE0ut119EefM2FAaK95xGTlz/kLEbBw6RFfu6
# r7VRwo0kriTGxycqoSkoGjpxKAI8LpGjwCUR4pwUR6F6aGivm6dcIFzZcbEMj7uo
# +MUSaJ/PQMtARKUT8OZkDCUIQjKyNookAv4vcn4c10lFluhZHen6dGRrsutmQ9qz
# sIzV6Q3d9gEgzpkxYz0IGhizgZtPxpMQBvwHgfqL2vmCSfdibqFT+hKUGIUukpHq
# aGxEMrJmoecYpJpkUe8wggU3MIIEH6ADAgECAhAFUi3UAAgCGeslOwtVg52XMA0G
# CSqGSIb3DQEBCwUAMHIxCzAJBgNVBAYTAlVTMRUwEwYDVQQKEwxEaWdpQ2VydCBJ
# bmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xMTAvBgNVBAMTKERpZ2lDZXJ0
# IFNIQTIgQXNzdXJlZCBJRCBDb2RlIFNpZ25pbmcgQ0EwHhcNMjEwMzIyMDAwMDAw
# WhcNMjIwMzMwMjM1OTU5WjB1MQswCQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZv
# cm5pYTESMBAGA1UEBxMJUGFsbyBBbHRvMRAwDgYDVQQKEwdIUCBJbmMuMRkwFwYD
# VQQLExBIUCBDeWJlcnNlY3VyaXR5MRAwDgYDVQQDEwdIUCBJbmMuMIIBIjANBgkq
# hkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtJ+rYUkseHcrB2M/GyomCEyKn9tCyfb+
# pByq/Jyf5kd3BGh+/ULRY7eWmR2cjXHa3qBAEHQQ1R7sX85kZ5sl2ukINGZv5jEM
# 04ERNfPoO9+pDndLWnaGYxxZP9Y+Icla09VqE/jfunhpLYMgb2CuTJkY2tT2isWM
# EMrKtKPKR5v6sfhsW6WOTtZZK+7dQ9aVrDqaIu+wQm/v4hjBYtqgrXT4cNZSPfcj
# 8W/d7lFgF/UvUnZaLU5Z/+lYbPf+449tx+raR6GD1WJBAzHcOpV6tDOI5tQcwHTo
# jJklvqBkPbL+XuS04IUK/Zqgh32YZvDnDohg0AEGilrKNiMes5wuAQIDAQABo4IB
# xDCCAcAwHwYDVR0jBBgwFoAUWsS5eyoKo6XqcQPAYPkt9mV1DlgwHQYDVR0OBBYE
# FD4tECf7wE2l8kA6HTvOgkbo33MvMA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAK
# BggrBgEFBQcDAzB3BgNVHR8EcDBuMDWgM6Axhi9odHRwOi8vY3JsMy5kaWdpY2Vy
# dC5jb20vc2hhMi1hc3N1cmVkLWNzLWcxLmNybDA1oDOgMYYvaHR0cDovL2NybDQu
# ZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC1jcy1nMS5jcmwwSwYDVR0gBEQwQjA2
# BglghkgBhv1sAwEwKTAnBggrBgEFBQcCARYbaHR0cDovL3d3dy5kaWdpY2VydC5j
# b20vQ1BTMAgGBmeBDAEEATCBhAYIKwYBBQUHAQEEeDB2MCQGCCsGAQUFBzABhhho
# dHRwOi8vb2NzcC5kaWdpY2VydC5jb20wTgYIKwYBBQUHMAKGQmh0dHA6Ly9jYWNl
# cnRzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydFNIQTJBc3N1cmVkSURDb2RlU2lnbmlu
# Z0NBLmNydDAMBgNVHRMBAf8EAjAAMA0GCSqGSIb3DQEBCwUAA4IBAQBZca1CZfgn
# DucOwEDZk0RXqb8ECXukFiih/rPQ+T5Xvl3bZppGgPnyMyQXXC0fb94p1socJzJZ
# fn7rEQ4tHxL1vpBvCepB3Jq+i3A8nnJFHSjY7aujglIphfGND97U8OUJKt2jwnni
# EgsWZnFHRI9alEvfGEFyFrAuSo+uBz5oyZeOAF0lRqaRht6MtGTma4AEgq6Mk/iP
# LYIIZ5hXmsGYWtIPyM8Yjf//kLNPRn2WeUFROlboU6EH4ZC0rLTMbSK5DV+xL/e8
# cRfWL76gd/qj7OzyJR7EsRPg92RQUC4RJhCrQqFFnmI/K84lPyHRgoctAMb8ie/4
# X6KaoyX0Z93PMYIPsTCCD60CAQEwgYYwcjELMAkGA1UEBhMCVVMxFTATBgNVBAoT
# DERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UE
# AxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIENvZGUgU2lnbmluZyBDQQIQBVIt
# 1AAIAhnrJTsLVYOdlzANBglghkgBZQMEAgEFAKB8MBAGCisGAQQBgjcCAQwxAjAA
# MBkGCSqGSIb3DQEJAzEMBgorBgEEAYI3AgEEMBwGCisGAQQBgjcCAQsxDjAMBgor
# BgEEAYI3AgEVMC8GCSqGSIb3DQEJBDEiBCDemA8mRrkL9MWQcjqXBE46mW6zrXYD
# KUZz0NFgY3wldzANBgkqhkiG9w0BAQEFAASCAQAUallJGocQSY+I2mlo+NmDMq5d
# U8OdnB4IAlqYoZnfGcrn2E+/HYB28bOX4M54259i+kMKr95WNotxhYDmYsPdjwOi
# fJo6/Prrgtapw/xsXSbFP168WvCsJKjMe6NFEwwh8s7i3VCAyD4zmKQNLFX2LY+w
# jomqduh2LCR+cTG29dMU9ISvN4Or+yufD2P0H/tMYA1YDIiFHrECpGia4/rfevIN
# 9lExKYCEmc4vc5WnUXWRZqQ9xeWPMDUXqsvvIerjWmofAfflXHE1UQbJcuxarqiM
# VV88BFRd19Uqmnh+fNzhZQgyhjiPYC4XiJrFg3rgc/Z5pF6IbMVnOeScfClzoYIN
# fTCCDXkGCisGAQQBgjcDAwExgg1pMIINZQYJKoZIhvcNAQcCoIINVjCCDVICAQMx
# DzANBglghkgBZQMEAgEFADB3BgsqhkiG9w0BCRABBKBoBGYwZAIBAQYJYIZIAYb9
# bAcBMDEwDQYJYIZIAWUDBAIBBQAEIN/hARZkPOm7agSR2u+hxCv5cSCirGwBepPZ
# S/mJaNPjAhBAbdondCS+SR4bCBevwgPXGA8yMDIxMTEyMjE5MTkwM1qgggo3MIIE
# /jCCA+agAwIBAgIQDUJK4L46iP9gQCHOFADw3TANBgkqhkiG9w0BAQsFADByMQsw
# CQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cu
# ZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQg
# VGltZXN0YW1waW5nIENBMB4XDTIxMDEwMTAwMDAwMFoXDTMxMDEwNjAwMDAwMFow
# SDELMAkGA1UEBhMCVVMxFzAVBgNVBAoTDkRpZ2lDZXJ0LCBJbmMuMSAwHgYDVQQD
# ExdEaWdpQ2VydCBUaW1lc3RhbXAgMjAyMTCCASIwDQYJKoZIhvcNAQEBBQADggEP
# ADCCAQoCggEBAMLmYYRnxYr1DQikRcpja1HXOhFCvQp1dU2UtAxQtSYQ/h3Ib5Fr
# DJbnGlxI70Tlv5thzRWRYlq4/2cLnGP9NmqB+in43Stwhd4CGPN4bbx9+cdtCT2+
# anaH6Yq9+IRdHnbJ5MZ2djpT0dHTWjaPxqPhLxs6t2HWc+xObTOKfF1FLUuxUOZB
# OjdWhtyTI433UCXoZObd048vV7WHIOsOjizVI9r0TXhG4wODMSlKXAwxikqMiMX3
# MFr5FK8VX2xDSQn9JiNT9o1j6BqrW7EdMMKbaYK02/xWVLwfoYervnpbCiAvSwnJ
# laeNsvrWY4tOpXIc7p96AXP4Gdb+DUmEvQECAwEAAaOCAbgwggG0MA4GA1UdDwEB
# /wQEAwIHgDAMBgNVHRMBAf8EAjAAMBYGA1UdJQEB/wQMMAoGCCsGAQUFBwMIMEEG
# A1UdIAQ6MDgwNgYJYIZIAYb9bAcBMCkwJwYIKwYBBQUHAgEWG2h0dHA6Ly93d3cu
# ZGlnaWNlcnQuY29tL0NQUzAfBgNVHSMEGDAWgBT0tuEgHf4prtLkYaWyoiWyyBc1
# bjAdBgNVHQ4EFgQUNkSGjqS6sGa+vCgtHUQ23eNqerwwcQYDVR0fBGowaDAyoDCg
# LoYsaHR0cDovL2NybDMuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmww
# MqAwoC6GLGh0dHA6Ly9jcmw0LmRpZ2ljZXJ0LmNvbS9zaGEyLWFzc3VyZWQtdHMu
# Y3JsMIGFBggrBgEFBQcBAQR5MHcwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRp
# Z2ljZXJ0LmNvbTBPBggrBgEFBQcwAoZDaHR0cDovL2NhY2VydHMuZGlnaWNlcnQu
# Y29tL0RpZ2lDZXJ0U0hBMkFzc3VyZWRJRFRpbWVzdGFtcGluZ0NBLmNydDANBgkq
# hkiG9w0BAQsFAAOCAQEASBzctemaI7znGucgDo5nRv1CclF0CiNHo6uS0iXEcFm+
# FKDlJ4GlTRQVGQd58NEEw4bZO73+RAJmTe1ppA/2uHDPYuj1UUp4eTZ6J7fz51Kf
# k6ftQ55757TdQSKJ+4eiRgNO/PT+t2R3Y18jUmmDgvoaU+2QzI2hF3MN9PNlOXBL
# 85zWenvaDLw9MtAby/Vh/HUIAHa8gQ74wOFcz8QRcucbZEnYIpp1FUL1LTI4gdr0
# YKK6tFL7XOBhJCVPst/JKahzQ1HavWPWH1ub9y4bTxMd90oNcX6Xt/Q/hOvB46NJ
# ofrOp79Wz7pZdmGJX36ntI5nePk2mOHLKNpbh6aKLzCCBTEwggQZoAMCAQICEAqh
# JdbWMht+QeQF2jaXwhUwDQYJKoZIhvcNAQELBQAwZTELMAkGA1UEBhMCVVMxFTAT
# BgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTEk
# MCIGA1UEAxMbRGlnaUNlcnQgQXNzdXJlZCBJRCBSb290IENBMB4XDTE2MDEwNzEy
# MDAwMFoXDTMxMDEwNzEyMDAwMFowcjELMAkGA1UEBhMCVVMxFTATBgNVBAoTDERp
# Z2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0LmNvbTExMC8GA1UEAxMo
# RGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFtcGluZyBDQTCCASIwDQYJ
# KoZIhvcNAQEBBQADggEPADCCAQoCggEBAL3QMu5LzY9/3am6gpnFOVQoV7YjSsQO
# B0UzURB90Pl9TWh+57ag9I2ziOSXv2MhkJi/E7xX08PhfgjWahQAOPcuHjvuzKb2
# Mln+X2U/4Jvr40ZHBhpVfgsnfsCi9aDg3iI/Dv9+lfvzo7oiPhisEeTwmQNtO4V8
# CdPuXciaC1TjqAlxa+DPIhAPdc9xck4Krd9AOly3UeGheRTGTSQjMF287Dxgaqwv
# B8z98OpH2YhQXv1mblZhJymJhFHmgudGUP2UKiyn5HU+upgPhH+fMRTWrdXyZMt7
# HgXQhBlyF/EXBu89zdZN7wZC/aJTKk+FHcQdPK/P2qwQ9d2srOlW/5MCAwEAAaOC
# Ac4wggHKMB0GA1UdDgQWBBT0tuEgHf4prtLkYaWyoiWyyBc1bjAfBgNVHSMEGDAW
# gBRF66Kv9JLLgjEtUYunpyGd823IDzASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1Ud
# DwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDCDB5BggrBgEFBQcBAQRtMGsw
# JAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBDBggrBgEFBQcw
# AoY3aHR0cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElE
# Um9vdENBLmNydDCBgQYDVR0fBHoweDA6oDigNoY0aHR0cDovL2NybDQuZGlnaWNl
# cnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDA6oDigNoY0aHR0cDov
# L2NybDMuZGlnaWNlcnQuY29tL0RpZ2lDZXJ0QXNzdXJlZElEUm9vdENBLmNybDBQ
# BgNVHSAESTBHMDgGCmCGSAGG/WwAAgQwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly93
# d3cuZGlnaWNlcnQuY29tL0NQUzALBglghkgBhv1sBwEwDQYJKoZIhvcNAQELBQAD
# ggEBAHGVEulRh1Zpze/d2nyqY3qzeM8GN0CE70uEv8rPAwL9xafDDiBCLK938ysf
# DCFaKrcFNB1qrpn4J6JmvwmqYN92pDqTD/iy0dh8GWLoXoIlHsS6HHssIeLWWywU
# NUMEaLLbdQLgcseY1jxk5R9IEBhfiThhTWJGJIdjjJFSLK8pieV4H9YLFKWA1xJH
# cLN11ZOFk362kmf7U2GJqPVrlsD0WGkNfMgBsbkodbeZY4UijGHKeZR+WfyMD+Nv
# tQEmtmyl7odRIeRYYJu6DC0rbaLEfrvEJStHAgh8Sa4TtuF8QkIoxhhWz0E0tmZd
# tnR79VYzIi8iNrJLokqV2PWmjlIxggKGMIICggIBATCBhjByMQswCQYDVQQGEwJV
# UzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQu
# Y29tMTEwLwYDVQQDEyhEaWdpQ2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1w
# aW5nIENBAhANQkrgvjqI/2BAIc4UAPDdMA0GCWCGSAFlAwQCAQUAoIHRMBoGCSqG
# SIb3DQEJAzENBgsqhkiG9w0BCRABBDAcBgkqhkiG9w0BCQUxDxcNMjExMTIyMTkx
# OTAzWjArBgsqhkiG9w0BCRACDDEcMBowGDAWBBTh14Ko4ZG+72vKFpG1qrSUpiSb
# 8zAvBgkqhkiG9w0BCQQxIgQglkGsx+epkhgXUqjIbIB/0XnUfWzku164Xnc6xODL
# mbkwNwYLKoZIhvcNAQkQAi8xKDAmMCQwIgQgsxCQBrwK2YMHkVcp4EQDQVyD4ykr
# YU8mlkyNNXHs9akwDQYJKoZIhvcNAQEBBQAEggEAqw7AicFYxDPTmR8wjPL70Gem
# OP60vDdtNeuJOIvlhwVl4aAeP8d/ufvtNXi1Eqd5ybCePppRhuz7gL92N1V/YRtZ
# vqbIgEcj3J7macr5qtT4raVTxr9YhABUbVscapyZZ8rYYRFf+28RaoWF1lae3uis
# iUtML5enUi2LCM/f6szz7j7Dl10EfJ/d04s1Aw09/m+od6hq68o2GQ6nqnFUlYnL
# 7mP2+krxEexkfRlM2siAQ6x7bvEuOAEQUAMeRsJX++7AzdwqpjI4z7CTFy6NmsxS
# E7AmHaRuKti5GmmwMmUL6LKK9IFjZQP3Y3emlz6QT+sSRUMI+aUe1iEdcxO5hg==
# SIG # End signature block