Blame Crypto/ECC.hs

Packit 141393
-- |
Packit 141393
-- Module      : Crypto.ECC
Packit 141393
-- License     : BSD-style
Packit 141393
-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
Packit 141393
-- Stability   : experimental
Packit 141393
-- Portability : unknown
Packit 141393
--
Packit 141393
-- Elliptic Curve Cryptography
Packit 141393
--
Packit 141393
{-# LANGUAGE DeriveDataTypeable #-}
Packit 141393
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
Packit 141393
{-# LANGUAGE TypeFamilies #-}
Packit 141393
{-# LANGUAGE ScopedTypeVariables #-}
Packit 141393
module Crypto.ECC
Packit 141393
    ( Curve_P256R1(..)
Packit 141393
    , Curve_P384R1(..)
Packit 141393
    , Curve_P521R1(..)
Packit 141393
    , Curve_X25519(..)
Packit 141393
    , Curve_X448(..)
Packit 141393
    , EllipticCurve(..)
Packit 141393
    , EllipticCurveDH(..)
Packit 141393
    , EllipticCurveArith(..)
Packit 141393
    , KeyPair(..)
Packit 141393
    , SharedSecret(..)
Packit 141393
    ) where
Packit 141393
Packit 141393
import qualified Crypto.PubKey.ECC.P256 as P256
Packit 141393
import qualified Crypto.ECC.Simple.Types as Simple
Packit 141393
import qualified Crypto.ECC.Simple.Prim as Simple
Packit 141393
import           Crypto.Random
Packit 141393
import           Crypto.Error
Packit 141393
import           Crypto.Internal.Proxy
Packit 141393
import           Crypto.Internal.Imports
Packit 141393
import           Crypto.Internal.ByteArray (ByteArray, ByteArrayAccess, ScrubbedBytes)
Packit 141393
import qualified Crypto.Internal.ByteArray as B
Packit 141393
import           Crypto.Number.Serialize (i2ospOf_, os2ip)
Packit 141393
import qualified Crypto.PubKey.Curve25519 as X25519
Packit 141393
import qualified Crypto.PubKey.Curve448 as X448
Packit 141393
import           Data.Function (on)
Packit 141393
import           Data.ByteArray (convert)
Packit 141393
import           Data.Data (Data())
Packit 141393
import           Data.Typeable (Typeable())
Packit 141393
Packit 141393
-- | An elliptic curve key pair composed of the private part (a scalar), and
Packit 141393
-- the associated point.
Packit 141393
data KeyPair curve = KeyPair
Packit 141393
    { keypairGetPublic  :: !(Point curve)
Packit 141393
    , keypairGetPrivate :: !(Scalar curve)
Packit 141393
    }
Packit 141393
Packit 141393
newtype SharedSecret = SharedSecret ScrubbedBytes
Packit 141393
    deriving (Eq, ByteArrayAccess)
Packit 141393
Packit 141393
class EllipticCurve curve where
Packit 141393
    -- | Point on an Elliptic Curve
Packit 141393
    type Point curve  :: *
Packit 141393
Packit 141393
    -- | Scalar in the Elliptic Curve domain
Packit 141393
    type Scalar curve :: *
Packit 141393
Packit 141393
    -- | Generate a new random scalar on the curve.
Packit 141393
    -- The scalar will represent a number between 1 and the order of the curve non included
Packit 141393
    curveGenerateScalar :: MonadRandom randomly => proxy curve -> randomly (Scalar curve)
Packit 141393
Packit 141393
    -- | Generate a new random keypair
Packit 141393
    curveGenerateKeyPair :: MonadRandom randomly => proxy curve -> randomly (KeyPair curve)
Packit 141393
Packit 141393
    -- | Get the curve size in bits
Packit 141393
    curveSizeBits :: proxy curve -> Int
Packit 141393
Packit 141393
    -- | Encode a elliptic curve point into binary form
Packit 141393
    encodePoint :: ByteArray bs => proxy curve -> Point curve -> bs
Packit 141393
Packit 141393
    -- | Try to decode the binary form of an elliptic curve point
Packit 141393
    decodePoint :: ByteArray bs => proxy curve -> bs -> CryptoFailable (Point curve)
Packit 141393
Packit 141393
class EllipticCurve curve => EllipticCurveDH curve where
Packit 141393
    -- | Generate a Diffie hellman secret value.
Packit 141393
    --
Packit 141393
    -- This is generally just the .x coordinate of the resulting point, that
Packit 141393
    -- is not hashed.
Packit 141393
    --
Packit 141393
    -- use `pointSmul` to keep the result in Point format.
Packit 141393
    --
Packit 141393
    -- /WARNING:/ Curve implementations may return a special value or an
Packit 141393
    -- exception when the public point lies in a subgroup of small order.
Packit 141393
    -- This function is adequate when the scalar is in expected range and
Packit 141393
    -- contributory behaviour is not needed.  Otherwise use 'ecdh'.
Packit 141393
    ecdhRaw :: proxy curve -> Scalar curve -> Point curve -> SharedSecret
Packit 141393
    ecdhRaw prx s = throwCryptoError . ecdh prx s
Packit 141393
Packit 141393
    -- | Generate a Diffie hellman secret value and verify that the result
Packit 141393
    -- is not the point at infinity.
Packit 141393
    --
Packit 141393
    -- This additional test avoids risks existing with function 'ecdhRaw'.
Packit 141393
    -- Implementations always return a 'CryptoError' instead of a special
Packit 141393
    -- value or an exception.
Packit 141393
    ecdh :: proxy curve -> Scalar curve -> Point curve -> CryptoFailable SharedSecret
Packit 141393
Packit 141393
class EllipticCurve curve => EllipticCurveArith curve where
Packit 141393
    -- | Add points on a curve
Packit 141393
    pointAdd :: proxy curve -> Point curve -> Point curve -> Point curve
Packit 141393
Packit 141393
    -- | Scalar Multiplication on a curve
Packit 141393
    pointSmul :: proxy curve -> Scalar curve -> Point curve -> Point curve
Packit 141393
Packit 141393
--   -- | Scalar Inverse
Packit 141393
--   scalarInverse :: Scalar curve -> Scalar curve
Packit 141393
Packit 141393
-- | P256 Curve
Packit 141393
--
Packit 141393
-- also known as P256
Packit 141393
data Curve_P256R1 = Curve_P256R1
Packit 141393
    deriving (Show,Data,Typeable)
Packit 141393
Packit 141393
instance EllipticCurve Curve_P256R1 where
Packit 141393
    type Point Curve_P256R1 = P256.Point
Packit 141393
    type Scalar Curve_P256R1 = P256.Scalar
Packit 141393
    curveSizeBits _ = 256
Packit 141393
    curveGenerateScalar _ = P256.scalarGenerate
Packit 141393
    curveGenerateKeyPair _ = toKeyPair <$> P256.scalarGenerate
Packit 141393
      where toKeyPair scalar = KeyPair (P256.toPoint scalar) scalar
Packit 141393
    encodePoint _ p = mxy
Packit 141393
      where
Packit 141393
        mxy :: forall bs. ByteArray bs => bs
Packit 141393
        mxy = B.concat [uncompressed, xy]
Packit 141393
          where
Packit 141393
            uncompressed, xy :: bs
Packit 141393
            uncompressed = B.singleton 4
Packit 141393
            xy = P256.pointToBinary p
Packit 141393
    decodePoint _ mxy = case B.uncons mxy of
Packit 141393
        Nothing -> CryptoFailed $ CryptoError_PointSizeInvalid
Packit 141393
        Just (m,xy)
Packit 141393
            -- uncompressed
Packit 141393
            | m == 4 -> P256.pointFromBinary xy
Packit 141393
            | otherwise -> CryptoFailed $ CryptoError_PointFormatInvalid
Packit 141393
Packit 141393
instance EllipticCurveArith Curve_P256R1 where
Packit 141393
    pointAdd  _ a b = P256.pointAdd a b
Packit 141393
    pointSmul _ s p = P256.pointMul s p
Packit 141393
Packit 141393
instance EllipticCurveDH Curve_P256R1 where
Packit 141393
    ecdhRaw _ s p = SharedSecret $ P256.pointDh s p
Packit 141393
    ecdh  prx s p = checkNonZeroDH (ecdhRaw prx s p)
Packit 141393
Packit 141393
data Curve_P384R1 = Curve_P384R1
Packit 141393
    deriving (Show,Data,Typeable)
Packit 141393
Packit 141393
instance EllipticCurve Curve_P384R1 where
Packit 141393
    type Point Curve_P384R1 = Simple.Point Simple.SEC_p384r1
Packit 141393
    type Scalar Curve_P384R1 = Simple.Scalar Simple.SEC_p384r1
Packit 141393
    curveSizeBits _ = 384
Packit 141393
    curveGenerateScalar _ = Simple.scalarGenerate
Packit 141393
    curveGenerateKeyPair _ = toKeyPair <$> Simple.scalarGenerate
Packit 141393
      where toKeyPair scalar = KeyPair (Simple.pointBaseMul scalar) scalar
Packit 141393
    encodePoint _ point = encodeECPoint point
Packit 141393
    decodePoint _ bs = decodeECPoint bs
Packit 141393
Packit 141393
instance EllipticCurveArith Curve_P384R1 where
Packit 141393
    pointAdd _ a b = Simple.pointAdd a b
Packit 141393
    pointSmul _ s p = Simple.pointMul s p
Packit 141393
Packit 141393
instance EllipticCurveDH Curve_P384R1 where
Packit 141393
    ecdh _ s p = encodeECShared prx (Simple.pointMul s p)
Packit 141393
      where
Packit 141393
        prx = Proxy :: Proxy Simple.SEC_p384r1
Packit 141393
Packit 141393
data Curve_P521R1 = Curve_P521R1
Packit 141393
    deriving (Show,Data,Typeable)
Packit 141393
Packit 141393
instance EllipticCurve Curve_P521R1 where
Packit 141393
    type Point Curve_P521R1 = Simple.Point Simple.SEC_p521r1
Packit 141393
    type Scalar Curve_P521R1 = Simple.Scalar Simple.SEC_p521r1
Packit 141393
    curveSizeBits _ = 521
Packit 141393
    curveGenerateScalar _ = Simple.scalarGenerate
Packit 141393
    curveGenerateKeyPair _ = toKeyPair <$> Simple.scalarGenerate
Packit 141393
      where toKeyPair scalar = KeyPair (Simple.pointBaseMul scalar) scalar
Packit 141393
    encodePoint _ point = encodeECPoint point
Packit 141393
    decodePoint _ bs = decodeECPoint bs
Packit 141393
Packit 141393
instance EllipticCurveArith Curve_P521R1 where
Packit 141393
    pointAdd _ a b = Simple.pointAdd a b
Packit 141393
    pointSmul _ s p = Simple.pointMul s p
Packit 141393
Packit 141393
instance EllipticCurveDH Curve_P521R1 where
Packit 141393
    ecdh _ s p = encodeECShared prx (Simple.pointMul s p)
Packit 141393
      where
Packit 141393
        prx = Proxy :: Proxy Simple.SEC_p521r1
Packit 141393
Packit 141393
data Curve_X25519 = Curve_X25519
Packit 141393
    deriving (Show,Data,Typeable)
Packit 141393
Packit 141393
instance EllipticCurve Curve_X25519 where
Packit 141393
    type Point Curve_X25519 = X25519.PublicKey
Packit 141393
    type Scalar Curve_X25519 = X25519.SecretKey
Packit 141393
    curveSizeBits _ = 255
Packit 141393
    curveGenerateScalar _ = X25519.generateSecretKey
Packit 141393
    curveGenerateKeyPair _ = do
Packit 141393
        s <- X25519.generateSecretKey
Packit 141393
        return $ KeyPair (X25519.toPublic s) s
Packit 141393
    encodePoint _ p = B.convert p
Packit 141393
    decodePoint _ bs = X25519.publicKey bs
Packit 141393
Packit 141393
instance EllipticCurveDH Curve_X25519 where
Packit 141393
    ecdhRaw _ s p = SharedSecret $ convert secret
Packit 141393
      where secret = X25519.dh p s
Packit 141393
    ecdh prx s p = checkNonZeroDH (ecdhRaw prx s p)
Packit 141393
Packit 141393
data Curve_X448 = Curve_X448
Packit 141393
    deriving (Show,Data,Typeable)
Packit 141393
Packit 141393
instance EllipticCurve Curve_X448 where
Packit 141393
    type Point Curve_X448 = X448.PublicKey
Packit 141393
    type Scalar Curve_X448 = X448.SecretKey
Packit 141393
    curveSizeBits _ = 448
Packit 141393
    curveGenerateScalar _ = X448.generateSecretKey
Packit 141393
    curveGenerateKeyPair _ = do
Packit 141393
        s <- X448.generateSecretKey
Packit 141393
        return $ KeyPair (X448.toPublic s) s
Packit 141393
    encodePoint _ p = B.convert p
Packit 141393
    decodePoint _ bs = X448.publicKey bs
Packit 141393
Packit 141393
instance EllipticCurveDH Curve_X448 where
Packit 141393
    ecdhRaw _ s p = SharedSecret $ convert secret
Packit 141393
      where secret = X448.dh p s
Packit 141393
    ecdh prx s p = checkNonZeroDH (ecdhRaw prx s p)
Packit 141393
Packit 141393
checkNonZeroDH :: SharedSecret -> CryptoFailable SharedSecret
Packit 141393
checkNonZeroDH s@(SharedSecret b)
Packit 141393
    | B.constAllZero b = CryptoFailed CryptoError_ScalarMultiplicationInvalid
Packit 141393
    | otherwise        = CryptoPassed s
Packit 141393
Packit 141393
encodeECShared :: Simple.Curve curve => Proxy curve -> Simple.Point curve -> CryptoFailable SharedSecret
Packit 141393
encodeECShared _   Simple.PointO      = CryptoFailed CryptoError_ScalarMultiplicationInvalid
Packit 141393
encodeECShared prx (Simple.Point x _) = CryptoPassed . SharedSecret $ i2ospOf_ (Simple.curveSizeBytes prx) x
Packit 141393
Packit 141393
encodeECPoint :: forall curve bs . (Simple.Curve curve, ByteArray bs) => Simple.Point curve -> bs
Packit 141393
encodeECPoint Simple.PointO      = error "encodeECPoint: cannot serialize point at infinity"
Packit 141393
encodeECPoint (Simple.Point x y) = B.concat [uncompressed,xb,yb]
Packit 141393
  where
Packit 141393
    size = Simple.curveSizeBytes (Proxy :: Proxy curve)
Packit 141393
    uncompressed, xb, yb :: bs
Packit 141393
    uncompressed = B.singleton 4
Packit 141393
    xb = i2ospOf_ size x
Packit 141393
    yb = i2ospOf_ size y
Packit 141393
Packit 141393
decodeECPoint :: (Simple.Curve curve, ByteArray bs) => bs -> CryptoFailable (Simple.Point curve)
Packit 141393
decodeECPoint mxy = case B.uncons mxy of
Packit 141393
    Nothing     -> CryptoFailed $ CryptoError_PointSizeInvalid
Packit 141393
    Just (m,xy)
Packit 141393
        -- uncompressed
Packit 141393
        | m == 4 ->
Packit 141393
            let siz = B.length xy `div` 2
Packit 141393
                (xb,yb) = B.splitAt siz xy
Packit 141393
                x = os2ip xb
Packit 141393
                y = os2ip yb
Packit 141393
             in Simple.pointFromIntegers (x,y)
Packit 141393
        | otherwise -> CryptoFailed $ CryptoError_PointFormatInvalid