As Light As A Cryptographic Feather?
Why have a separate hash and encryption function when ASCON does both?
While Post Quantum Cryptography (PQC) gets all the headlines these days, there’s also a new standard for Light-weight Cryptography (LWC). For this, NIST has defined that ASCON will move to become a standard. One of the great things about ASCON is that it uses a sponge function, such as used with SHA-3/Keccak, and which has an internal state. This allows us to implement hashing functions and encryption with the same code. Thus, rather than having a different method for SHA-256 and AES, we could combine these with ASCON, and significantly reduce the required code and running memory.
Overall, with lightweight encryption, we focus on the performance of the method, the energy consumption, the number of gates used, the size of the code in running memory, and the boot code.
So, I have previously implemented it in Python, so let’s implement it in my favouriate programming language: Golang. This will work so much faster than Python, and will compile to virtually any target system … because there are more ARM processors out there than there are Intel-based ones.
ASCON performance
And, so, since 2016, researchers have been probing the submitted methods, and in 2022 NIST published the final 10: ASCON, Elephant, GIFT-COFB, Grain128-AEAD, ISAP, Photon-Beetle, Romulus, Sparkle, TinyJambu, and Xoodyak (Table 1). A particular focus is on the security of the methods, along with their performance on low-cost FPGAs/embedded processes and their robustness against side-channel attacks.
The current set of benchmarks includes running on an Arduino Uno R3 (AVR ARmega 328P — Figure 1), Arduino Nano Every (AVR ARmega 4809), Arduino MKR Zero (ARM Cortex M10+) and Arduino Nano 33 BLE (ARM Cortex M4F). These are just 8-bit processors and fit into an Arduino board. Along with their processing limitations, they are also limited in their memory footprint (to run code and also to store it).
The lightweight cryptography method must thus overcome these limitations and still be secure and provide a good performance level. Running AES in block modes on these devices is often not possible, as there are insufficient resources. Overall, we use a benchmark for encryption — with AEAD (Authenticated Encryption with Additional Data) and for hashing. With AEAD, we add extra information — such as the session ID — into the encryption process. This type of method can bind the encryption to a specific stream.
In Table 2 [1], we see a sample run using an Arduino Due with an ARM Cortex M3 running at 84MHz. The tests are taken in comparison with the ChaCha20 stream cipher and defined for AEAD, and where the higher the value, the better the performance. We can see that Sparkle, Xoodyak and ASCON are the fastest of all. Sparkle has a 100% improvement, and Xoodyak gives a 60% increase in speed over ChaCha20. Elephant, ISAP and PHOTON-Beetle have the worst performance for encryption (with around 1/20th of the speed of ChaCha20).
For AEAD on Uno Nano Every [2], the benchmark is against AES GCM. We can see in Table 4, that Sparkle is 4.7 times faster than AES GCM for 128-bit data sizes, and Xoodyak comes in second with a 3.3 times improvement over AES GCM. When it comes to 8-bit data sizes, TinyJambu is actually the fastest, but where Sparkle and Xoodyak still perform well. PHOTON-Beetle, Grain128 and ISAP do not do well and only slightly improve on AES GCM. In fact, Grain128 and ISAP are actually slower than AES GCM.
Coding
ASCON uses a 128-bit encryption key and a 128-bit nonce value. We can also add additional data into the encryption process:
In order to generate the 128-bit encryption key (16 bytes), we can use the PBKDF2 with a password and with 10,000 rounds and a salt value:
key := pbkdf2.Key([]byte(passwd), salt, 10000, 16, sha256.New)
The encryption and decryption process is then:
aead, _ := ascon.New128(key)
ciphertext = aead.Seal(ciphertext[:0], nonce, plaintext, ad)
pt, _ := aead.Open(nil, nonce, ciphertext, ad)
An outline of the Golang code is [1][here]:
package main
import (
"github.com/ericlagergren/lwcrypto/ascon"
"fmt"
"crypto/sha256"
"golang.org/x/crypto/pbkdf2"
"io"
"crypto/rand"
"os"
)
func main() {
NonceSize:=16
passwd:="qwerty"
msg:="Hello 123"
addstr:="Adding"
argCount := len(os.Args[1:])
if (argCount>0) {msg= string(os.Args[1])}
if (argCount>1) {passwd= string(os.Args[2])}
if (argCount>2) {addstr= string(os.Args[3])}
plaintext:= []byte(msg)
nonce := make([]byte, NonceSize)
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err.Error())
}
salt:=[]byte("000000000000")
key := pbkdf2.Key([]byte(passwd), salt, 10000, 16, sha256.New)
ciphertext := make([]byte, NonceSize)
ad := []byte(addstr)
aead, _ := ascon.New128(key)
ciphertext = aead.Seal(ciphertext[:0], nonce, plaintext, ad)
pt, _ := aead.Open(nil, nonce, ciphertext, ad)
fmt.Printf("Password:\t%s\n",passwd)
fmt.Printf("Key:\t\t%x\n",key)
fmt.Printf("Nonce:\t\t%x\n",nonce)
fmt.Printf("Add data:\t%s\n",addstr)
fmt.Printf("\nPlaintext:\t%s\n",plaintext)
fmt.Printf("\nCiphertext\t%x\n",ciphertext)
fmt.Printf("\nRecovered plaintext: %s",pt)
}
A sample run is [here]:
Password: Qwerty123
Key: 3b011c268cbd6c947e64fb0e368dd03a
Nonce: 901199c6bd714f3b998cd3c6fed7d199
Add data: Additional data
Plaintext: Hello 123
Ciphertext 1faa743dac23b3bb1e474568fb02253eecb517f9d8a75e3422
Recovered plaintext: Hello 123
If you want to run the code yourself, try:
Conclusions
ASCON uses a cool sponge function, and which differs greatly from the approach of SHA-2. Overall, it is more efficient for energy consumption, and it supports hashing, MAC, PRF (Pseudo-random function) and symmetric key encryption … all from a single method. So, in the future, we may not need to have a separate function for encryption and another one for hashing and MAC.
References
[1] https://rweather.github.io/lightweight-crypto/performance.html
[3] Madushan, H., Salam, I., & Alawatugoda, J. (2022). A Review of the NIST Lightweight Cryptography Finalists and Their Fault Analyses. Electronics, 11(24), 4199.