pairing 0.2 → 0.3.0
raw patch · 16 files changed
+531/−114 lines, 16 filesdep +binarydep +errorsdep +integer-logarithms
Dependencies added: binary, errors, integer-logarithms
Files
- ChangeLog.md +8/−0
- bench/BenchPairing.hs +13/−1
- pairing.cabal +15/−3
- src/Pairing/ByteRepr.hs +33/−0
- src/Pairing/CyclicGroup.hs +19/−0
- src/Pairing/Fq.hs +27/−29
- src/Pairing/Fq12.hs +14/−1
- src/Pairing/Fq2.hs +64/−2
- src/Pairing/Fq6.hs +16/−1
- src/Pairing/Fr.hs +16/−16
- src/Pairing/Group.hs +56/−7
- src/Pairing/Hash.hs +22/−27
- src/Pairing/Modular.hs +39/−23
- src/Pairing/Serialize.hs +105/−0
- tests/TestFields.hs +18/−0
- tests/TestGroups.hs +66/−4
ChangeLog.md view
@@ -1,5 +1,13 @@ # Changelog for pairing +## 0.3++- Square root calculation on Fq2+- Both square roots returned on Fq+- Point serialisation for G1, G2 and GT++## 0.2+ ## 0.1 * Initial release.
bench/BenchPairing.hs view
@@ -8,7 +8,7 @@ import qualified Pairing.Group as Group import qualified Pairing.Point as Point import qualified Pairing.Pairing as Pairing-+import Pairing.CyclicGroup (asInteger) import qualified Pairing.Fq as Fq import qualified Pairing.Fr as Fr import qualified Pairing.Fq2 as Fq2@@ -159,8 +159,12 @@ $ whnf (uncurry (+)) (testFq_1, testFq_2) , bench "division" $ whnf (uncurry (/)) (testFq_1, testFq_2)+ , bench "pow"+ $ whnf (Fq.fqPow testFq_1) (asInteger testFr_1) , bench "inversion" $ whnf Fq.fqInv testFq_1+ , bench "fqFromX"+ $ whnf (Fq.fqYforX testFq_1) True ] , bgroup "Fr"@@ -172,6 +176,8 @@ $ whnf (uncurry (/)) (testFr_1, testFr_2) , bench "inversion" $ whnf Fr.frInv testFr_1+ , bench "pow"+ $ whnf (Fr.frPow testFr_1) (asInteger testFr_2) ] , bgroup "Fq2"@@ -183,12 +189,18 @@ $ whnf (uncurry (/)) (testFq2_1, testFq2_2) , bench "squaring" $ whnf Fq2.fq2sqr testFq2_1+ , bench "pow"+ $ whnf (Fq2.fq2pow testFq2_1) (asInteger testFr_1) , bench "negation" $ whnf negate testFq2_1 , bench "inversion" $ whnf Fq2.fq2inv testFq2_1 , bench "conjugation" $ whnf Fq2.fq2conj testFq2_1+ , bench "square root"+ $ whnf Fq2.fq2sqrt testFq2_1+ , bench "fq2FromX"+ $ whnf (Fq2.fq2YforX testFq2_1) True ] , bgroup "Fq6"
pairing.cabal view
@@ -2,10 +2,10 @@ -- -- see: https://github.com/sol/hpack ----- hash: dabb8024b0664bed6aa89f042dbbc95b098bf5f62fb35bd2d70d4a79992e2314+-- hash: d047536a7348fa341206f72c42c935d58cf595375dbccae1dbcf0789040cbeb0 name: pairing-version: 0.2+version: 0.3.0 synopsis: Optimal ate pairing over Barreto-Naehrig curves description: Optimal ate pairing over Barreto-Naehrig curves category: Cryptography@@ -48,18 +48,23 @@ Pairing.Jacobian Pairing.CyclicGroup Pairing.Hash+ Pairing.Serialize+ Pairing.ByteRepr other-modules: Pairing.Modular hs-source-dirs: src- default-extensions: LambdaCase RecordWildCards OverloadedStrings NoImplicitPrelude FlexibleInstances ExplicitForAll RankNTypes DataKinds KindSignatures GeneralizedNewtypeDeriving TypeApplications ExistentialQuantification ScopedTypeVariables DeriveGeneric+ default-extensions: LambdaCase RecordWildCards OverloadedStrings NoImplicitPrelude FlexibleInstances ExplicitForAll RankNTypes DataKinds KindSignatures GeneralizedNewtypeDeriving TypeApplications ExistentialQuantification ScopedTypeVariables DeriveGeneric BangPatterns FlexibleContexts ghc-options: -fwarn-tabs -fwarn-incomplete-patterns -fwarn-incomplete-record-updates -fwarn-redundant-constraints -fwarn-implicit-prelude -fwarn-overflowed-literals -fwarn-orphans -fwarn-identities -fwarn-dodgy-exports -fwarn-dodgy-imports -fwarn-duplicate-exports -fwarn-overlapping-patterns -fwarn-missing-fields -fwarn-missing-methods -fwarn-missing-signatures -fwarn-noncanonical-monad-instances -fwarn-unused-pattern-binds -fwarn-unused-type-patterns -fwarn-unrecognised-pragmas -fwarn-wrong-do-bind -fno-warn-name-shadowing -fno-warn-unused-binds -fno-warn-unused-matches -fno-warn-unused-do-bind build-depends: QuickCheck , arithmoi , base >=4.7 && <5+ , binary , bytestring , cryptonite+ , errors+ , integer-logarithms , memory , protolude >=0.2 , random@@ -77,12 +82,16 @@ Paths_pairing hs-source-dirs: tests+ default-extensions: FlexibleContexts build-depends: QuickCheck , arithmoi , base+ , binary , bytestring , cryptonite+ , errors+ , integer-logarithms , memory , pairing , protolude >=0.2@@ -106,9 +115,12 @@ QuickCheck , arithmoi , base >=4.7 && <5+ , binary , bytestring , criterion , cryptonite+ , errors+ , integer-logarithms , memory , pairing , protolude >=0.2
+ src/Pairing/ByteRepr.hs view
@@ -0,0 +1,33 @@+module Pairing.ByteRepr where + +import Protolude +import Data.ByteString as B +import Data.ByteString.Builder + +class ByteRepr a where + mkRepr :: a -> Maybe ByteString + fromRepr :: a -> ByteString -> Maybe a + reprLength :: a -> Int + +toBytes :: Integer -> ByteString +toBytes x = B.reverse . B.unfoldr (fmap go) . Just $ changeSign x + where + changeSign :: Num a => a -> a + changeSign | x < 0 = subtract 1 . negate + | otherwise = identity + go :: Integer -> (Word8, Maybe Integer) + go x = ( b, i ) + where + b = changeSign (fromInteger x) + i | x >= 128 = Just (x `shiftR` 8 ) + | otherwise = Nothing + +toPaddedBytes :: Int -> Integer -> Maybe ByteString +toPaddedBytes len a = if B.length bs > len then Nothing else Just (B.append (B.replicate (len - B.length bs) 0x0) bs) + where + bs = toBytes a + +fromBytesToInteger :: ByteString -> Integer +fromBytesToInteger = B.foldl' f 0 + where + f a b = a `shiftL` 8 .|. fromIntegral b
src/Pairing/CyclicGroup.hs view
@@ -2,18 +2,26 @@ ( AsInteger(..) , CyclicGroup(..) , sumG+ , FromX(..)+ , Validate (..) ) where import Protolude+import Crypto.Random (MonadRandom)+import Data.ByteString.Builder+import Data.ByteString as BS class AsInteger a where asInteger :: a -> Integer +type LargestY = Bool+ class Monoid g => CyclicGroup g where generator :: g order :: Proxy g -> Integer expn :: AsInteger e => g -> e -> g inverse :: g -> g+ random :: (MonadRandom m) => g -> m g -- | Sum all the elements of some container according to its group -- structure.@@ -22,3 +30,14 @@ instance AsInteger Int where asInteger = toInteger++instance AsInteger Integer where+ asInteger = identity++class FromX a where+ yFromX :: a -> LargestY -> Maybe a+ isLargestY :: a -> Bool++class Validate a where+ isValidElement :: a -> Bool+
src/Pairing/Fq.hs view
@@ -14,18 +14,20 @@ fqZero, fqOne, fqNqr,- euclidean,+ fqPow,+ fqSqrt, random,- Pairing.Fq.fromBytes+ fqYforX,+ fromBytesToInteger ) where import Protolude import Crypto.Random (MonadRandom) import Crypto.Number.Generate (generateMax) import Pairing.Params as Params-import Pairing.CyclicGroup+import Pairing.CyclicGroup (AsInteger(..), FromX(..))+import Pairing.ByteRepr import Pairing.Modular as M-import Data.Bits import qualified Data.ByteString as BS import Data.Bits import Math.NumberTheory.Moduli.Class@@ -56,14 +58,19 @@ (/) = fqDiv fromRational (a :% b) = Fq a / Fq b +instance FromX Fq where+ yFromX = fqYforX+ isLargestY y = y > negate y++instance ByteRepr Fq where+ mkRepr f@(Fq a) = toPaddedBytes (reprLength f) a+ fromRepr _ bs = Just (Fq $ fromBytesToInteger bs)+ reprLength _ = 32+ -- | Turn an integer into an @Fq@ number, should be used instead of -- the @Fq@ constructor. new :: Integer -> Fq-new a = Fq $ withQ $ (getVal . newMod a)--{-# INLINE norm #-}-norm :: Fq -> Fq-norm (Fq a) = new a+new a = Fq $ withQ (getVal . newMod a) {-# INLINE fqAdd #-} fqAdd :: Fq -> Fq -> Fq@@ -89,6 +96,10 @@ fqDiv :: Fq -> Fq -> Fq fqDiv (Fq a) (Fq b) = Fq $ withQ (modBinOp a b (/)) +{-# INLINE fqPow #-}+fqPow :: Integral e => Fq -> e -> Fq+fqPow (Fq a) b = Fq $ withQ (modUnOp a (flip powMod b))+ {-# INLINE fqNqr #-} -- | Quadratic non-residue fqNqr :: Fq@@ -109,30 +120,17 @@ fqOne :: Fq fqOne = Fq 1 -inv :: Fq -> Fq-inv (Fq a) = Fq $ euclidean a _q `mod` _q---- | Euclidean algorithm to compute inverse in an integral domain @a@-euclidean :: (Integral a) => a -> a -> a-euclidean a b = fst (inv' a b)--{-# INLINEABLE inv' #-}-{-# SPECIALISE inv' :: Integer -> Integer -> (Integer, Integer) #-}-inv' :: (Integral a) => a -> a -> (a, a)-inv' a b =- case b of- 1 -> (0, 1)- _ -> let (e, f) = inv' b d- in (f, e - c*f)- where c = a `div` b- d = a `mod` b+fqSqrt :: Bool -> Fq -> Maybe Fq+fqSqrt largestY (Fq a) = do+ (y1, y2) <- withQM (modUnOpMTup a bothSqrtOf)+ Fq <$> if largestY then Just (max y1 y2) else Just (min y1 y2) random :: MonadRandom m => m Fq random = do seed <- generateMax _q pure (Fq seed) -fromBytes :: ByteString -> Fq-fromBytes bs = Fq $ withQ (M.toInteger . M.fromBytes bs) -+fqYforX :: Fq -> Bool -> Maybe Fq+fqYforX x largestY = fqSqrt largestY (x `fqPow` 3 + new _b)+
src/Pairing/Fq12.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE Strict #-}+{-# LANGUAGE DeriveAnyClass, DeriveGeneric #-} -- | Final quadratic extension of the tower: --@@ -29,12 +30,15 @@ import Pairing.Fq6 (Fq6(..)) import qualified Pairing.Fq2 as Fq2 import qualified Pairing.Fq6 as Fq6+import Pairing.CyclicGroup (AsInteger(..), FromX(..)) import Pairing.Params+import Pairing.ByteRepr+import Data.ByteString as B (length, splitAt) -- | Field extension defined as Fq6[w]/w^2 - v data Fq12 = Fq12 { fq12x :: Fq6, fq12y :: Fq6 } -- ^ Use @new@ instead -- of this constructor- deriving (Eq, Show)+ deriving (Eq, Show, Generic, NFData) instance Num Fq12 where (+) = fq12add@@ -47,6 +51,15 @@ instance Fractional Fq12 where (/) = fq12div fromRational (a :% b) = fq12int a / fq12int b++instance ByteRepr Fq12 where+ mkRepr (Fq12 x y) = mkRepr x <> mkRepr y+ fromRepr (Fq12 x _) bs = do+ let (xbs, ybs) = B.splitAt (reprLength x) bs+ x <- fromRepr Fq6.fq6one xbs+ y <- fromRepr Fq6.fq6one ybs+ Just (Fq12 x y)+ reprLength (Fq12 x y) = reprLength x + reprLength y -- | Create a new value in @Fq12@ by providing a list of twelve -- coefficients in @Fq@, should be used instead of the @Fq12@
src/Pairing/Fq2.hs view
@@ -20,17 +20,24 @@ fq2zero, fq2conj, fq2sqr,+ fq2sqrt,+ fq2pow,+ fq2YforX, mulXi, divXi, xi,- Pairing.Fq2.random+ Pairing.Fq2.random, ) where import Protolude import Crypto.Random (MonadRandom)-+import Pairing.Modular import Pairing.Fq as Fq import qualified Pairing.Params as Params+import Data.Bits+import Data.ByteString as B (length, splitAt)+import Pairing.CyclicGroup (AsInteger(..), FromX(..))+import Pairing.ByteRepr -- | Quadratic extension of @Fq@ defined as @Fq[u]/x^2 + 1@ data Fq2 = Fq2 { fq2x :: Fq, fq2y :: Fq } -- ^ Use @new@ instead of@@ -53,6 +60,22 @@ (/) = fq2div fromRational (a :% b) = fq2int a / fq2int b +instance Ord Fq2 where+ compare (Fq2 x y) (Fq2 a b)+ | compare x a == EQ = compare y b+ | otherwise = compare x a++instance FromX Fq2 where+ yFromX = fq2YforX+ isLargestY y = y > negate y++instance ByteRepr Fq2 where+ mkRepr (Fq2 x y) = mkRepr x <> mkRepr y+ fromRepr (Fq2 x _) bs = do+ let (xbs, ybs) = B.splitAt (reprLength x) bs+ Just (Fq2 (Fq $ fromBytesToInteger xbs) (Fq $ fromBytesToInteger ybs))+ reprLength (Fq2 x y) = reprLength x + reprLength y+ -- | Cubic non-residue in @Fq2@ xi :: Fq2 xi = Fq2 xiA xiB@@ -110,6 +133,13 @@ c0 = bb * fqNqr + aa c1 = (a0 + a1) * (a0 + a1) - aa - bb +{-# INLINE fq2pow #-}+fq2pow :: Fq2 -> Integer -> Fq2+fq2pow b 0 = fq2one+fq2pow b e = t * fq2pow (b * b) (shiftR e 1)+ where + t = if testBit e 0 then b else fq2one+ -- | Multiplicative inverse fq2inv :: Fq2 -> Fq2 fq2inv (Fq2 a0 a1) = Fq2 c0 c1@@ -122,9 +152,41 @@ fq2conj :: Fq2 -> Fq2 fq2conj (Fq2 x y) = Fq2 x (negate y) +-- | Square root of Fq2 are specified by https://eprint.iacr.org/2012/685.pdf, Algorithm 9+-- with lots of help from https://docs.rs/pairing/0.14.1/src/pairing/bls12_381/fq2.rs.html#162-222+-- This implementation appears to return the larger square root so check the return value and+-- negate as necessary+fq2sqrt :: Fq2 -> Maybe Fq2+fq2sqrt a = do+ let a1 = a `fq2pow` qm3by4+ let alpha = (fq2sqr a1) * a+ let a0 = (alpha `fq2pow` Params._q) * alpha+ if a0 == neg1 then Nothing else do+ let x0 = a1 * a+ if alpha == neg1 then Just (a1 `fq2mul` Pairing.Fq2.new fqZero fqOne) else do+ let b = (alpha + fq2one) `fq2pow` qm1by2+ Just (b * x0)+ where+ neg1 = Pairing.Fq2.new (negate fqOne) fqZero+ qm3by4 = withQ (modBinOp (Params._q -3) 4 (/))+ qm1by2 = withQ (modBinOp (Params._q -1) 2 (/)) random :: MonadRandom m => m Fq2 random = do x <- Fq.random y <- Fq.random pure (Fq2 x y)++-- https://docs.rs/pairing/0.14.1/src/pairing/bls12_381/ec.rs.html#102-124+fq2YforX :: Fq2 -> Bool -> Maybe Fq2+fq2YforX x ly + | ly = newy+ | otherwise = negate <$> newy+ where+ newy = fq2sqrt (x `fq2pow` 3 + Fq2 (b * inv_xi_a) (b * inv_xi_b))+ (Fq2 inv_xi_a inv_xi_b) = fq2inv xi+ b = Fq Params._b++++
src/Pairing/Fq6.hs view
@@ -1,4 +1,5 @@ {-# LANGUAGE Strict #-}+{-# LANGUAGE DeriveAnyClass, DeriveGeneric #-} -- | Cubic extension of the tower: --@@ -26,6 +27,9 @@ import Pairing.Fq2 (Fq2) import qualified Pairing.Fq2 as Fq2+import Pairing.CyclicGroup (AsInteger(..), FromX(..))+import Data.ByteString as B (length, splitAt)+import Pairing.ByteRepr -- | Field extension defined as Fq2[v]/v^3 - (9 + u) data Fq6@@ -34,7 +38,7 @@ , fq6y :: Fq2 , fq6z :: Fq2 }- deriving (Eq, Show)+ deriving (Eq, Show, Generic, NFData) instance Num Fq6 where (+) = fq6add@@ -47,6 +51,17 @@ instance Fractional Fq6 where (/) = fq6div fromRational (a :% b) = fq6int a / fq6int b++instance ByteRepr Fq6 where+ mkRepr (Fq6 x y z) = mkRepr x <> mkRepr y <> mkRepr z+ fromRepr (Fq6 x _ _) bs = do+ let (xbs, yzbs) = B.splitAt (reprLength x) bs+ let (ybs, zbs) = B.splitAt (reprLength x) yzbs+ x <- fromRepr Fq2.fq2one xbs+ y <- fromRepr Fq2.fq2one ybs+ z <- fromRepr Fq2.fq2one zbs+ Just (Fq6 x y z)+ reprLength (Fq6 x y z) = reprLength x + reprLength y + reprLength z -- | Create a new value in @Fq6@, should be used instead of the @Fq6@ -- constructor.
src/Pairing/Fr.hs view
@@ -6,6 +6,9 @@ Fr(..), new, frInv,+ frPow,+ frAdd,+ frNeg, random, isRootOfUnity, isPrimitiveRootOfUnity,@@ -20,8 +23,9 @@ import Text.PrettyPrint.Leijen.Text import Pairing.Params-import Pairing.CyclicGroup-import Pairing.Fq (euclidean)+import Pairing.CyclicGroup (AsInteger(..))+import Pairing.Modular+import Math.NumberTheory.Moduli.Class instance AsInteger Fr where asInteger (Fr n) = n@@ -48,19 +52,15 @@ -- | Turn an integer into an @Fr@ number, should be used instead of -- the @Fr@ constructor. new :: Integer -> Fr-new a = Fr (a `mod` _r)--{-# INLINE norm #-}-norm :: Fr -> Fr-norm (Fr a) = Fr (a `mod` _r)+new a = Fr $ withR (getVal . newMod a) {-# INLINE frAdd #-} frAdd :: Fr -> Fr -> Fr-frAdd (Fr a) (Fr b) = norm (Fr (a+b))+frAdd (Fr a) (Fr b) = Fr $ withR (modBinOp a b (+)) {-# INLINE frMul #-} frMul :: Fr -> Fr -> Fr-frMul (Fr a) (Fr b) = norm (Fr (a*b))+frMul (Fr a) (Fr b) = Fr $ withR (modBinOp a b (*)) {-# INLINE frAbs #-} frAbs :: Fr -> Fr@@ -68,21 +68,21 @@ {-# INLINE frSig #-} frSig :: Fr -> Fr-frSig (Fr a) = Fr (signum a `mod` _r)+frSig (Fr a) = Fr $ withR (modUnOp a signum) {-# INLINE frNeg #-} frNeg :: Fr -> Fr-frNeg (Fr a) = Fr ((-a) `mod` _r)+frNeg (Fr a) = Fr $ withR (modUnOp a negate) {-# INLINE frDiv #-} frDiv :: Fr -> Fr -> Fr-frDiv a b = frMul a (inv b)--inv :: Fr -> Fr-inv (Fr a) = Fr $ euclidean a _r `mod` _r+frDiv (Fr a) (Fr b) = Fr $ withR (modBinOp a b (/)) frInv :: Fr -> Fr-frInv = inv+frInv a = 1 / a++frPow :: Integral e => Fr -> e -> Fr+frPow (Fr a) b = Fr $ withQ (modUnOp a (`powMod` b)) random :: MonadRandom m => m Fr random = do
src/Pairing/Group.hs view
@@ -15,8 +15,10 @@ b1, b2, hashToG1,- randomG1,- randomG2+ groupFromX,+ fromByteStringG1,+ fromByteStringG2,+ fromByteStringGT ) where import Protolude@@ -25,12 +27,17 @@ import Pairing.Fq as Fq import Pairing.Fq2 as Fq2 import Pairing.Fq12 as Fq12+import Pairing.Fr as Fr import Pairing.Point import Pairing.Params import Pairing.CyclicGroup import Test.QuickCheck import Pairing.Hash import Crypto.Random (MonadRandom)+import Pairing.Modular+import System.Random+import Pairing.Serialize+import Pairing.ByteRepr -- | G1 is E(Fq) defined by y^2 = x^3 + b type G1 = Point Fq@@ -60,7 +67,17 @@ order _ = _r expn a b = gMul a (asInteger b) inverse = gNeg+ random _ = randomG1 +instance Validate G1 where+ isValidElement = isOnCurveG1++instance ToCompressedForm G1 where+ serializeCompressed = fmap toS . toCompressedForm++instance ToUncompressedForm G1 where+ serializeUncompressed = fmap toS . toUncompressedForm+ instance Monoid G2 where mappend = gAdd mempty = Infinity@@ -70,7 +87,17 @@ order _ = _r expn a b = gMul a (asInteger b) inverse = gNeg+ random _ = randomG2 +instance Validate G2 where+ isValidElement = isOnCurveG2++instance ToCompressedForm G2 where+ serializeCompressed = fmap toS . toCompressedForm++instance ToUncompressedForm G2 where+ serializeUncompressed = fmap toS . toUncompressedForm+ instance Monoid GT where mappend = (*) mempty = 1@@ -80,7 +107,14 @@ order = notImplemented -- should be a factor of _r expn a b = a ^ asInteger b inverse = recip+ random _ = Fq12.random +instance ToUncompressedForm GT where+ serializeUncompressed = fmap toS . elementToUncompressedForm++instance Validate GT where+ isValidElement = isInGT+ -- | Generator for G1 g1 :: G1 g1 = Point 1 2@@ -103,7 +137,7 @@ isOnCurveG1 Infinity = True isOnCurveG1 (Point x y)- = (y ^ 2 == x ^ 3 + Fq _b)+ = (y `fqPow` 2 == x `fqPow` 3 + Fq _b) -- | Test whether a value in G2 satisfies the corresponding curve -- equation@@ -111,7 +145,7 @@ isOnCurveG2 Infinity = True isOnCurveG2 (Point x y)- = (y ^ 2 == x ^ 3 + (Fq2 (b * inv_xi_a) (b * inv_xi_b)))+ = y `fq2pow` 2 == x `fq2pow` 3 + Fq2 (b * inv_xi_a) (b * inv_xi_b) where (Fq2 inv_xi_a inv_xi_b) = Fq2.fq2inv Fq2.xi b = Fq _b@@ -138,15 +172,30 @@ instance Arbitrary (Point Fq2) where -- G2 arbitrary = gMul g2 . abs <$> (arbitrary :: Gen Integer) -hashToG1 :: (MonadIO m, MonadRandom m) => ByteString -> m G1+hashToG1 :: MonadIO m => ByteString -> m (Maybe G1) hashToG1 = swEncBN -randomG1 :: (MonadIO m, MonadRandom m) => m G1+randomG1 :: (MonadRandom m) => m G1 randomG1 = do Fq r <- Fq.random pure (gMul g1 r) -randomG2 :: (MonadIO m, MonadRandom m) => m G2+randomG2 :: (MonadRandom m) => m G2 randomG2 = do Fq r <- Fq.random pure (gMul g2 r)++groupFromX :: (Validate (Point a), FromX a) => Bool -> a -> Maybe (Point a)+groupFromX largestY x = do+ y <- yFromX x largestY+ if isValidElement (Point x y) then Just (Point x y) else Nothing++fromByteStringG1 :: ByteString -> Either Text G1+fromByteStringG1 = pointFromByteString fqOne . toSL++fromByteStringG2 :: ByteString -> Either Text G2+fromByteStringG2 = pointFromByteString fq2one . toSL++fromByteStringGT :: ByteString -> Either Text GT+fromByteStringGT = elementReadUncompressed fq12one . toSL+
src/Pairing/Hash.hs view
@@ -11,18 +11,10 @@ import Math.NumberTheory.Moduli.Sqrt import Crypto.Random (MonadRandom) import Data.List--sqrtOfMinusThree :: forall m . KnownNat m => Proxy m -> Mod m-sqrtOfMinusThree mName = sqrtOf mName (-3)---- |--- Picks the postive square root only--- |+import Control.Error (runMaybeT, hoistMaybe) -sqrtOf :: forall m . KnownNat m => Proxy m -> Mod m -> Mod m-sqrtOf mName i = case sqrtsMod i of- [] -> panic ("Could not calculate sqrt " <> show i)- (x:_) -> x+sqrtOfMinusThree :: forall m . KnownNat m => Proxy m -> Maybe (Mod m)+sqrtOfMinusThree _ = sqrtOf (-3) w :: forall m . KnownNat m => Proxy m -> Mod m -> Mod m -> Mod m w mname sq3 t = (sq3 * t) / (1 + (b mname) + (t `powMod` 2))@@ -30,8 +22,10 @@ b :: forall m . KnownNat m => Proxy m -> Mod m b mName = fromInteger @(Mod m) _b -x1 :: forall m . KnownNat m => Proxy m -> Mod m -> Mod m -> Mod m-x1 mName t w = ((sqrtOfMinusThree mName) - 1) / 2 - (t * w)+x1 :: forall m . KnownNat m => Proxy m -> Mod m -> Mod m -> Maybe (Mod m)+x1 mName t w = do+ m3 <- sqrtOfMinusThree mName+ pure $ (m3 - 1) / 2 - (t * w) x2 :: forall m . KnownNat m => Proxy m -> Mod m -> Mod m x2 mName x1' = (-1) - x1'@@ -51,11 +45,11 @@ i :: Integer -> Integer -> Integer i pa pb = (((pa - 1) * pb) `mod` 3) + 1 -swy :: forall m . KnownNat m => Proxy m -> Mod m -> Mod m -> Mod m -> Mod m -> Integer-swy mn pr3 pt pxi pb = ch * y+swy :: forall m . KnownNat m => Proxy m -> Mod m -> Mod m -> Mod m -> Mod m -> Maybe Integer+swy mn pr3 pt pxi pb = (ch *) <$> y where ch = chi mn ((pr3 `powMod` 2) * pt)- y = getVal $ sqrtOf mn ((pxi `powMod` 3) + pb)+ y = getVal <$> sqrtOf ((pxi `powMod` 3) + pb) -- | Encodes a given byte string to a point on the BN curve. -- The implemenation uses the Shallue van de Woestijne encoding to BN curves as specifed@@ -64,23 +58,24 @@ -- This function evaluates an empty bytestring or one that contains \NUL to zero -- which according to Definiton 2 of the paper is sent to an arbitrary point on the curve ---swEncBN :: (MonadIO m, MonadRandom m) => ByteString -> m (Point Fq)-swEncBN bs = withQM $ \mn -> do+swEncBN :: MonadIO m => ByteString -> m (Maybe (Point Fq))+swEncBN bs = runMaybeT $ withQM $ \mn -> do let t = M.fromBytes bs mn- let sq3 = sqrtOfMinusThree mn+ sq3 <- hoistMaybe (sqrtOfMinusThree mn) let w' = w mn sq3 t- let x1' = x1 mn t w'- if (t == 0) then- pure $ (Point (Fq.new (getVal x1')) (Fq.new (getVal $ sqrtOf mn (1 + (b mn)))))+ x1' <- hoistMaybe (x1 mn t w')+ if (t == 0) then do+ onebmn <- hoistMaybe (sqrtOf (1 + (b mn)))+ pure $ (Point (Fq.new (getVal x1')) (Fq.new (getVal $ onebmn))) else do let x2' = x2 mn x1' let x3' = x3 mn w' let lst = [x1', x2', x3']- r1 <- randomMod mn- r2 <- randomMod mn- r3 <- randomMod mn+ r1 <- liftIO (randomMod mn)+ r2 <- liftIO (randomMod mn)+ r3 <- liftIO (randomMod mn) let al = alphaBeta mn r1 x1' let bet = alphaBeta mn r2 x2' let i' = i al bet- let swy' = swy mn r3 t (genericIndex lst (i' - 1)) (b mn)- pure (Point (Fq.new (getVal $ genericIndex lst (i' - 1))) (Fq.new swy'))+ swy' <- hoistMaybe (swy mn r3 t (genericIndex lst (i' - 1)) (b mn))+ pure $ (Point (Fq.new (getVal $ genericIndex lst (i' - 1))) (Fq.new swy'))
src/Pairing/Modular.hs view
@@ -5,22 +5,37 @@ import Math.NumberTheory.Moduli.Sqrt import Math.NumberTheory.UniqueFactorisation import Pairing.Params +import Pairing.ByteRepr import Crypto.Random (MonadRandom) import Crypto.Number.Generate (generateMax) import qualified Data.ByteString as BS +import Math.NumberTheory.Logarithms +withMod :: Integer -> (forall m . KnownNat m => Proxy m -> r) -> r +withMod n cont = case someNatVal n of + Nothing -> panic ("Somehow " <> show n <> " was not a Nat") + Just (SomeNat mName) -> cont mName + +withModM :: Integer -> (forall n. KnownNat n => Proxy n -> m r) -> m r +withModM n cont = case someNatVal n of + Nothing -> panic ("Somehow " <> show n <> " was not a Nat") + Just (SomeNat mName) -> cont mName + -- Mod conversion and management withQ :: (forall m . KnownNat m => Proxy m -> r) -> r -withQ cont = case someNatVal _q of - Nothing -> panic ("Somehow " <> show _q <> " was not a Nat") - Just (SomeNat mName) -> cont mName +withQ = withMod _q -- Mod conversion and management withQM :: (forall n. KnownNat n => Proxy n -> m r) -> m r -withQM cont = case someNatVal _q of - Nothing -> panic ("Somehow " <> show _q <> " was not a Nat") - Just (SomeNat mName) -> cont mName +withQM = withModM _q +withR :: (forall m . KnownNat m => Proxy m -> r) -> r +withR = withMod _r + +-- Mod conversion and management +withRM :: (forall n. KnownNat n => Proxy n -> m r) -> m r +withRM = withModM _r + newMod :: forall m . KnownNat m => Integer -> Proxy m -> Mod m newMod n mName = fromInteger @(Mod m) n @@ -44,27 +59,32 @@ a <- f (fromInteger @(Mod m) n) pure (getVal a) -modPow :: Integral p => Integer -> p -> Integer -modPow a b = withQ (modUnOp a (flip powMod b)) - -modSqrt :: Integer -> [Integer] -modSqrt a = withQ (modUnOpM a sqrtsMod) +modUnOpMTup :: forall m a . (KnownNat m, Monad a) => Integer -> (Mod m -> a (Mod m, Mod m)) -> Proxy m -> a (Integer, Integer) +modUnOpMTup n f mName = do + (a, b) <- f (fromInteger @(Mod m) n) + pure (getVal a, getVal b) threeModFourCongruence :: Integer -> Bool threeModFourCongruence q = q `mod` 4 == 3 `mod` 4 isSquare :: forall m . KnownNat m => Proxy m -> Mod m -> Bool -isSquare _ a = if (threeModFourCongruence _q) then (length kp > 0) else False +isSquare _ a = if (threeModFourCongruence (getMod a)) then (length kp > 0) else False where kp = sqrtsMod a -isSquareIn3Mod4 :: Integer -> Integer -isSquareIn3Mod4 a = if (threeModFourCongruence _q) then sq else 0 - where - sq = withQ (modUnOp a f) - f m = m `powMod` p2 - p2 = (_q + 1) `quot` 4 +-- | +-- Picks the postive square root only +-- | +sqrtOf :: forall m . KnownNat m => Mod m -> Maybe (Mod m) +sqrtOf i = fst <$> bothSqrtOf i + +bothSqrtOf :: forall m . KnownNat m => Mod m -> Maybe (Mod m, Mod m) +bothSqrtOf i = case sqrtsMod i of + [] -> Nothing + (x : x1 : xs) -> Just (x, x1) + [_] -> Nothing + legendre :: Integer -> Integer legendre a = if conv > 1 then (-1) else conv where @@ -78,8 +98,4 @@ pure (fromInteger @(Mod n) seed) fromBytes :: forall n. (KnownNat n) => ByteString -> Proxy n -> Mod n -fromBytes bs mn = newMod (fromBytes' bs) mn - where - fromBytes' :: ByteString -> Integer - fromBytes' = BS.foldl' f 0 - f a b = a `shiftL` 8 .|. fromIntegral b+fromBytes bs mn = newMod (fromBytesToInteger bs) mn
+ src/Pairing/Serialize.hs view
@@ -0,0 +1,105 @@+module Pairing.Serialize where + +import Protolude hiding (putByteString) +import Pairing.Point +import Data.ByteString.Builder +import Data.ByteString as B +import Data.Binary.Get +import Data.Binary.Put (Put, putWord8, runPut, putByteString) +import Control.Error +import Pairing.ByteRepr +import Pairing.CyclicGroup + +class ToCompressedForm a where + -- | The serialisation may fail if y cannot be obtained from x + serializeCompressed :: a -> Maybe ByteString + +class ToUncompressedForm a where + serializeUncompressed :: a -> Maybe ByteString + +-- | Point serialisation using https://tools.ietf.org/id/draft-jivsov-ecc-compact-05.html +-- It is unclear if 02 is smallest y or not so the following is used in the first 2 bytes +-- 01 - Point at infinity +-- 02 - Compressed repr i.e. x only but use smallest y on decode +-- 03 - Compressed repr i.e. x only but use largest y on decode +-- 04 -- Uncompressed repr i.e. x & y + +header :: Word8 -> Put +header n = putWord8 0 >> putWord8 n + +elementToUncompressedForm :: (ByteRepr a) => a -> Maybe LByteString +elementToUncompressedForm a = do + repr <- mkRepr a + pure $ runPut $ do + header 4 + putByteString repr + +toUncompressedForm :: (ByteRepr a) => Point a -> Maybe LByteString +toUncompressedForm (Point x y) = do + rx <- mkRepr x + ry <- mkRepr y + pure $ runPut $ do + header 4 + putByteString rx + putByteString ry +toUncompressedForm Infinity = pure $ runPut (header 1) + +toCompressedForm :: (ByteRepr a, FromX a, Eq a) => Point a -> Maybe LByteString +toCompressedForm (Point x y) = do + ny <- yFromX x True + let yform = if ny == y then 3 else 2 + rx <- mkRepr x + pure (runPut $ header yform >> putByteString rx) +toCompressedForm Infinity = Just (toLazyByteString (word8 0 <> word8 1)) + +pointFromByteString :: (Show a, Validate (Point a), ByteRepr a, FromX a) => a -> LByteString -> Either Text (Point a) +pointFromByteString a = parseBS fromByteStringGet + where + fromByteStringGet = do + ctype <- getCompressionType + processCompressed a ctype + +processCompressed :: forall a . (ByteRepr a, FromX a) => a -> Word8 -> Get (Maybe (Point a)) +processCompressed one ct + | ct == 4 = do + xbs <- getByteString rlen + ybs <- getByteString rlen + pure (buildPoint one xbs ybs) + | ct == 2 = fromCompressed False + | ct == 3 = fromCompressed True + | ct == 1 = pure (Just Infinity) + | otherwise = pure Nothing + where + rlen = reprLength one + fromCompressed largestY = runMaybeT $ do + xbs <- lift $ getByteString rlen + x <- hoistMaybe $ fromRepr one xbs + y <- hoistMaybe $ yFromX x largestY + pure (Point x y) + +buildPoint :: ByteRepr a => a -> ByteString -> ByteString -> Maybe (Point a) +buildPoint one xbs ybs = do + x <- fromRepr one xbs + y <- fromRepr one ybs + pure (Point x y) + +getCompressionType :: Get Word8 +getCompressionType = getWord8 >> getWord8 + +elementReadUncompressed :: (Validate a, Show a, ByteRepr a) => a -> LByteString -> Either Text a +elementReadUncompressed ele = parseBS runc + where + runc = do + ctype <- getCompressionType + if ctype == 4 then do + bs <- getByteString (reprLength ele) + pure (fromRepr ele bs) + else + pure Nothing + +parseBS :: (Validate a, Show a) => Get (Maybe a) -> LByteString -> Either Text a +parseBS f bs = do + (_, _, mpt) <- first (\(_,_,err) -> toS err) (runGetOrFail f bs) + case mpt of + Just pt -> if isValidElement pt then (Right pt) else Left ("Element was not valid after deserialisation: " <> show pt) + Nothing -> Left "Point could not be parsed"
tests/TestFields.hs view
@@ -97,6 +97,24 @@ u = Fq2.new 0 1 minusOne = Fq2.new (-1) 0 +unit_fq2pow :: Assertion+unit_fq2pow = do+ fq2 <- Fq2.random+ let pow5 = fq2sqr (fq2sqr fq2) * fq2+ pow5 @=? fq2pow fq2 5+ let pow10 = ((fq2sqr (fq2sqr (fq2sqr fq2))) * fq2) * fq2+ pow10 @=? fq2pow fq2 10+ where+ u = Fq2.new 0 1+ minusOne = Fq2.new (-1) 0++unit_fq2sqrt :: Assertion+unit_fq2sqrt = do+ fq2 <- Fq2.random+ let sq = fq2sqr fq2+ let (Just rt) = fq2sqrt sq+ sq @=? fq2sqr rt+ ------------------------------------------------------------------------------- -- Fq6 -------------------------------------------------------------------------------
tests/TestGroups.hs view
@@ -5,15 +5,18 @@ import Protolude import Pairing.Fq as Fq+import Pairing.Fr as Fr import Pairing.Fq2+import Pairing.Fq12 import Pairing.Point-import Pairing.Group +import Pairing.Group as G import Pairing.Params-+import Pairing.Serialize+import Pairing.Pairing import Test.Tasty import Test.Tasty.QuickCheck import Test.Tasty.HUnit-import qualified Test.QuickCheck.Monadic as TQM (monadicIO, assert)+import qualified Test.QuickCheck.Monadic as TQM (monadicIO, assert, run) import Test.QuickCheck.Instances () import Data.ByteString as BS (null, dropWhile) import TestCommon@@ -41,6 +44,28 @@ $ isInverse binOp neg ident ] +serializeTest pt compFunc testFunc = do+ let (Just cbs) = compFunc pt+ let npt2e = testFunc cbs+ isRight npt2e @? (Protolude.show npt2e)+ let (Right npt2) = npt2e+ pt @=? npt2++g1FromXTest :: G1 -> Assertion+g1FromXTest Infinity = pure ()+g1FromXTest pt@(Point x y) = do+ let ysq = fqPow y 2+ let (Just lysqrt) = fqSqrt True ysq+ let (Just sysqrt) = fqSqrt False ysq+ let egly = groupFromX True x+ let egsy = groupFromX False x+ isJust egly @=? True+ isJust egsy @=? True+ let Just lyg = egly+ let Just syg = egsy+ (Point x lysqrt) @=? lyg+ (Point x sysqrt) @=? syg+ ------------------------------------------------------------------------------- -- G1 -------------------------------------------------------------------------------@@ -63,9 +88,21 @@ prop_hashToG1 :: ByteString -> Property prop_hashToG1 bs = TQM.monadicIO $ do- toCurve <- liftIO (hashToG1 bs) + toCurveMay <- liftIO (hashToG1 bs)+ TQM.assert (isJust toCurveMay)+ let Just toCurve = toCurveMay TQM.assert (isOnCurveG1 toCurve) +prop_g1FromX :: G1 -> Property+prop_g1FromX g = TQM.monadicIO $ do+ TQM.run $ g1FromXTest g++prop_g1SerializeUncomp :: G1 -> Property+prop_g1SerializeUncomp g = TQM.monadicIO $ TQM.run $ serializeTest g serializeUncompressed G.fromByteStringG1++prop_g1SerializeComp :: G1 -> Property+prop_g1SerializeComp g = TQM.monadicIO $ TQM.run $ serializeTest g serializeCompressed G.fromByteStringG1+ ------------------------------------------------------------------------------- -- G2 -------------------------------------------------------------------------------@@ -85,8 +122,33 @@ unit_order_g2_valid = gMul g2 _r @=? Infinity +g2FromXTest :: G2 -> Assertion+g2FromXTest Infinity = pure ()+g2FromXTest pt@(Point x y) = do+ let ysq = fq2pow y 2+ let (Just ny) = fq2YforX x True+ if (ny /= y) then (Point x y) @=? (Point x (negate ny)) else (Point x y) @=? (Point x ny)++prop_g2FromX :: G2 -> Property+prop_g2FromX g = TQM.monadicIO $ do+ TQM.run $ g2FromXTest g++prop_g2SerializeUncomp :: G2 -> Property+prop_g2SerializeUncomp g = TQM.monadicIO $ TQM.run $ serializeTest g serializeUncompressed G.fromByteStringG2++prop_g2SerializeComp :: G2 -> Property+prop_g2SerializeComp g = TQM.monadicIO $ TQM.run $ serializeTest g serializeUncompressed G.fromByteStringG2+ ------------------------------------------------------------------------------- -- GT ------------------------------------------------------------------------------- -- The group laws for GT are implied by the field tests for Fq12.++gtSerializeTest :: G1 -> G2 -> Assertion+gtSerializeTest g1 g2 = do+ let gt = reducedPairing g1 g2+ serializeTest gt serializeUncompressed fromByteStringGT++prop_gtSerializeUncomp :: G1 -> G2 -> Property+prop_gtSerializeUncomp g1 g2 = TQM.monadicIO $ TQM.run $ gtSerializeTest g1 g2