-
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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