1 {-# LANGUAGE AllowAmbiguousTypes #-}
2 {-# LANGUAGE DerivingStrategies #-}
4 {-# LANGUAGE OverloadedStrings #-}
5 {-# LANGUAGE Rank2Types #-} -- for ReifyCrypto
6 module Voting.Protocol.Arithmetic where
8 import Control.Arrow (first)
9 import Control.DeepSeq (NFData)
10 import Control.Monad (Monad(..))
11 import Data.Aeson (ToJSON(..),FromJSON(..))
14 import Data.Eq (Eq(..))
15 import Data.Foldable (Foldable, foldl')
16 import Data.Function (($), (.), id)
18 import Data.Maybe (Maybe(..), fromJust)
19 import Data.Ord (Ord(..))
20 import Data.Proxy (Proxy(..))
21 import Data.Reflection (Reifies(..))
22 import Data.String (IsString(..))
23 import GHC.Natural (minusNaturalMaybe)
24 import Numeric.Natural (Natural)
25 import Prelude (Integer, Bounded(..), Integral(..), fromIntegral, Enum(..))
26 import Text.Read (readMaybe)
27 import Text.Show (Show(..))
28 import qualified Data.Aeson as JSON
29 import qualified Data.Aeson.Types as JSON
30 import qualified Data.ByteString as BS
31 import qualified Data.Char as Char
32 import qualified Data.List as List
33 import qualified Data.Text as Text
34 import qualified Prelude as Num
35 import qualified System.Random as Random
37 -- * Class 'CryptoParams' where
39 ( EuclideanRing (G crypto c)
40 , FromNatural (G crypto c)
41 , ToNatural (G crypto c)
46 , FromJSON (G crypto c)
49 ) => CryptoParams crypto c where
50 -- | A generator of the subgroup.
51 groupGen :: G crypto c
52 -- | The order of the subgroup.
53 groupOrder :: Proxy c -> Natural
55 -- | 'groupGenPowers' returns the infinite list
56 -- of powers of 'groupGen'.
58 -- NOTE: In the 'CryptoParams' class to keep
59 -- computed values in memory across calls to 'groupGenPowers'.
60 groupGenPowers :: [G crypto c]
61 groupGenPowers = go one
62 where go g = g : go (g * groupGen)
64 -- | 'groupGenInverses' returns the infinite list
65 -- of 'inverse' powers of 'groupGen':
66 -- @['groupGen' '^' 'negate' i | i <- [0..]]@,
67 -- but by computing each value from the previous one.
69 -- NOTE: In the 'CryptoParams' class to keep
70 -- computed values in memory across calls to 'groupGenInverses'.
72 -- Used by 'intervalDisjunctions'.
73 groupGenInverses :: [G crypto c]
74 groupGenInverses = go one
76 invGen = inverse groupGen
77 go g = g : go (g * invGen)
79 -- ** Class 'ReifyCrypto'
80 class ReifyCrypto crypto where
81 -- | Like 'reify' but augmented with the 'CryptoParams' constraint.
82 reifyCrypto :: crypto -> (forall c. Reifies c crypto => CryptoParams crypto c => Proxy c -> r) -> r
85 -- | An additive semigroup.
86 class Additive a where
88 (+) :: a -> a -> a; infixl 6 +
89 sum :: Foldable f => f a -> a
91 instance Additive Natural where
94 instance Additive Integer where
97 instance Additive Int where
101 -- * Class 'Semiring'
102 -- | A multiplicative semigroup, with an additive semigroup (aka. a semiring).
103 class Additive a => Semiring a where
105 (*) :: a -> a -> a; infixl 7 *
106 instance Semiring Natural where
109 instance Semiring Integer where
112 instance Semiring Int where
116 -- | @(b '^' e)@ returns the modular exponentiation of base 'b' by exponent 'e'.
120 Semiring (G crypto c) =>
121 G crypto c -> E crypto c -> G crypto c
124 | otherwise = t * (b*b) ^ E (e`shiftR`1)
125 where t | testBit e 0 = b
130 -- | A semiring that support substraction (aka. a ring).
131 class Semiring a => Ring a where
133 (-) :: a -> a -> a; infixl 6 -
135 instance Ring Integer where
137 instance Ring Int where
140 -- ** Class 'EuclideanRing'
141 -- | A commutative ring that support division (aka. an euclidean ring).
142 class Ring a => EuclideanRing a where
144 (/) :: a -> a -> a; infixl 7 /
148 -- | The type of the elements of a subgroup of a field.
149 newtype G crypto c = G { unG :: FieldElement crypto }
151 -- *** Type family 'FieldElement'
152 type family FieldElement crypto :: *
155 -- | An exponent of a (cyclic) subgroup of a field.
156 -- The value is always in @[0..'groupOrder'-1]@.
157 newtype E crypto c = E { unE :: Natural }
158 deriving (Eq,Ord,Show)
159 deriving newtype NFData
160 instance ToJSON (E crypto c) where
161 toJSON = JSON.toJSON . show . unE
162 instance CryptoParams crypto c => FromJSON (E crypto c) where
163 parseJSON (JSON.String s)
164 | Just (c0,_) <- Text.uncons s
166 , Text.all Char.isDigit s
167 , Just x <- readMaybe (Text.unpack s)
168 , x < groupOrder (Proxy @c)
170 parseJSON json = JSON.typeMismatch "Exponent" json
171 instance CryptoParams crypto c => FromNatural (E crypto c) where
172 fromNatural n = E $ n `mod` groupOrder (Proxy @c)
173 instance ToNatural (E crypto c) where
175 instance CryptoParams crypto c => Additive (E crypto c) where
177 E x + E y = E $ (x + y) `mod` groupOrder (Proxy @c)
178 instance CryptoParams crypto c => Semiring (E crypto c) where
180 E x * E y = E $ (x * y) `mod` groupOrder (Proxy @c)
181 instance CryptoParams crypto c => Ring (E crypto c) where
182 negate (E x) = E $ fromJust $ groupOrder (Proxy @c)`minusNaturalMaybe`x
183 instance CryptoParams crypto c => Random.Random (E crypto c) where
184 randomR (E lo, E hi) =
185 first (E . fromIntegral) .
188 , toInteger hi`min`(toInteger (groupOrder (Proxy @c)) - 1) )
190 first (E . fromIntegral) .
191 Random.randomR (0, toInteger (groupOrder (Proxy @c)) - 1)
192 instance CryptoParams crypto c => Enum (E crypto c) where
193 toEnum = fromNatural . fromIntegral
194 fromEnum = fromIntegral . nat
195 enumFromTo lo hi = List.unfoldr
196 (\i -> if i<=hi then Just (i, i+one) else Nothing) lo
197 instance CryptoParams crypto c => Bounded (E crypto c) where
199 maxBound = E $ fromJust $ groupOrder (Proxy @c)`minusNaturalMaybe`1
201 -- * Class 'FromNatural'
202 class FromNatural a where
203 fromNatural :: Natural -> a
204 instance FromNatural Natural where
207 -- * Class 'ToNatural'
208 class ToNatural a where
210 instance ToNatural Natural where
213 -- | @('bytesNat' x)@ returns the serialization of 'x'.
214 bytesNat :: ToNatural n => n -> BS.ByteString
215 bytesNat = fromString . show . nat