1-- |
2-- Module      : Crypto.PubKey.ECC.P256
3-- License     : BSD-style
4-- Maintainer  : Vincent Hanquez <vincent@snarc.org>
5-- Stability   : experimental
6-- Portability : unknown
7--
8-- P256 support
9--
10{-# LANGUAGE GeneralizedNewtypeDeriving #-}
11{-# LANGUAGE EmptyDataDecls #-}
12{-# OPTIONS_GHC -fno-warn-unused-binds #-}
13module Crypto.PubKey.ECC.P256
14    ( Scalar
15    , Point
16    -- * Point arithmetic
17    , pointBase
18    , pointAdd
19    , pointNegate
20    , pointMul
21    , pointDh
22    , pointsMulVarTime
23    , pointIsValid
24    , pointIsAtInfinity
25    , toPoint
26    , pointX
27    , pointToIntegers
28    , pointFromIntegers
29    , pointToBinary
30    , pointFromBinary
31    , unsafePointFromBinary
32    -- * Scalar arithmetic
33    , scalarGenerate
34    , scalarZero
35    , scalarN
36    , scalarIsZero
37    , scalarAdd
38    , scalarSub
39    , scalarMul
40    , scalarInv
41    , scalarInvSafe
42    , scalarCmp
43    , scalarFromBinary
44    , scalarToBinary
45    , scalarFromInteger
46    , scalarToInteger
47    ) where
48
49import           Data.Word
50import           Foreign.Ptr
51import           Foreign.C.Types
52
53import           Crypto.Internal.Compat
54import           Crypto.Internal.Imports
55import           Crypto.Internal.ByteArray
56import qualified Crypto.Internal.ByteArray as B
57import           Data.Memory.PtrMethods (memSet)
58import           Crypto.Error
59import           Crypto.Random
60import           Crypto.Number.Serialize.Internal (os2ip, i2ospOf)
61import qualified Crypto.Number.Serialize as S (os2ip, i2ospOf)
62
63-- | A P256 scalar
64newtype Scalar = Scalar ScrubbedBytes
65    deriving (Show,Eq,ByteArrayAccess,NFData)
66
67-- | A P256 point
68newtype Point = Point Bytes
69    deriving (Show,Eq,NFData)
70
71scalarSize :: Int
72scalarSize = 32
73
74pointSize :: Int
75pointSize = 64
76
77type P256Digit  = Word32
78
79data P256Scalar
80data P256Y
81data P256X
82
83order :: Integer
84order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551
85
86------------------------------------------------------------------------
87-- Point methods
88------------------------------------------------------------------------
89
90-- | Get the base point for the P256 Curve
91pointBase :: Point
92pointBase =
93    case scalarFromInteger 1 of
94        CryptoPassed s  -> toPoint s
95        CryptoFailed _ -> error "pointBase: assumption failed"
96
97-- | Lift to curve a scalar
98--
99-- Using the curve generator as base point compute:
100--
101-- > scalar * G
102--
103toPoint :: Scalar -> Point
104toPoint s
105    | scalarIsZero s = error "cannot create point from zero"
106    | otherwise      =
107        withNewPoint $ \px py -> withScalar s $ \p ->
108            ccryptonite_p256_basepoint_mul p px py
109
110-- | Add a point to another point
111pointAdd :: Point -> Point -> Point
112pointAdd a b = withNewPoint $ \dx dy ->
113    withPoint a $ \ax ay -> withPoint b $ \bx by ->
114        ccryptonite_p256e_point_add ax ay bx by dx dy
115
116-- | Negate a point
117pointNegate :: Point -> Point
118pointNegate a = withNewPoint $ \dx dy ->
119    withPoint a $ \ax ay ->
120        ccryptonite_p256e_point_negate ax ay dx dy
121
122-- | Multiply a point by a scalar
123--
124-- warning: variable time
125pointMul :: Scalar -> Point -> Point
126pointMul scalar p = withNewPoint $ \dx dy ->
127    withScalar scalar $ \n -> withPoint p $ \px py ->
128        ccryptonite_p256e_point_mul n px py dx dy
129
130-- | Similar to 'pointMul', serializing the x coordinate as binary.
131-- When scalar is multiple of point order the result is all zero.
132pointDh :: ByteArray binary => Scalar -> Point -> binary
133pointDh scalar p =
134    B.unsafeCreate scalarSize $ \dst -> withTempPoint $ \dx dy -> do
135        withScalar scalar $ \n -> withPoint p $ \px py ->
136            ccryptonite_p256e_point_mul n px py dx dy
137        ccryptonite_p256_to_bin (castPtr dx) dst
138
139-- | multiply the point @p with @n2 and add a lifted to curve value @n1
140--
141-- > n1 * G + n2 * p
142--
143-- warning: variable time
144pointsMulVarTime :: Scalar -> Scalar -> Point -> Point
145pointsMulVarTime n1 n2 p = withNewPoint $ \dx dy ->
146    withScalar n1 $ \pn1 -> withScalar n2 $ \pn2 -> withPoint p $ \px py ->
147        ccryptonite_p256_points_mul_vartime pn1 pn2 px py dx dy
148
149-- | Check if a 'Point' is valid
150pointIsValid :: Point -> Bool
151pointIsValid p = unsafeDoIO $ withPoint p $ \px py -> do
152    r <- ccryptonite_p256_is_valid_point px py
153    return (r /= 0)
154
155-- | Check if a 'Point' is the point at infinity
156pointIsAtInfinity :: Point -> Bool
157pointIsAtInfinity (Point b) = constAllZero b
158
159-- | Return the x coordinate as a 'Scalar' if the point is not at infinity
160pointX :: Point -> Maybe Scalar
161pointX p
162    | pointIsAtInfinity p = Nothing
163    | otherwise           = Just $
164        withNewScalarFreeze $ \d    ->
165        withPoint p         $ \px _ ->
166            ccryptonite_p256_mod ccryptonite_SECP256r1_n (castPtr px) (castPtr d)
167
168-- | Convert a point to (x,y) Integers
169pointToIntegers :: Point -> (Integer, Integer)
170pointToIntegers p = unsafeDoIO $ withPoint p $ \px py ->
171    allocTemp 32 (serialize (castPtr px) (castPtr py))
172  where
173    serialize px py temp = do
174        ccryptonite_p256_to_bin px temp
175        x <- os2ip temp scalarSize
176        ccryptonite_p256_to_bin py temp
177        y <- os2ip temp scalarSize
178        return (x,y)
179
180-- | Convert from (x,y) Integers to a point
181pointFromIntegers :: (Integer, Integer) -> Point
182pointFromIntegers (x,y) = withNewPoint $ \dx dy ->
183    allocTemp scalarSize (\temp -> fill temp (castPtr dx) x >> fill temp (castPtr dy) y)
184  where
185    -- put @n to @temp in big endian format, then from @temp to @dest in p256 scalar format
186    fill :: Ptr Word8 -> Ptr P256Scalar -> Integer -> IO ()
187    fill temp dest n = do
188        -- write the integer in big endian format to temp
189        memSet temp 0 scalarSize
190        e <- i2ospOf n temp scalarSize
191        if e == 0
192            then error "pointFromIntegers: filling failed"
193            else return ()
194        -- then fill dest with the P256 scalar from temp
195        ccryptonite_p256_from_bin temp dest
196
197-- | Convert a point to a binary representation
198pointToBinary :: ByteArray ba => Point -> ba
199pointToBinary p = B.unsafeCreate pointSize $ \dst -> withPoint p $ \px py -> do
200    ccryptonite_p256_to_bin (castPtr px) dst
201    ccryptonite_p256_to_bin (castPtr py) (dst `plusPtr` 32)
202
203-- | Convert from binary to a valid point
204pointFromBinary :: ByteArrayAccess ba => ba -> CryptoFailable Point
205pointFromBinary ba = unsafePointFromBinary ba >>= validatePoint
206  where
207    validatePoint :: Point -> CryptoFailable Point
208    validatePoint p
209        | pointIsValid p = CryptoPassed p
210        | otherwise      = CryptoFailed CryptoError_PointCoordinatesInvalid
211
212-- | Convert from binary to a point, possibly invalid
213unsafePointFromBinary :: ByteArrayAccess ba => ba -> CryptoFailable Point
214unsafePointFromBinary ba
215    | B.length ba /= pointSize = CryptoFailed CryptoError_PublicKeySizeInvalid
216    | otherwise                =
217        CryptoPassed $ withNewPoint $ \px py -> B.withByteArray ba $ \src -> do
218            ccryptonite_p256_from_bin src                        (castPtr px)
219            ccryptonite_p256_from_bin (src `plusPtr` scalarSize) (castPtr py)
220
221------------------------------------------------------------------------
222-- Scalar methods
223------------------------------------------------------------------------
224
225-- | Generate a randomly generated new scalar
226scalarGenerate :: MonadRandom randomly => randomly Scalar
227scalarGenerate = unwrap . scalarFromBinary . witness <$> getRandomBytes 32
228  where
229    unwrap (CryptoFailed _) = error "scalarGenerate: assumption failed"
230    unwrap (CryptoPassed s) = s
231    witness :: ScrubbedBytes -> ScrubbedBytes
232    witness = id
233
234-- | The scalar representing 0
235scalarZero :: Scalar
236scalarZero = withNewScalarFreeze $ \d -> ccryptonite_p256_init d
237
238-- | The scalar representing the curve order
239scalarN :: Scalar
240scalarN = throwCryptoError (scalarFromInteger order)
241
242-- | Check if the scalar is 0
243scalarIsZero :: Scalar -> Bool
244scalarIsZero s = unsafeDoIO $ withScalar s $ \d -> do
245    result <- ccryptonite_p256_is_zero d
246    return $ result /= 0
247
248-- | Perform addition between two scalars
249--
250-- > a + b
251scalarAdd :: Scalar -> Scalar -> Scalar
252scalarAdd a b =
253    withNewScalarFreeze $ \d -> withScalar a $ \pa -> withScalar b $ \pb ->
254        ccryptonite_p256e_modadd ccryptonite_SECP256r1_n pa pb d
255
256-- | Perform subtraction between two scalars
257--
258-- > a - b
259scalarSub :: Scalar -> Scalar -> Scalar
260scalarSub a b =
261    withNewScalarFreeze $ \d -> withScalar a $ \pa -> withScalar b $ \pb ->
262        ccryptonite_p256e_modsub ccryptonite_SECP256r1_n pa pb d
263
264-- | Perform multiplication between two scalars
265--
266-- > a * b
267scalarMul :: Scalar -> Scalar -> Scalar
268scalarMul a b =
269    withNewScalarFreeze $ \d -> withScalar a $ \pa -> withScalar b $ \pb ->
270         ccryptonite_p256_modmul ccryptonite_SECP256r1_n pa 0 pb d
271
272-- | Give the inverse of the scalar
273--
274-- > 1 / a
275--
276-- warning: variable time
277scalarInv :: Scalar -> Scalar
278scalarInv a =
279    withNewScalarFreeze $ \b -> withScalar a $ \pa ->
280        ccryptonite_p256_modinv_vartime ccryptonite_SECP256r1_n pa b
281
282-- | Give the inverse of the scalar using safe exponentiation
283--
284-- > 1 / a
285scalarInvSafe :: Scalar -> Scalar
286scalarInvSafe a =
287    withNewScalarFreeze $ \b -> withScalar a $ \pa ->
288        ccryptonite_p256e_scalar_invert pa b
289
290-- | Compare 2 Scalar
291scalarCmp :: Scalar -> Scalar -> Ordering
292scalarCmp a b = unsafeDoIO $
293    withScalar a $ \pa -> withScalar b $ \pb -> do
294        v <- ccryptonite_p256_cmp pa pb
295        return $ compare v 0
296
297-- | convert a scalar from binary
298scalarFromBinary :: ByteArrayAccess ba => ba -> CryptoFailable Scalar
299scalarFromBinary ba
300    | B.length ba /= scalarSize = CryptoFailed CryptoError_SecretKeySizeInvalid
301    | otherwise                 =
302        CryptoPassed $ withNewScalarFreeze $ \p -> B.withByteArray ba $ \b ->
303            ccryptonite_p256_from_bin b p
304{-# NOINLINE scalarFromBinary #-}
305
306-- | convert a scalar to binary
307scalarToBinary :: ByteArray ba => Scalar -> ba
308scalarToBinary s = B.unsafeCreate scalarSize $ \b -> withScalar s $ \p ->
309    ccryptonite_p256_to_bin p b
310{-# NOINLINE scalarToBinary #-}
311
312-- | Convert from an Integer to a P256 Scalar
313scalarFromInteger :: Integer -> CryptoFailable Scalar
314scalarFromInteger i =
315    maybe (CryptoFailed CryptoError_SecretKeySizeInvalid) scalarFromBinary (S.i2ospOf 32 i :: Maybe Bytes)
316
317-- | Convert from a P256 Scalar to an Integer
318scalarToInteger :: Scalar -> Integer
319scalarToInteger s = S.os2ip (scalarToBinary s :: Bytes)
320
321------------------------------------------------------------------------
322-- Memory Helpers
323------------------------------------------------------------------------
324withNewPoint :: (Ptr P256X -> Ptr P256Y -> IO ()) -> Point
325withNewPoint f = Point $ B.unsafeCreate pointSize $ \px -> f px (pxToPy px)
326{-# NOINLINE withNewPoint #-}
327
328withPoint :: Point -> (Ptr P256X -> Ptr P256Y -> IO a) -> IO a
329withPoint (Point d) f = B.withByteArray d $ \px -> f px (pxToPy px)
330
331pxToPy :: Ptr P256X -> Ptr P256Y
332pxToPy px = castPtr (px `plusPtr` scalarSize)
333
334withNewScalarFreeze :: (Ptr P256Scalar -> IO ()) -> Scalar
335withNewScalarFreeze f = Scalar $ B.allocAndFreeze scalarSize f
336{-# NOINLINE withNewScalarFreeze #-}
337
338withTempPoint :: (Ptr P256X -> Ptr P256Y -> IO a) -> IO a
339withTempPoint f = allocTempScrubbed pointSize (\p -> let px = castPtr p in f px (pxToPy px))
340
341withScalar :: Scalar -> (Ptr P256Scalar -> IO a) -> IO a
342withScalar (Scalar d) f = B.withByteArray d f
343
344allocTemp :: Int -> (Ptr Word8 -> IO a) -> IO a
345allocTemp n f = ignoreSnd <$> B.allocRet n f
346  where
347    ignoreSnd :: (a, Bytes) -> a
348    ignoreSnd = fst
349
350allocTempScrubbed :: Int -> (Ptr Word8 -> IO a) -> IO a
351allocTempScrubbed n f = ignoreSnd <$> B.allocRet n f
352  where
353    ignoreSnd :: (a, ScrubbedBytes) -> a
354    ignoreSnd = fst
355
356------------------------------------------------------------------------
357-- Foreign bindings
358------------------------------------------------------------------------
359foreign import ccall "&cryptonite_SECP256r1_n"
360    ccryptonite_SECP256r1_n :: Ptr P256Scalar
361foreign import ccall "&cryptonite_SECP256r1_p"
362    ccryptonite_SECP256r1_p :: Ptr P256Scalar
363foreign import ccall "&cryptonite_SECP256r1_b"
364    ccryptonite_SECP256r1_b :: Ptr P256Scalar
365
366foreign import ccall "cryptonite_p256_init"
367    ccryptonite_p256_init :: Ptr P256Scalar -> IO ()
368foreign import ccall "cryptonite_p256_is_zero"
369    ccryptonite_p256_is_zero :: Ptr P256Scalar -> IO CInt
370foreign import ccall "cryptonite_p256_clear"
371    ccryptonite_p256_clear :: Ptr P256Scalar -> IO ()
372foreign import ccall "cryptonite_p256e_modadd"
373    ccryptonite_p256e_modadd :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO ()
374foreign import ccall "cryptonite_p256_add_d"
375    ccryptonite_p256_add_d :: Ptr P256Scalar -> P256Digit -> Ptr P256Scalar -> IO CInt
376foreign import ccall "cryptonite_p256e_modsub"
377    ccryptonite_p256e_modsub :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO ()
378foreign import ccall "cryptonite_p256_cmp"
379    ccryptonite_p256_cmp :: Ptr P256Scalar -> Ptr P256Scalar -> IO CInt
380foreign import ccall "cryptonite_p256_mod"
381    ccryptonite_p256_mod :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO ()
382foreign import ccall "cryptonite_p256_modmul"
383    ccryptonite_p256_modmul :: Ptr P256Scalar -> Ptr P256Scalar -> P256Digit -> Ptr P256Scalar -> Ptr P256Scalar -> IO ()
384foreign import ccall "cryptonite_p256e_scalar_invert"
385    ccryptonite_p256e_scalar_invert :: Ptr P256Scalar -> Ptr P256Scalar -> IO ()
386--foreign import ccall "cryptonite_p256_modinv"
387--    ccryptonite_p256_modinv :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO ()
388foreign import ccall "cryptonite_p256_modinv_vartime"
389    ccryptonite_p256_modinv_vartime :: Ptr P256Scalar -> Ptr P256Scalar -> Ptr P256Scalar -> IO ()
390foreign import ccall "cryptonite_p256_base_point_mul"
391    ccryptonite_p256_basepoint_mul :: Ptr P256Scalar
392                                   -> Ptr P256X -> Ptr P256Y
393                                   -> IO ()
394
395foreign import ccall "cryptonite_p256e_point_add"
396    ccryptonite_p256e_point_add :: Ptr P256X -> Ptr P256Y
397                                -> Ptr P256X -> Ptr P256Y
398                                -> Ptr P256X -> Ptr P256Y
399                                -> IO ()
400
401foreign import ccall "cryptonite_p256e_point_negate"
402    ccryptonite_p256e_point_negate :: Ptr P256X -> Ptr P256Y
403                                   -> Ptr P256X -> Ptr P256Y
404                                   -> IO ()
405
406-- compute (out_x,out_y) = n * (in_x,in_y)
407foreign import ccall "cryptonite_p256e_point_mul"
408    ccryptonite_p256e_point_mul :: Ptr P256Scalar -- n
409                                -> Ptr P256X -> Ptr P256Y -- in_{x,y}
410                                -> Ptr P256X -> Ptr P256Y -- out_{x,y}
411                                -> IO ()
412
413-- compute (out_x,out,y) = n1 * G + n2 * (in_x,in_y)
414foreign import ccall "cryptonite_p256_points_mul_vartime"
415    ccryptonite_p256_points_mul_vartime :: Ptr P256Scalar -- n1
416                                        -> Ptr P256Scalar -- n2
417                                        -> Ptr P256X -> Ptr P256Y -- in_{x,y}
418                                        -> Ptr P256X -> Ptr P256Y -- out_{x,y}
419                                        -> IO ()
420foreign import ccall "cryptonite_p256_is_valid_point"
421    ccryptonite_p256_is_valid_point :: Ptr P256X -> Ptr P256Y -> IO CInt
422
423foreign import ccall "cryptonite_p256_to_bin"
424    ccryptonite_p256_to_bin :: Ptr P256Scalar -> Ptr Word8 -> IO ()
425
426foreign import ccall "cryptonite_p256_from_bin"
427    ccryptonite_p256_from_bin :: Ptr Word8 -> Ptr P256Scalar -> IO ()
428