diff options
author | Robin Krahl <me@robin-krahl.de> | 2018-12-11 23:50:45 +0100 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2018-12-17 07:52:13 -0800 |
commit | 986ad2f782cf944990e4eda8bf88ea1821233302 (patch) | |
tree | 1717075a4eb11861c32e5c45d01e47360fb1264d /rand/src/prng/chacha.rs | |
parent | e97c287c01cf22a1b582a7da9b309b58f3935d0e (diff) | |
download | nitrocli-986ad2f782cf944990e4eda8bf88ea1821233302.tar.gz nitrocli-986ad2f782cf944990e4eda8bf88ea1821233302.tar.bz2 |
Add nitrokey as a dependency to nitrocli
The nitrokey crate provides a simple interface to the Nitrokey Storage
and the Nitrokey Pro based on the libnitrokey library developed by
Nitrokey UG. The low-level bindings to this library are available in
the nitrokey-sys crate.
This patch adds version v0.2.1 of the nitrokey crate as a dependency
for nitrocli. It includes the indirect dependencies nitrokey-sys
(version 3.4.1) and rand (version 0.4.3).
Import subrepo nitrokey/:nitrokey at 2eccc96ceec2282b868891befe9cda7f941fbe7b
Import subrepo nitrokey-sys/:nitrokey-sys at f1a11ebf72610fb9cf80ac7f9f147b4ba1a5336f
Import subrepo rand/:rand at d7d5da49daf7ceb3e5940072940d495cced3a1b3
Diffstat (limited to 'rand/src/prng/chacha.rs')
-rw-r--r-- | rand/src/prng/chacha.rs | 321 |
1 files changed, 321 insertions, 0 deletions
diff --git a/rand/src/prng/chacha.rs b/rand/src/prng/chacha.rs new file mode 100644 index 0000000..a73e8e7 --- /dev/null +++ b/rand/src/prng/chacha.rs @@ -0,0 +1,321 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or +// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license +// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The ChaCha random number generator. + +use core::num::Wrapping as w; +use {Rng, SeedableRng, Rand}; + +#[allow(bad_style)] +type w32 = w<u32>; + +const KEY_WORDS : usize = 8; // 8 words for the 256-bit key +const STATE_WORDS : usize = 16; +const CHACHA_ROUNDS: u32 = 20; // Cryptographically secure from 8 upwards as of this writing + +/// A random number generator that uses the ChaCha20 algorithm [1]. +/// +/// The ChaCha algorithm is widely accepted as suitable for +/// cryptographic purposes, but this implementation has not been +/// verified as such. Prefer a generator like `OsRng` that defers to +/// the operating system for cases that need high security. +/// +/// [1]: D. J. Bernstein, [*ChaCha, a variant of +/// Salsa20*](http://cr.yp.to/chacha.html) +#[derive(Copy, Clone, Debug)] +pub struct ChaChaRng { + buffer: [w32; STATE_WORDS], // Internal buffer of output + state: [w32; STATE_WORDS], // Initial state + index: usize, // Index into state +} + +static EMPTY: ChaChaRng = ChaChaRng { + buffer: [w(0); STATE_WORDS], + state: [w(0); STATE_WORDS], + index: STATE_WORDS +}; + + +macro_rules! quarter_round{ + ($a: expr, $b: expr, $c: expr, $d: expr) => {{ + $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left(16)); + $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left(12)); + $a = $a + $b; $d = $d ^ $a; $d = w($d.0.rotate_left( 8)); + $c = $c + $d; $b = $b ^ $c; $b = w($b.0.rotate_left( 7)); + }} +} + +macro_rules! double_round{ + ($x: expr) => {{ + // Column round + quarter_round!($x[ 0], $x[ 4], $x[ 8], $x[12]); + quarter_round!($x[ 1], $x[ 5], $x[ 9], $x[13]); + quarter_round!($x[ 2], $x[ 6], $x[10], $x[14]); + quarter_round!($x[ 3], $x[ 7], $x[11], $x[15]); + // Diagonal round + quarter_round!($x[ 0], $x[ 5], $x[10], $x[15]); + quarter_round!($x[ 1], $x[ 6], $x[11], $x[12]); + quarter_round!($x[ 2], $x[ 7], $x[ 8], $x[13]); + quarter_round!($x[ 3], $x[ 4], $x[ 9], $x[14]); + }} +} + +#[inline] +fn core(output: &mut [w32; STATE_WORDS], input: &[w32; STATE_WORDS]) { + *output = *input; + + for _ in 0..CHACHA_ROUNDS / 2 { + double_round!(output); + } + + for i in 0..STATE_WORDS { + output[i] = output[i] + input[i]; + } +} + +impl ChaChaRng { + + /// Create an ChaCha random number generator using the default + /// fixed key of 8 zero words. + /// + /// # Examples + /// + /// ```rust + /// use rand::{Rng, ChaChaRng}; + /// + /// let mut ra = ChaChaRng::new_unseeded(); + /// println!("{:?}", ra.next_u32()); + /// println!("{:?}", ra.next_u32()); + /// ``` + /// + /// Since this equivalent to a RNG with a fixed seed, repeated executions + /// of an unseeded RNG will produce the same result. This code sample will + /// consistently produce: + /// + /// - 2917185654 + /// - 2419978656 + pub fn new_unseeded() -> ChaChaRng { + let mut rng = EMPTY; + rng.init(&[0; KEY_WORDS]); + rng + } + + /// Sets the internal 128-bit ChaCha counter to + /// a user-provided value. This permits jumping + /// arbitrarily ahead (or backwards) in the pseudorandom stream. + /// + /// Since the nonce words are used to extend the counter to 128 bits, + /// users wishing to obtain the conventional ChaCha pseudorandom stream + /// associated with a particular nonce can call this function with + /// arguments `0, desired_nonce`. + /// + /// # Examples + /// + /// ```rust + /// use rand::{Rng, ChaChaRng}; + /// + /// let mut ra = ChaChaRng::new_unseeded(); + /// ra.set_counter(0u64, 1234567890u64); + /// println!("{:?}", ra.next_u32()); + /// println!("{:?}", ra.next_u32()); + /// ``` + pub fn set_counter(&mut self, counter_low: u64, counter_high: u64) { + self.state[12] = w((counter_low >> 0) as u32); + self.state[13] = w((counter_low >> 32) as u32); + self.state[14] = w((counter_high >> 0) as u32); + self.state[15] = w((counter_high >> 32) as u32); + self.index = STATE_WORDS; // force recomputation + } + + /// Initializes `self.state` with the appropriate key and constants + /// + /// We deviate slightly from the ChaCha specification regarding + /// the nonce, which is used to extend the counter to 128 bits. + /// This is provably as strong as the original cipher, though, + /// since any distinguishing attack on our variant also works + /// against ChaCha with a chosen-nonce. See the XSalsa20 [1] + /// security proof for a more involved example of this. + /// + /// The modified word layout is: + /// ```text + /// constant constant constant constant + /// key key key key + /// key key key key + /// counter counter counter counter + /// ``` + /// [1]: Daniel J. Bernstein. [*Extending the Salsa20 + /// nonce.*](http://cr.yp.to/papers.html#xsalsa) + fn init(&mut self, key: &[u32; KEY_WORDS]) { + self.state[0] = w(0x61707865); + self.state[1] = w(0x3320646E); + self.state[2] = w(0x79622D32); + self.state[3] = w(0x6B206574); + + for i in 0..KEY_WORDS { + self.state[4+i] = w(key[i]); + } + + self.state[12] = w(0); + self.state[13] = w(0); + self.state[14] = w(0); + self.state[15] = w(0); + + self.index = STATE_WORDS; + } + + /// Refill the internal output buffer (`self.buffer`) + fn update(&mut self) { + core(&mut self.buffer, &self.state); + self.index = 0; + // update 128-bit counter + self.state[12] = self.state[12] + w(1); + if self.state[12] != w(0) { return }; + self.state[13] = self.state[13] + w(1); + if self.state[13] != w(0) { return }; + self.state[14] = self.state[14] + w(1); + if self.state[14] != w(0) { return }; + self.state[15] = self.state[15] + w(1); + } +} + +impl Rng for ChaChaRng { + #[inline] + fn next_u32(&mut self) -> u32 { + if self.index == STATE_WORDS { + self.update(); + } + + let value = self.buffer[self.index % STATE_WORDS]; + self.index += 1; + value.0 + } +} + +impl<'a> SeedableRng<&'a [u32]> for ChaChaRng { + + fn reseed(&mut self, seed: &'a [u32]) { + // reset state + self.init(&[0u32; KEY_WORDS]); + // set key in place + let key = &mut self.state[4 .. 4+KEY_WORDS]; + for (k, s) in key.iter_mut().zip(seed.iter()) { + *k = w(*s); + } + } + + /// Create a ChaCha generator from a seed, + /// obtained from a variable-length u32 array. + /// Only up to 8 words are used; if less than 8 + /// words are used, the remaining are set to zero. + fn from_seed(seed: &'a [u32]) -> ChaChaRng { + let mut rng = EMPTY; + rng.reseed(seed); + rng + } +} + +impl Rand for ChaChaRng { + fn rand<R: Rng>(other: &mut R) -> ChaChaRng { + let mut key : [u32; KEY_WORDS] = [0; KEY_WORDS]; + for word in key.iter_mut() { + *word = other.gen(); + } + SeedableRng::from_seed(&key[..]) + } +} + + +#[cfg(test)] +mod test { + use {Rng, SeedableRng}; + use super::ChaChaRng; + + #[test] + fn test_rng_rand_seeded() { + let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>(); + let mut ra: ChaChaRng = SeedableRng::from_seed(&s[..]); + let mut rb: ChaChaRng = SeedableRng::from_seed(&s[..]); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_rng_seeded() { + let seed : &[_] = &[0,1,2,3,4,5,6,7]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + let mut rb: ChaChaRng = SeedableRng::from_seed(seed); + assert!(::test::iter_eq(ra.gen_ascii_chars().take(100), + rb.gen_ascii_chars().take(100))); + } + + #[test] + fn test_rng_reseed() { + let s = ::test::rng().gen_iter::<u32>().take(8).collect::<Vec<u32>>(); + let mut r: ChaChaRng = SeedableRng::from_seed(&s[..]); + let string1: String = r.gen_ascii_chars().take(100).collect(); + + r.reseed(&s); + + let string2: String = r.gen_ascii_chars().take(100).collect(); + assert_eq!(string1, string2); + } + + #[test] + fn test_rng_true_values() { + // Test vectors 1 and 2 from + // http://tools.ietf.org/html/draft-nir-cfrg-chacha20-poly1305-04 + let seed : &[_] = &[0u32; 8]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + + let v = (0..16).map(|_| ra.next_u32()).collect::<Vec<_>>(); + assert_eq!(v, + vec!(0xade0b876, 0x903df1a0, 0xe56a5d40, 0x28bd8653, + 0xb819d2bd, 0x1aed8da0, 0xccef36a8, 0xc70d778b, + 0x7c5941da, 0x8d485751, 0x3fe02477, 0x374ad8b8, + 0xf4b8436a, 0x1ca11815, 0x69b687c3, 0x8665eeb2)); + + let v = (0..16).map(|_| ra.next_u32()).collect::<Vec<_>>(); + assert_eq!(v, + vec!(0xbee7079f, 0x7a385155, 0x7c97ba98, 0x0d082d73, + 0xa0290fcb, 0x6965e348, 0x3e53c612, 0xed7aee32, + 0x7621b729, 0x434ee69c, 0xb03371d5, 0xd539d874, + 0x281fed31, 0x45fb0a51, 0x1f0ae1ac, 0x6f4d794b)); + + + let seed : &[_] = &[0,1,2,3,4,5,6,7]; + let mut ra: ChaChaRng = SeedableRng::from_seed(seed); + + // Store the 17*i-th 32-bit word, + // i.e., the i-th word of the i-th 16-word block + let mut v : Vec<u32> = Vec::new(); + for _ in 0..16 { + v.push(ra.next_u32()); + for _ in 0..16 { + ra.next_u32(); + } + } + + assert_eq!(v, + vec!(0xf225c81a, 0x6ab1be57, 0x04d42951, 0x70858036, + 0x49884684, 0x64efec72, 0x4be2d186, 0x3615b384, + 0x11cfa18e, 0xd3c50049, 0x75c775f6, 0x434c6530, + 0x2c5bad8f, 0x898881dc, 0x5f1c86d9, 0xc1f8e7f4)); + } + + #[test] + fn test_rng_clone() { + let seed : &[_] = &[0u32; 8]; + let mut rng: ChaChaRng = SeedableRng::from_seed(seed); + let mut clone = rng.clone(); + for _ in 0..16 { + assert_eq!(rng.next_u64(), clone.next_u64()); + } + } +} |