Ascon-Based Lightweight Cryptography Standards for Constrained Devices: Authenticated Encryption, Hash, and Extendable Output Functions.
This header-only C++ library implements the whole Ascon LwC cipher-suite, specifically Ascon-AEAD128, Ascon-Hash256, Ascon-XOF128, and Ascon-CXOF128. These algorithms, suitable for constrained environments, are part of the Ascon family designed for resource-limited devices, prioritizing security, performance, and efficiency. The library offers constexpr
functions where possible for enhanced performance and simplifies integration. The implementation conforms to the Ascon draft standard defined in NIST SP 800-232.
The library includes the following core Ascon cryptographic primitives:
- Ascon-AEAD128: Offers AEAD, encrypting the plaintext and authenticating both the ciphertext and associated data to ensure confidentiality and authenticity.
- Ascon-Hash256: A cryptographic hash function generating a 256-bit digest for data integrity verification and other cryptographic applications.
- Ascon-XOF128: An extendable output function (XOF) that produces variable-length outputs, useful in various cryptographic contexts where flexibility in output length is required.
- Ascon-CXOF128: A customizable XOF variant, offering additional flexibility by allowing for application-specific parameterization through a customization string.
This implementation leverages a sponge construction built upon the Ascon permutation. It employs std::span
for safe memory handling and provides constexpr
functions where feasible for optimized compile-time computations for statically known inputs.
Key Features:
- Header-Only: Simple integration; no linking required.
constexpr
Support: Compile-time evaluation for optimized performance where applicable.std::span
Usage: Type-safe memory management.- Thorough Testing: Includes property based tests and known-answer tests (KATs).
- Benchmarking Support: Prepared for benchmarking with Google Benchmark.
Important Considerations:
- Unaudited: This implementation has not yet undergone formal security audits. Production use requires careful consideration of the risks.
- Associated Data (AEAD): Associated data in Ascon-AEAD128 is authenticated but not encrypted. Confidentiality is provided only for the plaintext.
- Implementation Size: While header-only, the actual size will depend on compiler optimizations and included features.
- A C++20-compliant compiler (e.g., g++, clang++).
- Build tools:
make
andcmake
. - For testing: Google Test (Installation Instructions).
- For benchmarking: Google Benchmark (Installation Instructions).
- (Optional) For CPU cycle benchmarking: libPFM (Installation Instructions). Requires building Google Benchmark with libPFM support.
This project includes a comprehensive test suite verifying the functional correctness of Ascon-AEAD128, Ascon-Hash256, Ascon-XOF128, and Ascon-CXOF128. Known Answer Tests (KATs) ensure conformance to the specification.
Run tests using these commands (from the repository root):
make test -j # Run release build tests
make debug_asan_test -j # Run debug tests with AddressSanitizer (memory error detection)
make release_asan_test -j # Run release tests with AddressSanitizer
make debug_ubsan_test -j # Run debug tests with UndefinedBehaviorSanitizer
make release_ubsan_test -j # Run release tests with UndefinedBehaviorSanitizer
Test results (pass/fail) are printed to the console.
PASSED TESTS (16/16):
1 ms: build/test/test.out AsconCXOF128.CompileTimeComputeXofOutput
1 ms: build/test/test.out AsconAEAD128.CompileTimeEncryptAndThenDecrypt
1 ms: build/test/test.out AsconXof128.CompileTimeComputeXofOutput
2 ms: build/test/test.out AsconHash256.CompileTimeComputeMessageDigest
4 ms: build/test/test.out AsconAEAD128.KnownAnswerTests
6 ms: build/test/test.out AsconHash256.ForSameMessageOneshotHashingAndIncrementalHashingProducesSameDigest
7 ms: build/test/test.out AsconXof128.KnownAnswerTests
8 ms: build/test/test.out AsconHash256.KnownAnswerTests
449 ms: build/test/test.out AsconAEAD128.DecryptionFailureDueToBitFlippingInAssociatedData
451 ms: build/test/test.out AsconAEAD128.DecryptionFailureDueToBitFlippingInCipherText
452 ms: build/test/test.out AsconAEAD128.DecryptionFailureDueToBitFlippingInNonce
452 ms: build/test/test.out AsconAEAD128.DecryptionFailureDueToBitFlippingInKey
453 ms: build/test/test.out AsconAEAD128.DecryptionFailureDueToBitFlippingInTag
457 ms: build/test/test.out AsconAEAD128.EncryptThenDecrypt
638 ms: build/test/test.out AsconXof128.ForSameMessageOneshotHashingAndIncrementalHashingProducesSameOutput
3126 ms: build/test/test.out AsconCXOF128.ForSameMessageOneshotHashingAndIncrementalHashingProducesSameOutput
Note
There is a help menu, which introduces you to all available commands; just run make
from the root directory of this project.
This section details how to benchmark the performance of the implemented Ascon algorithms. The benchmarks measure throughput (bytes per second) and, optionally, cycles per byte if libPFM is used.
The Makefile provides two benchmarking targets:
make benchmark
: Runs benchmarks without libPFM-based CPU cycle counting. This is the faster option and suitable for initial performance assessments.make perf
: Runs benchmarks with libPFM support, providing detailed CPU cycle counts alongside throughput. This requires building Google Benchmark with libPFM support; see the Prerequisites section for more details.
To run the benchmarks, execute the following commands from the repository root:
make benchmark -j # Run benchmarks without CPU cycle counting
make perf -j # Run benchmarks with CPU cycle counting (requires libPFM)
Caution
Ensure that you've disabled CPU frequency scaling, when benchmarking, following this guide @ https://github.com/google/benchmark/blob/main/docs/reducing_variance.md.
JSON benchmark result lives in bench_result_on_Linux_6.11.0-9-generic_x86_64_with_g++_14.
JSON benchmark result lives in bench_result_on_Linux_6.6.62+rpt-rpi-v8_aarch64_with_g++_12.
This section demonstrates how to use the Ascon header-only library for authenticated encryption (AEAD), hashing, and extendable output functions (XOFs). Remember that this implementation is unaudited, and production use requires careful consideration of the risks. No linking is required; simply include the necessary header files.
Ascon-AEAD128 provides authenticated encryption with associated data. The associated data is authenticated but not encrypted.
#include <ascon/aead/ascon_aead128.hpp>
#include <array>
#include <iostream>
int main() {
// Key, Nonce, and Associated Data
std::array<uint8_t, 16> key = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f};
std::array<uint8_t, 16> nonce = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
std::array<uint8_t, 10> ad = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
std::array<uint8_t, 10> plaintext = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19};
std::array<uint8_t, 10> ciphertext{};
std::array<uint8_t, 16> tag{};
// Encryption
ascon_aead128::encrypt(key, nonce, ad, plaintext, ciphertext, tag);
// Decryption
std::array<uint8_t, 10> decrypted_plaintext{};
bool success = ascon_aead128::decrypt(key, nonce, ad, ciphertext, decrypted_plaintext, tag);
if (success) {
std::cout << "Decryption successful!" << std::endl;
// Check decrypted_plaintext
} else {
std::cout << "Decryption failed!" << std::endl;
}
return 0;
}
Ascon-Hash256 computes a 256-bit (32-byte) hash.
#include <ascon/hashes/ascon_hash256.hpp>
#include <array>
#include <cassert>
int main() {
std::array<uint8_t, 10> message = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
std::array<uint8_t, 32> digest{};
ascon_hash256::ascon_hash256_t hasher;
assert(hasher.absorb(message));
assert(hasher.finalize());
assert(hasher.digest(digest));
// digest now contains the hash
return 0;
}
Ascon-XOF128 and Ascon-CXOF128 are extendable output functions. XOF128 produces a variable-length output, while CXOF128 allows for customization with an application-specific string.
#include <ascon/hashes/ascon_xof128.hpp>
#include <ascon/hashes/ascon_cxof128.hpp>
#include <array>
#include <cassert>
int main() {
// XOF128
std::array<uint8_t, 10> message = {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
std::array<uint8_t, 20> output{};
ascon_xof128::ascon_xof128_t xof;
assert(xof.absorb(message));
assert(xof.finalize());
assert(xof.squeeze(output));
// CXOF128
std::array<uint8_t, 5> customization_string = {'A', 'S', 'C', 'O', 'N'};
std::array<uint8_t, 10> message2 = {0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19};
std::array<uint8_t, 30> output2{};
ascon_cxof128::ascon_cxof128_t cxof;
assert(cxof.customize(customization_string));
assert(cxof.absorb(message2));
assert(cxof.finalize());
assert(cxof.squeeze(output2));
return 0;
}
Remember to compile with a C++20 compliant compiler.
Several example programs demonstrating the usage of each Ascon primitive are provided in the examples
directory. To build and run these examples, simply execute the following command from the root directory of the project:
make example -j
This will build and run all example programs. A sample output might look like this (note that the hexadecimal values will vary due to random data generation):
Ascon-AEAD128
Key : b1c9e631b35c013803e6188faa0f0aaf
Nonce : a06a309c0b0dab74385257e5672c1867
Data : 0af5a7f7933fa257d305194a81466be64ddeeb35262d59a67b76fdd385745eb0
Text : 90cdc6cab91662f5452ea0f629f185f4e0b8b91bddf94b1ec0254db4dc53ddd7444ec1cf074167f3f85719ea6b79a8f004329d441883da873ec1ecb1d592df72
Encrypted : 8094a72e01b264d989b6d75e0901d2ee932602afaaba5116154d5ea278f6f322a776a8815be7afde77519ac7812cd71efbab23831918b40c223be54793b6fde2
Decrypted : 90cdc6cab91662f5452ea0f629f185f4e0b8b91bddf94b1ec0254db4dc53ddd7444ec1cf074167f3f85719ea6b79a8f004329d441883da873ec1ecb1d592df72
Ascon-Hash256
Message : 1c9a3592780c62e4c8cc8c2facaa4f74b6715ff33a58fd64a2216150a6a06d84df752381db67acb80d75e1374616279be3840e3a1f130222ecebcee8328ac997
Digest : 02da70ac9a9e253d6d6abda26b8023c6d982f188a70f381a04ebcef1f1c7e532
Ascon-XOF128
Message : de513722ba5d1b21c9249fd375c63785725940624ac2aa6f586fe62e7f17501a0f858e67a4c0927acd01c7476b2ac52c140d6d9167ec7832672f737c5828ab7e
Digest : da3c497a250cc889f66723eae9527184822308804ab20ad0a6d5a3652b713d2083a3699b6ed49be6b9fe38d438d0b2f13ad0f5378c5a2b3506966f9b2378bd19