1 {-# LANGUAGE DeriveAnyClass #-}
2 {-# LANGUAGE DeriveGeneric #-}
3 {-# LANGUAGE DerivingStrategies #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE UndecidableInstances #-} -- for Reifies instances
6 {-# OPTIONS_GHC -fno-warn-orphans #-}
7 -- | Finite Field Cryptography (FFC)
8 -- is a method of implementing discrete logarithm cryptography
9 -- using finite field mathematics.
10 module Voting.Protocol.FFC
11 ( module Voting.Protocol.FFC
18 import Control.Arrow (first)
19 import Control.Applicative (Applicative(..))
20 import Control.DeepSeq (NFData)
21 import Control.Monad (Monad(..), unless)
22 import Control.Monad.Trans.Reader (ReaderT(..), asks)
23 import Control.Monad.Trans.Class (MonadTrans(..))
24 import Data.Aeson (ToJSON(..),FromJSON(..),(.:),(.:?),(.=))
27 import Data.Eq (Eq(..))
28 import Data.Foldable (Foldable, foldl')
29 import Data.Function (($), (.), id)
30 import Data.Functor ((<$>))
32 import Data.Maybe (Maybe(..), fromMaybe, fromJust)
33 import Data.Ord (Ord(..))
34 import Data.Proxy (Proxy(..))
35 import Data.Reflection (Reifies(..), reify)
36 import Data.Semigroup (Semigroup(..))
37 import Data.String (IsString(..))
38 import Data.Text (Text)
39 import GHC.Generics (Generic)
40 import GHC.Natural (minusNaturalMaybe)
41 import Numeric.Natural (Natural)
42 import Prelude (Integer, Integral(..), fromIntegral, Enum(..))
43 import Text.Read (readMaybe)
44 import Text.Show (Show(..))
45 import qualified Control.Monad.Trans.State.Strict as S
46 import qualified Crypto.Hash as Crypto
47 import qualified Data.Aeson as JSON
48 import qualified Data.Aeson.Types as JSON
49 import qualified Data.ByteArray as ByteArray
50 import qualified Data.ByteString as BS
51 import qualified Data.Char as Char
52 import qualified Data.List as List
53 import qualified Data.Text as Text
54 import qualified Prelude as Num
55 import qualified System.Random as Random
58 -- | Mutiplicative Sub-Group of a Finite Prime Field.
60 -- NOTE: an 'FFC' term-value is brought into the context of many functions
61 -- through a type-variable @c@ whose 'Reifies' constraint enables to 'reflect'
62 -- that 'FFC' at the term-level (a surprising technique but a very useful one).
63 -- Doing like this is simpler than working in a 'Monad' (like a 'Reader'),
64 -- and enables that 'FFC' term to be used simply in instances' methods
65 -- not supporting an inner 'Monad', like 'parseJSON', 'randomR', 'fromEnum' or 'arbitrary'.
66 -- Aside from that, the sharing of 'FFC' amongst several types
67 -- is encoded at the type-level by including @c@
68 -- as a phantom type of 'F', 'G' and 'E'.
71 , ffc_fieldCharac :: !Natural
72 -- ^ The prime number characteristic of a Finite Prime Field.
74 -- ElGamal's hardness to decrypt requires a large prime number
75 -- to form the 'Multiplicative' subgroup.
76 , ffc_groupGen :: !Natural
77 -- ^ A generator of the 'Multiplicative' subgroup of the Finite Prime Field.
79 -- NOTE: since 'ffc_fieldCharac' is prime,
80 -- the 'Multiplicative' subgroup is cyclic,
81 -- and there are phi('fieldCharac'-1) many choices for the generator of the group,
82 -- where phi is the Euler totient function.
83 , ffc_groupOrder :: !Natural
84 -- ^ The order of the subgroup.
86 -- WARNING: 'ffc_groupOrder' MUST be a prime number dividing @('ffc_fieldCharac'-1)@
87 -- to ensure that ElGamal is secure in terms of the DDH assumption.
88 } deriving (Eq,Show,Generic,NFData)
89 deriving instance ToJSON FFC
90 instance FromJSON FFC where
91 parseJSON = JSON.withObject "FFC" $ \o -> do
92 ffc_name <- fromMaybe "" <$> (o .:? "name")
93 ffc_fieldCharac <- o .: "p"
94 ffc_groupGen <- o .: "g"
95 ffc_groupOrder <- o .: "q"
96 -- TODO: check p is probable prime
97 -- TODO: check q is probable prime
98 unless (nat ffc_groupGen < ffc_fieldCharac) $
99 JSON.typeMismatch "FFC: groupGen is not lower than fieldCharac" (JSON.Object o)
100 unless (ffc_groupOrder < ffc_fieldCharac) $
101 JSON.typeMismatch "FFC: groupOrder is not lower than fieldCharac" (JSON.Object o)
102 unless (nat ffc_groupGen > 1) $
103 JSON.typeMismatch "FFC: groupGen is not greater than 1" (JSON.Object o)
104 unless (fromJust (ffc_fieldCharac`minusNaturalMaybe`one) `rem` ffc_groupOrder == 0) $
105 JSON.typeMismatch "FFC: groupOrder does not divide fieldCharac-1" (JSON.Object o)
108 fieldCharac :: forall c. Reifies c FFC => Natural
109 fieldCharac = ffc_fieldCharac (reflect (Proxy::Proxy c))
111 groupGen :: forall c. Reifies c FFC => G c
112 groupGen = G $ F $ ffc_groupGen (reflect (Proxy::Proxy c))
114 groupOrder :: forall c. Reifies c FFC => Natural
115 groupOrder = ffc_groupOrder (reflect (Proxy::Proxy c))
118 -- | Weak parameters for debugging purposes only.
121 { ffc_name = "weakFFC"
122 , ffc_fieldCharac = 263
124 , ffc_groupOrder = 131
127 -- | Parameters used in Belenios.
128 -- A 2048-bit 'fieldCharac' of a Finite Prime Field,
129 -- with a 256-bit 'groupOrder' for a 'Multiplicative' subgroup
130 -- generated by 'groupGen'.
133 { ffc_name = "beleniosFFC"
134 , ffc_fieldCharac = 20694785691422546401013643657505008064922989295751104097100884787057374219242717401922237254497684338129066633138078958404960054389636289796393038773905722803605973749427671376777618898589872735865049081167099310535867780980030790491654063777173764198678527273474476341835600035698305193144284561701911000786737307333564123971732897913240474578834468260652327974647951137672658693582180046317922073668860052627186363386088796882120769432366149491002923444346373222145884100586421050242120365433561201320481118852408731077014151666200162313177169372189248078507711827842317498073276598828825169183103125680162072880719
135 , ffc_groupGen = 2402352677501852209227687703532399932712287657378364916510075318787663274146353219320285676155269678799694668298749389095083896573425601900601068477164491735474137283104610458681314511781646755400527402889846139864532661215055797097162016168270312886432456663834863635782106154918419982534315189740658186868651151358576410138882215396016043228843603930989333662772848406593138406010231675095763777982665103606822406635076697764025346253773085133173495194248967754052573659049492477631475991575198775177711481490920456600205478127054728238140972518639858334115700568353695553423781475582491896050296680037745308460627
136 , ffc_groupOrder = 78571733251071885079927659812671450121821421258408794611510081919805623223441
140 -- | The type of the elements of a Finite Prime Field.
142 -- A field must satisfy the following properties:
144 -- * @(f, ('+'), 'zero')@ forms an abelian group,
145 -- called the 'Additive' group of 'f'.
147 -- * @('NonNull' f, ('*'), 'one')@ forms an abelian group,
148 -- called the 'Multiplicative' group of 'f'.
150 -- * ('*') is associative:
151 -- @(a'*'b)'*'c == a'*'(b'*'c)@ and
152 -- @a'*'(b'*'c) == (a'*'b)'*'c@.
154 -- * ('*') and ('+') are both commutative:
155 -- @a'*'b == b'*'a@ and
158 -- * ('*') and ('+') are both left and right distributive:
159 -- @a'*'(b'+'c) == (a'*'b) '+' (a'*'c)@ and
160 -- @(a'+'b)'*'c == (a'*'c) '+' (b'*'c)@
162 -- The 'Natural' is always within @[0..'fieldCharac'-1]@.
163 newtype F c = F { unF :: Natural }
164 deriving (Eq,Ord,Show)
165 deriving newtype NFData
166 instance ToJSON (F c) where
167 toJSON (F x) = JSON.toJSON (show x)
168 instance Reifies c FFC => FromJSON (F c) where
169 parseJSON (JSON.String s)
170 | Just (c0,_) <- Text.uncons s
172 , Text.all Char.isDigit s
173 , Just x <- readMaybe (Text.unpack s)
176 parseJSON json = JSON.typeMismatch "F" json
177 instance Reifies c FFC => FromNatural (F c) where
178 fromNatural i = F $ abs $ i `mod` fieldCharac @c
180 abs x | x < 0 = x + fieldCharac @c
182 instance ToNatural (F c) where
184 instance Reifies c FFC => Additive (F c) where
186 F x + F y = F $ (x + y) `mod` fieldCharac @c
187 instance Reifies c FFC => Negable (F c) where
190 | otherwise = F $ fromJust $ nat (fieldCharac @c)`minusNaturalMaybe`x
191 instance Reifies c FFC => Multiplicative (F c) where
193 F x * F y = F $ (x * y) `mod` fieldCharac @c
194 instance Reifies c FFC => Random.Random (F c) where
195 randomR (F lo, F hi) =
196 first (F . fromIntegral) .
199 , toInteger hi`min`(toInteger (fieldCharac @c) - 1) )
201 first (F . fromIntegral) .
202 Random.randomR (0, toInteger (fieldCharac @c) - 1)
204 -- ** Class 'Additive'
205 class Additive a where
207 (+) :: a -> a -> a; infixl 6 +
208 sum :: Foldable f => f a -> a
209 sum = foldl' (+) zero
210 instance Additive Natural where
213 instance Additive Integer where
216 instance Additive Int where
220 -- *** Class 'Negable'
221 class Additive a => Negable a where
223 (-) :: a -> a -> a; infixl 6 -
225 instance Negable Integer where
227 instance Negable Int where
230 -- ** Class 'Multiplicative'
231 class Multiplicative a where
233 (*) :: a -> a -> a; infixl 7 *
234 instance Multiplicative Natural where
237 instance Multiplicative Integer where
240 instance Multiplicative Int where
244 -- ** Class 'Invertible'
245 class Multiplicative a => Invertible a where
247 (/) :: a -> a -> a; infixl 7 /
251 -- | The type of the elements of a 'Multiplicative' subgroup of a Finite Prime Field.
252 newtype G c = G { unG :: F c }
253 deriving (Eq,Ord,Show)
254 deriving newtype NFData
255 instance ToJSON (G c) where
256 toJSON (G x) = JSON.toJSON x
257 instance Reifies c FFC => FromJSON (G c) where
258 parseJSON (JSON.String s)
259 | Just (c0,_) <- Text.uncons s
261 , Text.all Char.isDigit s
262 , Just x <- readMaybe (Text.unpack s)
265 , r ^ E (groupOrder @c) == one
267 parseJSON json = JSON.typeMismatch "G" json
268 instance Reifies c FFC => FromNatural (G c) where
269 fromNatural = G . fromNatural
270 instance ToNatural (G c) where
272 instance Reifies c FFC => Multiplicative (G c) where
274 G x * G y = G (x * y)
275 instance Reifies c FFC => Invertible (G c) where
276 -- | NOTE: add 'groupOrder' so the exponent given to (^) is positive.
277 inv = (^ E (fromJust $ groupOrder @c`minusNaturalMaybe`1))
279 -- | 'groupGenInverses' returns the infinite list
280 -- of 'inv'erse powers of 'groupGen':
281 -- @['groupGen' '^' 'neg' i | i <- [0..]]@,
282 -- but by computing each value from the previous one.
284 -- Used by 'intervalDisjunctions'.
285 groupGenInverses :: forall c. Reifies c FFC => [G c]
286 groupGenInverses = go one
288 invGen = inv $ groupGen @c
289 go g = g : go (g * invGen)
291 groupGenPowers :: forall c. Reifies c FFC => [G c]
292 groupGenPowers = go one
293 where go g = g : go (g * groupGen @c)
295 -- | @('hash' bs gs)@ returns as a number in 'E'
296 -- the SHA256 of the given 'BS.ByteString' 'bs'
297 -- prefixing the decimal representation of given subgroup elements 'gs',
298 -- with a comma (",") intercalated between them.
300 -- NOTE: to avoid any collision when the 'hash' function is used in different contexts,
301 -- a message 'gs' is actually prefixed by a 'bs' indicating the context.
303 -- Used by 'proveEncryption' and 'verifyEncryption',
304 -- where the 'bs' usually contains the 'statement' to be proven,
305 -- and the 'gs' contains the 'commitments'.
306 hash :: Reifies c FFC => BS.ByteString -> [G c] -> E c
308 let s = bs <> BS.intercalate (fromString ",") (bytesNat <$> gs)
309 let h = ByteArray.convert (Crypto.hashWith Crypto.SHA256 s)
310 fromNatural (BS.foldl' (\acc b -> acc`shiftL`3 + fromIntegral b) (0::Natural) h)
313 -- | An exponent of a (necessarily cyclic) subgroup of a Finite Prime Field.
314 -- The value is always in @[0..'groupOrder'-1]@.
315 newtype E c = E { unE :: Natural }
316 deriving (Eq,Ord,Show)
317 deriving newtype NFData
318 instance ToJSON (E c) where
319 toJSON (E x) = JSON.toJSON x
320 instance Reifies c FFC => FromJSON (E c) where
321 parseJSON (JSON.String s)
322 | Just (c0,_) <- Text.uncons s
324 , Text.all Char.isDigit s
325 , Just x <- readMaybe (Text.unpack s)
328 parseJSON json = JSON.typeMismatch "E" json
330 instance Reifies c FFC => FromNatural (E c) where
332 E $ abs $ i `mod` groupOrder @c
334 abs x | x < 0 = x + groupOrder @c
336 instance ToNatural (E c) where
339 instance Reifies c FFC => Additive (E c) where
341 E x + E y = E $ (x + y) `mod` groupOrder @c
342 instance Reifies c FFC => Negable (E c) where
345 | otherwise = E $ fromJust $ nat (groupOrder @c)`minusNaturalMaybe`x
346 instance Reifies c FFC => Multiplicative (E c) where
348 E x * E y = E $ (x * y) `mod` groupOrder @c
349 instance Reifies c FFC => Random.Random (E c) where
350 randomR (E lo, E hi) =
351 first (E . fromIntegral) .
354 , toInteger hi`min`(toInteger (groupOrder @c) - 1) )
356 first (E . fromIntegral) .
357 Random.randomR (0, toInteger (groupOrder @c) - 1)
358 instance Reifies c FFC => Enum (E c) where
359 toEnum = fromNatural . fromIntegral
360 fromEnum = fromIntegral . nat
361 enumFromTo lo hi = List.unfoldr
362 (\i -> if i<=hi then Just (i, i+one) else Nothing) lo
365 -- | @(b '^' e)@ returns the modular exponentiation of base 'b' by exponent 'e'.
366 (^) :: Reifies c FFC => G c -> E c -> G c
369 | otherwise = t * (b*b) ^ E (e`shiftR`1)
374 -- | @('randomR' i)@ returns a random integer in @[0..i-1]@.
377 Random.RandomGen r =>
382 randomR i = S.StateT $ return . Random.randomR (zero, i-one)
384 -- | @('random')@ returns a random integer
385 -- in the range determined by its type.
388 Random.RandomGen r =>
393 random = S.StateT $ return . Random.random
395 instance Random.Random Natural where
396 randomR (mini,maxi) =
397 first (fromIntegral::Integer -> Natural) .
398 Random.randomR (fromIntegral mini, fromIntegral maxi)
399 random = first (fromIntegral::Integer -> Natural) . Random.random
403 -- ** Class 'FromNatural'
404 class FromNatural a where
405 fromNatural :: Natural -> a
407 -- ** Class 'ToNatural'
408 class ToNatural a where
410 instance ToNatural Natural where
413 -- | @('bytesNat' x)@ returns the serialization of 'x'.
414 bytesNat :: ToNatural n => n -> BS.ByteString
415 bytesNat = fromString . show . nat