{-# LANGUAGE NoImplicitPrelude #-}
{-# LANGUAGE OverloadedStrings #-}
module Example (
micpWrapper,
micpComponents,
testPedersen,
testBlumMicaliPRNG,
) where
import Protolude hiding (hash)
import Control.Concurrent.MVar
import Crypto.Hash
import Crypto.Number.Serialize (os2ip)
import Crypto.Random.Types (MonadRandom(..))
import qualified Data.ByteArray as BA
import Data.Maybe (fromJust)
import MICP
import MICP.Internal
import Pedersen
import PrimeField
testBlumMicaliPRNG :: IO Integer
testBlumMicaliPRNG = do
let k = 256
(a,cparams) <- setup k
let spf = pedersenSPF cparams
seed <- genPRNGSeed spf
blumMicaliPRNG k seed spf
testPedersen :: ByteString -> IO Bool
testPedersen bs = do
let hashedBs = os2ip $ sha256 bs
(a,commitParams) <- setup 256 -- hashStorage uses sha256
(Pedersen c r) <- commit hashedBs commitParams
return $ open commitParams c r
-- | This example illustrates how you might implement the server logic for two
-- parties to use MICP in a distributed network. MVars are used to simulate
-- message passing, but can be replaced with any message passing construct.
-- Note: this example does not handle Reject messages properly.
micpWrapper :: Int -> IO Bool
micpWrapper nbits = do
-- MVars for message passing between I and R
iMVar <- newEmptyMVar
rMVar <- newEmptyMVar
-- MVars for MICP thread reporting result
iResMVar <- newEmptyMVar
rResMVar <- newEmptyMVar
let aliceSecret = sha256 "123456789"
let bobSecret = sha256 "987654321"
-- Generate shared Safe Prime Field
spf <- mkSPF nbits
forkIO $ void $ runSPFT spf $ -- Alice thread
alice aliceSecret iMVar rMVar iResMVar
forkIO $ void $ runSPFT spf $ -- Bob thread
bob bobSecret rMVar iMVar rResMVar
-- Each party should have computed each other's secret
iRes <- takeMVar iResMVar
rRes <- takeMVar rResMVar
return $ iRes == bobSecret && rRes == aliceSecret
where
alice
:: ByteString
-> MVar IPhase
-> MVar RPhase
-> MVar ByteString
-> SPFM IO ()
alice secret ipMVar rpMVar resMVar = do
-- Phase 1
(ip1priv, ip1Msg) <- lift $ iPhase1 nbits
liftIO $ putMVar ipMVar $ IPhase1 ip1Msg
(RPhase1 rp1msg) <- liftIO $ takeMVar rpMVar
-- Phase 2
let ip2params = mkIPhase2Params secret rp1msg
(ip2priv, ip2Msg) <- iPhase2 ip2params
liftIO $ putMVar ipMVar $ IPhase2 ip2Msg
(RPhase2 rp2msg) <- liftIO $ takeMVar rpMVar
-- Phase 3 (Should case match on rp3msg for RPhase3Reject)
let ip3params = mkIPhase3Params ip1priv ip1Msg ip2priv ip2Msg rp1msg rp2msg
ip3Msg <- iPhase3 ip3params
liftIO $ putMVar ipMVar $ IPhase3 ip3Msg
(RPhase3 rp3msg) <- liftIO $ takeMVar rpMVar
-- Phase 4 (Should case match on rp4msg for RPhase4Reject)
let ip4params = mkIPhase4Params ip2priv rp1msg rp3msg
ip4Msg <- iPhase4 ip4params
liftIO $ putMVar ipMVar $ IPhase4 ip4Msg
(RPhase4 rp4msg) <- liftIO $ takeMVar rpMVar
-- Phase 5
let ip5Msg = iPhase5 ip2priv
liftIO $ putMVar ipMVar $ IPhase5 ip5Msg
-- Compute bob's secret
let k1Map = rGetK1Map rp4msg
let k2Map = rGetK2Map rp3msg
rSecret <- micpReveal k1Map k2Map
liftIO $ putMVar resMVar rSecret
bob
:: ByteString
-> MVar RPhase
-> MVar IPhase
-> MVar ByteString
-> SPFM IO ()
bob secret rpMVar ipMVar resMVar = do
-- Phase 1
(IPhase1 ip1msg) <- liftIO $ takeMVar ipMVar
let rp1params = mkRPhase1Params nbits secret ip1msg
(rp1priv, rp1Msg) <- rPhase1 rp1params
liftIO $ putMVar rpMVar $ RPhase1 rp1Msg
-- Phase 2
(IPhase2 ip2msg) <- liftIO $ takeMVar ipMVar
let rp2params = mkRPhase2Params rp1priv ip2msg
rp2Msg <- rPhase2 rp2params
liftIO $ putMVar rpMVar $ RPhase2 rp2Msg
-- Phase 3 (Should case match on ip3msg for IPhase3Reject)
(IPhase3 ip3msg) <- liftIO $ takeMVar ipMVar
case ip3msg of
IPhase3Reject -> panic "IPhase3Reject"
_ -> do
let rp3params = mkRPhase3Params rp1priv rp1Msg rp2Msg ip1msg ip2msg ip3msg
rp3Msg <- rPhase3 rp3params
liftIO $ putMVar rpMVar $ RPhase3 rp3Msg
-- Phase 4 (Should case match on ip4msg for IPhase4Reject)
(IPhase4 ip4msg) <- liftIO $ takeMVar ipMVar
let rp4params = mkRPhase4Params rp1priv ip2msg ip4msg
rp4Msg <- rPhase4 rp4params
liftIO $ putMVar rpMVar $ RPhase4 rp4Msg
-- Phase 5
(IPhase5 ip5msg) <- liftIO $ takeMVar ipMVar
-- Compute Alice's secret
let k1Map = iGetK1Map ip5msg
let k2Map = fromJust $ iGetK2Map ip4msg
aliceSecret <- micpReveal k1Map k2Map
liftIO $ putMVar resMVar aliceSecret
-- | In this test, all values computed are in scope for both Alice & Bob, so
-- instead of "sending" those values to one another, we can just use them for
-- the respective counterparty computations.
micpComponents :: Int -> IO Bool
micpComponents secParam = do
let aliceMsg = sha256 "123456789"
let aliceMsgBytes = BA.unpack aliceMsg
let bobMsg = sha256 "987654321"
let bobMsgBytes = BA.unpack bobMsg
putText "\nCreating Shared SPF and Local Params..."
sharedSPF <- mkSPF secParam
-- 1, 2(a): send pedersen bases to each other
(aliceA, aCommitParams) <- setup secParam
(bobA, bCommitParams) <- setup secParam
-- All further computation takes places in SPF
runSPFT sharedSPF $ do
-- 2(b): Send bobGKMap to alice
putText "Gen bob kmap"
(bobKMap,bobK'Map) <- genKMaps bobMsgBytes
bobGtoKMap <- kmapToGKMap bobKMap
bobGtoK'Map <- kmapToGKMap bobK'Map
-- 2(c): Send bobCommit to alice using alice params
putText "Gen bob r"
(bobR, bobPedersen) <- genAndCommitR aCommitParams
let (Pedersen bobCommitment bobReveal) = bobPedersen
-- 3(a): Send aliceGKMap to bob
putText "Gen alice kmap"
(aliceKMap, aliceK'Map) <- genKMaps aliceMsgBytes
aliceGtoKMap <- kmapToGKMap aliceKMap
aliceGtoK'Map <- kmapToGKMap aliceK'Map
-- 3(b): Send aliceCommit to bob
putText "Gen alice r"
(aliceR, alicePedersen) <- genAndCommitR bCommitParams
let (Pedersen aliceCommitment aliceReveal) = alicePedersen
-- 3(c): Send aliceC to bob
putText "Gen alice c"
aliceC <- genC
-- 4(a): Send bobC to alice
putText "Gen bob c"
bobC <- genC
-- 4(b): Send bobReveal to alice
-- 4(c): Send bobDMap to alice
putText "Compute bob dmap"
let bobDMap = computeDMap aliceC bobKMap bobR
-- 5(a): alice checks bob's commit
unless (open aCommitParams bobCommitment bobReveal) $
panic "Bob's commit is illegitimate!"
-- alice verifies g^di = (g^ki)^c + g^r
bobDMapVerified <- verifyDMap bobDMap bobGtoKMap aliceC $ revealVal bobReveal
unless bobDMapVerified $
panic "Bob's computations are wrong!"
-- 5(b): Send aliceReveal to bob
-- 5(c): Send aliceDMap to bob
putText "Compute alice dmap"
let aliceDMap = computeDMap bobC aliceKMap aliceR
-- 5(d): send alice's 'a' to bob
-- 6(a): bob checks alice's commit
unless (open bCommitParams aliceCommitment aliceReveal) $
panic "Alice's commit is illegitimate!"
-- bob verifies g^di = (g^ki)^c + g^r
aliceDMapVerified <- verifyDMap aliceDMap aliceGtoKMap bobC $ revealVal aliceReveal
unless aliceDMapVerified $
panic "Alice's computations are wrong!"
-- 6(b): bob checks that alice's ga^a == ha
unless (verifyCommitParams aliceA aCommitParams) $
panic "Alice's pedersen bases are not valid!"
-- 6(c): bob sends k'map and bob's 'a' to alice
-- 7(a): alice checks that bob's ga^a == ha
unless (verifyCommitParams bobA bCommitParams) $
panic "Bob's pedersen bases are not valid!"
-- 7(b): alice checks k'map from bob matches gk'map received earlier
bobGtoK'MapCheck <- kmapToGKMap bobK'Map
unless (bobGtoK'MapCheck == bobGtoK'Map) $
panic "Bob's k' and gk' maps are invalid!"
-- 7(c): alice sends k'map to bob
-- 8(a): bob checks k'map from alice matches gk'map recieved earlier
aliceGtoK'MapCheck <- kmapToGKMap aliceK'Map
unless (aliceGtoK'MapCheck == aliceGtoK'Map) $
panic "Alice's k' and gk' maps are invalid!"
-- REVEAL STAGE:
-- Alice & Bob reveal kMaps (map of k only, no k')
-- Using bob/alice env respectively to show this reveal can happen within
-- the shared env only, and doesn't care about local pedersen params
aliceMsgRes <- micpReveal aliceKMap aliceK'Map
let aliceResEqMsg = aliceMsgRes == aliceMsg
bobMsgRes <- micpReveal bobKMap bobK'Map
let bobResEqMsg = bobMsgRes == bobMsg
return $ aliceResEqMsg && bobResEqMsg
sha256 :: ByteString -> ByteString
sha256 bs = BA.convert (hash bs :: Digest SHA3_256)