Arithmetic primitives over curve Ed25519
This commit is contained in:
parent
fcf1ff55fb
commit
9ea718f55e
232
Crypto/ECC/Ed25519.hs
Normal file
232
Crypto/ECC/Ed25519.hs
Normal file
@ -0,0 +1,232 @@
|
||||
-- |
|
||||
-- Module : Crypto.ECC.Ed25519
|
||||
-- License : BSD-style
|
||||
-- Maintainer : Olivier Chéron <olivier.cheron@gmail.com>
|
||||
-- Stability : experimental
|
||||
-- Portability : unknown
|
||||
--
|
||||
-- Ed25519 arithmetic primitives.
|
||||
--
|
||||
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
|
||||
module Crypto.ECC.Ed25519
|
||||
( Scalar
|
||||
, Point
|
||||
-- * Scalars
|
||||
, scalarGenerate
|
||||
, scalarDecodeLong
|
||||
, scalarEncode
|
||||
-- * Points
|
||||
, pointDecode
|
||||
, pointEncode
|
||||
-- * Arithmetic functions
|
||||
, toPoint
|
||||
, pointAdd
|
||||
, pointDouble
|
||||
, pointMul
|
||||
) where
|
||||
|
||||
import Data.Bits
|
||||
import Data.Word
|
||||
import Foreign.C.Types
|
||||
import Foreign.Ptr
|
||||
import Foreign.Storable
|
||||
|
||||
import Crypto.Error
|
||||
import Crypto.Internal.ByteArray (ByteArrayAccess, Bytes,
|
||||
ScrubbedBytes, withByteArray)
|
||||
import qualified Crypto.Internal.ByteArray as B
|
||||
import Crypto.Internal.Compat
|
||||
import Crypto.Internal.Imports
|
||||
import Crypto.Random
|
||||
|
||||
|
||||
scalarArraySize :: Int
|
||||
scalarArraySize = 40 -- maximum [9 * 4 {- 32 bits -}, 5 * 8 {- 64 bits -}]
|
||||
|
||||
-- | A scalar modulo order of curve Ed25519.
|
||||
newtype Scalar = Scalar ScrubbedBytes
|
||||
deriving (Show,NFData)
|
||||
|
||||
instance Eq Scalar where
|
||||
(Scalar s1) == (Scalar s2) = unsafeDoIO $
|
||||
withByteArray s1 $ \ps1 ->
|
||||
withByteArray s2 $ \ps2 ->
|
||||
fmap (/= 0) (ed25519_scalar_eq ps1 ps2)
|
||||
{-# NOINLINE (==) #-}
|
||||
|
||||
pointArraySize :: Int
|
||||
pointArraySize = 160 -- maximum [4 * 10 * 4 {- 32 bits -}, 4 * 5 * 8 {- 64 bits -}]
|
||||
|
||||
-- | A point on curve Ed25519.
|
||||
newtype Point = Point Bytes
|
||||
deriving NFData
|
||||
|
||||
instance Show Point where
|
||||
showsPrec d p =
|
||||
let bs = pointEncode p :: Bytes
|
||||
in showParen (d > 10) $ showString "Point "
|
||||
. shows (B.convertToBase B.Base16 bs :: Bytes)
|
||||
|
||||
instance Eq Point where
|
||||
(Point p1) == (Point p2) = unsafeDoIO $
|
||||
withByteArray p1 $ \pp1 ->
|
||||
withByteArray p2 $ \pp2 ->
|
||||
fmap (/= 0) (ed25519_point_eq pp1 pp2)
|
||||
{-# NOINLINE (==) #-}
|
||||
|
||||
-- | Generate a random scalar.
|
||||
scalarGenerate :: MonadRandom randomly => randomly Scalar
|
||||
scalarGenerate = unwrap . scalarDecodeLong . clamp <$> generate
|
||||
where
|
||||
unwrap (CryptoPassed x) = x
|
||||
unwrap (CryptoFailed _) = error "scalarGenerate: assumption failed"
|
||||
|
||||
generate :: MonadRandom randomly => randomly ScrubbedBytes
|
||||
generate = getRandomBytes 32
|
||||
|
||||
-- Uses the same bit mask than during key-generation procedure,
|
||||
-- but without making divisible by 8. As a consequence of modular
|
||||
-- reduction, distribution is not uniform. But the curve order is
|
||||
-- very close to 2^252 so only a tiny fraction of the scalars have
|
||||
-- lower probability, roughly 1/(2^126) of all possible values.
|
||||
clamp :: ByteArrayAccess ba => ba -> ScrubbedBytes
|
||||
clamp bs = B.copyAndFreeze bs $ \p -> do
|
||||
b31 <- peekElemOff p 31 :: IO Word8
|
||||
pokeElemOff p 31 ((b31 .&. 0x7F) .|. 0x40)
|
||||
|
||||
-- | Serialize a scalar to binary, i.e. a 32-byte little-endian
|
||||
-- number.
|
||||
--
|
||||
-- Format is binary compatible with 'Crypto.PubKey.Curve25519.SecretKey'
|
||||
-- from module "Crypto.PubKey.Curve25519".
|
||||
scalarEncode :: B.ByteArray bs => Scalar -> bs
|
||||
scalarEncode (Scalar s) =
|
||||
B.allocAndFreeze 32 $ \out ->
|
||||
withByteArray s $ \ps -> ed25519_scalar_encode out ps
|
||||
|
||||
-- | Deserialize a little-endian number as a scalar. Input array can
|
||||
-- have any length from 0 to 64 bytes.
|
||||
scalarDecodeLong :: B.ByteArrayAccess bs => bs -> CryptoFailable Scalar
|
||||
scalarDecodeLong bs
|
||||
| B.length bs > 64 = CryptoFailed CryptoError_EcScalarOutOfBounds
|
||||
| otherwise = unsafeDoIO $ withByteArray bs initialize
|
||||
where
|
||||
len = fromIntegral $ B.length bs
|
||||
initialize inp = do
|
||||
s <- B.alloc scalarArraySize $ \ps ->
|
||||
ed25519_scalar_decode_long ps inp len
|
||||
return $ CryptoPassed (Scalar s)
|
||||
{-# NOINLINE scalarDecodeLong #-}
|
||||
|
||||
-- | Multiplies a scalar with the curve base point.
|
||||
toPoint :: Scalar -> Point
|
||||
toPoint (Scalar scalar) =
|
||||
Point $ B.allocAndFreeze pointArraySize $ \out ->
|
||||
withByteArray scalar $ \pscalar ->
|
||||
ed25519_point_base_scalarmul out pscalar
|
||||
|
||||
-- | Serialize a point to a 32-byte array.
|
||||
--
|
||||
-- Format is binary compatible with 'Crypto.PubKey.Ed25519.PublicKey'
|
||||
-- from module "Crypto.PubKey.Ed25519".
|
||||
pointEncode :: B.ByteArray bs => Point -> bs
|
||||
pointEncode (Point p) =
|
||||
B.allocAndFreeze 32 $ \out ->
|
||||
withByteArray p $ \pp ->
|
||||
ed25519_point_encode out pp
|
||||
|
||||
-- | Deserialize a 32-byte array as a point, ensuring the point is
|
||||
-- valid on Ed25519.
|
||||
--
|
||||
-- /WARNING:/ variable time
|
||||
pointDecode :: B.ByteArrayAccess bs => bs -> CryptoFailable Point
|
||||
pointDecode bs
|
||||
| B.length bs == 32 = unsafeDoIO $ withByteArray bs initialize
|
||||
| otherwise = CryptoFailed CryptoError_PointSizeInvalid
|
||||
where
|
||||
initialize inp = do
|
||||
(res, p) <- B.allocRet pointArraySize $ \pp ->
|
||||
ed25519_point_decode_vartime pp inp
|
||||
if res == 0 then return $ CryptoFailed CryptoError_PointCoordinatesInvalid
|
||||
else return $ CryptoPassed (Point p)
|
||||
{-# NOINLINE pointDecode #-}
|
||||
|
||||
-- | Add two points.
|
||||
pointAdd :: Point -> Point -> Point
|
||||
pointAdd (Point a) (Point b) =
|
||||
Point $ B.allocAndFreeze pointArraySize $ \out ->
|
||||
withByteArray a $ \pa ->
|
||||
withByteArray b $ \pb ->
|
||||
ed25519_point_add out pa pb
|
||||
|
||||
-- | Add a point to itself.
|
||||
--
|
||||
-- @
|
||||
-- pointDouble p = 'pointAdd' p p
|
||||
-- @
|
||||
pointDouble :: Point -> Point
|
||||
pointDouble (Point a) =
|
||||
Point $ B.allocAndFreeze pointArraySize $ \out ->
|
||||
withByteArray a $ \pa ->
|
||||
ed25519_point_double out pa
|
||||
|
||||
-- | Scalar multiplication over Ed25519.
|
||||
pointMul :: Scalar -> Point -> Point
|
||||
pointMul (Scalar scalar) (Point base) =
|
||||
Point $ B.allocAndFreeze pointArraySize $ \out ->
|
||||
withByteArray scalar $ \pscalar ->
|
||||
withByteArray base $ \pbase ->
|
||||
ed25519_point_scalarmul out pbase pscalar
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_scalar_eq"
|
||||
ed25519_scalar_eq :: Ptr Scalar
|
||||
-> Ptr Scalar
|
||||
-> IO CInt
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_scalar_encode"
|
||||
ed25519_scalar_encode :: Ptr Word8
|
||||
-> Ptr Scalar
|
||||
-> IO ()
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_scalar_decode_long"
|
||||
ed25519_scalar_decode_long :: Ptr Scalar
|
||||
-> Ptr Word8
|
||||
-> CSize
|
||||
-> IO ()
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_point_encode"
|
||||
ed25519_point_encode :: Ptr Word8
|
||||
-> Ptr Point
|
||||
-> IO ()
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_point_decode_vartime"
|
||||
ed25519_point_decode_vartime :: Ptr Point
|
||||
-> Ptr Word8
|
||||
-> IO CInt
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_point_eq"
|
||||
ed25519_point_eq :: Ptr Point
|
||||
-> Ptr Point
|
||||
-> IO CInt
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_point_add"
|
||||
ed25519_point_add :: Ptr Point -- sum
|
||||
-> Ptr Point -- a
|
||||
-> Ptr Point -- b
|
||||
-> IO ()
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_point_double"
|
||||
ed25519_point_double :: Ptr Point -- two_a
|
||||
-> Ptr Point -- a
|
||||
-> IO ()
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_point_base_scalarmul"
|
||||
ed25519_point_base_scalarmul :: Ptr Point -- scaled
|
||||
-> Ptr Scalar -- scalar
|
||||
-> IO ()
|
||||
|
||||
foreign import ccall "cryptonite_ed25519_point_scalarmul"
|
||||
ed25519_point_scalarmul :: Ptr Point -- scaled
|
||||
-> Ptr Point -- base
|
||||
-> Ptr Scalar -- scalar
|
||||
-> IO ()
|
||||
122
cbits/ed25519/ed25519-cryptonite-exts.h
Normal file
122
cbits/ed25519/ed25519-cryptonite-exts.h
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
Public domain by Olivier Chéron <olivier.cheron@gmail.com>
|
||||
|
||||
Arithmetic extensions to Ed25519-donna
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
Scalar functions
|
||||
*/
|
||||
|
||||
void
|
||||
ED25519_FN(ed25519_scalar_encode) (unsigned char out[32], const bignum256modm in) {
|
||||
contract256_modm(out, in);
|
||||
}
|
||||
|
||||
void
|
||||
ED25519_FN(ed25519_scalar_decode_long) (bignum256modm out, const unsigned char *in, size_t len) {
|
||||
expand256_modm(out, in, len);
|
||||
}
|
||||
|
||||
int
|
||||
ED25519_FN(ed25519_scalar_eq) (const bignum256modm a, const bignum256modm b) {
|
||||
bignum256modm_element_t e = 0;
|
||||
|
||||
for (int i = 0; i < bignum256modm_limb_size; i++) {
|
||||
e |= a[i] ^ b[i];
|
||||
}
|
||||
|
||||
return (int) (1 & ((e - 1) >> bignum256modm_bits_per_limb));
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Point functions
|
||||
*/
|
||||
|
||||
void
|
||||
ED25519_FN(ed25519_point_encode) (unsigned char r[32], const ge25519 *p) {
|
||||
ge25519_pack(r, p);
|
||||
}
|
||||
|
||||
int
|
||||
ED25519_FN(ed25519_point_decode_vartime) (ge25519 *r, const unsigned char p[32]) {
|
||||
unsigned char p_neg[32];
|
||||
|
||||
// invert parity bit of X coordinate so the point is negated twice
|
||||
// (once here, once in ge25519_unpack_negative_vartime)
|
||||
for (int i = 0; i < 31; i++) {
|
||||
p_neg[i] = p[i];
|
||||
}
|
||||
p_neg[31] = p[31] ^ 0x80;
|
||||
|
||||
return ge25519_unpack_negative_vartime(r, p_neg);
|
||||
}
|
||||
|
||||
int
|
||||
ED25519_FN(ed25519_point_eq) (const ge25519 *p, const ge25519 *q) {
|
||||
bignum25519 a, b;
|
||||
unsigned char contract_a[32], contract_b[32];
|
||||
int eq;
|
||||
|
||||
// pX * qZ = qX * pZ
|
||||
curve25519_mul(a, p->x, q->z);
|
||||
curve25519_contract(contract_a, a);
|
||||
curve25519_mul(b, q->x, p->z);
|
||||
curve25519_contract(contract_b, b);
|
||||
eq = ed25519_verify(contract_a, contract_b, 32);
|
||||
|
||||
// pY * qZ = qY * pZ
|
||||
curve25519_mul(a, p->y, q->z);
|
||||
curve25519_contract(contract_a, a);
|
||||
curve25519_mul(b, q->y, p->z);
|
||||
curve25519_contract(contract_b, b);
|
||||
eq &= ed25519_verify(contract_a, contract_b, 32);
|
||||
|
||||
return eq;
|
||||
}
|
||||
|
||||
void
|
||||
ED25519_FN(ed25519_point_add) (ge25519 *r, const ge25519 *p, const ge25519 *q) {
|
||||
ge25519_add(r, p, q);
|
||||
}
|
||||
|
||||
void
|
||||
ED25519_FN(ed25519_point_double) (ge25519 *r, const ge25519 *p) {
|
||||
ge25519_double(r, p);
|
||||
}
|
||||
|
||||
void
|
||||
ED25519_FN(ed25519_point_base_scalarmul) (ge25519 *r, const bignum256modm s) {
|
||||
ge25519_scalarmult_base_niels(r, ge25519_niels_base_multiples, s);
|
||||
}
|
||||
|
||||
void
|
||||
ED25519_FN(ed25519_point_scalarmul) (ge25519 *r, const ge25519 *p, const bignum256modm s) {
|
||||
ge25519 tmp;
|
||||
uint32_t scalar_bit;
|
||||
unsigned char ss[32];
|
||||
|
||||
// transform scalar as little-endian number
|
||||
contract256_modm(ss, s);
|
||||
|
||||
// initialize r to identity
|
||||
memset(r, 0, sizeof(ge25519));
|
||||
r->y[0] = 1;
|
||||
r->z[0] = 1;
|
||||
|
||||
// double-add-always
|
||||
for (int i = 31; i >= 0; i--) {
|
||||
for (int j = 7; j >= 0; j--) {
|
||||
ge25519_double(r, r);
|
||||
|
||||
ge25519_add(&tmp, r, p);
|
||||
scalar_bit = (ss[i] >> j) & 1;
|
||||
curve25519_swap_conditional(r->x, tmp.x, scalar_bit);
|
||||
curve25519_swap_conditional(r->y, tmp.y, scalar_bit);
|
||||
curve25519_swap_conditional(r->z, tmp.z, scalar_bit);
|
||||
curve25519_swap_conditional(r->t, tmp.t, scalar_bit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -11,6 +11,7 @@
|
||||
#include "ed25519.h"
|
||||
#include "ed25519-randombytes.h"
|
||||
#include "ed25519-hash.h"
|
||||
#include "ed25519-cryptonite-exts.h"
|
||||
|
||||
/*
|
||||
Generates a (extsk[0..31]) and aExt (extsk[32..63])
|
||||
|
||||
@ -121,6 +121,7 @@ Library
|
||||
Crypto.Data.AFIS
|
||||
Crypto.Data.Padding
|
||||
Crypto.ECC
|
||||
Crypto.ECC.Ed25519
|
||||
Crypto.Error
|
||||
Crypto.MAC.CMAC
|
||||
Crypto.MAC.Poly1305
|
||||
|
||||
Loading…
Reference in New Issue
Block a user