• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Synss / python-mbedtls / 15400568096

02 Jun 2025 07:03PM UTC coverage: 85.758% (-1.7%) from 87.47%
15400568096

Pull #127

github

pre-commit-ci[bot]
[pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci
Pull Request #127: [pre-commit.ci] pre-commit autoupdate

2523 of 2942 relevant lines covered (85.76%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.29
/src/mbedtls/mpi.pyx
1
# SPDX-License-Identifier: MIT
1✔
2
# Copyright (c) 2018, Mathias Laurin
3

4
# cython: c_api_binop_methods=True
5

6
"""Multi-precision integer library (MPI)."""
7

8

9
from libc.stdlib cimport free, malloc
10

11
cimport mbedtls._random as _rnd
12
cimport mbedtls.mpi as _mpi
13

14
import math
1✔
15
import numbers
1✔
16
from binascii import hexlify, unhexlify
1✔
17

18
import mbedtls._platform as _plt
1✔
19
import mbedtls._random as _rnd
1✔
20
import mbedtls.exceptions as _exc
1✔
21

22

23
cdef _rnd.Random __rng = _rnd.default_rng()
1✔
24

25

26
cdef to_bytes(value):
1✔
27
    xx = "{:02x}".format(value)
1✔
28
    return unhexlify((xx if not len(xx) % 2 else "0" + xx).encode("ascii"))
1✔
29

30

31
cdef from_bytes(value):
1✔
32
    return int(hexlify(value), 16)
1✔
33

34

35
cdef from_mpi_p(_mpi.mbedtls_mpi *mpi_p):
1✔
36
    cdef _mpi.MPI new_mpi = _mpi.MPI()
1✔
37
    _mpi.mbedtls_mpi_copy(&new_mpi._ctx, mpi_p)
1✔
38
    return new_mpi
1✔
39

40

41
cdef class MPI:
42
    """Multi-precision integer.
43

44
    This class implements `numbers.Integral`.  The representation
45
    of the MPI is overwritten with random bytes when the MPI is
46
    garbage collected.
47

48
    """
49
    def __init__(self, value=0):
50
        if isinstance(value, MPI):
1✔
51
            value_ = <MPI> value
1✔
52
            _exc.check_error(mbedtls_mpi_copy(&self._ctx, &value_._ctx))
1✔
53
        else:
54
            value = to_bytes(int(value))
1✔
55
            self._read_bytes(value)
1✔
56

57
    def __cinit__(self):
58
        """Initialize one MPI."""
59
        _mpi.mbedtls_mpi_init(&self._ctx)
1✔
60

61
    def __dealloc__(self):
62
        """Unallocate one MPI."""
63
        _exc.check_error(mbedtls_mpi_fill_random(
1✔
64
            &self._ctx, self._len(),
1✔
65
            &_rnd.mbedtls_ctr_drbg_random, &__rng._ctx))
66
        _mpi.mbedtls_mpi_free(&self._ctx)
1✔
67

68
    def __reduce__(self):
1✔
69
        byteorder = "big"
1✔
70
        return type(self).from_bytes, (
1✔
71
            self.to_bytes(self._len(), byteorder),
1✔
72
            byteorder,
1✔
73
        )
74

75
    cdef size_t _len(self):
1✔
76
        """Return the total size in bytes."""
77
        return _mpi.mbedtls_mpi_size(&self._ctx)
1✔
78

79
    def _read_bytes(self, const unsigned char[:] data not None):
1✔
80
        if data.size == 0:
1✔
81
            return MPI(0)
1✔
82
        _exc.check_error(
1✔
83
            _mpi.mbedtls_mpi_read_binary(&self._ctx, &data[0], data.shape[0]))
1✔
84

85
    def __str__(self):
86
        return "%i" % int(self)
1✔
87

88
    def __repr__(self):
89
        return "%s(%i)" % (type(self).__name__, self)
1✔
90

91
    def bit_length(self):
1✔
92
        """Return the number of bits necessary to represent MPI in binary."""
93
        return _mpi.mbedtls_mpi_bitlen(&self._ctx)
1✔
94

95
    @classmethod
1✔
96
    def from_int(cls, value):
97
        # mbedtls_mpi_lset is 'limited' to 64 bits.
98
        return cls.from_bytes(to_bytes(value), byteorder="big")
1✔
99

100
    @classmethod
1✔
101
    def from_bytes(cls, const unsigned char[:] data, byteorder):
102
        assert byteorder in {"big", "little"}
1✔
103
        self = cls()
1✔
104
        self._read_bytes(data[::-1 if byteorder == "little" else 1])
1✔
105
        return self
1✔
106

107
    def to_bytes(self, const size_t length, byteorder):
1✔
108
        assert byteorder in {"big", "little"}
1✔
109
        cdef unsigned char* output = <unsigned char*>malloc(
1✔
110
            length * sizeof(unsigned char))
111
        if not output:
1✔
112
            raise MemoryError()
113
        try:
1✔
114
            _exc.check_error(_mpi.mbedtls_mpi_write_binary(
1✔
115
                &self._ctx, output, length))
1✔
116
            return output[:length][::-1 if byteorder == "little" else 1]
1✔
117
        except Exception as exc:
×
118
            raise OverflowError from exc
×
119
        finally:
120
            free(output)
121

122
    __bytes__ = to_bytes
1✔
123

124
    @classmethod
1✔
125
    def prime(cls, size):
126
        """Return an MPI that is probably prime."""
127
        cdef MPI self_ = cls()
1✔
128
        _exc.check_error(mbedtls_mpi_gen_prime(
1✔
129
            &self_._ctx, size, 0,
1✔
130
            &_rnd.mbedtls_ctr_drbg_random, &__rng._ctx))
131
        return self_
1✔
132

133
    def __hash__(self):
134
        return int(self)
1✔
135

136
    def __bool__(self):
137
        return self != 0
1✔
138

139
    def __add__(self, other):
140
        if not all((isinstance(self, numbers.Integral),
1✔
141
                    isinstance(other, numbers.Integral))):
1✔
142
            return NotImplemented
×
143
        cdef MPI self_ = MPI(self)
1✔
144
        cdef MPI other_ = MPI(other)
1✔
145
        cdef MPI result = MPI()
1✔
146
        _exc.check_error(mbedtls_mpi_add_mpi(
1✔
147
            &result._ctx, &self_._ctx, &other_._ctx))
1✔
148
        return result
1✔
149

150
    def __neg__(self):
151
        raise TypeError("negative value")
×
152

153
    def __pos__(self):
154
        return self
×
155

156
    def __sub__(self, other):
157
        if not all((isinstance(self, numbers.Integral),
1✔
158
                    isinstance(other, numbers.Integral))):
1✔
159
            return NotImplemented
×
160
        cdef MPI self_ = MPI(self)
1✔
161
        cdef MPI other_ = MPI(other)
1✔
162
        cdef MPI result = MPI()
1✔
163
        _exc.check_error(mbedtls_mpi_sub_mpi(
1✔
164
            &result._ctx, &self_._ctx, &other_._ctx))
1✔
165
        return result
1✔
166

167
    def __mul__(self, other):
168
        if not all((isinstance(self, numbers.Integral),
1✔
169
                    isinstance(other, numbers.Integral))):
1✔
170
            return NotImplemented
×
171
        cdef MPI self_ = MPI(self)
1✔
172
        cdef MPI other_ = MPI(other)
1✔
173
        cdef MPI result = MPI()
1✔
174
        _exc.check_error(mbedtls_mpi_mul_mpi(
1✔
175
            &result._ctx, &self_._ctx, &other_._ctx))
1✔
176
        return result
1✔
177

178
    def __truediv__(self, other):
179
        return NotImplemented
×
180

181
    def __pow__(MPI self, exponent, modulus):
182
        if modulus is None:
1✔
183
            modulus = 0
×
184
        if not isinstance(exponent, numbers.Integral):
1✔
185
            return TypeError("exponent should be an integer")
×
186
        if not isinstance(modulus, numbers.Integral):
1✔
187
            return TypeError("modulus should be an integer")
×
188
        if exponent < 0:
1✔
189
            raise ValueError("exponent must be greater than zero")
×
190
        cdef MPI result = MPI()
1✔
191
        cdef MPI exponent_ = MPI(exponent)
1✔
192
        cdef MPI modulus_ = MPI(modulus)
1✔
193
        _exc.check_error(
1✔
194
            mbedtls_mpi_exp_mod(
1✔
195
                &result._ctx, &self._ctx, &exponent_._ctx, &modulus_._ctx, NULL
196
            )
197
        )
198
        return result
1✔
199

200
    def __abs__(self):
201
        # Negative values are not supported.
202
        return self
×
203

204
    def __eq__(MPI self, other):
205
        if not isinstance(other, numbers.Integral):
1✔
206
            return NotImplemented
1✔
207
        if not isinstance(other, MPI):
1✔
208
            other = MPI(other)
1✔
209
        cdef MPI other_ = other
1✔
210
        return mbedtls_mpi_cmp_mpi(&self._ctx, &other_._ctx) == 0
1✔
211

212
    def __float__(self):
213
        return float(int(self))
1✔
214

215
    def __trunc__(self):
1✔
216
        return self
1✔
217

218
    def __floor__(self):
1✔
219
        return self
1✔
220

221
    def __ceil__(self):
1✔
222
        return self
1✔
223

224
    def __round__(self, ndigits=None):
1✔
225
        if ndigits is None:
1✔
226
            return self
1✔
227
        if not isinstance(ndigits, numbers.Integral):
1✔
228
            raise TypeError(ndigits)
×
229
        if ndigits <= 0:
1✔
230
            return self
1✔
231
        return round(int(self), ndigits)
×
232

233
    def __divmod__(self, other):
234
        if not all((isinstance(self, numbers.Integral),
1✔
235
                    isinstance(other, numbers.Integral))):
1✔
236
            return NotImplemented
×
237
        cdef MPI self_ = MPI(self)
1✔
238
        cdef MPI other_ = MPI(other)
1✔
239
        cdef MPI quotient = MPI()
1✔
240
        cdef MPI rest = MPI()
1✔
241
        _exc.check_error(mbedtls_mpi_div_mpi(
1✔
242
            &quotient._ctx, &rest._ctx, &self_._ctx, &other_._ctx))
1✔
243
        return quotient, rest
1✔
244

245
    def __floordiv__(self, other):
246
        return divmod(self, other)[0]
1✔
247

248
    def __mod__(self, other):
249
        if not all((isinstance(self, numbers.Integral),
1✔
250
                    isinstance(other, numbers.Integral))):
1✔
251
            return NotImplemented
×
252
        cdef MPI self_ = MPI(self)
1✔
253
        cdef MPI other_ = MPI(other)
1✔
254
        cdef MPI result = MPI()
1✔
255
        _exc.check_error(mbedtls_mpi_mod_mpi(
1✔
256
            &result._ctx, &self_._ctx, &other_._ctx))
1✔
257
        return result
1✔
258

259
    def __lt__(MPI self, other):
260
        if not isinstance(other, numbers.Integral):
1✔
261
            return NotImplemented
1✔
262
        if not isinstance(other, MPI):
1✔
263
            other = MPI(other)
1✔
264
        cdef MPI other_ = other
1✔
265
        return mbedtls_mpi_cmp_mpi(&self._ctx, &other_._ctx) == -1
1✔
266

267
    def __le__(MPI self, other):
268
        return self < other or self == other
1✔
269

270
    def __gt__(MPI self, other):
271
        return not self <= other
1✔
272

273
    def __ge__(MPI self, other):
274
        return self > other or self == other
1✔
275

276
    def __complex__(self):
1✔
277
        return complex(float(self))
×
278

279
    def real(self):
1✔
280
        return self
×
281

282
    @property
283
    def imag(self):
284
        return 0
×
285

286
    def conjugate(self):
1✔
287
        return self
×
288

289
    def __int__(self):
290
        n = self._len()
1✔
291
        if n:
1✔
292
            return from_bytes(self.to_bytes(n, byteorder="big"))
1✔
293
        else:
294
            return 0
1✔
295

296
    def __index__(self):
297
        return int(self)
1✔
298

299
    def __lshift__(MPI self, other):
300
        _exc.check_error(mbedtls_mpi_shift_l(&self._ctx, int(other)))
1✔
301
        return self
1✔
302

303
    def __rshift__(MPI self, other):
304
        _exc.check_error(mbedtls_mpi_shift_r(&self._ctx, int(other)))
1✔
305
        return self
1✔
306

307
    def __and__(MPI self, other):
308
        if not isinstance(other, MPI):
1✔
309
            other = MPI(other)
1✔
310
        cdef size_t size = int(
1✔
311
            math.ceil(max(self.bit_length(), other.bit_length()) / 8)
1✔
312
        )
313
        self_bin = bytearray(self.to_bytes(size, "big"))
1✔
314
        other_bin = bytearray(other.to_bytes(size, "big"))
1✔
315
        output = bytearray(size)
1✔
316
        cdef size_t ii
317
        for ii in range(size):
1✔
318
            output[ii] = self_bin[ii] & other_bin[ii]
1✔
319
        result = MPI.from_bytes(output, "big")
1✔
320
        _plt.zeroize(self_bin)
1✔
321
        _plt.zeroize(other_bin)
1✔
322
        _plt.zeroize(output)
1✔
323
        return result
1✔
324

325
    def __xor__(MPI self, other):
326
        if not isinstance(other, MPI):
1✔
327
            other = MPI(other)
1✔
328
        cdef size_t size = int(
1✔
329
            math.ceil(max(self.bit_length(), other.bit_length()) / 8)
1✔
330
        )
331
        self_bin = bytearray(self.to_bytes(size, "big"))
1✔
332
        other_bin = bytearray(other.to_bytes(size, "big"))
1✔
333
        output = bytearray(size)
1✔
334
        cdef size_t ii
335
        for ii in range(size):
1✔
336
            output[ii] = self_bin[ii] ^ other_bin[ii]
1✔
337
        result = MPI.from_bytes(output, "big")
1✔
338
        _plt.zeroize(self_bin)
1✔
339
        _plt.zeroize(other_bin)
1✔
340
        _plt.zeroize(output)
1✔
341
        return result
1✔
342

343
    def __or__(MPI self, other):
344
        if not isinstance(other, MPI):
1✔
345
            other = MPI(other)
1✔
346
        cdef size_t size = int(
1✔
347
            math.ceil(max(self.bit_length(), other.bit_length()) / 8)
1✔
348
        )
349
        self_bin = bytearray(self.to_bytes(size, "big"))
1✔
350
        other_bin = bytearray(other.to_bytes(size, "big"))
1✔
351
        output = bytearray(size)
1✔
352
        cdef size_t ii
353
        for ii in range(size):
1✔
354
            output[ii] = self_bin[ii] | other_bin[ii]
1✔
355
        result = MPI.from_bytes(output, "big")
1✔
356
        _plt.zeroize(self_bin)
1✔
357
        _plt.zeroize(other_bin)
1✔
358
        _plt.zeroize(output)
1✔
359
        return result
1✔
360

361
    def __invert__(self):
362
        raise TypeError("negative value")
×
363

364
    @property
365
    def numerator(self):
366
        return self
×
367

368
    @property
369
    def denominator(self):
370
        return 1
×
371

372

373
numbers.Integral.register(MPI)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc