Skip to content

Commit

Permalink
Merge branch 'develop'
Browse files Browse the repository at this point in the history
  • Loading branch information
Synss committed Feb 24, 2018
2 parents 19b81fc + 25a8acb commit ec7d732
Show file tree
Hide file tree
Showing 18 changed files with 2,165 additions and 109 deletions.
23 changes: 20 additions & 3 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
[0.8] - 2018-02-24

Support X.509 Certificates

* x509/Certificate: X.509 certificate writer and parser.
* x509/CSR: X.509 certificate signing request writer and parser.
* x509/CRL: X.509 certificate revocation list and validation.

API Changes

* CipherBase/RSA: import_() method renamed from_buffer() for PEP 543.
* CipherBase/RSA: export(format="PEM") method renamed to_PEM()
* CipherBase/RSA: export(format="DER") method renamed to_DER()
* CipherBase/RSA: from_DER(), from_PEM() to import from DER or PEM.
* CipherBase/RSA: to_bytes() alias to_DER()


[0.7] - 2018-02-04

- Add support for Python 2.7
- Tests ported from nosetest to pytest
- Setup continuous integration
* Add support for Python 2.7, 3.5, and 3.6.
* Tests ported from nosetest to pytest.
* Setup continuous integration.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ PYX = $(wildcard mbedtls/*.pyx)
PYX += $(wildcard mbedtls/cipher/*.pyx)
PYX += $(wildcard mbedtls/pk/*.pyx)

LIBMBEDTLS = $(HOME)/lib/mbedtls-2.5.2
LIBMBEDTLS = $(HOME)/lib/mbedtls-2.4.2

release:
cython $(PYX)
Expand Down
77 changes: 52 additions & 25 deletions README.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
==========================
Python wrapper to mbed TLS
==========================
=======================================================
Cryptographic library for Python with Mbed TLS back end
=======================================================

.. image::
https://circleci.com/gh/Synss/python-mbedtls/tree/develop.svg?style=svg
Expand All @@ -11,18 +11,26 @@ Python wrapper to mbed TLS
:target: https://coveralls.io/github/Synss/python-mbedtls?branch=develop


`python-mbedtls`_ is a thin wrapper to ARM's mbed TLS library.

According to the `official mbed TLS website`_
`python-mbedtls`_ is a free cryptographic library for Python that uses
`mbed TLS`_ for back end.

mbed TLS (formerly known as PolarSSL) makes it trivially easy for
developers to include cryptographic and SSL/TLS capabilities in their
(embedded) products, facilitating this functionality with a minimal
coding footprint.

.. _python-mbedtls: https://synss.github.io/python-mbedtls
.. _official mbed TLS website: https://tls.mbed.org
`python-mbedtls` API follows the recommendations from `PEP 452`_: API for
Cryptographic Hash Functions v2.0 and `PEP 272`_ API for Block Encryption
Algorithms v1.0 and can therefore be used as a drop-in replacements to
`PyCrypto`_ or Python's `hashlib`_ and `hmac`_

.. _python-mbedtls: https://synss.github.io/python-mbedtls
.. _mbed TLS: https://tls.mbed.org
.. _PEP 452: https://www.python.org/dev/peps/pep-0452/
.. _PEP 272: https://www.python.org/dev/peps/pep-0272/
.. _PyCrypto: https://www.dlitz.net/software/pycrypto/
.. _hashlib: https://docs.python.org/3.6/library/hashlib.html
.. _hmac: https://docs.python.org/3.6/library/hmac.html

License
=======
Expand All @@ -42,23 +50,19 @@ The bindings are tested with Python 2.7, 3.4, 3.5, and 3.6.

# aptitude install libmbedtls-dev

and the wrapper::
and the `pyton-mbedtls`::

python -m pip install python-mbedtls
$ python -m pip install python-mbedtls


Hashing module (`md.h`)
-----------------------

Message digest algorithms
~~~~~~~~~~~~~~~~~~~~~~~~~
Message digest with `mbedtls.hash`
----------------------------------

The `mbedtls.hash` module provides MD5, SHA-1, SHA-2, and RIPEMD-160 secure
hashes and message digests. The API follows the recommendations from PEP 452
so that it can be used as a drop-in replacement to e.g. `hashlib` or
`PyCrypto`.

Here are the examples from `hashlib` executed with `python-mbedtls`::
Here are the examples from `hashlib` ported to `python-mbedtls`::

>>> from mbedtls import hash as hashlib
>>> m = hashlib.md5()
Expand All @@ -84,8 +88,8 @@ Using `new()`::
'cc4a5ce1b3df48aec5d22d1f16b894a0b894eccc'


HMAC algorithm
~~~~~~~~~~~~~~
HMAC algorithm with `mbedtls.hmac`
----------------------------------

The `mbedtls.hmac` module computes HMAC. The API follows the recommendations
from PEP 452 as well.
Expand All @@ -106,8 +110,8 @@ Warning:
per message.


Symmetric cipher module (`cipher.h`)
------------------------------------
Symmetric cipher with `mbedtls.cipher`
--------------------------------------

The `mbedtls.cipher` module provides symmetric encryption. The API follows the
recommendations from PEP 272 so that it can be used as a drop-in replacement to
Expand Down Expand Up @@ -138,8 +142,8 @@ Example::
b'This is a super-secret message!'


Public key module (`pk.h`)
--------------------------
RSA Public key with `mbedtls.pk`
--------------------------------

The `mbedtls.pk` module provides the RSA cryptosystem. This includes:

Expand Down Expand Up @@ -173,10 +177,33 @@ Message signature and verification::
True
>>> rsa.verify(b"Sorry, wrong message.", sig)
False
>>> prv, pub = rsa.export(format="DER")
>>> prv, pub = rsa.to_DER()
>>> other = pk.RSA()
>>> other.import_(pub)
>>> other.from_DER(pub)
>>> other.has_private()
False
>>> other.verify(b"Please sign here.", sig)
True


X.509 Certificate writing and parsing with `mbedtls.x509`
---------------------------------------------------------

Create new X.509 certificates::

>>> import datetime as dt
>>> from pathlib import Path
>>> from mbedtls.x509 import Certificate, CSR, CRL
>>> now = dt.datetime.utcnow()
>>> crt = Certificate(
... start=now, end=now + dt.timedelta(days=90),
... issuer="C=NL,O=PolarSSL,CN=PolarSSL Test CA", issuer_key=issuer_key,
... subject=None, subject_key=subject_key,
... md_alg=hash.sha1(), serial=None)
...
>>> csr = CSR.new(subject_key, hash.sha1(),
"C=NL,O=PolarSSL,CN=PolarSSL Server 1")

and load existing certificates from file::

>>> crl = CRL.from_file("ca/wp_crl.pem")
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
'sphinx.ext.autodoc',
'sphinx.ext.coverage',
'sphinx.ext.viewcode',
'sphinxcontrib.napoleon',
'sphinx.ext.napoleon',
]

autodoc_default_flags = ['members',
Expand Down Expand Up @@ -67,7 +67,7 @@
# built documents.
#
# The short X.Y version.
version = '0.5'
version = '0.7'
# The full version, including alpha/beta/rc tags.
release = version

Expand Down
75 changes: 75 additions & 0 deletions mbedtls/_mpi.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
"""Declaration from `mbedtls/bignum.h`."""

__author__ = "Mathias Laurin"
__copyright__ = "Copyright 2018, Mathias Laurin"
__license__ = "MIT License"


cdef extern from "mbedtls/bignum.h":
# Multi-precision integer library
# -------------------------------
ctypedef enum mbedtls_mpi: pass
ctypedef enum mbedtls_mpi_sint: pass

# mbedtls_mpi
# -----------
void mbedtls_mpi_init( mbedtls_mpi *X )
void mbedtls_mpi_free( mbedtls_mpi *X );

# mbedtls_mpi_grow
# mbedtls_mpi_shrink
# mbedtls_mpi_copy
# mbedtls_mpi_swap
# mbedtls_mpi_safe_cond_assign
# mbedtls_mpi_safe_cond_swap
# mbedtls_mpi_lset // limited to 64-bits
# mbedtls_mpi_get_bit
# mbedtls_mpi_set_bit
# mbedtls_mpi_lsb

size_t mbedtls_mpi_bitlen(const mbedtls_mpi *X)
size_t mbedtls_mpi_size(const mbedtls_mpi *X)

# mbedtls_mpi_read_string
# mbedtls_mpi_write_string
# mbedtls_mpi_read_file
# mbedtls_mpi_write_file

int mbedtls_mpi_read_binary(
mbedtls_mpi *X,
const unsigned char *buf,
size_t buflen)
int mbedtls_mpi_write_binary(
mbedtls_mpi *X,
unsigned char *buf,
size_t buflen)

# mbedtls_mpi_shift_l
# mbedtls_mpi_shift_r
# mbedtls_mpi_cmp_abs
# mbedtls_mpi_cmp_mpi
# mbedtls_mpi_cmp_int
# mbedtls_mpi_add_abs
# mbedtls_mpi_sub_abs
# mbedtls_mpi_add_mpi
# mbedtls_mpi_sub_mpi
# mbedtls_mpi_add_int
# mbedtls_mpi_sub_int
# mbedtls_mpi_mul_mpi
# mbedtls_mpi_mul_int
# mbedtls_mpi_div_mpi
# mbedtls_mpi_div_int
# mbedtls_mpi_mod_mpi
# mbedtls_mpi_mod_int
# mbedtls_mpi_exp_mod
# mbedtls_mpi_fill_random
# mbedtls_mpi_gcd
# mbedtls_mpi_inv_mod
# mbedtls_mpi_is_prime
# mbedtls_mpi_gen_prime


cdef class MPI:
cdef mbedtls_mpi _ctx
cdef _len(self)
cpdef _from_bytes(self, const unsigned char[:] bytes)
105 changes: 105 additions & 0 deletions mbedtls/_mpi.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
"""Multi-precision integer library (MPI)."""

__author__ = "Mathias Laurin"
__copyright__ = "Copyright 2018, Mathias Laurin"
__license__ = "MIT License"


cimport _mpi
from libc.stdlib cimport malloc, free

import numbers
from binascii import hexlify, unhexlify

from mbedtls.exceptions import *

try:
long
except NameError:
long = int


cdef to_bytes(value):
return unhexlify("{0:02x}".format(value).encode("ascii"))


cdef from_bytes(value):
return long(hexlify(value), 16)


cdef class MPI:
"""Multi-precision integer.
Only minimal bindings here because Python already has
arbitrary-precision integers.
"""
def __init__(self, value):
if value is None:
return # Implementation detail.
try:
value = to_bytes(value)
except TypeError:
pass
self._from_bytes(bytearray(value))

def __cinit__(self):
"""Initialize one MPI."""
_mpi.mbedtls_mpi_init(&self._ctx)

def __dealloc__(self):
"""Unallocate one MPI."""
_mpi.mbedtls_mpi_free(&self._ctx)

cdef _len(self):
"""Return the total size in bytes."""
return _mpi.mbedtls_mpi_size(&self._ctx)

cpdef _from_bytes(self, const unsigned char[:] bytes):
check_error(
_mpi.mbedtls_mpi_read_binary(&self._ctx, &bytes[0], bytes.shape[0]))
return self

def __str__(self):
return "%i" % long(self)

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

def __eq__(self, other):
if not isinstance(other, numbers.Integral):
raise NotImplemented
return long(self) == other

@classmethod
def from_int(cls, value):
# mbedtls_mpi_lset is 'limited' to 64 bits.
return cls.from_bytes(to_bytes(value), byteorder="big")

def __int__(self):
return from_bytes(self.to_bytes(self._len(), byteorder="big"))

@classmethod
def from_bytes(cls, bytes, byteorder):
assert byteorder in {"big", "little"}
order = slice(None, None, -1 if byteorder is "little" else None)
return cls(None)._from_bytes(bytearray(bytes[order]))

def to_bytes(self, length, byteorder):
assert byteorder in {"big", "little"}
order = slice(None, None, -1 if byteorder is "little" else None)
cdef unsigned char* output = <unsigned char*>malloc(
length * sizeof(unsigned char))
if not output:
raise MemoryError()
try:
check_error(_mpi.mbedtls_mpi_write_binary(
&self._ctx, output, length))
return bytes(output[:length])[order]
except Exception as exc:
raise OverflowError from exc
finally:
free(output)

__bytes__ = to_bytes
Loading

0 comments on commit ec7d732

Please sign in to comment.