packages feed

lol-apps-0.2.0.0: examples/HomomPRFMain.hs

{-|
Module      : HomomPRFMain
Description : Example, test, and macro-benchmark for homomorphic evaluation of a PRF.
Copyright   : (c) Eric Crockett, 2011-2017
                  Chris Peikert, 2011-2017
License     : GPL-2
Maintainer  : ecrockett0@email.com
Stability   : experimental
Portability : POSIX

Example, test, and macro-benchmark for homomorphic evaluation of a PRF.
-}

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE PartialTypeSignatures #-}
{-# LANGUAGE PolyKinds             #-}
{-# LANGUAGE RebindableSyntax      #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE TypeOperators         #-}

{-# OPTIONS_GHC -fno-warn-partial-type-signatures #-}

module HomomPRFMain where

import Control.DeepSeq
import Control.Monad.Except
import Control.Monad.Random
import Control.Monad.Reader
import Control.Monad.State

import Crypto.Lol hiding (lift)
import Crypto.Lol.Applications.HomomPRF
import Crypto.Lol.Applications.KeyHomomorphicPRF
import Crypto.Lol.Applications.SymmSHE
import Crypto.Lol.Cyclotomic.Tensor.CPP as CPP
import Crypto.Lol.Types.Proto
import Crypto.Lol.Types.Random
import Crypto.Random.DRBG

import Data.Promotion.Prelude
import Data.Time.Clock
import Data.Typeable
import MathObj.Matrix  (columns)
import System.FilePath ((</>), pathSeparator)
import System.IO
import Text.Printf

import HomomPRFParams

type T = CPP.CT
type Z = Int64

protoDir :: Int -> String -> String
protoDir p = (((pathSeparator : "home") </> "eric" </> "Desktop" </> "Lol" </> ("p" ++ show p ++ "-")) ++)

lfuncPath, thintPath, rhintPath, tskPath, rskPath :: Int -> String
thintPath p = protoDir p "tunnel.hint"
rhintPath p = protoDir p "round.hint"
lfuncPath p = protoDir p "tunnelfuncs.lfuns"
-- | The key used as the input to tunneling; also used for encryption
tskPath   p = protoDir p "encKey.secret"
-- | The output key of tunneling, used for rounding; also for decryption
rskPath   p = protoDir p "decKey.secret"

-- R' - ... - S'
-- |  -  |  - |
-- R  - ... - S
-- PRF evaluation usually goes from R' to R. Homomorphic evaluation
-- goes from R' to S, via S'. To test, we run the computation in the clear
-- from R' to R, then tunnel in the clear to S.
main :: IO ()
main = do
  putStrLn $ "Starting homomorphic PRF evaluation with tensor " ++ show (typeRep (Proxy::Proxy T))
  hints' <- runExceptT readHints
  (lfuns, hints :: EvalHints T RngList Z ZP ZQ ZQSeq KSGad, encKey, decKey) <- case hints' of
    (Right a) -> do
      putStrLn "Using precomputed hints."
      return a
    (Left st) -> do
      putStrLn $ "Could not read precomputed hints: " ++ st
      gen :: CryptoRand HashDRBG <- liftIO newGenIO -- uses system entropy
      (lfuns, hints, encKey, decKey) <- time "Generating hints..." $ flip evalRand gen $ do
        let v = 1.0 :: Double
        encKey <- genSK v
        (tHints, decKey) <- tunnelInfoChain encKey
        let lfuns = ptTunnelFuncs
        rHints <- roundHints decKey
        let hints = Hints tHints rHints
        return (lfuns, hints, encKey, decKey)
      writeHints lfuns hints encKey decKey
      return (lfuns, hints, encKey, decKey)
  gen :: CryptoRand HashDRBG <- liftIO newGenIO -- uses system entropy
  (family, s, ct) <- time "Generating random inputs..." $ flip evalRand gen $ do
    family :: PRFFamily PRFGad _ _ <- randomFamily 10 -- works on 10-bit input
    s <- getRandom
    ct <- encrypt encKey s
    return (family, s, ct)
  st <- time "Initializing PRF state..." $ prfState family Nothing --initialize with input 0
  encprf <- time "Evaluating PRF..." $ flip runReader hints $ flip evalStateT st $ homomPRFM ct 0
  hprf <- time "Decrypting PRF output..." $ decrypt decKey encprf

  -- test
  clearPRF <- time "In-the-clear PRF..." $ head $ head $ columns $ ringPRF s 0 st -- homomPRF only computes first elt
  clearPRF' <- time "In-the-clear tunnel..." $ ptTunnel lfuns clearPRF
  if clearPRF' == hprf
  then putStrLn "PASS: Homomorphic output matches in-the-clear."
  else putStrLn "TEST FAILED"

readHints :: forall mon t rngs z e zp zq zqs gad r' s' .
  (MonadIO mon, MonadError String mon, Mod zp, UnPP (CharOf zp) ~ '(Prime2, e),
   ProtoReadable (TunnelFuncs t (PTRings rngs) (TwoOf zp)),
   ProtoReadable (TunnelInfoChain gad t rngs zp (ZqUp zq zqs)),
   ProtoReadable (RoundHints t (Fst (Last rngs)) (Snd (Last rngs)) z e zp (ZqDown zq zqs) zqs gad),
   ProtoReadable (SK (Cyc t r' z)), ProtoReadable (SK (Cyc t s' z)))
  => mon (TunnelFuncs t (PTRings rngs) (TwoOf zp),
          EvalHints t rngs z zp zq zqs gad,
          SK (Cyc t r' z), SK (Cyc t s' z))
readHints = do
  let p = fromIntegral $ proxy modulus (Proxy::Proxy zp)
  lfuns  <- parseProtoFile $ lfuncPath p
  tHints <- parseProtoFile $ thintPath p
  rHints <- parseProtoFile $ rhintPath p
  tsk    <- parseProtoFile $ tskPath p
  rsk    <- parseProtoFile $ rskPath p
  return (lfuns, Hints tHints rHints, tsk, rsk)
{-
readOrGenHints :: (MonadIO mon, MonadRandom mon, Head RngList ~ '(r,r'), Last RngList ~ '(s,s'))
  => mon (EvalHints T RngList Z ZP ZQ ZQSeq KSGad, SK (Cyc T r' Z), SK (Cyc T s' Z))
readOrGenHints = do
  res <- runExceptT readHints
  case res of
    (Left st) -> do
      liftIO $ putStrLn $ "Could not read precomputed data. Error was: " ++ st
      (hints, sk, skout) <- do

      liftIO $ putStrLn "Writing hints to disk..."
      writeHints hints sk skout
      return (hints, sk, skout)
    (Right a) -> do
      liftIO $ putStrLn "Precomputed hints found."
      return a
-}
writeHints :: forall mon t rngs z e zp zq zqs gad r' s' .
  (MonadIO mon, Mod zp, UnPP (CharOf zp) ~ '(Prime2, e),
   ProtoReadable (TunnelFuncs t (PTRings rngs) (TwoOf zp)),
   ProtoReadable (TunnelInfoChain gad t rngs zp (ZqUp zq zqs)),
   ProtoReadable (RoundHints t (Fst (Last rngs)) (Snd (Last rngs)) z e zp (ZqDown zq zqs) zqs gad),
   ProtoReadable (SK (Cyc t r' z)), ProtoReadable (SK (Cyc t s' z)))
  => TunnelFuncs t (PTRings rngs) (TwoOf zp)
  -> EvalHints t rngs z zp zq zqs gad
  -> SK (Cyc t r' z)
  -> SK (Cyc t s' z)
  -> mon ()
writeHints lfuns (Hints tHints rHints) encKey decKey = do
  let p = fromIntegral $ proxy modulus (Proxy::Proxy zp)
  writeProtoFile (lfuncPath p) lfuns
  writeProtoFile (thintPath p) tHints
  writeProtoFile (rhintPath p) rHints
  writeProtoFile (tskPath p) encKey
  writeProtoFile (rskPath p) decKey

-- timing functionality
time :: (NFData a) => String -> a -> IO a
time s m = do
  putStr' s
  wallStart <- getCurrentTime
  m `deepseq` printTimes wallStart 1
  return m

-- flushes the print buffer
putStr' :: String -> IO ()
putStr' str = do
  putStr str
  hFlush stdout

printTimes :: UTCTime -> Int -> IO ()
printTimes wallStart iters = do
    wallEnd <- getCurrentTime
    let wallTime = realToFrac $ diffUTCTime wallEnd wallStart :: Double
    printf "Wall time: %0.3fs" wallTime
    if iters == 1
    then putStr "\n\n"
    else printf "\tAverage wall time: %0.3fs\n\n" $ wallTime / fromIntegral iters