module Crypto.Data.PKCS7 (
pad, padN
, unpad, unpadN
, padBytes, padBytesN
, unpadBytes, unpadBytesN
) where
import qualified Data.Char as C
import qualified Data.ByteString as B
import qualified Data.Word as W
padString :: Int -> String
padString n = replicate n (C.chr n)
padN :: Int -> String -> String
padN blockSize s = s ++ padString padLength
where padLength = blockSize remaining
remaining = length s `mod` blockSize
pad :: String -> String
pad = padN 16
lengthCheck :: Int -> String -> Maybe String
lengthCheck blockSize s = if extra s /= 0
then Nothing
else Just s
where extra = (`mod` blockSize) . length
validPadChar :: Int -> String -> Maybe String
validPadChar blockSize s = if C.ord (last s) <= blockSize
then Just s
else Nothing
dropPadding :: String -> Maybe String
dropPadding s = if all (== padChar) p
then Just m
else Nothing
where padChar = last s
leading = length s C.ord padChar
(m, p) = splitAt leading s
unpadN :: Int -> String -> Maybe String
unpadN blockSize s = lengthCheck blockSize s
>>= validPadChar blockSize
>>= dropPadding
unpad :: String -> Maybe String
unpad = unpadN 16
padByteString :: Int -> B.ByteString
padByteString n = B.pack . take n $ repeat padChar
where padChar = fromIntegral n :: W.Word8
padBytesN :: Int -> B.ByteString -> B.ByteString
padBytesN blockSize s = B.append s $ padByteString padLength
where padLength = blockSize remaining
remaining = B.length s `mod` blockSize
padBytes :: B.ByteString -> B.ByteString
padBytes = padBytesN 16
lengthCheckBytes :: Int -> B.ByteString -> Maybe B.ByteString
lengthCheckBytes blockSize s = if extra s /= 0
then Nothing
else Just s
where extra = (`mod` blockSize) . B.length
validPadCharBytes :: Int -> B.ByteString -> Maybe B.ByteString
validPadCharBytes blockSize s = if fromIntegral (B.last s) <= blockSize
then Just s
else Nothing
dropPaddingBytes :: B.ByteString -> Maybe B.ByteString
dropPaddingBytes s = if B.all (== fromIntegral padChar) p
then Just m
else Nothing
where padChar = fromIntegral $ B.last s
leading = B.length s padChar
(m, p) = B.splitAt leading s
unpadBytesN :: Int -> B.ByteString -> Maybe B.ByteString
unpadBytesN blockSize s = lengthCheckBytes blockSize s
>>= validPadCharBytes blockSize
>>= dropPaddingBytes
unpadBytes :: B.ByteString -> Maybe B.ByteString
unpadBytes = unpadBytesN 16