From 8c5f18cb7c56fad5a5fe210eec87b240e17c8fa3 Mon Sep 17 00:00:00 2001 From: Adam Wick Date: Wed, 8 Jan 2020 15:19:34 -1000 Subject: [PATCH] Commit the start of the work on modular X before I worry about From. --- generation/Main.hs | 2 + generation/generation.cabal | 2 + generation/src/Division.hs | 2 +- generation/src/ModOps.hs | 76 +++++++++++++++++++++++++++++++++++++ src/lib.rs | 18 +++++++++ 5 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 generation/src/ModOps.hs diff --git a/generation/Main.hs b/generation/Main.hs index 5547a9b..e821360 100644 --- a/generation/Main.hs +++ b/generation/Main.hs @@ -10,6 +10,7 @@ import CryptoNum(cryptoNum) import Control.Monad(forM_,unless) import Division(divisionOps) import File(File,Task(..),generateTasks) +import ModOps(modulusOps) import Multiply(safeMultiplyOps, unsafeMultiplyOps) import Scale(safeScaleOps, unsafeScaleOps) import Shift(shiftOps) @@ -38,6 +39,7 @@ unsignedFiles = [ , conversions , cryptoNum , divisionOps + , modulusOps , safeAddOps , safeMultiplyOps , safeScaleOps diff --git a/generation/generation.cabal b/generation/generation.cabal index ba46f6d..3d80b2f 100644 --- a/generation/generation.cabal +++ b/generation/generation.cabal @@ -22,6 +22,7 @@ library containers, directory, filepath, + integer-gmp, language-rust, largeword, mtl, @@ -40,6 +41,7 @@ library Gen, Generators, Karatsuba, + ModOps, Multiply, Scale, Shift, diff --git a/generation/src/Division.hs b/generation/src/Division.hs index 35ae3ad..fc61412 100644 --- a/generation/src/Division.hs +++ b/generation/src/Division.hs @@ -207,6 +207,6 @@ generateDivisionTests size g = go g numTestCases ("z", showX (x `div` y)), ("r", showX (x `mod` y))] in if y == 0 - then go g0 i + then go g2 i else tcase : go g2 (i - 1) diff --git a/generation/src/ModOps.hs b/generation/src/ModOps.hs new file mode 100644 index 0000000..a4e421d --- /dev/null +++ b/generation/src/ModOps.hs @@ -0,0 +1,76 @@ +{-# LANGUAGE QuasiQuotes #-} +module ModOps(modulusOps) + where + +import Data.Map.Strict(Map) +import qualified Data.Map.Strict as Map +import File +import Gen(toLit) +import Generators +import GHC.Integer.GMP.Internals(powModInteger) +import Language.Rust.Data.Ident +import Language.Rust.Data.Position +import Language.Rust.Quote +import Language.Rust.Syntax +import System.Random(RandomGen) + +numTestCases :: Int +numTestCases = 3000 + +modulusOps :: File +modulusOps = File { + predicate = \ me others -> (me * 2) `elem` others, + outputName = "modops", + isUnsigned = True, + generator = declareModOps, + testCase = Just generateModulusTests +} + +declareModOps :: Word -> SourceFile Span +declareModOps bitsize = + let sname = mkIdent ("U" ++ show bitsize) + bname = mkIdent ("U" ++ show (bitsize * 2)) + in [sourceFile| + use crate::unsigned::$$sname; + use crate::{DivMod, ModularOperations}; + + impl ModularOperations for $$sname { + fn reduce(&self, m: &$$sname) -> $$sname { + let (_, res) = self.divmod(m); + res + } + + fn modmul(&self, y: &$$sname, m: &$$sname) -> $$sname { + panic!("modmul") + } + + fn modsq(&self, m: &$$sname) -> $$sname { + panic!("reduce") + } + + fn modexp(&self, e: &$$sname, m: &$$sname) -> $$sname { + panic!("reduce") + } + } + |] + +generateModulusTests :: RandomGen g => Word -> g -> [Map String String] +generateModulusTests size g = go g numTestCases + where + go _ 0 = [] + go g0 i = + let (x, g1) = generateNum g0 size + (y, g2) = generateNum g1 size + (m, g3) = generateNum g2 size + tcase = Map.fromList [("x", showX x), ("y", showX y), + ("m", showX m), + ("r", showX (x `mod` m)), + ("t", showX ((x * y) `mod` m)), + ("s", showX ((x * x) `mod` m)), + ("e", showX (powModInteger x y m)) + ] + in if y < 2 + then go g3 i + else tcase : go g3 (i - 1) + + diff --git a/src/lib.rs b/src/lib.rs index 59436d2..e535b48 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -42,12 +42,30 @@ pub trait CryptoNum { /// this is used as the implementation of division and multiplication, and /// so you can save time doing both at once if you need them. /// +/// WARNING: There has been some effort made to make this have a constant-time +/// implementation, but it does use a single conditional inside an otherwise- +/// constant time loop. There may be unforeseen timing effects of this, or +/// the compiler may do something funny to "optimize" some math. pub trait DivMod: Sized { /// Divide and modulus as a single operation. The first element of the tuple /// is the quotient, the second is the modulus. fn divmod(&self, rhs: &Self) -> (Self, Self); } +// Provides support for a variety of modular mathematical operations, as beloved +// by cryptographers. +pub trait ModularOperations { + // reduce the current value by the provided modulus + fn reduce(&self, m: &Modulus) -> Self; + // multiply this value by the provided one, modulo the modulus + fn modmul(&self, rhs: &Self, m: &Modulus) -> Self; + // square the provided number, modulo the modulus + fn modsq(&self, m: &Modulus) -> Self; + // modular exponentiation! + fn modexp(&self, e: &Self, m: &Modulus) -> Self; + +} + /// An error in conversion of large numbers (either to primitives or to other numbers #[derive(Debug)] pub enum ConversionError {