crypto-cipher-types (empty) → 0.0.1
raw patch · 7 files changed
+455/−0 lines, 7 filesdep +HUnitdep +QuickCheckdep +basesetup-changed
Dependencies added: HUnit, QuickCheck, base, byteable, bytestring, crypto-cipher-types, mtl, securemem, test-framework, test-framework-hunit, test-framework-quickcheck2
Files
- Crypto/Cipher/Types.hs +303/−0
- Crypto/Cipher/Types/GF.hs +49/−0
- LICENSE +27/−0
- README.md +4/−0
- Setup.hs +2/−0
- crypto-cipher-types.cabal +45/−0
- tests/Tests.hs +25/−0
+ Crypto/Cipher/Types.hs view
@@ -0,0 +1,303 @@+-- |+-- Module : Crypto.Cipher.Types+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : Stable+-- Portability : Excellent+--+-- symmetric cipher basic types+--+{-# LANGUAGE DeriveDataTypeable #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE ExistentialQuantification #-}+module Crypto.Cipher.Types+ (+ -- * Cipher classes+ Cipher(..)+ , BlockCipher(..)+ , StreamCipher(..)+ , DataUnitOffset+ , AEAD(..)+ , AEADState(..)+ , AEADMode(..)+ , AEADModeImpl(..)+ -- * AEAD+ , aeadAppendHeader+ , aeadEncrypt+ , aeadDecrypt+ , aeadFinalize+ , aeadSimpleEncrypt+ , aeadSimpleDecrypt+ -- * Key type and constructor+ , Key+ , makeKey+ -- * Initial Vector type and constructor+ , IV+ , makeIV+ , nullIV+ , ivAdd+ -- * Authentification Tag+ , AuthTag(..)+ ) where++import Data.SecureMem+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import Data.Byteable+import Data.Word+import Data.Bits (shiftR, xor)+import Crypto.Cipher.Types.GF++-- | Offset inside an XTS data unit, measured in block size.+type DataUnitOffset = Word32++-- | Symmetric cipher class.+class Cipher cipher where+ -- | Initialize a cipher context from a key+ cipherInit :: Key cipher -> cipher+ -- | Cipher name+ cipherName :: cipher -> String+ -- | return the size of the key required for this cipher.+ -- Some cipher accept any size for key+ cipherKeySize :: cipher -> Maybe Int++-- | Symmetric stream cipher class+class Cipher cipher => StreamCipher cipher where+ -- | Encrypt using the stream cipher+ streamEncrypt :: cipher -> ByteString -> (ByteString, cipher)+ -- | Decrypt using the stream cipher+ streamDecrypt :: cipher -> ByteString -> (ByteString, cipher)++-- | Symmetric block cipher class+class Cipher cipher => BlockCipher cipher where+ -- | Return the size of block required for this block cipher+ blockSize :: cipher -> Int++ -- | Encrypt using the ECB mode.+ --+ -- input need to be a multiple of the blocksize+ ecbEncrypt :: cipher -> ByteString -> ByteString+ -- | Decrypt using the ECB mode.+ --+ -- input need to be a multiple of the blocksize+ ecbDecrypt :: cipher -> ByteString -> ByteString++ -- | encrypt using the CBC mode.+ --+ -- input need to be a multiple of the blocksize+ cbcEncrypt :: cipher -> IV cipher -> ByteString -> ByteString+ cbcEncrypt = cbcEncryptGeneric+ -- | decrypt using the CBC mode.+ --+ -- input need to be a multiple of the blocksize+ cbcDecrypt :: cipher -> IV cipher -> ByteString -> ByteString+ cbcDecrypt = cbcDecryptGeneric++ -- | combine using the CTR mode.+ --+ -- CTR mode produce a stream of randomized data that is combined+ -- (by XOR operation) with the input stream.+ --+ -- encryption and decryption are the same operation.+ --+ -- input can be of any size+ ctrCombine :: cipher -> IV cipher -> ByteString -> ByteString+ ctrCombine = ctrCombineGeneric++ -- | encrypt using the XTS mode.+ --+ -- input need to be a multiple of the blocksize+ xtsEncrypt :: (cipher, cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString+ xtsEncrypt = xtsEncryptGeneric+ -- | decrypt using the XTS mode.+ --+ -- input need to be a multiple of the blocksize+ xtsDecrypt :: (cipher, cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString+ xtsDecrypt = xtsDecryptGeneric++ -- | Initialize a new AEAD State+ --+ -- When Nothing is returns, it means the mode is not handled.+ aeadInit :: Byteable iv => AEADMode -> cipher -> iv -> Maybe (AEAD cipher)+ aeadInit _ _ _ = Nothing++-- | AEAD Mode+data AEADMode =+ AEAD_OCB+ | AEAD_CCM+ | AEAD_EAX+ | AEAD_CWC+ | AEAD_GCM+ deriving (Show,Eq)++-- | Authenticated Encryption with Associated Data algorithms+data AEAD cipher = AEAD cipher (AEADState cipher)++-- | Wrapper for any AEADState+data AEADState cipher = forall st . AEADModeImpl cipher st => AEADState st++-- | Class of AEAD Mode implementation+class BlockCipher cipher => AEADModeImpl cipher state where+ aeadStateAppendHeader :: cipher -> state -> ByteString -> state+ aeadStateEncrypt :: cipher -> state -> ByteString -> (ByteString, state)+ aeadStateDecrypt :: cipher -> state -> ByteString -> (ByteString, state)+ aeadStateFinalize :: cipher -> state -> Int -> AuthTag++-- | Append associated data into the AEAD state+aeadAppendHeader :: BlockCipher a => AEAD a -> ByteString -> AEAD a+aeadAppendHeader (AEAD cipher (AEADState state)) bs =+ AEAD cipher $ AEADState (aeadStateAppendHeader cipher state bs)++-- | Encrypt input and append into the AEAD state+aeadEncrypt :: BlockCipher a => AEAD a -> ByteString -> (ByteString, AEAD a)+aeadEncrypt (AEAD cipher (AEADState state)) input = (output, AEAD cipher (AEADState nst))+ where (output, nst) = aeadStateEncrypt cipher state input++-- | Decrypt input and append into the AEAD state+aeadDecrypt :: BlockCipher a => AEAD a -> ByteString -> (ByteString, AEAD a)+aeadDecrypt (AEAD cipher (AEADState state)) input = (output, AEAD cipher (AEADState nst))+ where (output, nst) = aeadStateDecrypt cipher state input++-- | Finalize the AEAD state and create an authentification tag+aeadFinalize :: BlockCipher a => AEAD a -> Int -> AuthTag+aeadFinalize (AEAD cipher (AEADState state)) len =+ aeadStateFinalize cipher state len++-- | Simple AEAD encryption+aeadSimpleEncrypt :: BlockCipher a+ => AEAD a -- ^ A new AEAD Context+ -> B.ByteString -- ^ Optional Authentified Header+ -> B.ByteString -- ^ Optional Plaintext+ -> Int -- ^ Tag length+ -> (AuthTag, B.ByteString) -- ^ Authentification tag and ciphertext+aeadSimpleEncrypt aeadIni header input taglen = (tag, output)+ where aead = aeadAppendHeader aeadIni header+ (output, aeadFinal) = aeadEncrypt aead input+ tag = aeadFinalize aeadFinal taglen++-- | Simple AEAD decryption+aeadSimpleDecrypt :: BlockCipher a+ => AEAD a -- ^ A new AEAD Context+ -> B.ByteString -- ^ Optional Authentified Header+ -> B.ByteString -- ^ Optional Plaintext+ -> AuthTag -- ^ Tag length+ -> Maybe B.ByteString -- ^ Plaintext+aeadSimpleDecrypt aeadIni header input authTag+ | tag == authTag = Just output+ | otherwise = Nothing+ where aead = aeadAppendHeader aeadIni header+ (output, aeadFinal) = aeadDecrypt aead input+ tag = aeadFinalize aeadFinal (byteableLength authTag)++-- | a Key parametrized by the cipher+newtype Key c = Key SecureMem deriving (Eq)++instance ToSecureMem (Key c) where+ toSecureMem (Key sm) = sm+instance Byteable (Key c) where+ toBytes (Key sm) = toBytes sm++-- | an IV parametrized by the cipher+newtype IV c = IV ByteString deriving (Eq)++instance Byteable (IV c) where+ toBytes (IV sm) = sm++-- | Authentification Tag for AE cipher mode+newtype AuthTag = AuthTag ByteString+ deriving (Show)++instance Eq AuthTag where+ (AuthTag a) == (AuthTag b) = constEqBytes a b+instance Byteable AuthTag where+ toBytes (AuthTag bs) = bs++-- | Create an IV for a specified block cipher+makeIV :: (Byteable b, BlockCipher c) => b -> Maybe (IV c)+makeIV b = toIV undefined+ where toIV :: BlockCipher c => c -> Maybe (IV c)+ toIV cipher+ | byteableLength b == sz = Just (IV $ toBytes b)+ | otherwise = Nothing+ where sz = blockSize cipher++-- | Create an IV that is effectively representing the number 0+nullIV :: BlockCipher c => IV c+nullIV = toIV undefined+ where toIV :: BlockCipher c => c -> IV c+ toIV cipher = IV $ B.replicate (blockSize cipher) 0++-- | Increment an IV by a number.+--+-- Assume the IV is in Big Endian format.+ivAdd :: BlockCipher c => IV c -> Int -> IV c+ivAdd (IV b) i = IV $ snd $ B.mapAccumR addCarry i b+ where addCarry :: Int -> Word8 -> (Int, Word8)+ addCarry acc w+ | acc == 0 = (0, w)+ | otherwise = let (hi,lo) = acc `divMod` 256+ nw = lo + (fromIntegral w)+ in (hi + (nw `shiftR` 8), fromIntegral nw)++-- | Create a Key for a specified cipher+makeKey :: (ToSecureMem b, Cipher c) => b -> Maybe (Key c)+makeKey b = toKey undefined (toSecureMem b)+ where toKey :: Cipher c => c -> SecureMem -> Maybe (Key c)+ toKey cipher sm =+ case cipherKeySize cipher of+ Nothing -> Just $ Key sm+ Just sz | sz == byteableLength sm -> Just $ Key sm+ | otherwise -> Nothing++cbcEncryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString+cbcEncryptGeneric cipher (IV ivini) input = B.concat $ doEnc ivini $ chunk (blockSize cipher) input+ where doEnc _ [] = []+ doEnc iv (i:is) =+ let o = ecbEncrypt cipher $ bxor iv i+ in o : doEnc o is++cbcDecryptGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString+cbcDecryptGeneric cipher (IV ivini) input = B.concat $ doDec ivini $ chunk (blockSize cipher) input+ where doDec _ [] = []+ doDec iv (i:is) =+ let o = bxor iv $ ecbDecrypt cipher i+ in o : doDec i is++ctrCombineGeneric :: BlockCipher cipher => cipher -> IV cipher -> ByteString -> ByteString+ctrCombineGeneric cipher ivini input = B.concat $ doCnt ivini $ chunk (blockSize cipher) input+ where doCnt _ [] = []+ doCnt iv (i:is) =+ let ivEnc = ecbEncrypt cipher (toBytes iv)+ in bxor i ivEnc : doCnt (ivAdd iv 1) is++xtsEncryptGeneric :: BlockCipher cipher => (cipher,cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString+xtsEncryptGeneric = xtsGeneric ecbEncrypt++xtsDecryptGeneric :: BlockCipher cipher => (cipher,cipher) -> IV cipher -> DataUnitOffset -> ByteString -> ByteString+xtsDecryptGeneric = xtsGeneric ecbDecrypt++xtsGeneric :: BlockCipher cipher+ => (cipher -> B.ByteString -> B.ByteString)+ -> (cipher,cipher)+ -> IV cipher+ -> DataUnitOffset+ -> ByteString+ -> ByteString+xtsGeneric f (cipher, tweakCipher) iv sPoint input = B.concat $ doXts iniTweak $ chunk (blockSize cipher) input+ where encTweak = ecbEncrypt tweakCipher (toBytes iv)+ iniTweak = iterate xtsGFMul encTweak !! fromIntegral sPoint+ doXts _ [] = []+ doXts tweak (i:is) =+ let o = bxor (f cipher $ bxor i tweak) tweak+ in o : doXts (xtsGFMul tweak) is+++chunk :: Int -> ByteString -> [ByteString]+chunk sz bs = split bs+ where split b | B.length b <= sz = [b]+ | otherwise =+ let (b1, b2) = B.splitAt sz b+ in b1 : split b2++bxor :: ByteString -> ByteString -> ByteString+bxor src dst = B.pack $ B.zipWith xor src dst
+ Crypto/Cipher/Types/GF.hs view
@@ -0,0 +1,49 @@+-- |+-- Module : Crypto.Cipher.Types.GF+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : Stable+-- Portability : Excellent+--+-- Slow Galois Field arithmetic for generic XTS and GCM implementation+--+module Crypto.Cipher.Types.GF+ (+ -- * XTS support+ xtsGFMul+ ) where++import Control.Applicative+import Data.ByteString (ByteString)+import qualified Data.ByteString as B+import qualified Data.ByteString.Internal as B+import Data.Byteable+import Foreign.Storable+import Foreign.Ptr+import Data.Word+import Data.Bits++-- block size need to be 128 bits.+--+-- FIXME: add support for big endian.+xtsGFMul :: ByteString -> ByteString+xtsGFMul b+ | B.length b == 16 = B.unsafeCreate (B.length b) $ \dst ->+ withBytePtr b $ \src -> do+ (hi,lo) <- gf <$> peek (castPtr src) <*> peek (castPtr src `plusPtr` 8)+ poke (castPtr dst) lo+ poke (castPtr dst `plusPtr` 8) hi+ | otherwise = error "unsupported block size in GF"+ where gf :: Word64 -> Word64 -> (Word64, Word64)+ gf srcLo srcHi =+ ((if carryLo then (.|. 1) else id) (srcHi `shiftL` 1)+ ,(if carryHi then xor 0x87 else id) $ (srcLo `shiftL` 1)+ )+ where carryHi = srcHi `testBit` 63 + carryLo = srcLo `testBit` 63+{-+ const uint64_t gf_mask = cpu_to_le64(0x8000000000000000ULL);+ uint64_t r = ((a->q[1] & gf_mask) ? cpu_to_le64(0x87) : 0);+ a->q[1] = cpu_to_le64((le64_to_cpu(a->q[1]) << 1) | (a->q[0] & gf_mask ? 1 : 0));+ a->q[0] = cpu_to_le64(le64_to_cpu(a->q[0]) << 1) ^ r;+-}
+ LICENSE view
@@ -0,0 +1,27 @@+Copyright (c) 2013 Vincent Hanquez <vincent@snarc.org>++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:+1. Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.+2. Redistributions in binary form must reproduce the above copyright+ notice, this list of conditions and the following disclaimer in the+ documentation and/or other materials provided with the distribution.+3. Neither the name of the author nor the names of his contributors+ may be used to endorse or promote products derived from this software+ without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS AS IS'' AND+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+SUCH DAMAGE.
+ README.md view
@@ -0,0 +1,4 @@+crypto-cipher-types+===================++Documentation: [crypto-cipher-types on hackage](http://hackage.haskell.org/package/crypto-cipher-types)
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ crypto-cipher-types.cabal view
@@ -0,0 +1,45 @@+Name: crypto-cipher-types+Version: 0.0.1+Synopsis: Generic cryptography cipher types+Description: Generic cryptography cipher types+License: BSD3+License-file: LICENSE+Copyright: Vincent Hanquez <vincent@snarc.org>+Author: Vincent Hanquez <vincent@snarc.org>+Maintainer: vincent@snarc.org+Category: Cryptography+Stability: experimental+Build-Type: Simple+Homepage: http://github.com/vincenthz/hs-crypto-cipher+Cabal-Version: >=1.8+data-files: README.md++Library+ Exposed-modules: Crypto.Cipher.Types+ Other-modules: Crypto.Cipher.Types.GF+ Build-depends: base >= 4 && < 5+ , bytestring+ , byteable >= 0.1.1+ , securemem >= 0.1.1+ ghc-options: -Wall -fwarn-tabs++Test-Suite test-crypto-cipher-types+ type: exitcode-stdio-1.0+ hs-source-dirs: tests ../tests+ Main-is: Tests.hs+ Build-Depends: base >= 3 && < 5+ , bytestring+ , byteable+ , crypto-cipher-types+ , mtl+ , QuickCheck >= 2+ , HUnit+ , test-framework+ , test-framework-quickcheck2+ , test-framework-hunit+ ghc-options: -Wall -fno-warn-orphans -fno-warn-missing-signatures++source-repository head+ type: git+ location: git://github.com/vincenthz/hs-crypto-cipher+ subdir: types
+ tests/Tests.hs view
@@ -0,0 +1,25 @@+{-# LANGUAGE ViewPatterns #-}+module Main where++import Test.Framework (defaultMain)+import Crypto.Cipher.Types+import Crypto.Cipher.Tests+import qualified Data.ByteString as B+import Data.Bits (xor)++-- | the XOR cipher is so awesome that it doesn't need any key or state.+data XorCipher = XorCipher++instance Cipher XorCipher where+ cipherInit _ = XorCipher+ cipherName _ = "xor"+ cipherKeySize _ = Just 0++instance BlockCipher XorCipher where+ blockSize _ = 16+ ecbEncrypt _ b = B.pack $ B.zipWith xor (B.replicate (B.length b) 0xa5) b+ ecbDecrypt _ b = B.pack $ B.zipWith xor (B.replicate (B.length b) 0xa5) b++tests = testBlockCipher defaultKATs (undefined :: XorCipher)++main = defaultMain [tests]