summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoe Crayne <joe@jerkface.net>2018-09-21 18:13:12 -0400
committerJoe Crayne <joe@jerkface.net>2018-09-21 18:18:58 -0400
commit92957a792a87dbc4d2b8120aaf57bf983c4796e2 (patch)
treeb6db171250edd3e5407ec94908ab3714b2128977
parentadf62395931f4b90491cc1338b9a5105da98fbbe (diff)
XEd25519 signature algorithm.
-rw-r--r--src/Crypto/Nonce.hs49
-rw-r--r--src/Crypto/XEd25519.hs185
-rw-r--r--src/Crypto/XEd25519/FieldElement.hs49
-rw-r--r--src/Data/Sized.hs14
4 files changed, 297 insertions, 0 deletions
diff --git a/src/Crypto/Nonce.hs b/src/Crypto/Nonce.hs
new file mode 100644
index 00000000..263f9b0a
--- /dev/null
+++ b/src/Crypto/Nonce.hs
@@ -0,0 +1,49 @@
1{-# LANGUAGE DeriveDataTypeable #-}
2{-# LANGUAGE GeneralizedNewtypeDeriving #-}
3module Crypto.Nonce
4 ( Nonce32
5 , generateNonce32
6 , zeros32
7 ) where
8
9import Crypto.Random
10import Data.ByteArray as BA
11import Data.ByteString as B
12import qualified Data.ByteString.Base64 as Base64
13import Data.ByteString.Char8 as B8
14import Data.Data
15import Data.Serialize
16import Data.Sized
17
18newtype Nonce32 = Nonce32 ByteString
19 deriving (Eq, Ord, ByteArrayAccess, Data)
20
21bin2base64 :: ByteArrayAccess bs => bs -> String
22bin2base64 = B8.unpack . Base64.encode . BA.convert
23
24instance Show Nonce32 where
25 showsPrec d nonce = mappend $ bin2base64 nonce
26
27instance Read Nonce32 where
28 readsPrec _ str = either (const []) id $ do
29 let (ds,ss) = Prelude.splitAt 43 str
30 ss' <- case ss of
31 '=':xs -> Right xs -- optional terminating '='
32 _ -> Right ss
33 bs <- Base64.decode (B8.pack $ ds ++ ['='])
34 if B.length bs == 32
35 then Right [ (Nonce32 bs, ss') ]
36 else Left "Truncated Nonce32 (expected 43 base64 digits)."
37
38instance Serialize Nonce32 where
39 get = Nonce32 <$> getBytes 32
40 put (Nonce32 bs) = putByteString bs
41
42instance Sized Nonce32 where size = ConstSize 32
43
44
45zeros32 :: Nonce32
46zeros32 = Nonce32 $ BA.replicate 32 0
47
48generateNonce32 :: MonadRandom m => m Nonce32
49generateNonce32 = Nonce32 <$> getRandomBytes 32
diff --git a/src/Crypto/XEd25519.hs b/src/Crypto/XEd25519.hs
new file mode 100644
index 00000000..372f31a8
--- /dev/null
+++ b/src/Crypto/XEd25519.hs
@@ -0,0 +1,185 @@
1module Crypto.XEd25519 where
2
3import Control.Arrow
4import Data.Bits
5import Data.ByteArray as BA
6import Data.Memory.PtrMethods (memCopy)
7import Crypto.Hash
8import Crypto.ECC.Edwards25519
9import Crypto.Error
10import qualified Crypto.PubKey.Ed25519 as Ed25519
11import Foreign.Marshal
12import Foreign.Ptr
13import Foreign.Storable
14import qualified Crypto.PubKey.Curve25519 as X25519
15
16import Crypto.XEd25519.FieldElement
17import Crypto.Nonce
18
19
20data SecretKey = SecretKey { secretScalar :: Scalar }
21
22data PublicKey = PublicKey Ed25519.PublicKey
23 deriving Eq
24
25type Nonce = Nonce32
26
27newtype EncodedPoint = EncodedPoint Point
28
29instance ByteArrayAccess SecretKey where
30 length _ = 32
31 withByteArray (SecretKey scalar) = withByteArray (scalarEncode scalar :: Bytes)
32
33instance ByteArrayAccess PublicKey where
34 length _ = 32
35 withByteArray (PublicKey edpub) = withByteArray edpub
36
37instance ByteArrayAccess EncodedPoint where
38 length _ = 32
39 withByteArray (EncodedPoint pt) f =
40 withByteArray (pointEncode pt :: Bytes) f
41
42
43data Signature = Signature EncodedPoint Scalar
44
45instance ByteArrayAccess Signature where
46 length _ = 64
47 withByteArray (Signature pt scalar) f =
48 withByteArray pt $ \ptptr -> do
49 withByteArray (SecretKey scalar) $ \scalarptr -> do
50 allocaBytes 64 $ \ptr -> do
51 memCopy ptr ptptr 32
52 memCopy (ptr `plusPtr` 32) scalarptr 32
53 f (castPtr ptr)
54
55
56padding :: Bytes
57padding = 0xFE `BA.cons` BA.replicate 31 0xFF
58
59sign :: ByteArrayAccess dta => dta -> Nonce -> SecretKey -> PublicKey -> Signature
60sign dta nonce sec pub = Signature rB s
61 where
62 rB = ge_p3_tobytes $ ge_scalarmult_base r
63
64 r = sc_reduce $ hashFinalize $ (`hashUpdate` padding)
65 >>> (`hashUpdate` sec)
66 >>> (`hashUpdate` dta)
67 >>> (`hashUpdate` nonce) $ hashInit
68
69 h = sc_reduce $ hashFinalize $ (`hashUpdate` rB)
70 >>> (`hashUpdate` pub)
71 >>> (`hashUpdate` dta) $ hashInit
72
73 -- s = r + ha (mod q)
74 s = sc_muladd h (secretScalar sec) r
75
76
77
78ge_p3_tobytes :: Point -> EncodedPoint
79ge_p3_tobytes = EncodedPoint
80
81ge_scalarmult_base :: Scalar -> Point
82ge_scalarmult_base = toPoint
83
84sc_muladd :: Scalar -> Scalar -> Scalar -> Scalar
85sc_muladd a b c = scalarAdd (scalarMul a b) c
86
87sc_reduce :: Digest SHA512 -> Scalar
88sc_reduce digest = x where CryptoPassed x = scalarDecodeLong digest -- ???
89
90-- Scalar is internally, at least on 64bit machines, represented as 5
91-- 56-bit words in little-endian order, each encoded as a Word64.
92sc_neg :: Scalar -> Scalar
93sc_neg = scalarMul sc_neg1
94
95verify :: ByteArrayAccess dta => PublicKey -> dta -> Signature -> Bool
96verify pub dta signature = Ed25519.verify ed_pub dta ed_sig
97 where
98 CryptoPassed ed_pub = Ed25519.publicKey pub'
99 CryptoPassed ed_sig = Ed25519.signature signature'
100
101 -- Get the sign bit from the s part of the signature.
102 sign_bit = BA.index signature 63 .&. 0x80
103
104 -- Set the sign bit to zero in the s part of the signature.
105 signature' :: Bytes
106 signature' = BA.copyAndFreeze signature $ \ptr -> do
107 let at63 = plusPtr ptr 63
108 byte63 <- peek at63
109 poke at63 $ byte63 .&. (0x7F `asTypeOf` sign_bit)
110
111 -- Restore the sign bit on the verification key, which should have 0 as its
112 -- current sign bit.
113 pub' :: Bytes
114 pub' = BA.copyAndFreeze pub $ \ptr -> do
115 let at31 = plusPtr ptr 31
116 byte31 <- peek at31
117 poke at31 $ (byte31 .&. 0x7F) .|. sign_bit
118
119
120-- typedef crypto_int32 fe[10];
121--
122-- fe means field element. Here the field is \Z/(2^255-19).
123-- An element t, entries t[0]...t[9], represents the integer
124-- t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
125-- Bounds on each t[i] vary depending on context.
126
127-- mont_pub_to_ed_pub
128toSigningKey :: X25519.PublicKey -> PublicKey
129toSigningKey mont_pub0 = PublicKey ed_pub
130 where
131 -- Read the public key as a field element
132 mont_pub = fe_frombytes mont_pub0
133
134 -- Convert the Montgomery public key to a twisted Edwards public key
135 fe_ONE = fe_1
136
137 -- Calculate the parameters (u - 1) and (u + 1)
138 mont_pub_minus_one = fe_sub mont_pub fe_ONE
139 mont_pub_plus_one0 = fe_add mont_pub fe_ONE
140
141 -- Prepare inv(u + 1)
142 mont_pub_plus_one = fe_invert mont_pub_plus_one0
143
144 -- Calculate y = (u - 1) * inv(u + 1) (mod p)
145 ed_pub0 = fe_mul mont_pub_minus_one mont_pub_plus_one
146 ed_pub = fe_tobytes ed_pub0
147
148-- mont_priv_to_ed_pair
149toSigningKeyPair :: X25519.SecretKey -> (SecretKey,PublicKey)
150toSigningKeyPair mont_priv0 = (SecretKey ed_priv, PublicKey ed_pub)
151 where
152 -- Prepare a buffer for the twisted Edwards private key
153 ed_priv1 = (throwCryptoError . scalarDecodeLong :: X25519.SecretKey -> Scalar) mont_priv0
154
155 -- Get the twisted edwards public key, including the sign bit
156 ed_pub0 = ge_p3_tobytes $ ge_scalarmult_base ed_priv1
157
158 -- Save the sign bit for later
159 sign_bit = (BA.index ed_pub0 31 `shiftR` 7) .&. 1
160
161 -- Force the sign bit to zero
162 pub' :: Bytes
163 pub' = BA.copyAndFreeze ed_pub0 $ \ptr -> do
164 let at31 = plusPtr ptr 31
165 byte31 <- peek at31
166 poke at31 $ (byte31 .&. 0x7F) `asTypeOf` sign_bit
167
168 CryptoPassed ed_pub = Ed25519.publicKey pub'
169
170
171 -- Prepare the negated private key
172 ed_priv_neg = sc_neg ed_priv1
173
174 -- Get the correct private key based on the sign stored above
175 ed_priv = if sign_bit/=0 then ed_priv_neg
176 else ed_priv1
177
178-- sc_zero = throwCryptoError $ scalarDecodeLong (b::Bytes)
179-- where
180-- b = BA.pack $ encodeLittleEndian $ 2^252 + 27742317777372353535851937790883648493
181
182sc_neg1 :: Scalar
183sc_neg1 = throwCryptoError $ scalarDecodeLong (b::Bytes)
184 where
185 b = BA.pack $ encodeLittleEndian $ 2^252 + 27742317777372353535851937790883648492
diff --git a/src/Crypto/XEd25519/FieldElement.hs b/src/Crypto/XEd25519/FieldElement.hs
new file mode 100644
index 00000000..7a916107
--- /dev/null
+++ b/src/Crypto/XEd25519/FieldElement.hs
@@ -0,0 +1,49 @@
1{-# LANGUAGE DataKinds #-}
2{-# LANGUAGE TypeOperators #-}
3module Crypto.XEd25519.FieldElement where
4
5import Crypto.Error
6import qualified Crypto.PubKey.Curve25519 as X25519
7import qualified Crypto.PubKey.Ed25519 as Ed25519
8import Data.ByteArray as BA (pack,unpack,Bytes)
9import Data.Modular
10import Data.Word
11
12-- 2^255 - 19
13type P25519 = 57896044618658097711785492504343953926634992332820282019728792003956564819949
14
15newtype FieldElement = FE (ℤ / P25519)
16
17
18fe_frombytes :: X25519.PublicKey -> FieldElement
19fe_frombytes pub = FE $ toMod $ decodeLittleEndian $ BA.unpack pub
20
21fe_tobytes :: FieldElement -> Ed25519.PublicKey
22fe_tobytes (FE x) = throwCryptoError $ Ed25519.publicKey (b :: Bytes)
23 where
24 b = BA.pack $ take 32 $ (encodeLittleEndian $ unMod x) ++ repeat 0
25
26fe_1 :: FieldElement
27fe_1 = FE $ toMod 1
28
29fe_sub :: FieldElement -> FieldElement -> FieldElement
30fe_sub (FE x) (FE y) = FE $ x - y
31
32fe_add :: FieldElement -> FieldElement -> FieldElement
33fe_add (FE x) (FE y) = FE $ x + y
34
35fe_invert :: FieldElement -> FieldElement
36fe_invert (FE x) = FE $ inv x
37
38fe_mul :: FieldElement -> FieldElement -> FieldElement
39fe_mul (FE x) (FE y) = FE (x * y)
40
41decodeLittleEndian :: [Word8] -> Integer
42decodeLittleEndian [] = 0
43decodeLittleEndian (x:xs) = fromIntegral x + 256 * decodeLittleEndian xs
44
45encodeLittleEndian :: Integer -> [Word8]
46encodeLittleEndian 0 = []
47encodeLittleEndian x = let (bs,b) = divMod x 256
48 in fromIntegral b : encodeLittleEndian bs
49
diff --git a/src/Data/Sized.hs b/src/Data/Sized.hs
new file mode 100644
index 00000000..0d3d5845
--- /dev/null
+++ b/src/Data/Sized.hs
@@ -0,0 +1,14 @@
1module Data.Sized where
2
3import Data.Typeable
4
5
6-- | Info about a type's serialized length. Either the length is known
7-- independently of the value, or the length depends on the value.
8data Size a
9 = VarSize (a -> Int)
10 | ConstSize !Int
11 deriving Typeable
12
13class Sized a where size :: Size a
14