From 59bc04e8761905f32530b2d1652348cd68b8befd Mon Sep 17 00:00:00 2001 From: dnaq Date: Thu, 5 Dec 2013 22:32:20 +0100 Subject: [PATCH] Initial commit --- .gitignore | 4 + AUTHORS | 3 + LICENSE | 20 ++ src/crypto/auth.rs | 170 +++++++++++++++ src/crypto/box.rs | 424 ++++++++++++++++++++++++++++++++++++++ src/crypto/hash.rs | 67 ++++++ src/crypto/onetimeauth.rs | 190 +++++++++++++++++ src/crypto/scalarmult.rs | 155 ++++++++++++++ src/crypto/secretbox.rs | 212 +++++++++++++++++++ src/crypto/sign.rs | 134 ++++++++++++ src/crypto/stream.rs | 150 ++++++++++++++ src/crypto/verify.rs | 84 ++++++++ src/lib.rs | 98 +++++++++ src/randombytes.rs | 42 ++++ src/utils.rs | 23 +++ 15 files changed, 1776 insertions(+) create mode 100644 .gitignore create mode 100644 AUTHORS create mode 100644 LICENSE create mode 100644 src/crypto/auth.rs create mode 100644 src/crypto/box.rs create mode 100644 src/crypto/hash.rs create mode 100644 src/crypto/onetimeauth.rs create mode 100644 src/crypto/scalarmult.rs create mode 100644 src/crypto/secretbox.rs create mode 100644 src/crypto/sign.rs create mode 100644 src/crypto/stream.rs create mode 100644 src/crypto/verify.rs create mode 100644 src/lib.rs create mode 100644 src/randombytes.rs create mode 100644 src/utils.rs diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fdfc5c2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +*~ +*.sw* +*.o +/doc diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..d517151 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,3 @@ +Daniel Ashhami (dnaq) + +Author of sodium: Frank Denis (jedisct1) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..b89c4f7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013 Daniel Ashhami + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/crypto/auth.rs b/src/crypto/auth.rs new file mode 100644 index 0000000..0c30990 --- /dev/null +++ b/src/crypto/auth.rs @@ -0,0 +1,170 @@ +/*! +Secret-key authentication + +# Security model +The `authenticate()` function, viewed as a function of the +message for a uniform random key, is designed to meet the standard +notion of unforgeability. This means that an attacker cannot find +authenticators for any messages not authenticated by the sender, even if +the attacker has adaptively influenced the messages authenticated by the +sender. For a formal definition see, e.g., Section 2.4 of Bellare, +Kilian, and Rogaway, "The security of the cipher block chaining message +authentication code," Journal of Computer and System Sciences 61 (2000), +362–399; http://www-cse.ucsd.edu/~mihir/papers/cbc.html. + +NaCl does not make any promises regarding "strong" unforgeability; +perhaps one valid authenticator can be converted into another valid +authenticator for the same message. NaCl also does not make any promises +regarding "truncated unforgeability." + +# Selected primitive +`authenticate()` is currently an implementation of +`HMAC-SHA-512-256`, i.e., the first 256 bits of `HMAC-SHA-512`. +`HMAC-SHA-512-256` is conjectured to meet the standard notion of +unforgeability. +*/ +use std::libc::{c_ulonglong, c_int}; +use std::vec::raw::{to_ptr, to_mut_ptr}; +use randombytes::randombytes_into; +use crypto::verify::verify_32; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_auth(a: *mut u8, + m: *u8, + mlen: c_ulonglong, + k: *u8) -> c_int; + fn crypto_auth_verify(a: *u8, + m: *u8, + mlen: c_ulonglong, + k: *u8) -> c_int; +} + +pub static KEYBYTES: uint = 32; +pub static TAGBYTES: uint = 32; + +/** + * Authentication `Key` + * + * When a `Key` goes out of scope its contents + * will be zeroed out + */ +pub struct Key([u8, ..KEYBYTES]); + +impl Drop for Key { + fn drop(&mut self) { + for e in self.mut_iter() { *e = 0 } + } +} + +/** + * Authentication `Tag` + * + * The tag implements the traits `TotalEq` and `Eq` using constant-time + * comparison functions. See `sodiumoxide::crypto::verify::verify_32` + */ +pub struct Tag([u8, ..TAGBYTES]); +impl TotalEq for Tag { + fn equals(&self, other: &Tag) -> bool { + verify_32(&**self, &**other) + } +} +impl Eq for Tag { + fn eq(&self, other: &Tag) -> bool { + verify_32(&**self, &**other) + } +} + +/** + * `gen_key()` randomly generates a key for authentication + * + * THREAD SAFETY: `gen_key()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +pub fn gen_key() -> ~Key { + let mut key = ~Key([0, ..KEYBYTES]); + randombytes_into(**key); + key +} + +/** + * `authenticate()` authenticates a message `m` using a secret key `k`. + * The function returns an authenticator tag. + */ +#[fixed_stack_segment] +pub fn authenticate(m: &[u8], k: &Key) -> ~Tag { + unsafe { + let mut tag = ~Tag([0, ..TAGBYTES]); + crypto_auth(to_mut_ptr(**tag), to_ptr(m), m.len() as c_ulonglong, to_ptr(**k)); + tag + } +} + +/** + * `verify()` returns `true` if `tag` is a correct authenticator of message `m` + * under a secret key `k`. Otherwise it returns false. + */ +#[fixed_stack_segment] +pub fn verify(tag: &Tag, m: &[u8], k: &Key) -> bool { + unsafe { + crypto_auth_verify(to_ptr(**tag), + to_ptr(m), + m.len() as c_ulonglong, + to_ptr(**k)) == 0 + } +} + +#[test] +fn test_auth_verify() { + use randombytes::randombytes; + for _ in range(0, 256) { + let k = gen_key(); + let m = randombytes(1024); + let tag = authenticate(m, k); + assert!(verify(tag, m, k)); + } +} + +#[test] +fn test_auth_verify_tamper() { + use randombytes::randombytes; + for _ in range(0, 32) { + let k = gen_key(); + let mut m = randombytes(1024); + let mut tag = authenticate(m, k); + for i in range(0, m.len()) { + m[i] ^= 0x20; + assert!(!verify(tag, m, k)); + m[i] ^= 0x20; + } + for i in range(0, tag.len()) { + tag[i] ^= 0x20; + assert!(!verify(tag, m, k)); + tag[i] ^= 0x20; + } + } +} + +#[test] +fn test_vector_1() { + /* "Test Case 2" from RFC 4231 */ + let key = Key([74, 101, 102, 101, 0, 0, 0, 0 + , 0, 0, 0, 0, 0, 0, 0, 0 + , 0, 0, 0, 0, 0, 0, 0, 0 + , 0, 0, 0, 0, 0, 0, 0, 0]); + let c = [0x77, 0x68, 0x61, 0x74, 0x20, 0x64, 0x6f, 0x20 + ,0x79, 0x61, 0x20, 0x77, 0x61, 0x6e, 0x74, 0x20 + ,0x66, 0x6f, 0x72, 0x20, 0x6e, 0x6f, 0x74, 0x68 + ,0x69, 0x6e, 0x67, 0x3f]; + + let a_expected = ~Tag([0x16,0x4b,0x7a,0x7b,0xfc,0xf8,0x19,0xe2 + ,0xe3,0x95,0xfb,0xe7,0x3b,0x56,0xe0,0xa3 + ,0x87,0xbd,0x64,0x22,0x2e,0x83,0x1f,0xd6 + ,0x10,0x27,0x0c,0xd7,0xea,0x25,0x05,0x54]); + + let a = authenticate(c, &key); + assert!((**a) == (**a_expected)); +} + diff --git a/src/crypto/box.rs b/src/crypto/box.rs new file mode 100644 index 0000000..1b5a39d --- /dev/null +++ b/src/crypto/box.rs @@ -0,0 +1,424 @@ +/*! +Public-key authenticated encryption + +# Security model +The `seal()` function is designed to meet the standard notions of privacy and +third-party unforgeability for a public-key authenticated-encryption scheme +using nonces. For formal definitions see, e.g., Jee Hea An, "Authenticated +encryption in the public-key setting: security notions and analyses," +http://eprint.iacr.org/2001/079. + +Distinct messages between the same {sender, receiver} set are required +to have distinct nonces. For example, the lexicographically smaller +public key can use nonce 1 for its first message to the other key, nonce +3 for its second message, nonce 5 for its third message, etc., while the +lexicographically larger public key uses nonce 2 for its first message +to the other key, nonce 4 for its second message, nonce 6 for its third +message, etc. Nonces are long enough that randomly generated nonces have +negligible risk of collision. + +There is no harm in having the same nonce for different messages if the +{sender, receiver} sets are different. This is true even if the sets +overlap. For example, a sender can use the same nonce for two different +messages if the messages are sent to two different public keys. + +The `seal()` function is not meant to provide non-repudiation. On the +contrary: the `seal()` function guarantees repudiability. A receiver +can freely modify a boxed message, and therefore cannot convince third +parties that this particular message came from the sender. The sender +and receiver are nevertheless protected against forgeries by other +parties. In the terminology of +http://groups.google.com/group/sci.crypt/msg/ec5c18b23b11d82c, +crypto_box uses "public-key authenticators" rather than "public-key +signatures." + +Users who want public verifiability (or receiver-assisted public +verifiability) should instead use signatures (or signcryption). +Signature support is a high priority for NaCl; a signature API will be +described in subsequent NaCl documentation. + +# Selected primitive +`seal()` is `curve25519xsalsa20poly1305` , a particular +combination of Curve25519, Salsa20, and Poly1305 specified in +[Cryptography in NaCl](http://nacl.cr.yp.to/valid.html). + +This function is conjectured to meet the standard notions of privacy and +third-party unforgeability. + +*/ +use std::libc::{c_ulonglong, c_int, size_t}; +use std::vec::raw::{to_mut_ptr, to_ptr}; +use utils::marshal; +use randombytes::randombytes_into; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_box_keypair(pk: *mut u8, + sk: *mut u8) -> c_int; + fn crypto_box(c: *mut u8, + m: *u8, + mlen: c_ulonglong, + n: *u8, + pk: *u8, + sk: *u8) -> c_int; + fn crypto_box_open(m: *mut u8, + c: *u8, + clen: c_ulonglong, + n: *u8, + pk: *u8, + sk: *u8) -> c_int; + fn crypto_box_beforenm(k: *mut u8, + pk: *u8, + sk: *u8) -> c_int; + fn crypto_box_afternm(c: *mut u8, + m: *u8, + mlen: c_ulonglong, + n: *u8, + k: *u8) -> c_int; + fn crypto_box_open_afternm(m: *mut u8, + c: *u8, + clen: c_ulonglong, + n: *u8, + k: *u8) -> c_int; +} + +pub static PUBLICKEYBYTES: size_t = 32; +pub static SECRETKEYBYTES: size_t = 32; +pub static NONCEBYTES: size_t = 24; +pub static PRECOMPUTEDKEYBYTES: size_t = 32; +static ZEROBYTES: uint = 32; +static BOXZEROBYTES: uint = 16; + +/** + * `PublicKey` for asymmetric authenticated encryption + */ +pub struct PublicKey([u8, ..PUBLICKEYBYTES]); +/** + * `SecretKey` for asymmetric authenticated encryption + * + * When a `SecretKey` goes out of scope its contents + * will be zeroed out + */ +pub struct SecretKey([u8, ..SECRETKEYBYTES]); +impl Drop for SecretKey { + fn drop(&mut self) { + for e in self.mut_iter() { *e = 0 } + } +} + +/** + * `Nonce` for asymmetric authenticated encryption + */ +pub struct Nonce([u8, ..NONCEBYTES]); + +/** + * `gen_keypair()` randomly generates a secret key and a corresponding public key. + * + * THREAD SAFETY: `gen_keypair()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +#[fixed_stack_segment] +pub fn gen_keypair() -> (~PublicKey, ~SecretKey) { + unsafe { + let mut pk = ~PublicKey([0u8, ..PUBLICKEYBYTES]); + let mut sk = ~SecretKey([0u8, ..SECRETKEYBYTES]); + crypto_box_keypair(to_mut_ptr(**pk), to_mut_ptr(**sk)); + (pk, sk) + } +} + +/** + * `gen_nonce()` randomly generates a nonce + * + * THREAD SAFETY: `gen_nonce()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +pub fn gen_nonce() -> ~Nonce { + let mut nonce = ~Nonce([0, ..NONCEBYTES]); + randombytes_into(**nonce); + nonce +} + +/** + * `seal()` encrypts and authenticates a message `m` using the senders secret key `sk`, + * the receivers public key `pk` and a nonce `n`. It returns a ciphertext `c`. + */ +#[fixed_stack_segment] +pub fn seal(m: &[u8], n: &Nonce, pk: &PublicKey, sk: &SecretKey) -> ~[u8] { + let (c, _) = do marshal(m, ZEROBYTES, BOXZEROBYTES) |dst, src, len| { + unsafe { + crypto_box(dst, src, len, to_ptr(**n), to_ptr(**pk), to_ptr(**sk)); + } + }; + c +} + +/** + * `open()` verifies and decrypts a ciphertext `c` using the receiver's secret key `sk`, + * the senders public key `pk`, and a nonce `n`. It returns a plaintext `Some(m)`. + * If the ciphertext fails verification, `open()` returns `None`. + */ +#[fixed_stack_segment] +pub fn open(c: &[u8], n: &Nonce, pk: &PublicKey, sk: &SecretKey) -> Option<~[u8]> { + if (c.len() < BOXZEROBYTES) { + return None + } + let (m, ret) = do marshal(c, BOXZEROBYTES, ZEROBYTES) |dst, src, len| { + unsafe { + crypto_box_open(dst, src, len, to_ptr(**n), to_ptr(**pk), to_ptr(**sk)) + } + }; + if ret == 0 { + Some(m) + } else { + None + } +} + +/** + * Applications that send several messages to the same receiver can gain speed by + * splitting `seal()` into two steps, `precompute()` and `seal_precomputed()`. + * Similarly, applications that receive several messages from the same sender can gain + * speed by splitting `open()` into two steps, `precompute()` and `open_precomputed()`. + * + * When a `PrecomputedKey` goes out of scope its contents will be zeroed out + */ +pub struct PrecomputedKey([u8, ..PRECOMPUTEDKEYBYTES]); +impl Drop for PrecomputedKey { + fn drop(&mut self) { + for e in self.mut_iter() { *e = 0 } + } +} + +/** + * `precompute()` computes an intermediate key that can be used by `seal_precomputed()` + * and `open_precomputed()` + */ +#[fixed_stack_segment] +pub fn precompute(pk: &PublicKey, sk: &SecretKey) -> ~PrecomputedKey { + let mut k = ~PrecomputedKey([0u8, ..PRECOMPUTEDKEYBYTES]); + unsafe { + crypto_box_beforenm(to_mut_ptr(**k), to_ptr(**pk), to_ptr(**sk)); + } + k +} + +/** + * `seal_precomputed()` encrypts and authenticates a message `m` using a precomputed key `k`, + * and a nonce `n`. It returns a ciphertext `c`. + */ +#[fixed_stack_segment] +pub fn seal_precomputed(m: &[u8], n: &Nonce, k: &PrecomputedKey) -> ~[u8] { + let (c, _) = do marshal(m, ZEROBYTES, BOXZEROBYTES) |dst, src, len| { + unsafe { + crypto_box_afternm(dst, src, len, to_ptr(**n), to_ptr(**k)); + } + }; + c +} + +/** + * `open_precomputed()` verifies and decrypts a ciphertext `c` using a precomputed + * key `k` and a nonce `n`. It returns a plaintext `Some(m)`. + * If the ciphertext fails verification, `open_precomputed()` returns `None`. + */ +#[fixed_stack_segment] +pub fn open_precomputed(c: &[u8], n: &Nonce, k: &PrecomputedKey) -> Option<~[u8]> { + if (c.len() < BOXZEROBYTES) { + return None + } + let (m, ret) = do marshal(c, BOXZEROBYTES, ZEROBYTES) |dst, src, len| { + unsafe { + crypto_box_open_afternm(dst, src, len, to_ptr(**n), to_ptr(**k)) + } + }; + if ret == 0 { + Some(m) + } else { + None + } +} + +#[test] +fn test_seal_open() { + use randombytes::randombytes; + for _ in range(0, 256) { + let (pk1, sk1) = gen_keypair(); + let (pk2, sk2) = gen_keypair(); + let m = randombytes(1024); + let n = gen_nonce(); + let c = seal(m, n, pk1, sk2); + let opened = open(c, n, pk2, sk1); + assert!(Some(m) == opened); + } +} + +#[test] +fn test_seal_open_precomputed() { + use randombytes::randombytes; + for _ in range(0, 256) { + let (pk1, sk1) = gen_keypair(); + let (pk2, sk2) = gen_keypair(); + let k1 = precompute(pk1, sk2); + let k2 = precompute(pk2, sk1); + assert!(**k1 == **k2); + let m = randombytes(1024); + let n = gen_nonce(); + let c = seal_precomputed(m, n, k1); + let opened = open_precomputed(c, n, k2); + assert!(Some(m) == opened); + } +} + +#[test] +fn test_seal_open_tamper() { + use randombytes::randombytes; + for _ in range(0, 32) { + let (pk1, sk1) = gen_keypair(); + let (pk2, sk2) = gen_keypair(); + let m = randombytes(1024); + let n = gen_nonce(); + let mut c = seal(m, n, pk1, sk2); + for i in range(0, c.len()) { + c[i] ^= 0x20; + let opened = open(c, n, pk2, sk1); + assert!(None == opened); + c[i] ^= 0x20; + } + } +} + +#[test] +fn test_seal_open_precomputed_tamper() { + use randombytes::randombytes; + for _ in range(0, 32) { + let (pk1, sk1) = gen_keypair(); + let (pk2, sk2) = gen_keypair(); + let k1 = precompute(pk1, sk2); + let k2 = precompute(pk2, sk1); + let m = randombytes(1024); + let n = gen_nonce(); + let mut c = seal_precomputed(m, n, k1); + for i in range(0, c.len()) { + c[i] ^= 0x20; + let opened = open_precomputed(c, n, k2); + assert!(None == opened); + c[i] ^= 0x20; + } + } +} + +#[test] +fn test_vector_1() { + let alicesk = SecretKey([0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d, + 0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45, + 0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a, + 0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x2a]); + let bobpk = PublicKey([0xde,0x9e,0xdb,0x7d,0x7b,0x7d,0xc1,0xb4, + 0xd3,0x5b,0x61,0xc2,0xec,0xe4,0x35,0x37, + 0x3f,0x83,0x43,0xc8,0x5b,0x78,0x67,0x4d, + 0xad,0xfc,0x7e,0x14,0x6f,0x88,0x2b,0x4f]); + let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73, + 0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6, + 0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]); + let m = [0xbe,0x07,0x5f,0xc5,0x3c,0x81,0xf2,0xd5, + 0xcf,0x14,0x13,0x16,0xeb,0xeb,0x0c,0x7b, + 0x52,0x28,0xc5,0x2a,0x4c,0x62,0xcb,0xd4, + 0x4b,0x66,0x84,0x9b,0x64,0x24,0x4f,0xfc, + 0xe5,0xec,0xba,0xaf,0x33,0xbd,0x75,0x1a, + 0x1a,0xc7,0x28,0xd4,0x5e,0x6c,0x61,0x29, + 0x6c,0xdc,0x3c,0x01,0x23,0x35,0x61,0xf4, + 0x1d,0xb6,0x6c,0xce,0x31,0x4a,0xdb,0x31, + 0x0e,0x3b,0xe8,0x25,0x0c,0x46,0xf0,0x6d, + 0xce,0xea,0x3a,0x7f,0xa1,0x34,0x80,0x57, + 0xe2,0xf6,0x55,0x6a,0xd6,0xb1,0x31,0x8a, + 0x02,0x4a,0x83,0x8f,0x21,0xaf,0x1f,0xde, + 0x04,0x89,0x77,0xeb,0x48,0xf5,0x9f,0xfd, + 0x49,0x24,0xca,0x1c,0x60,0x90,0x2e,0x52, + 0xf0,0xa0,0x89,0xbc,0x76,0x89,0x70,0x40, + 0xe0,0x82,0xf9,0x37,0x76,0x38,0x48,0x64, + 0x5e,0x07,0x05]; + let c = seal(m, &nonce, &bobpk, &alicesk); + let pk = precompute(&bobpk, &alicesk); + let cpre = seal_precomputed(m, &nonce, pk); + let cexp = ~[0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5, + 0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9, + 0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73, + 0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce, + 0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4, + 0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a, + 0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b, + 0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72, + 0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2, + 0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38, + 0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a, + 0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae, + 0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea, + 0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda, + 0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde, + 0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3, + 0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6, + 0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74, + 0xe3,0x55,0xa5]; + assert!(c == cexp); + assert!(cpre == cexp); +} + +#[test] +fn test_vector_2() { + let bobsk = SecretKey([0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b, + 0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6, + 0x6f,0x3b,0xb1,0x29,0x26,0x18,0xb6,0xfd, + 0x1c,0x2f,0x8b,0x27,0xff,0x88,0xe0,0xeb]); + let alicepk = PublicKey([0x85,0x20,0xf0,0x09,0x89,0x30,0xa7,0x54, + 0x74,0x8b,0x7d,0xdc,0xb4,0x3e,0xf7,0x5a, + 0x0d,0xbf,0x3a,0x0d,0x26,0x38,0x1a,0xf4, + 0xeb,0xa4,0xa9,0x8e,0xaa,0x9b,0x4e,0x6a]); + let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73, + 0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6, + 0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]); + let c = [0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5, + 0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9, + 0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73, + 0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce, + 0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4, + 0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a, + 0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b, + 0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72, + 0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2, + 0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38, + 0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a, + 0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae, + 0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea, + 0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda, + 0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde, + 0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3, + 0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6, + 0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74, + 0xe3,0x55,0xa5]; + let mexp = Some(~[0xbe,0x07,0x5f,0xc5,0x3c,0x81,0xf2,0xd5, + 0xcf,0x14,0x13,0x16,0xeb,0xeb,0x0c,0x7b, + 0x52,0x28,0xc5,0x2a,0x4c,0x62,0xcb,0xd4, + 0x4b,0x66,0x84,0x9b,0x64,0x24,0x4f,0xfc, + 0xe5,0xec,0xba,0xaf,0x33,0xbd,0x75,0x1a, + 0x1a,0xc7,0x28,0xd4,0x5e,0x6c,0x61,0x29, + 0x6c,0xdc,0x3c,0x01,0x23,0x35,0x61,0xf4, + 0x1d,0xb6,0x6c,0xce,0x31,0x4a,0xdb,0x31, + 0x0e,0x3b,0xe8,0x25,0x0c,0x46,0xf0,0x6d, + 0xce,0xea,0x3a,0x7f,0xa1,0x34,0x80,0x57, + 0xe2,0xf6,0x55,0x6a,0xd6,0xb1,0x31,0x8a, + 0x02,0x4a,0x83,0x8f,0x21,0xaf,0x1f,0xde, + 0x04,0x89,0x77,0xeb,0x48,0xf5,0x9f,0xfd, + 0x49,0x24,0xca,0x1c,0x60,0x90,0x2e,0x52, + 0xf0,0xa0,0x89,0xbc,0x76,0x89,0x70,0x40, + 0xe0,0x82,0xf9,0x37,0x76,0x38,0x48,0x64, + 0x5e,0x07,0x05]); + let m = open(c, &nonce, &alicepk, &bobsk); + let pk = precompute(&alicepk, &bobsk); + let m_pre = open_precomputed(c, &nonce, pk); + assert!(m == mexp); + assert!(m_pre == mexp); +} diff --git a/src/crypto/hash.rs b/src/crypto/hash.rs new file mode 100644 index 0000000..46acae0 --- /dev/null +++ b/src/crypto/hash.rs @@ -0,0 +1,67 @@ +/*! +Hashing + +# Security model +The `hash()` function is designed to be usable as a strong +component of DSA, RSA-PSS, key derivation, hash-based +message-authentication codes, hash-based ciphers, and various other +common applications. "Strong" means that the security of these +applications, when instantiated with `hash()`, is the same +as the security of the applications against generic attacks. In +particular, the `hash()` function is designed to make +finding collisions difficult. + +# Selected primitive +`hash()` is currently an implementation of `SHA-512`. + +There has been considerable degradation of public confidence in the +security conjectures for many hash functions, including `SHA-512`. +However, for the moment, there do not appear to be alternatives that +inspire satisfactory levels of confidence. One can hope that NIST's +SHA-3 competition will improve the situation. +*/ +use std::libc::{c_ulonglong, c_int}; +use std::vec::raw::{to_mut_ptr, to_ptr}; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_hash(h: *mut u8, + m: *u8, + mlen: c_ulonglong) -> c_int; +} + +pub static HASHBYTES: uint = 64; +pub static BLOCKBYTES: uint = 128; + +/** + * Digest-structure + */ +pub struct Digest([u8, ..HASHBYTES]); + +/** + * `hash` hashes a message `m`. It returns a hash `h`. + */ +#[fixed_stack_segment] +pub fn hash(m: &[u8]) -> ~Digest { + unsafe { + let mut h = ~Digest([0, ..HASHBYTES]); + crypto_hash(to_mut_ptr(**h), to_ptr(m), m.len() as c_ulonglong); + h + } +} + +#[test] +fn test_vector_1() { + let x = [0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0xa]; + let h_expected = ~Digest([0x24, 0xf9, 0x50, 0xaa, 0xc7, 0xb9, 0xea, 0x9b + ,0x3c, 0xb7, 0x28, 0x22, 0x8a, 0x0c, 0x82, 0xb6 + ,0x7c, 0x39, 0xe9, 0x6b, 0x4b, 0x34, 0x47, 0x98 + ,0x87, 0x0d, 0x5d, 0xae, 0xe9, 0x3e, 0x3a, 0xe5 + ,0x93, 0x1b, 0xaa, 0xe8, 0xc7, 0xca, 0xcf, 0xea + ,0x4b, 0x62, 0x94, 0x52, 0xc3, 0x80, 0x26, 0xa8 + ,0x1d, 0x13, 0x8b, 0xc7, 0xaa, 0xd1, 0xaf, 0x3e + ,0xf7, 0xbf, 0xd5, 0xec, 0x64, 0x6d, 0x6c, 0x28]); + let h = hash(x); + assert!((**h) == (**h_expected)); +} diff --git a/src/crypto/onetimeauth.rs b/src/crypto/onetimeauth.rs new file mode 100644 index 0000000..ab91198 --- /dev/null +++ b/src/crypto/onetimeauth.rs @@ -0,0 +1,190 @@ +/*! +Secret-key One-time authentication + +# Security model +The `authenticate()` function, viewed as a function +of the message for a uniform random key, is designed to meet the +standard notion of unforgeability after a single message. After the +sender authenticates one message, an attacker cannot find authenticators +for any other messages. + +The sender must not use `authenticate()` to authenticate more than one message +under the same key. Authenticators for two messages under the same key should +be expected to reveal enough information to allow forgeries of authenticators +on other messages. + +# Selected primitive +`authenticate()` is `crypto_onetimeauth_poly1305`, an authenticator specified +in [Cryptography in NaCl](http://nacl.cr.yp.to/valid.html), Section 9. This +authenticator is proven to meet the standard notion of unforgeability after a +single message. +*/ +use std::libc::{c_ulonglong, c_int}; +use std::vec::raw::{to_ptr, to_mut_ptr}; +use randombytes::randombytes_into; +use crypto::verify::verify_16; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_onetimeauth(a: *mut u8, + m: *u8, + mlen: c_ulonglong, + k: *u8) -> c_int; + fn crypto_onetimeauth_verify(a: *u8, + m: *u8, + mlen: c_ulonglong, + k: *u8) -> c_int; +} + +/** + * Length of `Key` (in bytes) + */ +pub static KEYBYTES: uint = 32; + +/** + * `Key` for one-time authentication + * + * When a `Key` goes out of scope its contents + * will be zeroed out + */ +pub struct Key([u8, ..KEYBYTES]); +impl Drop for Key { + fn drop(&mut self) { + for e in self.mut_iter() { + *e = 0 + } + } +} + +/** + * Length of `Tag` (in bytes) + */ +pub static TAGBYTES: uint = 16; + +/** + * Authentication tag + + * The tag implements the traits `TotalEq` and `Eq` using constant-time + * comparison functions. See `sodiumoxide::crypto::verify::verify_16` + */ +pub struct Tag([u8, ..TAGBYTES]); +impl TotalEq for Tag { + fn equals(&self, other: &Tag) -> bool { + verify_16(&**self, &**other) + } +} +impl Eq for Tag { + fn eq(&self, other: &Tag) -> bool { + verify_16(&**self, &**other) + } +} + +/** + * `gen_key()` randomly generates a key for one-time authentication + * + * THREAD SAFETY: `gen_key()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +pub fn gen_key() -> ~Key { + let mut key = ~Key([0, ..KEYBYTES]); + randombytes_into(**key); + key +} + +/** + * `authenticate()` authenticates a message `m` using a secret key `k` + * and returns an authenticator tag. + */ +#[fixed_stack_segment] +fn authenticate(m: &[u8], k: &Key) -> ~Tag { + unsafe { + let mut tag = ~Tag([0, ..TAGBYTES]); + crypto_onetimeauth(to_mut_ptr(**tag), + to_ptr(m), + m.len() as c_ulonglong, + to_ptr(**k)); + tag + } +} + +/** + * `verify()` returns `true` if `tag` is a correct authenticator of + * a message `m` under a secret key `k`. Otherwise `verify()` returns false. + */ +#[fixed_stack_segment] +fn verify(tag: &Tag, m: &[u8], k: &Key) -> bool { + unsafe { + crypto_onetimeauth_verify(to_ptr(**tag), + to_ptr(m), + m.len() as c_ulonglong, + to_ptr(**k)) == 0 + } +} + +#[test] +fn test_onetimeauth_verify() { + use randombytes::randombytes; + for _ in range(0, 256) { + let k = gen_key(); + let m = randombytes(1024); + let tag = authenticate(m, k); + assert!(verify(tag, m, k)); + assert!(tag == authenticate(m, k)); + } +} + +#[test] +fn test_onetimeauth_verify_tamper() { + use randombytes::randombytes; + for _ in range(0, 32) { + let k = gen_key(); + let mut m = randombytes(1024); + let mut tag = authenticate(m, k); + for i in range(0, m.len()) { + m[i] ^= 0x20; + assert!(!verify(tag, m, k)); + assert!(tag != authenticate(m, k)); + m[i] ^= 0x20; + } + for i in range(0, tag.len()) { + tag[i] ^= 0x20; + assert!(!verify(tag, m, k)); + assert!(tag != authenticate(m, k)); + tag[i] ^= 0x20; + } + } +} + +#[test] +fn test_vector_1() { + let key = Key([0xee,0xa6,0xa7,0x25,0x1c,0x1e,0x72,0x91 + ,0x6d,0x11,0xc2,0xcb,0x21,0x4d,0x3c,0x25 + ,0x25,0x39,0x12,0x1d,0x8e,0x23,0x4e,0x65 + ,0x2d,0x65,0x1f,0xa4,0xc8,0xcf,0xf8,0x80]); + + let c = [0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73 + ,0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce + ,0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4 + ,0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a + ,0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b + ,0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72 + ,0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2 + ,0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38 + ,0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a + ,0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae + ,0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea + ,0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda + ,0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde + ,0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3 + ,0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6 + ,0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74 + ,0xe3,0x55,0xa5]; + + let a_expected = Tag([0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5 + ,0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9]); + let a = authenticate(c, &key); + assert!(*a == a_expected); + assert!(verify(a, c, &key)); +} diff --git a/src/crypto/scalarmult.rs b/src/crypto/scalarmult.rs new file mode 100644 index 0000000..bfc08b5 --- /dev/null +++ b/src/crypto/scalarmult.rs @@ -0,0 +1,155 @@ +/*! Scalar multiplication + +# Representation of group elements +The correspondence between strings and group elements depends on the primitive +implemented by `scalarmult()`. The correspondence is not necessarily +injective in either direction, but it is compatible with scalar multiplication +in the group. The correspondence does not necessarily include all group +elements, but it does include all strings; i.e., every string represents at +least one group element. + +# Representation of integers +The correspondence between strings and integers also depends on the primitive +implemented by `scalarmult()`. Every string represents at least one integer. + +# Security model +`scalarmult` is designed to be strong as a component of various well-known +"hashed Diffie–Hellman" applications. In particular, it is designed to make the +"computational Diffie–Hellman" problem (CDH) difficult with respect to the +standard base. + +`scalarmult` is also designed to make CDH difficult with respect to other +nontrivial bases. In particular, if a represented group element has small +order, then it is annihilated by all represented scalars. This feature allows +protocols to avoid validating membership in the subgroup generated by the +standard base. + +NaCl does not make any promises regarding the "decisional Diffie–Hellman" +problem (DDH), the "static Diffie–Hellman" problem (SDH), etc. Users are +responsible for hashing group elements. + +# Selected primitive +`scalarmult is the function crypto_scalarmult_curve25519 specified in +[Cryptography in NaCl](http://nacl.cr.yp.to/valid.html), Sections 2, 3, and 4. +This function is conjectured to be strong. For background see Bernstein, +"Curve25519: new Diffie-Hellman speed records," Lecture Notes in Computer +Science 3958 (2006), 207–228, http://cr.yp.to/papers.html#curve25519. +*/ + +use std::libc::c_int; +use std::vec::raw::{to_ptr, to_mut_ptr}; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_scalarmult(q: *mut u8, + n: *u8, + p: *u8) -> c_int; + fn crypto_scalarmult_base(q: *mut u8, + n: *u8) -> c_int; +} + +pub static BYTES: uint = 32; +pub static SCALARBYTES: uint = 32; + +/** + * `Scalar` value (integer in byte representation) + */ +pub struct Scalar([u8, ..SCALARBYTES]); +/** + * `GroupElement` + */ +pub struct GroupElement([u8, ..BYTES]); + +/** + * `scalarmult()` multiplies a group element `p` + * by an integer `n`. It returns the resulting group element + * `q`. + */ +#[fixed_stack_segment] +pub fn scalarmult(n: &Scalar, + p: &GroupElement) -> ~GroupElement { + let mut q = ~GroupElement([0, ..BYTES]); + unsafe { + crypto_scalarmult(to_mut_ptr(**q), to_ptr(**n), to_ptr(**p)); + } + q +} + +/** + * `scalarmult_base()` computes the scalar product of a standard + * group element and an integer `n`. It returns the resulting + * group element `q`/ + */ +#[fixed_stack_segment] +pub fn scalarmult_base(n: &Scalar) -> ~GroupElement { + let mut q = ~GroupElement([0, ..BYTES]); + unsafe { + crypto_scalarmult_base(to_mut_ptr(**q), to_ptr(**n)); + } + q +} + +#[test] +fn test_vector_1() { + let alicesk = Scalar([0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d + ,0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45 + ,0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a + ,0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x2a]); + let alicepk_expected = GroupElement([0x85,0x20,0xf0,0x09,0x89,0x30,0xa7,0x54 + ,0x74,0x8b,0x7d,0xdc,0xb4,0x3e,0xf7,0x5a + ,0x0d,0xbf,0x3a,0x0d,0x26,0x38,0x1a,0xf4 + ,0xeb,0xa4,0xa9,0x8e,0xaa,0x9b,0x4e,0x6a]); + let alicepk = scalarmult_base(&alicesk); + assert!(**alicepk == *alicepk_expected); +} + +#[test] +fn test_vector_2() { + let bobsk = Scalar([0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b + ,0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6 + ,0x6f,0x3b,0xb1,0x29,0x26,0x18,0xb6,0xfd + ,0x1c,0x2f,0x8b,0x27,0xff,0x88,0xe0,0xeb]); + let bobpk_expected = GroupElement([0xde,0x9e,0xdb,0x7d,0x7b,0x7d,0xc1,0xb4 + ,0xd3,0x5b,0x61,0xc2,0xec,0xe4,0x35,0x37 + ,0x3f,0x83,0x43,0xc8,0x5b,0x78,0x67,0x4d + ,0xad,0xfc,0x7e,0x14,0x6f,0x88,0x2b,0x4f]); + let bobpk = scalarmult_base(&bobsk); + assert!(**bobpk == *bobpk_expected); +} + +#[test] +fn test_vector_3() { + let alicesk = Scalar([0x77,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d + ,0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45 + ,0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a + ,0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x2a]); + let bobpk = GroupElement([0xde,0x9e,0xdb,0x7d,0x7b,0x7d,0xc1,0xb4 + ,0xd3,0x5b,0x61,0xc2,0xec,0xe4,0x35,0x37 + ,0x3f,0x83,0x43,0xc8,0x5b,0x78,0x67,0x4d + ,0xad,0xfc,0x7e,0x14,0x6f,0x88,0x2b,0x4f]); + let k_expected = GroupElement([0x4a,0x5d,0x9d,0x5b,0xa4,0xce,0x2d,0xe1 + ,0x72,0x8e,0x3b,0xf4,0x80,0x35,0x0f,0x25 + ,0xe0,0x7e,0x21,0xc9,0x47,0xd1,0x9e,0x33 + ,0x76,0xf0,0x9b,0x3c,0x1e,0x16,0x17,0x42]); + let k = scalarmult(&alicesk, &bobpk); + assert!(**k == *k_expected); +} + +#[test] +fn test_vector_4() { + let bobsk = Scalar([0x5d,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b + ,0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6 + ,0x6f,0x3b,0xb1,0x29,0x26,0x18,0xb6,0xfd + ,0x1c,0x2f,0x8b,0x27,0xff,0x88,0xe0,0xeb]); + let alicepk = GroupElement([0x85,0x20,0xf0,0x09,0x89,0x30,0xa7,0x54 + ,0x74,0x8b,0x7d,0xdc,0xb4,0x3e,0xf7,0x5a + ,0x0d,0xbf,0x3a,0x0d,0x26,0x38,0x1a,0xf4 + ,0xeb,0xa4,0xa9,0x8e,0xaa,0x9b,0x4e,0x6a]); + let k_expected = GroupElement([0x4a,0x5d,0x9d,0x5b,0xa4,0xce,0x2d,0xe1 + ,0x72,0x8e,0x3b,0xf4,0x80,0x35,0x0f,0x25 + ,0xe0,0x7e,0x21,0xc9,0x47,0xd1,0x9e,0x33 + ,0x76,0xf0,0x9b,0x3c,0x1e,0x16,0x17,0x42]); + let k = scalarmult(&bobsk, &alicepk); + assert!(**k == *k_expected); +} diff --git a/src/crypto/secretbox.rs b/src/crypto/secretbox.rs new file mode 100644 index 0000000..cf21842 --- /dev/null +++ b/src/crypto/secretbox.rs @@ -0,0 +1,212 @@ +/*! +Secret-key authenticated encryption + +# Security model +The `seal()` function is designed to meet the standard notions of privacy and +authenticity for a secret-key authenticated-encryption scheme using nonces. For +formal definitions see, e.g., Bellare and Namprempre, "Authenticated +encryption: relations among notions and analysis of the generic composition +paradigm," Lecture Notes in Computer Science 1976 (2000), 531–545, +http://www-cse.ucsd.edu/~mihir/papers/oem.html. + +Note that the length is not hidden. Note also that it is the caller's +responsibility to ensure the uniqueness of nonces—for example, by using +nonce 1 for the first message, nonce 2 for the second message, etc. +Nonces are long enough that randomly generated nonces have negligible +risk of collision. + +# Selected primitive +`seal()` is `crypto_secretbox_xsalsa20poly1305`, a particular +combination of Salsa20 and Poly1305 specified in +[Cryptography in NaCl](http://nacl.cr.yp.to/valid.html). + +This function is conjectured to meet the standard notions of privacy and +authenticity. +*/ +use std::libc::{c_ulonglong, c_int}; +use std::vec::raw::{to_ptr}; +use utils::marshal; +use randombytes::randombytes_into; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_secretbox(c: *mut u8, + m: *u8, + mlen: c_ulonglong, + n: *u8, + k: *u8) -> c_int; + fn crypto_secretbox_open(m: *mut u8, + c: *u8, + clen: c_ulonglong, + n: *u8, + k: *u8) -> c_int; +} + +pub static KEYBYTES: uint = 32; +pub static NONCEBYTES: uint = 24; + +/** + * `Key` for symmetric authenticated encryption + * + * When a `Key` goes out of scope its contents + * will be zeroed out + */ +pub struct Key([u8, ..KEYBYTES]); +impl Drop for Key { + fn drop(&mut self) { + for e in self.mut_iter() { *e = 0 } + } +} + +/** + * `Nonce` for symmetric authenticated encryption + */ +pub struct Nonce([u8, ..NONCEBYTES]); + +pub static ZEROBYTES: uint = 32; +pub static BOXZEROBYTES: uint = 16; + +/** + * `gen_key()` randomly generates a secret key + * + * THREAD SAFETY: `gen_key()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +pub fn gen_key() -> ~Key { + let mut key = ~Key([0, ..KEYBYTES]); + randombytes_into(**key); + key +} + +/** + * `gen_nonce()` randomly generates a nonce + * + * THREAD SAFETY: `gen_key()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +pub fn gen_nonce() -> ~Nonce { + let mut nonce = ~Nonce([0, ..NONCEBYTES]); + randombytes_into(**nonce); + nonce +} + +/** + * `seal()` encrypts and authenticates a message `m` using a secret key `k` and a + * nonce `n`. It returns a ciphertext `c`. + */ +#[fixed_stack_segment] +pub fn seal(m: &[u8], n: &Nonce, k: &Key) -> ~[u8] { + let (c, _) = do marshal(m, ZEROBYTES, BOXZEROBYTES) |dst, src, len| { + unsafe { + crypto_secretbox(dst, src, len, to_ptr(**n), to_ptr(**k)) + } + }; + c +} + +/** + * `open()` verifies and decrypts a ciphertext `c` using a secret key `k` and a nonce `n`. + * It returns a plaintext `Some(m)`. + * If the ciphertext fails verification, `open()` returns `None`. + */ +#[fixed_stack_segment] +pub fn open(c: &[u8], n: &Nonce, k: &Key) -> Option<~[u8]> { + if (c.len() < BOXZEROBYTES) { + return None + } + let (m, ret) = do marshal(c, BOXZEROBYTES, ZEROBYTES) |dst, src, len| { + unsafe { + crypto_secretbox_open(dst, src, len, to_ptr(**n), to_ptr(**k)) + } + }; + if ret == 0 { + Some(m) + } else { + None + } +} + +#[test] +fn test_seal_open() { + use randombytes::randombytes; + for _ in range(0, 256) { + let k = gen_key(); + let m = randombytes(1024); + let n = gen_nonce(); + let c = seal(m, n, k); + let opened = open(c, n, k); + assert!(Some(m) == opened); + } +} + +#[test] +fn test_seal_open_tamper() { + use randombytes::randombytes; + for _ in range(0, 32) { + let k = gen_key(); + let m = randombytes(1024); + let n = gen_nonce(); + let mut c = seal(m, n, k); + for i in range(0, c.len()) { + c[i] ^= 0x20; + let opened = open(c, n, k); + assert!(None == opened); + c[i] ^= 0x20; + } + } +} + +#[test] +fn test_vector_1() { + let firstkey = Key([0x1b,0x27,0x55,0x64,0x73,0xe9,0x85,0xd4 + ,0x62,0xcd,0x51,0x19,0x7a,0x9a,0x46,0xc7 + ,0x60,0x09,0x54,0x9e,0xac,0x64,0x74,0xf2 + ,0x06,0xc4,0xee,0x08,0x44,0xf6,0x83,0x89]); + let nonce = Nonce([0x69,0x69,0x6e,0xe9,0x55,0xb6,0x2b,0x73 + ,0xcd,0x62,0xbd,0xa8,0x75,0xfc,0x73,0xd6 + ,0x82,0x19,0xe0,0x03,0x6b,0x7a,0x0b,0x37]); + let m = ~[0xbe,0x07,0x5f,0xc5,0x3c,0x81,0xf2,0xd5 + ,0xcf,0x14,0x13,0x16,0xeb,0xeb,0x0c,0x7b + ,0x52,0x28,0xc5,0x2a,0x4c,0x62,0xcb,0xd4 + ,0x4b,0x66,0x84,0x9b,0x64,0x24,0x4f,0xfc + ,0xe5,0xec,0xba,0xaf,0x33,0xbd,0x75,0x1a + ,0x1a,0xc7,0x28,0xd4,0x5e,0x6c,0x61,0x29 + ,0x6c,0xdc,0x3c,0x01,0x23,0x35,0x61,0xf4 + ,0x1d,0xb6,0x6c,0xce,0x31,0x4a,0xdb,0x31 + ,0x0e,0x3b,0xe8,0x25,0x0c,0x46,0xf0,0x6d + ,0xce,0xea,0x3a,0x7f,0xa1,0x34,0x80,0x57 + ,0xe2,0xf6,0x55,0x6a,0xd6,0xb1,0x31,0x8a + ,0x02,0x4a,0x83,0x8f,0x21,0xaf,0x1f,0xde + ,0x04,0x89,0x77,0xeb,0x48,0xf5,0x9f,0xfd + ,0x49,0x24,0xca,0x1c,0x60,0x90,0x2e,0x52 + ,0xf0,0xa0,0x89,0xbc,0x76,0x89,0x70,0x40 + ,0xe0,0x82,0xf9,0x37,0x76,0x38,0x48,0x64 + ,0x5e,0x07,0x05]; + + let c_expected = ~[0xf3,0xff,0xc7,0x70,0x3f,0x94,0x00,0xe5 + ,0x2a,0x7d,0xfb,0x4b,0x3d,0x33,0x05,0xd9 + ,0x8e,0x99,0x3b,0x9f,0x48,0x68,0x12,0x73 + ,0xc2,0x96,0x50,0xba,0x32,0xfc,0x76,0xce + ,0x48,0x33,0x2e,0xa7,0x16,0x4d,0x96,0xa4 + ,0x47,0x6f,0xb8,0xc5,0x31,0xa1,0x18,0x6a + ,0xc0,0xdf,0xc1,0x7c,0x98,0xdc,0xe8,0x7b + ,0x4d,0xa7,0xf0,0x11,0xec,0x48,0xc9,0x72 + ,0x71,0xd2,0xc2,0x0f,0x9b,0x92,0x8f,0xe2 + ,0x27,0x0d,0x6f,0xb8,0x63,0xd5,0x17,0x38 + ,0xb4,0x8e,0xee,0xe3,0x14,0xa7,0xcc,0x8a + ,0xb9,0x32,0x16,0x45,0x48,0xe5,0x26,0xae + ,0x90,0x22,0x43,0x68,0x51,0x7a,0xcf,0xea + ,0xbd,0x6b,0xb3,0x73,0x2b,0xc0,0xe9,0xda + ,0x99,0x83,0x2b,0x61,0xca,0x01,0xb6,0xde + ,0x56,0x24,0x4a,0x9e,0x88,0xd5,0xf9,0xb3 + ,0x79,0x73,0xf6,0x22,0xa4,0x3d,0x14,0xa6 + ,0x59,0x9b,0x1f,0x65,0x4c,0xb4,0x5a,0x74 + ,0xe3,0x55,0xa5]; + let c = seal(m, &nonce, &firstkey); + assert!(c == c_expected); + let m2 = open(c, &nonce, &firstkey); + assert!(Some(m) == m2); +} diff --git a/src/crypto/sign.rs b/src/crypto/sign.rs new file mode 100644 index 0000000..ed9cf85 --- /dev/null +++ b/src/crypto/sign.rs @@ -0,0 +1,134 @@ +/*! +Public-key signatures + +# Security model +The `sign()` function is designed to meet the standard +notion of unforgeability for a public-key signature scheme under +chosen-message attacks. + +# Selected primitive +`crypto::sign::sign` is `ed25519`, a signature scheme specified in +[Ed25519](http://ed25519.cr.yp.to/). This function is conjectured to meet the +standard notion of unforgeability for a public-key signature scheme under +chosen-message attacks. +*/ +use std::libc::{c_ulonglong, c_int}; +use std::vec::{from_elem}; +use std::vec::raw::{to_mut_ptr, to_ptr}; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_sign_keypair(pk: *mut u8, + sk: *mut u8) -> c_int; + fn crypto_sign(sm: *mut u8, + smlen: *mut c_ulonglong, + m: *u8, + mlen: c_ulonglong, + sk: *u8) -> c_int; + fn crypto_sign_open(m: *mut u8, + mlen: *mut c_ulonglong, + sm: *u8, + smlen: c_ulonglong, + pk: *u8) -> c_int; +} + +pub static SECRETKEYBYTES: uint = 64; +pub static PUBLICKEYBYTES: uint = 32; +pub static SIGNATUREBYTES: uint = 64; + +/** + * `SecretKey` for signatures + * + * When a `SecretKey` goes out of scope its contents + * will be zeroed out + */ +pub struct SecretKey([u8, ..SECRETKEYBYTES]); +impl Drop for SecretKey { + fn drop(&mut self) { + for e in self.mut_iter() { *e = 0 } + } +} +/** + * `PublicKey` for signatures + */ +pub struct PublicKey([u8, ..PUBLICKEYBYTES]); + +/** + * `gen_keypair()` randomly generates a secret key and a corresponding public + * key. + * + * THREAD SAFETY: `gen_keypair()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +#[fixed_stack_segment] +pub fn gen_keypair() -> (~PublicKey, ~SecretKey) { + unsafe { + let mut pk = ~PublicKey([0u8, ..PUBLICKEYBYTES]); + let mut sk = ~SecretKey([0u8, ..SECRETKEYBYTES]); + crypto_sign_keypair(to_mut_ptr(**pk), to_mut_ptr(**sk)); + (pk, sk) + } +} + +/** + * `sign()` signs a message `m` using the signer's secret key `sk`. + * `sign()` returns the resulting signed message `sm`. + */ +#[fixed_stack_segment] +pub fn sign(m: &[u8], sk: &SecretKey) -> ~[u8] { + unsafe { + let mut sm = from_elem(m.len() + SIGNATUREBYTES, 0u8); + let mut smlen = 0; + crypto_sign(to_mut_ptr(sm), &mut smlen, to_ptr(m), m.len() as c_ulonglong, to_ptr(**sk)); + sm.truncate(smlen as uint); + sm + } +} + +/** + * `verify()` verifies the signature in `sm` using the signer's public key `pk`. + * `verify()` returns the message `Some(m)`. + * If the signature fails verification, `verify()` returns `None`. + */ +#[fixed_stack_segment] +pub fn verify(sm: &[u8], pk: &PublicKey) -> Option<~[u8]> { + unsafe { + let mut m = from_elem(sm.len(), 0u8); + let mut mlen = 0; + if crypto_sign_open(to_mut_ptr(m), &mut mlen, to_ptr(sm), sm.len() as c_ulonglong, to_ptr(**pk)) == 0 { + m.truncate(mlen as uint); + Some(m) + } else { + None + } + } +} + +#[test] +fn test_sign_verify() { + use randombytes::randombytes; + for _ in range(0, 256) { + let (pk, sk) = gen_keypair(); + let m = randombytes(1024); + let sm = sign(m, sk); + let m2 = verify(sm, pk); + assert!(Some(m) == m2); + } +} + +#[test] +fn test_sign_verify_tamper() { + use randombytes::randombytes; + for _ in range(0, 32) { + let (pk, sk) = gen_keypair(); + let m = randombytes(1024); + let mut sm = sign(m, sk); + for i in range(0, sm.len()) { + sm[i] ^= 0x20; + assert!(None == verify(sm, pk)); + sm[i] ^= 0x20; + } + } +} diff --git a/src/crypto/stream.rs b/src/crypto/stream.rs new file mode 100644 index 0000000..ea2be71 --- /dev/null +++ b/src/crypto/stream.rs @@ -0,0 +1,150 @@ +/*! +Secret-key encryption + +# Security Model +The `stream()` function, viewed as a function of the nonce for a +uniform random key, is designed to meet the standard notion of +unpredictability ("PRF"). For a formal definition see, e.g., Section 2.3 +of Bellare, Kilian, and Rogaway, "The security of the cipher block +chaining message authentication code," Journal of Computer and System +Sciences 61 (2000), 362–399; +http://www-cse.ucsd.edu/~mihir/papers/cbc.html. + +This means that an attacker cannot distinguish this function from a +uniform random function. Consequently, if a series of messages is +encrypted by `stream_xor()` with a different nonce for each message, +the ciphertexts are indistinguishable from uniform random strings of the +same length. + +Note that the length is not hidden. Note also that it is the caller's +responsibility to ensure the uniqueness of nonces—for example, by using +nonce 1 for the first message, nonce 2 for the second message, etc. +Nonces are long enough that randomly generated nonces have negligible +risk of collision. + +NaCl does not make any promises regarding the resistance of `stream()` to +"related-key attacks." It is the caller's responsibility to use proper +key-derivation functions. + +# Selected primitive +`stream()` is `crypto_stream_xsalsa20`, a particular cipher specified in +[Cryptography in NaCl](http://nacl.cr.yp.to/valid.html), Section 7. +This cipher is conjectured to meet the standard notion of +unpredictability. +*/ +use std::libc::{c_ulonglong, c_int}; +use std::vec::{from_elem}; +use std::vec::raw::{to_mut_ptr, to_ptr}; +use utils::marshal; +use randombytes::randombytes_into; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_stream(c: *mut u8, + clen: c_ulonglong, + n: *u8, + k: *u8) -> c_int; + fn crypto_stream_xor(c: *mut u8, + m: *u8, + mlen: c_ulonglong, + n: *u8, + k: *u8) -> c_int; +} + +pub static KEYBYTES: uint = 32; +pub static NONCEBYTES: uint = 24; + +/** + * `Key` for symmetric encryption + * + * When a `Key` goes out of scope its contents + * will be zeroed out + */ +pub struct Key([u8, ..KEYBYTES]); +impl Drop for Key { + fn drop(&mut self) { + for e in self.mut_iter() { *e = 0 } + } +} + +/** + * `Nonce` for symmetric encryption + */ +pub struct Nonce([u8, ..NONCEBYTES]); + +/** + * `gen_key()` randomly generates a key for symmetric encryption + * + * THREAD SAFETY: `gen_key()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +fn gen_key() -> ~Key { + let mut key = ~Key([0, ..KEYBYTES]); + randombytes_into(**key); + key +} + +/** + * `gen_nonce()` randomly generates a nonce for symmetric encryption + * + * THREAD SAFETY: `gen_nonce()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +fn gen_nonce() -> ~Nonce { + let mut nonce = ~Nonce([0, ..NONCEBYTES]); + randombytes_into(**nonce); + nonce +} + +/** + * `stream()` produces a `len`-byte stream `c` as a function of a + * secret key `k` and a nonce `n`. + */ +#[fixed_stack_segment] +fn stream(len: uint, n: &Nonce, k: &Key) -> ~[u8] { + unsafe { + let mut c = from_elem(len, 0u8); + crypto_stream(to_mut_ptr(c), c.len() as c_ulonglong, to_ptr(**n), to_ptr(**k)); + c + } +} + +/** + * `stream_xor()` encrypts a message `m` using a secret key `k` and a nonce `n`. + * The `stream_xor()` function returns the ciphertext `c`. + * + * `stream_xor()` guarantees that the ciphertext has the same length as the plaintext, + * and is the plaintext xor the output of `stream()`. + * Consequently `stream_xor()` can also be used to decrypt. + */ +#[fixed_stack_segment] +fn stream_xor(m: &[u8], n: &Nonce, k: &Key) -> ~[u8] { + let (c, _) = do marshal(m, 0, 0) |dst, src, len| { + unsafe { + crypto_stream_xor(dst, src, len, to_ptr(**n), to_ptr(**k)) + } + }; + c +} + +/** +* `stream_xor_inplace` encrypts a message `m` using a secret key `k` and a nonce `n`. +* The `stream_xor_inplace()` function encrypts the message in place. +* +* `stream_xor_inplace()` guarantees that the ciphertext has the same length as +* the plaintext, and is the plaintext xor the output of `stream_inplace()`. +* Consequently `stream_xor_inplace()` can also be used to decrypt. +*/ +#[fixed_stack_segment] +fn stream_xor_inplace(m: &mut [u8], n: &Nonce, k: &Key) { + unsafe { + crypto_stream_xor(to_mut_ptr(m), + to_ptr(m), + m.len() as c_ulonglong, + to_ptr(**n), + to_ptr(**k)); + } +} diff --git a/src/crypto/verify.rs b/src/crypto/verify.rs new file mode 100644 index 0000000..63edf4d --- /dev/null +++ b/src/crypto/verify.rs @@ -0,0 +1,84 @@ +/*! +Constant-time comparison of fixed-size vecs +*/ +use std::libc::c_int; +use std::vec::raw::to_ptr; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn crypto_verify_16(x: *u8, y: *u8) -> c_int; + fn crypto_verify_32(x: *u8, y: *u8) -> c_int; +} + +/** + * `verify_16()` returns `true` if `x[0]`, `x[1]`, ..., `x[15]` are the + * same as `y[0]`, `y[1]`, ..., `y[15]`. Otherwise it returns `false`. + * + * This functions is safe to use for secrets `x[0]`, `x[1]`, ..., `x[15]`, + * `y[0]`, `y[1]`, ..., `y[15]`. The time taken by `verify_16` is independent + * of the contents of `x[0]`, `x[1]`, ..., `x[15]`, `y[0]`, `y[1]`, ..., `y[15]`. + * In contrast, the standard C comparison function `memcmp(x,y,16)` takes time + * that depends on the longest matching prefix of `x` and `y`, often allowing easy + * timing attacks. + */ +#[fixed_stack_segment] +pub fn verify_16(x: &[u8, ..16], y: &[u8, ..16]) -> bool { + unsafe { + crypto_verify_16(to_ptr(*x), to_ptr(*y)) == 0 + } +} + +/** + * `verify_32()` returns true if `x[0]`, `x[1]`, ..., `x[31]` are the + * same as `y[0]`, `y[1]`, ..., `y[31]`. Otherwise it returns false. + * + * This functions is safe to use for secrets `x[0]`, `x[1]`, ..., `x[31]`, + * `y[0]`, `y[1]`, ..., `y[31]`. The time taken by `verify_32` is independent + * of the contents of `x[0]`, `x[1]`, ..., `x[31]`, `y[0]`, `y[1]`, ..., `y[31]`. + * In contrast, the standard C comparison function `memcmp(x,y,32)` takes time + * that depends on the longest matching prefix of `x` and `y`, often allowing easy + * timing attacks. + */ +#[fixed_stack_segment] +pub fn verify_32(x: &[u8, ..32], y: &[u8, ..32]) -> bool { + unsafe { + crypto_verify_32(to_ptr(*x), to_ptr(*y)) == 0 + } +} + +#[test] +fn test_verify_16() { + use randombytes::randombytes_into; + + for _ in range(0, 256) { + let mut x = [0, ..16]; + let mut y = [0, ..16]; + assert!(verify_16(&x, &y)); + randombytes_into(x); + randombytes_into(y); + if (x == y) { + assert!(verify_16(&x, &y)) + } else { + assert!(!verify_16(&x, &y)) + } + } +} + +#[test] +fn test_verify_32() { + use randombytes::randombytes_into; + + for _ in range(0, 256) { + let mut x = [0, ..32]; + let mut y = [0, ..32]; + assert!(verify_32(&x, &y)); + randombytes_into(x); + randombytes_into(y); + if (x == y) { + assert!(verify_32(&x, &y)) + } else { + assert!(!verify_32(&x, &y)) + } + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..4b165ac --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,98 @@ +/*! +Rust bindings to the [sodium library](https://github.com/jedisct1/libsodium). + +Sodium is a portable implementation of Dan Bernsteins [NaCl: Networking and +Cryptography library](http://nacl.cr.yp.to) + +For most users, if you want public-key (asymmetric) cryptography you should use +the functions in `crypto::box` for encryption/decryption. + +If you want secret-key (symmetric) cryptography you should be using the +functions in `crypto::secretbox` for encryption/decryption. + +For public-key signatures you should use the functions in `crypto::sign` for +signature creation and verification. + +Unless you know what you're doing you most certainly don't want to use the +functions in `crypto::scalarmult`, `crypto::stream`, `crypto::auth` and +`crypto::onetimeauth`. + +## Thread Safety +All functions in this library are thread-safe provided that the `init()` +function has been called during program execution. + +If `init()` hasn't been called then all functions except the random-number +generation functions and the key-generation functions are thread-safe. + +# Public-key cryptography + `crypto::box` + + `crypto::sign` + +# Secret-key cryptography + `crypto::secretbox` + + `crypto::stream` + + `crypto::auth` + + `crypto::onetimeauth` + +# Low-level functions + `crypto::hash` + + `crypto::verify` + */ +#[link( name = "sodiumoxide" + , package_id = "sodiumoxide" + , vers = "0.0" + , author = "Daniel Ashhami" + , uuid = "28c02cec-5c30-11e3-8e0f-0021ccb4fd10")]; +#[crate_type = "lib"]; +//#[warn(owned_heap_memory)]; +//#[warn(unstable)]; +#[warn(missing_doc)]; +//#[warn(heap_memory)]; +#[warn(non_uppercase_statics)]; +#[warn(non_camel_case_types)]; +#[warn(managed_heap_memory)]; +#[warn(unnecessary_qualification)]; +use std::libc::c_int; + +#[link(name = "sodium")] +#[link_args = "-lsodium"] +extern { + fn sodium_init() -> c_int; +} + +/** + * `init()` initializes the sodium library and chooses faster versions of + * the primitives if possible. `init()` also makes the random number generation + * functions (`gen_key`, `gen_keypair`, `gen_nonce`, `randombytes`, `randombytes_into`) + * thread-safe + */ +#[fixed_stack_segment] +pub fn init() -> bool { + unsafe { + sodium_init() == 0 + } +} + +/** + * Cryptographic functions + */ +pub mod crypto { + pub mod box; + pub mod sign; + pub mod scalarmult; + pub mod auth; + pub mod hash; + pub mod secretbox; + pub mod onetimeauth; + pub mod stream; + + pub mod verify; +} + +pub mod randombytes; +mod utils; diff --git a/src/randombytes.rs b/src/randombytes.rs new file mode 100644 index 0000000..4b5b3d0 --- /dev/null +++ b/src/randombytes.rs @@ -0,0 +1,42 @@ +/*! Cryptographic random number generation +*/ +use std::libc::size_t; +use std::vec::from_elem; +use std::vec::raw::to_mut_ptr; + +#[link(name = "sodium")] +extern { + fn randombytes_buf(buf: *mut u8, + size: size_t); +} + +/** + * `randombytes()` randomly generates size bytes of data. + * + * THREAD SAFETY: `randombytes()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +#[fixed_stack_segment] +pub fn randombytes(size: uint) -> ~[u8] { + unsafe { + let mut buf = from_elem(size, 0u8); + let pbuf = to_mut_ptr(buf); + randombytes_buf(pbuf, size as size_t); + buf + } +} + +/** + * `randombytes_into()` fills a buffer `buf` with random data. + * + * THREAD SAFETY: `randombytes_into()` is thread-safe provided that you have + * called `sodiumoxide::init()` once before using any other function + * from sodiumoxide. + */ +#[fixed_stack_segment] +pub fn randombytes_into(buf: &mut [u8]) { + unsafe { + randombytes_buf(to_mut_ptr(buf), buf.len() as size_t); + } +} diff --git a/src/utils.rs b/src/utils.rs new file mode 100644 index 0000000..bc84dde --- /dev/null +++ b/src/utils.rs @@ -0,0 +1,23 @@ +use std::libc::{c_ulonglong}; +use std::vec::{with_capacity, append}; +use std::vec::raw::{to_mut_ptr, to_ptr}; + +#[doc(hidden)] +pub fn marshal(buf: &[u8], + padbefore: uint, + bytestodrop: uint, + f: &fn(*mut u8, *u8, c_ulonglong) -> T + ) -> (~[u8], T) { + let mut dst = with_capacity(buf.len() + padbefore); + for _ in range(0, padbefore) { + dst.push(0); + } + dst = append(dst, buf); + let pdst = to_mut_ptr(dst); + let psrc = to_ptr(dst); + let res = f(pdst, psrc, dst.len() as c_ulonglong); + for _ in range(0, bytestodrop) { + dst.shift(); + } + (dst, res) +}