-- Hoogle documentation, generated by Haddock -- See Hoogle, http://www.haskell.org/hoogle/ -- | Typed encryption with persistent support -- -- You can find documentation at hackage @package encryptable @version 0.1 -- | Data encryption is common security-related practice in database usage. -- One of the negative side effects of encryption is that typed data in -- its encrypted form becomes untyped and usually exists in form of -- ByteString or similar blind type. Operations with untyped data -- are very error-prone and should be avoided. This library proposes the -- way to fix it. -- -- Let's have an example of User sum type where his -- Login is not sensitive type, but Address is -- sensitive. Address should never be shown and should be stored -- only in encrypted form. -- --
--   newtype Login
--     = Login Text
--     deriving newtype (Eq, Arbitrary, Show, PersistField, PersistFieldSql)
--   
--   newtype Address
--     = Address Text
--     deriving newtype (Eq, Arbitrary, Encryptable ByteString UnicodeException)
--   
--   instance Show Address where
--     show = const "SECRET"
--   
--   data User = User {login :: Login, address :: Address}
--     deriving (Eq, Generic, Show)
--   
--   instance Arbitrary User where
--     arbitrary = genericArbitrary
--     shrink = genericShrink
--   
-- -- Note how easy we derived Encryptable ByteString -- UnicodeException class instance for Address type. -- Address is newtype around Text which already have this -- instance - so we just got it for free. -- GeneralizedNewtypeDeriving is a very powerful tool, indeed. -- Having this instance means that now we can encrypt Address to -- ByteString form and decrypt back with possible -- UnicodeException error (because not every encrypted -- ByteString represents valid Address). You can find -- more details in Encrypted, Encryptable and -- Encryptor documentation. -- -- Now let's define UserStorage type, representation of -- User stored in database. We will use Persistent -- library DSL for this. -- --
--   share
--     [mkPersist sqlSettings]
--     [persistLowerCase|
--       UserStorage
--         login Login
--         address (Encrypted ByteString UnicodeException Address)
--         UniqueUserStorage login
--     |]
--   
-- -- In spite of address database table column type is still just -- bytes, compiler knows that these bytes in reality are -- encrypted representation of Address value. -- -- Just for fun let's implement class instance to encrypt User -- value into UserStorage value. -- --
--   instance Encryptable UserStorage UnicodeException User where
--     encrypt c i x = Encrypted $ UserStorage (login x) $ encrypt c i (address x)
--     decrypt c i x0 = do
--       let x = coerce x0
--       a <- decrypt c i $ userStorageAddress x
--       return $ User (userStorageLogin x) a
--   
-- -- And then we can test property - User can be encrypted into -- UserStorage form and decrypted back. -- --
--   spec :: Spec
--   spec = before newEnv
--     $ it "UserStorage/User"
--     $ env -> property $ x -> do
--       let c = cipher env
--       let i = iv env
--       decrypt c i (encrypt c i x :: Encrypted UserStorage UnicodeException User)
--         `shouldBe` Right x
--   
module Data.Encryptable -- | Value of this type represents value of type a (phantom) -- encrypted in form of value of type b (non-phantom) which can -- cause error of type e (phantom) in case where a -- constructor fails after decryption. This design promotes usage of -- smart constructors. newtype Encrypted b e a Encrypted :: b -> Encrypted b e a -- | Class represents the idea of typed symmetric encryption and decryption class Encryptable b e a encrypt :: (Encryptable b e a, BlockCipher c) => c -> IV c -> a -> Encrypted b e a decrypt :: (Encryptable b e a, BlockCipher c) => c -> IV c -> Encrypted b e a -> Either e a -- | Class represents one particular case of Encryptable where -- BlockCipher and IV (initial vector) are hidden inside -- m which often is some sort of "application" monad which -- implements this Encryptor class. Promotes finally tagless -- style. class Encryptor m encryptM :: (Encryptor m, Encryptable b e a) => a -> m (Encrypted b e a) decryptM :: (Encryptor m, Encryptable b e a) => Encrypted b e a -> m (Either e a) -- | Casts original phantom type a of Encrypted value to some -- other type c. Useful for building Encryptable instances -- on top of other already existing Encryptable instances. reType :: Encrypted b e a -> Encrypted b e c -- | A simple Either like type to represent a computation that can fail -- -- 2 possibles values are: -- -- data CryptoFailable a CryptoPassed :: a -> CryptoFailable a CryptoFailed :: CryptoError -> CryptoFailable a -- | Symmetric block cipher class class Cipher cipher => BlockCipher cipher -- | AES with 256 bit key data AES256 -- | an IV parametrized by the cipher data IV c -- | Initialize a cipher context from a key cipherInit :: (Cipher cipher, ByteArray key) => key -> CryptoFailable cipher -- | Create an IV for a specified block cipher makeIV :: (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c) getRandomBytes :: (MonadRandom m, ByteArray byteArray) => Int -> m byteArray instance Database.Persist.Sql.Class.PersistFieldSql b => Database.Persist.Sql.Class.PersistFieldSql (Data.Encryptable.Encrypted b e a) instance Database.Persist.Class.PersistField.PersistField b => Database.Persist.Class.PersistField.PersistField (Data.Encryptable.Encrypted b e a) instance Data.Encryptable.Encryptable Data.ByteString.Internal.ByteString e Data.ByteString.Internal.ByteString instance Data.Encryptable.Encryptable Data.ByteString.Internal.ByteString e Data.ByteString.Lazy.Internal.ByteString instance Data.Encryptable.Encryptable Data.ByteString.Internal.ByteString Data.Text.Encoding.Error.UnicodeException Data.Text.Internal.Text instance Data.Encryptable.Encryptable Data.ByteString.Internal.ByteString Data.Text.Encoding.Error.UnicodeException Data.Text.Internal.Lazy.Text instance (Data.Traversable.Traversable f, Data.Encryptable.Encryptable b e a) => Data.Encryptable.Encryptable (f b) e (f a)