module VectorPedersen ( dot, mkGs, scalarGenerateN, ecCommit, ecOpen, ecVerifyAddCommitments, vecSum, ecAddVector, ecVerifyAddVector, ECReveal(..), ECPedersen(..), ECCommitParams(..), ECCommitment(..) ) where import Protolude hiding (hash) import Crypto.Hash import Crypto.Number.Serialize (os2ip) import qualified Crypto.PubKey.ECC.Prim as ECC import qualified Crypto.PubKey.ECC.Types as ECC import Crypto.Random.Types (MonadRandom(..)) import qualified Data.ByteArray as BA import Data.Monoid ((<>)) import Pedersen ( ecSetup, ECCommitParams(..), ECCommitment(..) ) ------------------------------------------------------------------------------- -- Vector Pedersen Commitment Scheme - Elliptic Curve (SECP256k1) ------------------------------------------------------------------------------- -- | ecRevealVal is a vector of scalars data ECReveal = ECReveal { ecRevealVal :: [Integer] , ecRevealScalar :: Integer } data ECPedersen = ECPedersen { ecCommitment :: ECCommitment , ecReveal :: ECReveal } -- | Outputs unpredictable but deterministic random values oracle :: ECC.Curve -> ByteString -> Integer oracle curve x = os2ip (sha256 x) `mod` n where -- | Order of the curve n :: Integer n = ECC.ecc_n $ ECC.common_curve curve -- | Secure cryptographic hash function sha256 :: ByteString -> ByteString sha256 bs = BA.convert (hash bs :: Digest SHA3_256) -- | Generate a commit value which is a vector of N elements scalarGenerateN :: MonadRandom m => ECC.Curve -> Word8 -> m [Integer] scalarGenerateN curve n = scalarGenerateN' curve n [] scalarGenerateN' :: MonadRandom m => ECC.Curve -> Word8 -> [Integer] -> m [Integer] scalarGenerateN' curve n v | n == 0 = return v | otherwise = do vi <- ECC.scalarGenerate curve if vi `elem` v then scalarGenerateN' curve n v else scalarGenerateN' curve (n-1) (vi:v) -- | Dot product between a vector of scalars and a vector of ECC.Points dot :: ECC.Curve -> [Integer] -> [ECC.Point] -> ECC.Point dot curve scalars points = foldl' (ECC.pointAdd curve) ECC.PointO $ zipWith (ECC.pointMul curve) scalars points -- | Concatenate point coordinates to create a hashable type appendCoordinates :: ECC.Point -> ByteString appendCoordinates ECC.PointO = "" appendCoordinates (ECC.Point x y) = show x <> show y -- | Generate vector of generators in a deterministic way from the curve generator g -- by applying H(encode(g) || i) where H is a secure hash function mkGs :: ECC.Curve -> [ECC.Point] mkGs curve = fmap (ECC.pointBaseMul curve . oracle curve . (<> appendCoordinates g) . show) [1..] where g = ECC.ecc_g $ ECC.common_curve curve -- | Commitment function. The value we commit to is now a vector ecCommit :: MonadRandom m => [Integer] -> ECCommitParams -> m ECPedersen ecCommit v (ECCommitParams curve h) = do r <- ECC.scalarGenerate curve let vG = dot curve v (mkGs curve) let rH = ECC.pointMul curve r h let commitment = ECCommitment $ ECC.pointAdd curve vG rH let reveal = ECReveal v r return $ ECPedersen commitment reveal -- | Open commitment to check its validity ecOpen :: ECCommitParams -> ECCommitment -> ECReveal -> Bool ecOpen (ECCommitParams curve h) (ECCommitment c) (ECReveal v r) = c == ECC.pointAdd curve vG rH where vG = dot curve v (mkGs curve) rH = ECC.pointMul curve r h -- | Sum of vectors in a curve vecSum :: ECC.Curve -> [Integer] -> [Integer] -> [Integer] vecSum curve = zipWith (\a b -> a + b `mod` n) where n :: Integer n = ECC.ecc_n $ ECC.common_curve curve -- | Verify the addition of two EC Vector Pedersen Commitments by constructing -- the new Pedersen commitment on the uncommitted values. ecVerifyAddCommitments :: ECCommitParams -> ECPedersen -> ECPedersen -> ECPedersen ecVerifyAddCommitments (ECCommitParams curve h) p1 p2 = ECPedersen newCommitment newReveal where ECReveal v r1 = ecReveal p1 ECReveal w r2 = ecReveal p2 vw = vecSum curve v w r = r1 + r2 vwG = dot curve vw (mkGs curve) rH = ECC.pointMul curve r h newCommitment = ECCommitment $ ECC.pointAdd curve vwG rH newReveal = ECReveal vw r -- | Add a vector to the committed value such that C'= C + wG ecAddVector :: ECCommitParams -> ECCommitment -> [Integer] -> ECCommitment ecAddVector (ECCommitParams curve h) (ECCommitment c) w = ECCommitment $ ECC.pointAdd curve wG c where wG = dot curve w (mkGs curve) -- Access the reveal values of the vector pedersen commitment (r and v) -- Return a new commitment adding an input vector w such that C' = (v + w)G + rH ecVerifyAddVector :: ECCommitParams -> ECPedersen -> [Integer] -> ECPedersen ecVerifyAddVector (ECCommitParams curve h) p w = ECPedersen newCommitment newReveal where ECReveal v r = ecReveal p vw = vecSum curve v w vwG = dot curve vw (mkGs curve) rH = ECC.pointMul curve r h -- rH doesn't change newCommitment = ECCommitment $ ECC.pointAdd curve vwG rH newReveal = ECReveal vw r -- r doesn't change