diff --git a/README.md b/README.md index bc4ce73..5d60005 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,12 @@ # PyTorch/CSPRNG -torchcsprng is a [PyTorch C++/CUDA extension](https://pytorch.org/tutorials/advanced/cpp_extension.html) that provides [cryptographically secure pseudorandom number generators](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) for PyTorch. - [![CircleCI](https://circleci.com/gh/pytorch/csprng.svg?style=shield&circle-token=64701692dd7f13f31019612289f0200fdb661dc2)](https://circleci.com/gh/pytorch/csprng) +torchcsprng is a [PyTorch C++/CUDA extension](https://pytorch.org/tutorials/advanced/cpp_extension.html) that provides: + +- [AES](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) 128-bit encryption/decryption in two modes: [ECB](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_(ECB)) and [CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pytorch/csprng/blob/master/examples/encrypt_decrypt.ipynb) +- [cryptographically secure pseudorandom number generators](https://en.wikipedia.org/wiki/Cryptographically_secure_pseudorandom_number_generator) for PyTorch. [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/pytorch/csprng/blob/master/examples/csprng.ipynb) + ## Design torchcsprng generates a random 128-bit key on CPU using one of its generators and runs @@ -22,6 +25,24 @@ Advantages: ## Features +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](https://en.wikipedia.org/wiki/Advanced_Encryption_Standard) cipher with 128-bit key in two modes: [ECB](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_(ECB)) and [CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)). + +* `torchcsprng.encrypt(input: Tensor, output: Tensor, key: Tensor, cipher: string, mode: string)` + +> - `input` tensor can be any CPU or CUDA tensor of any dtype and size in bytes(zero-padding is used to make its size in bytes divisible by block size in bytes) +> - `output` tensor can have any dtype and the same device as `input` tensor and the size in bytes rounded up to the block size in bytes(16 bytes for AES 128) +> - `key` tensor can have any dtype and the same device as `input` tensor and size in bytes equal to 16 for AES 128 +> - `cipher` currently can be only one supported value `"aes128"` +> - `mode` currently can be either [`"ecb"`](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_(ECB)) or [`"ctr"`](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) + +* `torchcsprng.decrypt(input: Tensor, output: Tensor, key: Tensor, cipher: string, mode: string)` + +> - `input` tensor can be any CPU or CUDA tensor of any dtype with size in bytes divisible by the block size in bytes(16 bytes for AES 128) +> - `output` tensor can have any dtype but the same device as `input` tensor and the same size in bytes as `input` tensor +> - `key` tensor can have any dtype and the same device as `input` tensor and size in bytes equal to 16 for AES 128 +> - `cipher` currently can be only one supported value `"aes128"` +> - `mode` currently can be either [`"ecb"`](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Electronic_codebook_(ECB)) or [`"ctr"`](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) + torchcsprng exposes two methods to create crypto-secure and non-crypto-secure PRNGs: | Method to create PRNG | Is crypto-secure? | Has seed? | Underlying implementation | @@ -48,52 +69,58 @@ The following list of methods supports all forementioned PRNGs: ## Installation -CSPRNG works with Python 3.6/3.7/3.8 on the following operating systems and can be used with PyTorch tensors on the following devices: +CSPRNG works with Python 3.6-3.9 on the following operating systems and can be used with PyTorch tensors on the following devices: | Tensor Device Type | Linux | macOS | MS Window | |--------------------|-----------|---------------|----------------| | CPU | Supported | Supported | Supported | -| CUDA | Supported | Not Supported | Coming | +| CUDA | Supported | Not Supported | Supported since 0.2.0 | + +The following is the corresponding CSPRNG versions and supported Python versions. + +| PyTorch | CSPRNG | Python | CUDA | +|---------|--------|----------|------------------| +| 1.8.0 | 0.2.0 | 3.7-3.9 | 10.1, 10.2, 11.1 | +| 1.7.1 | 0.1.4 | 3.6-3.8 | 9.2, 10.1, 10.2 | +| 1.7.0 | 0.1.3 | 3.6-3.8 | 9.2, 10.1, 10.2 | +| 1.6.0 | 0.1.2 | 3.6-3.8 | 9.2, 10.1, 10.2 | + ### Binary Installation Anaconda: -| OS | CUDA | | -|---------|-----------------------------------------------|----------------------------------------------| -| Linux | 9.2

10.1

10.2

None | conda install torchcsprng cudatoolkit=9.2 -c pytorch

conda install torchcsprng cudatoolkit=10.1 -c pytorch

conda install torchcsprng cudatoolkit=10.2 -c pytorch

conda install torchcsprng cpuonly -c pytorch | -| macOS | None | conda install torchcsprng cpuonly -c pytorch | -| Windows | None | conda install torchcsprng cpuonly -c pytorch | +| OS | CUDA | | +|---------------|------------------------------------------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Linux/Windows | 10.1

10.2

11.1

None | conda install torchcsprng cudatoolkit=10.1 -c pytorch -c conda-forge

conda install torchcsprng cudatoolkit=10.2 -c pytorch -c conda-forge

conda install torchcsprng cudatoolkit=11.1 -c pytorch -c conda-forge

conda install torchcsprng cpuonly -c pytorch -c conda-forge | +| macOS | None | conda install torchcsprng -c pytorch | pip: -| OS | CUDA | | -|---------|-----------------------------------------------|-------------------------------------------------------------------------------------| -| Linux | 9.2

10.1

10.2

None | pip install torchcsprng==0.1.2+cu92 torch==1.6.0+cu92 -f https://download.pytorch.org/whl/torch_stable.html

pip install torchcsprng==0.1.2+cu101 torch==1.6.0+cu101 -f https://download.pytorch.org/whl/torch_stable.html

pip install torchcsprng torch

pip install torchcsprng==0.1.2+cpu torch==1.6.0+cpu -f https://download.pytorch.org/whl/torch_stable.html | -| macOS | None | pip install torchcsprng torch | -| Windows | None | pip install torchcsprng torch -f https://download.pytorch.org/whl/torch_stable.html | +| OS | CUDA | | +|---------------|------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Linux/Windows | 10.1

10.2

11.1

None | pip install torchcsprng==0.2.0+cu101 torch==1.8.0+cu101 -f https://download.pytorch.org/whl/cu101/torch_stable.html

pip install torchcsprng==0.2.0 torch==1.8.0 -f https://download.pytorch.org/whl/cu102/torch_stable.html

pip install torchcsprng==0.2.0+cu111 torch==1.8.0+cu111 -f https://download.pytorch.org/whl/cu111/torch_stable.html

pip install torchcsprng==0.2.0+cpu torch==1.8.0+cpu -f https://download.pytorch.org/whl/cpu/torch_stable.html | +| macOS | None | pip install torchcsprng torch | ### Nightly builds: Anaconda: -| OS | CUDA | | -|---------|-----------------------------------------------|------------------------------------------------------| -| Linux | 9.2

10.1

10.2

None | conda install torchcsprng cudatoolkit=9.2 -c pytorch-nightly

conda install torchcsprng cudatoolkit=10.1 -c pytorch-nightly

conda install torchcsprng cudatoolkit=10.2 -c pytorch-nightly

conda install torchcsprng cpuonly -c pytorch-nightly | -| macOS | None | conda install torchcsprng cpuonly -c pytorch-nightly | -| Windows | None | conda install torchcsprng cpuonly -c pytorch-nightly | +| OS | CUDA | | +|---------------|------------------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Linux/Windows | 10.1

10.2

11.1

None | conda install torchcsprng cudatoolkit=10.1 -c pytorch-nightly -c conda-forge

conda install torchcsprng cudatoolkit=10.2 -c pytorch-nightly -c conda-forge

conda install torchcsprng cudatoolkit=11.1 -c pytorch-nightly -c conda-forge

conda install torchcsprng cpuonly -c pytorch-nightly -c conda-forge | +| macOS | None | conda install torchcsprng -c pytorch-nightly | pip: -| OS | CUDA | | -|---------|-----------------------------------------------|----------------------------------------------------------------------------------------------------| -| Linux | 9.2

10.1

10.2

None | pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cu92/torch_nightly.html

pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cu101/torch_nightly.html

pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cu102/torch_nightly.html

pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html | -| macOS | None | pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html | -| Windows | None | pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html | +| OS | CUDA | | +|---------------|------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Linux/Windows | 10.1

10.2

11.1

None | pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cu101/torch_nightly.html

pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cu102/torch_nightly.html

pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cu111/torch_nightly.html

pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html | +| macOS | None | pip install --pre torchcsprng -f https://download.pytorch.org/whl/nightly/cpu/torch_nightly.html | ### From Source -torchcsprng is a Python C++/CUDA extension that depends on PyTorch. In order to build CSPRNG from source it is required to have Python(>=3.6) with PyTorch(>=1.6.0) installed and C++ compiler(gcc/clang for Linux, XCode for macOS, Visual Studio for MS Windows). +torchcsprng is a Python C++/CUDA extension that depends on PyTorch. In order to build CSPRNG from source it is required to have Python(>=3.7) with PyTorch(>=1.8.0) installed and C++ compiler(gcc/clang for Linux, XCode for macOS, Visual Studio for MS Windows). To build torchcsprng you can run the following: ```console python setup.py install diff --git a/examples/csprng.ipynb b/examples/csprng.ipynb new file mode 100644 index 0000000..e0e719c --- /dev/null +++ b/examples/csprng.ipynb @@ -0,0 +1,226 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "csprng.ipynb", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "Lpno_zUJT8ms" + }, + "source": [ + "# Cryptographically secure pseudorandom number generators for PyTorch\n", + "\n", + "The torchcsprng API is available in `torchcsprng` module:\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "db4YYky-PDI_" + }, + "source": [ + "!pip install torchcsprng==0.2.0 torch==1.8.0 -f https://download.pytorch.org/whl/cu102/torch_stable.html" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "O1s_j8CPPHSn" + }, + "source": [ + "import torch\n", + "import torchcsprng as csprng" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "o1Kz25IoS9m-" + }, + "source": [ + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HLlLxkDIUWCG" + }, + "source": [ + "Create crypto-secure PRNG from /dev/urandom:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "yyyYlq5kUQss" + }, + "source": [ + "urandom_gen = csprng.create_random_device_generator('/dev/urandom')" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xbUCnJfkUdUI" + }, + "source": [ + "Create empty boolean tensor on the `device` and initialize it with random values from `urandom_gen`:\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "zmj_VlIzUYIO" + }, + "source": [ + "torch.empty(10, dtype=torch.bool, device=device).random_(generator=urandom_gen)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "ycODsYhtUud9" + }, + "source": [ + "Create empty int16 tensor on the `device` and initialize it with random values in range [0, 100) from `urandom_gen`:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "uel-jbW9UlZH" + }, + "source": [ + "torch.empty(10, dtype=torch.int16, device=device).random_(100, generator=urandom_gen)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "1jXW1FEmVMW_" + }, + "source": [ + "Create non-crypto-secure MT19937 PRNG:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "sL-cwFGfVOrp" + }, + "source": [ + "mt19937_gen = csprng.create_mt19937_generator()\n", + "torch.empty(10, dtype=torch.int64, device=device).random_(torch.iinfo(torch.int64).min, to=None, generator=mt19937_gen)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "KW96wT4UVXBm" + }, + "source": [ + "Create crypto-secure PRNG from default random device:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "tjwbuE6FVRgm" + }, + "source": [ + "default_device_gen = csprng.create_random_device_generator()\n", + "torch.randn(10, device=device, generator=default_device_gen)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "qYgdkZAYVfZT" + }, + "source": [ + "Create non-crypto-secure MT19937 PRNG with seed:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "xjOsYOxxVbzg" + }, + "source": [ + "mt19937_gen = csprng.create_mt19937_generator(42)\n", + "first = torch.empty(10, device=device).geometric_(p=0.2, generator=mt19937_gen)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cV77v7tHVlRd" + }, + "source": [ + "Recreate MT19937 PRNG with the same seed:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "i0O2lC0hVjAg" + }, + "source": [ + "mt19937_gen = csprng.create_mt19937_generator(42)\n", + "second = torch.empty(10, device=device).geometric_(p=0.2, generator=mt19937_gen)" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "OcgSK0mejcef" + }, + "source": [ + "Check that `first` equals to `second`:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "vMx1BRO3jh7L" + }, + "source": [ + "assert (first == second).all()" + ], + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file diff --git a/examples/encrypt_decrypt.ipynb b/examples/encrypt_decrypt.ipynb new file mode 100644 index 0000000..54a5a66 --- /dev/null +++ b/examples/encrypt_decrypt.ipynb @@ -0,0 +1,307 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "encrypt_decrypt.ipynb", + "provenance": [], + "collapsed_sections": [] + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "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" + }, + "source": [ + "!pip install torchcsprng==0.2.0 torch==1.8.0 -f https://download.pytorch.org/whl/cu102/torch_stable.html" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "su2RWWdOrWFU" + }, + "source": [ + "import torch\n", + "import torchcsprng as csprng" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "NHTOLPZ_3254" + }, + "source": [ + "device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')" + ], + "execution_count": null, + "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" + }, + "source": [ + "key = torch.empty(16, dtype=torch.uint8, device=device).random_(0, 256)\n", + "key" + ], + "execution_count": null, + "outputs": [] + }, + { + "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" + }, + "source": [ + "initial = torch.empty(42, dtype=torch.float32, device=device).normal_(-24.0, 42.0)\n", + "initial" + ], + "execution_count": null, + "outputs": [] + }, + { + "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": null, + "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" + }, + "source": [ + "csprng.encrypt(initial, encrypted, key, \"aes128\", \"ecb\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "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": null, + "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": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "9dEBSPD6EFSu" + }, + "source": [ + "Let's check that `decrypted` equals to `initial`:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "yOc1ftnM5yyj" + }, + "source": [ + "assert (decrypted == initial).all()" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cQWyteLlE4mQ" + }, + "source": [ + "Another example is to use [CTR](https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_(CTR)) mode with 128-bit `key` tensor of 4 elements of dtype `dtype=torch.float32`:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "ZFInqYawD7ks" + }, + "source": [ + "key = torch.empty(4, dtype=torch.float32, device=device).random_()\n", + "key" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "FRz94NaZGyRS" + }, + "source": [ + "Let's encrypt 100 elements `torch.bool` tensor and store the result in 56 elements `torch.int16` tensor:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "8uiqxiehF_is" + }, + "source": [ + "initial = torch.empty(100, dtype=torch.bool, device=device).random_()\n", + "initial" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "G0URlmQYGfcW" + }, + "source": [ + "encrypted = torch.empty(56, dtype=torch.int16, device=device)\n", + "csprng.encrypt(initial, encrypted, key, \"aes128\", \"ctr\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "U9Zz2oXoHw9Q" + }, + "source": [ + "Decrypt it back and check that `decrypted` equals to `initial`:" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "YXNcdUbXHoPC" + }, + "source": [ + "decrypted = torch.empty_like(initial)\n", + "csprng.decrypt(encrypted, decrypted, key, \"aes128\", \"ctr\")\n", + "decrypted" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "ie7epw1SKrdQ" + }, + "source": [ + "assert (decrypted == initial).all()" + ], + "execution_count": null, + "outputs": [] + } + ] +} \ No newline at end of file