    -- S2K.hs: OpenPGP (RFC4880) string-to-key conversion
    -- Copyright © 2013  Clint Adams
    -- This software is released under the terms of the Expat license.
    -- (See the LICENSE file).

    module Codec.Encryption.OpenPGP.S2K (
      string2Key
      ,skesk2Key
    ) where

    import Codec.Encryption.OpenPGP.BlockCipher (keySize)
    import Codec.Encryption.OpenPGP.Types
    import Control.Monad.Loops (untilM_)
    import Control.Monad.Trans.State.Lazy (execState, get, put)
    import qualified Crypto.Hash.SHA1 as SHA1
    import qualified Crypto.Hash.SHA512 as SHA512
    import qualified Data.ByteString as B
    import qualified Data.ByteString.Lazy as BL

    string2Key :: S2K -> Int -> BL.ByteString -> B.ByteString
    string2Key (Simple ha) ksz bs = B.take (fromIntegral ksz) $ hashpp (hf ha) ksz bs
    string2Key (Salted ha salt) ksz bs = string2Key (Simple ha) ksz (BL.append (BL.fromChunks [salt]) bs)
    string2Key (IteratedSalted ha salt cnt) ksz bs = string2Key (Simple ha) ksz (BL.take (fromIntegral cnt) . BL.cycle $ BL.append (BL.fromStrict salt) bs)
    string2Key _ _ _ = error "FIXME: unimplemented S2K type"

    hf :: HashAlgorithm -> BL.ByteString -> B.ByteString
    hf SHA1 = SHA1.hashlazy
    hf SHA512 = SHA512.hashlazy
    hf _ = error "FIXME: unimplemented S2K hash"

    skesk2Key :: SKESK -> BL.ByteString -> B.ByteString
    skesk2Key (SKESK 4 sa s2k Nothing) pass = string2Key s2k (keySize sa) pass
    skesk2Key _ _ = error "FIXME"

    hashpp :: (BL.ByteString -> B.ByteString) -> Int -> BL.ByteString -> B.ByteString
    hashpp hf keysize pp = snd (execState (hashround `untilM_` bigEnough) (0, B.empty))
        where
            hashround = get >>= \(ctr, bs) -> put (ctr + 1, bs `B.append` hf (nulpad ctr `BL.append` pp))
            nulpad = BL.pack . flip replicate 0
    	bigEnough = get >>= \(_, bs) -> return (B.length bs >= keysize)