cryptostore-0.3.0.0: src/Crypto/Store/PKCS5.hs
-- |
-- Module : Data.Store.PKCS5
-- License : BSD-style
-- Maintainer : Olivier Chéron <olivier.cheron@gmail.com>
-- Stability : experimental
-- Portability : unknown
--
-- Password-Based Cryptography, aka PKCS #5.
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE TypeFamilies #-}
module Crypto.Store.PKCS5
( ProtectionPassword
, emptyNotTerminated
, fromProtectionPassword
, toProtectionPassword
, EncryptedContent
-- * High-level API
, PKCS5(..)
, encrypt
, decrypt
-- * Encryption schemes
, EncryptionScheme(..)
, PBEParameter(..)
, PBES2Parameter(..)
-- * Key derivation
, KeyDerivationFunc(..)
, PBKDF2_PRF(..)
, Salt
, generateSalt
-- * Content encryption
, ContentEncryptionParams
, ContentEncryptionAlg(..)
, ContentEncryptionCipher(..)
, generateEncryptionParams
, getContentEncryptionAlg
-- * Low-level API
, pbEncrypt
, pbDecrypt
) where
import Data.ASN1.Types
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.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
-- | Password-Based Encryption Scheme (PBES).
data EncryptionScheme = PBES2 PBES2Parameter -- ^ PBES2
| PBE_MD5_DES_CBC PBEParameter -- ^ pbeWithMD5AndDES-CBC
| PBE_SHA1_DES_CBC PBEParameter -- ^ pbeWithSHA1AndDES-CBC
| PBE_SHA1_RC4_128 PBEParameter -- ^ pbeWithSHAAnd128BitRC4
| PBE_SHA1_RC4_40 PBEParameter -- ^ pbeWithSHAAnd40BitRC4
| PBE_SHA1_DES_EDE3_CBC PBEParameter -- ^ pbeWithSHAAnd3-KeyTripleDES-CBC
| PBE_SHA1_DES_EDE2_CBC PBEParameter -- ^ pbeWithSHAAnd2-KeyTripleDES-CBC
| PBE_SHA1_RC2_128 PBEParameter -- ^ pbeWithSHAAnd128BitRC2-CBC
| PBE_SHA1_RC2_40 PBEParameter -- ^ pbewithSHAAnd40BitRC2-CBC
deriving (Show,Eq)
-- | PBES2 parameters.
data PBES2Parameter = PBES2Parameter
{ pbes2KDF :: KeyDerivationFunc -- ^ Key derivation function
, pbes2EScheme :: ContentEncryptionParams -- ^ Underlying encryption scheme
}
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
-- High-level API
-- | Content encrypted with a Password-Based Encryption Scheme (PBES).
--
-- The content will usually be the binary representation of an ASN.1 object,
-- however the transformation may be applied to any bytestring.
data PKCS5 = PKCS5
{ encryptionAlgorithm :: EncryptionScheme -- ^ Scheme used to encrypt content
, encryptedData :: EncryptedContent -- ^ Encrypted content
}
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 a bytestring with the specified encryption scheme and password.
encrypt :: EncryptionScheme -> ProtectionPassword -> ByteString -> Either StoreError PKCS5
encrypt alg pwd bs = build <$> pbEncrypt alg bs pwd
where
build ed = ed `seq` PKCS5 { encryptionAlgorithm = alg, encryptedData = ed }
-- | Decrypt the PKCS #5 content with the specified password.
decrypt :: PKCS5 -> ProtectionPassword -> Either StoreError ByteString
decrypt obj = pbDecrypt (encryptionAlgorithm obj) (encryptedData obj)
-- Encryption Schemes
-- | Encrypt a bytestring with the specified encryption scheme and password.
pbEncrypt :: EncryptionScheme -> ByteString -> ProtectionPassword
-> 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
-- | Decrypt an encrypted bytestring with the specified encryption scheme and
-- password.
pbDecrypt :: EncryptionScheme -> EncryptedContent -> ProtectionPassword -> 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 :: (Key -> ContentEncryptionParams -> ByteString -> result)
-> PBES2Parameter -> ByteString -> ProtectionPassword -> result
pbes2 encdec PBES2Parameter{..} bs pwd = encdec key pbes2EScheme bs
where key = kdfDerive pbes2KDF len (fromProtectionPassword pwd) :: Key
len = fromMaybe (getMaximumKeySize pbes2EScheme) (kdfKeyLength pbes2KDF)