cryptonite 0.1 → 0.2
raw patch · 17 files changed
+566/−98 lines, 17 files
Files
- CHANGELOG.md +9/−0
- Crypto/Error/Types.hs +8/−0
- Crypto/Hash/Types.hs +1/−1
- Crypto/Number/ModArithmetic.hs +1/−1
- Crypto/Number/Serialize.hs +10/−54
- Crypto/Number/Serialize/Internal.hs +76/−0
- Crypto/PubKey/ECC/P256.hs +137/−37
- Crypto/Random/Entropy/Windows.hs +1/−1
- README.md +0/−1
- cbits/cryptonite_sysrand.c +46/−0
- cbits/p256/p256.c +15/−0
- cryptonite.cabal +4/−1
- tests/KAT_PubKey.hs +2/−0
- tests/KAT_PubKey/ECC.hs +47/−0
- tests/KAT_PubKey/P256.hs +143/−0
- tests/Number.hs +20/−1
- tests/Utils.hs +46/−1
CHANGELOG.md view
@@ -1,3 +1,12 @@+## 0.2++* Fix P256 compilation and exactness, + add tests+* Add a raw memory number serialization capability (i2osp, os2ip)+* Improve tests for number serialization+* Improve tests for ECC arithmetics+* Add Ord instance for Digest (Nicolas Di Prima)+* Fix entropy compilation on windows 64 bits.+ ## 0.1 * Initial release
Crypto/Error/Types.hs view
@@ -49,6 +49,14 @@ CryptoPassed a | CryptoFailed CryptoError +instance Show a => Show (CryptoFailable a) where+ show (CryptoPassed a) = "CryptoPassed " ++ show a+ show (CryptoFailed err) = "CryptoFailed " ++ show err+instance Eq a => Eq (CryptoFailable a) where+ (==) (CryptoPassed a) (CryptoPassed b) = a == b+ (==) (CryptoFailed e1) (CryptoFailed e2) = e1 == e2+ (==) _ _ = False+ instance Functor CryptoFailable where fmap f (CryptoPassed a) = CryptoPassed (f a) fmap _ (CryptoFailed r) = CryptoFailed r
Crypto/Hash/Types.hs view
@@ -51,7 +51,7 @@ -- | Represent a digest for a given hash algorithm. newtype Digest a = Digest Bytes- deriving (Eq,ByteArrayAccess,NFData)+ deriving (Eq,Ord,ByteArrayAccess,NFData) instance Show (Digest a) where show (Digest bs) = show (B.convertToBase B.Base16 bs :: Bytes)
Crypto/Number/ModArithmetic.hs view
@@ -47,7 +47,7 @@ -> Integer -- ^ result expSafe b e m | odd m = gmpPowModSecInteger b e m `onGmpUnsupported`- (gmpPowModInteger b e m `onGmpUnsupported`+ (gmpPowModInteger b e m `onGmpUnsupported` exponentiation b e m) | otherwise = gmpPowModInteger b e m `onGmpUnsupported` exponentiation b e m
Crypto/Number/Serialize.hs view
@@ -12,72 +12,38 @@ , os2ip , i2ospOf , i2ospOf_- , lengthBytes ) where -import Data.Bits-import Data.Word-import Foreign.Storable-import Foreign.Ptr-import Crypto.Number.Compat+import Crypto.Number.Basic import Crypto.Internal.Compat (unsafeDoIO) import qualified Crypto.Internal.ByteArray as B-import Data.Memory.PtrMethods--divMod256 :: Integer -> (Integer, Word8)-divMod256 n = (n `shiftR` 8, fromIntegral n)+import qualified Crypto.Number.Serialize.Internal as Internal -- | os2ip converts a byte string into a positive integer os2ip :: B.ByteArrayAccess ba => ba -> Integer-os2ip bs = unsafeDoIO $ B.withByteArray bs (loop 0 0)- where- len = B.length bs-- loop :: Integer -> Int -> Ptr Word8 -> IO Integer- loop !acc i p- | i == len = return acc- | otherwise = do- w <- peekByteOff p i :: IO Word8- loop ((acc `shiftL` 8) .|. fromIntegral w) (i+1) p+os2ip bs = unsafeDoIO $ B.withByteArray bs (\p -> Internal.os2ip p (B.length bs)) -- | i2osp converts a positive integer into a byte string -- -- first byte is MSB (most significant byte), last byte is the LSB (least significant byte) i2osp :: B.ByteArray ba => Integer -> ba-i2osp 0 = B.allocAndFreeze 1 $ \p -> pokeByteOff p 0 (0 :: Word8)-i2osp m = B.allocAndFreeze sz (\p -> fillPtr p >> return ())+i2osp 0 = B.allocAndFreeze 1 (\p -> Internal.i2osp 0 p 1 >> return ())+i2osp m = B.allocAndFreeze sz (\p -> Internal.i2osp m p sz >> return ()) where- !sz = lengthBytes m-- fillPtr p = gmpExportInteger m p `onGmpUnsupported` export p (sz-1) m- export p ofs i- | ofs == 0 = pokeByteOff p ofs (fromIntegral i :: Word8)- | otherwise = do- let (i', b) = divMod256 i- pokeByteOff p ofs b- export p (ofs-1) i'+ !sz = numBytes m -- | just like i2osp, but take an extra parameter for size. -- if the number is too big to fit in @len bytes, nothing is returned -- otherwise the number is padded with 0 to fit the @len required. i2ospOf :: B.ByteArray ba => Int -> Integer -> Maybe ba-i2ospOf 0 _ = error "cannot create integer serialization in 0 bytes"-i2ospOf len 0 = Just $ B.allocAndFreeze len $ \p -> memSet p 0 len i2ospOf len m+ | len <= 0 = Nothing+ | m < 0 = Nothing | sz > len = Nothing- | otherwise = Just $ B.allocAndFreeze len $ \p -> memSet p 0 len >> fillPtr (p `plusPtr` (len - sz))+ | otherwise = Just $ B.unsafeCreate len (\p -> Internal.i2ospOf m p len >> return ()) where- !sz = lengthBytes m-- fillPtr p = gmpExportInteger m p `onGmpUnsupported` export p (sz-1) m- export p ofs i- | ofs == 0 = pokeByteOff p ofs (fromIntegral i :: Word8)- | otherwise = do- let (i', b) = divMod256 i- pokeByteOff p ofs b- export p (ofs-1) i'+ !sz = numBytes m --- -- | just like i2ospOf except that it doesn't expect a failure: i.e. -- an integer larger than the number of output bytes requested --@@ -85,13 +51,3 @@ -- the size (example the RSA modulo n). i2ospOf_ :: B.ByteArray ba => Int -> Integer -> ba i2ospOf_ len = maybe (error "i2ospOf_: integer is larger than expected") id . i2ospOf len---- | returns the number of bytes to store an integer with i2osp------ with integer-simple, this function is really slow.-lengthBytes :: Integer -> Int-lengthBytes n = gmpSizeInBytes n `onGmpUnsupported` nbBytes n- where- nbBytes !v- | v < 256 = 1- | otherwise = 1 + nbBytes (v `shiftR` 8)
+ Crypto/Number/Serialize/Internal.hs view
@@ -0,0 +1,76 @@+-- |+-- Module : Crypto.Number.Serialize.Internal+-- License : BSD-style+-- Maintainer : Vincent Hanquez <vincent@snarc.org>+-- Stability : experimental+-- Portability : Good+--+-- fast serialization primitives for integer using raw pointers+{-# LANGUAGE BangPatterns #-}+module Crypto.Number.Serialize.Internal+ ( i2osp+ , i2ospOf+ , os2ip+ ) where++import Crypto.Number.Compat+import Crypto.Number.Basic+import Data.Bits+import Data.Memory.PtrMethods+import Data.Word (Word8)+import Foreign.Ptr+import Foreign.Storable++-- | fill a pointer with the big endian binary representation of an integer+--+-- if the room available @ptrSz is less than the number of bytes needed,+-- 0 is returned. Likewise if a parameter is invalid, 0 is returned.+--+-- returns the number of bytes written+i2osp :: Integer -> Ptr Word8 -> Int -> IO Int+i2osp m ptr ptrSz+ | ptrSz <= 0 = return 0+ | m < 0 = return 0+ | m == 0 = pokeByteOff ptr 0 (0 :: Word8) >> return 1+ | ptrSz < sz = return 0+ | otherwise = fillPtr ptr sz m >> return sz+ where+ !sz = numBytes m++-- | Similar to 'i2osp', except it will pad any remaining space with zero.+i2ospOf :: Integer -> Ptr Word8 -> Int -> IO Int+i2ospOf m ptr ptrSz+ | ptrSz <= 0 = return 0+ | m < 0 = return 0+ | ptrSz < sz = return 0+ | otherwise = do+ memSet ptr 0 ptrSz+ fillPtr (ptr `plusPtr` padSz) sz m+ return ptrSz+ where+ !sz = numBytes m+ !padSz = ptrSz - sz++fillPtr :: Ptr Word8 -> Int -> Integer -> IO ()+fillPtr p sz m = gmpExportInteger m p `onGmpUnsupported` export (sz-1) m+ where+ export ofs i+ | ofs == 0 = pokeByteOff p ofs (fromIntegral i :: Word8)+ | otherwise = do+ let (i', b) = i `divMod` 256+ pokeByteOff p ofs (fromIntegral b :: Word8)+ export (ofs-1) i'++-- | transform a big endian binary integer representation pointed by a pointer and a size+-- into an integer+os2ip :: Ptr Word8 -> Int -> IO Integer+os2ip ptr ptrSz+ | ptrSz <= 0 = return 0+ | otherwise = gmpImportInteger ptrSz ptr `onGmpUnsupported` loop 0 0 ptr+ where+ loop :: Integer -> Int -> Ptr Word8 -> IO Integer+ loop !acc i p+ | i == ptrSz = return acc+ | otherwise = do+ w <- peekByteOff p i :: IO Word8+ loop ((acc `shiftL` 8) .|. fromIntegral w) (i+1) p
Crypto/PubKey/ECC/P256.hs view
@@ -11,8 +11,6 @@ {-# LANGUAGE BangPatterns #-} {-# LANGUAGE EmptyDataDecls #-} {-# OPTIONS_GHC -fno-warn-unused-binds #-}-{-# OPTIONS_GHC -fno-warn-unused-matches #-}-{-# OPTIONS_GHC -fno-warn-unused-imports #-} module Crypto.PubKey.ECC.P256 ( Scalar , Point@@ -22,39 +20,51 @@ , pointsMulVarTime , pointIsValid , toPoint+ , pointToIntegers+ , pointFromIntegers+ , pointToBinary+ , pointFromBinary -- * scalar arithmetic , scalarZero+ , scalarIsZero , scalarAdd , scalarSub , scalarInv- , scalarInvVarTime , scalarCmp , scalarFromBinary , scalarToBinary+ , scalarFromInteger+ , scalarToInteger ) where import Data.Word import Foreign.Ptr import Foreign.C.Types+import Control.Monad import Crypto.Internal.Compat import Crypto.Internal.Imports---import Crypto.Internal.Memory import Crypto.Internal.ByteArray import qualified Crypto.Internal.ByteArray as B+import Data.Memory.PtrMethods (memSet) import Crypto.Error+import Crypto.Number.Serialize.Internal (os2ip, i2ospOf)+import qualified Crypto.Number.Serialize as S (os2ip, i2ospOf) -- | A P256 scalar-newtype Scalar = Scalar ScrubbedBytes+newtype Scalar = Scalar Bytes deriving (Eq,ByteArrayAccess) -- | A P256 point-data Point = Point !Bytes !Bytes+newtype Point = Point Bytes deriving (Show,Eq) scalarSize :: Int scalarSize = 32 +pointSize :: Int+pointSize = 64+ type P256Digit = Word32 data P256Scalar@@ -73,8 +83,11 @@ -- > scalar * G -- toPoint :: Scalar -> Point-toPoint s = withNewPoint $ \px py -> withScalar s $ \p ->- ccryptonite_p256_basepoint_mul p px py+toPoint s+ | scalarIsZero s = error "cannot create point from zero"+ | otherwise =+ withNewPoint $ \px py -> withScalar s $ \p ->+ ccryptonite_p256_basepoint_mul p px py -- | Add a point to another point pointAdd :: Point -> Point -> Point@@ -83,14 +96,18 @@ ccryptonite_p256e_point_add ax ay bx by dx dy -- | Multiply a point by a scalar+--+-- warning: variable time pointMul :: Scalar -> Point -> Point pointMul scalar p = withNewPoint $ \dx dy ->- withScalar scalar $ \n -> withPoint p $ \px py ->- ccryptonite_p256_point_mul n dx dy px py+ withScalar scalar $ \n -> withPoint p $ \px py -> withScalarZero $ \nzero ->+ ccryptonite_p256_points_mul_vartime nzero n px py dx dy -- | multiply the point @p with @n2 and add a lifted to curve value @n1 -- -- > n1 * G + n2 * p+--+-- warning: variable time pointsMulVarTime :: Scalar -> Scalar -> Point -> Point pointsMulVarTime n1 n2 p = withNewPoint $ \dx dy -> withScalar n1 $ \pn1 -> withScalar n2 $ \pn2 -> withPoint p $ \px py ->@@ -102,6 +119,46 @@ r <- ccryptonite_p256_is_valid_point px py return (r /= 0) +pointToIntegers :: Point -> (Integer, Integer)+pointToIntegers p = unsafeDoIO $ withPoint p $ \px py ->+ allocTemp 32 (serialize (castPtr px) (castPtr py))+ where+ serialize px py temp = do+ ccryptonite_p256_to_bin px temp+ x <- os2ip temp scalarSize+ ccryptonite_p256_to_bin py temp+ y <- os2ip temp scalarSize+ return (x,y)++pointFromIntegers :: (Integer, Integer) -> Point+pointFromIntegers (x,y) = withNewPoint $ \dx dy ->+ allocTemp scalarSize (\temp -> fill temp (castPtr dx) x >> fill temp (castPtr dy) y)+ where+ -- put @n to @temp in big endian format, then from @temp to @dest in p256 scalar format+ fill :: Ptr Word8 -> Ptr P256Scalar -> Integer -> IO ()+ fill temp dest n = do+ -- write the integer in big endian format to temp+ memSet temp 0 scalarSize+ e <- i2ospOf n temp scalarSize+ if e == 0+ then error "pointFromIntegers: filling failed"+ else return ()+ -- then fill dest with the P256 scalar from temp+ ccryptonite_p256_from_bin temp dest++pointToBinary :: ByteArray ba => Point -> ba+pointToBinary p = B.unsafeCreate pointSize $ \dst -> withPoint p $ \px py -> do+ ccryptonite_p256_to_bin (castPtr px) dst+ ccryptonite_p256_to_bin (castPtr py) (dst `plusPtr` 32)++pointFromBinary :: ByteArrayAccess ba => ba -> CryptoFailable Point+pointFromBinary ba+ | B.length ba /= pointSize = CryptoFailed $ CryptoError_PublicKeySizeInvalid+ | otherwise =+ CryptoPassed $ withNewPoint $ \px py -> B.withByteArray ba $ \src -> do+ ccryptonite_p256_from_bin src (castPtr px)+ ccryptonite_p256_from_bin (src `plusPtr` scalarSize) (castPtr py)+ ------------------------------------------------------------------------ -- Scalar methods ------------------------------------------------------------------------@@ -110,14 +167,27 @@ scalarZero :: Scalar scalarZero = withNewScalarFreeze $ \d -> ccryptonite_p256_init d +scalarIsZero :: Scalar -> Bool+scalarIsZero s = unsafeDoIO $ withScalar s $ \d -> do+ result <- ccryptonite_p256_is_zero d+ return $ result /= 0++scalarNeedReducing :: Ptr P256Scalar -> IO Bool+scalarNeedReducing d = do+ c <- ccryptonite_p256_cmp d ccryptonite_SECP256r1_n+ return (c >= 0)+ -- | Perform addition between two scalars -- -- > a + b scalarAdd :: Scalar -> Scalar -> Scalar scalarAdd a b = withNewScalarFreeze $ \d -> withScalar a $ \pa -> withScalar b $ \pb -> do- void $ ccryptonite_p256_add pa pb d- ccryptonite_p256_mod ccryptonite_SECP256r1_n d d+ carry <- ccryptonite_p256_add pa pb d+ when (carry /= 0) $ void $ ccryptonite_p256_sub d ccryptonite_SECP256r1_n d+ needReducing <- scalarNeedReducing d+ when needReducing $ do+ ccryptonite_p256_mod ccryptonite_SECP256r1_n d d -- | Perform subtraction between two scalars --@@ -125,22 +195,20 @@ scalarSub :: Scalar -> Scalar -> Scalar scalarSub a b = withNewScalarFreeze $ \d -> withScalar a $ \pa -> withScalar b $ \pb -> do- void $ ccryptonite_p256_sub pa pb d- ccryptonite_p256_mod ccryptonite_SECP256r1_n d d+ borrow <- ccryptonite_p256_sub pa pb d+ when (borrow /= 0) $ void $ ccryptonite_p256_add d ccryptonite_SECP256r1_n d+ --needReducing <- scalarNeedReducing d+ --when needReducing $ do+ -- ccryptonite_p256_mod ccryptonite_SECP256r1_n d d -- | Give the inverse of the scalar -- -- > 1 / a+--+-- warning: variable time scalarInv :: Scalar -> Scalar scalarInv a = withNewScalarFreeze $ \b -> withScalar a $ \pa ->- ccryptonite_p256_modinv ccryptonite_SECP256r1_n pa b---- | similar to 'scalarInv' but instead of--- trying to be constant time, do it as fast as possible-scalarInvVarTime :: Scalar -> Scalar-scalarInvVarTime a =- withNewScalarFreeze $ \b -> withScalar a $ \pa -> ccryptonite_p256_modinv_vartime ccryptonite_SECP256r1_n pa b -- | Compare 2 Scalar@@ -157,32 +225,62 @@ | otherwise = CryptoPassed $ withNewScalarFreeze $ \p -> B.withByteArray ba $ \b -> ccryptonite_p256_from_bin b p+{-# NOINLINE scalarFromBinary #-} -- | convert a scalar to binary scalarToBinary :: ByteArray ba => Scalar -> ba-scalarToBinary s = B.allocAndFreeze scalarSize $ \b -> withScalar s $ \p ->+scalarToBinary s = B.unsafeCreate scalarSize $ \b -> withScalar s $ \p -> ccryptonite_p256_to_bin p b+{-# NOINLINE scalarToBinary #-} +scalarFromInteger :: Integer -> CryptoFailable Scalar+scalarFromInteger i =+ maybe (CryptoFailed CryptoError_SecretKeySizeInvalid) scalarFromBinary (S.i2ospOf 32 i :: Maybe Bytes)++scalarToInteger :: Scalar -> Integer+scalarToInteger s = S.os2ip (scalarToBinary s :: Bytes)+ ------------------------------------------------------------------------ -- Memory Helpers ------------------------------------------------------------------------ withNewPoint :: (Ptr P256X -> Ptr P256Y -> IO ()) -> Point-withNewPoint f = unsafeDoIO $ do- (x,y) <- B.allocRet pointCoordSize $ \py -> B.alloc pointCoordSize $ \px -> f px py- return $! Point x y- where pointCoordSize = 32+withNewPoint f = Point $ B.unsafeCreate pointSize $ \px -> f px (pxToPy px) {-# NOINLINE withNewPoint #-} withPoint :: Point -> (Ptr P256X -> Ptr P256Y -> IO a) -> IO a-withPoint (Point x y) f = B.withByteArray x $ \px -> B.withByteArray y $ \py -> f px py+withPoint (Point d) f = B.withByteArray d $ \px -> f px (pxToPy px) +pxToPy :: Ptr P256X -> Ptr P256Y+pxToPy px = castPtr (px `plusPtr` scalarSize)+ withNewScalarFreeze :: (Ptr P256Scalar -> IO ()) -> Scalar withNewScalarFreeze f = Scalar $ B.allocAndFreeze scalarSize f {-# NOINLINE withNewScalarFreeze #-} +withTempScalar :: (Ptr P256Scalar -> IO a) -> IO a+withTempScalar f = allocTempScrubbed scalarSize (f . castPtr)+ withScalar :: Scalar -> (Ptr P256Scalar -> IO a) -> IO a withScalar (Scalar d) f = B.withByteArray d f +withScalarZero :: (Ptr P256Scalar -> IO a) -> IO a+withScalarZero f =+ withTempScalar $ \d -> do+ ccryptonite_p256_init d+ f d++allocTemp :: Int -> (Ptr Word8 -> IO a) -> IO a+allocTemp n f = ignoreSnd <$> B.allocRet n f+ where+ ignoreSnd :: (a, Bytes) -> a+ ignoreSnd = fst++allocTempScrubbed :: Int -> (Ptr Word8 -> IO a) -> IO a+allocTempScrubbed n f = ignoreSnd <$> B.allocRet n f+ where+ ignoreSnd :: (a, ScrubbedBytes) -> a+ ignoreSnd = fst+ ------------------------------------------------------------------------ -- Foreign bindings ------------------------------------------------------------------------@@ -195,10 +293,14 @@ foreign import ccall "cryptonite_p256_init" ccryptonite_p256_init :: Ptr P256Scalar -> IO ()+foreign import ccall "cryptonite_p256_is_zero"+ ccryptonite_p256_is_zero :: Ptr P256Scalar -> IO CInt foreign import ccall "cryptonite_p256_clear" ccryptonite_p256_clear :: Ptr P256Scalar -> IO () foreign import ccall "cryptonite_p256_add" ccryptonite_p256_add :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO CInt+foreign import ccall "cryptonite_p256_add_d"+ ccryptonite_p256_add_d :: Ptr P256Scalar -> P256Digit -> Ptr P256Scalar -> IO CInt foreign import ccall "cryptonite_p256_sub" ccryptonite_p256_sub :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO CInt foreign import ccall "cryptonite_p256_cmp"@@ -207,8 +309,8 @@ ccryptonite_p256_mod :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO () foreign import ccall "cryptonite_p256_modmul" ccryptonite_p256_modmul :: Ptr P256Scalar -> Ptr P256Scalar -> P256Digit -> Ptr P256Scalar -> Ptr P256Scalar -> IO ()-foreign import ccall "cryptonite_p256_modinv"- ccryptonite_p256_modinv :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO ()+--foreign import ccall "cryptonite_p256_modinv"+-- ccryptonite_p256_modinv :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO () foreign import ccall "cryptonite_p256_modinv_vartime" ccryptonite_p256_modinv_vartime :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO () foreign import ccall "cryptonite_p256_base_point_mul"@@ -221,15 +323,13 @@ -> Ptr P256X -> Ptr P256Y -> Ptr P256X -> Ptr P256Y -> IO ()-foreign import ccall "cryptonite_p256_point_mul"- ccryptonite_p256_point_mul :: Ptr P256Scalar- -> Ptr P256X -> Ptr P256Y- -> Ptr P256X -> Ptr P256Y- -> IO ()++-- compute (out_x,out,y) = n1 * G + n2 * (in_x,in_y) foreign import ccall "cryptonite_p256_points_mul_vartime"- ccryptonite_p256_points_mul_vartime :: Ptr P256Scalar -> Ptr P256Scalar- -> Ptr P256X -> Ptr P256Y- -> Ptr P256X -> Ptr P256Y+ ccryptonite_p256_points_mul_vartime :: Ptr P256Scalar -- n1+ -> Ptr P256Scalar -- n2+ -> Ptr P256X -> Ptr P256Y -- in_{x,y}+ -> Ptr P256X -> Ptr P256Y -- out_{x,y} -> IO () foreign import ccall "cryptonite_p256_is_valid_point" ccryptonite_p256_is_valid_point :: Ptr P256X -> Ptr P256Y -> IO CInt
Crypto/Random/Entropy/Windows.hs view
@@ -15,7 +15,7 @@ ) where import Data.Int (Int32)-import Data.Word (Word32, Word8)+import Data.Word import Foreign.C.String (CString, withCString) import Foreign.Ptr (Ptr, nullPtr) import Foreign.Marshal.Alloc (alloca)
README.md view
@@ -76,5 +76,4 @@ TODO ---- -* finish google P256 binding * add support for XSalsa
+ cbits/cryptonite_sysrand.c view
@@ -0,0 +1,46 @@+/*+ * Copyright (C) 2015 Vincent Hanquez <tab@snarc.org>+ *+ * All rights reserved.+ * + * Redistribution and use in source and binary forms, with or without+ * modification, are permitted provided that the following conditions+ * are met:+ * 1. Redistributions of source code must retain the above copyright+ * notice, this list of conditions and the following disclaimer.+ * 2. Redistributions in binary form must reproduce the above copyright+ * notice, this list of conditions and the following disclaimer in the+ * documentation and/or other materials provided with the distribution.+ * 3. Neither the name of the author nor the names of his contributors+ * may be used to endorse or promote products derived from this software+ * without specific prior written permission.+ * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF+ * SUCH DAMAGE.+ */++#include <stdlib.h>+#include <stdint.h>+#include <sys/time.h>+#include "cryptonite_sha512.h"++void cryptonite_sysrand_init(uint8_t *buf, uint32_t sz)+{+ struct timeval tv;+ struct sha512_ctx ctx;+ uint8_t out[64];+ + gettimeofday(&tv, NULL);+ cryptonite_sha512_init(&ctx);+ cryptonite_sha512_update(&ctx, (uint8_t *) &tv, sizeof(tv));+ cryptonite_sha512_finalize(&ctx, out);+}
cbits/p256/p256.c view
@@ -371,3 +371,18 @@ p += 4; } }++void cryptonite_p256_to_bin(const cryptonite_p256_int* src, uint8_t dst[P256_NBYTES])+{+ int i;+ uint8_t* p = &dst[0];++ for (i = P256_NDIGITS -1; i >= 0; --i) {+ const cryptonite_p256_digit dig = P256_DIGIT(src, i);+ p[0] = dig >> 24;+ p[1] = dig >> 16;+ p[2] = dig >> 8;+ p[3] = dig;+ p += 4;+ }+}
cryptonite.cabal view
@@ -1,5 +1,5 @@ Name: cryptonite-Version: 0.1+Version: 0.2 Synopsis: Cryptography Primitives sink Description: A repository of cryptographic primitives.@@ -86,6 +86,7 @@ Crypto.Number.ModArithmetic Crypto.Number.Prime Crypto.Number.Serialize+ Crypto.Number.Serialize.Internal Crypto.KDF.PBKDF2 Crypto.KDF.Scrypt Crypto.Hash@@ -189,6 +190,7 @@ , cbits/cryptonite_tiger.c , cbits/cryptonite_whirlpool.c , cbits/cryptonite_scrypt.c+ , cbits/cryptonite_sysrand.c include-dirs: cbits cbits/ed25519 -- FIXME armel or mispel is also little endian.@@ -254,6 +256,7 @@ KAT_PubKey.ECDSA KAT_PubKey.OAEP KAT_PubKey.PSS+ KAT_PubKey.P256 KAT_PubKey KAT_RC4 KAT_Scrypt
tests/KAT_PubKey.hs view
@@ -17,6 +17,7 @@ import KAT_PubKey.ECC import KAT_PubKey.ECDSA import Utils+import qualified KAT_PubKey.P256 as P256 data VectorMgf = VectorMgf { seed :: ByteString , dbMask :: ByteString@@ -39,6 +40,7 @@ , dsaTests , eccTests , ecdsaTests+ , P256.tests ] --newKats = [ eccKatTests ]
tests/KAT_PubKey/ECC.hs view
@@ -11,6 +11,43 @@ import Imports +instance Arbitrary ECC.Curve where+ arbitrary = ECC.getCurveByName <$> elements+ [ ECC.SEC_p112r1+ , ECC.SEC_p112r2+ , ECC.SEC_p128r1+ , ECC.SEC_p128r2+ , ECC.SEC_p160k1+ , ECC.SEC_p160r1+ , ECC.SEC_p160r2+ , ECC.SEC_p192k1+ , ECC.SEC_p192r1+ , ECC.SEC_p224k1+ , ECC.SEC_p224r1+ , ECC.SEC_p256k1+ , ECC.SEC_p256r1+ , ECC.SEC_p384r1+ , ECC.SEC_p521r1+ , ECC.SEC_t113r1+ , ECC.SEC_t113r2+ , ECC.SEC_t131r1+ , ECC.SEC_t131r2+ , ECC.SEC_t163k1+ , ECC.SEC_t163r1+ , ECC.SEC_t163r2+ , ECC.SEC_t193r1+ , ECC.SEC_t193r2+ , ECC.SEC_t233k1+ , ECC.SEC_t233r1+ , ECC.SEC_t239k1+ , ECC.SEC_t283k1+ , ECC.SEC_t283r1+ , ECC.SEC_t409k1+ , ECC.SEC_t409r1+ , ECC.SEC_t571k1+ , ECC.SEC_t571r1+ ]+ data VectorPoint = VectorPoint { curve :: ECC.Curve , x :: Integer@@ -101,8 +138,18 @@ doPointValidTest (i, vector) = testCase (show i) (valid vector @=? ECC.isPointValid (curve vector) (ECC.Point (x vector) (y vector))) + eccTests = testGroup "ECC" [ testGroup "valid-point" $ map doPointValidTest (zip [katZero..] vectorsPoint)+ , testGroup "property" $+ [ testProperty "point-add" $ \curve (QAInteger r1) (QAInteger r2) ->+ let curveN = ECC.ecc_n . ECC.common_curve $ curve+ curveGen = ECC.ecc_g . ECC.common_curve $ curve+ p1 = ECC.pointMul curve r1 curveGen+ p2 = ECC.pointMul curve r2 curveGen+ pR = ECC.pointMul curve ((r1 + r2) `mod` curveN) curveGen+ in pR `propertyEq` ECC.pointAdd curve p1 p2+ ] ] eccKatTests = do
+ tests/KAT_PubKey/P256.hs view
@@ -0,0 +1,143 @@+{-# LANGUAGE OverloadedStrings #-}+{-# LANGUAGE ScopedTypeVariables #-}+module KAT_PubKey.P256 (tests) where++import qualified Crypto.PubKey.ECC.Types as ECC+import qualified Crypto.PubKey.ECC.Prim as ECC+import qualified Crypto.PubKey.ECC.P256 as P256++import Data.ByteArray (Bytes)+import Crypto.Number.Serialize (i2ospOf, os2ip)+import Crypto.Number.ModArithmetic (inverseCoprimes)+import Crypto.Error++import Imports++newtype P256Scalar = P256Scalar Integer+ deriving (Show,Eq,Ord)++instance Arbitrary P256Scalar where+ arbitrary = P256Scalar . getQAInteger <$> arbitrary++curve = ECC.getCurveByName ECC.SEC_p256r1+curveN = ECC.ecc_n . ECC.common_curve $ curve+curveGen = ECC.ecc_g . ECC.common_curve $ curve++pointP256ToECC :: P256.Point -> ECC.Point+pointP256ToECC = uncurry ECC.Point . P256.pointToIntegers++unP256Scalar :: P256Scalar -> P256.Scalar+unP256Scalar (P256Scalar r') =+ let r = if r' == 0 then 0x2901 else (r' `mod` curveN)+ rBytes = i2ospScalar r+ in case P256.scalarFromBinary rBytes of+ CryptoFailed err -> error ("cannot convert scalar: " ++ show err)+ CryptoPassed scalar -> scalar+ where+ i2ospScalar :: Integer -> Bytes+ i2ospScalar i =+ case i2ospOf 32 i of+ Nothing -> error "invalid size of P256 scalar"+ Just b -> b++unP256 :: P256Scalar -> Integer+unP256 (P256Scalar r') = if r' == 0 then 0x2901 else (r' `mod` curveN)++p256ScalarToInteger :: P256.Scalar -> Integer+p256ScalarToInteger s = os2ip (P256.scalarToBinary s :: Bytes)++xS = 0xde2444bebc8d36e682edd27e0f271508617519b3221a8fa0b77cab3989da97c9+yS = 0xc093ae7ff36e5380fc01a5aad1e66659702de80f53cec576b6350b243042a256+xT = 0x55a8b00f8da1d44e62f6b3b25316212e39540dc861c89575bb8cf92e35e0986b+yT = 0x5421c3209c2d6c704835d82ac4c3dd90f61a8a52598b9e7ab656e9d8c8b24316+xR = 0x72b13dd4354b6b81745195e98cc5ba6970349191ac476bd4553cf35a545a067e+yR = 0x8d585cbb2e1327d75241a8a122d7620dc33b13315aa5c9d46d013011744ac264++tests = testGroup "P256"+ [ testGroup "scalar"+ [ testProperty "marshalling" $ \(QAInteger r') ->+ let r = r' `mod` curveN+ rBytes = i2ospScalar r+ in case P256.scalarFromBinary rBytes of+ CryptoFailed err -> error (show err)+ CryptoPassed scalar -> rBytes `propertyEq` P256.scalarToBinary scalar+ , testProperty "add" $ \r1 r2 ->+ let r = (unP256 r1 + unP256 r2) `mod` curveN+ r' = P256.scalarAdd (unP256Scalar r1) (unP256Scalar r2)+ in r `propertyEq` p256ScalarToInteger r'+ , testProperty "add0" $ \r ->+ let v = unP256 r+ v' = P256.scalarAdd (unP256Scalar r) P256.scalarZero+ in v `propertyEq` p256ScalarToInteger v'+ , testProperty "add-n-1" $ \r ->+ let nm1 = throwCryptoError $ P256.scalarFromInteger (curveN - 1)+ v = unP256 r+ v' = P256.scalarAdd (unP256Scalar r) nm1+ in (((curveN - 1) + v) `mod` curveN) `propertyEq` p256ScalarToInteger v'+ , testProperty "sub" $ \r1 r2 ->+ let r = (unP256 r1 - unP256 r2) `mod` curveN+ r' = P256.scalarSub (unP256Scalar r1) (unP256Scalar r2)+ v = (unP256 r2 - unP256 r1) `mod` curveN+ v' = P256.scalarSub (unP256Scalar r2) (unP256Scalar r1)+ in propertyHold+ [ eqTest "r1-r2" r (p256ScalarToInteger r')+ , eqTest "r2-r1" v (p256ScalarToInteger v')+ ]+ , testProperty "sub-n-1" $ \r ->+ let nm1 = throwCryptoError $ P256.scalarFromInteger (curveN - 1)+ v = unP256 r+ v' = P256.scalarSub (unP256Scalar r) nm1+ in ((v - (curveN - 1)) `mod` curveN) `propertyEq` p256ScalarToInteger v'+ , testProperty "inv" $ \r' ->+ let inv = inverseCoprimes (unP256 r') curveN+ inv' = P256.scalarInv (unP256Scalar r')+ in if unP256 r' == 0 then True else inv `propertyEq` p256ScalarToInteger inv'+ ]+ , testGroup "point"+ [ testProperty "marshalling" $ \rx ry ->+ let p = P256.pointFromIntegers (unP256 rx, unP256 ry)+ b = P256.pointToBinary p :: Bytes+ p' = P256.pointFromBinary b+ in propertyHold [ eqTest "point" (CryptoPassed p) p' ]+ , testProperty "marshalling-integer" $ \rx ry ->+ let p = P256.pointFromIntegers (unP256 rx, unP256 ry)+ (x,y) = P256.pointToIntegers p+ in propertyHold [ eqTest "x" (unP256 rx) x, eqTest "y" (unP256 ry) y ]+ , testCase "valid-point-1" $ casePointIsValid (xS,yS)+ , testCase "valid-point-2" $ casePointIsValid (xR,yR)+ , testCase "valid-point-3" $ casePointIsValid (xT,yT)+ , testCase "point-add-1" $+ let s = P256.pointFromIntegers (xS, yS)+ t = P256.pointFromIntegers (xT, yT)+ r = P256.pointFromIntegers (xR, yR)+ in r @=? P256.pointAdd s t+ , testProperty "lift-to-curve" $ propertyLiftToCurve+ , testProperty "point-add" $ propertyPointAdd+ ]+ ]+ where+ casePointIsValid pointTuple =+ let s = P256.pointFromIntegers pointTuple in True @=? P256.pointIsValid s++ propertyLiftToCurve r =+ let p = P256.toPoint (unP256Scalar r)+ (x,y) = P256.pointToIntegers p+ pEcc = ECC.pointMul curve (unP256 r) curveGen+ in pEcc `propertyEq` ECC.Point x y++ propertyPointAdd r1 r2 =+ let p1 = P256.toPoint (unP256Scalar r1)+ p2 = P256.toPoint (unP256Scalar r2)+ pe1 = ECC.pointMul curve (unP256 r1) curveGen+ pe2 = ECC.pointMul curve (unP256 r2) curveGen+ pR = P256.toPoint (P256.scalarAdd (unP256Scalar r1) (unP256Scalar r2))+ peR = ECC.pointAdd curve pe1 pe2+ in propertyHold [ eqTest "p256" pR (P256.pointAdd p1 p2)+ , eqTest "ecc" peR (pointP256ToECC pR)+ ]++ i2ospScalar :: Integer -> Bytes+ i2ospScalar i =+ case i2ospOf 32 i of+ Nothing -> error "invalid size of P256 scalar"+ Just b -> b
tests/Number.hs view
@@ -1,12 +1,24 @@+{-# LANGUAGE OverloadedStrings #-} module Number (tests) where import Imports +import Data.ByteArray (Bytes) import Crypto.Number.Basic import Crypto.Number.Generate+import Crypto.Number.Serialize import Crypto.Number.Prime import Data.Bits +serializationVectors :: [(Int, Integer, ByteString)]+serializationVectors =+ [ (128, 468189858948067662094510918729062682059955669513914188715630930503497261316361784677177564296207557978182700664806717692596876084916561811001371208806217360635705059859428069669992937334724312890015700331031248133952795914192719979937664050389500162437642525331653766885896869239678885404647468665996400635, "\x00\xaa\xae\x74\xc8\xec\x3c\x36\x06\x5e\x46\xca\x8e\x57\xab\x09\x87\xfd\xcd\x1f\xa4\xe7\xf9\xd2\x60\xd5\x4a\x1b\x74\xdc\xa8\x75\xd8\xdd\xff\x2b\x74\x28\x14\x59\x67\x6c\x82\xae\xa3\xa5\x1d\x3f\xb4\xb7\xfe\x5c\xd2\xf0\x7f\xd8\xd9\xa9\xb0\xce\x26\xc1\x26\x74\x96\xf5\xf6\x4c\x8f\x66\x7f\x5d\xf1\x68\x38\xd4\x03\x62\xe9\x30\xc8\xa1\xc1\x84\x97\x62\x20\xfd\xd7\x03\x35\xc1\x25\x45\x1b\x86\x81\x3d\xa4\x92\xc0\xd3\xdd\xfa\x86\x1d\xdf\x0a\xbb\xf4\xc0\x56\xf7\xa2\xb0\x3b\x52\xf7\xa5\x89\x4c\x69\x34\x91\x46\xd9\x57\xfb")+ , (128, 40031303476923779996794876613623495515025748694978019540894726181695410095832601107261950025830235596060960914255795497479135806963313279476038687192202016132891881954743054164975707083302554941058329647014950354509055121290280892911153779672733723699997592027662953953692834215577119173225643193201177329, "\x00\x0e\x97\xf9\xd5\x79\xb9\x90\x7c\x85\x48\x49\x01\x19\x64\xfb\x76\x31\xcd\x51\xfb\x8a\x9d\x55\xe5\xd3\x7b\x87\x2d\xad\x63\x2d\x6b\x1c\x84\x3f\x65\x95\xb6\xf3\x1a\xa9\x43\x3f\x06\x46\x7b\xf8\xf3\x35\x45\x84\x11\x56\x91\x53\x43\xd7\xe1\x6d\x80\x64\x14\x45\x35\x4e\x93\x7d\x5e\x48\xec\xe0\x79\x7b\x44\x8e\xab\x0f\xc4\x5f\xc6\xa1\x71\xee\x37\xb1\x55\x51\x98\x44\x57\xe3\xc3\x56\x3a\x50\x27\xaf\xa5\x1d\x1a\x0a\x90\x19\x0d\x14\xed\x3d\x93\x40\x62\x76\xa3\xaa\x00\x23\x86\xca\x98\xb2\x6e\x02\x43\xa7\xbc\xb1\xb2\xf1")+ , (128, 75152325976543603337003024341071663845101857195436434620947904288957274825323005869230041326941600298094896018190395352332646796347130114769768242670539699217743549573961461985255265474392937773768121046339453584830072421569334022498680626938734088755136253492360177084153487115846920446085149631919580041, "\x00\x1b\x65\xb1\x73\x74\xed\xd2\xcb\xb8\xf3\x6b\x3f\xc2\x05\xaa\x91\xab\x48\x5b\x03\x30\xae\x24\xa3\xec\x7a\x6a\xf0\x34\x73\x18\x04\xea\xe4\xd6\x19\x97\xc4\xc1\x13\x7d\x12\x0d\xd5\xcb\xbd\x18\x05\xc2\xce\x87\x66\x84\x12\xe8\x24\xa3\x31\x69\xfa\xf4\x2c\x21\x53\xa6\x04\x74\x78\xc4\x93\x0d\x38\x7f\x28\xfe\x80\x8e\xd2\x7b\x20\xc8\xf5\x1f\x0f\x73\x68\xb2\xe5\x08\xf1\x94\xa1\xe6\xcf\x3a\x2c\x12\x63\xda\x08\x3a\x78\x12\xb8\x11\x23\x3c\x38\x38\x10\x94\x2b\xac\x64\x5d\x67\x0c\xb6\x0d\xc3\x9a\x45\x39\x50\x8a\x63\x89")+ , (128, 132094272981815297755209818914225029878347650582749561568514551350741192910991391836297682842650690115955454061006435646226436379226218676796260483719213285072886626400953065229934239690821114513313427305727000011361769875430428291375851099221794646192854831002408178061474948738788927399080262963320752452, "\x00\x30\x27\xe0\xbf\x46\xec\x77\x2d\xc6\x06\x77\xbc\x68\x87\x3c\x1b\x2e\xc7\xb7\x6c\x88\x25\xec\x8c\x95\xbf\x74\xe5\x37\x01\x25\x96\xe1\x70\x33\x5c\x7d\xab\x1f\xc2\x9c\xad\xf7\xca\x26\x85\x2d\xfc\x8f\xc7\xab\x49\x28\xa4\x47\xe6\xd5\x6e\xfa\x0a\xbb\x57\xe4\xa2\x51\xc7\xc6\x12\x0f\xa9\x98\x69\xb8\x05\x84\xc5\xe3\x28\x86\x0f\x54\x1d\xf9\x92\x42\x9f\xb1\x77\x2b\x58\x89\xe2\xfc\x22\xb0\x1e\x71\x78\xea\x39\xc1\x87\x4f\xd4\x83\x2c\x96\x1d\xea\xd5\xf9\xf9\xb9\x7b\x86\xfa\xf6\xad\x5b\xb1\x3c\xe7\x11\xd7\x96\x89\x44")+ , (128, 577245873336454863811643140721674509319073059708446946821011267146688442860798353087462545395033001525475835015592425207995480357299993009193426638306801669333644226765032464458284920004140299209138389393494751627076239104390434285377314678827349631962212281858308570255468721491493027423799738158196939966, "\x00\xd2\x70\x41\xdb\x3d\xb5\xfe\x8c\xef\x79\xcf\x5b\x7b\x37\xb0\x05\xb8\x5a\x9b\x7d\x01\x28\xc7\xf5\x5a\x02\xba\xce\xbc\xf5\x8e\x91\x59\xd0\x42\x6f\x04\x82\x4b\x78\xb0\xdd\x91\x2e\x15\x9d\xea\x4f\x0c\x21\xc0\x67\x54\xa2\x39\xa8\xe1\x13\x8f\xa9\xff\x46\x2d\x11\x56\x04\xa0\xde\x64\xc8\x0f\xf4\x2c\xd2\x31\xdf\x2a\xfd\xac\xc7\x25\x58\xc8\xea\xfd\x47\x6e\xdd\x2a\x53\x02\x77\x49\xa7\x0d\x18\xfb\x05\x18\x4b\x28\xd3\xa2\x39\x8c\x83\x80\x90\xd1\xa8\x81\x56\x6f\xd1\x94\x9d\x65\x34\x95\x79\xc1\x27\xbc\x76\xc3\x5c\xbe")+ ]+ tests = testGroup "number" [ testProperty "num-bits" $ \(Positive i) -> and [ (numBits (2^i-1) == i)@@ -35,4 +47,11 @@ -- to the next is quite high, as the number generated has two highest bit set. -- in bits == numBits prime || (if baseBits < 64 then (bits + 1) == numBits prime else False)- ]+ , testProperty "marshalling" $ \qaInt ->+ getQAInteger qaInt == os2ip (i2osp (getQAInteger qaInt) :: Bytes)+ , testGroup "marshalling-kat-to-bytearray" $ map toSerializationKat $ zip [katZero..] serializationVectors+ , testGroup "marshalling-kat-to-integer" $ map toSerializationKatInteger $ zip [katZero..] serializationVectors+ ] + where+ toSerializationKat (i, (sz, n, ba)) = testCase (show i) (ba @=? i2ospOf_ sz n)+ toSerializationKatInteger (i, (_, n, ba)) = testCase (show i) (n @=? os2ip ba)
tests/Utils.hs view
@@ -1,12 +1,17 @@+{-# LANGUAGE ExistentialQuantification #-} module Utils where +import Control.Applicative import Control.Monad (replicateM) import Data.Char import Data.Word+import Data.List import Data.ByteString (ByteString) import qualified Data.ByteString as B import qualified Data.ByteString.Lazy as L import Crypto.Random+import Crypto.Number.Serialize (os2ip)+import Prelude import Test.Tasty.QuickCheck @@ -43,6 +48,23 @@ instance Arbitrary Int0_2901 where arbitrary = Int0_2901 `fmap` choose (0,2901) +-- | a integer wrapper with a better range property+newtype QAInteger = QAInteger { getQAInteger :: Integer }+ deriving (Show,Eq)++instance Arbitrary QAInteger where+ arbitrary = oneof+ [ QAInteger . fromIntegral <$> (choose (0, 65536) :: Gen Int) -- small integer+ , larger <$> choose (0,4096) <*> choose (0, 65536) -- medium integer+ , QAInteger . os2ip . B.pack <$> (choose (0,32) >>= \n -> replicateM n arbitrary) -- [ 0 .. 2^32 ] sized integer+ ]+ where+ larger :: Int -> Int -> QAInteger+ larger p b = QAInteger (fromIntegral p * somePrime + fromIntegral b)++ somePrime :: Integer+ somePrime = 18446744073709551557+ arbitraryBS :: Int -> Gen ByteString arbitraryBS n = B.pack `fmap` replicateM n arbitrary @@ -84,7 +106,7 @@ then let (b1, b2) = B.splitAt l b in b1 : splitB l b2- else + else [ b ] assertBytesEq :: ByteString -> ByteString -> Bool@@ -97,3 +119,26 @@ propertyEq :: (Show a, Eq a) => a -> a -> Bool propertyEq = assertEq++data PropertyTest =+ forall a . (Show a, Eq a) => EqTest String a a++type PropertyName = String++eqTest :: (Show a, Eq a)+ => PropertyName+ -> a -- ^ expected value+ -> a -- ^ got+ -> PropertyTest+eqTest name a b = EqTest name a b++propertyHold :: [PropertyTest] -> Bool+propertyHold l =+ case foldl runProperty [] l of+ [] -> True+ failed -> error (intercalate "\n" failed)+ where+ runProperty acc (EqTest name a b)+ | a == b = acc+ | otherwise =+ (name ++ ": expected " ++ show a ++ " but got: " ++ show b) : acc