urbit-hob

Haskell utilities for phonetic base wrangling.
Log | Files | Refs | README | LICENSE

commit 4974908e32ffb273d09a146821f27abfbc47e864
parent 1120754e3b178bb17debe529a4f4da3372ba7d89
Author: Jared Tobin <jared@jtobin.io>
Date:   Fri,  6 Sep 2019 04:57:35 -0230

ob: add module

'Ob' implements a generalised Feistel cipher.

Diffstat:
Asrc/Ob.hs | 174+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 174 insertions(+), 0 deletions(-)

diff --git a/src/Ob.hs b/src/Ob.hs @@ -0,0 +1,174 @@ +{-# OPTIONS_GHC -Wall #-} + +module Ob ( + fein + , fynd + , feis + , tail + , fe + , fen + , capF + , capFe + , capFen + ) where + +import Data.Bits +import Data.Word (Word32) +import Muk (muk) +import Prelude hiding (tail) + +-- | A PRF for j in [0, .., 3] +capF :: Int -> Word32 -> Word32 +capF j key = fromIntegral (muk seed key) where + seed = raku !! fromIntegral j + raku = [ + 0xb76d5eed + , 0xee281300 + , 0x85bcae01 + , 0x4b387af7 + ] + +-- | Conceal structure v3. +fein :: (Integral a, Bits a) => a -> a +fein = loop where + loop pyn = + let lo = pyn .&. 0xFFFFFFFF + hi = pyn .&. 0xFFFFFFFF00000000 + p32 = fromIntegral pyn :: Word32 + in if pyn >= 0x10000 && pyn <= 0xFFFFFFFF + then 0x10000 + fromIntegral (feis (p32 - 0x10000)) + else if pyn >= 0x100000000 && pyn <= 0xFFFFFFFFFFFFFFFF + then hi .|. loop lo + else pyn + +-- | Restore structure v3. +fynd :: (Integral a, Bits a) => a -> a +fynd = loop where + loop cry = + let lo = cry .&. 0xFFFFFFFF + hi = cry .&. 0xFFFFFFFF00000000 + c32 = fromIntegral cry :: Word32 + in if cry >= 0x10000 && cry <= 0xFFFFFFFF + then 0x10000 + fromIntegral (tail (c32 - 0x10000)) + else if cry >= 0x100000000 && cry <= 0xFFFFFFFFFFFFFFFF + then hi .|. loop lo + else cry + +-- | Generalised Feistel cipher +-- +-- See: Black and Rogaway (2002), "Ciphers with arbitrary finite domains." +-- +-- Note that this has been adjusted from the reference paper in order to +-- support some legacy behaviour. +feis :: Word32 -> Word32 +feis = capFe 4 65535 65536 0xFFFFFFFF capF + +-- | Reverse 'feis'. +-- +-- See: Black and Rogaway (2002), "Ciphers with arbitrary finite domains." +-- +-- Note that this has been adjusted from the reference paper in order to +-- support some legacy behaviour. +tail :: Word32 -> Word32 +tail = capFen 4 65535 65536 0xFFFFFFFF capF + +capFe + :: Integral a + => Int + -> a + -> a + -> a + -> (Int -> a -> a) + -> a + -> a +capFe r a b k f m + | c < k = c + | otherwise = fe r a b f c + where + c = fe r a b f m + +fe + :: Integral a + => Int + -> a + -> a + -> (Int -> a -> a) + -> a + -> a +fe r a b f m = loop 1 capL capR where + capL = m `mod` a + capR = m `div` a + loop j ell arr + | j > r = + if odd r + then a * arr + ell + else if arr == a + then a * arr + ell + else a * ell + arr + | otherwise = + let eff = f (pred j) arr + tmp = if odd j + then (ell + eff) `mod` a + else (ell + eff) `mod` b + in loop (succ j) arr tmp + +capFen + :: Integral a + => Int + -> a + -> a + -> a + -> (Int -> a -> a) + -> a + -> a +capFen r a b k f m + | c <= k = c + | otherwise = fen r a b f c + where + c = fen r a b f m + +fen + :: Integral a + => Int + -> a + -> a + -> (Int -> a -> a) + -> a + -> a +fen r a b f m = loop r capL capR where + ahh = + if odd r + then m `div` a + else m `mod` a + + ale = + if odd r + then m `mod` a + else m `div` a + + capL = + if ale == a + then ahh + else ale + + capR = + if ale == a + then ale + else ahh + + loop j ell arr + | j < 1 = a * arr + ell + | otherwise = + let eff = f (pred j) ell + -- NB (jtobin): + -- + -- Slight deviation from B&R (2002) here to prevent negative + -- values. We add 'a' or 'b' to arr as appropriate and reduce + -- 'eff' modulo the same number before performing subtraction. + -- + tmp = if odd j + then (arr + a - (eff `mod` a)) `mod` a + else (arr + b - (eff `mod` b)) `mod` b + in loop (pred j) tmp ell + +