summaryrefslogtreecommitdiff
path: root/Crypto/XEd25519.hs
blob: 10883479a830b1be820e9173d51d70a16b4c64cd (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
module Crypto.XEd25519 where

import Control.Arrow
import Data.Bits
import Data.ByteArray as BA
import Data.Memory.PtrMethods (memCopy)
import Crypto.Hash
import Crypto.ECC.Edwards25519
import Crypto.Error
import qualified Crypto.PubKey.Ed25519 as Ed25519
import Foreign.Marshal
import Foreign.Ptr
import Foreign.Storable
import qualified Crypto.PubKey.Curve25519 as X25519

import Crypto.XEd25519.FieldElement
-- import Crypto.Nonce


data SecretKey = SecretKey { secretScalar :: Scalar }

data PublicKey = PublicKey Ed25519.PublicKey
 deriving Eq

-- type Nonce = Nonce32
type Nonce = Bytes -- 32 bytes

newtype EncodedPoint = EncodedPoint Point

instance ByteArrayAccess SecretKey where
    length _ = 32
    withByteArray (SecretKey scalar) = withByteArray (scalarEncode scalar :: Bytes)

instance ByteArrayAccess PublicKey where
    length _ = 32
    withByteArray (PublicKey edpub) = withByteArray edpub

instance ByteArrayAccess EncodedPoint where
    length _ = 32
    withByteArray (EncodedPoint pt) f =
        withByteArray (pointEncode pt :: Bytes) f


data Signature = Signature EncodedPoint Scalar

instance ByteArrayAccess Signature where
    length _ = 64
    withByteArray (Signature pt scalar) f =
        withByteArray pt                 $ \ptptr -> do
        withByteArray (SecretKey scalar) $ \scalarptr -> do
        allocaBytes 64                   $ \ptr -> do
            memCopy ptr ptptr 32
            memCopy (ptr `plusPtr` 32) scalarptr 32
            f (castPtr ptr)

signatureDecode :: ByteArrayAccess ba => ba -> Maybe Signature
signatureDecode bs = do
    let (ptbs,scbs) = BA.splitAt 32 ( BA.convert bs :: Bytes)
    pt <- maybeCryptoError $ pointDecode ptbs
    sc <- maybeCryptoError $ scalarDecodeLong scbs
    return $ Signature (ge_p3_tobytes pt) sc


padding :: Bytes
padding = 0xFE `BA.cons` BA.replicate 31 0xFF

sign :: ByteArrayAccess dta => dta -> Nonce -> SecretKey -> PublicKey -> Signature
sign dta nonce sec pub = Signature rB s
 where
    rB = ge_p3_tobytes $ ge_scalarmult_base r

    r = sc_reduce $ hashFinalize $ (`hashUpdate` padding)
                                    >>> (`hashUpdate` sec)
                                    >>> (`hashUpdate` dta)
                                    >>> (`hashUpdate` nonce) $ hashInit

    h = sc_reduce $ hashFinalize $ (`hashUpdate` rB)
                                    >>> (`hashUpdate` pub)
                                    >>> (`hashUpdate` dta) $ hashInit

    -- s = r + ha (mod q)
    s = sc_muladd h (secretScalar sec) r



ge_p3_tobytes :: Point -> EncodedPoint
ge_p3_tobytes = EncodedPoint

ge_scalarmult_base :: Scalar -> Point
ge_scalarmult_base = toPoint

sc_muladd :: Scalar -> Scalar -> Scalar -> Scalar
sc_muladd a b c = scalarAdd (scalarMul a b) c

sc_reduce :: Digest SHA512 -> Scalar
sc_reduce digest = x where CryptoPassed x = scalarDecodeLong digest -- ???

-- Scalar is internally, at least on 64bit machines, represented as 5
-- 56-bit words in little-endian order, each encoded as a Word64.
sc_neg :: Scalar -> Scalar
sc_neg = scalarMul sc_neg1

verify :: ByteArrayAccess dta => PublicKey -> dta -> Signature -> Bool
verify pub dta signature = Ed25519.verify ed_pub dta ed_sig
 where
    CryptoPassed ed_pub = Ed25519.publicKey pub'
    CryptoPassed ed_sig = Ed25519.signature signature'

    -- Get the sign bit from the s part of the signature.
    sign_bit = BA.index signature 63 .&. 0x80

    -- Set the sign bit to zero in the s part of the signature.
    signature' :: Bytes
    signature' = BA.copyAndFreeze signature $ \ptr -> do
        let at63 = plusPtr ptr 63
        byte63 <- peek at63
        poke at63 $ byte63 .&. (0x7F `asTypeOf` sign_bit)

    -- Restore the sign bit on the verification key, which should have 0 as its
    -- current sign bit.
    pub' :: Bytes
    pub' = BA.copyAndFreeze pub $ \ptr -> do
        let at31 = plusPtr ptr 31
        byte31 <- peek at31
        poke at31 $ (byte31 .&. 0x7F) .|. sign_bit


-- typedef crypto_int32 fe[10];
--
-- fe means field element.  Here the field is \Z/(2^255-19).
-- An element t, entries t[0]...t[9], represents the integer
-- t[0]+2^26 t[1]+2^51 t[2]+2^77 t[3]+2^102 t[4]+...+2^230 t[9].
-- Bounds on each t[i] vary depending on context.

-- mont_pub_to_ed_pub
toSigningKey :: X25519.PublicKey -> PublicKey
toSigningKey mont_pub0 = PublicKey ed_pub
 where
    -- Read the public key as a field element
    mont_pub = fe_frombytes mont_pub0

    -- Convert the Montgomery public key to a twisted Edwards public key
    fe_ONE = fe_1

    -- Calculate the parameters (u - 1) and (u + 1)
    mont_pub_minus_one = fe_sub mont_pub fe_ONE
    mont_pub_plus_one0 = fe_add mont_pub fe_ONE

    -- Prepare inv(u + 1)
    mont_pub_plus_one = fe_invert mont_pub_plus_one0

    -- Calculate y = (u - 1) * inv(u + 1) (mod p)
    ed_pub0 = fe_mul mont_pub_minus_one mont_pub_plus_one
    ed_pub = fe_tobytes ed_pub0

-- mont_priv_to_ed_pair
toSigningKeyPair :: X25519.SecretKey -> (SecretKey,PublicKey)
toSigningKeyPair mont_priv0 = (SecretKey ed_priv, PublicKey ed_pub)
 where
    -- Prepare a buffer for the twisted Edwards private key
    ed_priv1 = (throwCryptoError . scalarDecodeLong :: X25519.SecretKey -> Scalar) mont_priv0

    -- Get the twisted edwards public key, including the sign bit
    ed_pub0 = ge_p3_tobytes $ ge_scalarmult_base ed_priv1

    -- Save the sign bit for later
    sign_bit = (BA.index ed_pub0 31 `shiftR` 7) .&. 1

    -- Force the sign bit to zero
    pub' :: Bytes
    pub' = BA.copyAndFreeze ed_pub0 $ \ptr -> do
        let at31 = plusPtr ptr 31
        byte31 <- peek at31
        poke at31 $ (byte31 .&. 0x7F) `asTypeOf` sign_bit

    CryptoPassed ed_pub = Ed25519.publicKey pub'


    -- Prepare the negated private key
    ed_priv_neg = sc_neg ed_priv1

    -- Get the correct private key based on the sign stored above
    ed_priv = if sign_bit/=0 then ed_priv_neg
                             else ed_priv1

-- sc_zero = throwCryptoError $ scalarDecodeLong (b::Bytes)
--  where
--     b = BA.pack $ encodeLittleEndian $ 2^252 + 27742317777372353535851937790883648493

sc_neg1 :: Scalar
sc_neg1 = throwCryptoError $ scalarDecodeLong (b::Bytes)
 where
    b = BA.pack $ encodeLittleEndian $ 2^252 + 27742317777372353535851937790883648492