Fix padding

This commit is contained in:
Gregor Kleen 2017-10-10 23:44:36 +02:00
parent 55f1382401
commit 2518872872
7 changed files with 51 additions and 15 deletions

View File

@ -1,3 +1,7 @@
# 0.3.0.0
- Better exception type (does no longer leak private information)
- 'Data.CryptoID.Poly' now supports padding the plaintext to a certain length before encryption
# 0.2.0.0 # 0.2.0.0
- Rename 'Data.CryptoID.Poly' to 'Data.CryptoID.ByteString' - Rename 'Data.CryptoID.Poly' to 'Data.CryptoID.ByteString'
- Introduce 'Data.CryptoID.Poly' doing actual serialization - Introduce 'Data.CryptoID.Poly' doing actual serialization
@ -12,3 +16,4 @@
# 0.0.0 # 0.0.0
First published version First published version

View File

@ -1,5 +1,5 @@
name: cryptoids name: cryptoids
version: 0.2.0.0 version: 0.3.0.0
synopsis: Reversable and secure encoding of object ids as a bytestring synopsis: Reversable and secure encoding of object ids as a bytestring
license: BSD3 license: BSD3
license-file: LICENSE license-file: LICENSE

View File

@ -29,6 +29,7 @@ import Data.Binary.Put
import Data.Binary.Get import Data.Binary.Get
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Char8 as ByteString.Char import qualified Data.ByteString.Char8 as ByteString.Char
import qualified Data.ByteString.Lazy as Lazy (ByteString) import qualified Data.ByteString.Lazy as Lazy (ByteString)
@ -100,10 +101,19 @@ instance Binary CryptoIDKey where
-- | Error cases that can be encountered during 'encrypt' and 'decrypt' -- | Error cases that can be encountered during 'encrypt' and 'decrypt'
--
-- Care has been taken to ensure that presenting values of 'CryptoIDError' to an
-- attacker leaks no plaintext (it does leak information about the length of the
-- plaintext).
data CryptoIDError data CryptoIDError
= AlgorithmError CryptoError = AlgorithmError CryptoError
-- ^ One of the underlying cryptographic algorithms -- ^ One of the underlying cryptographic algorithms
-- ('CryptoHash' or 'CryptoCipher') failed. -- ('CryptoHash' or 'CryptoCipher') failed.
| PlaintextIsWrongLength Int
-- ^ The length of the plaintext is not a multiple of the block size of
-- 'CryptoCipher'
--
-- The length of the offending plaintext is included.
| NamespaceHashIsWrongLength ByteString | NamespaceHashIsWrongLength ByteString
-- ^ The length of the digest produced by 'CryptoHash' does -- ^ The length of the digest produced by 'CryptoHash' does
-- not match the block size of 'CryptoCipher'. -- not match the block size of 'CryptoCipher'.
@ -112,10 +122,12 @@ data CryptoIDError
-- --
-- This error should not occur and is included primarily -- This error should not occur and is included primarily
-- for sake of totality. -- for sake of totality.
| CiphertextConversionFailed | CiphertextConversionFailed ByteString
-- ^ The produced 'ByteString' is the wrong length for conversion into a -- ^ The produced 'ByteString' is the wrong length for deserialization into
-- ciphertext. -- a ciphertext.
| DeserializationError (Lazy.ByteString, ByteOffset, String) --
-- The offending 'ByteString' is included.
| DeserializationError
-- ^ The plaintext obtained by decrypting a ciphertext with the given -- ^ The plaintext obtained by decrypting a ciphertext with the given
-- 'CryptoIDKey' in the context of the @namespace@ could not be -- 'CryptoIDKey' in the context of the @namespace@ could not be
-- deserialized into a value of the expected @payload@-type. -- deserialized into a value of the expected @payload@-type.
@ -183,8 +195,8 @@ encrypt :: forall m namespace.
encrypt (keyMaterial -> key) plaintext = do encrypt (keyMaterial -> key) plaintext = do
cipher <- cryptoFailable (cipherInit key :: CryptoFailable CryptoCipher) cipher <- cryptoFailable (cipherInit key :: CryptoFailable CryptoCipher)
namespace <- namespace' (Proxy :: Proxy namespace) namespace <- namespace' (Proxy :: Proxy namespace)
when (ByteArray.length plaintext `mod` blockSize cipher /= 0) $ when (ByteString.length plaintext `mod` blockSize cipher /= 0) $
throwM CiphertextConversionFailed throwM . PlaintextIsWrongLength $ ByteString.length plaintext
return . CryptoID $ cbcEncrypt cipher namespace plaintext return . CryptoID $ cbcEncrypt cipher namespace plaintext

View File

@ -33,11 +33,15 @@ import qualified Data.CryptoID.ByteString as ByteString (encrypt, decrypt)
import Data.Binary import Data.Binary
import Data.Monoid
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Lazy as Lazy.ByteString import qualified Data.ByteString.Lazy as Lazy.ByteString
import GHC.TypeLits import GHC.TypeLits
import Control.Monad
import Control.Monad.Catch (MonadThrow(..)) import Control.Monad.Catch (MonadThrow(..))
@ -50,10 +54,22 @@ encrypt :: forall a m c namespace.
( KnownSymbol namespace ( KnownSymbol namespace
, MonadThrow m , MonadThrow m
, Binary a , Binary a
) => (ByteString -> m c) -> CryptoIDKey -> a -> m (CryptoID namespace c) ) => Maybe Int -- ^ Ensure the resulting ciphertext is of this size (needs to be a multiple of the block size of 'CryptoCipher' in bytes, otherwise an exception will be thrown at runtime)
encrypt encode' key plaintext = do -> (ByteString -> m c)
cID <- ByteString.encrypt key . Lazy.ByteString.toStrict $ encode plaintext -> CryptoIDKey
-> a
-> m (CryptoID namespace c)
encrypt pLength encode' key plaintext = do
cID <- ByteString.encrypt key <=< pad . Lazy.ByteString.toStrict $ encode plaintext
_ciphertext encode' cID _ciphertext encode' cID
where
pad str
| Just l <- pLength
, l' <= l = return $ str <> ByteString.replicate (l - l') 0
| Just _ <- pLength = throwM $ CiphertextConversionFailed str
| otherwise = return str
where
l' = ByteString.length str
-- | Decrypt a serialized value -- | Decrypt a serialized value
@ -67,7 +83,7 @@ decrypt decode key cID = do
plaintext <- Lazy.ByteString.fromStrict <$> ByteString.decrypt key cID' plaintext <- Lazy.ByteString.fromStrict <$> ByteString.decrypt key cID'
case decodeOrFail plaintext of case decodeOrFail plaintext of
Left err -> throwM $ DeserializationError err Left _ -> throwM DeserializationError
Right (rem, _, res) Right (rem, _, res)
| Lazy.ByteString.all (== 0) rem -> return res | Lazy.ByteString.all (== 0) rem -> return res
| otherwise -> throwM InvalidNamespaceDetected | otherwise -> throwM InvalidNamespaceDetected

View File

@ -1,3 +1,6 @@
# 1.2.0.0
- Pad plaintext before encryption, allowing encryption of payloads shorter than 128 bits
# 1.1.1.0 # 1.1.1.0
- Switch to using the new 'Data.CryptoID.Poly' - Switch to using the new 'Data.CryptoID.Poly'

View File

@ -53,7 +53,7 @@ encrypt :: forall a m namespace.
, Binary a , Binary a
, MonadThrow m , MonadThrow m
) => CryptoIDKey -> a -> m (CryptoUUID namespace) ) => CryptoIDKey -> a -> m (CryptoUUID namespace)
encrypt = Poly.encrypt $ maybe (throwM CiphertextConversionFailed) return . fromByteString . Lazy.ByteString.fromStrict encrypt = Poly.encrypt (Just 16) $ \str -> maybe (throwM $ CiphertextConversionFailed str) return . fromByteString $ Lazy.ByteString.fromStrict str
-- | Decrypt an arbitrary serializable value -- | Decrypt an arbitrary serializable value
@ -68,7 +68,7 @@ decrypt :: forall a m namespace.
) => CryptoIDKey -> CryptoUUID namespace -> m a ) => CryptoIDKey -> CryptoUUID namespace -> m a
decrypt = Poly.decrypt $ check . decodeOrFail . toByteString decrypt = Poly.decrypt $ check . decodeOrFail . toByteString
where where
check (Left err) = throwM $ DeserializationError err check (Left _) = throwM DeserializationError
check (Right (rem, _, res)) check (Right (rem, _, res))
| Lazy.ByteString.all (== 0) rem = return res | Lazy.ByteString.all (== 0) rem = return res
| otherwise = throwM InvalidNamespaceDetected | otherwise = throwM InvalidNamespaceDetected

View File

@ -1,5 +1,5 @@
name: uuid-crypto name: uuid-crypto
version: 1.1.1.0 version: 1.2.0.0
synopsis: Reversable and secure encoding of object ids as uuids synopsis: Reversable and secure encoding of object ids as uuids
license: BSD3 license: BSD3
license-file: LICENSE license-file: LICENSE
@ -28,7 +28,7 @@ library
other-extensions: ScopedTypeVariables other-extensions: ScopedTypeVariables
build-depends: base >=4.9 && <4.11 build-depends: base >=4.9 && <4.11
, cryptoids-types ==0.0.0 , cryptoids-types ==0.0.0
, cryptoids ==0.2.0.* , cryptoids ==0.3.0.*
, uuid >=1.3.13 && <1.4 , uuid >=1.3.13 && <1.4
, binary >=0.8.3.0 && <0.9 , binary >=0.8.3.0 && <0.9
, bytestring >=0.10.8.1 && <0.11 , bytestring >=0.10.8.1 && <0.11