packages feed

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 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]