759 lines
36 KiB
Markdown
759 lines
36 KiB
Markdown
---
|
|
title: "![SlunkCrypt](etc/img/SlunkCrypt-Logo.png)"
|
|
---
|
|
|
|
|
|
Introduction
|
|
============
|
|
|
|
SlunkCrypt is an experimental cryptography library and command-line tool. See [*encryption algorithm*](#encryption-algorithm) for details.
|
|
|
|
|
|
Legal Warning
|
|
=============
|
|
|
|
Use of SlunkCrypt may be illegal in countries where encryption is outlawed. We believe it is legal to use SlunkCrypt in many countries all around the world, but we are not lawyers, and so if in doubt you should seek legal advice before downloading it. You may find useful information at [cryptolaw.org](http://www.cryptolaw.org/), which collects information on cryptography laws in many countries.
|
|
|
|
|
|
Command-line Usage
|
|
==================
|
|
|
|
This section describes the SlunkCypt command-line application.
|
|
|
|
Synopsis
|
|
--------
|
|
|
|
The SlunkCypt command-line program is invoked as follows:
|
|
|
|
slunkcrypt --encrypt [pass:<pass>|file:<file>] <input.txt> <output.enc>
|
|
slunkcrypt --decrypt [pass:<pass>|file:<file>] <input.enc> <output.txt>
|
|
slunkcrypt --make-pw [<length>]
|
|
|
|
Commands
|
|
--------
|
|
|
|
One of the following commands must be chosen:
|
|
|
|
- **`--encrypt` (`-e`):**
|
|
Run application in ***encrypt*** mode. Reads the given *plaintext* and generates *ciphertext*.
|
|
- **`--decrypt` (`-d`):**
|
|
Run application in ***decrypt*** mode. Reads the given *ciphertext* and restores *plaintext*.
|
|
- **`--make-pw` (`-p`):**
|
|
Generate a "strong" random passphrase, suitable for use with SlunkCrypt.
|
|
- **`--self-test` (`-t`):**
|
|
Run the application in ***self-test*** mode. Program will exit after all test are completed.
|
|
|
|
Options
|
|
-------
|
|
|
|
The following options are available:
|
|
|
|
- **`pass:<pass>`**:
|
|
* Specifies the "secret" passphrase directly on the command-line. This is considered *insecure*.
|
|
- **`file:<file>`**:
|
|
* Specifies a file to read the passphrase from. Only the *first* line of the file will be read!
|
|
* *Note:* It is also possible to specify **`-`** in order to read the passphrase from the *stdin*.
|
|
- **`<input>`**:
|
|
* In ***encrypt*** mode – specifies the *plaintext* file (unencrypted information) that is to be encrypted.
|
|
* In ***decrypt*** mode – specifies the *ciphertext* file (result of encryption) that is to be decrypted.
|
|
- **`<output>`**:
|
|
* In ***encrypt*** mode – specifies the file where the *ciphertext* (result of encryption) will be stored.
|
|
* In ***decrypt*** mode – specifies the file where the *plaintext* (unencrypted information) will be stored.
|
|
- **`<length>`**:
|
|
* Specifies the length of the passphrase to be generated. If *not* specified, defaults to 24.
|
|
|
|
### Remarks
|
|
|
|
- If the passphrase is **not** specified on the command-line, it will be read from the `SLUNK_PASSPHRASE` environment variable.
|
|
|
|
- The same passphrase must be used for both, ***encrypt*** and ***decrypt*** mode. The decryption of the ciphertext will only be possible, if the "correct" passphrase is known. It is recommended to choose a "random" password that is at least 12 characters in length and consists of a mix of upper-case characters, lower-case characters, digits as well as special characters.
|
|
|
|
- Passing the passphrase directly on the command-line is insecure, because the command-line may be visible to other users.
|
|
|
|
Examples
|
|
--------
|
|
|
|
Here are some examples on how to use the SlunkCrypt command-line application:
|
|
|
|
### Example #1
|
|
|
|
1. Let's generate a new random (secure) password first:
|
|
|
|
slunkcrypt --make-pw
|
|
|
|
*Example output:*
|
|
|
|
cdG2=fh<C=3[SSCzf[)iDjIV
|
|
|
|
2. Now, encrypt the plaintext message, using the generated password:
|
|
|
|
slunkcrypt --encrypt pass:"cdG2=fh<C=3[SSCzf[)iDjIV" plaintext.txt ciphertext.enc
|
|
|
|
Optionally, let's have a look at the ciphertext:
|
|
|
|
hexdump -C ciphertext.enc
|
|
|
|
3. Finally, decrypt the ciphertext, using the same password as before:
|
|
|
|
slunkcrypt --decrypt pass:"cdG2=fh<C=3[SSCzf[)iDjIV" ciphertext.enc plaintext.out
|
|
|
|
Optionally, verify that the decrypted file is identical to the original:
|
|
|
|
sha256sum -b plaintext.txt plaintext.out
|
|
|
|
### Example #2
|
|
|
|
1. Generate a new password and store it to a text file:
|
|
|
|
slunkcrypt --make-pw > passwd.txt
|
|
|
|
Optionally, output the generated password to the terminal:
|
|
|
|
cat passwd.txt
|
|
|
|
2. Encrypt file by reading the password from the text file:
|
|
|
|
slunkcrypt --encrypt file:passwd.txt plaintext.txt ciphertext.enc
|
|
|
|
### Example #3
|
|
|
|
1. Generate a new password directly to an environment variable:
|
|
|
|
MY_PASSWD="$(slunkcrypt --make-pw)"
|
|
|
|
Optionally, output the generated password to the terminal:
|
|
|
|
echo "${MY_PASSWD}"
|
|
|
|
2. Encrypt file by reading the password from the *stdin*:
|
|
|
|
slunkcrypt --encrypt - plaintext.txt ciphertext.enc <<< "${MY_PASSWD}"
|
|
|
|
|
|
Encryption algorithm
|
|
====================
|
|
|
|
The SlunkCrypt algorithm is based on concepts of the well-known [**Enigma**](https://en.wikipedia.org/wiki/Enigma_machine) machine, but with various improvements:
|
|
|
|
- The original Enigma machine had only *three* (or somtimes *four*) rotors, plus a static "reflector" wheel. In SlunkCrypt, we uses **256** simulated rotors for an improved security.
|
|
|
|
- The original Enigma machine supported only 26 distinct symbols, i.e. the letters `A` to `Z`. In SlunkCrypt, we use **256** distinct symbols, i.e. the byte values `0x00` to `0xFF`, which allows the encryption (and decryption) of arbitrary streams of bytes, rather than just plain text. Of course, SlunkCrypt can encrypt (and decrypt) text files as well.
|
|
|
|
- In the original Enigma machine, the signal passes through the rotors *twice*, once in forward direction and then again in backwards direction – thus the "reflector" wheel. This way, the Enigma's encryption was made *involutory*, i.e. encryption and decryption were the same operation. While this was highly convenient, it also severely weakened the cryptographic strength of the Enigma machine, because the number of possible permutations was reduced drastically! This is one of the main reasons why the Enigma machine eventually was defeated. In SlunkCrypt, the signal passes through the simulated rotors just *once*, in order to maximize the number of possible permutations. This eliminates the most important known weakness of the Enigma machine. Obviously, in SlunkCrypt, separate modes for encryption and decryption need to be provided, because encryption and decryption *no* longer are the same.
|
|
|
|
- In the original Enigma machine, *only* the rightmost rotor was stepped after every symbol. The other rotors were stepped infrequently. Specifically, if one rotor had completed a full turn, it caused the next rotor to move by one step – much like the odometer in a car. The fact that most of the rotors remained in the same "static" position for a long time was another important weakness of the Enigma machine that ultimately lead to its demise. Furthermore, the positions of the Enigma's rotors would start to repeat after "only" 16,900 characters. In SlunkCrypt, an improved stepping algorithm is used, which ensures that *all* rotors are stepped often. At the same time, the rotor positions in SlunkCrypt *never* repeat (practically).
|
|
|
|
- The internal wiring of each of the original Enigma machine's rotors was *fixed*. Each rotor "type" came with a different internal wiring (i.e. permutation). Some models had up to eight rotor "types" to choose from, but only three or four rotors were used at a time. Nonetheless, the internal wiring (i.e. permutation) of each of the supplied rotors was **not** modifiable. This severely restricted the key space of the Enigma machine, as far as the rotors are concerned, because *only* the order of the rotors and the initial position of each rotor could be varied. In SlunkCrypt, a fully *randomized* wiring (i.e. permutation) is generated from the password for each of the 256 simulated rotors. The initial rotor positions are *randomized* as well.
|
|
|
|
- SlunkCrypt does **not** currently implement the *plugboard* of the original Enigma machine. That is because, even though the plugboard has a large key space, it is just a *fixed* substitution cipher that does *not* contribute too much to the cryptographic strength of the Enigma machine. In fact, the plugboard could be "erased" by Welchman's [diagonal board](https://en.wikipedia.org/wiki/Bombe#Stecker_values).
|
|
|
|
|
|
Programming Interface (API)
|
|
===========================
|
|
|
|
This section describes the SlunkCypt library interface for software developers.
|
|
|
|
Getting started
|
|
---------------
|
|
|
|
In order to use the SlunkCypt library in your C++ code, include **`<slunkcrypt.hpp>`** header and instantiate the appropriate SlunkCypt classes:
|
|
|
|
### Example #1
|
|
|
|
Here is a simple example on how to use the SlunkCrypt [**`Encryptor`**](#encryptor) class:
|
|
|
|
#include <slunkcrypt.hpp>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
int main()
|
|
{
|
|
/* Open input and output files here */
|
|
uint8_t buffer[BUFF_SIZE];
|
|
slunkcrypt::Encryptor slunk_encrypt(passphrase);
|
|
while (input.good())
|
|
{
|
|
input.read(reinterpret_cast<char*>(buffer), BUFF_SIZE);
|
|
if ((!input.bad()) && (input.gcount() > 0))
|
|
{
|
|
if (!slunk_encrypt.inplace(buffer, (size_t)input.gcount()))
|
|
{
|
|
/* Implement error handling here */
|
|
}
|
|
output.write(reinterpret_cast<char*>(buffer), count);
|
|
}
|
|
}
|
|
std::cout << std::hex << slunk_encrypt.get_nonce() << std::endl;
|
|
}
|
|
|
|
### Example #2
|
|
|
|
Here is a simple example on how to use the SlunkCrypt [**`Decryptor`**](#decryptor) class:
|
|
|
|
#include <slunkcrypt.hpp>
|
|
#include <fstream>
|
|
#include <iostream>
|
|
|
|
int main()
|
|
{
|
|
/* Open input and output files here */
|
|
uint8_t buffer[BUFF_SIZE];
|
|
slunkcrypt::Decryptor slunk_decrypt(nonce, passphrase);
|
|
while (input.good())
|
|
{
|
|
input.read(reinterpret_cast<char*>(buffer), BUFF_SIZE);
|
|
if ((!input.bad()) && (input.gcount() > 0))
|
|
{
|
|
if (!slunk_decrypt.inplace(buffer, (size_t)input.gcount()))
|
|
{
|
|
/* Implement error handling here */
|
|
}
|
|
output.write(reinterpret_cast<char*>(buffer), count);
|
|
}
|
|
}
|
|
}
|
|
|
|
C++11 API
|
|
---------
|
|
|
|
This section describes the "high-level" C++11 API of the SlunkCrypt library. All SlunkCrypt classes live in the **`slunkcrypt`** namespace.
|
|
|
|
### Encryptor
|
|
|
|
Class for *encrypting* data using the SlunkCrypt library.
|
|
|
|
#### Constructor
|
|
|
|
Create and initialize a new **``Encryptor``** instance. Also generated a new, random nonce.
|
|
|
|
Encryptor::Encryptor(
|
|
const std::string &passwd
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `passwd`
|
|
The password to "protect" the message. The password is given as an `std::string`, e.g. UTF-8 encoded characters. The same password **may** be used to encrypt *multiple* messages. Also, the same password **must** be used for both, encryption *and* decryption; it will *only* be possible decrypt the ciphertext, if the "correct" password is known. The password must be kept confidential under all circumstances!
|
|
|
|
*Note:* In order to thwart *brute force* attacks, it is recommended to choose a "random" password that is at least 12 characters in length and that consists of upper-case characters, lower-case characters, digits as well as other "special" characters.
|
|
|
|
***Exceptions:***
|
|
|
|
* Throws `std::runtime_error`, if the nonce could not be generated, or if the SlunkCrypt context could not be allocated.
|
|
|
|
#### Encryptor::process() [1]
|
|
|
|
Encrypt the next message chunk, using separate input/output buffers.
|
|
|
|
bool process(
|
|
const uint8_t *const input,
|
|
uint8_t *const output,
|
|
size_t length
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `input`
|
|
A pointer to the *input* buffer containing the next chunk of the plaintext to be encrypted. The plaintext is given as a byte array (`uint8_t`). This can be arbitrary binary data, e.g. UTF-8 encoded text. NULL bytes are **not** treated specially.
|
|
|
|
The *input* buffer must contain *at least* `length` bytes of data. If the buffer is longer than `length` bytes, then only the first `length` bytes will be processed and the remainder is ignored!
|
|
|
|
* `output`
|
|
A pointer to the *output* buffer where the ciphertext chunk that corresponds to the given plaintext chunk will be stored. The ciphertext is stored as a byte array (`uint8_t`); it has the same length as the plaintext data.
|
|
|
|
The *output* buffer must provide sufficient space for storing *at least* `length` bytes of encrypted data. If the buffer is longer than `length` bytes, then only the first `length` bytes of the buffer will be filled with encrypted data!
|
|
|
|
* `length`
|
|
The length of the plaintext chunk contained in the *input* buffer given by the `input` parameter, in bytes. At the same time, this determines the minimum required size of the *output* buffer given by the `output` parameters, in bytes.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `true` is returned; otherwise `false` is returned.
|
|
|
|
#### Encryptor::process() [2]
|
|
|
|
Encrypt the next message chunk, using separate input/output containers (`std::vector`).
|
|
|
|
bool process(
|
|
const std::vector<uint8_t> &input,
|
|
std::vector<uint8_t> &output
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `input`
|
|
A reference to the `std::vector<uint8_t>` instance containing the next chunk of the plaintext to be encrypted. This can be arbitrary binary data, e.g. UTF-8 encoded text. NULL bytes are **not** treated specially.
|
|
|
|
* `output`
|
|
A reference to the `std::vector<uint8_t>` instance where the ciphertext that corresponds to the given plaintext will be stored.
|
|
|
|
The `output.size()` must be *greater than or equal* to `input.size()`. If the `output.size()` is larger than the `input.size()`, then only the first `input.size()` elements of `output` will be filled with encrypted data!
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `true` is returned; otherwise `false` is returned. The function fails, if the *output* `std::vector` is too small.
|
|
|
|
#### Encryptor::inplace() [1]
|
|
|
|
Encrypt the next message chunk, using a single buffer.
|
|
|
|
bool inplace(
|
|
uint8_t *const buffer,
|
|
size_t length
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `buffer`
|
|
A pointer to the buffer initially containing the next chunk of the plaintext to be encrypted. The plaintext is given as a byte array (`uint8_t`). This can be arbitrary binary data, e.g. UTF-8 encoded text. NULL bytes are **not** treated specially. The ciphertext chunk that corresponds to the given plaintext chunk will be stored to the *same* buffer, thus replacing the plaintext data.
|
|
|
|
The buffer must initially contain *at least* `length` bytes of input data; the first `length` bytes of the buffer will be overwritten with the encrypted data. If the buffer is longer than `length` bytes, then only the first `length` bytes will be processed and overwritten.
|
|
|
|
* `length`
|
|
The length of the plaintext chunk initially contained in the input/output buffer given by the `buffer` parameter, in bytes. At the same time, this determines the portion of the input/output buffer that will be overwritten with encrypted data, in bytes.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `true` is returned; otherwise `false` is returned.
|
|
|
|
#### Encryptor::inplace() [2]
|
|
|
|
Encrypt the next message chunk, using a single container (`std::vector`).
|
|
|
|
bool inplace(
|
|
std::vector<uint8_t> &buffer
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `buffer`
|
|
A reference to the `std::vector<uint8_t>` initially containing the next chunk of the plaintext to be encrypted. This can be arbitrary binary data, e.g. UTF-8 encoded text. NULL bytes are **not** treated specially. The ciphertext chunk that corresponds to the given plaintext chunk will be stored to the *same* `std::vector<uint8_t>`, thus replacing all the plaintext data.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `true` is returned; otherwise `false` is returned.
|
|
|
|
#### Encryptor::get_nonce()
|
|
|
|
Retrieve the random nonce that is used to encrypt the message.
|
|
|
|
uint64_t get_nonce();
|
|
|
|
***Return value:***
|
|
|
|
* Returns the nonce that is used to encrypt the message. The purpose of the nonce is to ensure that each message will be encrypted differently, even when the same password is used to encrypt multiple (possibly identical) messages. Therefore, a new random nonce must be chosen for each message! It is not necessary to keep the nonce confidential, but the same nonce must be used for both, encryption and decryption. Typically, the nonce is stored/transmitted alongside the ciphertext.
|
|
|
|
*Note:* The `Encryptor` class automatically generates a new, random nonce for each message to be encrypted. Use *this* function to retrieve that nonce, so that it can be passed to `Decryptor` for decryption later.
|
|
|
|
### Decryptor
|
|
|
|
Class for *decrypting* data using the SlunkCrypt library.
|
|
|
|
#### Constructor
|
|
|
|
Create and initialize a new **``Decryptor``** instance.
|
|
|
|
Decryptor::Decryptor(
|
|
const uint64_t nonce,
|
|
const std::string &passwd
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `nonce`
|
|
The *nonce* (number used once) to be used for the decryption process. The purpose of the nonce is to ensure that each message will be encrypted differently, even when the same password is used to encrypt *multiple* (possibly identical) messages. Therefore, a new *random* nonce **must** be chosen for each message! It is *not* necessary to keep the nonce confidential, but the same nonce **must** be used for both, encryption *and* decryption. Typically, the nonce is stored/transmitted alongside the ciphertext.
|
|
|
|
*Note:* The `Encryptor` class automatically generates a new, random nonce for each message to be encrypted. Use `Encryptor::get_nonce()` to retrieve that nonce, so that it can be passed to `Decryptor` for decryption later.
|
|
|
|
* `passwd`
|
|
The password to "protect" the message. The password is given as an `std::string`, e.g. UTF-8 encoded characters. The same password **may** be used to encrypt *multiple* messages. Also, the same password **must** be used for both, encryption *and* decryption; it will *only* be possible decrypt the ciphertext, if the "correct" password is known. The password must be kept confidential under all circumstances!
|
|
|
|
*Note:* In order to thwart *brute force* attacks, it is recommended to choose a "random" password that is at least 12 characters in length and that consists of upper-case characters, lower-case characters, digits as well as other "special" characters.
|
|
|
|
***Exceptions:***
|
|
|
|
* Throws `std::runtime_error`, if the SlunkCrypt context could not be allocated.
|
|
|
|
#### Decryptor::process() [1]
|
|
|
|
Decrypt the next message chunk, using separate input/output buffers.
|
|
|
|
bool process(
|
|
const uint8_t *const input,
|
|
uint8_t *const output,
|
|
size_t length
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `input`
|
|
A pointer to the *input* buffer containing the next chunk of the ciphertext to be decrypted. The ciphertext is given as a byte array (`uint8_t`).
|
|
|
|
The *input* buffer must contain *at least* `length` bytes of data. If the buffer is longer than `length` bytes, then only the first `length` bytes will be processed and the remainder is ignored!
|
|
|
|
* `output`
|
|
A pointer to the *output* buffer where the plaintext chunk that corresponds to the given ciphertext chunk will be stored. The plaintext is stored as a byte array (`uint8_t`); it has the same length as the ciphertext data.
|
|
|
|
The *output* buffer must provide sufficient space for storing *at least* `length` bytes of decrypted data. If the buffer is longer than `length` bytes, then only the first `length` bytes of the buffer will be filled with decrypted data!
|
|
|
|
* `length`
|
|
The length of the ciphertext chunk contained in the *input* buffer given by the `input` parameter, in bytes. At the same time, this determines the minimum required size of the *output* buffer given by the `output` parameters, in bytes.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `true` is returned; otherwise `false` is returned.
|
|
|
|
#### Decryptor::process() [2]
|
|
|
|
Decrypt the next message chunk, using separate input/output containers (`std::vector`).
|
|
|
|
bool process(
|
|
const std::vector<uint8_t> &input,
|
|
std::vector<uint8_t> &output
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `input`
|
|
A reference to the `std::vector<uint8_t>` instance containing the next chunk of the ciphertext to be decrypted.
|
|
|
|
* `output`
|
|
A reference to the `std::vector<uint8_t>` instance where the plaintext that corresponds to the given ciphertext will be stored.
|
|
|
|
The `output.size()` must be *greater than or equal* to `input.size()`. If the `output.size()` is greater than the `input.size()`, then only the first `input.size()` elements of `output` will be filled with decrypted data!
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `true` is returned; otherwise `false` is returned. The function fails, if the *output* `std::vector` is too small.
|
|
|
|
#### Decryptor::inplace() [1]
|
|
|
|
Decrypt the next message chunk, using a single buffer.
|
|
|
|
bool inplace(
|
|
uint8_t *const buffer,
|
|
size_t length
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `buffer`
|
|
A pointer to the buffer initially containing the next chunk of the ciphertext to be decrypted. The ciphertext is given as a byte array (`uint8_t`). The plaintext that corresponds to the given ciphertext will be stored to the *same* buffer, replacing the plaintext data.
|
|
|
|
The buffer must initially contain *at least* `length` bytes of input data; the first `length` bytes of the buffer will be overwritten with the encrypted data. If the buffer is longer than `length` bytes, then only the first `length` bytes will be processed and overwritten.
|
|
|
|
* `length`
|
|
The length of the ciphertext chunk initially contained in the input/output buffer given by the `buffer` parameter, in bytes. At the same time, this determines the portion of the input/output buffer that will be overwritten with decrypted data, in bytes.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `true` is returned; otherwise `false` is returned.
|
|
|
|
#### Decryptor::inplace() [2]
|
|
|
|
Decrypt the next message chunk, using a single container (`std::vector`).
|
|
|
|
bool inplace(
|
|
std::vector<uint8_t> &buffer
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `buffer`
|
|
A reference to the `std::vector<uint8_t>` initially containing the next chunk of the ciphertext to be decrypted. The plaintext that corresponds to the given ciphertext will be stored to the *same* `std::vector<uint8_t>`, replacing all the ciphertext data.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `true` is returned; otherwise `false` is returned.
|
|
|
|
C99 API
|
|
-------
|
|
|
|
This section describes the "low-level" C99 API of the SlunkCypt library.
|
|
|
|
### Functions
|
|
|
|
The SlunkCypt library defines the following functions:
|
|
|
|
#### slunkcrypt_alloc()
|
|
|
|
Allocate and initialize a new SlunkCrypt encryption/decryption context.
|
|
|
|
slunkcrypt_t slunkcrypt_alloc(
|
|
const uint64_t nonce,
|
|
const uint8_t *const passwd,
|
|
const size_t passwd_len,
|
|
const int mode
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `nonce`
|
|
The *nonce* (number used once) to be used for the encryption/decryption process. The purpose of the nonce is to ensure that each message will be encrypted differently, even when the same password is used to encrypt *multiple* (possibly identical) messages. Therefore, a new *random* nonce **must** be chosen for each message to be encrypted! It is *not* necessary to keep the nonce confidential, but the same nonce **must** be used for both, encryption *and* decryption. Typically, the nonce is stored/transmitted alongside the ciphertext.
|
|
|
|
*Note:* It is recommended to generate a random nonce via the [`slunkcrypt_generate_nonce()`](#slunkcrypt_generate_nonce) function for each message!
|
|
|
|
* `passwd`
|
|
The password to "protect" the message. The password is given as a byte array (`uint8_t`), e.g. UTF-8 encoded characters; a terminating NULL character is *not* required, as the length of the password is specified explicitly. The same password **may** be used to encrypt *multiple* messages. Also, the same password **must** be used for both, encryption *and* decryption; it will *only* be possible decrypt the ciphertext, if the "correct" password is known. The password must be kept confidential under all circumstances!
|
|
|
|
*Note:* In order to thwart *brute force* attacks, it is recommended to choose a "random" password that is at least 12 characters in length and that consists of upper-case characters, lower-case characters, digits as well as other "special" characters.
|
|
|
|
* `passwd_len`
|
|
The length of password given by the `passwd` parameter, in bytes, **not** counting a terminating NULL character. The minimum/maximum length of the password are given by the `SLUNKCRYPT_PWDLEN_MIN` and `SLUNKCRYPT_PWDLEN_MAX` constants, respectively.
|
|
|
|
* `mode`
|
|
The mode of operation. Use `SLUNKCRYPT_ENCRYPT` in order to set up this context for *encryption*, or use `SLUNKCRYPT_DECRYPT` in order to set up this context for *decryption*.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, a handle to the new SlunkCrypt context is return; otherwise `SLUNKCRYPT_NULL` is returned.
|
|
|
|
*Note:* Applications **should** treat `slunkcrypt_t` as an *opaque* handle type. Also, as soon as the SlunkCrypt context is *not* needed anymore, the application **shall** call [`slunkcrypt_free()`](#slunkcrypt_free) in order to "erase" and de-allocate that context. If a SlunkCrypt context is *not* de-allocated properly, it will result in a memory leak!
|
|
|
|
#### slunkcrypt_reset()
|
|
|
|
Re-initialize an existing SlunkCrypt encryption/decryption context.
|
|
|
|
int slunkcrypt_reset(
|
|
const slunkcrypt_t context,
|
|
const uint64_t nonce,
|
|
const uint8_t *const passwd,
|
|
const size_t passwd_len,
|
|
const int mode
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `context`
|
|
The existing SlunkCrypt context to be re-initialized. This must be a valid handle that was returned by a previous invocation of the [`slunkcrypt_alloc()`](#slunkcrypt_alloc) function.
|
|
|
|
* *other parameters*:
|
|
Please refer to the [`slunkcrypt_alloc()`](#slunkcrypt_alloc) function for details!
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `SLUNKCRYPT_SUCCESS` is returned; otherwise `SLUNKCRYPT_FAILURE` or `SLUNKCRYPT_ABORTED` is returned.
|
|
|
|
#### slunkcrypt_free()
|
|
|
|
De-allocate an existing SlunkCrypt encryption/decryption context. This will "clear" and release any memory occupied by the context.
|
|
|
|
void slunkcrypt_free(
|
|
const slunkcrypt_t context
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `context`
|
|
The existing SlunkCrypt context to be de-allocated. This must be a valid handle that was returned by a previous invocation of the [`slunkcrypt_alloc()`](#slunkcrypt_alloc) function.
|
|
|
|
*Note:* Once a handle has been passed to this function, that handle is *invalidated* and **must not** be used again!
|
|
|
|
#### slunkcrypt_generate_nonce()
|
|
|
|
Generate a new random *nonce* (number used once), using the system's "cryptographically secure" entropy source.
|
|
|
|
int slunkcrypt_generate_nonce(
|
|
int64_t *const nonce
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `nonce`
|
|
A pointer to a variable of type `int64_t` that receives the new random nonce.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `SLUNKCRYPT_SUCCESS` is returned; otherwise `SLUNKCRYPT_FAILURE` or `SLUNKCRYPT_ABORTED` is returned.
|
|
|
|
#### slunkcrypt_process()
|
|
|
|
Encrypt or decrypt the next message chunk, using separate input/output buffers.
|
|
|
|
int slunkcrypt_process(
|
|
const slunkcrypt_t context,
|
|
const uint8_t *const input,
|
|
uint8_t *const output,
|
|
size_t length
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `context`
|
|
The existing SlunkCrypt context to be used for processing the message chunk. This context will be updated.
|
|
|
|
*Note:* This function operates either in "encryption" mode or in "decryption" mode, depending on how the given SlunkCrypt context has been [initialized](#slunkcrypt_alloc) or [re-initialized](#slunkcrypt_reset).
|
|
|
|
* `input`
|
|
A pointer to the *input* buffer containing the next chunk of the plaintext to be encrypted (*encryption* mode), or the next chunk of the ciphertext to be decrypted (*decryption* mode). The input data is given as a byte array (`uint8_t`). This can be arbitrary binary data, e.g. UTF-8 encoded text. NULL bytes are **not** treated specially by this function.
|
|
|
|
The *input* buffer must contain *at least* `length` bytes of data. If the buffer is longer than `length` bytes, then only the first `length` bytes will be processed; the remainder is ignored!
|
|
|
|
* `output`
|
|
A pointer to the *output* buffer where the ciphertext that corresponds to the given plaintext chunk (*encryption* mode), or the plaintext that corresponds to the given ciphertext chunk (*decryption* mode) will be stored. The output data is stored as a byte array (`uint8_t`) and it always has the same length as the input data.
|
|
|
|
The *output* buffer **must** provide sufficient space for storing *at least* `length` bytes. If the buffer is longer than `length` bytes, then only the first `length` bytes will be filled!
|
|
|
|
* `length`
|
|
The length of the given plaintext chunk (*encryption* mode), or the length of the given ciphertext chunk (*decryption* mode) in the `input` buffer, in bytes. At the same time, this parameter determines the minimum required size of the `output` buffer, in bytes. If this parameter is set to *zero*, the function does nothing; this is *not* considered an error.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `SLUNKCRYPT_SUCCESS` is returned; otherwise `SLUNKCRYPT_FAILURE` or `SLUNKCRYPT_ABORTED` is returned.
|
|
|
|
#### slunkcrypt_inplace()
|
|
|
|
Encrypt or decrypt the next message chunk, using a *single* input/output buffer.
|
|
|
|
int slunkcrypt_inplace(
|
|
const slunkcrypt_t context,
|
|
uint8_t *const buffer,
|
|
size_t length
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `context`
|
|
The existing SlunkCrypt context to be used for processing the message chunk. This context will be updated.
|
|
|
|
*Note:* This function operates either in "encryption" mode or in "decryption" mode, depending on how the given SlunkCrypt context has been [initialized](#slunkcrypt_alloc) or [re-initialized](#slunkcrypt_reset).
|
|
|
|
* `buffer`
|
|
A pointer to the buffer containing the next chunk of the plaintext to be encrypted (*encryption* mode), or the next chunk of the ciphertext to be decrypted (*decryption* mode). The input data is given as a byte array (`uint8_t`). This can be arbitrary binary data, e.g. UTF-8 encoded text. NULL bytes are **not** treated specially by this function. The ciphertext that corresponds to the given plaintext chunk (*encryption* mode), or the plaintext that corresponds to the given ciphertext chunk (*decryption* mode) will be stored to the ***same*** buffer.
|
|
|
|
The given buffer **must** initially contain *at least* `length` bytes of input data. The first `length` bytes in the buffer will be processed and will be *overwritten* with the corresponding output data. If the buffer is longer than `length` bytes, then only the first `length` bytes in the buffer will be processed; the remainder is ignored!
|
|
|
|
* `length`
|
|
The length of the plaintext chunk (*encryption* mode), or the length of the ciphertext chunk (*decryption* mode) initially contained in the input/output buffer, in bytes.
|
|
|
|
***Return value:***
|
|
|
|
* If successful, `SLUNKCRYPT_SUCCESS` is returned; otherwise `SLUNKCRYPT_FAILURE` or `SLUNKCRYPT_ABORTED` is returned.
|
|
|
|
#### slunkcrypt_random_bytes()
|
|
|
|
Generate a sequence of random bytes, using the system's "cryptographically secure" entropy source.
|
|
|
|
size_t slunkcrypt_random_bytes(
|
|
uint8_t *const buffer,
|
|
const size_t length
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `buffer`
|
|
A pointer to the *output* buffer where the random bytes will be stored.
|
|
|
|
The *output* buffer **must** provide sufficient space for storing *at least* `length` bytes of random data. *At most* the first `length` bytes of the buffer will be filled with random data!
|
|
|
|
* `length`
|
|
The number of random bytes to be generated. At the same time, this parameter determines the minimum required size of the `output` buffer, in bytes.
|
|
|
|
***Return value:***
|
|
|
|
* The number of random bytes that have been generated and that have been stored to the `output` buffer buffer is returned.
|
|
|
|
The number of generated random bytes can be *at most* `length`. Less than `length` random bytes will be generated, if and only if the the system's "cryptographically secure" entropy source could *not* provide the requested number of bytes at this time – in that case, you can try again later. The number of generated bytes can be as low as **0**.
|
|
|
|
|
|
#### slunkcrypt_bzero()
|
|
|
|
Erase the contents of a byte array, by overwriting it with *zero* bytes. Compiler optimizations will **not** remove the erase operation.
|
|
|
|
void slunkcrypt_bzero(
|
|
void *const buffer,
|
|
const size_t length
|
|
);
|
|
|
|
***Parameters:***
|
|
|
|
* `buffer`
|
|
A pointer to the buffer whose content is to be erased.
|
|
|
|
The buffer **must** be *at least* `length` bytes in size. If the buffer is longer than `length` bytes, then *only* the first `length` bytes of the buffer will be erased!
|
|
|
|
* `length`
|
|
The size of the buffer to be erased, in bytes.
|
|
|
|
### Global variables
|
|
|
|
The SlunkCypt library defines the following global variables:
|
|
|
|
#### Version information
|
|
|
|
These variables can be used to determine the version of the SlunkCrypt library at runtime, using the [semantic versioning](https://semver.org/spec/v2.0.0.html) scheme:
|
|
|
|
* `const uint16_t SLUNKCRYPT_VERSION_MAJOR` – The current *major* version.
|
|
* `const uint16_t SLUNKCRYPT_VERSION_MINOR` – The current *minor* version.
|
|
* `const uint16_t SLUNKCRYPT_VERSION_PATCH` – The current *patch* version.
|
|
* `const char *SLUNKCRYPT_BUILD` – The build date and time, as a C string, in the *"mmm dd yyyy hh:mm:ss"* format.
|
|
|
|
#### Abort request
|
|
|
|
If this flag is set to a non-zero value by the application, any ongoing SlunkCypt library invocation will be aborted as quickly as possible:
|
|
|
|
* `volatile int g_slunkcrypt_abort_flag` – The global abort flag.
|
|
|
|
*Note:* Applications may set this flag in their signal handler, e.g. when a `SIGINT` is received, in order to "gracefully" shut down the SlunkCypt library. All long-running library functions will return `SLUNKCRYPT_ABORTED`, if the invocation was interrupted. The application still is responsible for [free](#slunkcrypt_free)'ing any SlunkCypt contexts that it allocated successfully!
|
|
|
|
### Constants
|
|
|
|
The SlunkCypt library defines the following constants:
|
|
|
|
#### Mode of operation
|
|
|
|
The SlunkCypt library supports the following modes of operation:
|
|
|
|
* `SLUNKCRYPT_ENCRYPT` – Run library in *encryption* mode, i.e. consume plaintext and produce ciphertext.
|
|
* `SLUNKCRYPT_DECRYPT` – Run library in *decryption* mode, i.e. consume ciphertext and produce plaintext.
|
|
|
|
#### Limits
|
|
|
|
The following limits are defined for the SlunkCypt library:
|
|
|
|
* `SLUNKCRYPT_PWDLEN_MIN` – The *minimum* required length of a password, currently **8** bytes.
|
|
* `SLUNKCRYPT_PWDLEN_MAX` – The *maximum* allowed length of a password, currently **256** bytes.
|
|
|
|
#### Error codes
|
|
|
|
SlunkCypt library functions that return an error code may return one of the following constants:
|
|
|
|
* `SLUNKCRYPT_SUCCESS` – The operation completed successfully.
|
|
* `SLUNKCRYPT_FAILURE` – The operation has failed.
|
|
* `SLUNKCRYPT_ABORTED` – The operation was aborted before completion, as requested by the application.
|
|
|
|
Thread safety
|
|
-------------
|
|
|
|
The following functions are fully "thread-safe" and thus may safely be called by *any* thread at *any* time ***without*** the need for synchronization:
|
|
|
|
* `slunkcrypt_alloc()`
|
|
* `slunkcrypt_generate_nonce()`
|
|
* `slunkcrypt_random_bytes()`
|
|
* `slunkcrypt_bzero()`
|
|
* `Encryptor::Encryptor()`
|
|
* `Decryptor::Decryptor()`
|
|
|
|
The following functions are "reentrant" and thus may safely be called by *any* thread at *any* time ***without*** the need for synchronization – provided that each instance of `slunkcrypt_t`, `Encryptor` or `Decryptor` is "owned" by a *single* thread **and** that each instance will *exclusively* be access by its respective "owner" thread:
|
|
|
|
* `slunkcrypt_reset()`
|
|
* `slunkcrypt_free()`
|
|
* `slunkcrypt_process()`
|
|
* `slunkcrypt_inplace()`
|
|
* `Encryptor::process()`
|
|
* `Encryptor::inplace()`
|
|
* `Encryptor::get_nonce()`
|
|
* `Decryptor::process()`
|
|
* `Decryptor::inplace()`
|
|
|
|
***Note:*** If the same `slunkcrypt_t`, `Encryptor` or `Decryptor` instance needs to be shared across *multiple* threads (i.e. the same instance is accessed by *concurrent* threads), then the application **must** *serialize* any invocation of the above functions on that shared instance, by using a suitable synchronization mechanism! This can be achieved by using a [*mutex*](https://linux.die.net/man/3/pthread_mutex_lock).
|
|
|
|
|
|
License
|
|
=======
|
|
|
|
This work has been released under the **CC0 1.0 Universal** license.
|
|
|
|
For details, please refer to:
|
|
<https://creativecommons.org/publicdomain/zero/1.0/legalcode>
|