When A Sponge Helps To Make Things Secure — And Light-weight

When can you get a hash function and an encryption? Well, when you have a sponge, and which stores and processes a permuation value. It…

Photo by NOAA on Unsplash

When A Sponge Helps To Make Things Secure — And Light-weight

When can you get a hash function and also implement encryption? Well, you can when you have a sponge, and which stores and processes a permutation value. It was first introduced with SHA-3 and where we define a state size (S) of 1600 bits (Keccak-f[1600]). This state (S) is made up from r (rate) and c (capacity). The total bits in the state is thus 1,600 bits. With this we can either use it as a hashing method or as an authenticated encryption method:

A background on the sponge method is here.

The lightweight nature of the sponge method makes it perfect for a lightweight encryption and/or hashing method. One of these is ASCON, and which uses a duplex-sponge mode. For encryption, it can use 64-bit or 128-bit block sizes. Currently, ASCON is one of the finalists for a NIST-defined lightweight encryption method.

ASCON — Authenticated Encryption

ASCON [2] was designed by Christoph Dobraunig, Maria Eichlseder, Florian Mendel and Martin Schläffer from Graz University of Technology, Infineon Technologies, and Radboud University. It is both a lightweight hashing and encryption method. ASCON uses a single lightweight permutation with Sponge-based mode of operation and an SPN (substitution–permutation network) permutation. Overall it has an easy method of implementing within hardware (2.6 gate equivalents) and software. A 5-bit S-box (as used in Keccak’s S-box core) is used to enable a lightweight approach and it has no known side-channel attacks. It can also achieve high throughputs such as throughputs of between 4.9 and 7.3 Gbps. It stores its current state with 320 bits.

Overall we have two C files and a few header files. To compile we can use the gcc compiler:

gcc main.c permutations.c printstate.c  -o ascon.exe

outline of the C code is [1][here]:

#include "api.h"
#include "ascon.h"
#include "crypto_aead.h"
#include "permutations.h"
#include "printstate.h"
#if !ASCON_INLINE_MODE
#undef forceinline
#define forceinline
#endif
#define CRYPTO_BYTES 64

void string2hexString(unsigned char* input, int clen, char* output);
static unsigned char ascii2byte(char *hexstring, unsigned char *bytearray);
int main (int argc, char *argv[]) {

  unsigned long long mlen;
unsigned long long clen;
  unsigned char plaintext[CRYPTO_BYTES];
unsigned char cipher[CRYPTO_BYTES];
unsigned char npub[CRYPTO_NPUBBYTES]="";
unsigned char ad[CRYPTO_ABYTES]="";
unsigned char nsec[CRYPTO_ABYTES]="";

unsigned char key[CRYPTO_KEYBYTES];
  char pl[CRYPTO_BYTES]="hello";
char chex[CRYPTO_BYTES]="";
char keyhex[2*CRYPTO_KEYBYTES+1]="0123456789ABCDEF0123456789ABCDEF";
char nonce[2*CRYPTO_NPUBBYTES+1]="000000000000111111111111";
char add[CRYPTO_ABYTES]="";
  if( argc > 1 ) {
strcpy(pl,argv[1]);
}
if( argc > 2 ) {
strcpy(keyhex,argv[2]);
}
if( argc > 3 ) {
strcpy(nonce,argv[3]);
}
if( argc > 4 ) {
strcpy(add,argv[4]);
}


  strcpy(plaintext,pl);
strcpy(ad,add);
ascii2byte(keyhex,key);
ascii2byte(nonce,npub);
  printf("Ascon light-weight cipher\n");
printf("Plaintext: %s\n",plaintext);
printf("Key: %s\n",keyhex);
printf("Nonce: %s\n",nonce);
printf("Additional Information: %s\n\n",ad);
  printf("Plaintext: %s\n",plaintext);
  int ret = crypto_aead_encrypt(cipher,&clen,plaintext,strlen(plaintext),ad,strlen(ad),nsec,npub,key);

string2hexString(cipher,clen,chex);
  printf("Cipher: %s, Len: %llu\n",chex, clen);
  ret = crypto_aead_decrypt(plaintext,&mlen,nsec,cipher,clen,ad,strlen(ad),npub,key);
  plaintext[mlen]='\0';
printf("Plaintext: %s, Len: %llu\n",plaintext, mlen);

  if (ret==0) {
printf("Success!\n");
}

return 0;
}
forceinline void ascon_loadkey(word_t* K0, word_t* K1, word_t* K2,
const uint8_t* k) {
KINIT(K0, K1, K2);
if (CRYPTO_KEYBYTES == 20) {
*K0 = XOR(*K0, KEYROT(WORD_T(0), LOAD(k, 4)));
k += 4;
}
*K1 = XOR(*K1, LOAD(k, 8));
*K2 = XOR(*K2, LOAD(k + 8, 8));
}
forceinline void ascon_init(state_t* s, const uint8_t* npub, const uint8_t* k) {
/* load nonce */
word_t N0 = LOAD(npub, 8);
word_t N1 = LOAD(npub + 8, 8);
/* load key */
word_t K0, K1, K2;
ascon_loadkey(&K0, &K1, &K2, k);
/* initialize */
PINIT(s);
if (CRYPTO_KEYBYTES == 16 && ASCON_RATE == 8)
s->x0 = XOR(s->x0, ASCON_128_IV);
if (CRYPTO_KEYBYTES == 16 && ASCON_RATE == 16)
s->x0 = XOR(s->x0, ASCON_128A_IV);
if (CRYPTO_KEYBYTES == 20) s->x0 = XOR(s->x0, ASCON_80PQ_IV);
if (CRYPTO_KEYBYTES == 20) s->x0 = XOR(s->x0, K0);
s->x1 = XOR(s->x1, K1);
s->x2 = XOR(s->x2, K2);
s->x3 = XOR(s->x3, N0);
s->x4 = XOR(s->x4, N1);
P(s, 12);
if (CRYPTO_KEYBYTES == 20) s->x2 = XOR(s->x2, K0);
s->x3 = XOR(s->x3, K1);
s->x4 = XOR(s->x4, K2);
printstate("initialization", s);
}
forceinline void ascon_adata(state_t* s, const uint8_t* ad, uint64_t adlen) {
const int nr = (ASCON_RATE == 8) ? 6 : 8;
if (adlen) {
/* full associated data blocks */
while (adlen >= ASCON_RATE) {
s->x0 = XOR(s->x0, LOAD(ad, 8));
if (ASCON_RATE == 16) s->x1 = XOR(s->x1, LOAD(ad + 8, 8));
P(s, nr);
ad += ASCON_RATE;
adlen -= ASCON_RATE;
}
/* final associated data block */
word_t* px = &s->x0;
if (ASCON_RATE == 16 && adlen >= 8) {
s->x0 = XOR(s->x0, LOAD(ad, 8));
px = &s->x1;
ad += 8;
adlen -= 8;
}
*px = XOR(*px, PAD(adlen));
if (adlen) *px = XOR(*px, LOAD(ad, adlen));
P(s, nr);
}
/* domain separation */
s->x4 = XOR(s->x4, WORD_T(1));
printstate("process associated data", s);
}
forceinline void ascon_encrypt(state_t* s, uint8_t* c, const uint8_t* m,
uint64_t mlen) {
const int nr = (ASCON_RATE == 8) ? 6 : 8;
/* full plaintext blocks */
while (mlen >= ASCON_RATE) {
s->x0 = XOR(s->x0, LOAD(m, 8));
STORE(c, s->x0, 8);
if (ASCON_RATE == 16) {
s->x1 = XOR(s->x1, LOAD(m + 8, 8));
STORE(c + 8, s->x1, 8);
}
P(s, nr);
m += ASCON_RATE;
c += ASCON_RATE;
mlen -= ASCON_RATE;
}
/* final plaintext block */
word_t* px = &s->x0;
if (ASCON_RATE == 16 && mlen >= 8) {
s->x0 = XOR(s->x0, LOAD(m, 8));
STORE(c, s->x0, 8);
px = &s->x1;
m += 8;
c += 8;
mlen -= 8;
}
*px = XOR(*px, PAD(mlen));
if (mlen) {
*px = XOR(*px, LOAD(m, mlen));
STORE(c, *px, mlen);
}
printstate("process plaintext", s);
}
forceinline void ascon_decrypt(state_t* s, uint8_t* m, const uint8_t* c,
uint64_t clen) {
const int nr = (ASCON_RATE == 8) ? 6 : 8;
/* full ciphertext blocks */
while (clen >= ASCON_RATE) {
word_t cx = LOAD(c, 8);
s->x0 = XOR(s->x0, cx);
STORE(m, s->x0, 8);
s->x0 = cx;
if (ASCON_RATE == 16) {
cx = LOAD(c + 8, 8);
s->x1 = XOR(s->x1, cx);
STORE(m + 8, s->x1, 8);
s->x1 = cx;
}
P(s, nr);
m += ASCON_RATE;
c += ASCON_RATE;
clen -= ASCON_RATE;
}
/* final ciphertext block */
word_t* px = &s->x0;
if (ASCON_RATE == 16 && clen >= 8) {
word_t cx = LOAD(c, 8);
s->x0 = XOR(s->x0, cx);
STORE(m, s->x0, 8);
s->x0 = cx;
px = &s->x1;
m += 8;
c += 8;
clen -= 8;
}
*px = XOR(*px, PAD(clen));
if (clen) {
word_t cx = LOAD(c, clen);
*px = XOR(*px, cx);
STORE(m, *px, clen);
*px = CLEAR(*px, clen);
*px = XOR(*px, cx);
}
printstate("process ciphertext", s);
}
forceinline void ascon_final(state_t* s, const uint8_t* k) {
/* load key */
word_t K0, K1, K2;
ascon_loadkey(&K0, &K1, &K2, k);
/* finalize */
if (CRYPTO_KEYBYTES == 16 && ASCON_RATE == 8) {
s->x1 = XOR(s->x1, K1);
s->x2 = XOR(s->x2, K2);
}
if (CRYPTO_KEYBYTES == 16 && ASCON_RATE == 16) {
s->x2 = XOR(s->x2, K1);
s->x3 = XOR(s->x3, K2);
}
if (CRYPTO_KEYBYTES == 20) {
s->x1 = XOR(s->x1, KEYROT(K0, K1));
s->x2 = XOR(s->x2, KEYROT(K1, K2));
s->x3 = XOR(s->x3, KEYROT(K2, WORD_T(0)));
}
P(s, 12);
s->x3 = XOR(s->x3, K1);
s->x4 = XOR(s->x4, K2);
printstate("finalization", s);
}
int crypto_aead_encrypt(unsigned char* c, unsigned long long* clen,
const unsigned char* m, unsigned long long mlen,
const unsigned char* ad, unsigned long long adlen,
const unsigned char* nsec, const unsigned char* npub,
const unsigned char* k) {
state_t s;
(void)nsec;
*clen = mlen + CRYPTO_ABYTES;
/* perform ascon computation */
ascon_init(&s, npub, k);
ascon_adata(&s, ad, adlen);
ascon_encrypt(&s, c, m, mlen);
ascon_final(&s, k);
/* set tag */
STOREBYTES(c + mlen, s.x3, 8);
STOREBYTES(c + mlen + 8, s.x4, 8);
return 0;
}
int crypto_aead_decrypt(unsigned char* m, unsigned long long* mlen,
unsigned char* nsec, const unsigned char* c,
unsigned long long clen, const unsigned char* ad,
unsigned long long adlen, const unsigned char* npub,
const unsigned char* k) {
state_t s;
(void)nsec;
if (clen < CRYPTO_ABYTES) return -1;
*mlen = clen = clen - CRYPTO_ABYTES;
/* perform ascon computation */
ascon_init(&s, npub, k);
ascon_adata(&s, ad, adlen);
ascon_decrypt(&s, m, c, clen);
ascon_final(&s, k);
/* verify tag (should be constant time, check compiler output) */
s.x3 = XOR(s.x3, LOADBYTES(c + clen, 8));
s.x4 = XOR(s.x4, LOADBYTES(c + clen + 8, 8));
return NOTZERO(s.x3, s.x4);
}

A sample run is [here]:

ASCON light-weight cipher
Plaintext: abc
Key: 0123456789ABCDEF0123456789ABCDEF
Nonce: AABB000000000111111111111
Additional Information: abc
Plaintext: abc
Cipher: 0A613CD5FA50ABC0F6C5, Len: 19
Plaintext: abc, Len: 3
Success!

The hashing method is here: