File: BlockIO.hs

package info (click to toggle)
haskell-crypto-cipher-types 0.0.9-13
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 116 kB
  • sloc: haskell: 464; makefile: 2
file content (186 lines) | stat: -rw-r--r-- 6,671 bytes parent folder | download | duplicates (6)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
-- |
-- Module      : Crypto.Cipher.Types.Block
-- License     : BSD-style
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
-- Stability   : Stable
-- Portability : Excellent
--
-- block cipher basic types
--
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE ViewPatterns #-}
module Crypto.Cipher.Types.BlockIO
    ( BlockCipherIO(..)
    , PtrDest
    , PtrSource
    , PtrIV
    , BufferLength
    , onBlock
    ) where

import Control.Applicative
import Data.Word
import Data.ByteString (ByteString)
import qualified Data.ByteString.Internal as B (fromForeignPtr, memcpy)
import Data.Byteable
import Data.Bits (xor, Bits)
import Foreign.Storable (poke, peek, Storable)
--import Foreign.Ptr (plusPtr, Ptr, castPtr, nullPtr)
import Crypto.Cipher.Types.Block
import Foreign.Ptr
import Foreign.ForeignPtr (newForeignPtr_)

-- | pointer to the destination data
type PtrDest   = Ptr Word8

-- | pointer to the source data
type PtrSource = Ptr Word8

-- | pointer to the IV data
type PtrIV     = Ptr Word8

-- | Length of the pointed data
type BufferLength = Word32

-- | Symmetric block cipher class, mutable API
class BlockCipher cipher => BlockCipherIO cipher where
    -- | Encrypt using the ECB mode.
    --
    -- input need to be a multiple of the blocksize
    ecbEncryptMutable :: cipher -> PtrDest -> PtrSource -> BufferLength -> IO ()

    -- | Decrypt using the ECB mode.
    --
    -- input need to be a multiple of the blocksize
    ecbDecryptMutable :: cipher -> PtrDest -> PtrSource -> BufferLength -> IO ()

    -- | encrypt using the CBC mode.
    --
    -- input need to be a multiple of the blocksize
    cbcEncryptMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
    cbcEncryptMutable = cbcEncryptGeneric

    -- | decrypt using the CBC mode.
    --
    -- input need to be a multiple of the blocksize
    cbcDecryptMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
    cbcDecryptMutable = cbcDecryptGeneric

{-
    -- | encrypt using the CFB mode.
    --
    -- input need to be a multiple of the blocksize
    cfbEncryptMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
    cfbEncryptMutable = cfbEncryptGeneric

    -- | decrypt using the CFB mode.
    --
    -- input need to be a multiple of the blocksize
    cfbDecryptMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
    cfbDecryptMutable = cfbDecryptGeneric

    -- | 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
    ctrCombineMutable :: cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
    ctrCombineMutable = ctrCombineGeneric

    -- | encrypt using the XTS mode.
    --
    -- input need to be a multiple of the blocksize
    xtsEncryptMutable :: (cipher, cipher) -> PtrIV -> DataUnitOffset -> PtrDest -> PtrSource -> BufferLength -> IO ()
    xtsEncryptMutable = xtsEncryptGeneric
    -- | decrypt using the XTS mode.
    --
    -- input need to be a multiple of the blocksize
    xtsDecryptMutable :: (cipher, cipher) -> PtrIV -> DataUnitOffset -> PtrDest -> PtrSource -> BufferLength -> IO ()
    xtsDecryptMutable = xtsDecryptGeneric
-}

cbcEncryptGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
cbcEncryptGeneric cipher = loopBS cipher encrypt
  where encrypt bs iv d s = do
            mutableXor d iv s bs
            ecbEncryptMutable cipher d d (fromIntegral bs)
            return s

cbcDecryptGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
cbcDecryptGeneric cipher = loopBS cipher decrypt
  where decrypt bs iv d s = do
            ecbEncryptMutable cipher d s (fromIntegral bs)
            -- FIXME only work if s != d
            mutableXor d iv d bs
            return d

{-
cfbEncryptGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
cfbEncryptGeneric cipher = loopBS cipher encrypt
  where encrypt bs iv d s = do
            ecbEncryptMutable cipher d iv (fromIntegral bs)
            mutableXor d d s bs
            return d


cfbDecryptGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
cfbDecryptGeneric cipher = loopBS cipher decrypt
  where decrypt bs iv d s = do
            ecbEncryptMutable cipher d iv (fromIntegral bs)
            mutableXor d d s bs
            return s

ctrCombineGeneric :: BlockCipherIO cipher => cipher -> PtrIV -> PtrDest -> PtrSource -> BufferLength -> IO ()
ctrCombineGeneric cipher ivini dst src len = return () {-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-}
-}

-- | Helper to use a purer interface
onBlock :: BlockCipherIO cipher
        => cipher
        -> (ByteString -> ByteString)
        -> PtrDest
        -> PtrSource
        -> BufferLength
        -> IO ()
onBlock cipher f dst src len = loopBS cipher wrap nullPtr dst src len
  where wrap bs fakeIv d s = do
            fSrc <- newForeignPtr_ s
            let res = f (B.fromForeignPtr fSrc 0 bs)
            withBytePtr res $ \r -> B.memcpy d r (fromIntegral bs)
            return fakeIv

loopBS :: BlockCipherIO cipher
       => cipher
       -> (Int -> PtrIV -> PtrDest -> PtrSource -> IO PtrIV)
       -> PtrIV -> PtrDest -> PtrSource -> BufferLength
       -> IO ()
loopBS cipher f iv dst src len = loop iv dst src len
  where bs = blockSize cipher
        loop _ _ _ 0 = return ()
        loop i d s n = do
            newIV <- f bs i d s
            loop newIV (d `plusPtr` bs) (s `plusPtr` bs) (n - fromIntegral bs)

mutableXor :: PtrDest -> PtrSource -> PtrIV -> Int -> IO ()
mutableXor (to64 -> dst) (to64 -> src) (to64 -> iv) 16 = do
    peeksAndPoke dst src iv
    peeksAndPoke (dst `plusPtr` 8) (src `plusPtr` 8) ((iv `plusPtr` 8) :: Ptr Word64)
mutableXor (to64 -> dst) (to64 -> src) (to64 -> iv) 8 = do
    peeksAndPoke dst src iv
mutableXor dst src iv len = loop dst src iv len
  where loop _ _ _ 0 = return ()
        loop d s i n = peeksAndPoke d s i >> loop (d `plusPtr` 1) (s `plusPtr` 1) (i `plusPtr` 1) (n-1)

to64 :: Ptr Word8 -> Ptr Word64
to64 = castPtr

peeksAndPoke :: (Bits a, Storable a) => Ptr a -> Ptr a -> Ptr a -> IO ()
peeksAndPoke dst a b = (xor <$> peek a <*> peek b) >>= poke dst