    -- |
    -- Module      : Crypto.PubKey.Rabin.OAEP
    -- License     : BSD-style
    -- Maintainer  : Carlos Rodriguez-Vega <crodveg@yahoo.es>
    -- Stability   : experimental
    -- Portability : unknown
    --
    -- OAEP padding scheme.
    -- See <http://en.wikipedia.org/wiki/Optimal_asymmetric_encryption_padding>.
    module Crypto.PubKey.Rabin.OAEP (
        OAEPParams (..),
        defaultOAEPParams,
        pad,
        unpad,
    ) where

    import Data.Bits (xor)
    import Data.ByteString (ByteString)
    import qualified Data.ByteString as B

    import Crypto.Hash
    import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess)
    import qualified Crypto.Internal.ByteArray as B (convert)
    import Crypto.PubKey.Internal (and')
    import Crypto.PubKey.MaskGenFunction
    import Crypto.PubKey.Rabin.Types

    -- | Parameters for OAEP padding.
    data OAEPParams hash seed output = OAEPParams
        { forall hash seed output. OAEPParams hash seed output -> hash
    oaepHash :: hash
        -- ^ hash function to use
        , forall hash seed output.
    OAEPParams hash seed output -> MaskGenAlgorithm seed output
    oaepMaskGenAlg :: MaskGenAlgorithm seed output
        -- ^ mask Gen algorithm to use
        , forall hash seed output.
    OAEPParams hash seed output -> Maybe ByteString
    oaepLabel :: Maybe ByteString
        -- ^ optional label prepended to message
        }

    -- | Default Params with a specified hash function.
    defaultOAEPParams
        :: (ByteArrayAccess seed, ByteArray output, HashAlgorithm hash)
        => hash
        -> OAEPParams hash seed output
    defaultOAEPParams :: forall seed output hash.
    (ByteArrayAccess seed, ByteArray output, HashAlgorithm hash) =>
    hash -> OAEPParams hash seed output
    defaultOAEPParams hash
    hashAlg =
        OAEPParams
            { oaepHash :: hash
    oaepHash = hash
    hashAlg
            , oaepMaskGenAlg :: MaskGenAlgorithm seed output
    oaepMaskGenAlg = hash -> MaskGenAlgorithm seed output
    forall seed output hashAlg.
    (ByteArrayAccess seed, ByteArray output, HashAlgorithm hashAlg) =>
    hashAlg -> seed -> Int -> output
    mgf1 hash
    hashAlg
            , oaepLabel :: Maybe ByteString
    oaepLabel = Maybe ByteString
    forall a. Maybe a
    Nothing
            }

    -- | Pad a message using OAEP.
    pad
        :: HashAlgorithm hash
        => ByteString
        -- ^ Seed
        -> OAEPParams hash ByteString ByteString
        -- ^ OAEP params to use
        -> Int
        -- ^ size of public key in bytes
        -> ByteString
        -- ^ Message pad
        -> Either Error ByteString
    pad :: forall hash.
    HashAlgorithm hash =>
    ByteString
    -> OAEPParams hash ByteString ByteString
    -> Int
    -> ByteString
    -> Either Error ByteString
    pad ByteString
    seed OAEPParams hash ByteString ByteString
    oaep Int
    k ByteString
    msg
        | Int
    k Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    2 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    hashLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    2 = Error -> Either Error ByteString
    forall a b. a -> Either a b
    Left Error
    InvalidParameters
        | ByteString -> Int
    B.length ByteString
    seed Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Int
    hashLen = Error -> Either Error ByteString
    forall a b. a -> Either a b
    Left Error
    InvalidParameters
        | Int
    mLen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    hashLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2 = Error -> Either Error ByteString
    forall a b. a -> Either a b
    Left Error
    MessageTooLong
        | Bool
    otherwise = ByteString -> Either Error ByteString
    forall a b. b -> Either a b
    Right ByteString
    em
      where
        -- parameters
        mLen :: Int
    mLen = ByteString -> Int
    B.length ByteString
    msg
        mgf :: MaskGenAlgorithm ByteString ByteString
    mgf = OAEPParams hash ByteString ByteString
    -> MaskGenAlgorithm ByteString ByteString
    forall hash seed output.
    OAEPParams hash seed output -> MaskGenAlgorithm seed output
    oaepMaskGenAlg OAEPParams hash ByteString ByteString
    oaep
        labelHash :: Digest hash
    labelHash = hash -> ByteString -> Digest hash
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith (OAEPParams hash ByteString ByteString -> hash
    forall hash seed output. OAEPParams hash seed output -> hash
    oaepHash OAEPParams hash ByteString ByteString
    oaep) (ByteString
    -> (ByteString -> ByteString) -> Maybe ByteString -> ByteString
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe ByteString
    B.empty ByteString -> ByteString
    forall a. a -> a
    id (Maybe ByteString -> ByteString) -> Maybe ByteString -> ByteString
    forall a b. (a -> b) -> a -> b
    $ OAEPParams hash ByteString ByteString -> Maybe ByteString
    forall hash seed output.
    OAEPParams hash seed output -> Maybe ByteString
    oaepLabel OAEPParams hash ByteString ByteString
    oaep)
        hashLen :: Int
    hashLen = hash -> Int
    forall a. HashAlgorithm a => a -> Int
    hashDigestSize (OAEPParams hash ByteString ByteString -> hash
    forall hash seed output. OAEPParams hash seed output -> hash
    oaepHash OAEPParams hash ByteString ByteString
    oaep)
        -- put fields
        ps :: ByteString
    ps = Int -> Word8 -> ByteString
    B.replicate (Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    mLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    * Int
    hashLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    2) Word8
    0
        db :: ByteString
    db = [ByteString] -> ByteString
    B.concat [Digest hash -> ByteString
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> bout
    B.convert Digest hash
    labelHash, ByteString
    ps, Word8 -> ByteString
    B.singleton Word8
    0x1, ByteString
    msg]
        dbmask :: ByteString
    dbmask = MaskGenAlgorithm ByteString ByteString
    mgf ByteString
    seed (Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    hashLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1)
        maskedDB :: ByteString
    maskedDB = [Word8] -> ByteString
    B.pack ([Word8] -> ByteString) -> [Word8] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> [Word8]
    forall a. (Word8 -> Word8 -> a) -> ByteString -> ByteString -> [a]
    B.zipWith Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor ByteString
    db ByteString
    dbmask
        seedMask :: ByteString
    seedMask = MaskGenAlgorithm ByteString ByteString
    mgf ByteString
    maskedDB Int
    hashLen
        maskedSeed :: ByteString
    maskedSeed = [Word8] -> ByteString
    B.pack ([Word8] -> ByteString) -> [Word8] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> [Word8]
    forall a. (Word8 -> Word8 -> a) -> ByteString -> ByteString -> [a]
    B.zipWith Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor ByteString
    seed ByteString
    seedMask
        em :: ByteString
    em = [ByteString] -> ByteString
    B.concat [Word8 -> ByteString
    B.singleton Word8
    0x0, ByteString
    maskedSeed, ByteString
    maskedDB]

    -- | Un-pad a OAEP encoded message.
    unpad
        :: HashAlgorithm hash
        => OAEPParams hash ByteString ByteString
        -- ^ OAEP params to use
        -> Int
        -- ^ size of public key in bytes
        -> ByteString
        -- ^ encoded message (not encrypted)
        -> Either Error ByteString
    unpad :: forall hash.
    HashAlgorithm hash =>
    OAEPParams hash ByteString ByteString
    -> Int -> ByteString -> Either Error ByteString
    unpad OAEPParams hash ByteString ByteString
    oaep Int
    k ByteString
    em
        | Bool
    paddingSuccess = ByteString -> Either Error ByteString
    forall a b. b -> Either a b
    Right ByteString
    msg
        | Bool
    otherwise = Error -> Either Error ByteString
    forall a b. a -> Either a b
    Left Error
    MessageNotRecognized
      where
        -- parameters
        mgf :: MaskGenAlgorithm ByteString ByteString
    mgf = OAEPParams hash ByteString ByteString
    -> MaskGenAlgorithm ByteString ByteString
    forall hash seed output.
    OAEPParams hash seed output -> MaskGenAlgorithm seed output
    oaepMaskGenAlg OAEPParams hash ByteString ByteString
    oaep
        labelHash :: ByteString
    labelHash = Digest hash -> ByteString
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> bout
    B.convert (Digest hash -> ByteString) -> Digest hash -> ByteString
    forall a b. (a -> b) -> a -> b
    $ hash -> ByteString -> Digest hash
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith (OAEPParams hash ByteString ByteString -> hash
    forall hash seed output. OAEPParams hash seed output -> hash
    oaepHash OAEPParams hash ByteString ByteString
    oaep) (ByteString
    -> (ByteString -> ByteString) -> Maybe ByteString -> ByteString
    forall b a. b -> (a -> b) -> Maybe a -> b
    maybe ByteString
    B.empty ByteString -> ByteString
    forall a. a -> a
    id (Maybe ByteString -> ByteString) -> Maybe ByteString -> ByteString
    forall a b. (a -> b) -> a -> b
    $ OAEPParams hash ByteString ByteString -> Maybe ByteString
    forall hash seed output.
    OAEPParams hash seed output -> Maybe ByteString
    oaepLabel OAEPParams hash ByteString ByteString
    oaep)
        hashLen :: Int
    hashLen = hash -> Int
    forall a. HashAlgorithm a => a -> Int
    hashDigestSize (OAEPParams hash ByteString ByteString -> hash
    forall hash seed output. OAEPParams hash seed output -> hash
    oaepHash OAEPParams hash ByteString ByteString
    oaep)
        -- getting em's fields
        (ByteString
    pb, ByteString
    em0) = Int -> ByteString -> (ByteString, ByteString)
    B.splitAt Int
    1 ByteString
    em
        (ByteString
    maskedSeed, ByteString
    maskedDB) = Int -> ByteString -> (ByteString, ByteString)
    B.splitAt Int
    hashLen ByteString
    em0
        seedMask :: ByteString
    seedMask = MaskGenAlgorithm ByteString ByteString
    mgf ByteString
    maskedDB Int
    hashLen
        seed :: ByteString
    seed = [Word8] -> ByteString
    B.pack ([Word8] -> ByteString) -> [Word8] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> [Word8]
    forall a. (Word8 -> Word8 -> a) -> ByteString -> ByteString -> [a]
    B.zipWith Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor ByteString
    maskedSeed ByteString
    seedMask
        dbmask :: ByteString
    dbmask = MaskGenAlgorithm ByteString ByteString
    mgf ByteString
    seed (Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    hashLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1)
        db :: ByteString
    db = [Word8] -> ByteString
    B.pack ([Word8] -> ByteString) -> [Word8] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ (Word8 -> Word8 -> Word8) -> ByteString -> ByteString -> [Word8]
    forall a. (Word8 -> Word8 -> a) -> ByteString -> ByteString -> [a]
    B.zipWith Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    xor ByteString
    maskedDB ByteString
    dbmask
        -- getting db's fields
        (ByteString
    labelHash', ByteString
    db1) = Int -> ByteString -> (ByteString, ByteString)
    B.splitAt Int
    hashLen ByteString
    db
        (ByteString
    _, ByteString
    db2) = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
    B.break (Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Word8
    0) ByteString
    db1
        (ByteString
    ps1, ByteString
    msg) = Int -> ByteString -> (ByteString, ByteString)
    B.splitAt Int
    1 ByteString
    db2

        paddingSuccess :: Bool
    paddingSuccess =
            [Bool] -> Bool
    and'
                [ ByteString
    labelHash' ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    == ByteString
    labelHash -- no need for constant eq
                , ByteString
    ps1 ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int -> Word8 -> ByteString
    B.replicate Int
    1 Word8
    0x1
                , ByteString
    pb ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int -> Word8 -> ByteString
    B.replicate Int
    1 Word8
    0x0
                ]