    -- |
    -- Module      : Crypto.PubKey.RSA.PSS
    -- License     : BSD-style
    -- Maintainer  : Vincent Hanquez <vincent@snarc.org>
    -- Stability   : experimental
    -- Portability : Good
    module Crypto.PubKey.RSA.PSS (
        PSSParams (..),
        defaultPSSParams,
        defaultPSSParamsSHA1,

        -- * Sign and verify functions
        signWithSalt,
        signDigestWithSalt,
        sign,
        signDigest,
        signSafer,
        signDigestSafer,
        verify,
        verifyDigest,
    ) where

    import Crypto.Hash
    import Crypto.Number.Basic (numBits)
    import Crypto.PubKey.MaskGenFunction
    import Crypto.PubKey.RSA (generateBlinder)
    import Crypto.PubKey.RSA.Prim
    import Crypto.PubKey.RSA.Types
    import Crypto.Random.Types
    import Data.Bits (shiftR, xor, (.&.))
    import Data.Word

    import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess)
    import qualified Crypto.Internal.ByteArray as B (convert, eq)

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

    -- | Parameters for PSS signature/verification.
    data PSSParams hash seed output = PSSParams
        { forall hash seed output. PSSParams hash seed output -> hash
    pssHash :: hash
        -- ^ Hash function to use
        , forall hash seed output.
    PSSParams hash seed output -> MaskGenAlgorithm seed output
    pssMaskGenAlg :: MaskGenAlgorithm seed output
        -- ^ Mask Gen algorithm to use
        , forall hash seed output. PSSParams hash seed output -> Int
    pssSaltLength :: Int
        -- ^ Length of salt. need to be <= to hLen.
        , forall hash seed output. PSSParams hash seed output -> Word8
    pssTrailerField :: Word8
        -- ^ Trailer field, usually 0xbc
        }

    -- | Default Params with a specified hash function
    defaultPSSParams
        :: (ByteArrayAccess seed, ByteArray output, HashAlgorithm hash)
        => hash
        -> PSSParams hash seed output
    defaultPSSParams :: forall seed output hash.
    (ByteArrayAccess seed, ByteArray output, HashAlgorithm hash) =>
    hash -> PSSParams hash seed output
    defaultPSSParams hash
    hashAlg =
        PSSParams
            { pssHash :: hash
    pssHash = hash
    hashAlg
            , pssMaskGenAlg :: MaskGenAlgorithm seed output
    pssMaskGenAlg = hash -> MaskGenAlgorithm seed output
    forall seed output hashAlg.
    (ByteArrayAccess seed, ByteArray output, HashAlgorithm hashAlg) =>
    hashAlg -> seed -> Int -> output
    mgf1 hash
    hashAlg
            , pssSaltLength :: Int
    pssSaltLength = hash -> Int
    forall a. HashAlgorithm a => a -> Int
    hashDigestSize hash
    hashAlg
            , pssTrailerField :: Word8
    pssTrailerField = Word8
    0xbc
            }

    -- | Default Params using SHA1 algorithm.
    defaultPSSParamsSHA1 :: PSSParams SHA1 ByteString ByteString
    defaultPSSParamsSHA1 :: PSSParams SHA1 ByteString ByteString
    defaultPSSParamsSHA1 = SHA1 -> PSSParams SHA1 ByteString ByteString
    forall seed output hash.
    (ByteArrayAccess seed, ByteArray output, HashAlgorithm hash) =>
    hash -> PSSParams hash seed output
    defaultPSSParams SHA1
    SHA1

    -- | Sign using the PSS parameters and the salt explicitely passed as parameters.
    --
    -- the function ignore SaltLength from the PSS Parameters
    signDigestWithSalt
        :: HashAlgorithm hash
        => ByteString
        -- ^ Salt to use
        -> Maybe Blinder
        -- ^ optional blinder to use
        -> PSSParams hash ByteString ByteString
        -- ^ PSS Parameters to use
        -> PrivateKey
        -- ^ RSA Private Key
        -> Digest hash
        -- ^ Message digest
        -> Either Error ByteString
    signDigestWithSalt :: forall hash.
    HashAlgorithm hash =>
    ByteString
    -> Maybe Blinder
    -> PSSParams hash ByteString ByteString
    -> PrivateKey
    -> Digest hash
    -> Either Error ByteString
    signDigestWithSalt ByteString
    salt Maybe Blinder
    blinder PSSParams hash ByteString ByteString
    params PrivateKey
    pk Digest hash
    digest
        | Int
    emLen Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    < Int
    hashLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    + Int
    saltLen 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
        | Bool
    otherwise = ByteString -> Either Error ByteString
    forall a b. b -> Either a b
    Right (ByteString -> Either Error ByteString)
    -> ByteString -> Either Error ByteString
    forall a b. (a -> b) -> a -> b
    $ Maybe Blinder -> PrivateKey -> ByteString -> ByteString
    forall ba. ByteArray ba => Maybe Blinder -> PrivateKey -> ba -> ba
    dp Maybe Blinder
    blinder PrivateKey
    pk ByteString
    em
      where
        k :: Int
    k = PrivateKey -> Int
    private_size PrivateKey
    pk
        emLen :: Int
    emLen = if Int -> Bool
    emTruncate Int
    pubBits then Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1 else Int
    k
        mHash :: ByteString
    mHash = Digest hash -> ByteString
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> bout
    B.convert Digest hash
    digest
        dbLen :: Int
    dbLen = Int
    emLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    hashLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1
        saltLen :: Int
    saltLen = ByteString -> Int
    B.length ByteString
    salt
        hashLen :: Int
    hashLen = hash -> Int
    forall a. HashAlgorithm a => a -> Int
    hashDigestSize (PSSParams hash ByteString ByteString -> hash
    forall hash seed output. PSSParams hash seed output -> hash
    pssHash PSSParams hash ByteString ByteString
    params)
        pubBits :: Int
    pubBits = Integer -> Int
    numBits (PrivateKey -> Integer
    private_n PrivateKey
    pk)
        m' :: ByteString
    m' = [ByteString] -> ByteString
    B.concat [Int -> Word8 -> ByteString
    B.replicate Int
    8 Word8
    0, ByteString
    mHash, ByteString
    salt]
        h :: ByteString
    h = 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 (PSSParams hash ByteString ByteString -> hash
    forall hash seed output. PSSParams hash seed output -> hash
    pssHash PSSParams hash ByteString ByteString
    params) ByteString
    m'
        db :: ByteString
    db = [ByteString] -> ByteString
    B.concat [Int -> Word8 -> ByteString
    B.replicate (Int
    dbLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    saltLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) Word8
    0, Word8 -> ByteString
    B.singleton Word8
    1, ByteString
    salt]
        dbmask :: ByteString
    dbmask = PSSParams hash ByteString ByteString
    -> MaskGenAlgorithm ByteString ByteString
    forall hash seed output.
    PSSParams hash seed output -> MaskGenAlgorithm seed output
    pssMaskGenAlg PSSParams hash ByteString ByteString
    params ByteString
    h Int
    dbLen
        maskedDB :: ByteString
    maskedDB = [Word8] -> ByteString
    B.pack ([Word8] -> ByteString) -> [Word8] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ Int -> [Word8] -> [Word8]
    normalizeToKeySize Int
    pubBits ([Word8] -> [Word8]) -> [Word8] -> [Word8]
    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
        em :: ByteString
    em = [ByteString] -> ByteString
    B.concat [ByteString
    maskedDB, ByteString
    h, Word8 -> ByteString
    B.singleton (PSSParams hash ByteString ByteString -> Word8
    forall hash seed output. PSSParams hash seed output -> Word8
    pssTrailerField PSSParams hash ByteString ByteString
    params)]

    -- | Sign using the PSS parameters and the salt explicitely passed as parameters.
    --
    -- the function ignore SaltLength from the PSS Parameters
    signWithSalt
        :: HashAlgorithm hash
        => ByteString
        -- ^ Salt to use
        -> Maybe Blinder
        -- ^ optional blinder to use
        -> PSSParams hash ByteString ByteString
        -- ^ PSS Parameters to use
        -> PrivateKey
        -- ^ RSA Private Key
        -> ByteString
        -- ^ Message to sign
        -> Either Error ByteString
    signWithSalt :: forall hash.
    HashAlgorithm hash =>
    ByteString
    -> Maybe Blinder
    -> PSSParams hash ByteString ByteString
    -> PrivateKey
    -> ByteString
    -> Either Error ByteString
    signWithSalt ByteString
    salt Maybe Blinder
    blinder PSSParams hash ByteString ByteString
    params PrivateKey
    pk ByteString
    m = ByteString
    -> Maybe Blinder
    -> PSSParams hash ByteString ByteString
    -> PrivateKey
    -> Digest hash
    -> Either Error ByteString
    forall hash.
    HashAlgorithm hash =>
    ByteString
    -> Maybe Blinder
    -> PSSParams hash ByteString ByteString
    -> PrivateKey
    -> Digest hash
    -> Either Error ByteString
    signDigestWithSalt ByteString
    salt Maybe Blinder
    blinder PSSParams hash ByteString ByteString
    params PrivateKey
    pk Digest hash
    mHash
      where
        mHash :: Digest hash
    mHash = hash -> ByteString -> Digest hash
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith (PSSParams hash ByteString ByteString -> hash
    forall hash seed output. PSSParams hash seed output -> hash
    pssHash PSSParams hash ByteString ByteString
    params) ByteString
    m

    -- | Sign using the PSS Parameters
    sign
        :: (HashAlgorithm hash, MonadRandom m)
        => Maybe Blinder
        -- ^ optional blinder to use
        -> PSSParams hash ByteString ByteString
        -- ^ PSS Parameters to use
        -> PrivateKey
        -- ^ RSA Private Key
        -> ByteString
        -- ^ Message to sign
        -> m (Either Error ByteString)
    sign :: forall hash (m :: * -> *).
    (HashAlgorithm hash, MonadRandom m) =>
    Maybe Blinder
    -> PSSParams hash ByteString ByteString
    -> PrivateKey
    -> ByteString
    -> m (Either Error ByteString)
    sign Maybe Blinder
    blinder PSSParams hash ByteString ByteString
    params PrivateKey
    pk ByteString
    m = do
        salt <- Int -> m ByteString
    forall byteArray. ByteArray byteArray => Int -> m byteArray
    forall (m :: * -> *) byteArray.
    (MonadRandom m, ByteArray byteArray) =>
    Int -> m byteArray
    getRandomBytes (PSSParams hash ByteString ByteString -> Int
    forall hash seed output. PSSParams hash seed output -> Int
    pssSaltLength PSSParams hash ByteString ByteString
    params)
        return (signWithSalt salt blinder params pk m)

    -- | Sign using the PSS Parameters
    signDigest
        :: (HashAlgorithm hash, MonadRandom m)
        => Maybe Blinder
        -- ^ optional blinder to use
        -> PSSParams hash ByteString ByteString
        -- ^ PSS Parameters to use
        -> PrivateKey
        -- ^ RSA Private Key
        -> Digest hash
        -- ^ Message digest
        -> m (Either Error ByteString)
    signDigest :: forall hash (m :: * -> *).
    (HashAlgorithm hash, MonadRandom m) =>
    Maybe Blinder
    -> PSSParams hash ByteString ByteString
    -> PrivateKey
    -> Digest hash
    -> m (Either Error ByteString)
    signDigest Maybe Blinder
    blinder PSSParams hash ByteString ByteString
    params PrivateKey
    pk Digest hash
    digest = do
        salt <- Int -> m ByteString
    forall byteArray. ByteArray byteArray => Int -> m byteArray
    forall (m :: * -> *) byteArray.
    (MonadRandom m, ByteArray byteArray) =>
    Int -> m byteArray
    getRandomBytes (PSSParams hash ByteString ByteString -> Int
    forall hash seed output. PSSParams hash seed output -> Int
    pssSaltLength PSSParams hash ByteString ByteString
    params)
        return (signDigestWithSalt salt blinder params pk digest)

    -- | Sign using the PSS Parameters and an automatically generated blinder.
    signSafer
        :: (HashAlgorithm hash, MonadRandom m)
        => PSSParams hash ByteString ByteString
        -- ^ PSS Parameters to use
        -> PrivateKey
        -- ^ private key
        -> ByteString
        -- ^ message to sign
        -> m (Either Error ByteString)
    signSafer :: forall hash (m :: * -> *).
    (HashAlgorithm hash, MonadRandom m) =>
    PSSParams hash ByteString ByteString
    -> PrivateKey -> ByteString -> m (Either Error ByteString)
    signSafer PSSParams hash ByteString ByteString
    params PrivateKey
    pk ByteString
    m = do
        blinder <- Integer -> m Blinder
    forall (m :: * -> *). MonadRandom m => Integer -> m Blinder
    generateBlinder (PrivateKey -> Integer
    private_n PrivateKey
    pk)
        sign (Just blinder) params pk m

    -- | Sign using the PSS Parameters and an automatically generated blinder.
    signDigestSafer
        :: (HashAlgorithm hash, MonadRandom m)
        => PSSParams hash ByteString ByteString
        -- ^ PSS Parameters to use
        -> PrivateKey
        -- ^ private key
        -> Digest hash
        -- ^ message digst
        -> m (Either Error ByteString)
    signDigestSafer :: forall hash (m :: * -> *).
    (HashAlgorithm hash, MonadRandom m) =>
    PSSParams hash ByteString ByteString
    -> PrivateKey -> Digest hash -> m (Either Error ByteString)
    signDigestSafer PSSParams hash ByteString ByteString
    params PrivateKey
    pk Digest hash
    digest = do
        blinder <- Integer -> m Blinder
    forall (m :: * -> *). MonadRandom m => Integer -> m Blinder
    generateBlinder (PrivateKey -> Integer
    private_n PrivateKey
    pk)
        signDigest (Just blinder) params pk digest

    -- | Verify a signature using the PSS Parameters
    verify
        :: HashAlgorithm hash
        => PSSParams hash ByteString ByteString
        -- ^ PSS Parameters to use to verify,
        --   this need to be identical to the parameters when signing
        -> PublicKey
        -- ^ RSA Public Key
        -> ByteString
        -- ^ Message to verify
        -> ByteString
        -- ^ Signature
        -> Bool
    verify :: forall hash.
    HashAlgorithm hash =>
    PSSParams hash ByteString ByteString
    -> PublicKey -> ByteString -> ByteString -> Bool
    verify PSSParams hash ByteString ByteString
    params PublicKey
    pk ByteString
    m = PSSParams hash ByteString ByteString
    -> PublicKey -> Digest hash -> ByteString -> Bool
    forall hash.
    HashAlgorithm hash =>
    PSSParams hash ByteString ByteString
    -> PublicKey -> Digest hash -> ByteString -> Bool
    verifyDigest PSSParams hash ByteString ByteString
    params PublicKey
    pk Digest hash
    mHash
      where
        mHash :: Digest hash
    mHash = hash -> ByteString -> Digest hash
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith (PSSParams hash ByteString ByteString -> hash
    forall hash seed output. PSSParams hash seed output -> hash
    pssHash PSSParams hash ByteString ByteString
    params) ByteString
    m

    -- | Verify a signature using the PSS Parameters
    verifyDigest
        :: HashAlgorithm hash
        => PSSParams hash ByteString ByteString
        -- ^ PSS Parameters to use to verify,
        --   this need to be identical to the parameters when signing
        -> PublicKey
        -- ^ RSA Public Key
        -> Digest hash
        -- ^ Digest to verify
        -> ByteString
        -- ^ Signature
        -> Bool
    verifyDigest :: forall hash.
    HashAlgorithm hash =>
    PSSParams hash ByteString ByteString
    -> PublicKey -> Digest hash -> ByteString -> Bool
    verifyDigest PSSParams hash ByteString ByteString
    params PublicKey
    pk Digest hash
    digest ByteString
    s
        | ByteString -> Int
    B.length ByteString
    s Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Int
    k = Bool
    False
        | (Word8 -> Bool) -> ByteString -> Bool
    B.any (Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Word8
    0) ByteString
    pre = Bool
    False
        | HasCallStack => ByteString -> Word8
    ByteString -> Word8
    B.last ByteString
    em Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    /= PSSParams hash ByteString ByteString -> Word8
    forall hash seed output. PSSParams hash seed output -> Word8
    pssTrailerField PSSParams hash ByteString ByteString
    params = Bool
    False
        | (Word8 -> Bool) -> ByteString -> Bool
    B.any (Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Word8
    0) ByteString
    ps0 = Bool
    False
        | ByteString
    b1 ByteString -> ByteString -> Bool
    forall a. Eq a => a -> a -> Bool
    /= Word8 -> ByteString
    B.singleton Word8
    1 = Bool
    False
        | PSSParams hash ByteString ByteString -> Int
    forall hash seed output. PSSParams hash seed output -> Int
    pssSaltLength PSSParams hash ByteString ByteString
    params Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    /= ByteString -> Int
    B.length ByteString
    salt = Bool
    False
        | Bool
    otherwise = ByteString -> Digest hash -> Bool
    forall bs1 bs2.
    (ByteArrayAccess bs1, ByteArrayAccess bs2) =>
    bs1 -> bs2 -> Bool
    B.eq ByteString
    h Digest hash
    h'
      where
        -- parameters
        hashLen :: Int
    hashLen = hash -> Int
    forall a. HashAlgorithm a => a -> Int
    hashDigestSize (PSSParams hash ByteString ByteString -> hash
    forall hash seed output. PSSParams hash seed output -> hash
    pssHash PSSParams hash ByteString ByteString
    params)
        mHash :: ByteString
    mHash = Digest hash -> ByteString
    forall bin bout.
    (ByteArrayAccess bin, ByteArray bout) =>
    bin -> bout
    B.convert Digest hash
    digest
        k :: Int
    k = PublicKey -> Int
    public_size PublicKey
    pk
        emLen :: Int
    emLen = if Int -> Bool
    emTruncate Int
    pubBits then Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1 else Int
    k
        dbLen :: Int
    dbLen = Int
    emLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    hashLen Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1
        pubBits :: Int
    pubBits = Integer -> Int
    numBits (PublicKey -> Integer
    public_n PublicKey
    pk)
        -- unmarshall fields
        (ByteString
    pre, ByteString
    em) = Int -> ByteString -> (ByteString, ByteString)
    B.splitAt (Int
    k Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    emLen) (PublicKey -> ByteString -> ByteString
    forall ba. ByteArray ba => PublicKey -> ba -> ba
    ep PublicKey
    pk ByteString
    s) -- drop 0..1 byte
        maskedDB :: ByteString
    maskedDB = Int -> ByteString -> ByteString
    B.take Int
    dbLen ByteString
    em
        h :: ByteString
    h = Int -> ByteString -> ByteString
    B.take Int
    hashLen (ByteString -> ByteString) -> ByteString -> ByteString
    forall a b. (a -> b) -> a -> b
    $ Int -> ByteString -> ByteString
    B.drop (ByteString -> Int
    B.length ByteString
    maskedDB) ByteString
    em
        dbmask :: ByteString
    dbmask = PSSParams hash ByteString ByteString
    -> MaskGenAlgorithm ByteString ByteString
    forall hash seed output.
    PSSParams hash seed output -> MaskGenAlgorithm seed output
    pssMaskGenAlg PSSParams hash ByteString ByteString
    params ByteString
    h Int
    dbLen
        db :: ByteString
    db = [Word8] -> ByteString
    B.pack ([Word8] -> ByteString) -> [Word8] -> ByteString
    forall a b. (a -> b) -> a -> b
    $ Int -> [Word8] -> [Word8]
    normalizeToKeySize Int
    pubBits ([Word8] -> [Word8]) -> [Word8] -> [Word8]
    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
        (ByteString
    ps0, ByteString
    z) = (Word8 -> Bool) -> ByteString -> (ByteString, ByteString)
    B.break (Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    == Word8
    1) ByteString
    db
        (ByteString
    b1, ByteString
    salt) = Int -> ByteString -> (ByteString, ByteString)
    B.splitAt Int
    1 ByteString
    z
        m' :: ByteString
    m' = [ByteString] -> ByteString
    B.concat [Int -> Word8 -> ByteString
    B.replicate Int
    8 Word8
    0, ByteString
    mHash, ByteString
    salt]
        h' :: Digest hash
    h' = hash -> ByteString -> Digest hash
    forall ba alg.
    (ByteArrayAccess ba, HashAlgorithm alg) =>
    alg -> ba -> Digest alg
    hashWith (PSSParams hash ByteString ByteString -> hash
    forall hash seed output. PSSParams hash seed output -> hash
    pssHash PSSParams hash ByteString ByteString
    params) ByteString
    m'

    -- When the modulus has bit length 1 modulo 8 we drop the first byte.
    emTruncate :: Int -> Bool
    emTruncate :: Int -> Bool
    emTruncate Int
    bits = ((Int
    bits Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) Int -> Int -> Int
    forall a. Bits a => a -> a -> a
    .&. Int
    0x7) Int -> Int -> Bool
    forall a. Eq a => a -> a -> Bool
    == Int
    0

    normalizeToKeySize :: Int -> [Word8] -> [Word8]
    normalizeToKeySize :: Int -> [Word8] -> [Word8]
    normalizeToKeySize Int
    _ [] = [] -- very unlikely
    normalizeToKeySize Int
    bits (Word8
    x : [Word8]
    xs) = Word8
    x Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    .&. Word8
    mask Word8 -> [Word8] -> [Word8]
    forall a. a -> [a] -> [a]
    : [Word8]
    xs
      where
        mask :: Word8
    mask = if Int
    sh Int -> Int -> Bool
    forall a. Ord a => a -> a -> Bool
    > Int
    0 then Word8
    0xff Word8 -> Int -> Word8
    forall a. Bits a => a -> Int -> a
    `shiftR` (Int
    8 Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    sh) else Word8
    0xff
        sh :: Int
    sh = (Int
    bits Int -> Int -> Int
    forall a. Num a => a -> a -> a
    - Int
    1) Int -> Int -> Int
    forall a. Bits a => a -> a -> a
    .&. Int
    0x7