summaryrefslogtreecommitdiff
path: root/src/M2Crypto/EC.py
blob: 522092a3cb55a121a36d26b0a56052f2e51aa9e0 (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
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
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 M2Crypto import BIO, Err, EVP, m2, util
from typing import AnyStr, Callable, Dict, Optional, Tuple, Union  # noqa
from M2Crypto.EVP import PKey

EC_Key = bytes


class ECError(Exception):
    pass


m2.ec_init(ECError)

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

NID_prime192v1 = m2.NID_X9_62_prime192v1  # type: int
NID_prime192v2 = m2.NID_X9_62_prime192v2  # type: int
NID_prime192v3 = m2.NID_X9_62_prime192v3  # type: int
NID_prime239v1 = m2.NID_X9_62_prime239v1  # type: int
NID_prime239v2 = m2.NID_X9_62_prime239v2  # type: int
NID_prime239v3 = m2.NID_X9_62_prime239v3  # type: int
NID_prime256v1 = m2.NID_X9_62_prime256v1  # type: int
NID_c2pnb163v1 = m2.NID_X9_62_c2pnb163v1  # type: int
NID_c2pnb163v2 = m2.NID_X9_62_c2pnb163v2  # type: int
NID_c2pnb163v3 = m2.NID_X9_62_c2pnb163v3  # type: int
NID_c2pnb176v1 = m2.NID_X9_62_c2pnb176v1  # type: int
NID_c2tnb191v1 = m2.NID_X9_62_c2tnb191v1  # type: int
NID_c2tnb191v2 = m2.NID_X9_62_c2tnb191v2  # type: int
NID_c2tnb191v3 = m2.NID_X9_62_c2tnb191v3  # type: int
NID_c2pnb208w1 = m2.NID_X9_62_c2pnb208w1  # type: int
NID_c2tnb239v1 = m2.NID_X9_62_c2tnb239v1  # type: int
NID_c2tnb239v2 = m2.NID_X9_62_c2tnb239v2  # type: int
NID_c2tnb239v3 = m2.NID_X9_62_c2tnb239v3  # type: int
NID_c2pnb272w1 = m2.NID_X9_62_c2pnb272w1  # type: int
NID_c2pnb304w1 = m2.NID_X9_62_c2pnb304w1  # type: int
NID_c2tnb359v1 = m2.NID_X9_62_c2tnb359v1  # type: int
NID_c2pnb368w1 = m2.NID_X9_62_c2pnb368w1  # type: int
NID_c2tnb431r1 = m2.NID_X9_62_c2tnb431r1  # type: int

# To preserve compatibility with older names
NID_X9_62_prime192v1 = NID_prime192v1  # type: int
NID_X9_62_prime192v2 = NID_prime192v2  # type: int
NID_X9_62_prime192v3 = NID_prime192v3  # type: int
NID_X9_62_prime239v1 = NID_prime239v1  # type: int
NID_X9_62_prime239v2 = NID_prime239v2  # type: int
NID_X9_62_prime239v3 = NID_prime239v3  # type: int
NID_X9_62_prime256v1 = NID_prime256v1  # type: int
NID_X9_62_c2pnb163v1 = NID_c2pnb163v1  # type: int
NID_X9_62_c2pnb163v2 = NID_c2pnb163v2  # type: int
NID_X9_62_c2pnb163v3 = NID_c2pnb163v3  # type: int
NID_X9_62_c2pnb176v1 = NID_c2pnb176v1  # type: int
NID_X9_62_c2tnb191v1 = NID_c2tnb191v1  # type: int
NID_X9_62_c2tnb191v2 = NID_c2tnb191v2  # type: int
NID_X9_62_c2tnb191v3 = NID_c2tnb191v3  # type: int
NID_X9_62_c2pnb208w1 = NID_c2pnb208w1  # type: int
NID_X9_62_c2tnb239v1 = NID_c2tnb239v1  # type: int
NID_X9_62_c2tnb239v2 = NID_c2tnb239v2  # type: int
NID_X9_62_c2tnb239v3 = NID_c2tnb239v3  # type: int
NID_X9_62_c2pnb272w1 = NID_c2pnb272w1  # type: int
NID_X9_62_c2pnb304w1 = NID_c2pnb304w1  # type: int
NID_X9_62_c2tnb359v1 = NID_c2tnb359v1  # type: int
NID_X9_62_c2pnb368w1 = NID_c2pnb368w1  # type: int
NID_X9_62_c2tnb431r1 = NID_c2tnb431r1  # type: int

NID_wap_wsg_idm_ecid_wtls1 = m2.NID_wap_wsg_idm_ecid_wtls1  # type: int
NID_wap_wsg_idm_ecid_wtls3 = m2.NID_wap_wsg_idm_ecid_wtls3  # type: int
NID_wap_wsg_idm_ecid_wtls4 = m2.NID_wap_wsg_idm_ecid_wtls4  # type: int
NID_wap_wsg_idm_ecid_wtls5 = m2.NID_wap_wsg_idm_ecid_wtls5  # type: int
NID_wap_wsg_idm_ecid_wtls6 = m2.NID_wap_wsg_idm_ecid_wtls6  # type: int
NID_wap_wsg_idm_ecid_wtls7 = m2.NID_wap_wsg_idm_ecid_wtls7  # type: int
NID_wap_wsg_idm_ecid_wtls8 = m2.NID_wap_wsg_idm_ecid_wtls8  # type: int
NID_wap_wsg_idm_ecid_wtls9 = m2.NID_wap_wsg_idm_ecid_wtls9  # type: int
NID_wap_wsg_idm_ecid_wtls10 = m2.NID_wap_wsg_idm_ecid_wtls10  # type: int
NID_wap_wsg_idm_ecid_wtls11 = m2.NID_wap_wsg_idm_ecid_wtls11  # type: int
NID_wap_wsg_idm_ecid_wtls12 = m2.NID_wap_wsg_idm_ecid_wtls12  # type: int

# 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):

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

    m2_ec_key_free = m2.ec_key_free

    def __init__(self, ec, _pyfree=0):
        # type: (EC, int) -> None
        assert m2.ec_key_type_check(ec), "'ec' type error"
        self.ec = ec
        self._pyfree = _pyfree

    def __del__(self):
        # type: () -> None
        if getattr(self, '_pyfree', 0):
            self.m2_ec_key_free(self.ec)

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

    def gen_key(self):
        # type: () -> int
        """
        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):
        # type: () -> EC_pub
        # Don't let python free
        return EC_pub(self.ec, 0)

    def sign_dsa(self, digest):
        # type: (bytes) -> Tuple[bytes, bytes]
        """
        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):
        # type: (bytes, bytes, bytes) -> int
        """
        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):
        # type: (bytes) -> bytes
        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):
        # type: (EC) -> Optional[bytes]
        """
        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):
        # type: (BIO.BIO, Optional[str], Callable) -> int
        """
        Save the key pair to an M2Crypto.BIO.BIO object in PEM format.

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

        :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.

        :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):
        # type: (AnyStr, Optional[str], Callable) -> int
        """
        Save the key pair to a file in PEM format.

        :param file: Name of filename to save key to.

        :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.

        :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.
        """
        with BIO.openfile(file, 'wb') as bio:
            return self.save_key_bio(bio, cipher, callback)

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

        :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):
        # type: (AnyStr) -> int
        """
        Save the public key to a filename in PEM format.

        :param file: Name of filename to save key to.
        """
        with BIO.openfile(file, 'wb') as bio:
            return m2.ec_key_write_pubkey(self.ec, bio._ptr())

    def as_pem(self, cipher='aes_128_cbc', callback=util.passphrase_callback):
        """
        Returns the key(pair) as a string in PEM format.
        If no password is passed and the cipher is set
        it exits with error
        """
        with BIO.MemoryBuffer() as bio:
            self.save_key_bio(bio, cipher, callback)
            return bio.read()

    def _check_key_type(self):
        # type: () -> int
        return m2.ec_key_type_check(self.ec)

    def check_key(self):
        # type: () -> int
        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):
        # type: (EC, int) -> None
        EC.__init__(self, ec, _pyfree)
        self.der = None  # type: Optional[bytes]

    def get_der(self):
        # type: () -> bytes
        """
        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):
        # type: () -> bytes
        """
        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):
    # type: (int) -> EC
    """
    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.
    """
    assert curve in [x['NID'] for x in m2.ec_get_builtin_curves()], \
        'Elliptic curve %s is not available on this system.' % \
        m2.obj_nid2sn(curve)
    return EC(m2.ec_key_new_by_curve_name(curve), 1)


def load_key(file, callback=util.passphrase_callback):
    # type: (AnyStr, Callable) -> EC
    """
    Factory function that instantiates a EC object.

    :param file: Names the filename 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.
    """
    with BIO.openfile(file) as bio:
        return load_key_bio(bio, callback)


def load_key_string(string, callback=util.passphrase_callback):
    # type: (str, Callable) -> EC
    """
    Load an EC key pair from a string.

    :param string: String containing EC key pair in PEM format.

    :param callback: A Python callable object that is invoked
                     to acquire a passphrase with which to unlock the
                     key. The default is util.passphrase_callback.

    :return: M2Crypto.EC.EC object.
    """
    with BIO.MemoryBuffer(string) as bio:
        return load_key_bio(bio, callback)


def load_key_bio(bio, callback=util.passphrase_callback):
    # type: (BIO.BIO, Callable) -> EC
    """
    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):
    # type: (AnyStr) -> EC_pub
    """
    Load an EC public key from filename.

    :param file: Name of filename containing EC public key in PEM
                 format.

    :return: M2Crypto.EC.EC_pub object.
    """
    with BIO.openfile(file) as bio:
        return load_pub_key_bio(bio)


def load_key_string_pubkey(string, callback=util.passphrase_callback):
    # type: (str, Callable) -> PKey
    """
    Load an M2Crypto.EC.PKey from a public key as a string.

    :param string: String containing the key in PEM format.

    :param callback: A Python callable object that is invoked
                     to acquire a passphrase with which to protect the
                     key.

    :return: M2Crypto.EC.PKey object.
    """
    with BIO.MemoryBuffer(string) as bio:
        return EVP.load_key_bio_pubkey(bio, callback)


def load_pub_key_bio(bio):
    # type: (BIO.BIO) -> EC_pub
    """
    Load an EC public key from an M2Crypto.BIO.BIO object.

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

    :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():
    # type: () -> ECError
    raise ECError(Err.get_error_message())


def pub_key_from_der(der):
    # type: (bytes) -> EC_pub
    """
    Create EC_pub from DER.
    """
    return EC_pub(m2.ec_key_from_pubkey_der(der), 1)


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


def get_builtin_curves():
    # type: () -> Tuple[Dict[str, Union[int, str]]]
    return m2.ec_get_builtin_curves()