packages feed

dpapi-0.1.0.0: test/Test/System/Win32/Dpapi.hs

{-
 - This Source Code Form is subject to the terms of the Mozilla Public
 - License, v. 2.0. If a copy of the MPL was not distributed with this
 - file, You can obtain one at https://mozilla.org/MPL/2.0/.
 -}
{-# LANGUAGE OverloadedStrings #-}

module Test.System.Win32.Dpapi (dpapiTests) where

import Data.Base64.Types (extractBase64)
import Data.ByteString (toStrict)
import Data.ByteString.Base64 (decodeBase64Untyped, encodeBase64)
import Data.ByteString.Lazy (ByteString, dropWhileEnd)
import Data.Char (chr, isSpace)
import Data.Text (unpack)
import System.Process.Typed
  ( ExitCode (ExitSuccess),
    nullStream,
    proc,
    readProcessStdout,
    setStderr,
  )
import System.Win32.Dpapi
  ( DataProtectionScope (CurrentUser, LocalMachine),
    cryptProtectData,
    cryptUnprotectData,
    cryptUnprotectDataCheck,
  )
import Test.HUnit

dpapiTests :: Test
dpapiTests = "System.Win32.Dpapi" ~: [testProtectCurrentUser, testProtectLocalMachine, testUnprotectCurrentUser, testUnprotectLocalMachine, testUnprotectLocalMachineMaybe, testUnprotectError]

testProtectCurrentUser :: Test
testProtectCurrentUser =
  "testProtectCurrentUser" ~: do
    protected <- cryptProtectData "fizz" Nothing CurrentUser
    (exitCode, out) <- readProcessStdout $ setStderr nullStream $ proc "powershell" ["-c", "Add-Type -AssemblyName System.Security; [System.Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect([System.Convert]::FromBase64String(\"" <> unpack (extractBase64 $ encodeBase64 protected) <> "\"), $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser))"]
    assertEqual "powershell exit code" ExitSuccess exitCode
    let out' = trimEndBs out
    out' @?= "fizz"

testProtectLocalMachine :: Test
testProtectLocalMachine =
  "testProtectLocalMachine" ~: do
    protected <- cryptProtectData "buzz" Nothing LocalMachine
    (exitCode, out) <- readProcessStdout $ setStderr nullStream $ proc "powershell" ["-c", "Add-Type -AssemblyName System.Security; [System.Text.Encoding]::UTF8.GetString([System.Security.Cryptography.ProtectedData]::Unprotect([System.Convert]::FromBase64String(\"" <> unpack (extractBase64 $ encodeBase64 protected) <> "\"), $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine))"]
    assertEqual "powershell exit code" ExitSuccess exitCode
    let out' = trimEndBs out
    out' @?= "buzz"

testUnprotectCurrentUser :: Test
testUnprotectCurrentUser =
  "testUnprotectCurrentUser" ~: do
    (exitCode, out) <- readProcessStdout $ setStderr nullStream $ proc "powershell" ["-c", "Add-Type -AssemblyName System.Security; [System.Convert]::ToBase64String([System.Security.Cryptography.ProtectedData]::Protect([System.Text.Encoding]::UTF8.GetBytes(\"foo\"), $null, [System.Security.Cryptography.DataProtectionScope]::CurrentUser))"]
    assertEqual "powershell exit code" ExitSuccess exitCode
    let out' = toStrict $ trimEndBs out -- Powershell outputs \r\n even when it's not wanted
    case decodeBase64Untyped out' of
      l@(Left _) ->
        Right out' @?= l
      Right bs -> do
        result <- cryptUnprotectData bs Nothing CurrentUser
        result @?= "foo"

testUnprotectLocalMachine :: Test
testUnprotectLocalMachine =
  "testUnprotectLocalMachine" ~: do
    (exitCode, out) <- readProcessStdout $ setStderr nullStream $ proc "powershell" ["-c", "Add-Type -AssemblyName System.Security; [System.Convert]::ToBase64String([System.Security.Cryptography.ProtectedData]::Protect([System.Text.Encoding]::UTF8.GetBytes(\"bar\"), $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine))"]
    assertEqual "powershell exit code" ExitSuccess exitCode
    let out' = toStrict $ trimEndBs out -- Powershell outputs \r\n even when it's not wanted
    case decodeBase64Untyped out' of
      l@(Left _) ->
        Right out' @?= l
      Right bs -> do
        result <- cryptUnprotectData bs Nothing LocalMachine
        result @?= "bar"

testUnprotectLocalMachineMaybe :: Test
testUnprotectLocalMachineMaybe =
  "testUnprotectLocalMachine" ~: do
    (exitCode, out) <- readProcessStdout $ setStderr nullStream $ proc "powershell" ["-c", "Add-Type -AssemblyName System.Security; [System.Convert]::ToBase64String([System.Security.Cryptography.ProtectedData]::Protect([System.Text.Encoding]::UTF8.GetBytes(\"bar\"), $null, [System.Security.Cryptography.DataProtectionScope]::LocalMachine))"]
    assertEqual "powershell exit code" ExitSuccess exitCode
    let out' = toStrict $ trimEndBs out -- Powershell outputs \r\n even when it's not wanted
    case decodeBase64Untyped out' of
      l@(Left _) ->
        Right out' @?= l
      Right bs -> do
        result <- cryptUnprotectDataCheck bs Nothing LocalMachine
        result @?= Just "bar"

testUnprotectError :: Test
testUnprotectError =
  "testUnprotectError" ~: do
    result <- cryptUnprotectDataCheck ciphertext Nothing CurrentUser
    result @?= Nothing
  where
    ciphertext = case decodeBase64Untyped "AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA8Zcdtib+QkybE9Tbcq0/EwQAAAACAAAAAAAQZgAAAAEAACAAAAB5qoj4OynizB5iEVZvAtyEZlVVn2RjP4SdHz5dOnIjYAAAAAAOgAAAAAIAACAAAAAGKRerRiwIK/xPPH8NtVhAxU9dg2qfcNsxLg+wtYml8RAAAADinEDleLmnP+qGkSpqgeZ9QAAAAAmaXs8XjSezA4qSWIFL3HD2bqd757HNPfXfuBz4eVYtfRawfRpFXFTmfIh+2nOzf1N2IYH44fBK7VrW+ITOZ74=" of
      (Right c) -> c
      (Left _) -> error "Programming error! Base64 text wasn't!"

trimEndBs :: ByteString -> ByteString
trimEndBs = dropWhileEnd (isSpace . chr . fromIntegral)