{-# OPTIONS_HADDOCK hide #-} {-# LANGUAGE NamedFieldPuns #-} {-# LANGUAGE TypeFamilies #-} {-| Module: Crypto.Spake2.Groups.IntegerGroup Description: Multiplicative group of integers modulo \(n\) -} module Crypto.Spake2.Groups.IntegerGroup ( IntegerGroup(..) , makeIntegerGroup , i1024 ) where import Protolude hiding (group, length) import Crypto.Error (CryptoFailable(..), CryptoError(..)) import Crypto.Number.Basic (numBits) import Crypto.Number.Generate (generateMax) import Crypto.Number.ModArithmetic (expSafe) import Crypto.Spake2.Group ( AbelianGroup(..) , Group(..) , KeyPair(..) , elementSizeBytes ) import Crypto.Spake2.Util ( expandArbitraryElementSeed , bytesToNumber , unsafeNumberToBytes ) -- | A finite group of integers with respect to multiplication modulo the group order. -- -- Construct with 'makeIntegerGroup'. data IntegerGroup = IntegerGroup { order :: !Integer , subgroupOrder :: !Integer , generator :: !Integer } deriving (Eq, Show) -- | Construct an 'IntegerGroup'. -- -- Will fail if generator is '1', -- since having the identity for a generator means the subgroup is the entire group. -- -- TODO: Find other things to check for validity. makeIntegerGroup :: Integer -> Integer -> Integer -> Maybe IntegerGroup makeIntegerGroup _ _ 1 = Nothing makeIntegerGroup order subgroupOrder generator = Just (IntegerGroup order subgroupOrder generator) instance Group IntegerGroup where type Element IntegerGroup = Integer elementAdd group x y = (x * y) `mod` order group -- At a guess, negation is scalar multiplication where the scalar is -1 elementNegate group x = expSafe x (subgroupOrder group - 1) (order group) groupIdentity _ = 1 encodeElement group = unsafeNumberToBytes (elementSizeBytes group) decodeElement group bytes = case bytesToNumber bytes of x | x <= 0 || x >= order group -> CryptoFailed CryptoError_PointSizeInvalid | expSafe x (subgroupOrder group) (order group) /= groupIdentity group -> CryptoFailed CryptoError_PointCoordinatesInvalid | otherwise -> CryptoPassed x elementSizeBits group = numBits (order group) arbitraryElement group seed = let processedSeed = expandArbitraryElementSeed seed (elementSizeBytes group) :: ByteString p = order group q = subgroupOrder group r = (p - 1) `div` q h = bytesToNumber processedSeed `mod` p in expSafe h r p instance AbelianGroup IntegerGroup where type Scalar IntegerGroup = Integer scalarMultiply group n x = expSafe x (n `mod` subgroupOrder group) (order group) integerToScalar group x = x `mod` subgroupOrder group scalarToInteger _ n = n generateElement group = do scalar <- generateMax (subgroupOrder group) let element = scalarMultiply group scalar (generator group) pure (KeyPair element scalar) scalarSizeBits group = numBits (subgroupOrder group) -- | 1024 bit integer group. -- -- Originally from http://haofeng66.googlepages.com/JPAKEDemo.java, -- via [python-spake2](https://github.com/warner/python-spake2). i1024 :: IntegerGroup i1024 = IntegerGroup { order = 0xE0A67598CD1B763BC98C8ABB333E5DDA0CD3AA0E5E1FB5BA8A7B4EABC10BA338FAE06DD4B90FDA70D7CF0CB0C638BE3341BEC0AF8A7330A3307DED2299A0EE606DF035177A239C34A912C202AA5F83B9C4A7CF0235B5316BFC6EFB9A248411258B30B839AF172440F32563056CB67A861158DDD90E6A894C72A5BBEF9E286C6B , subgroupOrder = 0xE950511EAB424B9A19A2AEB4E159B7844C589C4F , generator = 0xD29D5121B0423C2769AB21843E5A3240FF19CACC792264E3BB6BE4F78EDD1B15C4DFF7F1D905431F0AB16790E1F773B5CE01C804E509066A9919F5195F4ABC58189FD9FF987389CB5BEDF21B4DAB4F8B76A055FFE2770988FE2EC2DE11AD92219F0B351869AC24DA3D7BA87011A701CE8EE7BFE49486ED4527B7186CA4610A75 }