Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added Python wrapper for the CryptoLW classes (Acorn and Ascon) #50

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions libraries/CryptoLW/examples/Python/TestAcorn.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
from CryptoLW import Acorn128, Ascon128
import binascii
import secrets

header = b"header"
data = b"secret"
key = secrets.token_bytes(16) # get_random_bytes(16)
iv = secrets.token_bytes(16)

cipher = Acorn128()
cipher.clear()
cipher.setKey(key)
cipher.setIV(iv)
cipher.addAuthData(header)
ciphertext = cipher.encrypt(data)
tag = cipher.computeTag()

print("Cipher Text : %s" % binascii.hexlify(ciphertext).decode('utf-8'))
print("Tag : %s" % binascii.hexlify(tag).decode('utf-8'))

# Tester le decryptage

cipher.clear()
cipher.setKey(key)
cipher.setIV(iv)
cipher.addAuthData(header)
cleartext = cipher.decrypt(ciphertext)

print("Clear text : %s" % cleartext.decode('utf-8'))
cipher.checkTag(tag)
print("Tag OK!")

cipher.clear()
cipher.setKey(key)
cipher.setKey(iv)
cipher.addAuthData(header)
cleartext = cipher.decrypt(ciphertext)
try:
cipher.checkTag(iv) # Mauvais tag (IV)
print("Une erreur aurait du etre souleve (***incorrect***)")
except ValueError:
print("Echec verification (correct)")


# Test avec donnees du Arduino
header = binascii.unhexlify(b"0102030405060708")
key = binascii.unhexlify(b"233952DEE4D5ED5F9B9C6D6FF80FF478")
iv = binascii.unhexlify(b"2D2B10316ABE7766AABADFEFB8E139EA")
tag = binascii.unhexlify(b"3E67F10B7FD68BAA8206C62BD0026B1B")
buffer_crypte = binascii.unhexlify(b"136A14EC1F53E0C7CB19AADC38E70D274774E3CAC147223E")

cipher.clear()
cipher.setKey(key)
cipher.setIV(iv)
cipher.addAuthData(header)
cleartext = cipher.decrypt(buffer_crypte)

print("Clear text : %s" % binascii.hexlify(cleartext).decode('utf-8'))
cipher.checkTag(tag)
print("Tag OK!")
157 changes: 157 additions & 0 deletions libraries/CryptoLW/python/PythonWrapper.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// #include <stdexcept>
#include <boost/python.hpp>
#include "Acorn128.h"
#include "Ascon128.h"
namespace bp = boost::python;

// Wrapper for Python using Boost.Python (boost.org)

// ----------------------------
// Shamelessly stolen from pyRF24 (https://github.com/nRF24/RF24)
void throw_ba_exception(void)
{
PyErr_SetString(PyExc_TypeError, "buf parameter must be bytes or bytearray");
bp::throw_error_already_set();
};

char* get_bytes_or_bytearray_str(bp::object buf)
{
PyObject* py_ba;
py_ba = buf.ptr();
if (PyByteArray_Check(py_ba)) {
return PyByteArray_AsString(py_ba);
} else if (PyBytes_Check(py_ba)) {
return PyBytes_AsString(py_ba);
} else {
throw_ba_exception();
}
return NULL;
};

int get_bytes_or_bytearray_ln(bp::object buf)
{
PyObject* py_ba;
py_ba = buf.ptr();
if (PyByteArray_Check(py_ba)) {
return PyByteArray_Size(py_ba);
} else if (PyBytes_Check(py_ba)) {
return PyBytes_Size(py_ba);
} else {
throw_ba_exception();
}
return 0;
};
// ----------------------------

bool setKey_wrap(AuthenticatedCipher& ref, bp::object buf)
{
const char* buffer = get_bytes_or_bytearray_str(buf);
return ref.setKey((uint8_t*)buffer, get_bytes_or_bytearray_ln(buf));
};

bool setIV_wrap(AuthenticatedCipher& ref, bp::object buf)
{
const char* buffer = get_bytes_or_bytearray_str(buf);
return ref.setIV((uint8_t*)buffer, get_bytes_or_bytearray_ln(buf));
};

void addAuthData_wrap(AuthenticatedCipher& ref, bp::object buf)
{
const char* buffer = get_bytes_or_bytearray_str(buf);
ref.addAuthData((uint8_t*)buffer, get_bytes_or_bytearray_ln(buf));
};

bp::object encrypt_wrap(AuthenticatedCipher& ref, bp::object buf)
{
const char* inputBuffer = get_bytes_or_bytearray_str(buf);

// Recuperer la taille du buffer, identique pour output.
int len = get_bytes_or_bytearray_ln(buf);

// Creer un buffer d'output sur le heap et chiffrer
char* outputBuffer = new char[len + 1];
ref.encrypt((uint8_t*) outputBuffer, (uint8_t*) inputBuffer, len);

// Convertir le buffer d'output en objet Python
bp::object py_ba(bp::handle<>( PyByteArray_FromStringAndSize(outputBuffer, len) ));

delete[] outputBuffer; // Cleanup heap
return py_ba;
};

bp::object decrypt_wrap(AuthenticatedCipher& ref, bp::object buf)
{
const char* inputBuffer = get_bytes_or_bytearray_str(buf);

// Recuperer la taille du buffer, identique pour output.
int len = get_bytes_or_bytearray_ln(buf);

// Creer un buffer d'output sur le heap et dechiffrer
char* outputBuffer = new char[len + 1];
ref.decrypt((uint8_t*) outputBuffer, (uint8_t*) inputBuffer, len);

// Convertir le buffer d'output en objet Python
bp::object py_ba(bp::handle<>( PyByteArray_FromStringAndSize(outputBuffer, len) ));

delete[] outputBuffer; // Cleanup heap
return py_ba;
};

bp::object computeTag_wrap(AuthenticatedCipher& ref)
{
// Compute tag, sauver un buffer temporaire
char outputBuffer[16];
ref.computeTag((uint8_t*)&outputBuffer, sizeof(outputBuffer));

// Convertir le buffer d'output en objet Python
bp::object py_ba(bp::handle<>( PyByteArray_FromStringAndSize(outputBuffer, sizeof(outputBuffer)) ));

return py_ba;
};

void checkTag_wrap(AuthenticatedCipher& ref, bp::object buf)
{
const char* buffer = get_bytes_or_bytearray_str(buf);

if( ! ref.checkTag((uint8_t*)buffer, get_bytes_or_bytearray_ln(buf)) )
{
// Comportement Python habituel, lancer une exception plutot que retourner un false
PyErr_SetString(PyExc_ValueError, "AuthenticatedCipher: invalid tag");
bp::throw_error_already_set();
}
};

BOOST_PYTHON_MODULE(CryptoLW)
{
bp::class_<Cipher, boost::noncopyable>("Cipher", bp::no_init);

bp::class_<AuthenticatedCipher, bp::bases<Cipher>, boost::noncopyable>("AuthenticatedCipher", bp::no_init);

bp::class_<Acorn128, bp::bases<AuthenticatedCipher>>("Acorn128")
.def("keySize", &Acorn128::keySize)
.def("ivSize", &Acorn128::ivSize)
.def("tagSize", &Acorn128::tagSize)
.def("setKey", &setKey_wrap)
.def("setIV", &setIV_wrap)
.def("encrypt", &encrypt_wrap)
.def("decrypt", &decrypt_wrap)
.def("addAuthData", &addAuthData_wrap)
.def("computeTag", &computeTag_wrap)
.def("checkTag", &checkTag_wrap)
.def("clear", &Acorn128::clear)
;

bp::class_<Ascon128, bp::bases<AuthenticatedCipher>>("Ascon128")
.def("keySize", &Ascon128::keySize)
.def("ivSize", &Ascon128::ivSize)
.def("tagSize", &Ascon128::tagSize)
.def("setKey", &setKey_wrap)
.def("setIV", &setIV_wrap)
.def("encrypt", &encrypt_wrap)
.def("decrypt", &decrypt_wrap)
.def("addAuthData", &addAuthData_wrap)
.def("computeTag", &computeTag_wrap)
.def("checkTag", &checkTag_wrap)
.def("clear", &Ascon128::clear)
;
};
74 changes: 74 additions & 0 deletions libraries/CryptoLW/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
Python extension for the light-weight algorithms for the rweather Arduino Cryptography Library.
https://github.com/rweather/arduinolibs

### Installation

To install using Python 3 on Debian/Ubuntu: first ensure you have python3-dev, pip3.
Then install the Boost-Python library and the python setup-tools.

Example installation for Ubuntu and Debian:

* `sudo apt install -y python3-dev python3-pip libboost-python-dev`
* `sudo pip3 install setuptools`

From the libraries/CryptoLW/python directory, run:

* `sudo python3 setup.py install`

### Usage

#### Encryption
```
from CryptoLW import Acorn128
import secrets
# Note: secrets is new in Python 3.6. You can use a different library
# for random bytes, e.g. pycryptodome.Random.

# Sample data
header = b"header"
data = b"secret"
key = secrets.token_bytes(16)
iv = secrets.token_bytes(16)

# Prepare cipher
cipher = Acorn128()
cipher.clear() # Required if reusing the cipher
cipher.setKey(key) # Shared secret key
cipher.setIV(iv) # Initialization Vector

# Add data
cipher.addAuthData(header)
ciphertext = cipher.encrypt(data)

# Compute tag
tag = cipher.computeTag()
```
The initialisation vector (IV) must not be reused between encryption sessions and should
not be an easily predictable value (e.g. do not use iv++).
The header, tag, iv and ciphertext can be transmitted to the destination over an unsafe medium.
The shared secret key must not be transmitted, it must be handled prior to using this algorithm.

#### Decryption
```
from CryptoLW import Acorn128

# Prepare cipher
cipher = Acorn128()
cipher.clear() # Required if reusing the cipher
cipher.setKey(key) # Shared secret key
cipher.setIV(iv) # Initialization Vector

# Initialise with unencrypted content
cipher.addAuthData(header)

# Apply encrypted content to recover cleartext
cleartext = cipher.decrypt(ciphertext)

try:
cipher.checkTag(tag) # Use tag produced by encryption process
print("The decryption process is successful")
except ValueError:
print("The process failed, header cannot be trusted and cleartext is likely invalid")
```
A ValueError exception is thrown when the tag does not match the content that is applied
on the cipher.
45 changes: 45 additions & 0 deletions libraries/CryptoLW/python/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from distutils.core import setup, Extension
import sys
from os import path

path_libs = '../../'


def main():

if sys.version_info >= (3,):
BOOST_LIB = 'boost_python3'
else:
BOOST_LIB = 'boost_python'

setup(name="CryptoLW",
version="0.2.0",
description=
"""
Python extension for the light-weight algorithms for the rweather Arduino Cryptography Library.
https://github.com/rweather/arduinolibs
by Rhys Weatherley <rhys.weatherley@gmail.com>
""",
author="Mathieu Dugre",
author_email="mathieu.dugre@mdugre.info",
ext_modules=[Extension(
"CryptoLW",
[
path.join(path_libs, "Crypto/Crypto.cpp"),
path.join(path_libs, "Crypto/Cipher.cpp"),
path.join(path_libs, "Crypto/AuthenticatedCipher.cpp"),
path.join(path_libs, "CryptoLW/src/Acorn128.cpp"),
path.join(path_libs, "CryptoLW/src/Ascon128.cpp"),
"PythonWrapper.cpp",
],
libraries=[BOOST_LIB],
include_dirs=[
path.join(path_libs, "Crypto"),
path.join(path_libs, "Crypto/utility"),
path.join(path_libs, "CryptoLW/src"),
]
)])


if __name__ == "__main__":
main()