cryptostore-0.5.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(..)
-- * Message authentication schemes
, AuthenticationScheme(..)
, PBMAC1Parameter(..)
-- * Key derivation
, KeyDerivationFunc(..)
, PBKDF2_PRF(..)
, Salt
, generateSalt
-- * Content encryption
, ContentEncryptionParams
, ContentEncryptionAlg(..)
, ContentEncryptionCipher(..)
, ecbParams
, generateCBCParams
, generateRC2EncryptionParams
, generateCFBParams
, generateCTRParams
, getContentEncryptionAlg
-- * Low-level API
, pbEncrypt
, pbDecrypt
, pbMac
) 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.Authenticated
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
data AuthenticationSchemeType = Type_PBMAC1
instance Enumerable AuthenticationSchemeType where
values = [ Type_PBMAC1
]
instance OIDable AuthenticationSchemeType where
getObjectID Type_PBMAC1 = [1,2,840,113549,1,5,14]
instance OIDNameable AuthenticationSchemeType where
fromObjectID oid = unOIDNW <$> fromObjectID oid
-- | Password-Based Message Authentication Scheme (PBMAC).
newtype AuthenticationScheme = PBMAC1 PBMAC1Parameter -- ^ PBMAC1
deriving (Show,Eq)
instance HasStrength AuthenticationScheme where
getSecurityBits (PBMAC1 p) = getSecurityBits (pbmac1AScheme p)
-- | PBMAC1 parameters.
data PBMAC1Parameter = PBMAC1Parameter
{ pbmac1KDF :: KeyDerivationFunc -- ^ Key derivation function
, pbmac1AScheme :: MACAlgorithm -- ^ Underlying message authentication scheme
}
deriving (Show,Eq)
instance ASN1Elem e => ProduceASN1Object e PBMAC1Parameter where
asn1s PBMAC1Parameter{..} =
let kdFunc = algorithmASN1S Sequence pbmac1KDF
aScheme = algorithmASN1S Sequence pbmac1AScheme
in asn1Container Sequence (kdFunc . aScheme)
instance Monoid e => ParseASN1Object e PBMAC1Parameter where
parse = onNextContainer Sequence $ do
kdFunc <- parseAlgorithm Sequence
aScheme <- parseAlgorithm Sequence
case kdfKeyLength kdFunc of
Nothing -> return ()
Just sz
| validateKeySize aScheme sz -> return ()
| otherwise -> throwParseError "PBMAC1Parameter: parsed key length incompatible with message authentication scheme"
return PBMAC1Parameter { pbmac1KDF = kdFunc, pbmac1AScheme = aScheme }
instance AlgorithmId AuthenticationScheme where
type AlgorithmType AuthenticationScheme = AuthenticationSchemeType
algorithmName _ = "message authentication scheme"
algorithmType (PBMAC1 _) = Type_PBMAC1
parameterASN1S (PBMAC1 p) = asn1s p
parseParameter Type_PBMAC1 = PBMAC1 <$> parse
instance ASN1Elem e => ProduceASN1Object e AuthenticationScheme where
asn1s = algorithmASN1S Sequence
instance Monoid e => ParseASN1Object e AuthenticationScheme 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)
-- Message Authentication Schemes
-- | Authenticate a message with the specified authentication scheme and
-- password.
pbMac :: AuthenticationScheme -> EncapsulatedContent -> ProtectionPassword -> MessageAuthenticationCode
pbMac (PBMAC1 p) = pbmac1 p
pbmac1 :: PBMAC1Parameter -> ByteString -> ProtectionPassword -> MessageAuthenticationCode
pbmac1 PBMAC1Parameter{..} bs pwd = mac pbmac1AScheme key bs
where key = kdfDerive pbmac1KDF len (fromProtectionPassword pwd) :: Key
len = fromMaybe (getMaximumKeySize pbmac1AScheme) (kdfKeyLength pbmac1KDF)