diff --git a/Crypto/Data/Padding.hs b/Crypto/Data/Padding.hs new file mode 100644 index 0000000..0030b4f --- /dev/null +++ b/Crypto/Data/Padding.hs @@ -0,0 +1,47 @@ +-- | +-- Module : Crypto.Data.Padding +-- License : BSD-style +-- Maintainer : Vincent Hanquez +-- Stability : experimental +-- Portability : unknown +-- +-- Various cryptographic padding commonly used for block ciphers +-- or assymetric systems. +-- +module Crypto.Data.Padding + ( Format(..) + , pad + , unpad + ) where + +import Data.ByteArray (ByteArray, Bytes) +import qualified Data.ByteArray as B + +data Format = + PKCS5 -- ^ PKCS5: PKCS7 with hardcoded size of 8 + | PKCS7 Int -- ^ PKCS7 with padding size between 1 and 255 + deriving (Show, Eq) + +-- | Apply some pad to a bytearray +pad :: ByteArray byteArray => Format -> byteArray -> byteArray +pad PKCS5 bin = pad (PKCS7 8) bin +pad (PKCS7 sz) bin = bin `B.append` paddingString + where + paddingString = B.replicate paddingByte (fromIntegral paddingByte) + paddingByte = sz - (B.length bin `mod` sz) + +-- | Try to remove some padding from a bytearray. +unpad :: ByteArray byteArray => Format -> byteArray -> Maybe byteArray +unpad PKCS5 bin = unpad (PKCS7 8) bin +unpad (PKCS7 sz) bin + | len == 0 = Nothing + | (len `mod` sz) /= 0 = Nothing + | paddingSz < 1 || paddingSz > len = Nothing + | paddingWitness `B.constEq` padding = Just content + | otherwise = Nothing + where + len = B.length bin + paddingByte = B.index bin (len - 1) + paddingSz = fromIntegral paddingByte + (content, padding) = B.splitAt (len - paddingSz) bin + paddingWitness = B.replicate paddingSz paddingByte :: Bytes diff --git a/cryptonite.cabal b/cryptonite.cabal index a8e0e20..556a75f 100644 --- a/cryptonite.cabal +++ b/cryptonite.cabal @@ -83,6 +83,7 @@ Library Crypto.Cipher.TripleDES Crypto.Cipher.Types Crypto.Data.AFIS + Crypto.Data.Padding Crypto.Error Crypto.MAC.Poly1305 Crypto.MAC.HMAC diff --git a/tests/Padding.hs b/tests/Padding.hs new file mode 100644 index 0000000..7a6e7fd --- /dev/null +++ b/tests/Padding.hs @@ -0,0 +1,26 @@ +{-# LANGUAGE OverloadedStrings #-} +module Padding (tests) where + +import qualified Data.ByteString as B +import Imports +import Crypto.Error + +import Crypto.Data.Padding + +cases = + [ ("abcdef", 8, "abcdef\x02\x02") + , ("abcd", 4, "abcd\x04\x04\x04\x04") + , ("xyze", 5, "xyze\x01") + ] + +--instance Arbitrary where + +testPad :: Int -> (B.ByteString, Int, B.ByteString) -> TestTree +testPad n (inp, sz, padded) = + testCase (show n) $ propertyHoldCase [ eqTest "padded" padded (pad (PKCS7 sz) inp) + , eqTest "unpadded" (Just inp) (unpad (PKCS7 sz) padded) + ] + +tests = testGroup "Padding" + [ testGroup "Cases" $ map (uncurry testPad) (zip [1..] cases) + ] diff --git a/tests/Tests.hs b/tests/Tests.hs index ebba3fd..bab5508 100644 --- a/tests/Tests.hs +++ b/tests/Tests.hs @@ -24,10 +24,12 @@ import qualified KAT_RC4 import qualified KAT_TripleDES -- misc -------------------------------- import qualified KAT_AFIS +import qualified Padding tests = testGroup "cryptonite" [ Number.tests , Hash.tests + , Padding.tests , testGroup "MAC" [ Poly1305.tests , KAT_HMAC.tests