Skip to content

Instantly share code, notes, and snippets.

@reqshark
Forked from GraxRabble/sodium_demo.c
Created March 4, 2016 07:23
Show Gist options
  • Save reqshark/f75b80af8ed0b0b0a1ae to your computer and use it in GitHub Desktop.
Save reqshark/f75b80af8ed0b0b0a1ae to your computer and use it in GitHub Desktop.
This c file demostrates how to use each libsodium functions and lets you play with them to see their outputs.
/*
* GraxRabble
* 04 MAY 2014
* Note this was created for the 4.5 version of libSodium.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "sodium.h" /* library header */
#define BUFFER_SIZE 128 /* size of all input buffers in the demo */
/* ================================================================== *
* go down for the examples of libSodium *
* | *
* _|_ *
* \ / *
* * *
* ================================================================== */
/* ================================================================== *
* demo memory management *
* ================================================================== */
/*
* Linked list of all calls to malloc.
*/
struct allocation_node {
struct allocation_node *next;
};
static struct allocation_node *alloc_head = NULL;
/*
* Free all mallocs within the linked list.
*/
static void
free_allocations(void)
{
struct allocation_node *tmp;
struct allocation_node *n;
n = alloc_head;
alloc_head = NULL;
while (n != NULL) {
tmp = n->next;
free(n);
n = tmp;
}
}
/*
* Track all mallocs so it will be freed later.
*/
static void*
malloc_wrapper(size_t size)
{
struct allocation_node *node;
void *p;
node = malloc(sizeof *node + size);
if (node == NULL) abort();
node->next = alloc_head;
alloc_head = node;
p = node + 1;
return p;
}
/* ================================================================== *
* utility functions *
* ================================================================== */
/*
* Print hex.
*/
static char*
print_hex(const void *buf, const size_t len)
{
const unsigned char *b;
char *p;
b = buf;
p = malloc_wrapper(len * 2 + 1);
/* the library supplies a few utility functions like the one below */
return sodium_bin2hex(p, len * 2 + 1, b, len);
}
/*
* Display a prompt for input by user. It will save the input into a buffer
* of a specific size with room for the null terminator while removing
* trailing newline characters.
*/
static size_t
prompt_input(char *prompt, char *buf, const size_t len)
{
size_t n;
fputs(prompt, stdout);
fgets(buf, len, stdin); /* grab input with room for NULL */
n = strlen(buf);
if (buf[n - 1] == '\n') { /* trim excess new line */
buf[n - 1] = '\0';
--n;
}
return n;
}
/*
* Print a message if the function was sucessful or failed.
*/
static void
print_verification(int r)
{
if (r == 0) puts("Success\n");
else puts("Failure\n");
}
/*
* Dirty the stack to expose all padding zeroing errors.
*/
static void
dirty(void)
{
unsigned char memory[4096];
memset(memory, 'c', sizeof memory);
}
/* ================================================================== *
* libSodium examples *
* ================================================================== */
/*
* Full featured authentication which is used to verify that the message
* comes from the expected person. It should be safe to keep the same key
* for multiple messages.
*/
static void
auth(void)
{
unsigned char k[crypto_auth_KEYBYTES]; /* key */
unsigned char a[crypto_auth_BYTES]; /* authentication token */
unsigned char m[BUFFER_SIZE]; /* message */
size_t mlen; /* message length */
int r;
sodium_memzero(k, sizeof k); /* must zero the key */
puts("Example: crypto_auth\n");
prompt_input("Input your key > ", (char*) k, sizeof k);
mlen = prompt_input("Input your message > ", (char*) m, sizeof m);
printf("\nGenerating %s authentication...\n", crypto_auth_PRIMITIVE);
crypto_auth(a, m, mlen, k);
printf("Format: %s::%s\n", "authentication token", "message");
printf("%s::%s\n\n", print_hex(a, sizeof a), m);
puts("Verifying authentication...");
r = crypto_auth_verify(a, m, mlen, k);
print_verification(r);
}
/*
* This method is only effective for a single use per key. The benefit is
* the algorithm is quicker and output is half the size of auth. It is easy
* to see how weak the algorithm is when you use a one letter key.
*
* Note that the same key must not be used more than once.
*/
static void
onetimeauth(void)
{
unsigned char k[crypto_onetimeauth_KEYBYTES]; /* key */
unsigned char a[crypto_onetimeauth_BYTES]; /* authentication token */
unsigned char m[BUFFER_SIZE]; /* message */
size_t mlen; /* message length */
int r;
sodium_memzero(k, sizeof k); /* must zero the key */
puts("Example: crypto_onetimeauth\n");
prompt_input("Input your key > ", (char*) k, sizeof k);
mlen = prompt_input("Input your message > ", (char*) m, sizeof m);
printf("\nGenerating %s authentication...\n", crypto_onetimeauth_PRIMITIVE);
crypto_onetimeauth(a, m, mlen, k);
printf("Format: %s::%s\n", "authentication token", "message");
printf("%s::%s\n\n", print_hex(a, sizeof a), m);
puts("Verifying authentication...");
r = crypto_onetimeauth_verify(a, m, mlen, k);
print_verification(r);
}
/*
* Shows how crypto_box works using Bob and Alice with a simple message.
* Both clients must generate their own key pair and swap public key. The
* library will perform Diffie-Hellman to generate a shared key for
* symmetric encryption.
*
* Note that crypto_box uses padding at the start of both messages. The padding
* must be zero else the encryption or decryption will fail.
*
* Note the same nonce must not be used; it should be safe to use a counter.
*/
static void
box(void)
{
unsigned char bob_pk[crypto_box_PUBLICKEYBYTES]; /* Bob public */
unsigned char bob_sk[crypto_box_SECRETKEYBYTES]; /* Bob secret */
unsigned char alice_pk[crypto_box_PUBLICKEYBYTES]; /* Alice public */
unsigned char alice_sk[crypto_box_SECRETKEYBYTES]; /* Alice secret */
unsigned char n[crypto_box_NONCEBYTES]; /* message nonce */
unsigned char c[BUFFER_SIZE + crypto_box_ZEROBYTES]; /* cipher-text */
unsigned char m[BUFFER_SIZE + crypto_box_ZEROBYTES]; /* plain-text */
size_t mlen; /* length */
int r;
/* use the following to skip the padding */
unsigned char *c_ptr = c + crypto_box_ZEROBYTES;
unsigned char *m_ptr = m + crypto_box_ZEROBYTES;
size_t m_size = sizeof m - crypto_box_ZEROBYTES;
puts("Example: box\n");
puts("Generating keypairs...\n");
crypto_box_keypair(bob_pk, bob_sk); /* generate Bob's keys */
crypto_box_keypair(alice_pk, alice_sk); /* generate Alice's keys */
puts("Bob");
printf("Public: %s\n", print_hex(bob_pk, sizeof bob_pk));
printf("Secret: %s\n\n", print_hex(bob_sk, sizeof bob_sk));
puts("Alice");
printf("Public: %s\n", print_hex(alice_pk, sizeof alice_pk));
printf("Secret: %s\n\n", print_hex(alice_sk, sizeof alice_sk));
/* nonce must be generated per message, safe to send with message */
puts("Generating nonce...");
randombytes_buf(n, sizeof n);
printf("Nonce: %s\n\n", print_hex(n, sizeof n));
/* read input */
mlen = prompt_input("Input your message > ", (char*) m_ptr, m_size);
/* encrypt the message */
printf("\nEncrypting with %s\n", crypto_box_PRIMITIVE);
/* must zero at least the padding */
sodium_memzero(m, crypto_box_ZEROBYTES);
crypto_box(c, m, mlen + crypto_box_ZEROBYTES, n, alice_pk, bob_sk);
/* sent message */
puts("\nBob sending message...\n");
printf("Format: %s::%s\n", "nonce", "message");
printf("Ciphertext: %s::%s\n\n", print_hex(n, sizeof n),
print_hex(c_ptr, mlen));
/* decrypt the message */
puts("Alice opening message...");
/* must zero at least the padding */
sodium_memzero(c, crypto_box_BOXZEROBYTES);
r = crypto_box_open(m, c, mlen + crypto_box_ZEROBYTES, n, bob_pk, alice_sk);
print_verification(r);
if (r == 0) printf("Plaintext: %s\n\n", m_ptr);
}
/*
* Signs a message with secret key which will authenticate a message.
* Everybody else can use the public key to ensure that the message is both
* valid and untampered.
*
* Note that both message and signed message must be padded for signature.
* The padding does not have to be set to zero.
*/
static void
sign(void)
{
unsigned char pk[crypto_sign_PUBLICKEYBYTES]; /* Bob public */
unsigned char sk[crypto_sign_SECRETKEYBYTES]; /* Bob secret */
unsigned char m[BUFFER_SIZE + crypto_sign_BYTES]; /* message */
unsigned char sm[BUFFER_SIZE + crypto_sign_BYTES]; /* signed message */
unsigned long long int mlen; /* message length */
unsigned long long int smlen; /* signed length */
int r;
/* use the following to skip the padding */
unsigned char *sm_ptr = sm + crypto_sign_BYTES;
size_t m_size = sizeof m - crypto_sign_BYTES;
puts("Example: sign\n");
puts("Generating keypair...");
crypto_sign_keypair(pk, sk); /* generate Bob's keys */
printf("Public: %s\n", print_hex(pk, sizeof pk));
printf("Secret: %s\n\n", print_hex(sk, sizeof sk));
/* read input */
mlen = prompt_input("Input your message > ", (char*) m, m_size);
printf("\nSigning message with %s...\n", crypto_sign_PRIMITIVE);
crypto_sign(sm, &smlen, m, mlen, sk);
printf("Format: %s::%s\n", "signature", "message");
printf("Signed: %s::%s\n\n", print_hex(sm, crypto_sign_BYTES),
sm_ptr);
puts("Validating message...");
r = crypto_sign_open(m, &mlen, sm, smlen, pk);
print_verification(r);
if (r == 0) printf("Message: %s\n\n", m);
}
/*
* Stream utilizes a nonce to generate a sequence of bytes. The library has
* an internal function which XOR data and the stream into an encrypted result.
*
* Note that this method does not supply authentication. Try secretbox instead.
*
* Note that nonce must be different for each message since it provides
* change between each operation. It should be safe to use a counter
* instead of purely random data each time.
*/
static void
stream(void)
{
unsigned char k[crypto_stream_KEYBYTES]; /* secret key */
unsigned char n[crypto_stream_NONCEBYTES]; /* message nonce */
unsigned char m[BUFFER_SIZE]; /* plain-text */
unsigned char c[BUFFER_SIZE]; /* cipher-text */
size_t mlen; /* length */
int r;
puts("Example: stream\n");
sodium_memzero(k, sizeof k);
prompt_input("Input your key > ", (char*) k, sizeof k);
/* nonce must be generated per message, safe to send with message */
puts("\nGenerating nonce...");
randombytes_buf(n, sizeof n);
printf("Nonce: %s\n\n", print_hex(n, sizeof n));
mlen = prompt_input("Input your message > ", (char*) m, sizeof m);
printf("\nEncrypting with (xor) %s\n", crypto_stream_PRIMITIVE);
crypto_stream_xor(c, m, mlen, n, k);
puts("\nSending message...");
printf("Format: %s::%s\n", "nonce", "message");
printf("Ciphertext: %s::%s\n\n", print_hex(n, sizeof n),
print_hex(c, mlen));
puts("Opening message...");
r = crypto_stream_xor(m, c, mlen, n, k);
print_verification(r);
if (r == 0) printf("Plaintext: %s\n\n", m);
}
/*
* This is a wrapper around stream which does XOR automatically.
*
* Note that the buffer must be padded at the front. The same nonce must
* not be used; it should be safe to use a counter.
*/
static void
secretbox(void)
{
unsigned char k[crypto_secretbox_KEYBYTES]; /* secret */
unsigned char n[crypto_secretbox_NONCEBYTES]; /* nonce */
unsigned char m[BUFFER_SIZE + crypto_secretbox_ZEROBYTES]; /* plain */
unsigned char c[BUFFER_SIZE + crypto_secretbox_ZEROBYTES]; /* cipher */
size_t mlen; /* length */
int r;
/* use the following to skip the padding */
unsigned char *m_ptr = m + crypto_secretbox_ZEROBYTES;
unsigned char *c_ptr = c + crypto_secretbox_ZEROBYTES;
size_t m_size = sizeof m - crypto_secretbox_ZEROBYTES;
puts("Example: secretbox\n");
sodium_memzero(k, sizeof k);
prompt_input("Input your key > ", (char*) k, sizeof k);
/* nonce must be generated per message, safe to send with message */
puts("Generating nonce...");
randombytes_buf(n, sizeof n);
printf("Nonce: %s\n\n", print_hex(n, sizeof n));
mlen = prompt_input("Input your message > ", (char*) m_ptr, m_size);
/* encrypting message */
printf("\nEncrypting with %s\n", crypto_secretbox_PRIMITIVE);
/* must zero at least the padding */
sodium_memzero(m, crypto_secretbox_ZEROBYTES);
crypto_secretbox(c, m, mlen + crypto_secretbox_ZEROBYTES, n, k);
puts("\nSending message...");
printf("Format: %s::%s\n", "nonce", "message");
printf("Ciphertext: %s::%s\n\n", print_hex(n, sizeof n),
print_hex(c_ptr, mlen));
/* decrypting message */
puts("Opening message...");
/* must zero at least the padding */
sodium_memzero(c, crypto_secretbox_BOXZEROBYTES);
r = crypto_secretbox_open(m, c, mlen + crypto_secretbox_ZEROBYTES, n, k);
print_verification(r);
if (r == 0) printf("Plaintext: %s\n\n", m_ptr);
}
/*
* The library ships with a one-shot SHA-512 implementation. Simply allocate
* all desired data into a single continuous buffer.
*/
static void
hash(void)
{
unsigned char h[crypto_hash_BYTES]; /* hash output */
unsigned char m[BUFFER_SIZE]; /* message */
size_t mlen; /* length */
puts("Example: hash\n");
mlen = prompt_input("Input your message > ", (char*) m, sizeof m);
printf("\nHashing message with %s\n", crypto_hash_PRIMITIVE);
crypto_hash(h, m, mlen);
printf("Hash: %s\n\n", print_hex(h, sizeof h));
}
/*
* Short hash is a fast algorithm intended for hash tables and anything
* else that does not require data integrity. There is the added benefit
* of a key which will alter the output of the hash.
*/
void
shorthash(void)
{
unsigned char k[crypto_shorthash_KEYBYTES]; /* key */
unsigned char h[crypto_shorthash_BYTES]; /* hash output */
unsigned char m[BUFFER_SIZE]; /* message */
size_t mlen; /* length */
puts("Example: shorthash\n");
sodium_memzero(k, sizeof k);
prompt_input("Input your key > ", (char*) k, sizeof k);
mlen = prompt_input("Input your message > ", (char*) m, sizeof m);
printf("\nHashing message with %s\n", crypto_shorthash_PRIMITIVE);
crypto_shorthash(h, m, mlen, k);
printf("Hash: %s\n\n", print_hex(h, sizeof h));
}
/*
* Generic hash is intended as a variable output hash with enough strength
* to ensure data integrity. The hash out put is also able to vary in size.
* Key is optional and is able to vary in size.
*
* Note that it is recommended to stay within the range of MIN and MAX
* output because larger output will produce gaps.
*/
void
generichash(void)
{
unsigned char k[crypto_generichash_KEYBYTES_MAX]; /* key */
unsigned char h[crypto_generichash_BYTES_MIN]; /* hash output */
unsigned char m[BUFFER_SIZE]; /* message */
size_t mlen; /* length */
puts("Example: generichash\n");
sodium_memzero(k, sizeof k);
prompt_input("Input your key > ", (char*) k, sizeof k);
mlen = prompt_input("Input your message > ", (char*) m, sizeof m);
printf("\nHashing message with %s\n", crypto_generichash_PRIMITIVE);
crypto_generichash(h, sizeof h, m, mlen, k, sizeof k);
printf("Hash: %s\n\n", print_hex( h, sizeof h));
}
/*
* Streaming variant of generic hash. This has the ability to hash
* data in chunks at a time and compute the same result as hashing
* all of the data at once.
*/
void
generichashstream(void)
{
unsigned char k[crypto_generichash_KEYBYTES_MAX]; /* key */
unsigned char h[crypto_generichash_BYTES_MIN]; /* hash output */
crypto_generichash_state state; /* hash stream */
unsigned char m[BUFFER_SIZE]; /* input buffer */
size_t mlen; /* input length */
puts("Example: generichashstream\n");
sodium_memzero(k, sizeof k);
prompt_input("Input your key > ", (char*) k, sizeof k);
printf("\nHashing message with %s\n", crypto_generichash_PRIMITIVE);
/* initialize the stream */
crypto_generichash_init(&state, k, sizeof k, sizeof h);
while (1) {
mlen = prompt_input("> ", (char*) m, sizeof m);
if (mlen == 0) break;
crypto_generichash_update(&state, m, mlen); /* keep appending data */
}
crypto_generichash_final(&state, h, sizeof h);
printf("\nHash: %s\n\n", print_hex(h, sizeof h));
}
/*
* Try to dump all the sizes of each constant here.
*/
void
sizes(void)
{
#define DUMP_SIZE(X) printf("%-32s %4u\n", #X, X)
puts("Example: sizes\n");
printf("%-32s %s\n", "Macro constant", "size (bytes)");
puts("=============================================");
DUMP_SIZE(crypto_auth_BYTES);
DUMP_SIZE(crypto_auth_KEYBYTES);
putchar('\n');
DUMP_SIZE(crypto_onetimeauth_BYTES);
DUMP_SIZE(crypto_onetimeauth_KEYBYTES);
putchar('\n');
DUMP_SIZE(crypto_box_PUBLICKEYBYTES);
DUMP_SIZE(crypto_box_SECRETKEYBYTES);
DUMP_SIZE(crypto_box_NONCEBYTES);
DUMP_SIZE(crypto_box_ZEROBYTES);
DUMP_SIZE(crypto_box_BOXZEROBYTES);
putchar('\n');
DUMP_SIZE(crypto_sign_PUBLICKEYBYTES);
DUMP_SIZE(crypto_sign_SECRETKEYBYTES);
DUMP_SIZE(crypto_sign_BYTES);
putchar('\n');
DUMP_SIZE(crypto_stream_KEYBYTES);
DUMP_SIZE(crypto_stream_NONCEBYTES);
putchar('\n');
DUMP_SIZE(crypto_secretbox_KEYBYTES);
DUMP_SIZE(crypto_secretbox_NONCEBYTES);
DUMP_SIZE(crypto_secretbox_ZEROBYTES);
putchar('\n');
DUMP_SIZE(crypto_hash_BYTES);
putchar('\n');
DUMP_SIZE(crypto_shorthash_BYTES);
DUMP_SIZE(crypto_shorthash_KEYBYTES);
putchar('\n');
DUMP_SIZE(crypto_generichash_BYTES);
DUMP_SIZE(crypto_generichash_BYTES_MAX);
DUMP_SIZE(crypto_generichash_BYTES_MIN);
DUMP_SIZE(crypto_generichash_KEYBYTES);
DUMP_SIZE(crypto_generichash_KEYBYTES_MAX);
DUMP_SIZE(crypto_generichash_KEYBYTES_MIN);
putchar('\n');
}
/* ================================================================== *
* examples array *
* ================================================================== */
typedef void (*example_fn_t)(void);
struct example {
const char *name;
const example_fn_t func;
};
#define EXAMPLE(x) { #x, &x }
void list(void);
const struct example examples[] = {
EXAMPLE(list),
EXAMPLE(sizes),
EXAMPLE(auth),
EXAMPLE(onetimeauth),
EXAMPLE(box),
EXAMPLE(sign),
EXAMPLE(stream),
EXAMPLE(secretbox),
EXAMPLE(hash),
EXAMPLE(shorthash),
EXAMPLE(generichash),
EXAMPLE(generichashstream),
};
/*
* Print a list of all examples within the executable.
*/
void
list(void)
{
size_t i;
puts("Examples:\n");
for (i = 0; i < sizeof examples / sizeof examples[0]; ++i) {
printf("%s\n", examples[i].name);
}
putchar('\n');
}
/* ================================================================== *
* main *
* ================================================================== */
int
main(int argc, char **argv)
{
size_t i;
dirty(); /* dirty the stack to find padding zeroing errors */
atexit(&free_allocations); /* clean up the demo's memory on exit */
if (argc < 2) { /* print usage */
printf("usage: %s [example]\n", *argv);
printf("list all examples: %s list\n\n", *argv);
exit(EXIT_SUCCESS);
}
printf("Running libSodium version: %s\n\n", sodium_version_string());
/*
* Init has to only be executed once so the library can optimize
* itself to the current computer.
*
* Not required but recommended.
*
* Note that this function is not thread safe.
*/
sodium_init();
/* search for the example */
for (i = 0; i < sizeof examples / sizeof examples[0]; ++i) {
if (strcmp(argv[1], examples[i].name) == 0) {
examples[i].func();
exit(EXIT_SUCCESS);
}
}
puts("Example does not exist\n");
exit(EXIT_SUCCESS);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment