summaryrefslogtreecommitdiff
path: root/M2Crypto/EC.py
blob: f27a5746c7c111e3ad2859042a67c9c33ad21c98 (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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
from __future__ import absolute_import

"""
M2Crypto wrapper for OpenSSL ECDH/ECDSA API.

@requires: OpenSSL 0.9.8 or newer

Copyright (c) 1999-2003 Ng Pheng Siong. All rights reserved.

Portions copyright (c) 2005-2006 Vrije Universiteit Amsterdam.
All rights reserved."""

from . import util, BIO, m2

class ECError(Exception): pass

m2.ec_init(ECError)

# Curve identifier constants
NID_secp112r1 = m2.NID_secp112r1
NID_secp112r2 = m2.NID_secp112r2
NID_secp128r1 = m2.NID_secp128r1
NID_secp128r2 = m2.NID_secp128r2
NID_secp160k1 = m2.NID_secp160k1
NID_secp160r1 = m2.NID_secp160r1
NID_secp160r2 = m2.NID_secp160r2
NID_secp192k1 = m2.NID_secp192k1
NID_secp224k1 = m2.NID_secp224k1
NID_secp224r1 = m2.NID_secp224r1
NID_secp256k1 = m2.NID_secp256k1
NID_secp384r1 = m2.NID_secp384r1
NID_secp521r1 = m2.NID_secp521r1
NID_sect113r1 = m2.NID_sect113r1
NID_sect113r2 = m2.NID_sect113r2
NID_sect131r1 = m2.NID_sect131r1
NID_sect131r2 = m2.NID_sect131r2
NID_sect163k1 = m2.NID_sect163k1
NID_sect163r1 = m2.NID_sect163r1
NID_sect163r2 = m2.NID_sect163r2
NID_sect193r1 = m2.NID_sect193r1
NID_sect193r2 = m2.NID_sect193r2
NID_sect233k1 = m2.NID_sect233k1  # default for secg.org TLS test server
NID_sect233r1 = m2.NID_sect233r1
NID_sect239k1 = m2.NID_sect239k1
NID_sect283k1 = m2.NID_sect283k1
NID_sect283r1 = m2.NID_sect283r1
NID_sect409k1 = m2.NID_sect409k1
NID_sect409r1 = m2.NID_sect409r1
NID_sect571k1 = m2.NID_sect571k1
NID_sect571r1 = m2.NID_sect571r1

NID_X9_62_prime192v1 = m2.NID_X9_62_prime192v1
NID_X9_62_prime192v2 = m2.NID_X9_62_prime192v2
NID_X9_62_prime192v3 = m2.NID_X9_62_prime192v3
NID_X9_62_prime239v1 = m2.NID_X9_62_prime239v1
NID_X9_62_prime239v2 = m2.NID_X9_62_prime239v2
NID_X9_62_prime239v3 = m2.NID_X9_62_prime239v3
NID_X9_62_prime256v1 = m2.NID_X9_62_prime256v1
NID_X9_62_c2pnb163v1 = m2.NID_X9_62_c2pnb163v1
NID_X9_62_c2pnb163v2 = m2.NID_X9_62_c2pnb163v2
NID_X9_62_c2pnb163v3 = m2.NID_X9_62_c2pnb163v3
NID_X9_62_c2pnb176v1 = m2.NID_X9_62_c2pnb176v1
NID_X9_62_c2tnb191v1 = m2.NID_X9_62_c2tnb191v1
NID_X9_62_c2tnb191v2 = m2.NID_X9_62_c2tnb191v2
NID_X9_62_c2tnb191v3 = m2.NID_X9_62_c2tnb191v3
NID_X9_62_c2pnb208w1 = m2.NID_X9_62_c2pnb208w1
NID_X9_62_c2tnb239v1 = m2.NID_X9_62_c2tnb239v1
NID_X9_62_c2tnb239v2 = m2.NID_X9_62_c2tnb239v2
NID_X9_62_c2tnb239v3 = m2.NID_X9_62_c2tnb239v3
NID_X9_62_c2pnb272w1 = m2.NID_X9_62_c2pnb272w1
NID_X9_62_c2pnb304w1 = m2.NID_X9_62_c2pnb304w1
NID_X9_62_c2tnb359v1 = m2.NID_X9_62_c2tnb359v1
NID_X9_62_c2pnb368w1 = m2.NID_X9_62_c2pnb368w1
NID_X9_62_c2tnb431r1 = m2.NID_X9_62_c2tnb431r1

NID_wap_wsg_idm_ecid_wtls1 = m2.NID_wap_wsg_idm_ecid_wtls1
NID_wap_wsg_idm_ecid_wtls3 = m2.NID_wap_wsg_idm_ecid_wtls3
NID_wap_wsg_idm_ecid_wtls4 = m2.NID_wap_wsg_idm_ecid_wtls4
NID_wap_wsg_idm_ecid_wtls5 = m2.NID_wap_wsg_idm_ecid_wtls5
NID_wap_wsg_idm_ecid_wtls6 = m2.NID_wap_wsg_idm_ecid_wtls6
NID_wap_wsg_idm_ecid_wtls7 = m2.NID_wap_wsg_idm_ecid_wtls7
NID_wap_wsg_idm_ecid_wtls8 = m2.NID_wap_wsg_idm_ecid_wtls8
NID_wap_wsg_idm_ecid_wtls9 = m2.NID_wap_wsg_idm_ecid_wtls9
NID_wap_wsg_idm_ecid_wtls10 = m2.NID_wap_wsg_idm_ecid_wtls10
NID_wap_wsg_idm_ecid_wtls11 = m2.NID_wap_wsg_idm_ecid_wtls11
NID_wap_wsg_idm_ecid_wtls12 = m2.NID_wap_wsg_idm_ecid_wtls12

# The following two curves, according to OpenSSL, have a
# "Questionable extension field!" and are not supported by
# the OpenSSL inverse function.  ECError: no inverse.
# As such they cannot be used for signing.  They might,
# however, be usable for encryption but that has not
# been tested.  Until thir usefulness can be established,
# they are not supported at this time.
# NID_ipsec3 = m2.NID_ipsec3
# NID_ipsec4 = m2.NID_ipsec4


class EC:

    """
    Object interface to a EC key pair.
    """

    m2_ec_key_free = m2.ec_key_free

    def __init__(self, ec, _pyfree=0):
        assert m2.ec_key_type_check(ec), "'ec' type error"
        self.ec = ec
        self._pyfree = _pyfree

    def __del__(self):
        if getattr(self, '_pyfree', 0):
            self.m2_ec_key_free(self.ec)

    def __len__(self):
        assert m2.ec_key_type_check(self.ec), "'ec' type error"
        return m2.ec_key_keylen(self.ec)

    def gen_key(self):
        """
        Generates the key pair from its parameters. Use::
            keypair = EC.gen_params(curve)
            keypair.gen_key()
        to create an EC key pair.
        """
        assert m2.ec_key_type_check(self.ec), "'ec' type error"
        m2.ec_key_gen_key(self.ec)

    def pub(self):
        # Don't let python free
        return EC_pub(self.ec, 0)

    def sign_dsa(self, digest):
        """
        Sign the given digest using ECDSA. Returns a tuple (r,s), the two
        ECDSA signature parameters.
        """
        assert self._check_key_type(), "'ec' type error"
        return m2.ecdsa_sign(self.ec, digest)

    def verify_dsa(self, digest, r, s):
        """
        Verify the given digest using ECDSA. r and s are the ECDSA
        signature parameters.
        """
        assert self._check_key_type(), "'ec' type error"
        return m2.ecdsa_verify(self.ec, digest, r, s)

    def sign_dsa_asn1(self, digest):
        assert self._check_key_type(), "'ec' type error"
        return m2.ecdsa_sign_asn1(self.ec, digest)

    def verify_dsa_asn1(self, digest, blob):
        assert self._check_key_type(), "'ec' type error"
        return m2.ecdsa_verify_asn1(self.ec, digest, blob)

    def compute_dh_key(self, pub_key):
        """
        Compute the ECDH shared key of this key pair and the given public
        key object. They must both use the same curve. Returns the
        shared key in binary as a buffer object. No Key Derivation Function is
        applied.
        """
        assert self.check_key(), 'key is not initialised'
        return m2.ecdh_compute_key(self.ec, pub_key.ec)

    def save_key_bio(self, bio, cipher='aes_128_cbc', callback=util.passphrase_callback):
        """
        Save the key pair to an M2Crypto.BIO.BIO object in PEM format.

        @type bio: M2Crypto.BIO.BIO
        @param bio: M2Crypto.BIO.BIO object to save key to.

        @type cipher: string
        @param cipher: Symmetric cipher to protect the key. The default
        cipher is 'aes_128_cbc'. If cipher is None, then the key is saved
        in the clear.

        @type callback: Python callable
        @param callback: A Python callable object that is invoked
        to acquire a passphrase with which to protect the key.
        The default is util.passphrase_callback.
        """
        if cipher is None:
            return m2.ec_key_write_bio_no_cipher(self.ec, bio._ptr(), callback)
        else:
            ciph = getattr(m2, cipher, None)
            if ciph is None:
                raise ValueError('not such cipher %s' % cipher)
            return m2.ec_key_write_bio(self.ec, bio._ptr(), ciph(), callback)

    def save_key(self, file, cipher='aes_128_cbc', callback=util.passphrase_callback):
        """
        Save the key pair to a file in PEM format.

        @type file: string
        @param file: Name of file to save key to.

        @type cipher: string
        @param cipher: Symmetric cipher to protect the key. The default
        cipher is 'aes_128_cbc'. If cipher is None, then the key is saved
        in the clear.

        @type callback: Python callable
        @param callback: A Python callable object that is invoked
        to acquire a passphrase with which to protect the key.
        The default is util.passphrase_callback.
        """
        bio = BIO.openfile(file, 'wb')
        return self.save_key_bio(bio, cipher, callback)

    def save_pub_key_bio(self, bio):
        """
        Save the public key to an M2Crypto.BIO.BIO object in PEM format.

        @type bio: M2Crypto.BIO.BIO
        @param bio: M2Crypto.BIO.BIO object to save key to.
        """
        return m2.ec_key_write_pubkey(self.ec, bio._ptr())

    def save_pub_key(self, file):
        """
        Save the public key to a file in PEM format.

        @type file: string
        @param file: Name of file to save key to.
        """
        bio = BIO.openfile(file, 'wb')
        return m2.ec_key_write_pubkey(self.ec, bio._ptr())

    def _check_key_type(self):
        return m2.ec_key_type_check(self.ec)

    def check_key(self):
        assert m2.ec_key_type_check(self.ec), "'ec' type error"
        return m2.ec_key_check_key(self.ec)


class EC_pub(EC):

    """
    Object interface to an EC public key.
    ((don't like this implementation inheritance))
    """
    def __init__(self, ec, _pyfree=0):
        EC.__init__(self, ec, _pyfree)
        self.der = None

    def get_der(self):
        """
        Returns the public key in DER format as a buffer object.
        """
        assert self.check_key(), 'key is not initialised'
        if self.der is None:
            self.der = m2.ec_key_get_public_der(self.ec)
        return self.der

    def get_key(self):
        """
        Returns the public key as a byte string.
        """
        assert self.check_key(), 'key is not initialised'
        return m2.ec_key_get_public_key(self.ec)

    save_key = EC.save_pub_key

    save_key_bio = EC.save_pub_key_bio


def gen_params(curve):
    """
    Factory function that generates EC parameters and
    instantiates a EC object from the output.

    @param curve: This is the OpenSSL nid of the curve to use.
    """
    return EC(m2.ec_key_new_by_curve_name(curve), 1)


def load_key(file, callback=util.passphrase_callback):
    """
    Factory function that instantiates a EC object.

    @param file: Names the file that contains the PEM representation
    of the EC key pair.

    @param callback: Python callback object that will be invoked
    if the EC key pair is passphrase-protected.
    """
    bio = BIO.openfile(file)
    return load_key_bio(bio, callback)


def load_key_bio(bio, callback=util.passphrase_callback):
    """
    Factory function that instantiates a EC object.

    @param bio: M2Crypto.BIO object that contains the PEM
    representation of the EC key pair.

    @param callback: Python callback object that will be invoked
    if the EC key pair is passphrase-protected.
    """
    return EC(m2.ec_key_read_bio(bio._ptr(), callback), 1)

def load_pub_key(file):
    """
    Load an EC public key from file.

    @type file: string
    @param file: Name of file containing EC public key in PEM format.

    @rtype: M2Crypto.EC.EC_pub
    @return: M2Crypto.EC.EC_pub object.
    """
    bio = BIO.openfile(file)
    return load_pub_key_bio(bio)


def load_pub_key_bio(bio):
    """
    Load an EC public key from an M2Crypto.BIO.BIO object.

    @type bio: M2Crypto.BIO.BIO
    @param bio: M2Crypto.BIO.BIO object containing EC public key in PEM
    format.

    @rtype: M2Crypto.EC.EC_pub
    @return: M2Crypto.EC.EC_pub object.
    """
    ec = m2.ec_key_read_pubkey(bio._ptr())
    if ec is None:
        ec_error()
    return EC_pub(ec, 1)

def ec_error():
    raise ECError(m2.err_reason_error_string(m2.err_get_error()))

def pub_key_from_der(der):
    """
    Create EC_pub from DER.
    """
    return EC_pub(m2.ec_key_from_pubkey_der(der), 1)

def pub_key_from_params(curve, bytes):
    """
    Create EC_pub from curve name and octet string.
    """
    return EC_pub(m2.ec_key_from_pubkey_params(curve, bytes), 1)