Private/Storage/ActiveDirectory/Unprotect-PukAdAttributeValue.ps1

function Unprotect-PukAdAttributeValue {
    [CmdletBinding()]
    [OutputType([psobject])]
    param(
        [Parameter(Mandatory)]
        [string]$Value,

        [Parameter(Mandatory)]
        [System.Security.Cryptography.X509Certificates.X509Certificate2]$Certificate
    )

    function Test-PukConstantTimeEqual {
        param([byte[]]$A, [byte[]]$B)
        if ($A.Length -ne $B.Length) { return $false }
        $diff = 0
        for ($i = 0; $i -lt $A.Length; $i++) {
            $diff = $diff -bor ($A[$i] -bxor $B[$i])
        }
        return $diff -eq 0
    }

    $parts = $Value -split '\.'
    if ($parts.Count -ne 4) {
        throw "PWSHPUKMGT AD attribute value is not in the expected 'wrappedKey.iv.ciphertext.mac' format."
    }

    $wrappedKey = [Convert]::FromBase64String($parts[0])
    $iv = [Convert]::FromBase64String($parts[1])
    $cipherBytes = [Convert]::FromBase64String($parts[2])
    $mac = [Convert]::FromBase64String($parts[3])

    $combinedKeyMaterial = Unprotect-PukAesKeyWithCertificate -WrappedKey $wrappedKey -Certificate $Certificate
    $key = $combinedKeyMaterial[0..31]
    $hmacKey = $combinedKeyMaterial[32..63]

    $hmac = [System.Security.Cryptography.HMACSHA256]::new($hmacKey)
    try {
        $expectedMac = $hmac.ComputeHash($iv + $cipherBytes)
    }
    finally {
        $hmac.Dispose()
    }

    if (-not (Test-PukConstantTimeEqual -A $mac -B $expectedMac)) {
        throw 'PWSHPUKMGT AD attribute value failed integrity verification (HMAC mismatch) -- it may have been tampered with or corrupted.'
    }

    $aes = [System.Security.Cryptography.Aes]::Create()
    try {
        $aes.Key = $key
        $aes.IV = $iv
        $decryptor = $aes.CreateDecryptor()
        try {
            $plaintextBytes = $decryptor.TransformFinalBlock($cipherBytes, 0, $cipherBytes.Length)
        }
        finally {
            $decryptor.Dispose()
        }
    }
    finally {
        $aes.Dispose()
    }

    return [System.Text.Encoding]::UTF8.GetString($plaintextBytes) | ConvertFrom-Json
}