Skip to content
This repository has been archived by the owner on Sep 20, 2023. It is now read-only.

Rewriting ECC #114

Merged
merged 36 commits into from
Dec 4, 2016
Merged
Show file tree
Hide file tree
Changes from 24 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
e00c89f
adding toByteString and fromByteString to PRK.
kazu-yamamoto Nov 8, 2016
60bb2ca
[ECC] Improve the code base to allow multiples different implementations
vincenthz Oct 16, 2015
7c833ed
improve description
vincenthz Mar 21, 2016
f3255c2
fix imports on older versions
vincenthz Mar 23, 2016
c29fa82
add a note about scalarInverse
vincenthz Mar 24, 2016
9a0ec91
implementing ecdh fpr P256 and P521.
kazu-yamamoto Nov 15, 2016
dea0469
adding Curve_P384R1.
kazu-yamamoto Nov 16, 2016
aa33c00
adding Curve_X25519.
kazu-yamamoto Nov 16, 2016
a6f1773
Eq and Show for Point and Scalar.
kazu-yamamoto Nov 16, 2016
c0b0846
implmenting encodePoint and decodePoint for TLS.
kazu-yamamoto Nov 17, 2016
2b9dce2
Dropping Show from PRK.
kazu-yamamoto Nov 28, 2016
39ecb35
removing a trailing space / a warning.
kazu-yamamoto Nov 30, 2016
3a2eb3c
using ByteArray(Access) instead of ByteString.
kazu-yamamoto Nov 30, 2016
be6bf11
using ScrubbedBytes directly.
kazu-yamamoto Nov 30, 2016
f84aa5d
documentation & relaxing types.
kazu-yamamoto Nov 30, 2016
58151b9
making PRK an instance of ByteArrayAccess and removing fromPRK/toPRK.
kazu-yamamoto Nov 30, 2016
e9ea55a
relaxing types of encodePoint and decodePoint.
kazu-yamamoto Nov 30, 2016
a5fb2ee
don't export function that replace existing functionality and by-pass…
vincenthz Dec 1, 2016
d80a87d
add new EC errors
vincenthz Dec 1, 2016
a9e3917
fix Curve25519 generate secret key to work in the MonadRandom instead…
vincenthz Dec 1, 2016
55f385a
change point decoding to be able to fail explicitely instead of async…
vincenthz Dec 1, 2016
f37d0b7
remove arithmetic on Curve25519. it's mathematically not possible
vincenthz Dec 1, 2016
f1ebbff
fixup haddock markup
vincenthz Dec 1, 2016
07b6e80
Rewrite EC primitive and types to have the curve as type
vincenthz Dec 1, 2016
422c5fd
remove reference to the old api in the documentation
vincenthz Dec 2, 2016
11e42a2
add the binding to get the size by bytes
vincenthz Dec 2, 2016
955f010
add internal proxy type to create witnesses
vincenthz Dec 2, 2016
7e6d7cc
complete rewrite of the type class
vincenthz Dec 2, 2016
8b5a36f
fix ECIES to work with the rewrite
vincenthz Dec 2, 2016
922bed5
add some documentation to ECIES
vincenthz Dec 2, 2016
052417e
properly check for point validity before making a point
vincenthz Dec 2, 2016
5e52a7f
use binary serializer for P256 instead of going through the simple po…
vincenthz Dec 2, 2016
f627bf4
make a faster and more secure related to memory blits of pointDh for …
vincenthz Dec 2, 2016
a9b722b
Add missing compatibility modules
vincenthz Dec 2, 2016
6e1d18f
use the correct compat imports
vincenthz Dec 2, 2016
07bfa10
fix proxy
vincenthz Dec 2, 2016
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
236 changes: 236 additions & 0 deletions Crypto/ECC.hs
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
-- |
-- Module : Crypto.ECC
-- License : BSD-style
-- Maintainer : Vincent Hanquez <[email protected]>
-- Stability : experimental
-- Portability : unknown
--
-- Elliptic Curve Cryptography
--
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Crypto.ECC
( Curve_P256R1(..)
, Curve_P384R1(..)
, Curve_P521R1(..)
, Curve_X25519(..)
, EllipticCurve(..)
, EllipticCurveDH(..)
, EllipticCurveArith(..)
, KeyPair(..)
, SharedSecret(..)
) where

import qualified Crypto.PubKey.ECC.P256 as P256
import qualified Crypto.PubKey.ECC.Types as H
import qualified Crypto.PubKey.ECC.Prim as H
import Crypto.Random
import Crypto.Error
import Crypto.Internal.Imports
import Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes)
import qualified Crypto.Internal.ByteArray as B
import Crypto.Number.Serialize (i2ospOf_, os2ip)
import qualified Crypto.PubKey.Curve25519 as X25519
import Data.Function (on)
import Data.ByteArray (convert)

-- | An elliptic curve key pair composed of the private part (a scalar), and
-- the associated point.
data KeyPair curve = KeyPair
{ keypairGetPublic :: !(Point curve)
, keypairGetPrivate :: !(Scalar curve)
}

newtype SharedSecret = SharedSecret ScrubbedBytes
deriving (Eq, ByteArrayAccess)

class EllipticCurve curve where
-- | Point on an Elliptic Curve
data Point curve :: *

-- | Scalar in the Elliptic Curve domain
data Scalar curve :: *

-- | get the order of the Curve
curveGetOrder :: curve -> Integer

-- | get the curve related to a point on a curve
curveOfPoint :: Point curve -> curve

-- | get the curve related to a curve's scalar
curveOfScalar :: Scalar curve -> curve

-- | get the base point of the Curve
curveGetBasePoint :: Point curve

-- | Generate a new random scalar on the curve.
-- The scalar will represent a number between 1 and the order of the curve non included
curveGenerateScalar :: MonadRandom randomly => randomly (Scalar curve)

-- | Generate a new random keypair
curveGenerateKeyPair :: MonadRandom randomly => randomly (KeyPair curve)

encodePoint :: ByteArray bs => Point curve -> bs
decodePoint :: ByteArray bs => bs -> CryptoFailable (Point curve)

instance {-# OVERLAPPABLE #-} Show (Point a) where
show _ = undefined

instance {-# OVERLAPPABLE #-} Eq (Point a) where
_ == _ = undefined

instance {-# OVERLAPPABLE #-} Show (Scalar a) where
show _ = undefined

instance {-# OVERLAPPABLE #-} Eq (Scalar a) where
_ == _ = undefined

class EllipticCurve curve => EllipticCurveDH curve where
-- | Generate a Diffie hellman secret value.
--
-- This is generally just the .x coordinate of the resulting point, that
-- is not hashed.
--
-- use `pointSmul` to keep the result in Point format.
ecdh :: Scalar curve -> Point curve -> SharedSecret

class EllipticCurve curve => EllipticCurveArith curve where
-- | Add points on a curve
pointAdd :: Point curve -> Point curve -> Point curve

-- | Scalar Multiplication on a curve
pointSmul :: Scalar curve -> Point curve -> Point curve

-- -- | Scalar Inverse
-- scalarInverse :: Scalar curve -> Scalar curve

-- | P256 Curve
--
-- also known as P256
data Curve_P256R1 = Curve_P256R1

instance EllipticCurve Curve_P256R1 where
newtype Point Curve_P256R1 = P256Point { unP256Point :: P256.Point } deriving (Eq,Show)
newtype Scalar Curve_P256R1 = P256Scalar { unP256Scalar :: P256.Scalar } deriving (Eq,Show)
curveGetOrder _ = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
curveGetBasePoint = P256Point P256.pointBase
curveOfScalar _ = Curve_P256R1
curveOfPoint _ = Curve_P256R1
curveGenerateScalar = P256Scalar <$> P256.scalarGenerate
curveGenerateKeyPair = toKeyPair <$> P256.scalarGenerate
where toKeyPair scalar = KeyPair (P256Point $ P256.toPoint scalar) (P256Scalar scalar)
encodePoint (P256Point p) = encodeECPoint x y 32
where
(x,y) = P256.pointToIntegers p
decodePoint bs = fromPoint <$> decodeECPoint bs
where fromPoint (H.Point x y) = P256Point $ P256.pointFromIntegers (x,y)
fromPoint H.PointO = error "impossible happened: fromPoint is infinite"

instance EllipticCurveArith Curve_P256R1 where
pointAdd a b = P256Point $ (P256.pointAdd `on` unP256Point) a b
pointSmul s p = P256Point $ P256.pointMul (unP256Scalar s) (unP256Point p)

instance EllipticCurveDH Curve_P256R1 where
ecdh s p = shared
where
(x, _) = P256.pointToIntegers $ unP256Point $ pointSmul s p
len = 32 -- (256 + 7) `div` 8
shared = SharedSecret $ i2ospOf_ len x

data Curve_P384R1 = Curve_P384R1

instance EllipticCurve Curve_P384R1 where
newtype Point Curve_P384R1 = P384Point { unP384Point :: H.Point } deriving (Eq,Show)
newtype Scalar Curve_P384R1 = P384Scalar { unP384Scalar :: H.PrivateNumber } deriving (Eq,Show)
curveGetOrder _ = H.ecc_n $ H.common_curve $ H.getCurveByName H.SEC_p384r1
curveGetBasePoint = P384Point $ H.ecc_g $ H.common_curve $ H.getCurveByName H.SEC_p384r1
curveOfScalar _ = Curve_P384R1
curveOfPoint _ = Curve_P384R1
curveGenerateScalar = P384Scalar <$> H.scalarGenerate (H.getCurveByName H.SEC_p384r1)
curveGenerateKeyPair = toKeyPair <$> H.scalarGenerate (H.getCurveByName H.SEC_p384r1)
where toKeyPair scalar = KeyPair (P384Point $ H.pointBaseMul (H.getCurveByName H.SEC_p384r1) scalar) (P384Scalar scalar)
encodePoint (P384Point (H.Point x y)) = encodeECPoint x y 48
encodePoint (P384Point _) = error "encodePoint P384"
decodePoint bs = P384Point <$> decodeECPoint bs

instance EllipticCurveArith Curve_P384R1 where
pointAdd a b = P384Point $ (H.pointAdd (H.getCurveByName H.SEC_p384r1) `on` unP384Point) a b
pointSmul s p = P384Point (H.pointMul (H.getCurveByName H.SEC_p384r1) (unP384Scalar s) (unP384Point p))

instance EllipticCurveDH Curve_P384R1 where
ecdh s p = shared
where
H.Point x _ = unP384Point $ pointSmul s p
len = 48 -- (384 + 7) `div` 8
shared = SharedSecret $ i2ospOf_ len x

data Curve_P521R1 = Curve_P521R1

instance EllipticCurve Curve_P521R1 where
newtype Point Curve_P521R1 = P521Point { unP521Point :: H.Point } deriving (Eq,Show)
newtype Scalar Curve_P521R1 = P521Scalar { unP521Scalar :: H.PrivateNumber } deriving (Eq,Show)
curveGetOrder _ = H.ecc_n $ H.common_curve $ H.getCurveByName H.SEC_p521r1
curveGetBasePoint = P521Point $ H.ecc_g $ H.common_curve $ H.getCurveByName H.SEC_p521r1
curveOfScalar _ = Curve_P521R1
curveOfPoint _ = Curve_P521R1
curveGenerateScalar = P521Scalar <$> H.scalarGenerate (H.getCurveByName H.SEC_p521r1)
curveGenerateKeyPair = toKeyPair <$> H.scalarGenerate (H.getCurveByName H.SEC_p521r1)
where toKeyPair scalar = KeyPair (P521Point $ H.pointBaseMul (H.getCurveByName H.SEC_p521r1) scalar) (P521Scalar scalar)
encodePoint (P521Point (H.Point x y)) = encodeECPoint x y 66
encodePoint (P521Point _) = error "encodePoint P521"
decodePoint bs = P521Point <$> decodeECPoint bs

instance EllipticCurveArith Curve_P521R1 where
pointAdd a b = P521Point $ (H.pointAdd (H.getCurveByName H.SEC_p521r1) `on` unP521Point) a b
pointSmul s p = P521Point (H.pointMul (H.getCurveByName H.SEC_p521r1) (unP521Scalar s) (unP521Point p))

instance EllipticCurveDH Curve_P521R1 where
ecdh s p = shared
where
H.Point x _ = unP521Point $ pointSmul s p
len = 66 -- (521 + 7) `div` 8
shared = SharedSecret $ i2ospOf_ len x

data Curve_X25519 = Curve_X25519

instance EllipticCurve Curve_X25519 where
newtype Point Curve_X25519 = X25519Point X25519.PublicKey deriving (Eq,Show)
newtype Scalar Curve_X25519 = X25519Scalar X25519.SecretKey deriving (Eq,Show)
curveGetOrder _ = undefined
curveGetBasePoint = undefined
curveOfScalar _ = Curve_X25519
curveOfPoint _ = Curve_X25519
curveGenerateScalar = X25519Scalar <$> X25519.generateSecretKey
curveGenerateKeyPair = do
s <- X25519.generateSecretKey
let p = X25519.toPublic s
return $ KeyPair (X25519Point p) (X25519Scalar s)
encodePoint (X25519Point p) = B.convert p
decodePoint bs = X25519Point <$> X25519.publicKey bs

instance EllipticCurveDH Curve_X25519 where
ecdh (X25519Scalar s) (X25519Point p) = SharedSecret $ convert secret
where
secret = X25519.dh p s

encodeECPoint :: forall bs. ByteArray bs => Integer -> Integer -> Int -> bs
encodeECPoint x y siz = B.concat [uncompressed,xb,yb]
where
uncompressed, xb, yb :: bs
uncompressed = B.singleton 4
xb = i2ospOf_ siz x
yb = i2ospOf_ siz y

decodeECPoint :: ByteArray bs => bs -> CryptoFailable H.Point
decodeECPoint mxy = case B.uncons mxy of
Nothing -> CryptoFailed $ CryptoError_PointSizeInvalid
Just (m,xy)
-- uncompressed
| m == 4 ->
let siz = B.length xy `div` 2
(xb,yb) = B.splitAt siz xy
x = os2ip xb
y = os2ip yb
in CryptoPassed $ H.Point x y
| otherwise -> CryptoFailed $ CryptoError_PointFormatInvalid
Loading