1 {-# LANGUAGE DeriveAnyClass #-}
2 {-# LANGUAGE DeriveGeneric #-}
3 {-# LANGUAGE OverloadedStrings #-}
4 {-# LANGUAGE UndecidableInstances #-} -- for Reifies instances
5 module Voting.Protocol.Trustee.Indispensable where
7 import Control.DeepSeq (NFData)
8 import Control.Monad (Monad(..), foldM, unless)
9 import Control.Monad.Trans.Except (ExceptT(..), throwE)
10 import Data.Aeson (ToJSON(..), FromJSON(..), (.:), (.=))
11 import Data.Eq (Eq(..))
12 import Data.Function (($))
13 import Data.Functor ((<$>))
14 import Data.Maybe (maybe)
15 import Data.Reflection (Reifies(..))
16 import Data.Semigroup (Semigroup(..))
17 import Data.Tuple (fst)
18 import GHC.Generics (Generic)
19 import System.Random (RandomGen)
20 import Text.Show (Show(..))
21 import qualified Control.Monad.Trans.State.Strict as S
22 import qualified Data.Aeson as JSON
23 import qualified Data.ByteString as BS
24 import qualified Data.List as List
26 import Voting.Protocol.Utils
27 import Voting.Protocol.Arithmetic
28 import Voting.Protocol.Version
29 import Voting.Protocol.Cryptography
30 import Voting.Protocol.Credential
31 import Voting.Protocol.Tally
33 -- * Type 'TrusteePublicKey'
34 data TrusteePublicKey crypto v c = TrusteePublicKey
35 { trustee_PublicKey :: !(PublicKey crypto c)
36 , trustee_SecretKeyProof :: !(Proof crypto v c)
37 -- ^ NOTE: It is important to ensure
38 -- that each trustee generates its key pair independently
39 -- of the 'PublicKey's published by the other trustees.
40 -- Otherwise, a dishonest trustee could publish as 'PublicKey'
41 -- its genuine 'PublicKey' divided by the 'PublicKey's of the other trustees.
42 -- This would then lead to the 'election_PublicKey'
43 -- being equal to this dishonest trustee's 'PublicKey',
44 -- which means that knowing its 'SecretKey' would be sufficient
45 -- for decrypting messages encrypted to the 'election_PublicKey'.
46 -- To avoid this attack, each trustee publishing a 'PublicKey'
47 -- must 'prove' knowledge of the corresponding 'SecretKey'.
48 -- Which is done in 'proveIndispensableTrusteePublicKey'
49 -- and 'verifyIndispensableTrusteePublicKey'.
51 deriving instance Eq (G crypto c) => Eq (TrusteePublicKey crypto v c)
52 deriving instance (Show (G crypto c), Show (PublicKey crypto c)) => Show (TrusteePublicKey crypto v c)
53 deriving instance NFData (G crypto c) => NFData (TrusteePublicKey crypto v c)
57 ) => ToJSON (TrusteePublicKey crypto v c) where
58 toJSON TrusteePublicKey{..} =
60 [ "pok" .= trustee_SecretKeyProof
61 , "public_key" .= trustee_PublicKey
63 toEncoding TrusteePublicKey{..} =
65 ( "pok" .= trustee_SecretKeyProof
66 <> "public_key" .= trustee_PublicKey
70 , CryptoParams crypto c
71 ) => FromJSON (TrusteePublicKey crypto v c) where
72 parseJSON = JSON.withObject "TrusteePublicKey" $ \o -> do
73 trustee_PublicKey <- o .: "public_key"
74 trustee_SecretKeyProof <- o .: "pok"
75 return TrusteePublicKey{..}
77 -- ** Generating a 'TrusteePublicKey'
79 -- | @('proveIndispensableTrusteePublicKey' trustSecKey)@
80 -- returns the 'PublicKey' associated to 'trustSecKey'
81 -- and a 'Proof' of its knowledge.
82 proveIndispensableTrusteePublicKey ::
84 CryptoParams crypto c =>
86 Monad m => RandomGen r =>
87 SecretKey crypto c -> S.StateT r m (TrusteePublicKey crypto v c)
88 proveIndispensableTrusteePublicKey trustSecKey = do
89 let trustee_PublicKey = publicKey trustSecKey
90 trustee_SecretKeyProof <-
91 prove trustSecKey [groupGen] $
92 hash (indispensableTrusteePublicKeyStatement trustee_PublicKey)
93 return TrusteePublicKey{..}
95 -- ** Checking a 'TrusteePublicKey' before incorporating it into the 'Election''s 'PublicKey'
97 -- | @('verifyIndispensableTrusteePublicKey' trustPubKey)@
98 -- returns 'True' iif. the given 'trustee_SecretKeyProof'
99 -- does 'prove' that the 'SecretKey' associated with
100 -- the given 'trustee_PublicKey' is known by the trustee.
101 verifyIndispensableTrusteePublicKey ::
103 CryptoParams crypto c =>
105 TrusteePublicKey crypto v c ->
106 ExceptT ErrorTrusteePublicKey m ()
107 verifyIndispensableTrusteePublicKey TrusteePublicKey{..} =
109 proof_challenge trustee_SecretKeyProof == hash
110 (indispensableTrusteePublicKeyStatement trustee_PublicKey)
111 [commit trustee_SecretKeyProof groupGen trustee_PublicKey]
113 throwE ErrorTrusteePublicKey_WrongProof
115 -- ** Type 'ErrorTrusteePublicKey'
116 data ErrorTrusteePublicKey
117 = ErrorTrusteePublicKey_WrongProof
118 -- ^ The 'trustee_SecretKeyProof' is wrong.
122 indispensableTrusteePublicKeyStatement ::
123 CryptoParams crypto c =>
124 PublicKey crypto c -> BS.ByteString
125 indispensableTrusteePublicKeyStatement trustPubKey =
126 "pok|"<>bytesNat trustPubKey<>"|"
128 -- * 'Election''s 'PublicKey'
130 -- ** Generating an 'Election''s 'PublicKey' from multiple 'TrusteePublicKey's.
132 combineIndispensableTrusteePublicKeys ::
133 CryptoParams crypto c =>
134 [TrusteePublicKey crypto v c] -> PublicKey crypto c
135 combineIndispensableTrusteePublicKeys =
136 List.foldr (\TrusteePublicKey{..} -> (trustee_PublicKey *)) one
138 -- ** Checking the trustee's 'DecryptionShare's before decrypting an 'EncryptedTally'.
140 verifyIndispensableDecryptionShareByTrustee ::
142 CryptoParams crypto c =>
144 EncryptedTally crypto v c -> [PublicKey crypto c] -> [DecryptionShare crypto v c] ->
145 ExceptT ErrorTally m ()
146 verifyIndispensableDecryptionShareByTrustee encByChoiceByQuest =
147 isoZipWithM_ (throwE $ ErrorTally_NumberOfTrustees)
148 (verifyDecryptionShare encByChoiceByQuest)
150 -- ** Decrypting an 'EncryptedTally' from multiple 'TrusteePublicKey's.
152 -- | @('combineDecryptionShares' pubKeyByTrustee decShareByTrustee)@
153 -- returns the 'DecryptionFactor's by choice by 'Question'
154 combineIndispensableDecryptionShares ::
156 CryptoParams crypto c =>
157 [PublicKey crypto c] -> DecryptionShareCombinator crypto v c
158 combineIndispensableDecryptionShares
161 decByChoiceByQuestByTrustee = do
162 verifyIndispensableDecryptionShareByTrustee
165 decByChoiceByQuestByTrustee
166 (DecryptionShare dec0,decs) <-
167 maybe (throwE ErrorTally_NumberOfTrustees) return $
168 List.uncons decByChoiceByQuestByTrustee
169 foldM (isoZipWithM (throwE ErrorTally_NumberOfQuestions)
170 (maybe (throwE ErrorTally_NumberOfChoices) return `o2`
171 isoZipWith (\a (decFactor, _proof) -> a * decFactor)))
172 ((fst <$>) <$> dec0) (unDecryptionShare <$> decs)