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
- Rename 'Data.CryptoID.Poly' to 'Data.CryptoID.ByteString'
- Introduce 'Data.CryptoID.Poly' doing actual serialization
@ -12,3 +16,4 @@
# 0.0.0
First published version

View File

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

View File

@ -29,6 +29,7 @@ import Data.Binary.Put
import Data.Binary.Get
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Char8 as ByteString.Char
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'
--
-- 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
= AlgorithmError CryptoError
-- ^ One of the underlying cryptographic algorithms
-- ('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
-- ^ The length of the digest produced by 'CryptoHash' does
-- not match the block size of 'CryptoCipher'.
@ -112,10 +122,12 @@ data CryptoIDError
--
-- This error should not occur and is included primarily
-- for sake of totality.
| CiphertextConversionFailed
-- ^ The produced 'ByteString' is the wrong length for conversion into a
-- ciphertext.
| DeserializationError (Lazy.ByteString, ByteOffset, String)
| CiphertextConversionFailed ByteString
-- ^ The produced 'ByteString' is the wrong length for deserialization into
-- a ciphertext.
--
-- The offending 'ByteString' is included.
| DeserializationError
-- ^ The plaintext obtained by decrypting a ciphertext with the given
-- 'CryptoIDKey' in the context of the @namespace@ could not be
-- deserialized into a value of the expected @payload@-type.
@ -183,8 +195,8 @@ encrypt :: forall m namespace.
encrypt (keyMaterial -> key) plaintext = do
cipher <- cryptoFailable (cipherInit key :: CryptoFailable CryptoCipher)
namespace <- namespace' (Proxy :: Proxy namespace)
when (ByteArray.length plaintext `mod` blockSize cipher /= 0) $
throwM CiphertextConversionFailed
when (ByteString.length plaintext `mod` blockSize cipher /= 0) $
throwM . PlaintextIsWrongLength $ ByteString.length 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.Monoid
import Data.ByteString (ByteString)
import qualified Data.ByteString as ByteString
import qualified Data.ByteString.Lazy as Lazy.ByteString
import GHC.TypeLits
import Control.Monad
import Control.Monad.Catch (MonadThrow(..))
@ -50,10 +54,22 @@ encrypt :: forall a m c namespace.
( KnownSymbol namespace
, MonadThrow m
, Binary a
) => (ByteString -> m c) -> CryptoIDKey -> a -> m (CryptoID namespace c)
encrypt encode' key plaintext = do
cID <- ByteString.encrypt key . Lazy.ByteString.toStrict $ encode plaintext
) => 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)
-> (ByteString -> m c)
-> 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
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
@ -67,7 +83,7 @@ decrypt decode key cID = do
plaintext <- Lazy.ByteString.fromStrict <$> ByteString.decrypt key cID'
case decodeOrFail plaintext of
Left err -> throwM $ DeserializationError err
Left _ -> throwM DeserializationError
Right (rem, _, res)
| Lazy.ByteString.all (== 0) rem -> return res
| 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
- Switch to using the new 'Data.CryptoID.Poly'

View File

@ -53,7 +53,7 @@ encrypt :: forall a m namespace.
, Binary a
, MonadThrow m
) => 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
@ -68,7 +68,7 @@ decrypt :: forall a m namespace.
) => CryptoIDKey -> CryptoUUID namespace -> m a
decrypt = Poly.decrypt $ check . decodeOrFail . toByteString
where
check (Left err) = throwM $ DeserializationError err
check (Left _) = throwM DeserializationError
check (Right (rem, _, res))
| Lazy.ByteString.all (== 0) rem = return res
| otherwise = throwM InvalidNamespaceDetected

View File

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