1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
|
-- |
-- Module : Crypto.ECC.Class
-- License : BSD-style
-- Stability : experimental
-- Portability : unknown
--
-- Elliptic Curve Cryptography
--
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Crypto.ECC.Class
( Curve_X25519(..)
, EllipticCurve(..)
, EllipticCurveDH(..)
, EllipticCurveArith(..)
, KeyPair(..)
, SharedSecret(..)
) where
import qualified Crypto.ECC.Simple.Types as Simple
import qualified Crypto.ECC.Simple.Prim as Simple
import Crypto.Random
-- import Crypto.Error
import Crypto.Error.Types
-- import Crypto.Internal.Proxy
import Data.Typeable
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.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
type Point curve :: *
-- | Scalar in the Elliptic Curve domain
type Scalar 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 => proxy curve -> randomly (Scalar curve)
-- | Generate a new random keypair
curveGenerateKeyPair :: MonadRandom randomly => proxy curve -> randomly (KeyPair curve)
-- | Get the curve size in bits
curveSizeBits :: proxy curve -> Int
-- | Encode a elliptic curve point into binary form
encodePoint :: ByteArray bs => proxy curve -> Point curve -> bs
-- | Try to decode the binary form of an elliptic curve point
decodePoint :: ByteArray bs => proxy curve -> bs -> CryptoFailable (Point curve)
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 :: proxy curve -> Scalar curve -> Point curve -> SharedSecret
class EllipticCurve curve => EllipticCurveArith curve where
-- | Add points on a curve
pointAdd :: proxy curve -> Point curve -> Point curve -> Point curve
-- | Scalar Multiplication on a curve
pointSmul :: proxy curve -> Scalar curve -> Point curve -> Point curve
-- -- | Scalar Inverse
-- scalarInverse :: Scalar curve -> Scalar curve
data Curve_X25519 = Curve_X25519
instance EllipticCurve Curve_X25519 where
type Point Curve_X25519 = X25519.PublicKey
type Scalar Curve_X25519 = X25519.SecretKey
curveSizeBits _ = 255
curveGenerateScalar _ = X25519.generateSecretKey
curveGenerateKeyPair _ = do
s <- X25519.generateSecretKey
return $ KeyPair (X25519.toPublic s) s
encodePoint _ p = B.convert p
decodePoint _ bs = X25519.publicKey bs
instance EllipticCurveDH Curve_X25519 where
ecdh _ s p = SharedSecret $ convert secret
where secret = X25519.dh p s
encodeECPoint :: forall curve bs . (Simple.Curve curve, ByteArray bs) => Simple.Point curve -> bs
encodeECPoint Simple.PointO = error "encodeECPoint: cannot serialize point at infinity"
encodeECPoint (Simple.Point x y) = B.concat [uncompressed,xb,yb]
where
size = Simple.curveSizeBytes (Proxy :: Proxy curve)
uncompressed, xb, yb :: bs
uncompressed = B.singleton 4
xb = i2ospOf_ size x
yb = i2ospOf_ size y
decodeECPoint :: (Simple.Curve curve, ByteArray bs) => bs -> CryptoFailable (Simple.Point curve)
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 Simple.pointFromIntegers (x,y)
| otherwise -> CryptoFailed $ CryptoError_PointFormatInvalid
curveSizeBytes :: EllipticCurve c => Proxy c -> Int
curveSizeBytes proxy = (curveSizeBits proxy + 7) `div` 8
|