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