diff --git a/notebooks/encrypt_decrypt.ipynb b/notebooks/encrypt_decrypt.ipynb new file mode 100644 index 0000000..edd222e --- /dev/null +++ b/notebooks/encrypt_decrypt.ipynb @@ -0,0 +1,315 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "Encryption examples.ipynb", + "provenance": [], + "include_colab_link": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "view-in-github", + "colab_type": "text" + }, + "source": [ + "\"Open" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "4JG-7IJgz_dK" + }, + "source": [ + "# PyTorch/CSPRNG encrypt/decrypt examples" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "H8TZemj30JvQ" + }, + "source": [ + "torchcsprng 0.2.0 exposes new API for tensor encryption/decryption. Tensor encryption/decryption API is dtype agnostic, so a tensor of any dtype can be encrypted and the result can be stored to a tensor of any dtype. An encryption key also can be a tensor of any dtype. Currently torchcsprng supports AES cipher with 128-bit key in two modes: ECB and CTR." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "jC1O-C25vI0W", + "outputId": "beee40f0-7fa8-4354-affd-2fcf18305685", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "# !pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html\n", + "# for CUDA runtime:\n", + "!pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cu101/torch_nightly.html" + ], + "execution_count": 1, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Looking in links: https://download.pytorch.org/whl/nightly/cu101/torch_nightly.html\n", + "Collecting torchcsprng\n", + "\u001b[?25l Downloading https://download.pytorch.org/whl/nightly/cu101/torchcsprng-0.2.0.dev20201210%2Bcu101-cp36-cp36m-linux_x86_64.whl (4.4MB)\n", + "\u001b[K |████████████████████████████████| 4.4MB 552kB/s \n", + "\u001b[?25hCollecting torch==1.8.0.dev20201210+cu101\n", + "\u001b[?25l Downloading https://download.pytorch.org/whl/nightly/cu101/torch-1.8.0.dev20201210%2Bcu101-cp36-cp36m-linux_x86_64.whl (788.6MB)\n", + "\u001b[K |████████████████████████████████| 788.6MB 25kB/s \n", + "\u001b[?25hRequirement already satisfied: typing-extensions in /usr/local/lib/python3.6/dist-packages (from torch==1.8.0.dev20201210+cu101->torchcsprng) (3.7.4.3)\n", + "Requirement already satisfied: dataclasses; python_version < \"3.7\" in /usr/local/lib/python3.6/dist-packages (from torch==1.8.0.dev20201210+cu101->torchcsprng) (0.8)\n", + "Requirement already satisfied: numpy in /usr/local/lib/python3.6/dist-packages (from torch==1.8.0.dev20201210+cu101->torchcsprng) (1.18.5)\n", + "\u001b[31mERROR: torchvision 0.8.1+cu101 has requirement torch==1.7.0, but you'll have torch 1.8.0.dev20201210+cu101 which is incompatible.\u001b[0m\n", + "Installing collected packages: torch, torchcsprng\n", + " Found existing installation: torch 1.7.0+cu101\n", + " Uninstalling torch-1.7.0+cu101:\n", + " Successfully uninstalled torch-1.7.0+cu101\n", + "Successfully installed torch-1.8.0.dev20201210+cu101 torchcsprng-0.2.0.dev20201210+cu101\n" + ], + "name": "stdout" + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "su2RWWdOrWFU" + }, + "source": [ + "import torch\n", + "import torchcsprng as csprng" + ], + "execution_count": 2, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "NHTOLPZ_3254" + }, + "source": [ + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')" + ], + "execution_count": 3, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "17L0sgmy0R6o" + }, + "source": [ + "torchcsprng implementation of AES with 128 bit key requires to have a key tensor of 16 bytes but of any dtype" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "rw7WYZ-50To9", + "outputId": "7e9accec-5ac1-4de2-fcc5-860df06ad338", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "key = torch.empty(16, dtype=torch.uint8, device=device).random_(0, 256)\n", + "key" + ], + "execution_count": 4, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "tensor([ 94, 155, 111, 73, 36, 65, 95, 175, 103, 249, 39, 221, 225, 106,\n", + " 147, 18], dtype=torch.uint8)" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 4 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "RRfvyfHM4MY1" + }, + "source": [ + "Alternatively it can be a tensor of 8 elements of `torch.int16` or even 4 elements of `torch.float32`" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rCy01t1-0dtO" + }, + "source": [ + "The size of input tensor is 42 * (32/8) = 168 bytes. AES 128 operates with 16-bytes blocks, so zero-padding of 8 bytes will be used to form 176 bytes(eleven 16-bytes blocks)" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "LcuVmhyU0WTn", + "outputId": "2b887caa-ef4c-4e2a-bc7e-d00191cb727c", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "initial = torch.empty(42, dtype=torch.float32, device=device).normal_(-24.0, 42.0)\n", + "initial" + ], + "execution_count": 5, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "tensor([ -33.2154, -4.4617, -2.9105, -37.1893, -6.0623, -47.4273,\n", + " -44.7682, 23.7199, 52.3595, -80.1355, 10.7041, -52.0069,\n", + " -59.1335, -51.7051, -96.7531, -47.6263, -37.4668, -26.9140,\n", + " -8.7239, -68.0884, -73.8865, -35.0378, -24.9022, -71.3780,\n", + " -83.3700, -73.9181, -56.9136, -14.0332, -0.1193, -24.6635,\n", + " -12.3755, 26.1501, -21.7292, 61.0362, -8.2424, -27.3287,\n", + " -116.2249, -32.5120, -56.9908, -44.0908, 15.9910, -12.2838])" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 5 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "rPNq2u4e3tlJ" + }, + "source": [ + "torchcsprng requires output tensor to be of the same size in bytes as input tensor rounded up to 16 bytes(AES 128 block size), so if `torch.int64` is dtype of the destination tensor size must be 176 / (64/8) = 22" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "RAJya9GT0gb4" + }, + "source": [ + "encrypted = torch.empty(22, dtype=torch.int64, device=device)" + ], + "execution_count": 6, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "-DCI4QOh4oGX" + }, + "source": [ + "Call `torchcsprng.encrypt` to encrypt `initial` tensor in [ECB](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_(ECB)) mode with 128-bit `key` tensor and store the result to `encrypted` tensor." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "TK4OjPRq4lsJ", + "outputId": "a9c7bee0-e95c-49fa-a6a8-8a33b58695e0", + "colab": { + "base_uri": "https://localhost:8080/" + } + }, + "source": [ + "csprng.encrypt(initial, encrypted, key, \"aes128\", \"ecb\")" + ], + "execution_count": 7, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "tensor([-1712310488599806515, -8311769289706173237, -8219472943074007291,\n", + " 4944862344658876406, 5289614356287313567, -6556419268017486570,\n", + " -2070634943111059129, -6988005632242749066, -6786984565899259673,\n", + " 2617853683610034142, 7394987086142621793, 9037048344238914391,\n", + " -4389724417591829293, 639143755047053321, 6630155308790632093,\n", + " 8233678956273288167, 7762736707078557167, -66820074992426839,\n", + " 2506358826618882964, 3811127519115226076, 7223286519671228245,\n", + " -4680879586720463643])" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 7 + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "yXUAwFHh5PSy" + }, + "source": [ + "Create an output tensor" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "4LtJ-kD446DJ" + }, + "source": [ + "decrypted = torch.empty_like(initial)" + ], + "execution_count": 8, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "8VcF04mf6Rn5" + }, + "source": [ + "Call `torchcsprng.decrypt` to decrypt `encrypted` tensor in ECB mode with 128-bit `key` tensor and store the result to `decrypted` tensor." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "kojXCFGK5v6l" + }, + "source": [ + "# csprng.decrypt(encrypted, decrypted, key, \"aes128\", \"ecb\")" + ], + "execution_count": 10, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "yOc1ftnM5yyj" + }, + "source": [ + "" + ], + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file