Merge pull request #271 from ocheron/hash-nat-constraints

Hash algorithms with runtime output length
This commit is contained in:
Olivier Chéron 2019-03-04 06:33:59 +01:00
commit 8e28d7b2cd
7 changed files with 70 additions and 17 deletions

View File

@ -31,7 +31,6 @@ import qualified Crypto.ECC.Simple.Types as Simple
import qualified Crypto.ECC.Simple.Prim as Simple import qualified Crypto.ECC.Simple.Prim as Simple
import Crypto.Random import Crypto.Random
import Crypto.Error import Crypto.Error
import Crypto.Internal.Proxy
import Crypto.Internal.Imports import Crypto.Internal.Imports
import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes) import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes)
import qualified Crypto.Internal.ByteArray as B import qualified Crypto.Internal.ByteArray as B
@ -41,6 +40,7 @@ import qualified Crypto.PubKey.Curve448 as X448
import Data.ByteArray (convert) import Data.ByteArray (convert)
import Data.Data (Data()) import Data.Data (Data())
import Data.Kind (Type) import Data.Kind (Type)
import Data.Proxy
-- | An elliptic curve key pair composed of the private part (a scalar), and -- | An elliptic curve key pair composed of the private part (a scalar), and
-- the associated point. -- the associated point.

View File

@ -17,7 +17,7 @@ module Crypto.ECC.Simple.Prim
) where ) where
import Data.Maybe import Data.Maybe
import Crypto.Internal.Proxy import Data.Proxy
import Crypto.Number.ModArithmetic import Crypto.Number.ModArithmetic
import Crypto.Number.F2m import Crypto.Number.F2m
import Crypto.Number.Generate (generateBetween) import Crypto.Number.Generate (generateBetween)

View File

@ -9,6 +9,7 @@ module Crypto.Internal.Nat
, type IsAtMost, type IsAtLeast , type IsAtMost, type IsAtLeast
, byteLen , byteLen
, integralNatVal , integralNatVal
, type IsDiv8
, type Div8 , type Div8
, type Mod8 , type Mod8
) where ) where
@ -207,4 +208,6 @@ type family Mod8 (n :: Nat) where
Mod8 63 = 7 Mod8 63 = 7
Mod8 n = Mod8 (n - 64) Mod8 n = Mod8 (n - 64)
-- | ensure the given `bitlen` is divisible by 8
--
type IsDivisibleBy8 bitLen = IsDiv8 bitLen bitLen ~ 'True type IsDivisibleBy8 bitLen = IsDiv8 bitLen bitLen ~ 'True

View File

@ -1,13 +0,0 @@
-- |
-- Module : Crypto.Internal.Proxy
-- License : BSD-style
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
-- Stability : experimental
-- Portability : Good
--
module Crypto.Internal.Proxy
( Proxy(..)
) where
-- | A type witness for 'a' as phantom type
data Proxy a = Proxy

63
Crypto/Number/Nat.hs Normal file
View File

@ -0,0 +1,63 @@
-- |
-- Module : Crypto.Number.Nat
-- License : BSD-style
-- Maintainer : Vincent Hanquez <vincent@snarc.org>
-- Stability : experimental
-- Portability : Good
--
-- Numbers at type level.
--
-- This module provides extensions to "GHC.TypeLits" and "GHC.TypeNats" useful
-- to work with cryptographic algorithms parameterized with a variable bit
-- length. Constraints like @'IsDivisibleBy8' n@ ensure that the type-level
-- parameter is applicable to the algorithm.
--
-- Functions are also provided to test whether constraints are satisfied from
-- values known at runtime. The following example shows how to discharge
-- 'IsDivisibleBy8' in a computation @fn@ requiring this constraint:
--
-- > withDivisibleBy8 :: Integer
-- > -> (forall proxy n . (KnownNat n, IsDivisibleBy8 n) => proxy n -> a)
-- > -> Maybe a
-- > withDivisibleBy8 len fn = do
-- > SomeNat p <- someNatVal len
-- > Refl <- isDivisibleBy8 p
-- > pure (fn p)
--
-- Function @withDivisibleBy8@ above returns 'Nothing' when the argument @len@
-- is negative or not divisible by 8.
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE TypeOperators #-}
module Crypto.Number.Nat
( type IsDivisibleBy8
, type IsAtMost, type IsAtLeast
, isDivisibleBy8
, isAtMost
, isAtLeast
) where
import Data.Type.Equality
import GHC.TypeLits
import Unsafe.Coerce (unsafeCoerce)
import Crypto.Internal.Nat
-- | get a runtime proof that the constraint @'IsDivisibleBy8' n@ is satified
isDivisibleBy8 :: KnownNat n => proxy n -> Maybe (IsDiv8 n n :~: 'True)
isDivisibleBy8 n
| mod (natVal n) 8 == 0 = Just (unsafeCoerce Refl)
| otherwise = Nothing
-- | get a runtime proof that the constraint @'IsAtMost' value bound@ is
-- satified
isAtMost :: (KnownNat value, KnownNat bound)
=> proxy value -> proxy' bound -> Maybe ((value <=? bound) :~: 'True)
isAtMost x y
| natVal x <= natVal y = Just (unsafeCoerce Refl)
| otherwise = Nothing
-- | get a runtime proof that the constraint @'IsAtLeast' value bound@ is
-- satified
isAtLeast :: (KnownNat value, KnownNat bound)
=> proxy value -> proxy' bound -> Maybe ((bound <=? value) :~: 'True)
isAtLeast = flip isAtMost

View File

@ -14,8 +14,8 @@ module Crypto.Random.Entropy.Backend
) where ) where
import Foreign.Ptr import Foreign.Ptr
import Data.Proxy
import Data.Word (Word8) import Data.Word (Word8)
import Crypto.Internal.Proxy
import Crypto.Random.Entropy.Source import Crypto.Random.Entropy.Source
#ifdef SUPPORT_RDRAND #ifdef SUPPORT_RDRAND
import Crypto.Random.Entropy.RDRand import Crypto.Random.Entropy.RDRand

View File

@ -130,6 +130,7 @@ Library
Crypto.Number.F2m Crypto.Number.F2m
Crypto.Number.Generate Crypto.Number.Generate
Crypto.Number.ModArithmetic Crypto.Number.ModArithmetic
Crypto.Number.Nat
Crypto.Number.Prime Crypto.Number.Prime
Crypto.Number.Serialize Crypto.Number.Serialize
Crypto.Number.Serialize.Internal Crypto.Number.Serialize.Internal
@ -220,7 +221,6 @@ Library
Crypto.PubKey.ElGamal Crypto.PubKey.ElGamal
Crypto.ECC.Simple.Types Crypto.ECC.Simple.Types
Crypto.ECC.Simple.Prim Crypto.ECC.Simple.Prim
Crypto.Internal.Proxy
Crypto.Internal.ByteArray Crypto.Internal.ByteArray
Crypto.Internal.Compat Crypto.Internal.Compat
Crypto.Internal.CompatPrim Crypto.Internal.CompatPrim