packages feed

fpe (empty) → 0.1.0

raw patch · 5 files changed

+201/−0 lines, 5 filesdep +basedep +bytestringdep +cryptonitesetup-changed

Dependencies added: base, bytestring, cryptonite, integer-logarithms, vector

Files

+ FPE/FF1.hs view
@@ -0,0 +1,92 @@+module FPE.FF1 (encrypt, decrypt, BlockCipher, Crypter, Tweak) where++import Data.Bits+import Control.Arrow+import Control.Monad+import Data.Tuple (swap)+import Math.NumberTheory.Logarithms+import Data.Vector.Generic (Vector)+import qualified Data.Vector.Generic as V+import qualified Data.ByteString as S+import qualified Data.ByteString.Lazy as L++type BlockCipher = S.ByteString -> S.ByteString+type Tweak = S.ByteString+type Crypter v a = BlockCipher -> Int -> Tweak -> v a -> v a+++--  Number of bytes to store a message of given length and radix.+--  Defined in FF1 step 3 using (redundant) double ceiling.+bytesFor :: Int -> Int -> Int+bytesFor radix len =+   integerLog2 ((fromIntegral radix ^ len) - 1) `div` 8 + 1++xorBytes :: S.ByteString -> S.ByteString -> S.ByteString+xorBytes a b = S.pack $ S.zipWith xor a b++--  Conversion functions.++vecToNum :: (Vector v a, Integral a) => Int -> v a -> Integer+vecToNum radix = V.foldl go 0 where+   go val c = val * fromIntegral radix + fromIntegral c++numToVec :: (Vector v a, Integral a) => Int -> Int -> Integer -> v a+numToVec radix len num = V.reverse $ V.fromListN len $+   map (fromIntegral . (`mod` radix_)) $ iterate (`div` radix_) num+      where radix_ = fromIntegral radix++--  Same as above, but with a ByteString of fixed radix.+--  Possibly we could use Vector Word8 instead of ByteStrings?++bytesToNum :: Integral a => S.ByteString -> a+bytesToNum = S.foldl (\val c -> val * 256 + fromIntegral c) 0+{-# SPECIALIZE bytesToNum :: S.ByteString -> Integer #-}++numToBytes :: Integral a => Int -> a -> S.ByteString+numToBytes len num = S.reverse $ S.pack $ map fromIntegral $+   take len $ iterate (`div` 256) num++--  Cipherish functions.++prf :: BlockCipher -> S.ByteString -> S.ByteString+prf cipher = loop (S.replicate 16 0) where+   loop y src = if S.null rest then y' else loop y' rest where+      (x, rest) = S.splitAt 16 src+      y' = cipher $ x `xorBytes` y++--  Extends (or shortens) a block to arbitrary length using secure hashing.+extend :: BlockCipher -> Int -> S.ByteString -> S.ByteString+extend cipher len blk = L.toStrict $ L.take (fromIntegral len) $ L.fromChunks $+   blk : [ cipher $ blk `xorBytes` numToBytes @Int 16 i | i <- [1..] ]+++--  Encrypt and decrypt.++--  True for encryption, False for decryption.+crypt :: (Vector v a, Integral a) => Bool -> Crypter v a+crypt isEncrypt cipher radix tweak msg =+   numToVec radix u finalA V.++ numToVec radix v finalB where+      t = S.length tweak+      n = V.length msg; u = n `div` 2; v = n - u+      b = bytesFor radix v+      d = 4*((b-1)`div`4) + 8+      rpow = (fromIntegral radix ^)+      bP = S.concat [+            S.pack [1, 2, 1], numToBytes 3 radix,+            S.pack [10, fromIntegral u], numToBytes 4 n, numToBytes 4 t]+      pfxQ = tweak <> S.pack (replicate ((-t-b-1)`mod`16) 0)+      (numA0, numB0) = join (***) (vecToNum radix) $ V.splitAt u msg+      loop (numA, numB) i = (numB, numC) where+         y = bytesToNum $ extend cipher d $ prf cipher $ S.concat [+               bP, pfxQ, S.singleton i, numToBytes b numB]+         op = if isEncrypt then (+ y) else subtract y+         numC = op numA `mod` (if even i then rpow u else rpow v)+      wrap = if isEncrypt then id else swap+      (finalA, finalB) = wrap $+         foldl loop (wrap (numA0, numB0)) $+         if isEncrypt then [0..9] else [9,8..0]++encrypt, decrypt :: (Vector v a, Integral a) => Crypter v a+encrypt = crypt True+decrypt = crypt False+
+ LICENSE view
@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2018-2019 Galen Huntington++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ ff1test.hs view
@@ -0,0 +1,30 @@+import FPE.FF1 as FF1+import Crypto.Cipher.Types+import Crypto.Cipher.AES+import Crypto.Error+import Control.Monad (when)++import qualified Data.Vector.Unboxed as V+import qualified Data.ByteString as B+++--  The first two samples from+--  https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Standards-and-Guidelines/documents/examples/FF1samples.pdf++CryptoPassed (key :: AES128) = cipherInit $ B.pack [+  0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,+  0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c]++main = do+   let cipher = ecbEncrypt key+   let tweak = mempty+   let plain = V.fromList @Int [0..9]+   let crypt = FF1.encrypt cipher 10 tweak plain+   print crypt+   when (V.toList crypt /= [2,4,3,3,4,7,7,4,8,4]) $ error "bad encrypt"+   when (FF1.decrypt cipher 10 tweak crypt /= plain) $ error "bad decrypt"+   let tweak = B.pack [ 0x39, 0x38 .. 0x30]+   let crypt = FF1.encrypt cipher 10 tweak plain+   print crypt+   when (V.toList crypt /= [6,1,2,4,2,0,0,7,7,3]) $ error "bad tweak encrypt"+
+ fpe.cabal view
@@ -0,0 +1,56 @@+cabal-version: 1.12++-- This file has been generated from package.yaml by hpack version 0.31.1.+--+-- see: https://github.com/sol/hpack+--+-- hash: 8fbbdfc71a8796c3cda5555879b81de5510d7c2662c55142ea49394a54df5cdb++name:           fpe+version:        0.1.0+synopsis:       Format-preserving encryption.+category:       Cryptography+homepage:       https://github.com/galenhuntington/fpe#readme+bug-reports:    https://github.com/galenhuntington/fpe/issues+maintainer:     Galen Huntington+license:        MIT+license-file:   LICENSE+build-type:     Simple++source-repository head+  type: git+  location: https://github.com/galenhuntington/fpe++library+  exposed-modules:+      FPE.FF1+  other-modules:+      Paths_fpe+  hs-source-dirs:+      ./.+  default-extensions: TypeApplications ScopedTypeVariables+  ghc-options: -Wall -Wredundant-constraints -Wno-name-shadowing -Wno-missing-signatures -Wno-unused-do-bind -Wno-orphans -Wcompat+  build-depends:+      base >=4.9 && <5+    , bytestring >=0.10 && <0.11+    , integer-logarithms >=1 && <1.1+    , vector >=0.12 && <0.13+  default-language: Haskell2010++test-suite suite+  type: exitcode-stdio-1.0+  main-is: ff1test.hs+  other-modules:+      FPE.FF1+      Paths_fpe+  hs-source-dirs:+      ./.+  default-extensions: TypeApplications ScopedTypeVariables+  ghc-options: -Wall -Wredundant-constraints -Wno-name-shadowing -Wno-missing-signatures -Wno-unused-do-bind -Wno-orphans -Wcompat+  build-depends:+      base >=4.9 && <5+    , bytestring >=0.10 && <0.11+    , cryptonite+    , integer-logarithms >=1 && <1.1+    , vector >=0.12 && <0.13+  default-language: Haskell2010