.. default-domain:: chpl
.. module:: Crypto
:synopsis: A cryptographic library based on 'OpenSSL'.
Crypto
======
**Usage**
.. code-block:: chapel
use Crypto;
or
.. code-block:: chapel
import Crypto;
**Submodules**
.. toctree::
:maxdepth: 1
:glob:
Crypto/*
A cryptographic library based on 'OpenSSL'.
This module provides a cryptographic library based on `OpenSSL
`_, targeted at flexible encryption
purposes.
The Crypto module focuses on providing various cryptographic utilities such as
- Symmetric Ciphers
+ :chpl:class:`AES`
+ :chpl:class:`Blowfish`
- Asymmetric Ciphers
+ :chpl:class:`RSA`
- Hashing Functions
+ :chpl:class:`Hash`
- Key Derivation Functions
+ :chpl:class:`KDF`
- Cryptographically-Secure Pseudo-Random Number Generators
+ :chpl:class:`CryptoRandom`
- Miscellaneous Utilities.
+ :chpl:class:`CryptoBuffer`
+ :chpl:class:`RSAKey`
+ :chpl:class:`Envelope`
.. note::
This module is currently under development and will expand significantly
in upcoming releases.
Compiling with Crypto
----------------------
In order to compile a Chapel program that uses this module, the
Crypto and C_OpenSSL (C wrappers to OpenSSL) libraries must be installed on the system.
The paths to both the ``openssl/.h`` header files and OpenSSL library
must be passed to the ``-I`` and ``-L`` compiler arguments. The library name,
typically ``crypto``, must be passed to the ``-l`` argument as well.
The compilation command should look something like this:
.. code-block:: sh
chpl -I$PATH_TO_OPENSSL_DIR \
-L$PATH_TO_OPENSSL_LIBS -lcrypto source.chpl
This would also work by setting the ``LDFLAGS`` and ``CPPFLAGS`` environment
variables to the expected paths on certain systems.
Chapel Crypto API
------------------
.. class:: CryptoBuffer
The `CryptoBuffer` class is a wrapper around the internal representation
of how the values in this library are stored. Every sequence of bytes going
into a Crypto utility or coming out of it, is a `CryptoBuffer`.
A `CryptoBuffer` can enclose a `string` or a `[] uint(8)` passed to its
initializer and provides helper functions to access those values.
.. method:: proc init(s: string)
The `CryptoBuffer` class initializer that initializes the buffer
when a `string` is supplied to it.
:arg s: `string` input for buffer conversion.
:type s: `string`
:return: An object of class `CryptoBuffer`.
:rtype: `CryptoBuffer`
.. method:: proc init(s: bytes)
The `CryptoBuffer` class initializer that initializes the buffer
when a `bytes` is supplied to it.
:arg s: `bytes` input for buffer conversion.
:type s: `bytes`
:return: An object of class `CryptoBuffer`.
:rtype: `CryptoBuffer`
.. method:: proc init(s: [] uint(8))
The `CryptoBuffer` class initializer that initializes the buffer
when a `[] uint(8)` is supplied to it.
:arg s: `[] uint(8)` input for buffer conversion.
:type s: `[] uint(8)`
:return: An object of class `CryptoBuffer`.
:rtype: `CryptoBuffer`
.. method:: proc getBuffData()
Returns the entire internal buffer with each byte in ASCII.
:return: the internal buffer representation.
:rtype: `[] uint(8)`
.. method:: proc getBuffPtr()
Returns the pointer to the entire internal buffer with
each byte in ASCII.
:return: pointer to the internal buffer representation.
:rtype: `c_ptr([] uint(8))`
.. method:: proc getBuffSize(): int
Returns the length of the entire internal buffer.
:return: length of the internal buffer representation.
:rtype: `int`
.. method:: proc toHex() throws
Returns the hexadecimal array representation of the entire internal buffer.
:return: hex array representation of the internal buffer.
:rtype: `[] string`
.. method:: proc toHexString() throws
Returns the hexadecimal string representation of the entire internal buffer.
:return: hex string representation of the internal buffer.
:rtype: `string`
.. class:: RSAKey
`RSAKey` class encloses the `EVP_PKEY` object provided by the
OpenSSL primitives. The `EVP_PKEY` object can contain the public key,
private key or both of them. Hence, the contents of an object of the
class `RSAKey` may be decided by the user.
Calling the `RSAKey` initializer without using any key import or export
functions may result in generation of a single object that contains both the
keys (public and private).
In order to separate out the key objects, keys can be imported
from a `.pem` file. (TODO)
.. method:: proc init(keyLen: int)
The `RSAKey` class initializer that initializes the `EVP_PKEY` object
of OpenSSL and basically, initializes a set of public and private keys.
It checks for valid RSA key lengths and generates a public key and private
key pair accordingly.
:arg keyLen: RSA Key length in bits.
:type keyLen: `int`
:return: An object of class `RSAKey` representing the key pair.
:rtype: `RSAKey`
.. class:: Envelope
The `Envelope` class wraps all the data returned by the encrypt function
of the `RSA` class along with some utility data. An `RSA` encrypt function
returns an array of `RSA` encrypted symmetric keys and a single `AES`
encrypted message. The `Envelope` also encloses the initialization vector
used during encryption such that it can be utilized during the decryption
phase as well.
.. method:: proc init(iv: owned CryptoBuffer, encSymmKey: [] owned CryptoBuffer, encSymmValue: owned CryptoBuffer)
The `Envelope` class initializer that encapsulates the IV, AES encrypted
ciphertext buffer and an array of encrypted key buffers.
:arg iv: Initialization Vector.
:type iv: `owned CryptoBuffer`
:arg encSymmKey: Array of encrypted symmetric (AES) keys.
:type encSymmKey: `[] owned CryptoBuffer`
:arg encSymmValue: AES-encrypted ciphertext buffer.
:type encSymmValue: `owned CryptoBuffer`
:return: An object of class `Envelope`.
:rtype: `Envelope`
.. method:: proc getEncMessage()
This function returns the encrypted version of the plaintext
supplied by the user.
:return: A 'CryptoBuffer' representing the ciphertext.
:rtype: `CryptoBuffer`
.. method:: proc getIV()
This function returns the IV generated by the `encrypt` routine and
encapsulated in the `Envelope`. This is used for both encryption and
decryption.
:return: Initialization Vector.
:rtype: `CryptoBuffer`
.. method:: proc getEncKeyByIndex(i: int)
This function returns a particular symmetric key buffer based
on the index supplied as the argument.
.. note::
The supplied index should be in the domain of the RSA-encrypted keys array.
:arg i: An index of the symmetric key buffer array.
:type i: `int`
:return: A specific key buffer based on the index.
:rtype: `CryptoBuffer`
.. method:: proc getEncKeys()
This function returns the entire array of symmetric key buffers.
:return: Entire array of key buffers.
:rtype: `[] CryptoBuffer`
.. enum:: enum Digest { MD5, SHA1, SHA224, SHA256, SHA384, SHA512, RIPEMD160 }
The `Digest` enum represents all the hashing functions provided by the
OpenSSL primitives. Value from this enum is passed to the `Hash` class
initializer in order to select the type of hashing function to be used.
.. enumconstant:: enum constant MD5
.. enumconstant:: enum constant SHA1
.. enumconstant:: enum constant SHA224
.. enumconstant:: enum constant SHA256
.. enumconstant:: enum constant SHA384
.. enumconstant:: enum constant SHA512
.. enumconstant:: enum constant RIPEMD160
.. class:: Hash
The `Hash` class represents all the hashing functions provided by the
OpenSSL primitives. It supports all the prominent and most commonly used
deterministic hashing functions.
.. method:: proc init(digestName: Digest)
The `Hash` class initializer that initializes the hashing function
to be used. This initializer sets the byte length of the
respective hash and allocates a domain for memory allocation
for hashing. It currently supports the following hashing functions -
``MD5``, ``SHA1``, ``SHA224``, ``SHA256``, ``SHA384``, ``SHA512`` and
``RIPEMD160`` consumed via an enum, `Digest`.
:arg digestName: Hashing function to be used.
:type digestName: `Digest`
:return: An object of class `Hash`.
:rtype: `Hash`
Initialization example,
.. code-block:: chapel
var h = new Hash(Digest.SHA256);
.. method:: proc getDigestName(): string
Returns the name of the hashing function used. For instance,
instantiating an object with ``var hash = new Hash("SHA256")``,
would make this function return the string `SHA256`.
:return: Name of the hashing function.
:rtype: `string`
.. method:: proc getDigest(inputBuffer: CryptoBuffer): owned CryptoBuffer
Given a CryptoBuffer (buffer) as input, this function returns it's
hash digest. The returned hash digest is also a buffer that can be
accessed by using buffer utility functions.
:arg inputBuffer: A `CryptoBuffer` representing the input to be hashed.
:type inputBuffer: `CryptoBuffer`
:return: An `owned CryptoBuffer` representing the hash digest.
:rtype: `owned CryptoBuffer`
.. enum:: enum CryptoChainMode { cbc, ecb, cfb, ofb }
The `CryptoChainMode` enum represents all cipher chaining modes
that can be used by a symmetric cipher. It is used by the `AES` and
`Blowfish` class initializers to select a chaining mode.
.. enumconstant:: enum constant cbc
.. enumconstant:: enum constant ecb
.. enumconstant:: enum constant cfb
.. enumconstant:: enum constant ofb
.. class:: AES
The `AES` class represents the symmetric encryption algorithm, AES.
The Advanced Encryption Standard (AES), also known by its original name Rijndael
is a specification for the encryption of electronic data established by the
U.S. National Institute of Standards and Technology (NIST) in 2001.
It is the most widely used symmetric cipher and is also used to encrypt
messages in public-key cryptography such as within `RSA`.
Currently, the `AES` class allows symmetric encryption using only the CBC or
Cipher Block Chaining mode in 128, 192, and 256 key size variants.
After thorough testing, ECB, OCB and other chaining mode variants will also
be added to this library(TODO).
.. method:: proc init(bits: int, mode: CryptoChainMode)
The `AES` class initializer that initializes the AES encryption
algorithm with the right key length and chaining mode.
:arg bits: Number of bits representing the variant of AES based on
key-size. (128, 192 or 256)
:type bits: `int`
:arg mode: Chaining mode to be used.
:type mode: `CryptoChainMode`
:return: An object of class `AES`.
:rtype: `AES`
Initialization example,
.. code-block:: chapel
var aes = new AES(256, CryptoChainMode.cbc);
.. method:: proc getByteSize(): int
This function returns the size in bytes of the key-length/variant of
AES which is to be used. For instance,
- AES128 returns 16
- AES192 returns 24
- AES256 returns 32
:return: Key length of AES in bytes.
:rtype: `int`
.. method:: proc encrypt(plaintext: CryptoBuffer, key: CryptoBuffer, IV: CryptoBuffer): owned CryptoBuffer
This is the 'AES' encrypt routine that encrypts the user supplied message buffer
using the key and IV.
The `encrypt` takes in the plaintext buffer, key buffer and IV buffer as the
arguments and returns a buffer of the ciphertext.
:arg plaintext: A `CryptoBuffer` representing the plaintext to be encrypted.
:type plaintext: `CryptoBuffer`
:arg key: A `CryptoBuffer` representing the key to be used for encryption.
:type key: `CryptoBuffer`
:arg IV: A `CryptoBuffer` representing the initialization vector to be used
for encryption.
:type IV: `CryptoBuffer`
:return: An `owned CryptoBuffer` representing the ciphertext.
:rtype: `owned CryptoBuffer`
.. method:: proc decrypt(ciphertext: CryptoBuffer, key: CryptoBuffer, IV: CryptoBuffer): owned CryptoBuffer
This is the 'AES' decrypt routine that decrypts the user supplied ciphertext
buffer using the same key and IV used for encryption.
The `decrypt` takes in the ciphertext buffer, key buffer and IV buffer as the
arguments and returns a buffer of the decrypted plaintext.
:arg ciphertext: A `CryptoBuffer` representing the ciphertext to be decrypted.
:type ciphertext: `CryptoBuffer`
:arg key: A `CryptoBuffer` representing the key to be used for decryption
(same as the one used in encryption).
:type key: `CryptoBuffer`
:arg IV: A `CryptoBuffer` representing the initialization vector to be used
for decryption (same as the one used in encryption).
:type IV: `CryptoBuffer`
:return: An `owned CryptoBuffer` representing the obtained plaintext.
:rtype: `owned CryptoBuffer`
.. class:: Blowfish
The `Blowfish` class represents a symmetric-key block cipher called Blowfish, designed
in 1993 by Bruce Schneier. Considering current scenario, the Advanced Encryption
Standard(AES) cipher is used more in practice. Since, Blowfish is unpatented and placed
in the public domain, it receives a decent amount of attention from the community.
.. note::
Since the key in Blowfish can be of various sizes, use a :chpl:class:`KDF` routine
to generate a secure-key with the recommended byte-size as 16 bytes. Also, the
initialization vector in Blowfish cipher should strictly be 8 bytes long.
The `Blowfish` class supports 4 `chaining modes `_:
- ``cbc`` or `Cipher Block Chaining`
- ``ecb`` or `Electronic Codebook`
- ``cfb`` or `Cipher Feedback`
- ``ofb`` or `Output Feedback`
.. note:
This cipher is removed in OpenSSL 3 and therefore deprecated here as
well. The deprecated Blowfish support here does not function with OpenSSL
3.
.. method:: proc init(mode: CryptoChainMode)
The `Blowfish` class initializer that initializes the Blowfish encryption
algorithm with the right key length and chaining mode.
:arg mode: Name of the chaining mode to be used.
:type mode: `CryptoChainMode`
:return: An object of class `Blowfish`.
:rtype: `Blowfish`
Initialization example,
.. code-block:: chapel
var bf = new Blowfish(CryptoChainMode.cbc);
.. method:: proc encrypt(plaintext: CryptoBuffer, key: CryptoBuffer, IV: CryptoBuffer): owned CryptoBuffer throws
This is the 'Blowfish' encrypt routine that encrypts the user supplied message buffer
using the key and IV.
The `encrypt` takes in the plaintext buffer, key buffer and IV buffer as the
arguments and returns a buffer of the ciphertext.
:arg plaintext: A `CryptoBuffer` representing the plaintext to be encrypted.
:type plaintext: `CryptoBuffer`
:arg key: A `CryptoBuffer` representing the key to be used for encryption.
:type key: `CryptoBuffer`
:arg IV: A `CryptoBuffer` representing the initialization vector to be used
for encryption.
:type IV: `CryptoBuffer`
:return: An `owned CryptoBuffer` representing the ciphertext.
:rtype: `owned CryptoBuffer`
.. method:: proc decrypt(ciphertext: CryptoBuffer, key: CryptoBuffer, IV: CryptoBuffer): owned CryptoBuffer
This is the 'Blowfish' decrypt routine that decrypts the user supplied ciphertext
buffer using the same key and IV used for encryption.
The `decrypt` takes in the ciphertext buffer, key buffer and IV buffer as the
arguments and returns a buffer of the decrypted plaintext.
:arg ciphertext: A `CryptoBuffer` representing the ciphertext to be decrypted.
:type ciphertext: `CryptoBuffer`
:arg key: A `CryptoBuffer` representing the key to be used for decryption
(same as the one used in encryption).
:type key: `CryptoBuffer`
:arg IV: A `CryptoBuffer` representing the initialization vector to be used
for decryption (same as the one used in encryption).
:type IV: `CryptoBuffer`
:return: An `owned CryptoBuffer` representing the obtained plaintext.
:rtype: `owned CryptoBuffer`
.. class:: CryptoRandom
The `CryptoRandom` class represents a CSPRNG provided by OpenSSL that
automatically does the seeding part before returning a random sequence
of bytes of the type `CryptoBuffer`.
Given the length of the buffer required by the user, it generates a random
buffer of the same size.
.. method:: proc getRandomBuffer(buffLen: int): owned CryptoBuffer throws
This function represents a CSPRNG that generates and allocates the desired
number of random values as specified by the argument. Halts for number of
bytes less than 1 (invalid). For instance,
.. code-block:: chapel
var a = (new CryptoRandom()).getRandomBuffer(5)
would give us a `CryptoBuffer` of size `5` and pre-initialized with values.
:arg buffLen: Number of random values to be generated in the buffer.
:type buffLen: `int`
:return: An `owned CryptoBuffer` representing the generated values.
:rtype: `owned CryptoBuffer`
.. class:: KDF
The `KDF` class contains the most widely used key derivation functions.
Currently it supports a single password based key derivation function which
is the most secure one as of writing this library. More KDFs will be added
to this in the future.
.. method:: proc init(byteLen: int, iterCount: int, digest: Hash)
The `KDF` class initializer that initializes the common data used by most
of the Key Derivation Functions.
:arg byteLen: Size of the expected key in bytes / key length.
:type byteLen: `int`
:arg iterCount: The iteration count used by OpenSSL to repeat the `Update`
primitive. Min. recommended value = 1000. Greater the
`iterCount`, greater the security of the key.
:type iterCount: `int`
:arg digest: An object of class 'Hash'. This decides the Hashing function
to be used for HMAC based KDFs.
:type digest: `Hash`
:return: An object of class `KDF`.
:rtype: `KDF`
.. method:: proc passKDF(userKey: string, saltBuff: CryptoBuffer): owned CryptoBuffer
This function represents Password-Based KDF 2. It generates a secure-key
buffer based on the salt and also on the metadata provided in the `KDF`
initializer.
:arg userKey: User-specified `string` representation of the key.
:type userKey: `string`
:arg saltBuff: A `CryptoBuffer` representing the user generated salt.
:type saltBuff: `CryptoBuffer`
:return: An `owned CryptoBuffer` representing the generated key.
:rtype: `owned CryptoBuffer`
.. class:: RSA
RSA is one of the first practical public-key cryptosystems and is widely
used for secure data transmission. A user of the `RSA` first generates the
public and private keys using the `RSAKey` class. The `RSAKey` object is
then passed to the `RSA` functions for further encryption and decryption
purposes.
The `encrypt` function of this class takes in an array of `RSAKey` objects
and uses all of them to perform the encryption.
Similarly, decryption can be done individually performed (without passing an array
as an argument) by passing a single `RSAKey` object every time the decryption takes place.
.. method:: proc encrypt(plaintext: CryptoBuffer, keys: [] RSAKey): owned Envelope
This is the 'RSA' encrypt routine that encrypts the plaintext buffer. This
uses the `AES` encryption algorithm to encrypt the plaintext and auto-generates
a secure IV and key. Furthermore, the key is encrypted using `RSA` based on the
'RSAKey' objects supplied in the arguments.
The function returns an `Envelope` object that encloses the auto-generated
IV, AES encrypted ciphertext and an array of `RSA` encrypted key buffers.
The number of encrypted keys is equal to the number of `RSAKey` objects
in the array. This kind of use case is useful specifically in developing
one-to-many systems such as GPG.
For instance, consider a server-client model where the server performs
encryption using the public keys of all the clients and returns an `Envelope`
to each client. Now, the decryption can only be performed by each client
using only their own private key.
:arg plaintext: A `CryptoBuffer` representing the plaintext to be
encrypted.
:type plaintext: `CryptoBuffer`
:arg keys: An array of `RSAKey` objects. This depends on the number of
different RSA encryptions that are to be performed on the same
plaintext.
:type keys: `[] RSAKey`
:return: An `owned Envelope` object which comprises of the IV buffer,
array of RSA encrypted keys and AES encrypted ciphertext.
:rtype: `owned Envelope`
.. method:: proc decrypt(envp: Envelope, key: RSAKey): owned CryptoBuffer throws
This is the 'RSA' decrypt routine that decrypts the ciphertext buffer. This
uses `AES` decryption to decrypt the ciphertext.
It accepts the entire `Envelope` object but only a single `RSAKey` object
for unilateral decryption.
:arg envp: A `CryptoBuffer` representing the plaintext to be
encrypted.
:type envp: `Envelope`
:arg key: An array of `RSAKey` objects. This depends on the number of
different RSA encryptions that are to be performed on the same
plaintext.
:type key: `RSAKey`
:return: An `owned CryptoBuffer` representing the obtained plaintext.
:rtype: `owned CryptoBuffer`