CRYSTALS (Cryptographic Suite for Algebraic Lattices) supports two quantum robust mechanisms: Kyber for key-encapsulation mechanism (KEM) and key exchange; and Dilithium for a digital signature algorithm. CRYSTALS-Kyber uses LWE (Learning with Errors) with lattice methods. With KEX, Bob and Alice exchange information, and end up with the same shared encryption key. Bob and Alice both generate public and private keys, use these to generate the shared secret. We can either have UAKE (unilaterally authenticated key exchange) or AKE (mutually authenticated key exchange). With UAKE, we only authenticate one end of the exchange (such as where we only authenticate the server in TLS), or with AKE, we authenticate both ends with their public keys. With Kyber we have three main packages: Kyber-512 (equivalent to AES-128); Kyber-768 (equivalent to AES-192) and Kyber-1024 (equivalent to AES-256). Each of these will be quantum robust. In this case we will be using Kyber-512, and which produces a public key of 800 bytes, and a secret key of 1632 bytes.
CRYSTALS-KYBER (Lattice + LWE) - Key EXchange Mechanism (KEX) |
Outline
#include stddef.h #include stdio.h #include string.h #include stdlib.h #include "kem.h" #include "kex.h" char *showhex(uint8_t a[], int size) ; char *showhex(uint8_t a[], int size) { char *s = malloc(size * 2 + 1); for (int i = 0; i < size; i++) sprintf(s + i * 2, "%02x", a[i]); return(s); } int main(void) { uint8_t pkb[CRYPTO_PUBLICKEYBYTES]; uint8_t skb[CRYPTO_SECRETKEYBYTES]; uint8_t pka[CRYPTO_PUBLICKEYBYTES]; uint8_t ska[CRYPTO_SECRETKEYBYTES]; uint8_t eska[CRYPTO_SECRETKEYBYTES]; uint8_t uake_senda[KEX_UAKE_SENDABYTES]; uint8_t uake_sendb[KEX_UAKE_SENDBBYTES]; uint8_t ake_senda[KEX_AKE_SENDABYTES]; uint8_t ake_sendb[KEX_AKE_SENDBBYTES]; uint8_t tk[KEX_SSBYTES]; uint8_t ka[KEX_SSBYTES]; uint8_t kb[KEX_SSBYTES]; uint8_t zero[KEX_SSBYTES]; int i; for(i=0;i< KEX_SSBYTES;i++) zero[i] = 0; crypto_kem_keypair(pkb, skb); // Generate static key for Bob crypto_kem_keypair(pka, ska); // Generate static key for Alice // Perform unilaterally authenticated key exchange kex_uake_initA(uake_senda, tk, eska, pkb); // Run by Alice kex_uake_sharedB(uake_sendb, kb, uake_senda, skb); // Run by Bob kex_uake_sharedA(ka, uake_sendb, tk, eska); // Run by Alice if(memcmp(ka,kb,KEX_SSBYTES)) printf("Error in UAKE\n"); if(!memcmp(ka,zero,KEX_SSBYTES)) printf("Error: UAKE produces zero key\n"); // Perform mutually authenticated key exchange kex_ake_initA(ake_senda, tk, eska, pkb); // Run by Alice kex_ake_sharedB(ake_sendb, kb, ake_senda, skb, pka); // Run by Bob kex_ake_sharedA(ka, ake_sendb, tk, eska, ska); // Run by Alice if(memcmp(ka,kb,KEX_SSBYTES)) printf("Error in AKE\n"); if(!memcmp(ka,zero,KEX_SSBYTES)) printf("Error: AKE produces zero key\n"); printf("KEX_UAKE_SENDABYTES: %d\n",KEX_UAKE_SENDABYTES); printf("KEX_UAKE_SENDBBYTES: %d\n",KEX_UAKE_SENDBBYTES); printf("KEX_AKE_SENDABYTES: %d\n",KEX_AKE_SENDABYTES); printf("KEX_AKE_SENDBBYTES: %d\n",KEX_AKE_SENDBBYTES); printf("Alice Public key (only showing 1/8 of key): %s\n",showhex(pka,CRYPTO_PUBLICKEYBYTES/8)); printf("Alice Secret key (only showing 1/8 of key): %s\n",showhex(ska,CRYPTO_SECRETKEYBYTES/8)); printf("Bob Public key (only showing 1/8 of key): %s\n",showhex(pkb,CRYPTO_PUBLICKEYBYTES/8)); printf("Bob Secret key (only showing 1/8 of key): %s\n",showhex(skb,CRYPTO_SECRETKEYBYTES/8)); printf("Key (A): %s\n",showhex(ka,CRYPTO_BYTES)); printf("Key (B): %s\n",showhex(kb,CRYPTO_BYTES)); return 0; }
A sample run of Kyber512 shows that the public key size is 800 bytes, the secret key is 1,632 bytes, and the key exchange sizes of 1568 bytes (for mutual authentication):
KEX_UAKE_SENDABYTES: 1568 KEX_UAKE_SENDBBYTES: 768 KEX_AKE_SENDABYTES: 1568 KEX_AKE_SENDBBYTES: 1536 Alice Public key (only showing 1/8 of key): 6d0a3d9fd91d6b38c9a72c6e2d83cd9d360360c49bf5ab4289912c4e07aa27e66bfdb2ca7cda03d64625403c63d556b23bf161b8a61538a08ac8646a8ba54174e8b3b66638c7334f74e8258140c44a75bf0f67ca5e10bdaf3c3bbe070f85b0099ec1108d Alice Secret key (only showing 1/8 of key): bdc222f756991b82ac6bc15016eabc7ae67e76a597133b35b2166dbce68812523decf74795649ef68086d0c751638b3f0b414b5ba614892ca1f6544eb9aa983ae15e78073a7a0732253c4ecea01bc0d3add3e4113a929dcf114f350866bc5484668a50b970ccf129857b2b663f6629ea1c7ab231054d1bb9041274cc0593effb25b0e012d5c855e8bb4a6a96b38836b8a37535bde1cb99d01fe1b315269680edab0ce7c0c0f1f26a93293175d95cd336563b7a43be2795de2bb5ca596c86e0a59e82ca4588b52d5b7ffaa668 Bob Public key (only showing 1/8 of key): 30fa69be4b237f657371e3c8319a4cc762a0ca521187c0675343214de66fe54a83116b98bb77b0e5486760c32dfd830eedb8435e863e43f32b37a26b5e251e721310009bc2382b42343b7291510e6188772b99091afca6b1e1aa50760e0405bc72ab53c4 Bob Secret key (only showing 1/8 of key): dfb0653a9544091470e31809f929ce37514ce9214472e74e34481788d5a0c5d12ad66c54d00c887ed83cbf822dc796393145c5c8808d07c95a35328686b50fcd166383384c71a1c16103b7af98c5f1164c85b39ffcc49918908684ec1413b34cbae957191a067a25aa519c576732c78dc95011d121f900558d1249e6349e06804777139a43e91cbde89dcd1930f4a29ce3678f85a8bc055c0e3b990df9d8651636b687c6acc83bb2f73caf071c828cb051facc1b7b31bddac997190752f596be6ed865cfc3603d522d611a13 Key (A): 06a0fb5d8563ef96e97d9f91e6159f70990c824b2976a2324bb7821777f9a1e2 Key (B): 06a0fb5d8563ef96e97d9f91e6159f70990c824b2976a2324bb7821777f9a1e2
The code compiles to “test_kyber512”, and which is a Linux build. To run just run the executable: