diff options
Diffstat (limited to 'src/Crypto')
-rw-r--r-- | src/Crypto/Nonce.hs | 49 | ||||
-rw-r--r-- | src/Crypto/XEd25519.hs | 185 | ||||
-rw-r--r-- | src/Crypto/XEd25519/FieldElement.hs | 49 |
3 files changed, 283 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 #-} | ||
3 | module Crypto.Nonce | ||
4 | ( Nonce32 | ||
5 | , generateNonce32 | ||
6 | , zeros32 | ||
7 | ) where | ||
8 | |||
9 | import Crypto.Random | ||
10 | import Data.ByteArray as BA | ||
11 | import Data.ByteString as B | ||
12 | import qualified Data.ByteString.Base64 as Base64 | ||
13 | import Data.ByteString.Char8 as B8 | ||
14 | import Data.Data | ||
15 | import Data.Serialize | ||
16 | import Data.Sized | ||
17 | |||
18 | newtype Nonce32 = Nonce32 ByteString | ||
19 | deriving (Eq, Ord, ByteArrayAccess, Data) | ||
20 | |||
21 | bin2base64 :: ByteArrayAccess bs => bs -> String | ||
22 | bin2base64 = B8.unpack . Base64.encode . BA.convert | ||
23 | |||
24 | instance Show Nonce32 where | ||
25 | showsPrec d nonce = mappend $ bin2base64 nonce | ||
26 | |||
27 | instance 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 | |||
38 | instance Serialize Nonce32 where | ||
39 | get = Nonce32 <$> getBytes 32 | ||
40 | put (Nonce32 bs) = putByteString bs | ||
41 | |||
42 | instance Sized Nonce32 where size = ConstSize 32 | ||
43 | |||
44 | |||
45 | zeros32 :: Nonce32 | ||
46 | zeros32 = Nonce32 $ BA.replicate 32 0 | ||
47 | |||
48 | generateNonce32 :: MonadRandom m => m Nonce32 | ||
49 | generateNonce32 = 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 @@ | |||
1 | module Crypto.XEd25519 where | ||
2 | |||
3 | import Control.Arrow | ||
4 | import Data.Bits | ||
5 | import Data.ByteArray as BA | ||
6 | import Data.Memory.PtrMethods (memCopy) | ||
7 | import Crypto.Hash | ||
8 | import Crypto.ECC.Edwards25519 | ||
9 | import Crypto.Error | ||
10 | import qualified Crypto.PubKey.Ed25519 as Ed25519 | ||
11 | import Foreign.Marshal | ||
12 | import Foreign.Ptr | ||
13 | import Foreign.Storable | ||
14 | import qualified Crypto.PubKey.Curve25519 as X25519 | ||
15 | |||
16 | import Crypto.XEd25519.FieldElement | ||
17 | import Crypto.Nonce | ||
18 | |||
19 | |||
20 | data SecretKey = SecretKey { secretScalar :: Scalar } | ||
21 | |||
22 | data PublicKey = PublicKey Ed25519.PublicKey | ||
23 | deriving Eq | ||
24 | |||
25 | type Nonce = Nonce32 | ||
26 | |||
27 | newtype EncodedPoint = EncodedPoint Point | ||
28 | |||
29 | instance ByteArrayAccess SecretKey where | ||
30 | length _ = 32 | ||
31 | withByteArray (SecretKey scalar) = withByteArray (scalarEncode scalar :: Bytes) | ||
32 | |||
33 | instance ByteArrayAccess PublicKey where | ||
34 | length _ = 32 | ||
35 | withByteArray (PublicKey edpub) = withByteArray edpub | ||
36 | |||
37 | instance ByteArrayAccess EncodedPoint where | ||
38 | length _ = 32 | ||
39 | withByteArray (EncodedPoint pt) f = | ||
40 | withByteArray (pointEncode pt :: Bytes) f | ||
41 | |||
42 | |||
43 | data Signature = Signature EncodedPoint Scalar | ||
44 | |||
45 | instance 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 | |||
56 | padding :: Bytes | ||
57 | padding = 0xFE `BA.cons` BA.replicate 31 0xFF | ||
58 | |||
59 | sign :: ByteArrayAccess dta => dta -> Nonce -> SecretKey -> PublicKey -> Signature | ||
60 | sign 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 | |||
78 | ge_p3_tobytes :: Point -> EncodedPoint | ||
79 | ge_p3_tobytes = EncodedPoint | ||
80 | |||
81 | ge_scalarmult_base :: Scalar -> Point | ||
82 | ge_scalarmult_base = toPoint | ||
83 | |||
84 | sc_muladd :: Scalar -> Scalar -> Scalar -> Scalar | ||
85 | sc_muladd a b c = scalarAdd (scalarMul a b) c | ||
86 | |||
87 | sc_reduce :: Digest SHA512 -> Scalar | ||
88 | sc_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. | ||
92 | sc_neg :: Scalar -> Scalar | ||
93 | sc_neg = scalarMul sc_neg1 | ||
94 | |||
95 | verify :: ByteArrayAccess dta => PublicKey -> dta -> Signature -> Bool | ||
96 | verify 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 | ||
128 | toSigningKey :: X25519.PublicKey -> PublicKey | ||
129 | toSigningKey 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 | ||
149 | toSigningKeyPair :: X25519.SecretKey -> (SecretKey,PublicKey) | ||
150 | toSigningKeyPair 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 | |||
182 | sc_neg1 :: Scalar | ||
183 | sc_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 #-} | ||
3 | module Crypto.XEd25519.FieldElement where | ||
4 | |||
5 | import Crypto.Error | ||
6 | import qualified Crypto.PubKey.Curve25519 as X25519 | ||
7 | import qualified Crypto.PubKey.Ed25519 as Ed25519 | ||
8 | import Data.ByteArray as BA (pack,unpack,Bytes) | ||
9 | import Data.Modular | ||
10 | import Data.Word | ||
11 | |||
12 | -- 2^255 - 19 | ||
13 | type P25519 = 57896044618658097711785492504343953926634992332820282019728792003956564819949 | ||
14 | |||
15 | newtype FieldElement = FE (ℤ / P25519) | ||
16 | |||
17 | |||
18 | fe_frombytes :: X25519.PublicKey -> FieldElement | ||
19 | fe_frombytes pub = FE $ toMod $ decodeLittleEndian $ BA.unpack pub | ||
20 | |||
21 | fe_tobytes :: FieldElement -> Ed25519.PublicKey | ||
22 | fe_tobytes (FE x) = throwCryptoError $ Ed25519.publicKey (b :: Bytes) | ||
23 | where | ||
24 | b = BA.pack $ take 32 $ (encodeLittleEndian $ unMod x) ++ repeat 0 | ||
25 | |||
26 | fe_1 :: FieldElement | ||
27 | fe_1 = FE $ toMod 1 | ||
28 | |||
29 | fe_sub :: FieldElement -> FieldElement -> FieldElement | ||
30 | fe_sub (FE x) (FE y) = FE $ x - y | ||
31 | |||
32 | fe_add :: FieldElement -> FieldElement -> FieldElement | ||
33 | fe_add (FE x) (FE y) = FE $ x + y | ||
34 | |||
35 | fe_invert :: FieldElement -> FieldElement | ||
36 | fe_invert (FE x) = FE $ inv x | ||
37 | |||
38 | fe_mul :: FieldElement -> FieldElement -> FieldElement | ||
39 | fe_mul (FE x) (FE y) = FE (x * y) | ||
40 | |||
41 | decodeLittleEndian :: [Word8] -> Integer | ||
42 | decodeLittleEndian [] = 0 | ||
43 | decodeLittleEndian (x:xs) = fromIntegral x + 256 * decodeLittleEndian xs | ||
44 | |||
45 | encodeLittleEndian :: Integer -> [Word8] | ||
46 | encodeLittleEndian 0 = [] | ||
47 | encodeLittleEndian x = let (bs,b) = divMod x 256 | ||
48 | in fromIntegral b : encodeLittleEndian bs | ||
49 | |||