Repository: randombit/botan
Pull request #1996 (2019-06-15 15:19:58)

Add a compatability shim for libsodium
Not complete, just trying to hit the most commonly used APIs plus the ones that are easy to do.


Patch:
diff --git a/doc/dev_ref/todo.rst b/doc/dev_ref/todo.rst
index a16f53103..5737e9b94 100644
--- a/doc/dev_ref/todo.rst
+++ b/doc/dev_ref/todo.rst
@@ -117,7 +117,6 @@ New Protocols / Formats
 * Roughtime client (https://roughtime.googlesource.com/roughtime/)
 * PKCS7 / Cryptographic Message Syntax
 * PKCS12 / PFX
-* NaCl compatible cryptobox functions
 * Off-The-Record v3 https://otr.cypherpunks.ca/
 * Certificate Management Protocol (RFC 5273); requires CMS
 * Fernet symmetric encryption (https://cryptography.io/en/latest/fernet/)
diff --git a/src/lib/compat/sodium/info.txt b/src/lib/compat/sodium/info.txt
new file mode 100644
index 000000000..4adc853d9
--- /dev/null
+++ b/src/lib/compat/sodium/info.txt
@@ -0,0 +1,17 @@
+<defines>
+SODIUM_API -> 20190615
+</defines>
+
+<requires>
+chacha
+salsa20
+poly1305
+chacha20poly1305
+curve25519
+ed25519
+sha2_32
+sha2_64
+hmac
+siphash
+system_rng
+</requires>
diff --git a/src/lib/compat/sodium/sodium.h b/src/lib/compat/sodium/sodium.h
new file mode 100644
index 000000000..4bb37e974
--- /dev/null
+++ b/src/lib/compat/sodium/sodium.h
@@ -0,0 +1,1453 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#ifndef BOTAN_SODIUM_COMPAT_H_
+#define BOTAN_SODIUM_COMPAT_H_
+
+#include <botan/types.h>
+
+namespace Botan {
+
+/**
+* The Sodium namespace contains a partial implementation of the
+* libsodium API.
+*/
+namespace Sodium {
+
+// sodium/randombytes.h
+enum Sodium_Constants {
+   SODIUM_SIZE_MAX = 0xFFFFFFFF,
+
+   crypto_aead_chacha20poly1305_ABYTES = 16,
+   crypto_aead_chacha20poly1305_KEYBYTES = 32,
+   crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_aead_chacha20poly1305_NPUBBYTES = 8,
+   crypto_aead_chacha20poly1305_NSECBYTES = 0,
+
+   crypto_aead_chacha20poly1305_ietf_ABYTES = 16,
+   crypto_aead_chacha20poly1305_ietf_KEYBYTES = 32,
+   crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_aead_chacha20poly1305_ietf_NPUBBYTES = 12,
+   crypto_aead_chacha20poly1305_ietf_NSECBYTES = 0,
+
+   crypto_aead_xchacha20poly1305_ietf_ABYTES = 16,
+   crypto_aead_xchacha20poly1305_ietf_KEYBYTES = 32,
+   crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_aead_xchacha20poly1305_ietf_NPUBBYTES = 24,
+   crypto_aead_xchacha20poly1305_ietf_NSECBYTES = 0,
+
+   crypto_auth_hmacsha256_BYTES = 32,
+   crypto_auth_hmacsha256_KEYBYTES = 32,
+   crypto_auth_hmacsha512256_BYTES = 32,
+   crypto_auth_hmacsha512256_KEYBYTES = 32,
+   crypto_auth_hmacsha512_BYTES = 64,
+   crypto_auth_hmacsha512_KEYBYTES = 32,
+
+   crypto_auth_BYTES = crypto_auth_hmacsha512256_BYTES,
+   crypto_auth_KEYBYTES = crypto_auth_hmacsha512256_KEYBYTES,
+
+   crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES = 32,
+   crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES = 16,
+   crypto_box_curve25519xsalsa20poly1305_MACBYTES = 16,
+   crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_box_curve25519xsalsa20poly1305_NONCEBYTES = 24,
+   crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES = 32,
+   crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES = 32,
+   crypto_box_curve25519xsalsa20poly1305_SEEDBYTES = 32,
+   crypto_box_curve25519xsalsa20poly1305_ZEROBYTES = crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES + crypto_box_curve25519xsalsa20poly1305_MACBYTES,
+
+   crypto_box_BEFORENMBYTES = crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES,
+   crypto_box_BOXZEROBYTES = crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES,
+   crypto_box_MACBYTES = crypto_box_curve25519xsalsa20poly1305_MACBYTES,
+   crypto_box_MESSAGEBYTES_MAX = crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX,
+   crypto_box_NONCEBYTES = crypto_box_curve25519xsalsa20poly1305_NONCEBYTES,
+   crypto_box_PUBLICKEYBYTES = crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES,
+   crypto_box_SECRETKEYBYTES = crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES,
+   crypto_box_SEEDBYTES = crypto_box_curve25519xsalsa20poly1305_SEEDBYTES,
+   crypto_box_ZEROBYTES = crypto_box_curve25519xsalsa20poly1305_ZEROBYTES,
+
+   crypto_core_hchacha20_CONSTBYTES = 16,
+   crypto_core_hchacha20_INPUTBYTES = 16,
+   crypto_core_hchacha20_KEYBYTES = 32,
+   crypto_core_hchacha20_OUTPUTBYTES = 32,
+
+   crypto_core_hsalsa20_CONSTBYTES = 16,
+   crypto_core_hsalsa20_INPUTBYTES = 16,
+   crypto_core_hsalsa20_KEYBYTES = 32,
+   crypto_core_hsalsa20_OUTPUTBYTES = 32,
+
+   crypto_hash_sha256_BYTES = 32,
+   crypto_hash_sha512_BYTES = 64,
+   crypto_hash_BYTES = crypto_hash_sha512_BYTES,
+
+   crypto_onetimeauth_poly1305_BYTES = 16,
+   crypto_onetimeauth_poly1305_KEYBYTES = 32,
+   crypto_onetimeauth_BYTES = crypto_onetimeauth_poly1305_BYTES,
+   crypto_onetimeauth_KEYBYTES = crypto_onetimeauth_poly1305_KEYBYTES,
+
+   crypto_scalarmult_curve25519_BYTES = 32,
+   crypto_scalarmult_curve25519_SCALARBYTES = 32,
+   crypto_scalarmult_BYTES = crypto_scalarmult_curve25519_BYTES,
+   crypto_scalarmult_SCALARBYTES = crypto_scalarmult_curve25519_SCALARBYTES,
+
+   crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES = 16,
+   crypto_secretbox_xsalsa20poly1305_KEYBYTES = 32,
+   crypto_secretbox_xsalsa20poly1305_MACBYTES = 16,
+   crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_secretbox_xsalsa20poly1305_NONCEBYTES = 24,
+   crypto_secretbox_xsalsa20poly1305_ZEROBYTES = crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES + crypto_secretbox_xsalsa20poly1305_MACBYTES,
+
+   crypto_secretbox_BOXZEROBYTES = crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES,
+   crypto_secretbox_KEYBYTES = crypto_secretbox_xsalsa20poly1305_KEYBYTES,
+   crypto_secretbox_MACBYTES = crypto_secretbox_xsalsa20poly1305_MACBYTES,
+   crypto_secretbox_MESSAGEBYTES_MAX = crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX,
+   crypto_secretbox_NONCEBYTES = crypto_secretbox_xsalsa20poly1305_NONCEBYTES,
+   crypto_secretbox_ZEROBYTES = crypto_secretbox_xsalsa20poly1305_ZEROBYTES,
+
+   crypto_shorthash_siphash24_BYTES = 8,
+   crypto_shorthash_siphash24_KEYBYTES = 16,
+   crypto_shorthash_BYTES = crypto_shorthash_siphash24_BYTES,
+   crypto_shorthash_KEYBYTES = crypto_shorthash_siphash24_KEYBYTES,
+
+   crypto_sign_ed25519_BYTES = 64,
+   crypto_sign_ed25519_MESSAGEBYTES_MAX = (SODIUM_SIZE_MAX - crypto_sign_ed25519_BYTES),
+   crypto_sign_ed25519_PUBLICKEYBYTES = 32,
+   crypto_sign_ed25519_SECRETKEYBYTES = (32 + 32),
+   crypto_sign_ed25519_SEEDBYTES = 32,
+   crypto_sign_BYTES = crypto_sign_ed25519_BYTES,
+   crypto_sign_MESSAGEBYTES_MAX = crypto_sign_ed25519_MESSAGEBYTES_MAX,
+   crypto_sign_PUBLICKEYBYTES = crypto_sign_ed25519_PUBLICKEYBYTES,
+   crypto_sign_SECRETKEYBYTES = crypto_sign_ed25519_SECRETKEYBYTES,
+   crypto_sign_SEEDBYTES = crypto_sign_ed25519_SEEDBYTES,
+
+   crypto_stream_chacha20_KEYBYTES = 32,
+   crypto_stream_chacha20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_chacha20_NONCEBYTES = 8,
+   crypto_stream_chacha20_ietf_KEYBYTES = 32,
+   crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_chacha20_ietf_NONCEBYTES = 12,
+   crypto_stream_salsa20_KEYBYTES = 32,
+   crypto_stream_salsa20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_salsa20_NONCEBYTES = 8,
+   crypto_stream_xchacha20_KEYBYTES = 32,
+   crypto_stream_xchacha20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_xchacha20_NONCEBYTES = 24,
+   crypto_stream_xsalsa20_KEYBYTES = 32,
+   crypto_stream_xsalsa20_MESSAGEBYTES_MAX = SODIUM_SIZE_MAX,
+   crypto_stream_xsalsa20_NONCEBYTES = 24,
+   crypto_stream_KEYBYTES = crypto_stream_xsalsa20_KEYBYTES,
+   crypto_stream_MESSAGEBYTES_MAX = crypto_stream_xsalsa20_MESSAGEBYTES_MAX,
+   crypto_stream_NONCEBYTES = crypto_stream_xsalsa20_NONCEBYTES,
+
+   crypto_verify_16_BYTES = 16,
+   crypto_verify_32_BYTES = 32,
+   crypto_verify_64_BYTES = 64,
+
+   randombytes_SEEDBYTES = 32,
+};
+
+inline const char* sodium_version_string() { return "Botan Sodium Compat"; }
+
+inline int sodium_library_version_major() { return 0; }
+
+inline int sodium_library_version_minor() { return 0; }
+
+inline int sodium_library_minimal() { return 0; }
+
+inline int sodium_init() { return 0; }
+
+// sodium/crypto_verify_{16,32,64}.h
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_16(const uint8_t x[16], const uint8_t y[16]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_32(const uint8_t x[32], const uint8_t y[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_verify_64(const uint8_t x[64], const uint8_t y[64]);
+
+// sodium/utils.h
+BOTAN_PUBLIC_API(2,11)
+void sodium_memzero(void* ptr, size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_memcmp(const void* x, const void* y, size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_compare(const uint8_t x[], const uint8_t y[], size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_is_zero(const uint8_t nonce[], size_t nlen);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_increment(uint8_t n[], size_t nlen);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_add(uint8_t a[], const uint8_t b[], size_t len);
+
+BOTAN_PUBLIC_API(2,11)
+void* sodium_malloc(size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void* sodium_allocarray(size_t count, size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void sodium_free(void* ptr);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_mprotect_noaccess(void* ptr);
+
+BOTAN_PUBLIC_API(2,11)
+int sodium_mprotect_readwrite(void* ptr);
+
+// sodium/randombytes.h
+
+inline size_t randombytes_seedbytes() { return randombytes_SEEDBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+void randombytes_buf(void* buf, size_t size);
+
+BOTAN_PUBLIC_API(2,11)
+void randombytes_buf_deterministic(void* buf, size_t size,
+                                   const uint8_t seed[32]);
+
+BOTAN_PUBLIC_API(2,11)
+uint32_t randombytes_uniform(uint32_t upper_bound);
+
+inline uint32_t randombytes_random()
+   {
+   uint32_t x = 0;
+   randombytes_buf(&x, 4);
+   return x;
+   }
+
+inline void randombytes_stir() {}
+
+inline int randombytes_close() { return 0; }
+
+inline const char* randombytes_implementation_name()
+   {
+   return "botan";
+   }
+
+inline void randombytes(uint8_t buf[], size_t buf_len)
+   {
+   return randombytes_buf(buf, buf_len);
+   }
+
+// sodium/crypto_secretbox_xsalsa20poly1305.h
+
+inline size_t crypto_secretbox_xsalsa20poly1305_keybytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_KEYBYTES;
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_noncebytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_NONCEBYTES;
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_macbytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_MACBYTES;
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_messagebytes_max()
+   {
+   return crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_xsalsa20poly1305(uint8_t ctext[],
+                                      const uint8_t ptext[],
+                                      size_t ptext_len,
+                                      const uint8_t nonce[],
+                                      const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_xsalsa20poly1305_open(uint8_t ptext[],
+                                           const uint8_t ctext[],
+                                           size_t ctext_len,
+                                           const uint8_t nonce[],
+                                           const uint8_t key[]);
+
+inline void crypto_secretbox_xsalsa20poly1305_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_boxzerobytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_BOXZEROBYTES;
+   }
+
+inline size_t crypto_secretbox_xsalsa20poly1305_zerobytes()
+   {
+   return crypto_secretbox_xsalsa20poly1305_ZEROBYTES;
+   }
+
+// sodium/crypto_secretbox.h
+
+inline size_t crypto_secretbox_keybytes() { return crypto_secretbox_KEYBYTES; }
+
+inline size_t crypto_secretbox_noncebytes() { return crypto_secretbox_NONCEBYTES; }
+
+inline size_t crypto_secretbox_macbytes() { return crypto_secretbox_MACBYTES; }
+
+inline size_t crypto_secretbox_messagebytes_max() { return crypto_secretbox_xsalsa20poly1305_MESSAGEBYTES_MAX; }
+
+inline const char* crypto_secretbox_primitive() { return "xsalsa20poly1305"; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_detached(uint8_t ctext[], uint8_t mac[],
+                              const uint8_t ptext[],
+                              size_t ptext_len,
+                              const uint8_t nonce[],
+                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_secretbox_open_detached(uint8_t ptext[],
+                                   const uint8_t ctext[],
+                                   const uint8_t mac[],
+                                   size_t ctext_len,
+                                   const uint8_t nonce[],
+                                   const uint8_t key[]);
+
+inline int crypto_secretbox_easy(uint8_t ctext[], const uint8_t ptext[],
+                                 size_t ptext_len, const uint8_t nonce[],
+                                 const uint8_t key[])
+   {
+   return crypto_secretbox_detached(ctext + crypto_secretbox_MACBYTES, ctext,
+                                    ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_secretbox_open_easy(uint8_t out[], const uint8_t ctext[], size_t ctext_len,
+                                      const uint8_t nonce[], const uint8_t key[])
+   {
+   if(ctext_len < crypto_secretbox_MACBYTES)
+      {
+      return -1;
+      }
+
+   return crypto_secretbox_open_detached(out, ctext + crypto_secretbox_MACBYTES,
+                                         ctext, ctext_len - crypto_secretbox_MACBYTES,
+                                         nonce, key);
+   }
+
+inline void crypto_secretbox_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+inline size_t crypto_secretbox_zerobytes()
+   {
+   return crypto_secretbox_ZEROBYTES;
+   }
+
+inline size_t crypto_secretbox_boxzerobytes()
+   {
+   return crypto_secretbox_BOXZEROBYTES;
+   }
+
+inline int crypto_secretbox(uint8_t ctext[], const uint8_t ptext[],
+                            size_t ptext_len, const uint8_t nonce[],
+                            const uint8_t key[])
+   {
+   return crypto_secretbox_xsalsa20poly1305(ctext, ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_secretbox_open(uint8_t ptext[], const uint8_t ctext[],
+                                 size_t ctext_len, const uint8_t nonce[],
+                                 const uint8_t key[])
+   {
+   return crypto_secretbox_xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, key);
+   }
+
+// sodium/crypto_aead_xchacha20poly1305.h
+
+inline size_t crypto_aead_chacha20poly1305_ietf_keybytes()
+   {
+   return crypto_aead_chacha20poly1305_ietf_KEYBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_nsecbytes()
+   {
+   return crypto_aead_chacha20poly1305_ietf_NSECBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_npubbytes()
+   {
+   return crypto_aead_chacha20poly1305_ietf_NPUBBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_abytes()
+   {
+   return crypto_aead_chacha20poly1305_ietf_ABYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_ietf_messagebytes_max()
+   {
+   return crypto_aead_chacha20poly1305_ietf_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_encrypt(uint8_t ctext[],
+                                              unsigned long long* ctext_len,
+                                              const uint8_t ptext[],
+                                              size_t ptext_len,
+                                              const uint8_t ad[],
+                                              size_t ad_len,
+                                              const uint8_t unused_secret_nonce[],
+                                              const uint8_t nonce[],
+                                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_decrypt(uint8_t ptext[],
+                                              unsigned long long* ptext_len,
+                                              uint8_t unused_secret_nonce[],
+                                              const uint8_t ctext[],
+                                              size_t ctext_len,
+                                              const uint8_t ad[],
+                                              size_t ad_len,
+                                              const uint8_t nonce[],
+                                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+                                                       uint8_t mac[],
+                                                       unsigned long long* mac_len,
+                                                       const uint8_t ptext[],
+                                                       size_t ptext_len,
+                                                       const uint8_t ad[],
+                                                       size_t ad_len,
+                                                       const uint8_t unused_secret_nonce[],
+                                                       const uint8_t nonce[],
+                                                       const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_ietf_decrypt_detached(uint8_t m[],
+                                                       uint8_t unused_secret_nonce[],
+                                                       const uint8_t ctext[],
+                                                       size_t ctext_len,
+                                                       const uint8_t mac[],
+                                                       const uint8_t ad[],
+                                                       size_t ad_len,
+                                                       const uint8_t nonce[],
+                                                       const uint8_t key[]);
+
+inline void crypto_aead_chacha20poly1305_ietf_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, crypto_aead_chacha20poly1305_ietf_KEYBYTES);
+   }
+
+inline size_t crypto_aead_chacha20poly1305_keybytes()
+   {
+   return crypto_aead_chacha20poly1305_KEYBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_nsecbytes()
+   {
+   return crypto_aead_chacha20poly1305_NSECBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_npubbytes()
+   {
+   return crypto_aead_chacha20poly1305_NPUBBYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_abytes()
+   {
+   return crypto_aead_chacha20poly1305_ABYTES;
+   }
+
+inline size_t crypto_aead_chacha20poly1305_messagebytes_max()
+   {
+   return crypto_aead_chacha20poly1305_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+                                         unsigned long long* ctext_len,
+                                         const uint8_t ptext[],
+                                         size_t ptext_len,
+                                         const uint8_t ad[],
+                                         size_t ad_len,
+                                         const uint8_t unused_secret_nonce[],
+                                         const uint8_t nonce[],
+                                         const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_decrypt(uint8_t m[],
+                                         unsigned long long* ptext_len,
+                                         uint8_t unused_secret_nonce[],
+                                         const uint8_t ctext[],
+                                         size_t ctext_len,
+                                         const uint8_t ad[],
+                                         size_t ad_len,
+                                         const uint8_t nonce[],
+                                         const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+                                                  uint8_t mac[],
+                                                  unsigned long long* mac_len,
+                                                  const uint8_t ptext[],
+                                                  size_t ptext_len,
+                                                  const uint8_t ad[],
+                                                  size_t ad_len,
+                                                  const uint8_t unused_secret_nonce[],
+                                                  const uint8_t nonce[],
+                                                  const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_chacha20poly1305_decrypt_detached(uint8_t m[],
+                                                  uint8_t unused_secret_nonce[],
+                                                  const uint8_t ctext[],
+                                                  size_t ctext_len,
+                                                  const uint8_t mac[],
+                                                  const uint8_t ad[],
+                                                  size_t ad_len,
+                                                  const uint8_t nonce[],
+                                                  const uint8_t key[]);
+
+inline void crypto_aead_chacha20poly1305_keygen(uint8_t k[32])
+   {
+   randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_aead_xchacha20poly1305.h
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_keybytes()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_KEYBYTES;
+   }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_nsecbytes()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_NSECBYTES;
+   }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_npubbytes()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_NPUBBYTES;
+   }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_abytes()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_ABYTES;
+   }
+
+inline size_t crypto_aead_xchacha20poly1305_ietf_messagebytes_max()
+   {
+   return crypto_aead_xchacha20poly1305_ietf_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_encrypt(uint8_t ctext[],
+                                               unsigned long long* ctext_len,
+                                               const uint8_t ptext[],
+                                               size_t ptext_len,
+                                               const uint8_t ad[],
+                                               size_t ad_len,
+                                               const uint8_t unused_secret_nonce[],
+                                               const uint8_t nonce[],
+                                               const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_decrypt(uint8_t ptext[],
+                                               unsigned long long* ptext_len,
+                                               uint8_t unused_secret_nonce[],
+                                               const uint8_t ctext[],
+                                               size_t ctext_len,
+                                               const uint8_t ad[],
+                                               size_t ad_len,
+                                               const uint8_t nonce[],
+                                               const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+                                                        uint8_t mac[],
+                                                        unsigned long long* mac_len,
+                                                        const uint8_t ptext[],
+                                                        size_t ptext_len,
+                                                        const uint8_t ad[],
+                                                        size_t ad_len,
+                                                        const uint8_t unused_secret_nonce[],
+                                                        const uint8_t nonce[],
+                                                        const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_aead_xchacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+                                                        uint8_t unused_secret_nonce[],
+                                                        const uint8_t ctext[],
+                                                        size_t ctext_len,
+                                                        const uint8_t mac[],
+                                                        const uint8_t ad[],
+                                                        size_t ad_len,
+                                                        const uint8_t nonce[],
+                                                        const uint8_t key[]);
+
+inline void crypto_aead_xchacha20poly1305_ietf_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_box_curve25519xsalsa20poly1305.h
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_seedbytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_SEEDBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_publickeybytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_PUBLICKEYBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_secretkeybytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_SECRETKEYBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_beforenmbytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_BEFORENMBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_noncebytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_NONCEBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_macbytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_MACBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_messagebytes_max()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_MESSAGEBYTES_MAX;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_boxzerobytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_BOXZEROBYTES;
+   }
+
+inline size_t crypto_box_curve25519xsalsa20poly1305_zerobytes()
+   {
+   return crypto_box_curve25519xsalsa20poly1305_ZEROBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_seed_keypair(uint8_t pk[32],
+                                                       uint8_t sk[32],
+                                                       const uint8_t seed[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_keypair(uint8_t pk[32],
+                                                  uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_beforenm(uint8_t key[],
+                                                   const uint8_t pk[32],
+                                                   const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305(uint8_t ctext[],
+                                          const uint8_t ptext[],
+                                          size_t ptext_len,
+                                          const uint8_t nonce[],
+                                          const uint8_t pk[32],
+                                          const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_curve25519xsalsa20poly1305_open(uint8_t ptext[],
+                                               const uint8_t ctext[],
+                                               size_t ctext_len,
+                                               const uint8_t nonce[],
+                                               const uint8_t pk[32],
+                                               const uint8_t sk[32]);
+
+inline int crypto_box_curve25519xsalsa20poly1305_afternm(uint8_t ctext[],
+                                                         const uint8_t ptext[],
+                                                         size_t ptext_len,
+                                                         const uint8_t nonce[],
+                                                         const uint8_t key[])
+   {
+   return crypto_secretbox_xsalsa20poly1305(ctext, ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_box_curve25519xsalsa20poly1305_open_afternm(uint8_t ptext[],
+                                                              const uint8_t ctext[],
+                                                              size_t ctext_len,
+                                                              const uint8_t nonce[],
+                                                              const uint8_t key[])
+   {
+   return crypto_secretbox_xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, key);
+   }
+
+// sodium/crypto_box.h
+
+inline size_t crypto_box_seedbytes()
+   {
+   return crypto_box_SEEDBYTES;
+   }
+
+inline size_t crypto_box_publickeybytes()
+   {
+   return crypto_box_PUBLICKEYBYTES;
+   }
+
+inline size_t crypto_box_secretkeybytes()
+   {
+   return crypto_box_SECRETKEYBYTES;
+   }
+
+inline size_t crypto_box_noncebytes()
+   {
+   return crypto_box_NONCEBYTES;
+   }
+
+inline size_t crypto_box_macbytes()
+   {
+   return crypto_box_MACBYTES;
+   }
+
+inline size_t crypto_box_messagebytes_max()
+   {
+   return crypto_box_MESSAGEBYTES_MAX;
+   }
+
+inline size_t crypto_box_beforenmbytes()
+   {
+   return crypto_box_BEFORENMBYTES;
+   }
+
+inline const char* crypto_box_primitive() { return "curve25519xsalsa20poly1305"; }
+
+inline int crypto_box_seed_keypair(uint8_t pk[32], uint8_t sk[32],
+                                   const uint8_t seed[])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_seed_keypair(pk, sk, seed);
+   }
+
+inline int crypto_box_keypair(uint8_t pk[32], uint8_t sk[32])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_keypair(pk, sk);
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_detached(uint8_t ctext[], uint8_t mac[],
+                        const uint8_t ptext[], size_t ptext_len,
+                        const uint8_t nonce[], const uint8_t pk[32],
+                        const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_box_open_detached(uint8_t ptext[], const uint8_t ctext[],
+                             const uint8_t mac[],
+                             size_t ctext_len,
+                             const uint8_t nonce[],
+                             const uint8_t pk[32],
+                             const uint8_t sk[32]);
+
+inline int crypto_box_easy(uint8_t ctext[], const uint8_t ptext[],
+                           size_t ptext_len, const uint8_t nonce[],
+                           const uint8_t pk[32], const uint8_t sk[32])
+   {
+   return crypto_box_detached(ctext + crypto_box_MACBYTES, ctext, ptext, ptext_len, nonce, pk, sk);
+   }
+
+inline int crypto_box_open_easy(uint8_t ptext[], const uint8_t ctext[],
+                                size_t ctext_len, const uint8_t nonce[],
+                                const uint8_t pk[32], const uint8_t sk[32])
+   {
+   if(ctext_len < crypto_box_MACBYTES)
+      {
+      return -1;
+      }
+
+   return crypto_box_open_detached(ptext, ctext + crypto_box_MACBYTES,
+                                   ctext, ctext_len - crypto_box_MACBYTES,
+                                   nonce, pk, sk);
+   }
+
+inline int crypto_box_beforenm(uint8_t key[], const uint8_t pk[32],
+                               const uint8_t sk[32])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_beforenm(key, pk, sk);
+   }
+
+inline int crypto_box_afternm(uint8_t ctext[], const uint8_t ptext[],
+                              size_t ptext_len, const uint8_t nonce[],
+                              const uint8_t key[])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_afternm(ctext, ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_box_open_afternm(uint8_t ptext[], const uint8_t ctext[],
+                                   size_t ctext_len, const uint8_t nonce[],
+                                   const uint8_t key[])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_open_afternm(ptext, ctext, ctext_len, nonce, key);
+   }
+
+inline int crypto_box_open_detached_afternm(uint8_t ptext[], const uint8_t ctext[],
+                                            const uint8_t mac[],
+                                            size_t ctext_len, const uint8_t nonce[],
+                                            const uint8_t key[])
+   {
+   return crypto_secretbox_open_detached(ptext, ctext, mac, ctext_len, nonce, key);
+   }
+
+inline int crypto_box_open_easy_afternm(uint8_t ptext[], const uint8_t ctext[],
+                                        size_t ctext_len, const uint8_t nonce[],
+                                        const uint8_t key[])
+   {
+   if(ctext_len < crypto_box_MACBYTES)
+      {
+      return -1;
+      }
+
+   return crypto_box_open_detached_afternm(ptext, ctext + crypto_box_MACBYTES,
+                                           ctext, ctext_len - crypto_box_MACBYTES,
+                                           nonce, key);
+   }
+
+inline int crypto_box_detached_afternm(uint8_t ctext[], uint8_t mac[],
+                                       const uint8_t ptext[], size_t ptext_len,
+                                       const uint8_t nonce[], const uint8_t key[])
+   {
+   return crypto_secretbox_detached(ctext, mac, ptext, ptext_len, nonce, key);
+   }
+
+inline int crypto_box_easy_afternm(uint8_t ctext[], const uint8_t ptext[],
+                                   size_t ptext_len, const uint8_t nonce[],
+                                   const uint8_t key[])
+   {
+   return crypto_box_detached_afternm(ctext + crypto_box_MACBYTES, ctext, ptext, ptext_len, nonce, key);
+   }
+
+inline size_t crypto_box_zerobytes() { return crypto_box_ZEROBYTES; }
+
+inline size_t crypto_box_boxzerobytes() { return crypto_box_BOXZEROBYTES; }
+
+inline int crypto_box(uint8_t ctext[], const uint8_t ptext[],
+                      size_t ptext_len, const uint8_t nonce[],
+                      const uint8_t pk[32], const uint8_t sk[32])
+   {
+   return crypto_box_curve25519xsalsa20poly1305(ctext, ptext, ptext_len, nonce, pk, sk);
+   }
+
+inline int crypto_box_open(uint8_t ptext[], const uint8_t ctext[],
+                           size_t ctext_len, const uint8_t nonce[],
+                           const uint8_t pk[32], const uint8_t sk[32])
+   {
+   return crypto_box_curve25519xsalsa20poly1305_open(ptext, ctext, ctext_len, nonce, pk, sk);
+   }
+
+// sodium/crypto_hash_sha512.h
+
+inline size_t crypto_hash_sha512_bytes() { return crypto_hash_sha512_BYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_hash_sha512(uint8_t out[64], const uint8_t in[], size_t in_len);
+
+// sodium/crypto_auth_hmacsha512.h
+
+inline size_t crypto_auth_hmacsha512_bytes() { return crypto_auth_hmacsha512_BYTES; }
+
+inline size_t crypto_auth_hmacsha512_keybytes() { return crypto_auth_hmacsha512_KEYBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512(uint8_t out[],
+                           const uint8_t in[],
+                           size_t in_len,
+                           const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512_verify(const uint8_t h[],
+                                  const uint8_t in[],
+                                  size_t in_len,
+                                  const uint8_t key[]);
+
+inline void crypto_auth_hmacsha512_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_auth_hmacsha512256.h
+
+inline size_t crypto_auth_hmacsha512256_bytes()
+   {
+   return crypto_auth_hmacsha512256_BYTES;
+   }
+
+inline size_t crypto_auth_hmacsha512256_keybytes()
+   {
+   return crypto_auth_hmacsha512256_KEYBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512256(uint8_t out[],
+                              const uint8_t in[],
+                              size_t in_len,
+                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha512256_verify(const uint8_t h[],
+                                     const uint8_t in[],
+                                     size_t in_len,
+                                     const uint8_t key[]);
+
+inline void crypto_auth_hmacsha512256_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_auth.h
+
+inline size_t crypto_auth_bytes() { return crypto_auth_BYTES; }
+
+inline size_t crypto_auth_keybytes() { return crypto_auth_KEYBYTES; }
+
+inline const char* crypto_auth_primitive() { return "hmacsha512256"; }
+
+inline int crypto_auth(uint8_t out[], const uint8_t in[],
+                       size_t in_len, const uint8_t key[])
+   {
+   return crypto_auth_hmacsha512256(out, in, in_len, key);
+   }
+
+inline int crypto_auth_verify(const uint8_t mac[], const uint8_t in[],
+                              size_t in_len, const uint8_t key[])
+   {
+   return crypto_auth_hmacsha512256_verify(mac, in, in_len, key);
+   }
+
+inline void crypto_auth_keygen(uint8_t k[])
+   {
+   return randombytes_buf(k, crypto_auth_KEYBYTES);
+   }
+
+// sodium/crypto_hash_sha256.h
+
+inline size_t crypto_hash_sha256_bytes()
+   {
+   return crypto_hash_sha256_BYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_hash_sha256(uint8_t out[], const uint8_t in[], size_t in_len);
+
+// sodium/crypto_auth_hmacsha256.h
+
+inline size_t crypto_auth_hmacsha256_bytes()
+   {
+   return crypto_auth_hmacsha256_BYTES;
+   }
+
+inline size_t crypto_auth_hmacsha256_keybytes()
+   {
+   return crypto_auth_hmacsha256_KEYBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha256(uint8_t out[],
+                           const uint8_t in[],
+                           size_t in_len,
+                           const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_auth_hmacsha256_verify(const uint8_t h[],
+                                  const uint8_t in[],
+                                  size_t in_len,
+                                  const uint8_t key[]);
+
+inline void crypto_auth_hmacsha256_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_stream_xsalsa20.h
+
+inline size_t crypto_stream_xsalsa20_keybytes()
+   {
+   return crypto_stream_xsalsa20_KEYBYTES;
+   }
+
+inline size_t crypto_stream_xsalsa20_noncebytes()
+   {
+   return crypto_stream_xsalsa20_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_xsalsa20_messagebytes_max()
+   {
+   return crypto_stream_xsalsa20_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20(uint8_t out[], size_t ctext_len,
+                           const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20_xor(uint8_t out[], const uint8_t ptext[],
+                               size_t ptext_len, const uint8_t nonce[],
+                               const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xsalsa20_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                  size_t ptext_len,
+                                  const uint8_t nonce[], uint64_t ic,
+                                  const uint8_t key[]);
+
+inline void crypto_stream_xsalsa20_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_core_hsalsa20.h
+
+inline size_t crypto_core_hsalsa20_outputbytes()
+   {
+   return crypto_core_hsalsa20_OUTPUTBYTES;
+   }
+
+inline size_t crypto_core_hsalsa20_inputbytes()
+   {
+   return crypto_core_hsalsa20_INPUTBYTES;
+   }
+
+inline size_t crypto_core_hsalsa20_keybytes()
+   {
+   return crypto_core_hsalsa20_KEYBYTES;
+   }
+
+inline size_t crypto_core_hsalsa20_constbytes()
+   {
+   return crypto_core_hsalsa20_CONSTBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_core_hsalsa20(uint8_t out[], const uint8_t in[],
+                         const uint8_t key[], const uint8_t c[]);
+
+// sodium/crypto_hash.h
+
+inline size_t crypto_hash_bytes()
+   {
+   return crypto_hash_BYTES;
+   }
+
+inline int crypto_hash(uint8_t out[], const uint8_t in[], size_t in_len)
+   {
+   return crypto_hash_sha512(out, in, in_len);
+   }
+
+inline const char* crypto_hash_primitive() { return "sha512"; }
+
+// sodium/crypto_onetimeauth_poly1305.h
+
+inline size_t crypto_onetimeauth_poly1305_bytes()
+   {
+   return crypto_onetimeauth_poly1305_BYTES;
+   }
+
+inline size_t crypto_onetimeauth_poly1305_keybytes()
+   {
+   return crypto_onetimeauth_poly1305_KEYBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_onetimeauth_poly1305(uint8_t out[],
+                                const uint8_t in[],
+                                size_t in_len,
+                                const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_onetimeauth_poly1305_verify(const uint8_t h[],
+                                       const uint8_t in[],
+                                       size_t in_len,
+                                       const uint8_t key[]);
+
+inline void crypto_onetimeauth_poly1305_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_onetimeauth.h
+
+inline size_t crypto_onetimeauth_bytes() { return crypto_onetimeauth_BYTES; }
+
+inline size_t crypto_onetimeauth_keybytes() { return crypto_onetimeauth_KEYBYTES; }
+
+inline const char* crypto_onetimeauth_primitive() { return "poly1305"; }
+
+inline int crypto_onetimeauth(uint8_t out[], const uint8_t in[],
+                              size_t in_len, const uint8_t key[])
+   {
+   return crypto_onetimeauth_poly1305(out, in, in_len, key);
+   }
+
+inline int crypto_onetimeauth_verify(const uint8_t h[], const uint8_t in[],
+                                     size_t in_len, const uint8_t key[])
+   {
+   return crypto_onetimeauth_poly1305_verify(h, in, in_len, key);
+   }
+
+inline void crypto_onetimeauth_keygen(uint8_t k[32])
+   {
+   return crypto_onetimeauth_poly1305_keygen(k);
+   }
+
+// sodium/crypto_scalarmult_curve25519.h
+
+inline size_t crypto_scalarmult_curve25519_bytes()
+   {
+   return crypto_scalarmult_curve25519_BYTES;
+   }
+
+inline size_t crypto_scalarmult_curve25519_scalarbytes()
+   {
+   return crypto_scalarmult_curve25519_SCALARBYTES;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_scalarmult_curve25519(uint8_t out[], const uint8_t scalar[], const uint8_t basepoint[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_scalarmult_curve25519_base(uint8_t out[], const uint8_t scalar[]);
+
+// sodium/crypto_scalarmult.h
+
+inline size_t crypto_scalarmult_bytes() { return crypto_scalarmult_curve25519_bytes(); }
+
+inline size_t crypto_scalarmult_scalarbytes() { return crypto_scalarmult_curve25519_scalarbytes(); }
+
+inline const char* crypto_scalarmult_primitive() { return "curve25519"; }
+
+inline int crypto_scalarmult_base(uint8_t out[], const uint8_t scalar[])
+   {
+   return crypto_scalarmult_curve25519_base(out, scalar);
+   }
+
+inline int crypto_scalarmult(uint8_t out[], const uint8_t scalar[], const uint8_t base[])
+   {
+   return crypto_scalarmult_curve25519(out, scalar, base);
+   }
+
+// sodium/crypto_stream_chacha20.h
+
+inline size_t crypto_stream_chacha20_keybytes()
+   {
+   return crypto_stream_chacha20_KEYBYTES;
+   }
+
+inline size_t crypto_stream_chacha20_noncebytes()
+   {
+   return crypto_stream_chacha20_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_chacha20_messagebytes_max()
+   {
+   return crypto_stream_chacha20_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20(uint8_t out[], size_t ctext_len,
+                           const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_xor(uint8_t out[], const uint8_t ptext[],
+                               size_t ptext_len, const uint8_t nonce[],
+                               const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                  size_t ptext_len,
+                                  const uint8_t nonce[], uint64_t ic,
+                                  const uint8_t key[]);
+
+inline void crypto_stream_chacha20_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+inline size_t crypto_stream_chacha20_ietf_keybytes()
+   {
+   return crypto_stream_chacha20_ietf_KEYBYTES;
+   }
+
+inline size_t crypto_stream_chacha20_ietf_noncebytes()
+   {
+   return crypto_stream_chacha20_ietf_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_chacha20_ietf_messagebytes_max()
+   {
+   return crypto_stream_chacha20_ietf_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf(uint8_t out[], size_t ctext_len,
+                                const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf_xor(uint8_t out[], const uint8_t ptext[],
+                                    size_t ptext_len, const uint8_t nonce[],
+                                    const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_chacha20_ietf_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                       size_t ptext_len,
+                                       const uint8_t nonce[], uint32_t ic,
+                                       const uint8_t key[]);
+
+inline void crypto_stream_chacha20_ietf_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_stream_xchacha20.h
+
+inline size_t crypto_stream_xchacha20_keybytes()
+   {
+   return crypto_stream_xchacha20_KEYBYTES;
+   }
+
+inline size_t crypto_stream_xchacha20_noncebytes()
+   {
+   return crypto_stream_xchacha20_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_xchacha20_messagebytes_max()
+   {
+   return crypto_stream_xchacha20_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20(uint8_t out[], size_t ctext_len,
+                            const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20_xor(uint8_t out[], const uint8_t ptext[],
+                                size_t ptext_len, const uint8_t nonce[],
+                                const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_xchacha20_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                   size_t ptext_len,
+                                   const uint8_t nonce[], uint64_t ic,
+                                   const uint8_t key[]);
+
+inline void crypto_stream_xchacha20_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, crypto_stream_xchacha20_KEYBYTES);
+   }
+
+// sodium/crypto_stream_salsa20.h
+
+inline size_t crypto_stream_salsa20_keybytes()
+   {
+   return crypto_stream_xsalsa20_KEYBYTES;
+   }
+
+inline size_t crypto_stream_salsa20_noncebytes()
+   {
+   return crypto_stream_salsa20_NONCEBYTES;
+   }
+
+inline size_t crypto_stream_salsa20_messagebytes_max()
+   {
+   return crypto_stream_salsa20_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20(uint8_t out[], size_t ctext_len,
+                          const uint8_t nonce[], const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20_xor(uint8_t out[], const uint8_t ptext[],
+                              size_t ptext_len, const uint8_t nonce[],
+                              const uint8_t key[]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_stream_salsa20_xor_ic(uint8_t out[], const uint8_t ptext[],
+                                 size_t ptext_len,
+                                 const uint8_t nonce[], uint64_t ic,
+                                 const uint8_t key[]);
+
+inline void crypto_stream_salsa20_keygen(uint8_t k[32])
+   {
+   return randombytes_buf(k, 32);
+   }
+
+// sodium/crypto_stream.h
+
+inline size_t crypto_stream_keybytes() { return crypto_stream_xsalsa20_keybytes(); }
+
+inline size_t crypto_stream_noncebytes() { return crypto_stream_xsalsa20_noncebytes(); }
+
+inline size_t crypto_stream_messagebytes_max() { return crypto_stream_MESSAGEBYTES_MAX; }
+
+inline const char* crypto_stream_primitive() { return "xsalsa20"; }
+
+inline int crypto_stream(uint8_t out[], size_t out_len,
+                         const uint8_t nonce[24], const uint8_t key[32])
+   {
+   return crypto_stream_xsalsa20(out, out_len, nonce, key);
+   }
+
+inline int crypto_stream_xor(uint8_t out[], const uint8_t in[], size_t in_len,
+                             const uint8_t nonce[24], const uint8_t key[32])
+   {
+   return crypto_stream_xsalsa20_xor(out, in, in_len, nonce, key);
+   }
+
+inline void crypto_stream_keygen(uint8_t key[32])
+   {
+   return randombytes_buf(key, 32);
+   }
+
+// sodium/crypto_shorthash_siphash24.h
+
+inline size_t crypto_shorthash_siphash24_bytes() { return crypto_shorthash_siphash24_BYTES; }
+
+inline size_t crypto_shorthash_siphash24_keybytes() { return crypto_shorthash_siphash24_KEYBYTES; }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_shorthash_siphash24(uint8_t out[8], const uint8_t in[], size_t in_len, const uint8_t key[16]);
+
+// sodium/crypto_shorthash.h
+
+inline size_t crypto_shorthash_bytes() { return crypto_shorthash_siphash24_bytes(); }
+
+inline size_t crypto_shorthash_keybytes() { return crypto_shorthash_siphash24_keybytes(); }
+
+inline const char* crypto_shorthash_primitive() { return "siphash24"; }
+
+inline int crypto_shorthash(uint8_t out[], const uint8_t in[],
+                            size_t in_len, const uint8_t k[16])
+   {
+   return crypto_shorthash_siphash24(out, in, in_len, k);
+   }
+
+inline void crypto_shorthash_keygen(uint8_t k[16])
+   {
+   randombytes_buf(k, crypto_shorthash_siphash24_KEYBYTES);
+   }
+
+// sodium/crypto_sign_ed25519.h
+
+inline size_t crypto_sign_ed25519_bytes()
+   {
+   return crypto_sign_ed25519_BYTES;
+   }
+
+inline size_t crypto_sign_ed25519_seedbytes()
+   {
+   return crypto_sign_ed25519_SEEDBYTES;
+   }
+
+inline size_t crypto_sign_ed25519_publickeybytes()
+   {
+   return crypto_sign_ed25519_PUBLICKEYBYTES;
+   }
+
+inline size_t crypto_sign_ed25519_secretkeybytes()
+   {
+   return crypto_sign_ed25519_SECRETKEYBYTES;
+   }
+
+inline size_t crypto_sign_ed25519_messagebytes_max()
+   {
+   return crypto_sign_ed25519_MESSAGEBYTES_MAX;
+   }
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_detached(uint8_t sig[],
+                                 unsigned long long* sig_len,
+                                 const uint8_t msg[],
+                                 size_t msg_len,
+                                 const uint8_t sk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_verify_detached(const uint8_t sig[],
+                                        const uint8_t msg[],
+                                        size_t msg_len,
+                                        const uint8_t pk[32]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_keypair(uint8_t pk[32], uint8_t sk[64]);
+
+BOTAN_PUBLIC_API(2,11)
+int crypto_sign_ed25519_seed_keypair(uint8_t pk[32], uint8_t sk[64],
+                                     const uint8_t seed[]);
+
+// sodium/crypto_sign.h
+
+inline size_t crypto_sign_bytes()
+   {
+   return crypto_sign_BYTES;
+   }
+
+inline size_t crypto_sign_seedbytes()
+   {
+   return crypto_sign_SEEDBYTES;
+   }
+
+inline size_t crypto_sign_publickeybytes()
+   {
+   return crypto_sign_PUBLICKEYBYTES;
+   }
+
+inline size_t crypto_sign_secretkeybytes()
+   {
+   return crypto_sign_SECRETKEYBYTES;
+   }
+
+inline size_t crypto_sign_messagebytes_max()
+   {
+   return crypto_sign_MESSAGEBYTES_MAX;
+   }
+
+inline const char* crypto_sign_primitive()
+   {
+   return "ed25519";
+   }
+
+inline int crypto_sign_seed_keypair(uint8_t pk[32], uint8_t sk[32],
+                                    const uint8_t seed[])
+   {
+   return crypto_sign_ed25519_seed_keypair(pk, sk, seed);
+   }
+
+inline int crypto_sign_keypair(uint8_t pk[32], uint8_t sk[32])
+   {
+   return crypto_sign_ed25519_keypair(pk, sk);
+   }
+
+inline int crypto_sign_detached(uint8_t sig[], unsigned long long* sig_len,
+                                const uint8_t msg[], size_t msg_len,
+                                const uint8_t sk[32])
+   {
+   return crypto_sign_ed25519_detached(sig, sig_len, msg, msg_len, sk);
+   }
+
+inline int crypto_sign_verify_detached(const uint8_t sig[],
+                                       const uint8_t in[],
+                                       size_t in_len,
+                                       const uint8_t pk[32])
+   {
+   return crypto_sign_ed25519_verify_detached(sig, in, in_len, pk);
+   }
+
+}
+
+}
+
+#endif
diff --git a/src/lib/compat/sodium/sodium_25519.cpp b/src/lib/compat/sodium/sodium_25519.cpp
new file mode 100644
index 000000000..1d520d7a3
--- /dev/null
+++ b/src/lib/compat/sodium/sodium_25519.cpp
@@ -0,0 +1,60 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/ed25519.h>
+#include <botan/curve25519.h>
+
+namespace Botan {
+
+int Sodium::crypto_scalarmult_curve25519(uint8_t out[32], const uint8_t scalar[32], const uint8_t point[32])
+   {
+   curve25519_donna(out, scalar, point);
+   return 0;
+   }
+
+int Sodium::crypto_scalarmult_curve25519_base(uint8_t out[32], const uint8_t scalar[32])
+   {
+   curve25519_basepoint(out, scalar);
+   return 0;
+   }
+int Sodium::crypto_sign_ed25519_detached(uint8_t sig[],
+                                         unsigned long long* sig_len,
+                                         const uint8_t msg[],
+                                         size_t msg_len,
+                                         const uint8_t sk[32])
+   {
+   ed25519_sign(sig, msg, msg_len, sk);
+
+   if(sig_len)
+      *sig_len = 64;
+   return 0;
+   }
+
+int Sodium::crypto_sign_ed25519_verify_detached(const uint8_t sig[64],
+                                                const uint8_t msg[],
+                                                size_t msg_len,
+                                                const uint8_t pk[32])
+   {
+   const bool ok = ed25519_verify(msg, msg_len, sig, pk);
+   return ok ? 0 : -1;
+   }
+
+int Sodium::crypto_sign_ed25519_keypair(uint8_t pk[32], uint8_t sk[64])
+   {
+   secure_vector<uint8_t> seed(32);
+   randombytes_buf(seed.data(), seed.size());
+   return crypto_sign_ed25519_seed_keypair(pk, sk, seed.data());
+   }
+
+int Sodium::crypto_sign_ed25519_seed_keypair(uint8_t pk[32], uint8_t sk[64],
+                                             const uint8_t seed[32])
+   {
+   ed25519_gen_keypair(pk, sk, seed);
+   return 0;
+   }
+
+}
diff --git a/src/lib/compat/sodium/sodium_aead.cpp b/src/lib/compat/sodium/sodium_aead.cpp
new file mode 100644
index 000000000..f81154e73
--- /dev/null
+++ b/src/lib/compat/sodium/sodium_aead.cpp
@@ -0,0 +1,359 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/aead.h>
+
+namespace Botan {
+
+namespace {
+
+int sodium_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+                                         unsigned long long* ctext_len,
+                                         const uint8_t ptext[],
+                                         size_t ptext_len,
+                                         const uint8_t ad[],
+                                         size_t ad_len,
+                                         const uint8_t nonce[],
+                                         size_t nonce_len,
+                                         const uint8_t key[])
+   {
+   auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", ENCRYPTION);
+
+   chacha20poly1305->set_key(key, 32);
+   chacha20poly1305->set_associated_data(ad, ad_len);
+   chacha20poly1305->start(nonce, nonce_len);
+
+   // FIXME do this in-place
+   secure_vector<uint8_t> buf;
+   buf.reserve(ptext_len + 16);
+   buf.assign(ptext, ptext + ptext_len);
+
+   chacha20poly1305->finish(buf);
+
+   copy_mem(ctext, buf.data(), buf.size());
+   if(ctext_len)
+      *ctext_len = buf.size();
+   return 0;
+   }
+
+int sodium_aead_chacha20poly1305_decrypt(uint8_t ptext[],
+                                         unsigned long long* ptext_len,
+                                         const uint8_t ctext[],
+                                         size_t ctext_len,
+                                         const uint8_t ad[],
+                                         size_t ad_len,
+                                         const uint8_t nonce[],
+                                         size_t nonce_len,
+                                         const uint8_t key[])
+   {
+   if(ctext_len < 16)
+      return -1;
+
+   *ptext_len = 0;
+
+   auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", DECRYPTION);
+
+   chacha20poly1305->set_key(key, 32);
+   chacha20poly1305->set_associated_data(ad, ad_len);
+   chacha20poly1305->start(nonce, nonce_len);
+
+   // FIXME do this in-place
+   secure_vector<uint8_t> buf;
+   buf.assign(ctext, ctext + ctext_len);
+
+   try
+      {
+      chacha20poly1305->finish(buf);
+      }
+   catch(Invalid_Authentication_Tag&)
+      {
+      return -1;
+      }
+
+   *ptext_len = ctext_len - 16;
+
+   copy_mem(ptext, buf.data(), buf.size());
+   return 0;
+   }
+
+int sodium_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+                                                  uint8_t mac[],
+                                                  const uint8_t ptext[],
+                                                  size_t ptext_len,
+                                                  const uint8_t ad[],
+                                                  size_t ad_len,
+                                                  const uint8_t nonce[],
+                                                  size_t nonce_len,
+                                                  const uint8_t key[])
+   {
+   auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", ENCRYPTION);
+
+   chacha20poly1305->set_key(key, 32);
+   chacha20poly1305->set_associated_data(ad, ad_len);
+   chacha20poly1305->start(nonce, nonce_len);
+
+   // FIXME do this in-place
+   secure_vector<uint8_t> buf;
+   buf.reserve(ptext_len + 16);
+   buf.assign(ptext, ptext + ptext_len);
+
+   chacha20poly1305->finish(buf);
+
+   copy_mem(ctext, buf.data(), ptext_len);
+   copy_mem(mac, buf.data() + ptext_len, 16);
+   return 0;
+   }
+
+int sodium_aead_chacha20poly1305_decrypt_detached(uint8_t ptext[],
+                                                  const uint8_t ctext[],
+                                                  size_t ctext_len,
+                                                  const uint8_t mac[],
+                                                  const uint8_t ad[],
+                                                  size_t ad_len,
+                                                  const uint8_t nonce[],
+                                                  size_t nonce_len,
+                                                  const uint8_t key[])
+   {
+   auto chacha20poly1305 = AEAD_Mode::create_or_throw("ChaCha20Poly1305", DECRYPTION);
+
+   chacha20poly1305->set_key(key, 32);
+   chacha20poly1305->set_associated_data(ad, ad_len);
+   chacha20poly1305->start(nonce, nonce_len);
+
+   // FIXME do this in-place
+   secure_vector<uint8_t> buf;
+   buf.reserve(ctext_len + 16);
+   buf.assign(ctext, ctext + ctext_len);
+   buf.insert(buf.end(), mac, mac + 16);
+
+   try
+      {
+      chacha20poly1305->finish(buf);
+      }
+   catch(Invalid_Authentication_Tag&)
+      {
+      return -1;
+      }
+
+   copy_mem(ptext, buf.data(), buf.size());
+   return 0;
+   }
+
+}
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_encrypt(uint8_t ctext[],
+                                                      unsigned long long* ctext_len,
+                                                      const uint8_t ptext[],
+                                                      size_t ptext_len,
+                                                      const uint8_t ad[],
+                                                      size_t ad_len,
+                                                      const uint8_t* unused_secret_nonce,
+                                                      const uint8_t nonce[],
+                                                      const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_encrypt(
+      ctext, ctext_len, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_decrypt(uint8_t ptext[],
+                                                      unsigned long long* ptext_len,
+                                                      uint8_t* unused_secret_nonce,
+                                                      const uint8_t ctext[],
+                                                      size_t ctext_len,
+                                                      const uint8_t ad[],
+                                                      size_t ad_len,
+                                                      const uint8_t nonce[],
+                                                      const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_decrypt(
+      ptext, ptext_len, ctext, ctext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+                                                               uint8_t mac[],
+                                                               unsigned long long* mac_len,
+                                                               const uint8_t ptext[],
+                                                               size_t ptext_len,
+                                                               const uint8_t ad[],
+                                                               size_t ad_len,
+                                                               const uint8_t unused_secret_nonce[],
+                                                               const uint8_t nonce[],
+                                                               const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   if(mac_len)
+      *mac_len = 16;
+
+   return sodium_aead_chacha20poly1305_encrypt_detached(
+      ctext, mac, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+                                                               uint8_t unused_secret_nonce[],
+                                                               const uint8_t ctext[],
+                                                               size_t ctext_len,
+                                                               const uint8_t mac[],
+                                                               const uint8_t ad[],
+                                                               size_t ad_len,
+                                                               const uint8_t nonce[],
+                                                               const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_decrypt_detached(
+      ptext, ctext, ctext_len, mac,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_encrypt(uint8_t ctext[],
+                                                 unsigned long long* ctext_len,
+                                                 const uint8_t ptext[],
+                                                 size_t ptext_len,
+                                                 const uint8_t ad[],
+                                                 size_t ad_len,
+                                                 const uint8_t unused_secret_nonce[],
+                                                 const uint8_t nonce[],
+                                                 const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   return sodium_aead_chacha20poly1305_encrypt(
+      ctext, ctext_len, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_decrypt(uint8_t ptext[],
+                                                 unsigned long long* ptext_len,
+                                                 uint8_t unused_secret_nonce[],
+                                                 const uint8_t ctext[],
+                                                 size_t ctext_len,
+                                                 const uint8_t ad[],
+                                                 size_t ad_len,
+                                                 const uint8_t nonce[],
+                                                 const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   return sodium_aead_chacha20poly1305_decrypt(
+      ptext, ptext_len, ctext, ctext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_encrypt_detached(uint8_t ctext[],
+                                                          uint8_t mac[],
+                                                          unsigned long long* mac_len,
+                                                          const uint8_t ptext[],
+                                                          size_t ptext_len,
+                                                          const uint8_t ad[],
+                                                          size_t ad_len,
+                                                          const uint8_t unused_secret_nonce[],
+                                                          const uint8_t nonce[],
+                                                          const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   if(mac_len)
+      *mac_len = 16;
+
+   return sodium_aead_chacha20poly1305_encrypt_detached(
+      ctext, mac, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_chacha20poly1305_decrypt_detached(uint8_t ptext[],
+                                                          uint8_t unused_secret_nonce[],
+                                                          const uint8_t ctext[],
+                                                          size_t ctext_len,
+                                                          const uint8_t mac[],
+                                                          const uint8_t ad[],
+                                                          size_t ad_len,
+                                                          const uint8_t nonce[],
+                                                          const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_decrypt_detached(
+      ptext, ctext, ctext_len, mac,
+      ad, ad_len, nonce, crypto_aead_chacha20poly1305_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt(uint8_t ctext[],
+                                                       unsigned long long* ctext_len,
+                                                       const uint8_t ptext[],
+                                                       size_t ptext_len,
+                                                       const uint8_t ad[],
+                                                       size_t ad_len,
+                                                       const uint8_t unused_secret_nonce[],
+                                                       const uint8_t nonce[],
+                                                       const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_encrypt(
+      ctext, ctext_len, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt(uint8_t ptext[],
+                                                       unsigned long long* ptext_len,
+                                                       uint8_t unused_secret_nonce[],
+                                                       const uint8_t ctext[],
+                                                       size_t ctext_len,
+                                                       const uint8_t ad[],
+                                                       size_t ad_len,
+                                                       const uint8_t nonce[],
+                                                       const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+
+   return sodium_aead_chacha20poly1305_decrypt(
+      ptext, ptext_len, ctext, ctext_len,
+      ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt_detached(uint8_t ctext[],
+                                                                uint8_t mac[],
+                                                                unsigned long long* mac_len,
+                                                                const uint8_t ptext[],
+                                                                size_t ptext_len,
+                                                                const uint8_t ad[],
+                                                                size_t ad_len,
+                                                                const uint8_t unused_secret_nonce[],
+                                                                const uint8_t nonce[],
+                                                                const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   if(mac_len)
+      *mac_len = 16;
+
+   return sodium_aead_chacha20poly1305_encrypt_detached(
+      ctext, mac, ptext, ptext_len,
+      ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+   }
+
+int Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt_detached(uint8_t ptext[],
+                                                                uint8_t unused_secret_nonce[],
+                                                                const uint8_t ctext[],
+                                                                size_t ctext_len,
+                                                                const uint8_t mac[],
+                                                                const uint8_t ad[],
+                                                                size_t ad_len,
+                                                                const uint8_t nonce[],
+                                                                const uint8_t key[])
+   {
+   BOTAN_UNUSED(unused_secret_nonce);
+   return sodium_aead_chacha20poly1305_decrypt_detached(
+      ptext, ctext, ctext_len, mac,
+      ad, ad_len, nonce, crypto_aead_xchacha20poly1305_ietf_npubbytes(), key);
+   }
+
+}
diff --git a/src/lib/compat/sodium/sodium_auth.cpp b/src/lib/compat/sodium/sodium_auth.cpp
new file mode 100644
index 000000000..747b8af33
--- /dev/null
+++ b/src/lib/compat/sodium/sodium_auth.cpp
@@ -0,0 +1,131 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/mac.h>
+#include <botan/hash.h>
+
+namespace Botan {
+
+int Sodium::crypto_hash_sha512(uint8_t out[64], const uint8_t in[], size_t in_len)
+   {
+   auto sha512 = HashFunction::create_or_throw("SHA-512");
+   sha512->update(in, in_len);
+   sha512->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_hash_sha256(uint8_t out[], const uint8_t in[], size_t in_len)
+   {
+   auto sha256 = HashFunction::create_or_throw("SHA-256");
+   sha256->update(in, in_len);
+   sha256->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_shorthash_siphash24(uint8_t out[8], const uint8_t in[],
+                                       size_t in_len, const uint8_t key[16])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("SipHash(2,4)");
+   mac->set_key(key, crypto_shorthash_siphash24_KEYBYTES);
+   mac->update(in, in_len);
+   mac->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_onetimeauth_poly1305(uint8_t out[],
+                                        const uint8_t in[],
+                                        size_t in_len,
+                                        const uint8_t key[])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("Poly1305");
+   mac->set_key(key, crypto_onetimeauth_poly1305_KEYBYTES);
+   mac->update(in, in_len);
+   mac->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_onetimeauth_poly1305_verify(const uint8_t mac[],
+                                               const uint8_t in[],
+                                               size_t in_len,
+                                               const uint8_t key[])
+   {
+   secure_vector<uint8_t> computed(crypto_onetimeauth_poly1305_BYTES);
+   crypto_onetimeauth_poly1305(computed.data(), in, in_len, key);
+   return crypto_verify_16(computed.data(), mac) ? 0 : -1;
+   }
+
+int Sodium::crypto_auth_hmacsha512(uint8_t out[],
+                                   const uint8_t in[],
+                                   size_t in_len,
+                                   const uint8_t key[])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+   mac->set_key(key, crypto_auth_hmacsha512_KEYBYTES);
+   mac->update(in, in_len);
+   mac->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_auth_hmacsha512_verify(const uint8_t mac[],
+                                          const uint8_t in[],
+                                          size_t in_len,
+                                          const uint8_t key[])
+   {
+   secure_vector<uint8_t> computed(crypto_auth_hmacsha512_BYTES);
+   crypto_auth_hmacsha512(computed.data(), in, in_len, key);
+   return crypto_verify_64(computed.data(), mac) ? 0 : -1;
+   }
+
+int Sodium::crypto_auth_hmacsha512256(uint8_t out[],
+                                      const uint8_t in[],
+                                      size_t in_len,
+                                      const uint8_t key[])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-512)");
+   mac->set_key(key, crypto_auth_hmacsha512256_KEYBYTES);
+   mac->update(in, in_len);
+
+   secure_vector<uint8_t> buf(64);
+   mac->final(buf);
+
+   copy_mem(out, buf.data(), crypto_auth_hmacsha512256_BYTES);
+   return 0;
+   }
+
+int Sodium::crypto_auth_hmacsha512256_verify(const uint8_t mac[],
+                                             const uint8_t in[],
+                                             size_t in_len,
+                                             const uint8_t key[])
+   {
+   secure_vector<uint8_t> computed(crypto_auth_hmacsha512256_BYTES);
+   crypto_auth_hmacsha512256(computed.data(), in, in_len, key);
+   return crypto_verify_32(computed.data(), mac) ? 0 : -1;
+   }
+
+int Sodium::crypto_auth_hmacsha256(uint8_t out[],
+                                   const uint8_t in[],
+                                   size_t in_len,
+                                   const uint8_t key[])
+   {
+   auto mac = MessageAuthenticationCode::create_or_throw("HMAC(SHA-256)");
+   mac->set_key(key, crypto_auth_hmacsha256_KEYBYTES);
+   mac->update(in, in_len);
+   mac->final(out);
+   return 0;
+   }
+
+int Sodium::crypto_auth_hmacsha256_verify(const uint8_t mac[],
+                                          const uint8_t in[],
+                                          size_t in_len,
+                                          const uint8_t key[])
+   {
+   secure_vector<uint8_t> computed(crypto_auth_hmacsha256_BYTES);
+   crypto_auth_hmacsha256(computed.data(), in, in_len, key);
+   return crypto_verify_32(computed.data(), mac) ? 0 : -1;
+   }
+
+}
diff --git a/src/lib/compat/sodium/sodium_box.cpp b/src/lib/compat/sodium/sodium_box.cpp
new file mode 100644
index 000000000..52b9a0303
--- /dev/null
+++ b/src/lib/compat/sodium/sodium_box.cpp
@@ -0,0 +1,100 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/secmem.h>
+
+namespace Botan {
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_seed_keypair(uint8_t pk[32],
+                                                               uint8_t sk[32],
+                                                               const uint8_t seed[32])
+   {
+   secure_vector<uint8_t> digest(64);
+   crypto_hash_sha512(digest.data(), seed, 32);
+   copy_mem(sk, digest.data(), 32);
+   return crypto_scalarmult_curve25519_base(pk, sk);
+   }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_keypair(uint8_t pk[32],
+                                                          uint8_t sk[32])
+   {
+   randombytes_buf(sk, 32);
+   return crypto_scalarmult_curve25519_base(pk, sk);
+   }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_beforenm(uint8_t key[],
+                                                           const uint8_t pk[32],
+                                                           const uint8_t sk[32])
+   {
+   const uint8_t zero[16] = { 0 };
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_scalarmult_curve25519(shared.data(), sk, pk) != 0)
+      return -1;
+
+   return crypto_core_hsalsa20(key, zero, shared.data(), nullptr);
+   }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305(uint8_t ctext[],
+                                                  const uint8_t ptext[],
+                                                  size_t ptext_len,
+                                                  const uint8_t nonce[],
+                                                  const uint8_t pk[32],
+                                                  const uint8_t sk[32])
+   {
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_box_curve25519xsalsa20poly1305_beforenm(shared.data(), pk, sk) != 0)
+      return -1;
+
+   return crypto_box_curve25519xsalsa20poly1305_afternm(ctext, ptext, ptext_len, nonce, shared.data());
+   }
+
+int Sodium::crypto_box_curve25519xsalsa20poly1305_open(uint8_t ptext[],
+                                                       const uint8_t ctext[],
+                                                       size_t ctext_len,
+                                                       const uint8_t nonce[],
+                                                       const uint8_t pk[32],
+                                                       const uint8_t sk[32])
+   {
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_box_curve25519xsalsa20poly1305_beforenm(shared.data(), pk, sk) != 0)
+      return -1;
+
+   return crypto_box_curve25519xsalsa20poly1305_open_afternm(ptext, ctext, ctext_len, nonce, shared.data());
+   }
+
+int Sodium::crypto_box_detached(uint8_t ctext[], uint8_t mac[],
+                                const uint8_t ptext[], size_t ptext_len,
+                                const uint8_t nonce[], const uint8_t pk[32],
+                                const uint8_t sk[32])
+   {
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_box_beforenm(shared.data(), pk, sk) != 0)
+      return -1;
+
+   return crypto_box_detached_afternm(ctext, mac, ptext, ptext_len, nonce, shared.data());
+   }
+
+int Sodium::crypto_box_open_detached(uint8_t ptext[], const uint8_t ctext[],
+                                     const uint8_t mac[],
+                                     size_t ctext_len,
+                                     const uint8_t nonce[],
+                                     const uint8_t pk[32],
+                                     const uint8_t sk[32])
+   {
+   secure_vector<uint8_t> shared(32);
+
+   if(crypto_box_beforenm(shared.data(), pk, sk) != 0)
+      return -1;
+
+   return crypto_box_open_detached_afternm(ptext, ctext, mac, ctext_len, nonce, shared.data());
+   }
+
+}
diff --git a/src/lib/compat/sodium/sodium_chacha.cpp b/src/lib/compat/sodium/sodium_chacha.cpp
new file mode 100644
index 000000000..fed7a52f6
--- /dev/null
+++ b/src/lib/compat/sodium/sodium_chacha.cpp
@@ -0,0 +1,109 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/stream_cipher.h>
+
+namespace Botan {
+
+int Sodium::crypto_stream_chacha20(uint8_t out[], size_t out_len,
+                                   const uint8_t nonce[], const uint8_t key[])
+   {
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_chacha20_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_chacha20_NONCEBYTES);
+   chacha->write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_chacha20_xor(uint8_t out[], const uint8_t in[],
+                                       size_t in_len, const uint8_t nonce[],
+                                       const uint8_t key[])
+   {
+   return crypto_stream_chacha20_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_chacha20_xor_ic(uint8_t out[], const uint8_t in[],
+                                          size_t in_len,
+                                          const uint8_t nonce[], uint64_t ic,
+                                          const uint8_t key[])
+   {
+   if((ic >> 6) != 0) // otherwise multiply overflows
+      return -1;
+
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_chacha20_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_chacha20_NONCEBYTES);
+   chacha->seek(ic * 64);
+   chacha->cipher(in, out, in_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_chacha20_ietf(uint8_t out[], size_t out_len,
+                                        const uint8_t nonce[], const uint8_t key[])
+   {
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_chacha20_ietf_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_chacha20_ietf_NONCEBYTES);
+   chacha->write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_chacha20_ietf_xor(uint8_t out[],
+                                            const uint8_t in[], size_t in_len,
+                                            const uint8_t nonce[],
+                                            const uint8_t key[])
+   {
+   return crypto_stream_chacha20_ietf_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_chacha20_ietf_xor_ic(uint8_t out[],
+                                               const uint8_t in[], size_t in_len,
+                                               const uint8_t nonce[], uint32_t ic,
+                                               const uint8_t key[])
+   {
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_chacha20_ietf_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_chacha20_ietf_NONCEBYTES);
+   chacha->seek(static_cast<uint64_t>(ic) * 64);
+   chacha->cipher(in, out, in_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_xchacha20(uint8_t out[], size_t out_len,
+                                    const uint8_t nonce[], const uint8_t key[])
+   {
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_xchacha20_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_xchacha20_NONCEBYTES);
+   chacha->write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_xchacha20_xor(uint8_t out[], const uint8_t in[],
+                                       size_t in_len, const uint8_t nonce[],
+                                       const uint8_t key[])
+   {
+   return crypto_stream_xchacha20_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_xchacha20_xor_ic(uint8_t out[], const uint8_t in[],
+                                           size_t in_len,
+                                           const uint8_t nonce[], uint64_t ic,
+                                           const uint8_t key[])
+   {
+   if((ic >> 6) != 0) // otherwise multiply overflows
+      return -1;
+
+   auto chacha = StreamCipher::create_or_throw("ChaCha(20)");
+   chacha->set_key(key, crypto_stream_xchacha20_KEYBYTES);
+   chacha->set_iv(nonce, crypto_stream_xchacha20_NONCEBYTES);
+   chacha->seek(ic * 64);
+   chacha->cipher(in, out, in_len);
+   return 0;
+   }
+
+}
diff --git a/src/lib/compat/sodium/sodium_salsa.cpp b/src/lib/compat/sodium/sodium_salsa.cpp
new file mode 100644
index 000000000..c1465a9bb
--- /dev/null
+++ b/src/lib/compat/sodium/sodium_salsa.cpp
@@ -0,0 +1,124 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/salsa20.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+int Sodium::crypto_core_hsalsa20(uint8_t out[], const uint8_t in[],
+                                 const uint8_t key[], const uint8_t c[])
+   {
+   uint32_t in32[16] = { 0 };
+
+   static const uint32_t SIGMA[] =
+      { 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574 };
+
+   if(c == nullptr)
+      {
+      in32[0] = SIGMA[0];
+      in32[5] = SIGMA[1];
+      in32[10] = SIGMA[2];
+      in32[15] = SIGMA[3];
+      }
+   else
+      {
+      in32[0] = load_le<uint32_t>(c, 0);
+      in32[5] = load_le<uint32_t>(c, 1);
+      in32[10] = load_le<uint32_t>(c, 2);
+      in32[15] = load_le<uint32_t>(c, 3);
+      }
+
+   in32[1] = load_le<uint32_t>(key, 0);
+   in32[2] = load_le<uint32_t>(key, 1);
+   in32[3] = load_le<uint32_t>(key, 2);
+   in32[4] = load_le<uint32_t>(key, 3);
+
+   in32[6] = load_le<uint32_t>(in, 0);
+   in32[7] = load_le<uint32_t>(in, 1);
+   in32[8] = load_le<uint32_t>(in, 2);
+   in32[9] = load_le<uint32_t>(in, 3);
+
+   in32[11] = load_le<uint32_t>(key, 4);
+   in32[12] = load_le<uint32_t>(key, 5);
+   in32[13] = load_le<uint32_t>(key, 6);
+   in32[14] = load_le<uint32_t>(key, 7);
+
+   uint32_t out32[8] = { 0 };
+   Salsa20::hsalsa20(out32, in32);
+
+   copy_out_le(out, 32, out32);
+   return 0;
+   }
+
+int Sodium::crypto_stream_salsa20(uint8_t out[], size_t out_len,
+                                  const uint8_t nonce[], const uint8_t key[])
+   {
+   Salsa20 salsa;
+   salsa.set_key(key, crypto_stream_salsa20_KEYBYTES);
+   salsa.set_iv(nonce, crypto_stream_salsa20_NONCEBYTES);
+   salsa.write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_salsa20_xor(uint8_t out[], const uint8_t in[],
+                                      size_t in_len, const uint8_t nonce[],
+                                      const uint8_t key[])
+   {
+   return crypto_stream_salsa20_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_salsa20_xor_ic(uint8_t out[], const uint8_t in[],
+                                         size_t in_len,
+                                         const uint8_t nonce[], uint64_t ic,
+                                         const uint8_t key[])
+   {
+   if((ic >> 6) != 0) // otherwise multiply overflows
+      return -1;
+
+   Salsa20 salsa;
+   salsa.set_key(key, crypto_stream_salsa20_KEYBYTES);
+   salsa.set_iv(nonce, crypto_stream_salsa20_NONCEBYTES);
+   salsa.seek(ic * 64);
+   salsa.cipher(in, out, in_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_xsalsa20(uint8_t out[], size_t out_len,
+                                   const uint8_t nonce[], const uint8_t key[])
+   {
+   Salsa20 salsa;
+   salsa.set_key(key, crypto_stream_xsalsa20_KEYBYTES);
+   salsa.set_iv(nonce, crypto_stream_xsalsa20_NONCEBYTES);
+   salsa.write_keystream(out, out_len);
+   return 0;
+   }
+
+int Sodium::crypto_stream_xsalsa20_xor(uint8_t out[], const uint8_t in[],
+                                       size_t in_len, const uint8_t nonce[],
+                                       const uint8_t key[])
+   {
+   return crypto_stream_xsalsa20_xor_ic(out, in, in_len, nonce, 0, key);
+   }
+
+int Sodium::crypto_stream_xsalsa20_xor_ic(uint8_t out[], const uint8_t in[],
+                                          size_t in_len,
+                                          const uint8_t nonce[], uint64_t ic,
+                                          const uint8_t key[])
+   {
+   if((ic >> 6) != 0) // otherwise multiply overflows
+      return -1;
+
+   Salsa20 salsa;
+   salsa.set_key(key, crypto_stream_xsalsa20_KEYBYTES);
+   salsa.set_iv(nonce, crypto_stream_xsalsa20_NONCEBYTES);
+   salsa.seek(ic * 64);
+   salsa.cipher(in, out, in_len);
+   return 0;
+   }
+
+}
diff --git a/src/lib/compat/sodium/sodium_secretbox.cpp b/src/lib/compat/sodium/sodium_secretbox.cpp
new file mode 100644
index 000000000..255e0631b
--- /dev/null
+++ b/src/lib/compat/sodium/sodium_secretbox.cpp
@@ -0,0 +1,123 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/secmem.h>
+#include <botan/stream_cipher.h>
+#include <botan/mac.h>
+
+namespace Botan {
+
+int Sodium::crypto_secretbox_xsalsa20poly1305(uint8_t ctext[],
+                                              const uint8_t ptext[],
+                                              size_t ptext_len,
+                                              const uint8_t nonce[],
+                                              const uint8_t key[])
+   {
+   if(ptext_len < 32)
+      return -1;
+
+   auto salsa = StreamCipher::create_or_throw("Salsa20");
+   salsa->set_key(key, crypto_secretbox_KEYBYTES);
+   salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+   secure_vector<uint8_t> auth_key(32);
+   salsa->write_keystream(auth_key.data(), auth_key.size());
+
+   salsa->cipher(ptext + 32, ctext + 32, ptext_len - 32);
+
+   auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+   poly1305->set_key(auth_key);
+   poly1305->update(ctext + 32, ptext_len - 32);
+   poly1305->final(ctext + 16);
+
+   clear_mem(ctext, 16);
+   return 0;
+   }
+
+int Sodium::crypto_secretbox_xsalsa20poly1305_open(uint8_t ptext[],
+                                                   const uint8_t ctext[],
+                                                   size_t ctext_len,
+                                                   const uint8_t nonce[],
+                                                   const uint8_t key[])
+   {
+   if(ctext_len < crypto_box_curve25519xsalsa20poly1305_ZEROBYTES)
+      {
+      return -1;
+      }
+
+   auto salsa = StreamCipher::create_or_throw("Salsa20");
+   salsa->set_key(key, crypto_secretbox_KEYBYTES);
+   salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+   secure_vector<uint8_t> auth_key(32);
+   salsa->write_keystream(auth_key.data(), auth_key.size());
+
+   auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+   poly1305->set_key(auth_key);
+   poly1305->update(ctext + 32, ctext_len - 32);
+   secure_vector<uint8_t> computed = poly1305->final();
+
+   if(!constant_time_compare(computed.data(), ctext + 16, 16))
+      return -1;
+
+   salsa->cipher(ctext + 32, ptext + 32, ctext_len - 32);
+
+   clear_mem(ptext, 32);
+   return 0;
+   }
+
+int Sodium::crypto_secretbox_detached(uint8_t ctext[], uint8_t mac[],
+                                      const uint8_t ptext[],
+                                      size_t ptext_len,
+                                      const uint8_t nonce[],
+                                      const uint8_t key[])
+   {
+   auto salsa = StreamCipher::create_or_throw("Salsa20");
+   salsa->set_key(key, crypto_secretbox_KEYBYTES);
+   salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+   secure_vector<uint8_t> auth_key(32);
+   salsa->write_keystream(auth_key.data(), auth_key.size());
+
+   salsa->cipher(ptext, ctext, ptext_len);
+
+   auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+   poly1305->set_key(auth_key);
+   poly1305->update(ctext, ptext_len);
+   poly1305->final(mac);
+
+   return 0;
+   }
+
+int Sodium::crypto_secretbox_open_detached(uint8_t ptext[],
+                                           const uint8_t ctext[],
+                                           const uint8_t mac[],
+                                           size_t ctext_len,
+                                           const uint8_t nonce[],
+                                           const uint8_t key[])
+   {
+   auto salsa = StreamCipher::create_or_throw("Salsa20");
+   salsa->set_key(key, crypto_secretbox_KEYBYTES);
+   salsa->set_iv(nonce, crypto_secretbox_NONCEBYTES);
+
+   secure_vector<uint8_t> auth_key(32);
+   salsa->write_keystream(auth_key.data(), auth_key.size());
+
+   auto poly1305 = MessageAuthenticationCode::create_or_throw("Poly1305");
+   poly1305->set_key(auth_key);
+   poly1305->update(ctext, ctext_len);
+   secure_vector<uint8_t> computed_mac = poly1305->final();
+
+   if(!constant_time_compare(mac, computed_mac.data(), computed_mac.size()))
+      return -1;
+
+   salsa->cipher(ctext, ptext, ctext_len);
+
+   return 0;
+   }
+
+}
diff --git a/src/lib/compat/sodium/sodium_utils.cpp b/src/lib/compat/sodium/sodium_utils.cpp
new file mode 100644
index 000000000..208355583
--- /dev/null
+++ b/src/lib/compat/sodium/sodium_utils.cpp
@@ -0,0 +1,160 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include <botan/sodium.h>
+#include <botan/chacha.h>
+#include <botan/mem_ops.h>
+#include <botan/system_rng.h>
+#include <botan/internal/os_utils.h>
+#include <botan/internal/ct_utils.h>
+#include <botan/loadstor.h>
+
+namespace Botan {
+
+void Sodium::randombytes_buf(void* buf, size_t len)
+   {
+   system_rng().randomize(static_cast<uint8_t*>(buf), len);
+   }
+
+uint32_t Sodium::randombytes_uniform(uint32_t upper_bound)
+   {
+   if(upper_bound <= 1)
+      return 0;
+
+   // Not completely uniform
+   uint64_t x;
+   randombytes_buf(&x, sizeof(x));
+   return x % upper_bound;
+   }
+
+void Sodium::randombytes_buf_deterministic(void* buf, size_t size, const uint8_t seed[randombytes_SEEDBYTES])
+   {
+   const unsigned char nonce[12] = {
+      'L', 'i', 'b', 's', 'o', 'd', 'i', 'u', 'm', 'D', 'R', 'G'
+   };
+
+   ChaCha chacha(20);
+   chacha.set_key(seed, randombytes_SEEDBYTES);
+   chacha.set_iv(nonce, sizeof(nonce));
+   chacha.write_keystream(static_cast<uint8_t*>(buf), size);
+   }
+
+int Sodium::crypto_verify_16(const uint8_t* x, const uint8_t* y)
+   {
+   return same_mem(x, y, 16);
+   }
+
+int Sodium::crypto_verify_32(const uint8_t* x, const uint8_t* y)
+   {
+   return same_mem(x, y, 32);
+   }
+
+int Sodium::crypto_verify_64(const uint8_t* x, const uint8_t* y)
+   {
+   return same_mem(x, y, 64);
+   }
+
+void Sodium::sodium_memzero(void* ptr, size_t len)
+   {
+   secure_scrub_memory(ptr, len);
+   }
+
+int Sodium::sodium_memcmp(const void* x, const void* y, size_t len)
+   {
+   const bool same = constant_time_compare(static_cast<const uint8_t*>(x), static_cast<const uint8_t*>(y), len);
+   return same ? 0 : -1;
+   }
+
+int Sodium::sodium_compare(const uint8_t x[], const uint8_t y[], size_t len)
+   {
+   const uint8_t LT = static_cast<uint8_t>(-1);
+   const uint8_t EQ = 0;
+   const uint8_t GT = 1;
+
+   uint8_t result = EQ; // until found otherwise
+
+   for(size_t i = 0; i != len; ++i)
+      {
+      const auto is_eq = CT::Mask<uint8_t>::is_equal(x[i], y[i]);
+      const auto is_lt = CT::Mask<uint8_t>::is_lt(x[i], y[i]);
+      result = is_eq.select(result, is_lt.select(LT, GT));
+      }
+
+   return static_cast<int8_t>(result);
+   }
+
+int Sodium::sodium_is_zero(const uint8_t b[], size_t len)
+   {
+   uint8_t sum = 0;
+   for(size_t i = 0; i != len; ++i)
+      sum |= b[i];
+   return static_cast<int>(CT::Mask<uint8_t>::expand(sum).if_not_set_return(1));
+   }
+
+void Sodium::sodium_increment(uint8_t b[], size_t len)
+   {
+   uint8_t carry = 1;
+   for(size_t i = 0; i != len; ++i)
+      {
+      b[i] += carry;
+      carry &= (b[i] == 0);
+      }
+   }
+
+void Sodium::sodium_add(uint8_t a[], const uint8_t b[], size_t len)
+   {
+   uint8_t carry = 0;
+   for(size_t i = 0; i != len; ++i)
+      {
+      a[i] += b[i] + carry;
+      carry = (a[i] < b[i]);
+      }
+   }
+
+void* Sodium::sodium_malloc(size_t size)
+   {
+   const uint64_t len = size;
+
+   if(size + sizeof(len) < size)
+      return nullptr;
+
+   uint8_t* p = static_cast<uint8_t*>(std::calloc(size + sizeof(len), 1));
+   store_le(len, p);
+   return p + 8;
+   }
+
+void Sodium::sodium_free(void* ptr)
+   {
+   if(ptr == nullptr)
+      return;
+
+   uint8_t* p = static_cast<uint8_t*>(ptr) - 8;
+   const uint64_t len = load_le<uint64_t>(p, 0);
+   secure_scrub_memory(ptr, len);
+   std::free(p);
+   }
+
+void* Sodium::sodium_allocarray(size_t count, size_t size)
+   {
+   const size_t bytes = count * size;
+   if(bytes < count || bytes < size)
+      return nullptr;
+   return sodium_malloc(bytes);
+   }
+
+int Sodium::sodium_mprotect_noaccess(void* ptr)
+   {
+   OS::page_prohibit_access(ptr);
+   return 0;
+   }
+
+int Sodium::sodium_mprotect_readwrite(void* ptr)
+   {
+   OS::page_allow_access(ptr);
+   return 0;
+   }
+
+}
diff --git a/src/lib/stream/salsa20/salsa20.cpp b/src/lib/stream/salsa20/salsa20.cpp
index 407a3183e..1e30391d2 100644
--- a/src/lib/stream/salsa20/salsa20.cpp
+++ b/src/lib/stream/salsa20/salsa20.cpp
@@ -12,8 +12,6 @@
 
 namespace Botan {
 
-namespace {
-
 #define SALSA20_QUARTER_ROUND(x1, x2, x3, x4)    \
    do {                                          \
       x2 ^= rotl<7>(x1 + x4);                    \
@@ -25,7 +23,8 @@ namespace {
 /*
 * Generate HSalsa20 cipher stream (for XSalsa20 IV setup)
 */
-void hsalsa20(uint32_t output[8], const uint32_t input[16])
+//static
+void Salsa20::hsalsa20(uint32_t output[8], const uint32_t input[16])
    {
    uint32_t x00 = input[ 0], x01 = input[ 1], x02 = input[ 2], x03 = input[ 3],
             x04 = input[ 4], x05 = input[ 5], x06 = input[ 6], x07 = input[ 7],
@@ -55,8 +54,6 @@ void hsalsa20(uint32_t output[8], const uint32_t input[16])
    output[7] = x09;
    }
 
-}
-
 /*
 * Generate Salsa20 cipher stream
 */
diff --git a/src/lib/stream/salsa20/salsa20.h b/src/lib/stream/salsa20/salsa20.h
index 60143f8e0..0870c9fd2 100644
--- a/src/lib/stream/salsa20/salsa20.h
+++ b/src/lib/stream/salsa20/salsa20.h
@@ -33,6 +33,7 @@ class BOTAN_PUBLIC_API(2,0) Salsa20 final : public StreamCipher
       StreamCipher* clone() const override;
 
       static void salsa_core(uint8_t output[64], const uint32_t input[16], size_t rounds);
+      static void hsalsa20(uint32_t output[8], const uint32_t input[16]);
 
       void seek(uint64_t offset) override;
    private:


Test patch:
diff --git a/src/tests/test_sodium.cpp b/src/tests/test_sodium.cpp
new file mode 100644
index 000000000..378f6f634
--- /dev/null
+++ b/src/tests/test_sodium.cpp
@@ -0,0 +1,672 @@
+/*
+* (C) 2019 Jack Lloyd
+*
+* Botan is released under the Simplified BSD License (see license.txt)
+*/
+
+#include "tests.h"
+
+#if defined(BOTAN_HAS_SODIUM_API)
+   #include <botan/sodium.h>
+#endif
+
+namespace Botan_Tests {
+
+#if defined(BOTAN_HAS_SODIUM_API)
+
+class Sodium_API_Tests : public Test
+   {
+   public:
+      std::vector<Test::Result> run() override
+         {
+         std::vector<Test::Result> results;
+
+         results.push_back(aead_chacha20poly1305());
+         results.push_back(aead_chacha20poly1305_ietf());
+         results.push_back(aead_xchacha20poly1305());
+         results.push_back(auth_hmacsha256());
+         results.push_back(auth_hmacsha512());
+         results.push_back(auth_hmacsha512256());
+         results.push_back(auth_poly1305());
+         results.push_back(box_curve25519xsalsa20poly1305());
+         results.push_back(hash_sha256());
+         results.push_back(hash_sha512());
+         results.push_back(randombytes_buf_deterministic());
+         results.push_back(secretbox_xsalsa20poly1305());
+         results.push_back(secretbox_xsalsa20poly1305_detached());
+         results.push_back(shorthash_siphash24());
+         results.push_back(stream_chacha20());
+         results.push_back(stream_chacha20_ietf());
+         results.push_back(stream_salsa20());
+         results.push_back(stream_xchacha20());
+         results.push_back(stream_xsalsa20());
+         results.push_back(sign_ed25519());
+         results.push_back(sodium_malloc());
+         results.push_back(sodium_utils());
+
+         return results;
+         }
+
+   private:
+
+      Test::Result sodium_malloc()
+         {
+         Test::Result result("sodium_malloc");
+
+         void* p = Botan::Sodium::sodium_malloc(50);
+         std::memset(p, 0xFF, 50);
+
+         Botan::Sodium::sodium_free(p);
+         Botan::Sodium::sodium_free(nullptr);
+
+         result.test_success("Didn't crash");
+
+         return result;
+         }
+
+      Test::Result sodium_utils()
+         {
+         Test::Result result("sodium math utils");
+
+         result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(nullptr, 0) == 1);
+
+         std::vector<uint8_t> a(5);
+         result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(a.data(), a.size()) == 1);
+         Botan::Sodium::sodium_increment(a.data(), a.size());
+         result.test_eq("sodium_increment", a, "0100000000");
+         result.confirm("sodium_is_zero", Botan::Sodium::sodium_is_zero(a.data(), a.size()) == 0);
+
+         std::memset(a.data(), 0xFF, a.size());
+         Botan::Sodium::sodium_increment(a.data(), a.size());
+         result.test_eq("sodium_increment", a, "0000000000");
+         Botan::Sodium::sodium_increment(a.data(), a.size());
+         result.test_eq("sodium_increment", a, "0100000000");
+
+         result.confirm("sodium_compare", Botan::Sodium::sodium_compare(a.data(), a.data(), a.size()) == 0);
+         result.confirm("sodium_memcmp", Botan::Sodium::sodium_memcmp(a.data(), a.data(), a.size()) == 0);
+
+         std::vector<uint8_t> b(5, 0x10);
+         result.confirm("sodium_compare a<b", Botan::Sodium::sodium_compare(a.data(), b.data(), a.size()) == -1);
+         result.confirm("sodium_compare b<a", Botan::Sodium::sodium_compare(b.data(), a.data(), a.size()) == 1);
+         result.confirm("sodium_memcmp a<b", Botan::Sodium::sodium_memcmp(a.data(), b.data(), a.size()) == -1);
+         result.confirm("sodium_memcmp b<a", Botan::Sodium::sodium_memcmp(b.data(), a.data(), a.size()) == -1);
+
+         Botan::Sodium::sodium_add(a.data(), b.data(), a.size());
+         result.test_eq("sodium_add", a, "1110101010");
+         Botan::Sodium::sodium_add(b.data(), a.data(), a.size());
+         result.test_eq("sodium_add", b, "2120202020");
+         Botan::Sodium::sodium_add(a.data(), b.data(), a.size());
+         result.test_eq("sodium_add", a, "3230303030");
+         Botan::Sodium::sodium_add(b.data(), a.data(), a.size());
+         result.test_eq("sodium_add", b, "5350505050");
+
+         return result;
+         }
+
+      Test::Result randombytes_buf_deterministic()
+         {
+         Test::Result result("randombytes_buf_deterministic");
+
+         const uint8_t seed[32] = { 1, 0 };
+         std::vector<uint8_t> output(18);
+
+         Botan::Sodium::randombytes_buf_deterministic(output.data(), output.size(), seed);
+
+         result.test_eq("output", output, "04069B5F37E82F91DC37FD5EB99F1A4124B1");
+
+         return result;
+         }
+
+      Test::Result hash_sha512()
+         {
+         Test::Result result("crypto_hash_sha512");
+
+         std::vector<uint8_t> output(64);
+         Botan::Sodium::crypto_hash_sha512(output.data(), nullptr, 0);
+
+         result.test_eq("expected output", output,
+                        "cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e");
+
+         return result;
+         }
+
+      Test::Result hash_sha256()
+         {
+         Test::Result result("crypto_hash_sha256");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_hash_sha256(output.data(), nullptr, 0);
+
+         result.test_eq("expected output", output,
+                        "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
+
+         return result;
+         }
+
+      Test::Result box_curve25519xsalsa20poly1305()
+         {
+         Test::Result result("crypto_box_curve25519xsalsa20poly1305");
+
+         const std::vector<uint8_t> seed(32);
+
+         std::vector<uint8_t> pk1(32), sk1(32);
+         result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_box_seed_keypair(pk1.data(), sk1.data(), seed.data()));
+         result.test_eq("pk1", pk1, "5BF55C73B82EBE22BE80F3430667AF570FAE2556A6415E6B30D4065300AA947D");
+         result.test_eq("sk1", sk1, "5046ADC1DBA838867B2BBBFDD0C3423E58B57970B5267A90F57960924A87F196");
+
+         std::vector<uint8_t> pk2(32), sk2(32);
+         result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_box_seed_keypair(pk2.data(), sk2.data(), sk1.data()));
+         result.test_eq("pk2", pk2, "E0CFC9C6B2FE5BF85F48671691225C03D763F2305206FE3D3B0ED7B76153684A");
+         result.test_eq("sk2", sk2, "58E2E4C71F138FBC97F9341735B4581746761F9A104540007FE12CFC4D9FDA15");
+
+         const std::vector<uint8_t> ptext(15);
+         std::vector<uint8_t> ctext(ptext.size() + 16);
+         const std::vector<uint8_t> nonce(Botan::Sodium::crypto_box_noncebytes());
+
+         result.test_rc_ok("crypto_box_easy",
+                           Botan::Sodium::crypto_box_easy(ctext.data(), ptext.data(), ptext.size(), nonce.data(),
+                                           pk2.data(), sk1.data()));
+
+         result.test_eq("ctext1", ctext, "11D78D4C32C5674390C0425D8BBB5928AFE7F767E2A7E4427E1A1362F1FD92");
+
+         result.test_rc_ok("crypto_box_easy",
+                           Botan::Sodium::crypto_box_easy(ctext.data(), ptext.data(), ptext.size(), nonce.data(),
+                                           pk1.data(), sk2.data()));
+
+         // same shared secret, same nonce, same data -> same ciphertext
+         result.test_eq("ctext2", ctext, "11D78D4C32C5674390C0425D8BBB5928AFE7F767E2A7E4427E1A1362F1FD92");
+
+         std::vector<uint8_t> recovered(15);
+
+         result.test_rc_ok("crypto_box_open_easy",
+                           Botan::Sodium::crypto_box_open_easy(recovered.data(), ctext.data(), ctext.size(),
+                                                nonce.data(), pk1.data(), sk2.data()));
+
+         result.test_eq("recover1", recovered, ptext);
+
+         result.test_rc_ok("crypto_box_open_easy",
+                           Botan::Sodium::crypto_box_open_easy(recovered.data(), ctext.data(), ctext.size(),
+                                                nonce.data(), pk2.data(), sk1.data()));
+
+         result.test_eq("recover1", recovered, ptext);
+
+         return result;
+         }
+
+      Test::Result aead_chacha20poly1305()
+         {
+         Test::Result result("crypto_aead_chacha20poly1305");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0000000000000000000000000000000000000000000000000000000000000000");
+         const std::vector<uint8_t> ad;
+         const std::vector<uint8_t> nonce = Botan::hex_decode("0000000000000000");
+         const std::vector<uint8_t> in = Botan::hex_decode("000000000000000000000000000000");
+
+         result.test_eq("key len", Botan::Sodium::crypto_aead_chacha20poly1305_keybytes(), key.size());
+         result.test_eq("nonce len", Botan::Sodium::crypto_aead_chacha20poly1305_npubbytes(), nonce.size());
+
+         std::vector<uint8_t> ctext(in.size());
+         std::vector<uint8_t> mac(16);
+         unsigned long long maclen = 0;
+         Botan::Sodium::crypto_aead_chacha20poly1305_encrypt_detached(
+            ctext.data(), mac.data(), &maclen, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data());
+
+         result.test_eq("maclen", size_t(maclen), 16);
+         result.test_eq("mac", mac, "09998877ABA156DDC68F8344098F68B9");
+         result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D08");
+
+         std::vector<uint8_t> recovered(ctext.size());
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_chacha20poly1305_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("plaintext", recovered, in);
+
+         mac[0] ^= 1;
+         result.test_rc_fail("decrypt", "invalid ciphertext", Botan::Sodium::crypto_aead_chacha20poly1305_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         ctext.resize(in.size() + mac.size());
+         unsigned long long ctext_len;
+         result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_chacha20poly1305_encrypt(
+            ctext.data(), &ctext_len, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data()));
+
+         result.test_eq("ctext_len", size_t(ctext_len), ctext.size());
+         result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D0809998877ABA156DDC68F8344098F68B9");
+
+         unsigned long long recovered_len = 0;
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_chacha20poly1305_decrypt(
+                              recovered.data(), &recovered_len, nullptr,
+                              ctext.data(), ctext.size(), ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("recovered", recovered, in);
+
+         return result;
+         }
+
+      Test::Result aead_chacha20poly1305_ietf()
+         {
+         Test::Result result("crypto_aead_chacha20poly1305_ietf");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0000000000000000000000000000000000000000000000000000000000000000");
+         const std::vector<uint8_t> ad;
+         const std::vector<uint8_t> nonce = Botan::hex_decode("000000000000000000000000");
+         const std::vector<uint8_t> in = Botan::hex_decode("000000000000000000000000000000");
+
+         result.test_eq("key len", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_keybytes(), key.size());
+         result.test_eq("nonce len", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_npubbytes(), nonce.size());
+
+         std::vector<uint8_t> ctext(in.size());
+         std::vector<uint8_t> mac(16);
+         unsigned long long maclen = 0;
+         Botan::Sodium::crypto_aead_chacha20poly1305_ietf_encrypt_detached(
+            ctext.data(), mac.data(), &maclen, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data());
+
+         result.test_eq("maclen", size_t(maclen), 16);
+         result.test_eq("mac", mac, "3679F1FB9843FD81E26D962888296954");
+         result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D08");
+
+         std::vector<uint8_t> recovered(ctext.size());
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("plaintext", recovered, in);
+
+         mac[0] ^= 1;
+         result.test_rc_fail("decrypt", "invalid ciphertext", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         ctext.resize(in.size() + mac.size());
+         unsigned long long ctext_len;
+         result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_encrypt(
+            ctext.data(), &ctext_len, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data()));
+
+         result.test_eq("ctext_len", size_t(ctext_len), ctext.size());
+         result.test_eq("ctext", ctext, "9F07E7BE5551387A98BA977C732D083679F1FB9843FD81E26D962888296954");
+
+         unsigned long long recovered_len = 0;
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_chacha20poly1305_ietf_decrypt(
+                              recovered.data(), &recovered_len, nullptr,
+                              ctext.data(), ctext.size(), ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("recovered", recovered, in);
+
+         return result;
+         }
+
+      Test::Result aead_xchacha20poly1305()
+         {
+         Test::Result result("crypto_aead_xchacha20poly1305");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0000000000000000000000000000000000000000000000000000000000000000");
+         const std::vector<uint8_t> ad;
+         const std::vector<uint8_t> nonce = Botan::hex_decode("000000000000000000000000000000000000000000000000");
+         const std::vector<uint8_t> in = Botan::hex_decode("000000000000000000000000000000");
+
+         result.test_eq("key len", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_keybytes(), key.size());
+         result.test_eq("nonce len", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_npubbytes(), nonce.size());
+
+         std::vector<uint8_t> ctext(in.size());
+         std::vector<uint8_t> mac(16);
+         unsigned long long maclen = 0;
+         Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt_detached(
+            ctext.data(), mac.data(), &maclen, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data());
+
+         result.test_eq("maclen", size_t(maclen), 16);
+         result.test_eq("mac", mac, "b2f7033812ac9ebd3745e2c99c7bbfeb");
+         result.test_eq("ctext", ctext, "789e9689e5208d7fd9e1f3c5b5341f");
+
+         std::vector<uint8_t> recovered(ctext.size());
+         result.test_rc_ok("decrypt",
+                           Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
+                              recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+                              ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("plaintext", recovered, in);
+
+         mac[0] ^= 1;
+         result.test_rc_fail("decrypt", "invalid ciphertext", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt_detached(
+            recovered.data(), nullptr, ctext.data(), ctext.size(), mac.data(),
+            ad.data(), ad.size(), nonce.data(), key.data()));
+
+         ctext.resize(in.size() + mac.size());
+         unsigned long long ctext_len;
+         result.test_rc_ok("encrypt", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_encrypt(
+            ctext.data(), &ctext_len, in.data(), in.size(),
+            ad.data(), ad.size(), nullptr, nonce.data(), key.data()));
+
+         result.test_eq("ctext_len", size_t(ctext_len), ctext.size());
+         result.test_eq("ctext", ctext, "789e9689e5208d7fd9e1f3c5b5341fb2f7033812ac9ebd3745e2c99c7bbfeb");
+
+         unsigned long long recovered_len = 0;
+         result.test_rc_ok("decrypt", Botan::Sodium::crypto_aead_xchacha20poly1305_ietf_decrypt(
+                              recovered.data(), &recovered_len, nullptr,
+                              ctext.data(), ctext.size(), ad.data(), ad.size(), nonce.data(), key.data()));
+
+         result.test_eq("recovered", recovered, in);
+
+         return result;
+         }
+
+      Test::Result auth_hmacsha512()
+         {
+         Test::Result result("crypto_auth_hmacsha512");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> in = Botan::hex_decode("616263");
+
+         result.test_eq("key_size", key.size(), Botan::Sodium::crypto_auth_hmacsha512_keybytes());
+
+         std::vector<uint8_t> mac(64);
+         Botan::Sodium::crypto_auth_hmacsha512(mac.data(), in.data(), in.size(), key.data());
+
+         result.test_eq("expected mac", mac,
+                        "69D4A21E226BF0D348CB9A847C01CF24E93E8AC30D7C951704B936F82F795A624B470E23ABD33AC8700E797F0F2A499B932BAC7D283BBBB37D8FECF70D5E08A7");
+
+         result.test_rc_ok("verify",
+                           Botan::Sodium::crypto_auth_hmacsha512_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         mac[0] ^= 1;
+         result.test_rc_fail("verify", "invalid mac",
+                             Botan::Sodium::crypto_auth_hmacsha512_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         return result;
+         }
+
+      Test::Result auth_hmacsha512256()
+         {
+         Test::Result result("crypto_auth_hmacsha512256");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> in = Botan::hex_decode("616263");
+
+         std::vector<uint8_t> mac(32);
+         Botan::Sodium::crypto_auth_hmacsha512256(mac.data(), in.data(), in.size(), key.data());
+
+         result.test_eq("expected mac", mac,
+                        "69D4A21E226BF0D348CB9A847C01CF24E93E8AC30D7C951704B936F82F795A62");
+
+         result.test_rc_ok("verify",
+                           Botan::Sodium::crypto_auth_hmacsha512256_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         mac[0] ^= 1;
+         result.test_rc_fail("verify", "invalid mac",
+                             Botan::Sodium::crypto_auth_hmacsha512256_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         return result;
+         }
+
+      Test::Result auth_hmacsha256()
+         {
+         Test::Result result("crypto_auth_hmacsha256");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F20");
+         const std::vector<uint8_t> in = Botan::hex_decode("616263");
+
+         std::vector<uint8_t> mac(32);
+         Botan::Sodium::crypto_auth_hmacsha256(mac.data(), in.data(), in.size(), key.data());
+
+         result.test_eq("expected mac", mac,
+                        "A21B1F5D4CF4F73A4DD939750F7A066A7F98CC131CB16A6692759021CFAB8181");
+
+         result.test_rc_ok("verify",
+                           Botan::Sodium::crypto_auth_hmacsha256_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         mac[0] ^= 1;
+         result.test_rc_fail("verify", "invalid mac",
+                             Botan::Sodium::crypto_auth_hmacsha256_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         return result;
+         }
+
+      Test::Result auth_poly1305()
+         {
+         Test::Result result("crypto_onetimeauth_poly1305");
+
+         const std::vector<uint8_t> key(Botan::Sodium::crypto_onetimeauth_keybytes(), 0x42);
+         const std::vector<uint8_t> in(15);
+
+         std::vector<uint8_t> mac(16);
+
+         result.test_rc_ok("poly1305",
+                           Botan::Sodium::crypto_onetimeauth_poly1305(mac.data(), in.data(), in.size(), key.data()));
+
+         result.test_eq("expected mac", mac, "12154512151545121515451215154584");
+
+         result.test_rc_ok("poly1305 verify",
+                           Botan::Sodium::crypto_onetimeauth_poly1305_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         mac[0] ^= 1;
+         result.test_rc_fail("poly1305 verify", "invalid mac",
+                             Botan::Sodium::crypto_onetimeauth_poly1305_verify(mac.data(), in.data(), in.size(), key.data()));
+
+         return result;
+         }
+
+      Test::Result shorthash_siphash24()
+         {
+         Test::Result result("crypto_shorthash_siphash24");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F");
+         const std::vector<uint8_t> in = Botan::hex_decode("000102030405060708090A0B0C0D0E");
+
+         std::vector<uint8_t> mac(8);
+         Botan::Sodium::crypto_shorthash_siphash24(mac.data(), in.data(), in.size(), key.data());
+
+         result.test_eq("expected mac", mac, "E545BE4961CA29A1");
+
+         return result;
+         }
+
+      Test::Result secretbox_xsalsa20poly1305()
+         {
+         Test::Result result("secretbox_xsalsa20poly1305");
+
+         const std::vector<uint8_t> ptext(33);
+         std::vector<uint8_t> ctext(33);
+         const std::vector<uint8_t> nonce(Botan::Sodium::crypto_secretbox_xsalsa20poly1305_noncebytes());
+         const std::vector<uint8_t> key(Botan::Sodium::crypto_secretbox_xsalsa20poly1305_keybytes());
+
+         result.test_rc_ok("encrypt",
+                           Botan::Sodium::crypto_secretbox_xsalsa20poly1305(ctext.data(),
+                                                               ptext.data(),
+                                                               ptext.size(),
+                                                               nonce.data(),
+                                                               key.data()));
+
+         result.test_eq("ctext", ctext, "0000000000000000000000000000000042E45EB764A1B706D4776A849BC2526BC6");
+
+         std::vector<uint8_t> recovered(33);
+         result.test_rc_ok("decrypt",
+                           Botan::Sodium::crypto_secretbox_xsalsa20poly1305_open(
+                              recovered.data(),
+                              ctext.data(),
+                              ctext.size(),
+                              nonce.data(),
+                              key.data()));
+
+         result.test_eq("decrypted", recovered, ptext);
+
+         return result;
+         }
+
+      Test::Result secretbox_xsalsa20poly1305_detached()
+         {
+         Test::Result result("secretbox_xsalsa20poly1305");
+
+         const std::vector<uint8_t> ptext(33);
+         const std::vector<uint8_t> nonce(Botan::Sodium::crypto_secretbox_xsalsa20poly1305_noncebytes());
+         const std::vector<uint8_t> key(Botan::Sodium::crypto_secretbox_xsalsa20poly1305_keybytes());
+         std::vector<uint8_t> ctext(33);
+         std::vector<uint8_t> mac(16);
+
+         result.test_rc_ok("encrypt detached",
+                           Botan::Sodium::crypto_secretbox_detached(ctext.data(),
+                                                       mac.data(),
+                                                       ptext.data(),
+                                                       ptext.size(),
+                                                       nonce.data(),
+                                                       key.data()));
+
+         result.test_eq("ctext", ctext, "C63EBBFFFE85CE2CEBDEF7DC42F494576D05BDD7B929EBB045F2A793F740277D05");
+         result.test_eq("mac", mac, "0D6681DCED740667C699F0AC71BFD1BD");
+
+         std::vector<uint8_t> recovered(ctext.size());
+
+         result.test_rc_ok("open detached",
+                           Botan::Sodium::crypto_secretbox_open_detached(recovered.data(),
+                                                                         ctext.data(),
+                                                                         mac.data(),
+                                                                         ctext.size(),
+                                                                         nonce.data(),
+                                                                         key.data()));
+
+         result.test_eq("recovered", recovered, ptext);
+
+         return result;
+         }
+
+      Test::Result sign_ed25519()
+         {
+         Test::Result result("crypto_sign_ed25519");
+
+         const std::vector<uint8_t> seed(32);
+         std::vector<uint8_t> pk(32), sk(64);
+
+         result.test_rc_ok("seed_keypair", Botan::Sodium::crypto_sign_ed25519_seed_keypair(pk.data(), sk.data(), seed.data()));
+
+         result.test_eq("pk", pk, "3B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29");
+         result.test_eq("sk", sk, "00000000000000000000000000000000000000000000000000000000000000003B6A27BCCEB6A42D62A3A8D02A6F0D73653215771DE243A63AC048A18B59DA29");
+
+         const std::vector<uint8_t> msg = { 1, 2, 3 };
+         std::vector<uint8_t> sig(64);
+         unsigned long long sig_len = 0;
+         result.test_rc_ok("sign_detached", Botan::Sodium::crypto_sign_ed25519_detached(sig.data(), &sig_len, msg.data(), msg.size(), sk.data()));
+         result.confirm("sig len", sig_len == 64);
+
+         result.test_eq("sig", sig, "2A26779BA6CBB5E54292257F725AF112B273C38728329682D99ED81BA6D7670350AE4CC53C5456FA437128D19298A5D949AB46E3D41AB3DBCFB0B35C895E9304");
+
+         result.test_rc_ok("verify", Botan::Sodium::crypto_sign_ed25519_verify_detached(sig.data(), msg.data(), msg.size(), pk.data()));
+
+         sig[0] ^= 1;
+         result.test_rc_fail("verify", "reject invalid",
+                             Botan::Sodium::crypto_sign_ed25519_verify_detached(sig.data(), msg.data(), msg.size(), pk.data()));
+
+         return result;
+         }
+
+      Test::Result stream_salsa20()
+         {
+         Test::Result result("crypto_stream_salsa20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("0F62B5085BAE0154A7FA4DA0F34699EC3F92E5388BDE3184D72A7DD02376C91C");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("288FF65DC42B92F9");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "5E5E71F90199340304ABB22A37B6625BF883FB89CE3B21F54A10B81066EF87DA");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_salsa20(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_salsa20_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+      Test::Result stream_xsalsa20()
+         {
+         Test::Result result("crypto_stream_xsalsa20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("1B27556473E985D462CD51197A9A46C76009549EAC6474F206C4EE0844F68389");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("69696EE955B62B73CD62BDA875FC73D68219E0036B7A0B37");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "EEA6A7251C1E72916D11C2CB214D3C252539121D8E234E652D651FA4C8CFF880");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_xsalsa20(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_xsalsa20_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+      Test::Result stream_chacha20()
+         {
+         Test::Result result("crypto_stream_chacha20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("0001020304050607");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "F798A189F195E66982105FFB640BB7757F579DA31602FC93EC01AC56F85AC3C1");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_chacha20(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_chacha20_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+      Test::Result stream_chacha20_ietf()
+         {
+         Test::Result result("crypto_stream_chacha20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("000102030405060708090A0B0C");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "103AF111C18B549D39248FB07D60C29A95D1DB88D892F7B4AF709A5FD47A9E4B");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_chacha20_ietf(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_chacha20_ietf_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+      Test::Result stream_xchacha20()
+         {
+         Test::Result result("crypto_stream_xchacha20");
+
+         const std::vector<uint8_t> key = Botan::hex_decode("000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D1E1F");
+         const std::vector<uint8_t> nonce = Botan::hex_decode("000102030405060708090a0b0c0d0e0f1011121314151617");
+         const std::vector<uint8_t> expected = Botan::hex_decode(
+            "e53a61cef151e81401067de33adfc02e90ab205361b49b539fda7f0e63b1bc7d");
+
+         std::vector<uint8_t> output(32);
+         Botan::Sodium::crypto_stream_xchacha20(output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", output, expected);
+
+         std::vector<uint8_t> xor_output(32);
+         Botan::Sodium::crypto_stream_xchacha20_xor(xor_output.data(), output.data(), output.size(), nonce.data(), key.data());
+         result.test_eq("stream", xor_output, std::vector<uint8_t>(32)); // all zeros
+
+         return result;
+         }
+
+   };
+
+BOTAN_REGISTER_TEST("sodium", Sodium_API_Tests);
+
+#endif
+
+}