cryptostore-0.3.0.0: tests/PKCS12/Tests.hs
-- | PKCS #12 tests.
module PKCS12.Tests (pkcs12Tests) where
import Control.Monad (forM_)
import Data.PEM (pemContent)
import Data.String (fromString)
import Crypto.Store.PKCS12
import Crypto.Store.PKCS8
import Crypto.Store.X509 (readSignedObject)
import Test.Tasty
import Test.Tasty.HUnit
import Test.Tasty.QuickCheck
import Util
import PKCS12.Instances
import X509.Instances
testType :: TestName -> String -> TestTree
testType caseName prefix = testCaseSteps caseName $ \step -> do
let fKey = testFile (prefix ++ "-unencrypted-pkcs8.pem")
fCert = testFile (prefix ++ "-self-signed-cert.pem")
p12 = testFile (prefix ++ "-pkcs12.pem")
step "Reading PKCS #12 files"
pems <- readPEMs p12
length pems @?= length names
step "Reading private key"
[Unprotected key] <- readKeyFile fKey
step "Reading certificate"
certs <- readSignedObject fCert
forM_ (zip names pems) $ \(name, pem) -> do
let r = readP12FileFromMemory (pemContent pem)
assertRight r $ \integrity ->
assertRight (recoverAuthenticated pwd integrity) $ \(ppwd, privacy) ->
assertRight (recover ppwd $ unPKCS12 privacy) $ \scs -> do
step ("Testing " ++ name)
recover ppwd (getAllSafeKeys scs) @?= Right [key]
getAllSafeX509Certs scs @?= certs
where
pwd = fromString "dontchangeme"
nameIntegrity n = "integrity with " ++ n
namePrivacy t n = t ++ " privacy with " ++ n
integrityNames = map nameIntegrity integrityModes
privacyNames t = ("without " ++ t ++ " privacy") :
map (namePrivacy t) privacyModes
names = [ "without integrity" ] ++ integrityNames ++
privacyNames "certificate" ++ privacyNames "private-key"
integrityModes = [ "SHA-1"
, "SHA-256"
, "SHA-384"
]
privacyModes = [ "aes-128-cbc"
, "PBE-SHA1-RC2-128"
, "PBE-SHA1-RC2-40"
]
testEmptyPassword :: TestTree
testEmptyPassword = testCaseSteps "empty password" $ \step -> do
step "Reading PKCS #12 files"
pems <- readPEMs path
length pems @?= length infos
forM_ (zip infos pems) $ \((name, numKeys, numCerts), pem) -> do
let r = readP12FileFromMemory (pemContent pem)
assertRight r $ \integrity ->
assertRight (recoverAuthenticated pwd integrity) $ \(ppwd, privacy) ->
assertRight (recover ppwd $ unPKCS12 privacy) $ \scs -> do
step ("Testing " ++ name)
assertRight (recover ppwd $ getAllSafeKeys scs) $ \keys ->
length keys @?= numKeys
length (getAllSafeX509Certs scs) @?= numCerts
where
pwd = fromString ""
path = testFile "pkcs12-empty-password.pem"
infos = [ ("Windows Certificate Export Wizard", 1, 2)
, ("OpenSSL", 1, 1)
, ("GnuTLS with --empty-password", 1, 1)
, ("GnuTLS with --null-password", 1, 1)
]
propertyTests :: TestTree
propertyTests = localOption (QuickCheckMaxSize 5) $ testGroup "properties"
[ testProperty "marshalling" $ do
pE <- arbitrary
c <- arbitraryPKCS12 pE
let r = readP12FileFromMemory $ writeUnprotectedP12FileToMemory c
unused = fromString "not-used"
return $ Right (Right c) === (fmap snd . recoverAuthenticated unused <$> r)
, testProperty "marshalling with authentication" $ do
params <- arbitraryIntegrityParams
c <- arbitrary >>= arbitraryPKCS12
pI <- arbitrary
let r = readP12FileFromMemory <$> writeP12FileToMemory params pI c
p = fromProtectionPassword pI
return $ Right (Right (Right (pI, c))) === (fmap (recoverAuthenticated p) <$> r)
, localOption (QuickCheckTests 20) $ testProperty "converting credentials" $
\pChain pKey privKey ->
testCredConv privKey toCredential (fromCredential pChain pKey)
, localOption (QuickCheckTests 20) $ testProperty "converting named credentials" $
\pChain pKey privKey -> do
name <- arbitraryAlias
testCredConv privKey
(toNamedCredential name)
(fromNamedCredential name pChain pKey)
]
where
testCredConv privKey to from = do
pwd <- arbitrary
chain <- arbitrary >>= arbitraryCertificateChain
chain' <- shuffleCertificateChain chain
let cred = (chain, privKey)
r = from pwd (chain', privKey) >>= recover pwd . to
return $ Right (Just cred) === r
pkcs12Tests :: TestTree
pkcs12Tests =
testGroup "PKCS12"
[ testType "RSA" "rsa"
, testType "Ed25519" "ed25519"
, testEmptyPassword
, propertyTests
]