LibAscon 1.2.1
Lightweight Authenticated Encryption & Hashing, also with Init-Update-Final paradigm.
LibAscon - Lightweight Authenticated Encryption & Hashing

Build Status Master Build Status Develop GitHub release (latest by date) GitHub

LibAscon is an ISO C99/C11 cryptographic library wrapping the reference C implementation of the Ascon family of lightweight authenticated encryption schemes with associated data (AEAD) and hashing functions, but it also includes Init-Update-Final processing and variable tag length. Heavily tested and ready for embedded systems!

Security disclaimer

This is not a security-hardened implementation, just a simple one, focused mostly on usability, portability, and high(er) set of features compared to the reference implementation. There is no explicit protection against side-channel attacks other than what the Ascon algorithm itself provides by design.

Nevertheless, this implementation:

  • uses constant-time operations (should help against timing attacks),
  • tries to force the compiler to actually clear sensitive data instead of optimising the operations away (this is hard to achieve properly),
  • has 100% line and 100% branch test coverage.

Features

LibAscon provides:

  • 3 AEAD ciphers:
    • Ascon128 v1.2 (128 bit key, 64 bit rate)
    • Ascon128a v1.2 (128 bit key, 128 bit rate)
    • Ascon80pq v1.2 (160 bit key, 64 bit rate)
  • 4 hashing functions
    • Ascon-Hash v1.2 (fixed-length output, 12-rounds absorption/squeeze)
    • Ascon-XOF v1.2 (variable-length output, 12-rounds absorption/squeeze)
    • Ascon-Hasha v1.2 (fixed-length output, 8-rounds absorption/squeeze)
    • Ascon-XOFa v1.2 (variable-length output, 8-rounds absorption/squeeze)
  • Online processing (Init-Update-Final paradigm) for hashing and encryption/decryption. This means that the data can be processed one chunk at the time. Useful when operating on large amounts of data that are not available contiguously in memory, e.g. a too-large file or data in transmission.
  • Offline processing (whole data contiguous in memory) is also available with a simple wrapper.
  • Variable tag length for authenticated encryption: can generate any tag length. Of course at least 16 bytes (128 bits) is recommended.

    Note: the tag bytes above 16 bytes are an extension of the original Ascon algorithm using the same sponge squeezing technique as for the XOF;

  • Encryption/decryption can be performed in-place, without the need of a second output buffer.
  • AEAD tag may be provided to a separate location, i.e. not concatenated to the ciphertext.
  • Same performance as the original reference (unoptimised) C implementation in Release mode, about 2x slower in MinSizeRel mode.
  • Hashing functions that can also automatically validate the digest against a known one when the hashing process is completed.
  • A heavily documented developer-friendly API, making it easier to compile and add to your project, both through static and dynamic inclusion.
  • Tested with 100% line and branch* coverage, with CI running on Linux, macOS and Windows with GCC, Clang and CL (MSVC) (*: branch coverage excludes the debugging-asserts).

Usage example

En/decrypting using Ascon128's Init-Update-Final API

#include "ascon.h"
// Initialisation
// We need the key and the nonce, both 128 bits.
// Note: Ascon80pq uses longer keys
const uint8_t secret_key[ASCON_AEAD128_KEY_LEN] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6
};
const uint8_t unique_nonce[ASCON_AEAD_NONCE_LEN] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6
};
ascon_aead128_init(&ctx, secret_key, unique_nonce);
// Now we feed any associated data into the cipher first
// Our data is fragmented into 2 parts, so we feed one at the time.
const char associated_data_pt1[] = "2 messages will foll";
const char associated_data_pt2[] = "ow, but they are both secret.";
ascon_aead128_assoc_data_update(&ctx, (uint8_t*) associated_data_pt1, strlen
(associated_data_pt1));
ascon_aead128_assoc_data_update(&ctx, (uint8_t*) associated_data_pt2, strlen
(associated_data_pt2));
// Next, we feed the plaintext, which is also fragmented in 2 parts.
const char plaintext_pt1[] = "Hello, I'm a secret mes";
const char plaintext_pt2[] = "sage and I should be encrypted!";
uint8_t buffer[100];
// The ciphertext is generated block-wise, so we need the return value
// to know how to offset the pointer to where the next ciphertext
// part should be written.
size_t ciphertext_len = 0;
ciphertext_len += ascon_aead128_encrypt_update(
&ctx, buffer + ciphertext_len,
(uint8_t*) plaintext_pt1, strlen(plaintext_pt1));
ciphertext_len += ascon_aead128_encrypt_update(
&ctx, buffer + ciphertext_len,
(uint8_t*) plaintext_pt2, strlen(plaintext_pt2));
// Finally, we wrap up the encryption and generate the tag.
// There may still be some trailing ciphertext to be produced.
// The tag length can be specified. ASCON_AEAD_TAG_MIN_SECURE_LEN is
// the minimum recommended (128 b)
ciphertext_len += ascon_aead128_encrypt_final(
&ctx, buffer + ciphertext_len,
tag, sizeof(tag));
// The final function zeroes out the context automatically.
// Now the buffer contains our ciphertext, long ciphertext_len bytes.
// Now we can decrypt, reusing the same key, nonce and associated data
ascon_aead128_init(&ctx, secret_key, unique_nonce);
ascon_aead128_assoc_data_update(&ctx, (uint8_t*) associated_data_pt1,
strlen(associated_data_pt1));
ascon_aead128_assoc_data_update(&ctx, (uint8_t*) associated_data_pt2,
strlen(associated_data_pt2));
// This time, we perform the decryption in-place, in the same buffer
// where the ciphertext it: to do so, we pass the same pointer for
// plaintext and ciphertext.
size_t plaintext_len = 0;
&ctx, buffer,
buffer, ciphertext_len);
// The final decryption step automatically checks the tag
bool is_tag_valid = false;
plaintext_len += ascon_aead128_decrypt_final(
&ctx, buffer + plaintext_len,
&is_tag_valid, tag, sizeof(tag));
// The final function zeroes out the context automatically.
buffer[plaintext_len] = '\0'; // Null terminated, because it's text
printf("Decrypted msg: %s, tag is valid: %d\n", buffer, is_tag_valid);
// The macros ASCON_TAG_OK=true and ASCON_TAG_INVALID=false are also
// available if you prefer them over booleans for is_tag_valid.
Ascon cipher - Lightweight Authenticated Encryption & Hashing, also with Init-Update-Final paradigm.
#define ASCON_AEAD_TAG_MIN_SECURE_LEN
Minimum recommended length in bytes of the authentication tag generated by the authenticated encrypti...
Definition: ascon.h:131
ASCON_API void ascon_aead128_init(ascon_aead_ctx_t *ctx, const uint8_t key[ASCON_AEAD128_KEY_LEN], const uint8_t nonce[ASCON_AEAD_NONCE_LEN])
Online symmetric encryption/decryption using Ascon128, initialisation.
ASCON_API void ascon_aead128_assoc_data_update(ascon_aead_ctx_t *ctx, const uint8_t *assoc_data, size_t assoc_data_len)
Online symmetric encryption/decryption using Ascon128, feeding associated data.
#define ASCON_AEAD128_KEY_LEN
Length in bytes of the secret symmetric key used for the Ascon128 cipher.
Definition: ascon.h:109
ASCON_API size_t ascon_aead128_decrypt_update(ascon_aead_ctx_t *ctx, uint8_t *plaintext, const uint8_t *ciphertext, size_t ciphertext_len)
Online symmetric decryption using Ascon128, feeding ciphertext and getting plaintext.
ASCON_API size_t ascon_aead128_encrypt_final(ascon_aead_ctx_t *ctx, uint8_t *ciphertext, uint8_t *tag, size_t tag_len)
Online symmetric encryption using Ascon128, finalisation and tag generation.
ASCON_API size_t ascon_aead128_encrypt_update(ascon_aead_ctx_t *ctx, uint8_t *ciphertext, const uint8_t *plaintext, size_t plaintext_len)
Online symmetric encryption using Ascon128, feeding plaintext and getting ciphertext.
ASCON_API size_t ascon_aead128_decrypt_final(ascon_aead_ctx_t *ctx, uint8_t *plaintext, bool *is_tag_valid, const uint8_t *expected_tag, size_t expected_tag_len)
Online symmetric decryption using Ascon128, finalisation and tag validation.
#define ASCON_AEAD_NONCE_LEN
Length in bytes of the public nonce used for authenticated encryption and decryption.
Definition: ascon.h:125
Cipher context for authenticated encryption and validated decryption.
Definition: ascon.h:230

Encrypting using Ascon128's offline API for contiguous data

// We need the key and the nonce, both 128 bits.
// Note: Ascon80pq uses longer keys
const uint8_t secret_key[ASCON_AEAD128_KEY_LEN] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6
};
const uint8_t unique_nonce[ASCON_AEAD_NONCE_LEN] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6
};
// For the "offline" operation, we need all data to be already contiguous
// in memory. The operation is just one Ascon function call for the user.
const char associated_data[] = "1 contiguous message will follow.";
const char plaintext[] = "Hello, I'm a secret message and I should be encrypted!";
uint8_t buffer[100];
uint8_t tag[42];
// To showcase the LibAscon extensions, we will generate an arbitrary-length
// tag. Here is 42 bytes, but shorter-than-default (16) tags are also
// possible e.g. 12 bytes for systems with heavy limitations.
tag,
secret_key,
unique_nonce,
(uint8_t*) associated_data,
(uint8_t*) plaintext,
strlen(associated_data),
strlen(plaintext),
sizeof(tag));
// The function zeroes out the context automatically.
// The decryption looks almost the same. Just for fun, we will again
// reuse the same buffer where the ciphertext is to write the plaintext into.
bool is_tag_valid = ascon_aead128_decrypt(buffer, // Output plaintext
secret_key,
unique_nonce,
(uint8_t*) associated_data,
(uint8_t*) buffer, // Input ciphertext,
tag, // Expected tag the ciphertext comes with
strlen(associated_data),
strlen(plaintext),
sizeof(tag));
// This time we get a boolean as a return value, which is true when
// the tag is OK. To avoid confusion, it can also be compared to
// two handy macros
if (is_tag_valid == ASCON_TAG_OK) {
puts("Correct decryption!");
} else { // ASCON_TAG_INVALID
puts("Something went wrong!");
}
// The function zeroes out the context automatically.
ASCON_API bool ascon_aead128_decrypt(uint8_t *plaintext, const uint8_t key[ASCON_AEAD128_KEY_LEN], const uint8_t nonce[ASCON_AEAD_NONCE_LEN], const uint8_t *assoc_data, const uint8_t *ciphertext, const uint8_t *expected_tag, size_t assoc_data_len, size_t ciphertext_len, size_t expected_tag_len)
Offline symmetric decryption using Ascon128.
ASCON_API void ascon_aead128_encrypt(uint8_t *ciphertext, uint8_t *tag, const uint8_t key[ASCON_AEAD128_KEY_LEN], const uint8_t nonce[ASCON_AEAD_NONCE_LEN], const uint8_t *assoc_data, const uint8_t *plaintext, size_t assoc_data_len, size_t plaintext_len, size_t tag_len)
Offline symmetric encryption using Ascon128.
#define ASCON_TAG_OK
The tag is valid thus the associated data and ciphertext were intact.
Definition: ascon.h:161

Keyed hashing using Ascon-XOF's Init-Update-Final API

// We want to authenticate a message with a keyed hash and transmit it.
const uint8_t secret_key[] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6
};
const char message_pt1[] = "Hello, I'm some data we need the digest of";
const char message_pt2[] = " but I'm very long and I was not transmit";
const char message_pt3[] = "ted in one contiguous block.";
// Preparing the Ascon-XOF function. The XOF allows arbitrary digest size,
// while Ascon-Hash (ascon_hash_init) has a fixed digest length.
// NOTE: Ascon-Hash and Ascon-XOF produce different outputs!
// If we want a Message Authentication Code that also proves authenticity,
// not only integrity of the message, we can simply prepend the key to the
// message to hash, just like we would for SHA-3. Length extension attacks
// are not a problem for Ascon-Hash/XOF, so the HMAC scheme is not needed.
ascon_hash_xof_update(&ctx, (uint8_t*) secret_key, sizeof(secret_key));
// Feeding chunk by chunk
ascon_hash_xof_update(&ctx, (uint8_t*) message_pt1, strlen(message_pt1));
ascon_hash_xof_update(&ctx, (uint8_t*) message_pt2, strlen(message_pt2));
ascon_hash_xof_update(&ctx, (uint8_t*) message_pt3, strlen(message_pt3));
// Finally, we get a digest of arbitrary length
uint8_t digest[21]; // Choose any length from 0 to SIZE_MAX
ascon_hash_xof_final(&ctx, digest, sizeof(digest));
// The final function zeroes out the context automatically.
// Now let's imagine we transmit the message alongside with the digest.
// The receiver also has the secret key and can easily verify the keyed hash.
ascon_hash_xof_update(&ctx, (uint8_t*) secret_key, sizeof(secret_key));
ascon_hash_xof_update(&ctx, (uint8_t*) message_pt1, strlen(message_pt1));
ascon_hash_xof_update(&ctx, (uint8_t*) message_pt2, strlen(message_pt2));
ascon_hash_xof_update(&ctx, (uint8_t*) message_pt3, strlen(message_pt3));
// A handy function computing the obtained digest and validating it
// against the obtained.
bool is_tag_valid = ascon_hash_xof_final_matches(&ctx, digest, sizeof(digest));
if (is_tag_valid == ASCON_TAG_OK) {
puts("Correct decryption!");
} else { // ASCON_TAG_INVALID
puts("Something went wrong!");
}
ASCON_API void ascon_hash_xof_init(ascon_hash_ctx_t *ctx)
Online Ascon-Hash with custom digest length (eXtendable Output Function, XOF), initialisation.
ASCON_API void ascon_hash_xof_update(ascon_hash_ctx_t *ctx, const uint8_t *data, size_t data_len)
Online Ascon-Hash with custom digest length (eXtendable Output Function, XOF), feeding data to hash.
ASCON_API bool ascon_hash_xof_final_matches(ascon_hash_ctx_t *ctx, const uint8_t *expected_digest, size_t expected_digest_len)
Online Ascon-Hash with custom digest length (eXtendable Output Function, XOF), finalisation and diges...
ASCON_API void ascon_hash_xof_final(ascon_hash_ctx_t *ctx, uint8_t *digest, size_t digest_len)
Online Ascon-Hash with custom digest length (eXtendable Output Function, XOF), finalisation and diges...
Internal cipher sponge state associated with a buffer holding for less-than-rate updates.
Definition: ascon.h:190

For more examples, check the test suite and how the Ascon API is used in there.

Dependencies of the library

Only the C standard library, mostly C99 features:

  • stdint.h: uint8_t, uint_fast8_t, uint64_t
  • stddef.h: size_t, NULL
  • stdbool.h: bool, true, false

Optional dependency: assert.h, for assert(). Used to add runtime checks of the Ascon API input for debugging (NULL pointers and incorrect order of function calling). The CMake script uses it only if assert.h is found and only in the debug build type (CMAKE_BUILD_TYPE=Debug). If compiled without the included CMakeLists.txt, it's not used unless ASCON_INPUT_ASSERTS is defined at compile time. The assertion function ASCON_ASSERT can also be changed, if assert() is not available.

FAQ

  • Q: May I use this library for-

    A: YES! :D The library (and also the Ascon reference implementation) is released under the Creative Commons Zero (CC0) license which basically(*) means you can do whatever you want with it. Commercial purposes, personal projects, modifications of it, anything goes.

    (*) for the legally-binding text check the license file.

  • Q: Where is the documentation?

    A: The library header file inc/ascon.h is documented with Doxygen and you can find it compiled here.

  • Q: Why should I use Ascon-AEAD instead of, say, AES?

    A: Ascon is designed to be lightweight (great for embedded systems) and natively supports authenticated encryption of data of any length, while AES must be wrapped in an AEAD mode such as AES-GCM, which is well-proven but heavier.

  • Q: Why should I use Ascon-Hash instead of, say, SHA-256?

    A: Ascon is designed to be lightweight (great for embedded systems) and does not suffer from length-extension attacks as SHA-256, given its sponge construction (similarly to what SHA-3 does). Additionally Ascon offers also the XOF hashing producing digests of variable length.

  • Q: Who designed Ascon? Who wrote this library?

    A: Check the Authors file for details.

  • Q: I don't trust Ascon.

    A: Good. You should not keep your guard up for everything, but Ascon has been selected as the primary choice for lightweight authenticated encryption in the final portfolio of the CAESAR competition (2014–2019) and is currently competing in the NIST Lightweight Cryptography competition (2019–) . Cryptographers like it and that's a good sign, right?

  • Q: I don't trust this implementation.

    A: Good, again. You can read the source code to see what it does to be sure ;) If you find any bugs or possible improvements, open a pull request or an issue. I would like to make as clear and as good as possible.

Known limitations

  • There is no architecture-specific optimisation, only a generic portable implementation using mostly uint64_t data types.

Compiling

The project's compilation has been tested with GCC, Clang and CL (MSVC). Most compiler warnings have already been mitigated, so they hopefully don't occur on your platform.

Static source inclusion

The project is relatively small, so you can simply include it into yours as a Git Subtree, Git Submodule or by simply copy-pasting the inc and src folders. Be sure to:

  • Add inc and src to the include folders list (the internal header is in src).
  • Add src to the sources folders list.
  • Compile.

Compiling Ascon into all possible targets with CMake

A note if you are compiling on Windows from the command line:

  • you will need the environment variables to use the MSVC toolchain from the command line
  • be sure to select the generator NMake Makefiles when configuring CMake

Compile an out of source build:

mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release # On Windows add: -G "NMake Makefiles"
cmake --build . --parallel 8

This will build all useful targets:

  • static libraries:
    • asconfull with full feature set
    • ascon128 with only Ascon128
    • ascon128a with only Ascon128a
    • ascon80pq with only Ascon80pq
    • asconhash with only Ascon-hash and Ascon-XOF
    • ascon128hash with only Ascon128, Ascon-hash and Ascon-XOF
    • ascon128ahash with only Ascon128a, Ascon-hash and Ascon-XOF
    • ascon80pqhash with only Ascon80pq, Ascon-hash and Ascon-XOF for a smaller build result when not all features are needed
  • ascon a shared library (.dll or .dylib or .so) with full feature set (like asconfull, but shared)
  • testascon a test runner executable , which test all features of the static library
  • testasconshared a test runner executable , which test all features of the shared library

Doxygen (if installed) is built separately to avoid recompiling it for any library change:

cmake --build . --target ascon_doxygen

To compile only a single target, for example ascon80pq, run

mkdir build && cd build
cmake .. -DCMAKE_BUILD_TYPE=Release
cmake --build . --target ascon80pq

To compile with the optimisation for size, use the -DCMAKE_BUILD_TYPE=MinSizeRel flag instead.

To run the test executables, call ctest in the build directory.