-- |
-- Module      : Crypto.Data.Padding
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : experimental
-- Portability : unknown
--
-- Various cryptographic padding commonly used for block ciphers
-- or asymmetric systems.
--
module Crypto.Data.Padding
    ( Format(..)
    , pad
    , unpad
    ) where

import           Data.ByteArray (ByteArray, Bytes)
import qualified Data.ByteArray as B

-- | Format of padding
data Format =
      PKCS5     -- ^ PKCS5: PKCS7 with hardcoded size of 8
    | PKCS7 Int -- ^ PKCS7 with padding size between 1 and 255
    | ZERO Int  -- ^ zero padding with block size
    deriving (Int -> Format -> ShowS
[Format] -> ShowS
Format -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Format] -> ShowS
$cshowList :: [Format] -> ShowS
show :: Format -> String
$cshow :: Format -> String
showsPrec :: Int -> Format -> ShowS
$cshowsPrec :: Int -> Format -> ShowS
Show, Format -> Format -> Bool
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Format -> Format -> Bool
$c/= :: Format -> Format -> Bool
== :: Format -> Format -> Bool
$c== :: Format -> Format -> Bool
Eq)

-- | Apply some pad to a bytearray
pad :: ByteArray byteArray => Format -> byteArray -> byteArray
pad :: forall byteArray.
ByteArray byteArray =>
Format -> byteArray -> byteArray
pad  Format
PKCS5     byteArray
bin = forall byteArray.
ByteArray byteArray =>
Format -> byteArray -> byteArray
pad (Int -> Format
PKCS7 Int
8) byteArray
bin
pad (PKCS7 Int
sz) byteArray
bin = byteArray
bin forall bs. ByteArray bs => bs -> bs -> bs
`B.append` byteArray
paddingString
  where
    paddingString :: byteArray
paddingString = forall ba. ByteArray ba => Int -> Word8 -> ba
B.replicate Int
paddingByte (forall a b. (Integral a, Num b) => a -> b
fromIntegral Int
paddingByte)
    paddingByte :: Int
paddingByte   = Int
sz forall a. Num a => a -> a -> a
- (forall ba. ByteArrayAccess ba => ba -> Int
B.length byteArray
bin forall a. Integral a => a -> a -> a
`mod` Int
sz)
pad (ZERO Int
sz)  byteArray
bin = byteArray
bin forall bs. ByteArray bs => bs -> bs -> bs
`B.append` byteArray
paddingString
  where
    paddingString :: byteArray
paddingString = forall ba. ByteArray ba => Int -> Word8 -> ba
B.replicate Int
paddingSz Word8
0
    paddingSz :: Int
paddingSz
      | Int
len forall a. Eq a => a -> a -> Bool
== Int
0   =  Int
sz
      | Int
m forall a. Eq a => a -> a -> Bool
== Int
0     =  Int
0
      | Bool
otherwise  =  Int
sz forall a. Num a => a -> a -> a
- Int
m
    m :: Int
m = Int
len forall a. Integral a => a -> a -> a
`mod` Int
sz
    len :: Int
len = forall ba. ByteArrayAccess ba => ba -> Int
B.length byteArray
bin

-- | Try to remove some padding from a bytearray.
unpad :: ByteArray byteArray => Format -> byteArray -> Maybe byteArray
unpad :: forall byteArray.
ByteArray byteArray =>
Format -> byteArray -> Maybe byteArray
unpad  Format
PKCS5     byteArray
bin = forall byteArray.
ByteArray byteArray =>
Format -> byteArray -> Maybe byteArray
unpad (Int -> Format
PKCS7 Int
8) byteArray
bin
unpad (PKCS7 Int
sz) byteArray
bin
    | Int
len forall a. Eq a => a -> a -> Bool
== Int
0                           = forall a. Maybe a
Nothing
    | (Int
len forall a. Integral a => a -> a -> a
`mod` Int
sz) forall a. Eq a => a -> a -> Bool
/= Int
0                = forall a. Maybe a
Nothing
    | Int
paddingSz forall a. Ord a => a -> a -> Bool
< Int
1 Bool -> Bool -> Bool
|| Int
paddingSz forall a. Ord a => a -> a -> Bool
> Int
len   = forall a. Maybe a
Nothing
    | Bytes
paddingWitness forall bs1 bs2.
(ByteArrayAccess bs1, ByteArrayAccess bs2) =>
bs1 -> bs2 -> Bool
`B.constEq` byteArray
padding = forall a. a -> Maybe a
Just byteArray
content
    | Bool
otherwise                          = forall a. Maybe a
Nothing
  where
    len :: Int
len         = forall ba. ByteArrayAccess ba => ba -> Int
B.length byteArray
bin
    paddingByte :: Word8
paddingByte = forall a. ByteArrayAccess a => a -> Int -> Word8
B.index byteArray
bin (Int
len forall a. Num a => a -> a -> a
- Int
1)
    paddingSz :: Int
paddingSz   = forall a b. (Integral a, Num b) => a -> b
fromIntegral Word8
paddingByte
    (byteArray
content, byteArray
padding) = forall bs. ByteArray bs => Int -> bs -> (bs, bs)
B.splitAt (Int
len forall a. Num a => a -> a -> a
- Int
paddingSz) byteArray
bin
    paddingWitness :: Bytes
paddingWitness     = forall ba. ByteArray ba => Int -> Word8 -> ba
B.replicate Int
paddingSz Word8
paddingByte :: Bytes
unpad (ZERO Int
sz)  byteArray
bin
    | Int
len forall a. Eq a => a -> a -> Bool
== Int
0                           = forall a. Maybe a
Nothing
    | (Int
len forall a. Integral a => a -> a -> a
`mod` Int
sz) forall a. Eq a => a -> a -> Bool
/= Int
0                = forall a. Maybe a
Nothing
    | forall a. ByteArrayAccess a => a -> Int -> Word8
B.index byteArray
bin (Int
len forall a. Num a => a -> a -> a
- Int
1) forall a. Eq a => a -> a -> Bool
/= Word8
0         = forall a. a -> Maybe a
Just byteArray
bin
    | Bool
otherwise                          = forall a. Maybe a
Nothing
  where
    len :: Int
len         = forall ba. ByteArrayAccess ba => ba -> Int
B.length byteArray
bin