encryptable-0.1: Typed encryption with persistent support

Safe HaskellNone
LanguageHaskell2010

Data.Encryptable

Contents

Description

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
Synopsis

Type

newtype Encrypted b e a Source #

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.

Constructors

Encrypted b 
Instances
PersistField b => PersistField (Encrypted b e a) Source # 
Instance details

Defined in Data.Encryptable

PersistFieldSql b => PersistFieldSql (Encrypted b e a) Source # 
Instance details

Defined in Data.Encryptable

Methods

sqlType :: Proxy (Encrypted b e a) -> SqlType #

Class

class Encryptable b e a where Source #

Class represents the idea of typed symmetric encryption and decryption

Methods

encrypt :: BlockCipher c => c -> IV c -> a -> Encrypted b e a Source #

decrypt :: BlockCipher c => c -> IV c -> Encrypted b e a -> Either e a Source #

Instances
Encryptable ByteString e ByteString Source # 
Instance details

Defined in Data.Encryptable

Encryptable ByteString e ByteString Source # 
Instance details

Defined in Data.Encryptable

Encryptable ByteString UnicodeException Text Source # 
Instance details

Defined in Data.Encryptable

Encryptable ByteString UnicodeException Text Source # 
Instance details

Defined in Data.Encryptable

(Traversable f, Encryptable b e a) => Encryptable (f b) e (f a) Source # 
Instance details

Defined in Data.Encryptable

Methods

encrypt :: BlockCipher c => c -> IV c -> f a -> Encrypted (f b) e (f a) Source #

decrypt :: BlockCipher c => c -> IV c -> Encrypted (f b) e (f a) -> Either e (f a) Source #

class Encryptor m where Source #

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.

Methods

encryptM :: Encryptable b e a => a -> m (Encrypted b e a) Source #

decryptM :: Encryptable b e a => Encrypted b e a -> m (Either e a) Source #

Utility

reType :: Encrypted b e a -> Encrypted b e c Source #

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.

Re-export

data CryptoFailable a #

A simple Either like type to represent a computation that can fail

2 possibles values are:

  • CryptoPassed : The computation succeeded, and contains the result of the computation
  • CryptoFailed : The computation failed, and contains the cryptographic error associated
Instances
Monad CryptoFailable 
Instance details

Defined in Crypto.Error.Types

Functor CryptoFailable 
Instance details

Defined in Crypto.Error.Types

Methods

fmap :: (a -> b) -> CryptoFailable a -> CryptoFailable b #

(<$) :: a -> CryptoFailable b -> CryptoFailable a #

Applicative CryptoFailable 
Instance details

Defined in Crypto.Error.Types

MonadFailure CryptoFailable 
Instance details

Defined in Crypto.Error.Types

Associated Types

type Failure CryptoFailable :: Type #

Eq a => Eq (CryptoFailable a) 
Instance details

Defined in Crypto.Error.Types

Show a => Show (CryptoFailable a) 
Instance details

Defined in Crypto.Error.Types

type Failure CryptoFailable 
Instance details

Defined in Crypto.Error.Types

class Cipher cipher => BlockCipher cipher #

Symmetric block cipher class

Minimal complete definition

blockSize, ecbEncrypt, ecbDecrypt

Instances
BlockCipher AES128 
Instance details

Defined in Crypto.Cipher.AES

Methods

blockSize :: AES128 -> Int #

ecbEncrypt :: ByteArray ba => AES128 -> ba -> ba #

ecbDecrypt :: ByteArray ba => AES128 -> ba -> ba #

cbcEncrypt :: ByteArray ba => AES128 -> IV AES128 -> ba -> ba #

cbcDecrypt :: ByteArray ba => AES128 -> IV AES128 -> ba -> ba #

cfbEncrypt :: ByteArray ba => AES128 -> IV AES128 -> ba -> ba #

cfbDecrypt :: ByteArray ba => AES128 -> IV AES128 -> ba -> ba #

ctrCombine :: ByteArray ba => AES128 -> IV AES128 -> ba -> ba #

aeadInit :: ByteArrayAccess iv => AEADMode -> AES128 -> iv -> CryptoFailable (AEAD AES128) #

BlockCipher AES192 
Instance details

Defined in Crypto.Cipher.AES

Methods

blockSize :: AES192 -> Int #

ecbEncrypt :: ByteArray ba => AES192 -> ba -> ba #

ecbDecrypt :: ByteArray ba => AES192 -> ba -> ba #

cbcEncrypt :: ByteArray ba => AES192 -> IV AES192 -> ba -> ba #

cbcDecrypt :: ByteArray ba => AES192 -> IV AES192 -> ba -> ba #

cfbEncrypt :: ByteArray ba => AES192 -> IV AES192 -> ba -> ba #

cfbDecrypt :: ByteArray ba => AES192 -> IV AES192 -> ba -> ba #

ctrCombine :: ByteArray ba => AES192 -> IV AES192 -> ba -> ba #

aeadInit :: ByteArrayAccess iv => AEADMode -> AES192 -> iv -> CryptoFailable (AEAD AES192) #

BlockCipher AES256 
Instance details

Defined in Crypto.Cipher.AES

Methods

blockSize :: AES256 -> Int #

ecbEncrypt :: ByteArray ba => AES256 -> ba -> ba #

ecbDecrypt :: ByteArray ba => AES256 -> ba -> ba #

cbcEncrypt :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

cbcDecrypt :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

cfbEncrypt :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

cfbDecrypt :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

ctrCombine :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

aeadInit :: ByteArrayAccess iv => AEADMode -> AES256 -> iv -> CryptoFailable (AEAD AES256) #

data AES256 #

AES with 256 bit key

Instances
NFData AES256 
Instance details

Defined in Crypto.Cipher.AES

Methods

rnf :: AES256 -> () #

BlockCipher AES256 
Instance details

Defined in Crypto.Cipher.AES

Methods

blockSize :: AES256 -> Int #

ecbEncrypt :: ByteArray ba => AES256 -> ba -> ba #

ecbDecrypt :: ByteArray ba => AES256 -> ba -> ba #

cbcEncrypt :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

cbcDecrypt :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

cfbEncrypt :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

cfbDecrypt :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

ctrCombine :: ByteArray ba => AES256 -> IV AES256 -> ba -> ba #

aeadInit :: ByteArrayAccess iv => AEADMode -> AES256 -> iv -> CryptoFailable (AEAD AES256) #

BlockCipher128 AES256 
Instance details

Defined in Crypto.Cipher.AES

Methods

xtsEncrypt :: ByteArray ba => (AES256, AES256) -> IV AES256 -> DataUnitOffset -> ba -> ba #

xtsDecrypt :: ByteArray ba => (AES256, AES256) -> IV AES256 -> DataUnitOffset -> ba -> ba #

Cipher AES256 
Instance details

Defined in Crypto.Cipher.AES

data IV c #

an IV parametrized by the cipher

Instances
Eq (IV c) 
Instance details

Defined in Crypto.Cipher.Types.Block

Methods

(==) :: IV c -> IV c -> Bool #

(/=) :: IV c -> IV c -> Bool #

BlockCipher c => ByteArrayAccess (IV c) 
Instance details

Defined in Crypto.Cipher.Types.Block

Methods

length :: IV c -> Int #

withByteArray :: IV c -> (Ptr p -> IO a) -> IO a #

copyByteArrayToPtr :: IV c -> Ptr p -> IO () #

cipherInit :: (Cipher cipher, ByteArray key) => key -> CryptoFailable cipher #

Initialize a cipher context from a key

makeIV :: (ByteArrayAccess b, BlockCipher c) => b -> Maybe (IV c) #

Create an IV for a specified block cipher

getRandomBytes :: (MonadRandom m, ByteArray byteArray) => Int -> m byteArray #