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.Arith
28 import Voting.Protocol.Credential
29 import Voting.Protocol.Election
30 import Voting.Protocol.Tally
32 -- * Type 'TrusteePublicKey'
33 data TrusteePublicKey crypto v c = TrusteePublicKey
34 { trustee_PublicKey :: !(PublicKey crypto c)
35 , trustee_SecretKeyProof :: !(Proof crypto v c)
36 -- ^ NOTE: It is important to ensure
37 -- that each trustee generates its key pair independently
38 -- of the 'PublicKey's published by the other trustees.
39 -- Otherwise, a dishonest trustee could publish as 'PublicKey'
40 -- its genuine 'PublicKey' divided by the 'PublicKey's of the other trustees.
41 -- This would then lead to the 'election_PublicKey'
42 -- being equal to this dishonest trustee's 'PublicKey',
43 -- which means that knowing its 'SecretKey' would be sufficient
44 -- for decrypting messages encrypted to the 'election_PublicKey'.
45 -- To avoid this attack, each trustee publishing a 'PublicKey'
46 -- must 'prove' knowledge of the corresponding 'SecretKey'.
47 -- Which is done in 'proveIndispensableTrusteePublicKey'
48 -- and 'verifyIndispensableTrusteePublicKey'.
50 deriving instance Eq (G crypto c) => Eq (TrusteePublicKey crypto v c)
51 deriving instance (Show (G crypto c), Show (PublicKey crypto c)) => Show (TrusteePublicKey crypto v c)
52 deriving instance NFData (G crypto c) => NFData (TrusteePublicKey crypto v c)
56 ) => ToJSON (TrusteePublicKey crypto v c) where
57 toJSON TrusteePublicKey{..} =
59 [ "pok" .= trustee_SecretKeyProof
60 , "public_key" .= trustee_PublicKey
62 toEncoding TrusteePublicKey{..} =
64 ( "pok" .= trustee_SecretKeyProof
65 <> "public_key" .= trustee_PublicKey
70 , FromJSON (PublicKey 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 ::
87 Multiplicative (G crypto c) =>
88 Invertible (G crypto c) =>
89 ToNatural (G crypto c) =>
90 Monad m => RandomGen r =>
91 SecretKey crypto c -> S.StateT r m (TrusteePublicKey crypto v c)
92 proveIndispensableTrusteePublicKey trustSecKey = do
93 let trustee_PublicKey = publicKey trustSecKey
94 trustee_SecretKeyProof <-
95 prove trustSecKey [groupGen] $
96 hash (indispensableTrusteePublicKeyStatement trustee_PublicKey)
97 return TrusteePublicKey{..}
99 -- ** Checking a 'TrusteePublicKey' before incorporating it into the 'Election''s 'PublicKey'
101 -- | @('verifyIndispensableTrusteePublicKey' trustPubKey)@
102 -- returns 'True' iif. the given 'trustee_SecretKeyProof'
103 -- does 'prove' that the 'SecretKey' associated with
104 -- the given 'trustee_PublicKey' is known by the trustee.
105 verifyIndispensableTrusteePublicKey ::
109 Multiplicative (G crypto c) =>
110 Invertible (G crypto c) =>
111 ToNatural (G crypto c) =>
113 TrusteePublicKey crypto v c ->
114 ExceptT ErrorTrusteePublicKey m ()
115 verifyIndispensableTrusteePublicKey TrusteePublicKey{..} =
117 proof_challenge trustee_SecretKeyProof == hash
118 (indispensableTrusteePublicKeyStatement trustee_PublicKey)
119 [commit trustee_SecretKeyProof groupGen trustee_PublicKey]
121 throwE ErrorTrusteePublicKey_WrongProof
123 -- ** Type 'ErrorTrusteePublicKey'
124 data ErrorTrusteePublicKey
125 = ErrorTrusteePublicKey_WrongProof
126 -- ^ The 'trustee_SecretKeyProof' is wrong.
130 indispensableTrusteePublicKeyStatement ::
132 ToNatural (G crypto c) =>
133 PublicKey crypto c -> BS.ByteString
134 indispensableTrusteePublicKeyStatement trustPubKey =
135 "pok|"<>bytesNat trustPubKey<>"|"
137 -- * 'Election''s 'PublicKey'
139 -- ** Generating an 'Election''s 'PublicKey' from multiple 'TrusteePublicKey's.
141 combineIndispensableTrusteePublicKeys ::
143 Multiplicative (G crypto c) =>
144 Invertible (G crypto c) =>
145 ToNatural (G crypto c) =>
146 [TrusteePublicKey crypto v c] -> PublicKey crypto c
147 combineIndispensableTrusteePublicKeys =
148 List.foldr (\TrusteePublicKey{..} -> (trustee_PublicKey *)) one
150 -- ** Checking the trustee's 'DecryptionShare's before decrypting an 'EncryptedTally'.
152 verifyIndispensableDecryptionShareByTrustee ::
156 Multiplicative (G crypto c) =>
157 Invertible (G crypto c) =>
158 ToNatural (G crypto c) =>
160 EncryptedTally crypto v c -> [PublicKey crypto c] -> [DecryptionShare crypto v c] ->
161 ExceptT ErrorTally m ()
162 verifyIndispensableDecryptionShareByTrustee encByChoiceByQuest =
163 isoZipWithM_ (throwE $ ErrorTally_NumberOfTrustees)
164 (verifyDecryptionShare encByChoiceByQuest)
166 -- ** Decrypting an 'EncryptedTally' from multiple 'TrusteePublicKey's.
168 -- | @('combineDecryptionShares' pubKeyByTrustee decShareByTrustee)@
169 -- returns the 'DecryptionFactor's by choice by 'Question'
170 combineIndispensableDecryptionShares ::
174 Multiplicative (G crypto c) =>
175 Invertible (G crypto c) =>
176 ToNatural (G crypto c) =>
177 [PublicKey crypto c] -> DecryptionShareCombinator crypto v c
178 combineIndispensableDecryptionShares
181 decByChoiceByQuestByTrustee = do
182 verifyIndispensableDecryptionShareByTrustee
185 decByChoiceByQuestByTrustee
186 (DecryptionShare dec0,decs) <-
187 maybe (throwE ErrorTally_NumberOfTrustees) return $
188 List.uncons decByChoiceByQuestByTrustee
189 foldM (isoZipWithM (throwE ErrorTally_NumberOfQuestions)
190 (maybe (throwE ErrorTally_NumberOfChoices) return `o2`
191 isoZipWith (\a (decFactor, _proof) -> a * decFactor)))
192 ((fst <$>) <$> dec0) (unDecryptionShare <$> decs)