{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
module Crypto.Store.PKCS5
( Password
, EncryptedContent
, PKCS5(..)
, encrypt
, decrypt
, EncryptionScheme(..)
, PBEParameter(..)
, PBES2Parameter(..)
, KeyDerivationFunc(..)
, PBKDF2_PRF(..)
, Salt
, generateSalt
, ContentEncryptionParams
, ContentEncryptionAlg(..)
, ContentEncryptionCipher(..)
, generateEncryptionParams
, getContentEncryptionAlg
, pbEncrypt
, pbDecrypt
) where
import Data.ASN1.Types
import Data.ByteArray (ByteArrayAccess)
import Data.ByteString (ByteString)
import Data.Maybe (fromMaybe)
import Crypto.Store.ASN1.Parse
import Crypto.Store.ASN1.Generate
import Crypto.Store.CMS.Algorithms
import Crypto.Store.CMS.Encrypted
import Crypto.Store.CMS.Enveloped
import Crypto.Store.CMS.Util
import Crypto.Store.Error
import Crypto.Store.PKCS5.PBES1
data EncryptionSchemeType = Type_PBES2
| Type_PBE_MD5_DES_CBC
| Type_PBE_SHA1_DES_CBC
| Type_PBE_SHA1_RC4_128
| Type_PBE_SHA1_RC4_40
| Type_PBE_SHA1_DES_EDE3_CBC
| Type_PBE_SHA1_DES_EDE2_CBC
| Type_PBE_SHA1_RC2_128
| Type_PBE_SHA1_RC2_40
instance Enumerable EncryptionSchemeType where
values = [ Type_PBES2
, Type_PBE_MD5_DES_CBC
, Type_PBE_SHA1_DES_CBC
, Type_PBE_SHA1_RC4_128
, Type_PBE_SHA1_RC4_40
, Type_PBE_SHA1_DES_EDE3_CBC
, Type_PBE_SHA1_DES_EDE2_CBC
, Type_PBE_SHA1_RC2_128
, Type_PBE_SHA1_RC2_40
]
instance OIDable EncryptionSchemeType where
getObjectID Type_PBES2 = [1,2,840,113549,1,5,13]
getObjectID Type_PBE_MD5_DES_CBC = [1,2,840,113549,1,5,3]
getObjectID Type_PBE_SHA1_DES_CBC = [1,2,840,113549,1,5,10]
getObjectID Type_PBE_SHA1_RC4_128 = [1,2,840,113549,1,12,1,1]
getObjectID Type_PBE_SHA1_RC4_40 = [1,2,840,113549,1,12,1,2]
getObjectID Type_PBE_SHA1_DES_EDE3_CBC = [1,2,840,113549,1,12,1,3]
getObjectID Type_PBE_SHA1_DES_EDE2_CBC = [1,2,840,113549,1,12,1,4]
getObjectID Type_PBE_SHA1_RC2_128 = [1,2,840,113549,1,12,1,5]
getObjectID Type_PBE_SHA1_RC2_40 = [1,2,840,113549,1,12,1,6]
instance OIDNameable EncryptionSchemeType where
fromObjectID oid = unOIDNW <$> fromObjectID oid
data EncryptionScheme = PBES2 PBES2Parameter
| PBE_MD5_DES_CBC PBEParameter
| PBE_SHA1_DES_CBC PBEParameter
| PBE_SHA1_RC4_128 PBEParameter
| PBE_SHA1_RC4_40 PBEParameter
| PBE_SHA1_DES_EDE3_CBC PBEParameter
| PBE_SHA1_DES_EDE2_CBC PBEParameter
| PBE_SHA1_RC2_128 PBEParameter
| PBE_SHA1_RC2_40 PBEParameter
deriving (Show,Eq)
data PBES2Parameter = PBES2Parameter
{ pbes2KDF :: KeyDerivationFunc
, pbes2EScheme :: ContentEncryptionParams
}
deriving (Show,Eq)
instance ASN1Elem e => ProduceASN1Object e PBES2Parameter where
asn1s PBES2Parameter{..} =
let kdFunc = algorithmASN1S Sequence pbes2KDF
eScheme = asn1s pbes2EScheme
in asn1Container Sequence (kdFunc . eScheme)
instance Monoid e => ParseASN1Object e PBES2Parameter where
parse = onNextContainer Sequence $ do
kdFunc <- parseAlgorithm Sequence
eScheme <- parse
case kdfKeyLength kdFunc of
Nothing -> return ()
Just sz
| validateKeySize eScheme sz -> return ()
| otherwise -> throwParseError "PBES2Parameter: parsed key length incompatible with encryption scheme"
return PBES2Parameter { pbes2KDF = kdFunc, pbes2EScheme = eScheme }
instance AlgorithmId EncryptionScheme where
type AlgorithmType EncryptionScheme = EncryptionSchemeType
algorithmName _ = "encryption scheme"
algorithmType (PBES2 _) = Type_PBES2
algorithmType (PBE_MD5_DES_CBC _) = Type_PBE_MD5_DES_CBC
algorithmType (PBE_SHA1_DES_CBC _) = Type_PBE_SHA1_DES_CBC
algorithmType (PBE_SHA1_RC4_128 _) = Type_PBE_SHA1_RC4_128
algorithmType (PBE_SHA1_RC4_40 _) = Type_PBE_SHA1_RC4_40
algorithmType (PBE_SHA1_DES_EDE3_CBC _) = Type_PBE_SHA1_DES_EDE3_CBC
algorithmType (PBE_SHA1_DES_EDE2_CBC _) = Type_PBE_SHA1_DES_EDE2_CBC
algorithmType (PBE_SHA1_RC2_128 _) = Type_PBE_SHA1_RC2_128
algorithmType (PBE_SHA1_RC2_40 _) = Type_PBE_SHA1_RC2_40
parameterASN1S (PBES2 p) = asn1s p
parameterASN1S (PBE_MD5_DES_CBC p) = asn1s p
parameterASN1S (PBE_SHA1_DES_CBC p) = asn1s p
parameterASN1S (PBE_SHA1_RC4_128 p) = asn1s p
parameterASN1S (PBE_SHA1_RC4_40 p) = asn1s p
parameterASN1S (PBE_SHA1_DES_EDE3_CBC p) = asn1s p
parameterASN1S (PBE_SHA1_DES_EDE2_CBC p) = asn1s p
parameterASN1S (PBE_SHA1_RC2_128 p) = asn1s p
parameterASN1S (PBE_SHA1_RC2_40 p) = asn1s p
parseParameter Type_PBES2 = PBES2 <$> parse
parseParameter Type_PBE_MD5_DES_CBC = PBE_MD5_DES_CBC <$> parse
parseParameter Type_PBE_SHA1_DES_CBC = PBE_SHA1_DES_CBC <$> parse
parseParameter Type_PBE_SHA1_RC4_128 = PBE_SHA1_RC4_128 <$> parse
parseParameter Type_PBE_SHA1_RC4_40 = PBE_SHA1_RC4_40 <$> parse
parseParameter Type_PBE_SHA1_DES_EDE3_CBC = PBE_SHA1_DES_EDE3_CBC <$> parse
parseParameter Type_PBE_SHA1_DES_EDE2_CBC = PBE_SHA1_DES_EDE2_CBC <$> parse
parseParameter Type_PBE_SHA1_RC2_128 = PBE_SHA1_RC2_128 <$> parse
parseParameter Type_PBE_SHA1_RC2_40 = PBE_SHA1_RC2_40 <$> parse
instance ASN1Elem e => ProduceASN1Object e EncryptionScheme where
asn1s = algorithmASN1S Sequence
instance Monoid e => ParseASN1Object e EncryptionScheme where
parse = parseAlgorithm Sequence
data PKCS5 = PKCS5
{ encryptionAlgorithm :: EncryptionScheme
, encryptedData :: EncryptedContent
}
deriving (Show,Eq)
instance ASN1Elem e => ProduceASN1Object e PKCS5 where
asn1s PKCS5{..} = asn1Container Sequence (alg . bs)
where alg = asn1s encryptionAlgorithm
bs = gOctetString encryptedData
instance Monoid e => ParseASN1Object e PKCS5 where
parse = onNextContainer Sequence $ do
alg <- parse
OctetString bs <- getNext
return PKCS5 { encryptionAlgorithm = alg, encryptedData = bs }
instance ASN1Object PKCS5 where
toASN1 = asn1s
fromASN1 = runParseASN1State parse
encrypt :: EncryptionScheme -> Password -> ByteString -> Either StoreError PKCS5
encrypt alg pwd bs = build <$> pbEncrypt alg bs pwd
where
build ed = ed `seq` PKCS5 { encryptionAlgorithm = alg, encryptedData = ed }
decrypt :: PKCS5 -> Password -> Either StoreError ByteString
decrypt obj = pbDecrypt (encryptionAlgorithm obj) (encryptedData obj)
pbEncrypt :: EncryptionScheme -> ByteString -> Password
-> Either StoreError EncryptedContent
pbEncrypt (PBES2 p) = pbes2 contentEncrypt p
pbEncrypt (PBE_MD5_DES_CBC p) = pkcs5 Left contentEncrypt MD5 DES p
pbEncrypt (PBE_SHA1_DES_CBC p) = pkcs5 Left contentEncrypt SHA1 DES p
pbEncrypt (PBE_SHA1_RC4_128 p) = pkcs12stream Left rc4Combine SHA1 16 p
pbEncrypt (PBE_SHA1_RC4_40 p) = pkcs12stream Left rc4Combine SHA1 5 p
pbEncrypt (PBE_SHA1_DES_EDE3_CBC p) = pkcs12 Left contentEncrypt SHA1 DES_EDE3 p
pbEncrypt (PBE_SHA1_DES_EDE2_CBC p) = pkcs12 Left contentEncrypt SHA1 DES_EDE2 p
pbEncrypt (PBE_SHA1_RC2_128 p) = pkcs12rc2 Left contentEncrypt SHA1 128 p
pbEncrypt (PBE_SHA1_RC2_40 p) = pkcs12rc2 Left contentEncrypt SHA1 40 p
pbDecrypt :: EncryptionScheme -> EncryptedContent -> Password -> Either StoreError ByteString
pbDecrypt (PBES2 p) = pbes2 contentDecrypt p
pbDecrypt (PBE_MD5_DES_CBC p) = pkcs5 Left contentDecrypt MD5 DES p
pbDecrypt (PBE_SHA1_DES_CBC p) = pkcs5 Left contentDecrypt SHA1 DES p
pbDecrypt (PBE_SHA1_RC4_128 p) = pkcs12stream Left rc4Combine SHA1 16 p
pbDecrypt (PBE_SHA1_RC4_40 p) = pkcs12stream Left rc4Combine SHA1 5 p
pbDecrypt (PBE_SHA1_DES_EDE3_CBC p) = pkcs12 Left contentDecrypt SHA1 DES_EDE3 p
pbDecrypt (PBE_SHA1_DES_EDE2_CBC p) = pkcs12 Left contentDecrypt SHA1 DES_EDE2 p
pbDecrypt (PBE_SHA1_RC2_128 p) = pkcs12rc2 Left contentDecrypt SHA1 128 p
pbDecrypt (PBE_SHA1_RC2_40 p) = pkcs12rc2 Left contentDecrypt SHA1 40 p
pbes2 :: ByteArrayAccess password
=> (Key -> ContentEncryptionParams -> ByteString -> result)
-> PBES2Parameter -> ByteString -> password -> result
pbes2 encdec PBES2Parameter{..} bs pwd = encdec key pbes2EScheme bs
where key = kdfDerive pbes2KDF len pwd :: Key
len = fromMaybe (getMaximumKeySize pbes2EScheme) (kdfKeyLength pbes2KDF)