]> Git — Sourcephile - majurity.git/blob - hjugement-protocol/Protocol/Election.hs
protocol: polish extensions
[majurity.git] / hjugement-protocol / Protocol / Election.hs
1 {-# LANGUAGE OverloadedStrings #-}
2 module Protocol.Election where
3
4 import Control.Monad (Monad(..), join, mapM, zipWithM)
5 import Control.Monad.Morph (MFunctor(..))
6 import Control.Monad.Trans.Class (MonadTrans(..))
7 import Data.Bool
8 import Data.Either (either)
9 import Data.Eq (Eq(..))
10 import Data.Foldable (Foldable, foldMap, and)
11 import Data.Function (($), id, const)
12 import Data.Functor (Functor, (<$>))
13 import Data.Functor.Identity (Identity(..))
14 import Data.Maybe (Maybe(..), fromMaybe)
15 import Data.Ord (Ord(..))
16 import Data.Semigroup (Semigroup(..))
17 import Data.Text (Text)
18 import Data.Traversable (Traversable(..))
19 import Data.Tuple (fst, snd, uncurry)
20 import GHC.Natural (minusNaturalMaybe)
21 import Numeric.Natural (Natural)
22 import Prelude (fromIntegral)
23 import Text.Show (Show(..))
24 import qualified Control.Monad.Trans.Except as Exn
25 import qualified Control.Monad.Trans.State.Strict as S
26 import qualified Data.ByteString as BS
27 import qualified Data.List as List
28
29 import Protocol.Arithmetic
30 import Protocol.Credential
31
32 -- * Type 'Encryption'
33 -- | ElGamal-like encryption.
34 -- Its security relies on the /Discrete Logarithm problem/.
35 --
36 -- Because ('groupGen' '^'encNonce '^'secKey '==' 'groupGen' '^'secKey '^'encNonce),
37 -- knowing @secKey@, one can divide 'encryption_vault' by @('encryption_nonce' '^'secKey)@
38 -- to decipher @('groupGen' '^'clear)@, then the @clear@ text must be small to be decryptable,
39 -- because it is encrypted as a power of 'groupGen' (hence the "-like" in "ElGamal-like")
40 -- to enable the additive homomorphism.
41 --
42 -- NOTE: Since @('encryption_vault' '*' 'encryption_nonce' '==' 'encryption_nonce' '^' (secKey '+' clear))@,
43 -- then: @(logBase 'encryption_nonce' ('encryption_vault' '*' 'encryption_nonce') '==' secKey '+' clear)@.
44 data Encryption q = Encryption
45 { encryption_nonce :: G q
46 -- ^ Public part of the randomness 'encNonce' used to 'encrypt' the 'clear' text,
47 -- equal to @('groupGen' '^'encNonce)@
48 , encryption_vault :: G q
49 -- ^ Encrypted 'clear' text, equal to @('pubKey' '^'r '*' 'groupGen' '^'clear)@
50 } deriving (Eq,Show)
51
52 -- | Additive homomorphism.
53 -- Using the fact that: @'groupGen' '^'x '*' 'groupGen' '^'y '==' 'groupGen' '^'(x'+'y)@.
54 instance SubGroup q => Additive (Encryption q) where
55 zero = Encryption one one
56 x+y = Encryption
57 (encryption_nonce x * encryption_nonce y)
58 (encryption_vault x * encryption_vault y)
59
60 -- *** Type 'EncryptionNonce'
61 type EncryptionNonce = E
62
63 -- | @('encrypt' pubKey clear)@ returns an ElGamal-like 'Encryption'.
64 --
65 -- WARNING: the secret encryption nonce (@encNonce@)
66 -- is returned alongside the 'Encryption'
67 -- in order to 'prove' the validity of the encrypted 'clear' text in 'proveEncryption',
68 -- but this secret @encNonce@ MUST be forgotten after that,
69 -- as it may be used to decipher the 'Encryption'
70 -- without the secret key associated with 'pubKey'.
71 encrypt ::
72 Monad m => RandomGen r => SubGroup q =>
73 PublicKey q -> E q ->
74 S.StateT r m (EncryptionNonce q, Encryption q)
75 encrypt pubKey clear = do
76 encNonce <- random
77 -- NOTE: preserve the 'encNonce' for 'prove' in 'proveEncryption'.
78 return $ (encNonce,)
79 Encryption
80 { encryption_nonce = groupGen^encNonce
81 , encryption_vault = pubKey ^encNonce * groupGen^clear
82 }
83
84 -- * Type 'Proof'
85 -- | 'Proof' of knowledge of a discrete logarithm:
86 -- @(secret == logBase base (base^secret))@.
87 data Proof q = Proof
88 { proof_challenge :: Challenge q
89 -- ^ 'Challenge' sent by the verifier to the prover
90 -- to ensure that the prover really has knowledge
91 -- of the secret and is not replaying.
92 -- Actually, 'proof_challenge' is not sent to the prover,
93 -- but derived from the prover's 'Commitment's and statements
94 -- with a collision resistant 'hash'.
95 -- Hence the prover cannot chose the 'proof_challenge' to his/her liking.
96 , proof_response :: E q
97 -- ^ A discrete logarithm sent by the prover to the verifier,
98 -- as a response to 'proof_challenge'.
99 --
100 -- If the verifier observes that @('proof_challenge' '==' 'hash' statement [commitment])@
101 -- where:
102 --
103 -- * @statement@ is a serialization of a tag, 'base' and 'basePowSec',
104 -- * @(commitment '==' 'commit' proof base basePowSec '=='
105 -- base '^' 'proof_response' '*' basePowSec '^' 'proof_challenge')@,
106 -- * and @(basePowSec '==' base'^'sec)@,
107 --
108 -- then, with overwhelming probability due to the 'hash' function:
109 -- @(commitment '==' base'^'nonce)@.
110 -- Therefore by expanding 'commitment':
111 -- @('proof_response' '==' logBase base (base'^'nonce) '-' logBase basePowSec (basePowSec '^' 'proof_challenge'))@,
112 -- which means that the prover must have known 'nonce' and 'sec'
113 -- to compute 'proof_response' efficiently with:
114 -- @('proof_response' '==' nonce '-' sec '*' 'proof_challenge')@,
115 --
116 -- The 'nonce' is introduced to ensure each 'prove' does not reveal
117 -- any information regarding the prover's secret 'sec',
118 -- by being randomly chosen by the prover.
119 } deriving (Eq,Show)
120
121 -- ** Type 'ZKP'
122 -- | Zero-knowledge proof
123 --
124 -- DOC: Mihir Bellare and Phillip Rogaway. Random oracles are practical:
125 -- A paradigm for designing efficient protocols. In ACM-CCS’93, 1993.
126 --
127 -- DOC: Pierrick Gaudry. <https://hal.inria.fr/hal-01576379 Some ZK security proofs for Belenios>, 2017.
128 newtype ZKP = ZKP BS.ByteString
129
130 -- ** Type 'Challenge'
131 type Challenge = E
132
133 -- ** Type 'Oracle'
134 -- An 'Oracle' returns the 'Challenge' of the 'Commitment's
135 -- by 'hash'ing them (eventually with other 'Commitment's).
136 --
137 -- Used in 'prove' it enables a Fiat-Shamir transformation
138 -- of an /interactive zero-knowledge/ (IZK) proof
139 -- into a /non-interactive zero-knowledge/ (NIZK) proof.
140 -- That is to say that the verifier does not have
141 -- to send a 'Challenge' to the prover.
142 -- Indeed, the prover now handles the 'Challenge'
143 -- which becomes a (collision resistant) 'hash'
144 -- of the prover's commitments (and statements to be a stronger proof).
145 type Oracle list q = list (Commitment q) -> Challenge q
146
147 -- | @('prove' sec commitBases oracle)@
148 -- returns a 'Proof' that @sec@ is known.
149 --
150 -- The 'Oracle' is given the 'commitBases'
151 -- raised to the power of the secret nonce of the 'Proof',
152 -- as those are the 'commitBases' that the verifier will obtain
153 -- when composing the 'proof_challenge' and 'proof_response' together
154 -- (in 'commit').
155 --
156 -- NOTE: 'sec' is @secKey@ in 'signature_proof' or @encNonce@ in 'proveEncryption'.
157 --
158 -- WARNING: for 'prove' to be a so-called /strong Fiat-Shamir transformation/ (not a weak):
159 -- the statement must be included in the 'hash' (not only the commitments).
160 --
161 -- NOTE: a 'random' @nonce@ is used to ensure each 'prove'
162 -- does not reveal any information regarding the secret 'sec'.
163 prove ::
164 Monad m => RandomGen r => SubGroup q => Functor list =>
165 E q -> list (Commitment q) -> Oracle list q -> S.StateT r m (Proof q)
166 prove sec commitBases oracle = do
167 nonce <- random
168 let proof_challenge = oracle $ (^ nonce) <$> commitBases
169 return Proof
170 { proof_challenge
171 , proof_response = nonce - sec*proof_challenge
172 }
173
174 -- ** Type 'Commitment'
175 type Commitment = G
176
177 -- | @('commit' proof base basePowSec)@ returns a 'Commitment'
178 -- from the given 'Proof' with the knowledge of the verifier.
179 commit :: SubGroup q => Proof q -> G q -> G q -> Commitment q
180 commit Proof{..} base basePowSec =
181 base^proof_response *
182 basePowSec^proof_challenge
183 -- NOTE: Contrary to some textbook presentations,
184 -- @('*')@ is used instead of @('/')@ to avoid the performance cost
185 -- of a modular exponentiation @('^' ('groupOrder' '-' 'one'))@,
186 -- this is compensated by using @('-')@ instead of @('+')@ in 'prove'.
187 {-# INLINE commit #-}
188
189 -- * Type 'Disjunction'
190 -- | A 'Disjunction' is an 'inv'ersed @('groupGen' '^'opinion)@
191 -- it's used in 'proveEncryption' to generate a 'Proof'
192 -- that an 'encryption_vault' contains a given @('groupGen' '^'opinion)@,
193 type Disjunction = G
194
195 booleanDisjunctions :: SubGroup q => [Disjunction q]
196 booleanDisjunctions = List.take 2 groupGenInverses
197
198 intervalDisjunctions :: SubGroup q => Opinion q -> Opinion q -> [Disjunction q]
199 intervalDisjunctions mini maxi =
200 List.genericTake (fromMaybe 0 $ (nat maxi + 1)`minusNaturalMaybe`nat mini) $
201 List.genericDrop (nat mini) $
202 groupGenInverses
203
204 -- ** Type 'Opinion'
205 -- | Index of a 'Disjunction' within a list of them.
206 -- It is encrypted as an 'E'xponent by 'encrypt'.
207 type Opinion = E
208
209 -- ** Type 'DisjProof'
210 -- | A list of 'Proof's to prove that the 'Opinion' within an 'Encryption'
211 -- is indexing a 'Disjunction' within a list of them,
212 -- without revealing which 'Opinion' it is.
213 newtype DisjProof q = DisjProof [Proof q]
214 deriving (Eq,Show)
215
216 -- | @('proveEncryption' elecPubKey voterZKP (prevDisjs,nextDisjs) (encNonce,enc))@
217 -- returns a 'DisjProof' that 'enc' 'encrypt's
218 -- the 'Disjunction's between 'prevDisjs' and 'nextDisjs'.
219 --
220 -- A /NIZK Disjunctive Chaum Pedersen Logarithm Equality/ is used.
221 proveEncryption ::
222 forall m r q.
223 Monad m => RandomGen r => SubGroup q =>
224 PublicKey q -> ZKP ->
225 ([Disjunction q],[Disjunction q]) ->
226 (EncryptionNonce q, Encryption q) ->
227 S.StateT r m (DisjProof q)
228 proveEncryption elecPubKey voterZKP (prevDisjs,nextDisjs) (encNonce,enc) = do
229 -- Fake proofs for all values except the correct one.
230 prevFakes <- fakeProof `mapM` prevDisjs
231 nextFakes <- fakeProof `mapM` nextDisjs
232 let prevProofs = fst <$> prevFakes
233 let nextProofs = fst <$> nextFakes
234 let challengeSum =
235 sum (proof_challenge <$> prevProofs) +
236 sum (proof_challenge <$> nextProofs)
237 let statement = encryptionStatement voterZKP enc
238 correctProof <- prove encNonce [groupGen, elecPubKey] $
239 -- 'Oracle'
240 \correctCommitments ->
241 let commitments =
242 foldMap snd prevFakes <>
243 correctCommitments <>
244 foldMap snd nextFakes in
245 hash statement commitments - challengeSum
246 return $ DisjProof $ prevProofs <> (correctProof : nextProofs)
247 where
248 fakeProof :: Disjunction q -> S.StateT r m (Proof q, [Commitment q])
249 fakeProof disj = do
250 -- Returns 'Commitment's verifiables by the verifier,
251 -- but computed from random 'proof_challenge' and 'proof_response'
252 -- instead of correct ones.
253 proof_challenge <- random
254 proof_response <- random
255 let proof = Proof{..}
256 return (proof, encryptionCommitments elecPubKey enc (disj, proof))
257
258 verifyEncryption ::
259 Monad m =>
260 SubGroup q =>
261 PublicKey q -> ZKP ->
262 [Disjunction q] ->
263 (Encryption q, DisjProof q) ->
264 Exn.ExceptT ErrorValidateEncryption m Bool
265 verifyEncryption elecPubKey voterZKP disjs (enc, DisjProof proofs)
266 | List.length proofs /= List.length disjs =
267 Exn.throwE $ ErrorValidateEncryption_InvalidProofLength
268 (fromIntegral $ List.length proofs)
269 (fromIntegral $ List.length disjs)
270 | otherwise = return $ challengeSum == hash (encryptionStatement voterZKP enc) commitments
271 where
272 challengeSum = sum (proof_challenge <$> proofs)
273 commitments = foldMap
274 (encryptionCommitments elecPubKey enc)
275 (List.zip disjs proofs)
276
277 -- ** Hashing
278 encryptionStatement :: SubGroup q => ZKP -> Encryption q -> BS.ByteString
279 encryptionStatement (ZKP voterZKP) Encryption{..} =
280 "prove|"<>voterZKP<>"|"
281 <> bytesNat encryption_nonce<>","
282 <> bytesNat encryption_vault<>"|"
283 -- NOTE: the commitment base 'elecPubKey' is notably absent here
284 -- despite it being used in 'encryptionCommitments',
285 -- maybe this is not necessary because it is already known
286 -- by every participant.
287
288 -- | @('encryptionCommitments' elecPubKey enc (disj,proof))@
289 -- returns the 'Commitment's with only the knowledge of the verifier.
290 --
291 -- The 'Proof' comes from 'prove' of @fakeProof@ in 'proveEncryption'.
292 encryptionCommitments ::
293 SubGroup q =>
294 PublicKey q -> Encryption q ->
295 (Disjunction q, Proof q) -> [G q]
296 encryptionCommitments elecPubKey Encryption{..} (disj, proof) =
297 [ commit proof groupGen encryption_nonce
298 -- == groupGen ^ nonce if 'Proof' comes from 'prove'
299 , commit proof elecPubKey (encryption_vault*disj)
300 -- == elecPubKey ^ nonce if 'Proof' comes from 'prove'
301 -- and 'encryption_vault' encrypts (- logBase groupGen disj).
302 ]
303
304 -- ** Type 'ErrorValidateEncryption'
305 -- | Error raised by 'verifyEncryption'.
306 data ErrorValidateEncryption
307 = ErrorValidateEncryption_InvalidProofLength Natural Natural
308 -- ^ When the number of proofs is different than
309 -- the number of 'Disjunction's.
310 deriving (Eq,Show)
311
312 -- * Type 'Question'
313 data Question q = Question
314 { question_text :: Text
315 , question_choices :: [Text]
316 , question_mini :: Opinion q
317 , question_maxi :: Opinion q
318 -- , question_blank :: Maybe Bool
319 } deriving (Eq, Show)
320
321 -- * Type 'Answer'
322 data Answer q = Answer
323 { answer_opinions :: [(Encryption q, DisjProof q)]
324 -- ^ Encrypted 'Opinion' for each 'question_choices'
325 -- with a 'DisjProof' that they belong to [0,1].
326 , answer_sumProof :: DisjProof q
327 -- ^ Proofs that the sum of the 'Opinon's encrypted in 'answer_opinions'
328 -- is an element of @[mini..maxi]@.
329 -- , answer_blankProof ::
330 } deriving (Eq,Show)
331
332 -- | @('encryptAnswer' elecPubKey zkp quest opinions)@
333 -- returns an 'Answer' validable by 'verifyAnswer',
334 -- unless an 'ErrorAnswer' is returned.
335 encryptAnswer ::
336 Monad m => RandomGen r => SubGroup q =>
337 PublicKey q -> ZKP ->
338 Question q -> [Bool] ->
339 S.StateT r (Exn.ExceptT ErrorAnswer m) (Answer q)
340 encryptAnswer elecPubKey zkp Question{..} opinionsBools
341 | not (question_mini <= opinionsSum && opinionsSum <= question_maxi) =
342 lift $ Exn.throwE $
343 ErrorAnswer_WrongSumOfOpinions
344 (nat opinionsSum)
345 (nat question_mini)
346 (nat question_maxi)
347 | List.length opinions /= List.length question_choices =
348 lift $ Exn.throwE $
349 ErrorAnswer_WrongNumberOfOpinions
350 (fromIntegral $ List.length opinions)
351 (fromIntegral $ List.length question_choices)
352 | otherwise = do
353 encryptions <- encrypt elecPubKey `mapM` opinions
354 individualProofs <- zipWithM
355 (\opinion -> proveEncryption elecPubKey zkp $
356 if opinion
357 then ([booleanDisjunctions List.!!0],[])
358 else ([],[booleanDisjunctions List.!!1]))
359 opinionsBools encryptions
360 sumProof <- proveEncryption elecPubKey zkp
361 ((List.tail <$>) $ List.genericSplitAt (nat (opinionsSum - question_mini)) $
362 intervalDisjunctions question_mini question_maxi)
363 ( sum (fst <$> encryptions) -- NOTE: sum the 'encNonce's
364 , sum (snd <$> encryptions) -- NOTE: sum the 'Encryption's
365 )
366 return $ Answer
367 { answer_opinions = List.zip
368 (snd <$> encryptions) -- NOTE: drop encNonce
369 individualProofs
370 , answer_sumProof = sumProof
371 }
372 where
373 opinionsSum = sum opinions
374 opinions = (\o -> if o then one else zero) <$> opinionsBools
375
376 verifyAnswer ::
377 SubGroup q =>
378 PublicKey q -> ZKP ->
379 Question q -> Answer q -> Bool
380 verifyAnswer elecPubKey zkp Question{..} Answer{..}
381 | List.length question_choices /= List.length answer_opinions = False
382 | otherwise = either (const False) id $ Exn.runExcept $ do
383 validOpinions <-
384 verifyEncryption elecPubKey zkp booleanDisjunctions
385 `traverse` answer_opinions
386 validSum <- verifyEncryption elecPubKey zkp
387 (intervalDisjunctions question_mini question_maxi)
388 ( sum (fst <$> answer_opinions)
389 , answer_sumProof )
390 return (and validOpinions && validSum)
391
392 -- ** Type 'ErrorAnswer'
393 -- | Error raised by 'encryptAnswer'.
394 data ErrorAnswer
395 = ErrorAnswer_WrongNumberOfOpinions Natural Natural
396 -- ^ When the number of opinions is different than
397 -- the number of choices ('question_choices').
398 | ErrorAnswer_WrongSumOfOpinions Natural Natural Natural
399 -- ^ When the sum of opinions is not within the bounds
400 -- of 'question_mini' and 'question_maxi'.
401 deriving (Eq,Show)
402
403 -- * Type 'Election'
404 data Election q = Election
405 { election_name :: Text
406 , election_description :: Text
407 , election_publicKey :: PublicKey q
408 , election_questions :: [Question q]
409 , election_uuid :: UUID
410 , election_hash :: Hash -- TODO: serialize to JSON to calculate this
411 } deriving (Eq,Show)
412
413 -- ** Type 'Hash'
414 newtype Hash = Hash Text
415 deriving (Eq,Ord,Show)
416
417 -- * Type 'Ballot'
418 data Ballot q = Ballot
419 { ballot_answers :: [Answer q]
420 , ballot_signature :: Maybe (Signature q)
421 , ballot_election_uuid :: UUID
422 , ballot_election_hash :: Hash
423 }
424
425 -- | @('encryptBallot' elec ('Just' secKey) opinionsByQuest)@
426 -- returns a 'Ballot' signed by 'secKey' (the voter's secret key)
427 -- where 'opinionsByQuest' is a list of 'Opinion's
428 -- on each 'question_choices' of each 'election_questions'.
429 encryptBallot ::
430 Monad m => RandomGen r => SubGroup q =>
431 Election q -> Maybe (SecretKey q) -> [[Bool]] ->
432 S.StateT r (Exn.ExceptT ErrorBallot m) (Ballot q)
433 encryptBallot Election{..} secKeyMay opinionsByQuest
434 | List.length election_questions /= List.length opinionsByQuest =
435 lift $ Exn.throwE $
436 ErrorBallot_WrongNumberOfAnswers
437 (fromIntegral $ List.length opinionsByQuest)
438 (fromIntegral $ List.length election_questions)
439 | otherwise = do
440 let (voterKeys, voterZKP) =
441 case secKeyMay of
442 Nothing -> (Nothing, ZKP "")
443 Just secKey ->
444 ( Just (secKey, pubKey)
445 , ZKP (bytesNat pubKey) )
446 where pubKey = publicKey secKey
447 ballot_answers <-
448 hoist (Exn.withExceptT ErrorBallot_Answer) $
449 zipWithM (encryptAnswer election_publicKey voterZKP)
450 election_questions opinionsByQuest
451 ballot_signature <- case voterKeys of
452 Nothing -> return Nothing
453 Just (secKey, signature_publicKey) -> do
454 signature_proof <-
455 prove secKey (Identity groupGen) $
456 \(Identity commitment) ->
457 hash
458 -- NOTE: the order is unusual, the commitments are first
459 -- then comes the statement. Best guess is that
460 -- this is easier to code due to their respective types.
461 (signatureCommitments voterZKP commitment)
462 (signatureStatement ballot_answers)
463 return $ Just Signature{..}
464 return Ballot
465 { ballot_answers
466 , ballot_election_hash = election_hash
467 , ballot_election_uuid = election_uuid
468 , ballot_signature
469 }
470
471 verifyBallot :: SubGroup q => Election q -> Ballot q -> Bool
472 verifyBallot Election{..} Ballot{..} =
473 ballot_election_uuid == election_uuid &&
474 ballot_election_hash == election_hash &&
475 List.length election_questions == List.length ballot_answers &&
476 let (isValidSign, zkpSign) =
477 case ballot_signature of
478 Nothing -> (True, ZKP "")
479 Just Signature{..} ->
480 let zkp = ZKP (bytesNat signature_publicKey) in
481 (, zkp) $
482 proof_challenge signature_proof == hash
483 (signatureCommitments zkp (commit signature_proof groupGen signature_publicKey))
484 (signatureStatement ballot_answers)
485 in
486 and $ isValidSign :
487 List.zipWith (verifyAnswer election_publicKey zkpSign)
488 election_questions ballot_answers
489
490 -- ** Type 'Signature'
491 -- | Schnorr-like signature.
492 --
493 -- Used by each voter to sign his/her encrypted 'Ballot'
494 -- using his/her 'Credential',
495 -- in order to avoid ballot stuffing.
496 data Signature q = Signature
497 { signature_publicKey :: PublicKey q
498 -- ^ Verification key.
499 , signature_proof :: Proof q
500 }
501
502 -- *** Hashing
503
504 -- | @('signatureStatement' answers)@
505 -- returns the encrypted material to be signed:
506 -- all the 'encryption_nonce's and 'encryption_vault's of the given @answers@.
507 signatureStatement :: Foldable f => SubGroup q => f (Answer q) -> [G q]
508 signatureStatement =
509 foldMap $ \Answer{..} ->
510 (`foldMap` answer_opinions) $ \(Encryption{..}, _proof) ->
511 [encryption_nonce, encryption_vault]
512
513 -- | @('signatureCommitments' voterZKP commitment)@
514 signatureCommitments :: SubGroup q => ZKP -> Commitment q -> BS.ByteString
515 signatureCommitments (ZKP voterZKP) commitment =
516 "sig|"<>voterZKP<>"|"<>bytesNat commitment<>"|"
517
518 -- ** Type 'ErrorBallot'
519 -- | Error raised by 'encryptBallot'.
520 data ErrorBallot
521 = ErrorBallot_WrongNumberOfAnswers Natural Natural
522 -- ^ When the number of answers
523 -- is different than the number of questions.
524 | ErrorBallot_Answer ErrorAnswer
525 -- ^ When 'encryptAnswer' raised an 'ErrorAnswer'.
526 deriving (Eq,Show)