更新libclamav库1.0.0版本

This commit is contained in:
2023-01-14 18:28:39 +08:00
parent b879ee0b2e
commit 45fe15f472
8531 changed files with 1222046 additions and 177272 deletions

View File

@@ -0,0 +1 @@
{"files":{"Cargo.toml":"0092c7ff927d9966f9f3ddea55475e1f2988ae0374747010df519fd87438b48d","LICENSE.md":"ad4d94921cc5b085cb7c7d3c9120e14c89eef4240b07f8386b909d218cc7f922","README.md":"b6aea014b38cad6b2720fbbe6f25ebee5f3c56157d1e64f3fac72994e951d64a","README.tpl":"3bdb6718fcba47d31fd2662860e0682ba0203aeb82ca5f3b1357c8cd3de771bf","deny.toml":"8249258c924be1be4a53cd8c583fe5d9a2e2667aed5ca3b59c149a684b8b7a7d","enforce-conventional-commits.py":"92c65450283518dde0f8ef7da8145274e74c8d401ec5756a8416e93955a22493","src/buffer.rs":"3523981adc7effb9adb0ab81620f426869cbc12fa2819692204341c753ef3818","src/crypto.rs":"18fd0e3b5f5d8ef57ea428c5502986eb62201fbc2f56ea2a03b40b7b0f893eb2","src/crypto/chacha.rs":"1b9788c884ffa25e9a24ce47acc4ea878651559bc24ed25b6a8b789213ddba61","src/entropy.rs":"e308d027bf0584fb1650f90d8c81acd6dd9ecbe601caa2c96d67975177f3362b","src/entropy/darwin.rs":"da18b88f2005f9af35854ab622035d8c399f1b57dd79252098208996f5a56730","src/entropy/linux.rs":"4944a453a7c63b473c1b4fc4eb243d2396cb4eb2faa718ec37276657517ceb25","src/entropy/windows.rs":"4c9565fa77d1022251f9540f9f26886727faf58050dec9370c103bd27fa07966","src/entropy/windows_uwp.rs":"3fd4cd3ecbec389072b0a9caaaa84020d0d9984555bd80382a61755924f54735","src/gen.rs":"5a67a38efd0d6bf5b2a42b3cc1ec52aa314476db8d34d6157eb4bbafc11dcb07","src/lib.rs":"257abb9be6c3785465859e931cc4a92540931f6d8f831e010e4b4e13991e9849","src/rand.rs":"7311732f46d5c658c3b497fd14211e83502065c4cf519f8bbc89e6886205fbe9","src/rand/chacha.rs":"8e9a792e6655c41c096804e47e674ffb879bb006299675777d40e1f4a46492d8","src/rand/pcg64.rs":"9c2146a3f248fb93c346d5e7f2cdae05484c96981260c0c047f75c932f657b89","src/rand/wyrand.rs":"0f9a90108b3cc815c03f46397cb5f08cc85e03fadaa00468c5328c6cf6f2edc1","src/tls.rs":"776746aeb1f40706b5cbda65dae8fa86c0c8949e770f10add896d729159e821d"},"package":"6a51313c5820b0b02bd422f4b44776fbf47961755c74ce64afc73bfad10226c3"}

View File

@@ -0,0 +1,50 @@
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
#
# When uploading crates to the registry Cargo will automatically
# "normalize" Cargo.toml files for maximal compatibility
# with all versions of Cargo and also rewrite `path` dependencies
# to registry (e.g., crates.io) dependencies.
#
# If you are reading this file be aware that the original Cargo.toml
# will likely look very different (and much more reasonable).
# See Cargo.toml.orig for the original contents.
[package]
edition = "2021"
name = "nanorand"
version = "0.7.0"
authors = ["Lucy <lucy@absolucy.moe>"]
description = "A tiny, fast, zero-dep library for random number generation."
readme = "README.md"
keywords = ["rand", "random", "no-std", "entropy"]
categories = ["algorithms", "no-std"]
license = "Zlib"
repository = "https://github.com/Absolucy/nanorand-rs"
resolver = "2"
[package.metadata.docs.rs]
all-features = true
default-target = "x86_64-unknown-linux-gnu"
targets = ["x86_64-pc-windows-msvc"]
[profile.bench]
lto = "thin"
[dependencies.getrandom]
version = "0.2.5"
features = ["rdrand", "js"]
optional = true
[dependencies.zeroize]
version = "1.5.3"
features = ["zeroize_derive"]
optional = true
[dev-dependencies.hex]
version = "0.4.3"
[features]
alloc = []
chacha = []
default = ["std", "tls", "wyrand", "pcg64", "chacha"]
pcg64 = []
rdseed = ["std"]
std = ["alloc"]
tls = ["std", "wyrand"]
wyrand = []

View File

@@ -0,0 +1,22 @@
The zlib/libpng License
=======================
Copyright (c) 2021 lucy
This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use of
this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim
that you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@@ -0,0 +1,115 @@
[![crates.io](https://img.shields.io/crates/v/nanorand.svg)](https://crates.io/crates/nanorand) [![docs.rs](https://docs.rs/nanorand/badge.svg)](https://docs.rs/nanorand) [![License: Zlib](https://img.shields.io/badge/License-Zlib-brightgreen.svg)](https://opensource.org/licenses/Zlib) [![Tests](https://github.com/Absolucy/nanorand-rs/workflows/Tests/badge.svg?event=push&branch=master)](https://github.com/Absolucy/nanorand-rs/actions?query=workflow%3A%22Run+Tests%22) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/Absolucy/nanorand-rs.svg)](https://isitmaintained.com/project/Absolucy/nanorand-rs "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/Absolucy/nanorand-rs.svg)](https://isitmaintained.com/project/Absolucy/nanorand-rs "Percentage of issues still open") ![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg)
# nanorand
Current version: **0.7.0**
A library meant for fast, random number generation with quick compile time, and minimal dependencies.
## Examples
### Generating a number with an initialized RNG
```rust
use nanorand::{Rng, WyRand};
let mut rng = WyRand::new();
println!("Random number: {}", rng.generate::<u64>());
```
### Generating a number with a thread-local RNG
```rust
use nanorand::Rng;
let mut rng = nanorand::tls_rng();
println!("Random number: {}", rng.generate::<u64>());
```
### Generating a number in a range
```rust
use nanorand::{Rng, WyRand};
let mut rng = WyRand::new();
println!("Random number between 1 and 100: {}", rng.generate_range(1_u64..=100));
println!("Random number between -100 and 50: {}", rng.generate_range(-100_i64..=50));
```
#### Buffering random bytes
```rust
use nanorand::{Rng, BufferedRng, WyRand};
let mut thingy = [0u8; 5];
let mut rng = BufferedRng::new(WyRand::new());
rng.fill(&mut thingy);
// As WyRand generates 8 bytes of output, and our target is only 5 bytes,
// 3 bytes will remain in the buffer.
assert_eq!(rng.buffered(), 3);
```
### Shuffling a Vec
```rust
use nanorand::{Rng, WyRand};
let mut rng = WyRand::new();
let mut items = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
rng.shuffle(&mut items);
```
### Why should I use this over...
* `rand` - The standard rand crate is a complex beast. It contains unsafe code in the core implementations, and while it has much more options than we do, that's kind of the point. We're straight to the point, while rand is everything and the kitchen sink.
* `fastrand`, `oorandom`, `random-fast-rng`, or `randomize` - These are all minimal, zero-dep implementations of the PCG family of RNGs (Pcg32 and Pcg64). While these are decent, they are _much_ slower than wyrand (which beats the speed of these Pcg32 implementations while providing 64 random bits), and do not provide CSPRNGs.
* `getrandom` - The getrandom crate just provides OS entropy sources. It is not meant for random number generation. In fact, we provide it as an optional entropy source.
### RNG Implementations
**RNG**|**nanorand type**|**Output Size**|**Cryptographically Secure**|**Speed**<sup>1</sup>|**Notes**|**Original Implementation**
:-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:
wyrand|[`nanorand::WyRand`](rand/wyrand/struct.WyRand.html), [`nanorand::tls::TlsWyRand`](tls/fn.tls_rng.html)|64 bits (`u64`)|🚫|16.4 GB/s||[https://github.com/lemire/testingRNG/blob/master/source/wyrand.h](https://github.com/lemire/testingRNG/blob/master/source/wyrand.h)
Pcg64|[`nanorand::Pcg64`](rand/pcg64/struct.Pcg64.html)|64 bits (`u64`)|🚫|1.6 GB/s||[https://github.com/rkern/pcg64](https://github.com/rkern/pcg64)
ChaCha|[`nanorand::ChaCha`](rand/chacha/struct.ChaCha.html)|512 bits (`[u32; 16]`)|✅|204 MB/s (ChaCha8), 79 MB/s (ChaCha20)|Only works in Rust 1.47 or above|[https://cr.yp.to/chacha.html](https://cr.yp.to/chacha.html)
<sup>1. Speed benchmarked on an M1 Macbook Air</sup>
### Entropy Sources
_Listed in order of priority_
* If the `getrandom` feature is enabled, then [`getrandom::getrandom`](https://docs.rs/getrandom/*/getrandom/fn.getrandom.html) will be called.
* If the `rdseed` feature is enabled, and is running on an x86(-64) system with the [RDSEED](https://en.wikipedia.org/wiki/RDRAND) instruction, then
we will attempt to source as much entropy as possible via our [`rdseed_entropy`](entropy::rdseed_entropy) function
* Linux and Android will attempt to use the [`getrandom`](https://man7.org/linux/man-pages/man2/getrandom.2.html) syscall.
* macOS and iOS (Darwin-based systems) will use Security.framework's [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes).
* Windows
* If we're targeting UWP, then the [`BCryptGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom) is used with system-preferred RNG (`BCRYPT_USE_SYSTEM_PREFERRED_RNG`).
* Otherwise, we'll use [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom).
### Feature Flags
* `alloc` (default) - Enables Rust `alloc` lib features, such as a buffering Rng wrapper.
* `std` (default) - Enables Rust `std` lib features, such as seeding from OS entropy sources. Requires `alloc` to be enabled.
* `tls` (default) - Enables a thread-local [`WyRand`](rand/wyrand/struct.WyRand.html) RNG (see below). Requires `std` to be enabled.
* `wyrand` (default) - Enable the [`WyRand`](rand/wyrand/struct.WyRand.html) RNG.
* `pcg64` (default) - Enable the [`Pcg64`](rand/pcg64/struct.Pcg64.html) RNG.
* `chacha` - Enable the [`ChaCha`](rand/chacha/struct.ChaCha.html) RNG. Requires Rust 1.47 or later.
* `rdseed` - On x86 and x86-64 platforms, the `rdseed` intrinsic will be used when OS entropy isn't available.
* `zeroize` - Implement the [Zeroize](https://crates.io/crates/zeroize) trait for all RNGs.
* `getrandom` - Use the [`getrandom`](https://crates.io/crates/getrandom) crate as an entropy source.
Works on most systems, optional due to the fact that it brings in more dependencies.
## License
The zlib/libpng License
Copyright (c) 2022 Lucy <lucy@absolucy.moe>
This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use of
this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim
that you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@@ -0,0 +1,31 @@
[![crates.io](https://img.shields.io/crates/v/nanorand.svg)](https://crates.io/crates/nanorand) [![docs.rs](https://docs.rs/nanorand/badge.svg)](https://docs.rs/nanorand) [![License: Zlib](https://img.shields.io/badge/License-Zlib-brightgreen.svg)](https://opensource.org/licenses/Zlib) [![Tests](https://github.com/Absolucy/nanorand-rs/workflows/Tests/badge.svg?event=push&branch=master)](https://github.com/Absolucy/nanorand-rs/actions?query=workflow%3A%22Run+Tests%22) [![Average time to resolve an issue](https://isitmaintained.com/badge/resolution/Absolucy/nanorand-rs.svg)](https://isitmaintained.com/project/Absolucy/nanorand-rs "Average time to resolve an issue") [![Percentage of issues still open](https://isitmaintained.com/badge/open/Absolucy/nanorand-rs.svg)](https://isitmaintained.com/project/Absolucy/nanorand-rs "Percentage of issues still open") ![Maintenance](https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg)
# {{crate}}
Current version: **{{version}}**
{{readme}}
## License
The zlib/libpng License
Copyright (c) 2022 Lucy <lucy@absolucy.moe>
This software is provided 'as-is', without any express or implied warranty. In
no event will the authors be held liable for any damages arising from the use of
this software.
Permission is granted to anyone to use this software for any purpose, including
commercial applications, and to alter it and redistribute it freely, subject to
the following restrictions:
1. The origin of this software must not be misrepresented; you must not claim
that you wrote the original software. If you use this software in a product,
an acknowledgment in the product documentation would be appreciated but is
not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@@ -0,0 +1,55 @@
[advisories]
db-path = "~/.cargo/advisory-db"
db-urls = ["https://github.com/rustsec/advisory-db"]
vulnerability = "deny"
unmaintained = "warn"
yanked = "warn"
notice = "warn"
[licenses]
unlicensed = "deny"
copyleft = "warn"
allow-osi-fsf-free = "neither"
confidence-threshold = 0.8
allow = [
"MIT",
"Zlib",
"Apache-2.0",
"Unlicense",
"0BSD",
"BSL-1.0",
"BSD-3-Clause"
]
[bans]
multiple-versions = "warn"
wildcards = "allow"
highlight = "all"
deny = [
{ name = "CoreFoundation-sys" },
{ name = "exif" },
{ name = "exif-sys" },
{ name = "gphoto" },
{ name = "gphoto2-sys" },
{ name = "ioctl-rs" },
{ name = "IOKit-rs" },
{ name = "libraw" },
{ name = "libraw-sys" },
{ name = "libudev" },
{ name = "libudev-sys" },
{ name = "libusb" },
{ name = "libusb-sys" },
{ name = "mach" },
{ name = "serial" },
{ name = "serial-core" },
{ name = "serial-unix" },
{ name = "serial-windows" },
{ name = "termios" },
{ name = "zwave" },
]
[sources]
unknown-registry = "warn"
unknown-git = "warn"
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
allow-git = []

View File

@@ -0,0 +1,14 @@
#!/usr/bin/env python
import re, sys, os
def main():
# example:
# feat(apikey): added the ability to add api key to configuration
pattern = r'(build|ci|docs|feat|fix|perf|refactor|style|test|chore|revert)(\([\w\-]+\))?:\s.*'
filename = sys.argv[1]
ss = open(filename, 'r').read()
m = re.match(pattern, ss)
if m == None: raise Exception("conventional commit validation failed")
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,110 @@
use crate::rand::{Rng, SeedableRng};
use alloc::vec::Vec;
use core::default::Default;
/// A buffered wrapper for any [Rng] implementation.
/// It will keep unused bytes from the last call to [`Rng::rand`], and use them
/// for subsequent randomness if needed, rather than throwing them away.
///
/// ```rust
/// use nanorand::{Rng, BufferedRng, WyRand};
///
/// let mut thingy = [0u8; 5];
/// let mut rng = BufferedRng::new(WyRand::new());
/// rng.fill(&mut thingy);
/// // As WyRand generates 8 bytes of output, and our target is only 5 bytes,
/// // 3 bytes will remain in the buffer.
/// assert_eq!(rng.buffered(), 3);
/// ```
#[derive(Clone)]
pub struct BufferedRng<InternalGenerator: Rng<OUTPUT>, const OUTPUT: usize> {
rng: InternalGenerator,
buffer: Vec<u8>,
}
impl<InternalGenerator: Rng<OUTPUT>, const OUTPUT: usize> BufferedRng<InternalGenerator, OUTPUT> {
/// Wraps a [`Rng`] InternalGenerator in a [`BufferedRng`] instance.
pub fn new(rng: InternalGenerator) -> Self {
Self {
rng,
buffer: Vec::new(),
}
}
/// Returns the internal RNG, dropping the buffer.
pub fn into_inner(self) -> InternalGenerator {
self.rng
}
/// Returns how many unused bytes are currently buffered.
pub fn buffered(&self) -> usize {
self.buffer.len()
}
}
impl<InternalGenerator: Rng<OUTPUT>, const OUTPUT: usize> Rng<OUTPUT>
for BufferedRng<InternalGenerator, OUTPUT>
{
fn rand(&mut self) -> [u8; OUTPUT] {
let mut out = [0_u8; OUTPUT];
self.fill_bytes(&mut out);
out
}
fn fill_bytes<Bytes>(&mut self, mut output: Bytes)
where
Bytes: AsMut<[u8]>,
{
let output = output.as_mut();
let mut remaining = output.len();
while remaining > 0 {
if self.buffer.is_empty() {
self.buffer.extend_from_slice(&self.rng.rand());
}
let to_copy = core::cmp::min(remaining, self.buffer.len());
let output_len = output.len();
let start_idx = output_len - remaining;
output[start_idx..start_idx + to_copy].copy_from_slice(&self.buffer[..to_copy]);
self.buffer.drain(..to_copy);
remaining = remaining.saturating_sub(to_copy);
}
}
}
#[cfg(feature = "std")]
impl<InternalGenerator: Rng<OUTPUT>, const OUTPUT: usize> std::io::Read
for BufferedRng<InternalGenerator, OUTPUT>
{
fn read(&mut self, output: &mut [u8]) -> std::io::Result<usize> {
self.fill_bytes(&mut *output);
Ok(output.len())
}
fn read_to_end(&mut self, buf: &mut Vec<u8>) -> std::io::Result<usize> {
buf.extend_from_slice(&self.buffer);
Ok(self.buffer.drain(..).count())
}
fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result<usize> {
panic!("attempted to read an rng into a string")
}
}
impl<
InternalGenerator: SeedableRng<SEED_SIZE, OUTPUT>,
const OUTPUT: usize,
const SEED_SIZE: usize,
> SeedableRng<SEED_SIZE, OUTPUT> for BufferedRng<InternalGenerator, OUTPUT>
{
fn reseed(&mut self, seed: [u8; SEED_SIZE]) {
self.rng.reseed(seed);
}
}
impl<InternalGenerator: Rng<OUTPUT> + Default, const OUTPUT: usize> Default
for BufferedRng<InternalGenerator, OUTPUT>
{
fn default() -> Self {
Self::new(InternalGenerator::default())
}
}

View File

@@ -0,0 +1,3 @@
/// Implementation of the ChaCha cryptographic primitives.
/// More details can be seen at https://en.wikipedia.org/wiki/Salsa20
pub mod chacha;

View File

@@ -0,0 +1,148 @@
const CHACHA_TAU: &[u8] = b"expand 32-byte k";
fn chacha_quarter_round(state: &mut [u32; 16], a: usize, b: usize, c: usize, d: usize) {
state[a] = state[a].wrapping_add(state[b]);
state[d] ^= state[a];
state[d] = state[d].rotate_left(16);
state[c] = state[c].wrapping_add(state[d]);
state[b] ^= state[c];
state[b] = state[b].rotate_left(12);
state[a] = state[a].wrapping_add(state[b]);
state[d] ^= state[a];
state[d] = state[d].rotate_left(8);
state[c] = state[c].wrapping_add(state[d]);
state[b] ^= state[c];
state[b] = state[b].rotate_left(7);
}
const fn chacha_pack(unpacked: &[u8], idx: usize) -> u32 {
(unpacked[idx] as u32)
| ((unpacked[idx + 1] as u32) << 8)
| ((unpacked[idx + 2] as u32) << 16)
| ((unpacked[idx + 3] as u32) << 24)
}
/// Do one ChaCha round on the input data.
pub fn chacha_block<const ROUNDS: u8>(input: [u32; 16]) -> [u32; 16] {
let mut x = input;
assert_eq!(ROUNDS % 2, 0, "ChaCha rounds must be divisble by 2!");
for _ in (0..ROUNDS).step_by(2) {
// Odd rounds
chacha_quarter_round(&mut x, 0, 4, 8, 12);
chacha_quarter_round(&mut x, 1, 5, 9, 13);
chacha_quarter_round(&mut x, 2, 6, 10, 14);
chacha_quarter_round(&mut x, 3, 7, 11, 15);
// Even rounds
chacha_quarter_round(&mut x, 0, 5, 10, 15);
chacha_quarter_round(&mut x, 1, 6, 11, 12);
chacha_quarter_round(&mut x, 2, 7, 8, 13);
chacha_quarter_round(&mut x, 3, 4, 9, 14);
}
x.iter_mut()
.zip(input.iter())
.for_each(|(l, r)| *l = l.wrapping_add(*r));
x
}
/// Initialize the ChaCha internal state, with a 256-bit key and 64-bit nonce.
pub const fn chacha_init(key: [u8; 32], nonce: [u8; 8]) -> [u32; 16] {
let mut state = [0u32; 16];
state[0] = chacha_pack(CHACHA_TAU, 0);
state[1] = chacha_pack(CHACHA_TAU, 4);
state[2] = chacha_pack(CHACHA_TAU, 8);
state[3] = chacha_pack(CHACHA_TAU, 12);
state[4] = chacha_pack(&key, 0);
state[5] = chacha_pack(&key, 4);
state[6] = chacha_pack(&key, 8);
state[7] = chacha_pack(&key, 12);
state[8] = chacha_pack(&key, 16);
state[9] = chacha_pack(&key, 20);
state[10] = chacha_pack(&key, 24);
state[11] = chacha_pack(&key, 28);
// 64-bit counter
state[12] = 0;
state[13] = 0;
// Nonce
state[14] = chacha_pack(&nonce, 0);
state[15] = chacha_pack(&nonce, 4);
state
}
/// Increment the 64-bit counter of the internal ChaCha20 state by 1.
/// Returns `false` if it overflows, `true` otherwise.
pub fn chacha_increment_counter(state: &mut [u32; 16]) -> bool {
let counter = ((state[13] as u64) << 32) | (state[12] as u64);
match counter.checked_add(1) {
Some(new_counter) => {
state[12] = (new_counter & 0xFFFFFFFF) as u32;
state[13] = ((counter >> 32) & 0xFFFFFFFF) as u32;
true
}
None => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::convert::TryInto;
macro_rules! ietf_test_vector {
($key_hex: tt, $nonce_hex: tt, $keystream_hex: tt) => {
let key: [u8; 32] = hex::decode($key_hex).unwrap().try_into().unwrap();
let nonce: [u8; 8] = hex::decode($nonce_hex).unwrap().try_into().unwrap();
let expected_keystream: Vec<u8> = hex::decode($keystream_hex).unwrap();
let mut state = chacha_init(key, nonce);
let mut keystream: Vec<u8> = Vec::with_capacity(expected_keystream.len());
while expected_keystream.len() > keystream.len() {
chacha_block::<20>(state)
.iter()
.for_each(|packed| keystream.extend_from_slice(&packed.to_le_bytes()));
chacha_increment_counter(&mut state);
}
keystream.resize(expected_keystream.len(), 0);
assert_eq!(keystream, expected_keystream);
};
}
#[test]
fn test_ietf_chacha20_test_vectors() {
ietf_test_vector!(
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000000",
"76b8e0ada0f13d90405d6ae55386bd28bdd219b8a08ded1aa836efcc8b770dc7da41597c5157488d7724e03fb8d84a376a43b8f41518a11cc387b669b2ee6586"
);
ietf_test_vector!(
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000",
"4540f05a9f1fb296d7736e7b208e3c96eb4fe1834688d2604f450952ed432d41bbe2a0b6ea7566d2a5d1e7e20d42af2c53d792b1c43fea817e9ad275ae546963"
);
ietf_test_vector!(
"0000000000000000000000000000000000000000000000000000000000000000",
"0000000000000001",
"de9cba7bf3d69ef5e786dc63973f653a0b49e015adbff7134fcb7df137821031e85a050278a7084527214f73efc7fa5b5277062eb7a0433e445f41e3"
);
ietf_test_vector!(
"0000000000000000000000000000000000000000000000000000000000000000",
"0100000000000000",
"ef3fdfd6c61578fbf5cf35bd3dd33b8009631634d21e42ac33960bd138e50d32111e4caf237ee53ca8ad6426194a88545ddc497a0b466e7d6bbdb0041b2f586b"
);
ietf_test_vector!(
"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f",
"0001020304050607",
"f798a189f195e66982105ffb640bb7757f579da31602fc93ec01ac56f85ac3c134a4547b733b46413042c9440049176905d3be59ea1c53f15916155c2be8241a38008b9a26bc35941e2444177c8ade6689de95264986d95889fb60e84629c9bd9a5acb1cc118be563eb9b3a4a472f82e09a7e778492b562ef7130e88dfe031c79db9d4f7c7a899151b9a475032b63fc385245fe054e3dd5a97a5f576fe064025d3ce042c566ab2c507b138db853e3d6959660996546cc9c4a6eafdc777c040d70eaf46f76dad3979e5c5360c3317166a1c894c94a371876a94df7628fe4eaaf2ccb27d5aaae0ad7ad0f9d4b6ad3b54098746d4524d38407a6deb3ab78fab78c9"
);
}
}

View File

@@ -0,0 +1,129 @@
#[cfg(all(target_vendor = "apple", not(feature = "getrandom")))]
pub use darwin::entropy as system;
#[cfg(all(
any(target_os = "linux", target_os = "android"),
not(feature = "getrandom")
))]
pub use linux::entropy as system;
#[cfg(all(windows, not(target_vendor = "uwp"), not(feature = "getrandom")))]
pub use windows::entropy as system;
#[cfg(all(windows, target_vendor = "uwp", not(feature = "getrandom")))]
pub use windows_uwp::entropy as system;
#[cfg(all(
any(target_os = "linux", target_os = "android"),
not(feature = "getrandom")
))]
/// An entropy generator for Linux, using libc's `getrandom` function.
pub mod linux;
#[cfg(all(target_vendor = "apple", not(feature = "getrandom")))]
/// An entropy generator for macOS/iOS, using libc's `getrandom` function.
pub mod darwin;
#[cfg(all(windows, target_vendor = "uwp", not(feature = "getrandom")))]
/// An entropy generator for Windows, using WinAPI's `BCryptGenRandom` function.
pub mod windows_uwp;
#[cfg(all(windows, not(target_vendor = "uwp"), not(feature = "getrandom")))]
/// An entropy generator for Windows, using WinAPI's `RtlGenRandom` function.
pub mod windows;
#[cfg(feature = "getrandom")]
/// Pull in system entropy using the [`getrandom`](https://crates.io/crates/getrandom) crate.
/// Uses backup entropy (rdseed and system time) if it fails.
pub fn system(out: &mut [u8]) {
match getrandom::getrandom(out) {
Ok(_) => (),
Err(_) => backup(out),
}
}
/// Pull in backup entropy (rdseed and system time).
#[cfg(not(any(
feature = "getrandom",
target_os = "linux",
target_os = "android",
target_vendor = "apple",
windows
)))]
pub fn system(out: &mut [u8]) {
backup_entropy(out);
}
#[cfg(feature = "rdseed")]
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
fn stupid_rdseed_hack() -> Option<u64> {
#[cfg(target_arch = "x86")]
use core::arch::x86::_rdseed64_step as rdseed;
#[cfg(target_arch = "x86_64")]
use core::arch::x86_64::_rdseed64_step as rdseed;
let mut x = 0;
for _ in 0..10 {
if 0 != unsafe { rdseed(&mut x) } {
return Some(x);
}
}
None
}
#[cfg(all(feature = "rdseed", any(target_arch = "x86", target_arch = "x86_64")))]
/// An rdseed-based entropy source.
/// Only works on x86/x86_64 platforms where the `rdseed` instructions are available.
/// Returns [`None`] if `rdseed` is not available.
/// Returns [`Some`] if it successfully managed to pull some bytes.
/// ***VERY unreliable.***
pub fn rdseed(out: &mut [u8]) -> Option<usize> {
if !std::is_x86_feature_detected!("rdseed") {
return None;
}
let amt = out.len();
let mut bytes_pulled: usize = 0;
let rdseed_amt = ((amt + core::mem::size_of::<u64>() - 1) / core::mem::size_of::<u64>()).max(0);
for n in 0..rdseed_amt {
let seed = match stupid_rdseed_hack() {
Some(s) => s,
None => return Some(bytes_pulled),
};
let x = seed.to_ne_bytes();
bytes_pulled += x.len();
x.iter()
.enumerate()
.for_each(|(i, val)| out[(core::mem::size_of::<u64>() * n) + i] = *val);
}
Some(bytes_pulled)
}
/// A wrapper function for non-x86(64) platforms that do not have rdseed.
#[cfg(any(
not(feature = "rdseed"),
not(any(target_arch = "x86", target_arch = "x86_64"))
))]
pub fn rdseed(_out: &mut [u8]) -> Option<usize> {
None
}
#[cfg(feature = "std")]
/// A backup entropy source, trying rdseed first,
/// and if it fails or does not complete, combining it with or
/// using system time-based entropy generation.
///
/// # Panics
///
/// This function panics if sufficient entropy could not be obtained.
pub fn backup(out: &mut [u8]) {
if let Some(amt) = rdseed(out) {
if amt >= out.len() {
return;
}
};
panic!("Failed to source sufficient entropy!")
}
#[cfg(not(feature = "std"))]
/// This just panics.
pub fn backup_entropy(_: &mut [u8]) {
panic!("Failed to source any entropy!")
}

View File

@@ -0,0 +1,11 @@
use core::ffi::c_void;
#[link(name = "Security", kind = "framework")]
extern "C" {
fn SecRandomCopyBytes(rnd: *const c_void, count: usize, bytes: *mut u8) -> u32;
}
/// Obtain a series of random bytes.
pub fn entropy(out: &mut [u8]) -> bool {
unsafe { SecRandomCopyBytes(core::ptr::null(), out.len(), out.as_mut_ptr()) == 0 }
}

View File

@@ -0,0 +1,8 @@
extern "C" {
fn getrandom(buf: *mut u8, buflen: usize, flags: u32) -> isize;
}
/// Obtain a series of random bytes.
pub fn entropy(out: &mut [u8]) -> bool {
unsafe { getrandom(out.as_mut_ptr(), out.len(), 0x0001) >= 1 }
}

View File

@@ -0,0 +1,9 @@
extern "system" {
#[link_name = "SystemFunction036"]
fn RtlGenRandom(pBuffer: *mut u8, cbBuffer: usize) -> u32;
}
/// Obtain a random 64-bit number using WinAPI's `RtlGenRandom` function.
pub fn entropy(out: &mut [u8]) -> bool {
unsafe { RtlGenRandom(out.as_mut_ptr(), out.len()) == 0 }
}

View File

@@ -0,0 +1,26 @@
use core::{ffi::c_void, ptr};
use super::backup_entropy;
const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
extern "system" {
fn BCryptGenRandom(
hAlgorithm: *mut c_void,
pBuffer: *mut u8,
cbBuffer: usize,
dwFlags: u32,
) -> u32;
}
/// Obtain a random 64-bit number using WinAPI's `BCryptGenRandom` function.
pub fn entropy(out: &mut [u8]) -> bool {
unsafe {
BCryptGenRandom(
ptr::null_mut(),
out.as_mut_ptr(),
out.len(),
BCRYPT_USE_SYSTEM_PREFERRED_RNG,
) == 0
}
}

View File

@@ -0,0 +1,188 @@
use crate::Rng;
use core::ops::{Bound, RangeBounds};
macro_rules! gen {
($($type:ty),+) => {
$(
impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomGen<Generator, OUTPUT> for $type {
fn random(rng: &mut Generator) -> Self {
let mut bytes = [0u8; core::mem::size_of::<$type>()];
rng.fill_bytes(&mut bytes);
Self::from_ne_bytes(bytes)
}
}
)+
};
}
macro_rules! range {
($(($type:ty, $bigger:ty, $signed:ty)),+) => {
$(
impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomRange<Generator, OUTPUT> for $type {
fn random_range<Bounds: RangeBounds<Self>>(rng: &mut Generator, bounds: Bounds) -> Self {
const BITS: $bigger = core::mem::size_of::<$type>() as $bigger * 8;
let lower = match bounds.start_bound() {
Bound::Included(lower) => *lower,
Bound::Excluded(lower) => lower.saturating_add(1),
Bound::Unbounded => <$type>::MIN,
};
let upper = match bounds.end_bound() {
Bound::Included(upper) => upper.saturating_add(1),
Bound::Excluded(upper) => *upper,
Bound::Unbounded => <$type>::MAX,
};
assert!(upper >= lower, "{} >= {} (lower bound was bigger than upper bound)", upper, lower);
let upper = upper.saturating_sub(lower);
let mut value = Self::random(rng);
let mut m = (upper as $bigger).wrapping_mul(value as $bigger);
if (m as $type) < upper {
let t = (!upper + 1) % upper;
while (m as $type) < t {
value = Self::random(rng);
m = (upper as $bigger).wrapping_mul(value as $bigger);
}
}
(m >> BITS) as $type + lower
}
}
impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomRange<Generator, OUTPUT> for $signed {
fn random_range<Bounds: RangeBounds<Self>>(r: &mut Generator, bounds: Bounds) -> Self {
let lower = match bounds.start_bound() {
Bound::Included(lower) => *lower,
Bound::Excluded(lower) => lower.saturating_add(1),
Bound::Unbounded => <$signed>::MIN
};
let upper = match bounds.end_bound() {
Bound::Included(upper) => *upper,
Bound::Excluded(upper) => upper.saturating_sub(1),
Bound::Unbounded => <$signed>::MAX,
};
assert!(upper >= lower, "{} >= {} (lower bound was bigger than upper bound)", upper, lower);
let lower = lower.wrapping_sub(<$signed>::MIN) as $type;
let upper = upper.wrapping_sub(<$signed>::MIN) as $type;
<$type>::random_range(r, lower..=upper).wrapping_add(<$signed>::MAX as $type) as $signed
}
}
)+
}
}
/// A trait used for generating a random object with an RNG,
pub trait RandomGen<Generator: Rng<OUTPUT>, const OUTPUT: usize> {
/// Return a random instance of the implementing type, from the specified RNG instance.
fn random(rng: &mut Generator) -> Self;
}
/// A trait used for generating a random number within a range, with an RNG,
pub trait RandomRange<Generator: Rng<OUTPUT>, const OUTPUT: usize>:
RandomGen<Generator, OUTPUT>
{
/// Return a ranged number of the implementing type, from the specified RNG instance.
///
/// # Panics
/// This function will panic if the lower bound of the range is greater than the upper bound.
fn random_range<Bounds: RangeBounds<Self>>(nng: &mut Generator, range: Bounds) -> Self;
}
impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomGen<Generator, OUTPUT> for bool {
fn random(rng: &mut Generator) -> Self {
u8::random(rng) < 0b10000000
}
}
impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomGen<Generator, OUTPUT> for f32 {
fn random(rng: &mut Generator) -> Self {
(u32::random(rng) as f32) / (u32::MAX as f32)
}
}
impl<Generator: Rng<OUTPUT>, const OUTPUT: usize> RandomGen<Generator, OUTPUT> for f64 {
fn random(rng: &mut Generator) -> Self {
(u64::random(rng) as f64) / (u64::MAX as f64)
}
}
gen!(i8, u8, i16, u16, i32, u32, i64, u64, i128, u128, isize, usize);
range!(
(u8, u16, i8),
(u16, u32, i16),
(u32, u64, i32),
(u64, u128, i64)
);
#[cfg(target_pointer_width = "16")]
range!((usize, u32, isize));
#[cfg(target_pointer_width = "32")]
range!((usize, u64, isize));
#[cfg(target_pointer_width = "64")]
range!((usize, u128, isize));
#[cfg(test)]
mod tests {
use crate::{Rng, WyRand};
#[test]
fn ensure_unsigned_in_range() {
let mut rng = WyRand::new();
for _ in 0..1000 {
let number = rng.generate_range(10_u64..=20);
assert!(
(10..=20).contains(&number),
"{} was outside of 10..=20",
number
);
let number = rng.generate_range(10_u64..30);
assert!(
(10..30).contains(&number),
"{} was outside of 10..30",
number
);
let number = rng.generate_range(512_u64..);
assert!((512..).contains(&number), "{} was outside of 512..", number);
let number = rng.generate_range(..1024_u64);
assert!(
(..1024).contains(&number),
"{} was outside of ..1024",
number
);
}
}
#[test]
fn ensure_signed_in_range() {
let mut rng = WyRand::new();
for _ in 0..1000 {
let number = rng.generate_range(-50..);
assert!((-50..).contains(&number), "{} was outside of -50..", number);
let number = rng.generate_range(..512);
assert!((..512).contains(&number), "{} was outside of ..512", number);
let number = rng.generate_range(..-32);
assert!((..-32).contains(&number), "{} was outside of ..-32", number);
}
}
#[test]
fn ensure_floats_generate_properly() {
let mut rng = WyRand::new();
for _ in 0..1000 {
let number = rng.generate::<f32>();
assert!(1.0 >= number, "{} was bigger than 1.0", number);
assert!(number >= 0.0, "0 was bigger than {}", number);
let number = rng.generate::<f64>();
assert!(1.0 >= number, "{} was bigger than 1.0", number);
assert!(number >= 0.0, "0 was bigger than {}", number);
}
}
#[test]
#[should_panic]
fn ensure_invalid_range_panics() {
let mut rng = WyRand::new();
#[allow(clippy::reversed_empty_ranges)]
rng.generate_range(10..=5);
}
}

View File

@@ -0,0 +1,122 @@
#![cfg_attr(not(feature = "std"), no_std)]
#![forbid(missing_docs)]
#![warn(
clippy::perf,
clippy::complexity,
clippy::style,
clippy::correctness,
clippy::missing_const_for_fn
)]
//! A library meant for fast, random number generation with quick compile time, and minimal dependencies.
//!
//! # Examples
//! ## Generating a number with an initialized RNG
//! ```rust
//! use nanorand::{Rng, WyRand};
//!
//! let mut rng = WyRand::new();
//! println!("Random number: {}", rng.generate::<u64>());
//! ```
//! ## Generating a number with a thread-local RNG
//! ```rust
//! use nanorand::Rng;
//!
//! let mut rng = nanorand::tls_rng();
//! println!("Random number: {}", rng.generate::<u64>());
//! ```
//! ## Generating a number in a range
//! ```rust
//! use nanorand::{Rng, WyRand};
//!
//! let mut rng = WyRand::new();
//! println!("Random number between 1 and 100: {}", rng.generate_range(1_u64..=100));
//! println!("Random number between -100 and 50: {}", rng.generate_range(-100_i64..=50));
//! ```
//! ### Buffering random bytes
//! ```rust
//! use nanorand::{Rng, BufferedRng, WyRand};
//!
//! let mut thingy = [0u8; 5];
//! let mut rng = BufferedRng::new(WyRand::new());
//! rng.fill(&mut thingy);
//! // As WyRand generates 8 bytes of output, and our target is only 5 bytes,
//! // 3 bytes will remain in the buffer.
//! assert_eq!(rng.buffered(), 3);
//! ```
//! ## Shuffling a Vec
//! ```rust
//! use nanorand::{Rng, WyRand};
//!
//! let mut rng = WyRand::new();
//! let mut items = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
//! rng.shuffle(&mut items);
//! ```
//!
//! ## Why should I use this over...
//!
//! * `rand` - The standard rand crate is a complex beast. It contains unsafe code in the core implementations, and while it has much more options than we do, that's kind of the point. We're straight to the point, while rand is everything and the kitchen sink.
//! * `fastrand`, `oorandom`, `random-fast-rng`, or `randomize` - These are all minimal, zero-dep implementations of the PCG family of RNGs (Pcg32 and Pcg64). While these are decent, they are _much_ slower than wyrand (which beats the speed of these Pcg32 implementations while providing 64 random bits), and do not provide CSPRNGs.
//! * `getrandom` - The getrandom crate just provides OS entropy sources. It is not meant for random number generation. In fact, we provide it as an optional entropy source.
//!
//! ## RNG Implementations
//!
//! **RNG**|**nanorand type**|**Output Size**|**Cryptographically Secure**|**Speed**<sup>1</sup>|**Notes**|**Original Implementation**
//! :-----:|:-----:|:-----:|:-----:|:-----:|:-----:|:-----:
//! wyrand|[`nanorand::WyRand`](rand/wyrand/struct.WyRand.html), [`nanorand::tls::TlsWyRand`](tls/fn.tls_rng.html)|64 bits (`u64`)|🚫|16.4 GB/s||[https://github.com/lemire/testingRNG/blob/master/source/wyrand.h](https://github.com/lemire/testingRNG/blob/master/source/wyrand.h)
//! Pcg64|[`nanorand::Pcg64`](rand/pcg64/struct.Pcg64.html)|64 bits (`u64`)|🚫|1.6 GB/s||[https://github.com/rkern/pcg64](https://github.com/rkern/pcg64)
//! ChaCha|[`nanorand::ChaCha`](rand/chacha/struct.ChaCha.html)|512 bits (`[u32; 16]`)|✅|204 MB/s (ChaCha8), 79 MB/s (ChaCha20)|Only works in Rust 1.47 or above|[https://cr.yp.to/chacha.html](https://cr.yp.to/chacha.html)
//!
//! <sup>1. Speed benchmarked on an M1 Macbook Air</sup>
//!
//! ## Entropy Sources
//! _Listed in order of priority_
//!
//! * If the `getrandom` feature is enabled, then [`getrandom::getrandom`](https://docs.rs/getrandom/*/getrandom/fn.getrandom.html) will be called, and no other entropy sources will be used.
//! * If the `rdseed` feature is enabled, and is running on an x86(-64) system with the [RDSEED](https://en.wikipedia.org/wiki/RDRAND) instruction, then
//! we will attempt to source as much entropy as possible via our [`rdseed_entropy`](entropy::rdseed_entropy) function
//! * Linux and Android will attempt to use the [`getrandom`](https://man7.org/linux/man-pages/man2/getrandom.2.html) syscall.
//! * macOS and iOS (Darwin-based systems) will use Security.framework's [`SecRandomCopyBytes`](https://developer.apple.com/documentation/security/1399291-secrandomcopybytes).
//! * Windows
//! * If we're targeting UWP, then the [`BCryptGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom) is used with system-preferred RNG (`BCRYPT_USE_SYSTEM_PREFERRED_RNG`).
//! * Otherwise, we'll use [`RtlGenRandom`](https://docs.microsoft.com/en-us/windows/win32/api/ntsecapi/nf-ntsecapi-rtlgenrandom).
//!
//! ## Feature Flags
//!
//! * `alloc` (default) - Enables Rust `alloc` lib features, such as a buffering Rng wrapper.
//! * `std` (default) - Enables Rust `std` lib features, such as seeding from OS entropy sources. Requires `alloc` to be enabled.
//! * `tls` (default) - Enables a thread-local [`WyRand`](rand/wyrand/struct.WyRand.html) RNG (see below). Requires `std` to be enabled.
//! * `wyrand` (default) - Enable the [`WyRand`](rand/wyrand/struct.WyRand.html) RNG.
//! * `pcg64` (default) - Enable the [`Pcg64`](rand/pcg64/struct.Pcg64.html) RNG.
//! * `chacha` - Enable the [`ChaCha`](rand/chacha/struct.ChaCha.html) RNG. Requires Rust 1.47 or later.
//! * `rdseed` - On x86 and x86-64 platforms, the `rdseed` intrinsic will be used when OS entropy isn't available.
//! * `zeroize` - Implement the [Zeroize](https://crates.io/crates/zeroize) trait for all RNGs.
//! * `getrandom` - Use the [`getrandom`](https://crates.io/crates/getrandom) crate as an entropy source. Works on most systems, optional due to the fact that it brings in more dependencies.
//!
//! ## MSRV
//! The minimum supported Rust version for the latest version of nanorand is **Rust 1.56.0**, released October 21st, 2021.
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
pub use buffer::BufferedRng;
pub use gen::*;
pub use rand::*;
#[cfg(feature = "tls")]
pub use tls::tls_rng;
#[cfg(feature = "alloc")]
/// Provides a buffered wrapper for RNGs, preventing bits from being wasted.
pub mod buffer;
/// Implementation of cryptography, for CSPRNGs.
pub mod crypto;
/// Sources for obtaining entropy.
pub mod entropy;
/// Traits for generating types from an RNG.
pub mod gen;
/// RNG algorithms.
pub mod rand;
#[cfg(feature = "tls")]
/// Provides a thread-local [`WyRand`] RNG.
pub mod tls;

View File

@@ -0,0 +1,87 @@
#[cfg(feature = "chacha")]
pub use chacha::{ChaCha, ChaCha12, ChaCha20, ChaCha8};
#[cfg(feature = "pcg64")]
pub use pcg64::Pcg64;
#[cfg(feature = "wyrand")]
pub use wyrand::WyRand;
use crate::gen::{RandomGen, RandomRange};
use core::ops::RangeBounds;
/// Implementation of the wyrand PRNG algorithm.
/// More details can be seen at <https://github.com/wangyi-fudan/wyhash>
#[cfg(feature = "wyrand")]
pub mod wyrand;
/// Implementation of the Pcg64 PRNG algorithm.
/// More details can be seen at <https://www.pcg-random.org/index.html>
#[cfg(feature = "pcg64")]
pub mod pcg64;
/// Implementation of the ChaCha CSPRNG algorithm.
/// More details can be seen at <https://en.wikipedia.org/wiki/Salsa20>
#[cfg(feature = "chacha")]
pub mod chacha;
/// A trait that represents a random number generator.
pub trait Rng<const OUTPUT: usize>: Clone {
/// Generates a random sequence of bytes, seeding from the internal state.
fn rand(&mut self) -> [u8; OUTPUT];
/// Generates a random of the specified type, seeding from the internal state.
fn generate<Generated>(&mut self) -> Generated
where
Generated: RandomGen<Self, OUTPUT>,
{
Generated::random(self)
}
/// Fill an array of bytes with randomness.
fn fill_bytes<Bytes>(&mut self, mut buffer: Bytes)
where
Bytes: AsMut<[u8]>,
{
let mut buffer = buffer.as_mut();
let mut length = buffer.len();
while length > 0 {
let chunk = self.rand();
let generated = chunk.len().min(length);
buffer[..generated].copy_from_slice(&chunk[..generated]);
buffer = &mut buffer[generated..];
length -= generated;
}
}
/// Fill an array with the specified type.
fn fill<Contents, Array>(&mut self, mut target: Array)
where
Contents: RandomGen<Self, OUTPUT>,
Array: AsMut<[Contents]>,
{
let target = target.as_mut();
target.iter_mut().for_each(|entry| *entry = self.generate());
}
/// Generates a random of the specified type, seeding from the internal state.
fn generate_range<Number, Bounds>(&mut self, range: Bounds) -> Number
where
Number: RandomRange<Self, OUTPUT>,
Bounds: RangeBounds<Number>,
{
Number::random_range(self, range)
}
/// Shuffle a slice, using the RNG.
fn shuffle<Contents, Array>(&mut self, mut target: Array)
where
Array: AsMut<[Contents]>,
{
let target = target.as_mut();
let target_len = target.len();
for idx in 0..target_len {
let random_idx = self.generate_range(0..target_len);
target.swap(idx, random_idx);
}
}
}
/// A trait that represents an RNG that can be reseeded from arbitrary bytes.
pub trait SeedableRng<const SEED_SIZE: usize, const OUTPUT: usize>: Rng<OUTPUT> {
/// Re-seed the RNG with the specified bytes.
fn reseed(&mut self, seed: [u8; SEED_SIZE]);
}

View File

@@ -0,0 +1,111 @@
use crate::{
crypto::chacha,
rand::{Rng, SeedableRng},
};
use core::fmt::{self, Debug, Display, Formatter};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// The ChaCha CSPRNG, with 8 rounds.
pub type ChaCha8 = ChaCha<8>;
/// The ChaCha CSPRNG, with 12 rounds.
pub type ChaCha12 = ChaCha<12>;
/// The ChaCha CSPRNG, with 20 rounds.
pub type ChaCha20 = ChaCha<20>;
/// An instance of the ChaCha random number generator.
/// Seeded from the system entropy generator when available.
/// **This generator _is theoretically_ cryptographically secure.**
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
#[cfg_attr(feature = "zeroize", zeroize(drop))]
pub struct ChaCha<const ROUNDS: u8> {
state: [u32; 16],
}
impl<const ROUNDS: u8> ChaCha<ROUNDS> {
/// Create a new [`ChaCha`] instance, seeding from the system's default source of entropy.
#[must_use]
pub fn new() -> Self {
let mut key: [u8; 32] = Default::default();
crate::entropy::system(&mut key);
let mut nonce: [u8; 8] = Default::default();
crate::entropy::system(&mut nonce);
let state = chacha::chacha_init(key, nonce);
Self { state }
}
/// Create a new [`ChaCha`] instance, using the provided key and nonce.
#[must_use]
pub const fn new_key(key: [u8; 32], nonce: [u8; 8]) -> Self {
let state = chacha::chacha_init(key, nonce);
Self { state }
}
}
impl<const ROUNDS: u8> Default for ChaCha<ROUNDS> {
fn default() -> Self {
let mut key: [u8; 32] = Default::default();
crate::entropy::system(&mut key);
let mut nonce: [u8; 8] = Default::default();
crate::entropy::system(&mut nonce);
let state = chacha::chacha_init(key, nonce);
Self { state }
}
}
impl<const ROUNDS: u8> Rng<64> for ChaCha<ROUNDS> {
fn rand(&mut self) -> [u8; 64] {
let block = chacha::chacha_block::<ROUNDS>(self.state);
let mut ret = [0_u8; 64];
block.iter().enumerate().for_each(|(idx, num)| {
let x = num.to_ne_bytes();
let n = idx * 4;
ret[n] = x[0];
ret[n + 1] = x[1];
ret[n + 2] = x[2];
ret[n + 3] = x[3];
});
// Now, we're going to just increment our counter so we get an entirely new output next time.
// If the counter overflows, we just reseed entirely instead.
if !chacha::chacha_increment_counter(&mut self.state) {
let mut new_seed: [u8; 40] = [42_u8; 40];
crate::entropy::system(&mut new_seed);
self.reseed(new_seed);
}
ret
}
}
impl<const ROUNDS: u8> Clone for ChaCha<ROUNDS> {
fn clone(&self) -> Self {
Self { state: self.state }
}
}
impl<const ROUNDS: u8> Display for ChaCha<ROUNDS> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "ChaCha ({:p}, {} rounds)", self, ROUNDS)
}
}
impl<const ROUNDS: u8> SeedableRng<40, 64> for ChaCha<ROUNDS> {
fn reseed(&mut self, seed: [u8; 40]) {
let mut key = [0_u8; 32];
let mut nonce = [0_u8; 8];
key.copy_from_slice(&seed[..32]);
nonce.copy_from_slice(&seed[32..]);
self.state = chacha::chacha_init(key, nonce);
}
}
impl<const ROUNDS: u8> Debug for ChaCha<ROUNDS> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let counter = ((self.state[13] as u64) << 32) | (self.state[12] as u64);
f.debug_struct("ChaCha20")
.field("rounds", &ROUNDS)
.field("counter", &counter)
.finish()
}
}

View File

@@ -0,0 +1,115 @@
// Based off Robert Kern's C implementation at https://github.com/rkern/pcg64/blob/master/pcg64.c
use crate::rand::{Rng, SeedableRng};
use core::fmt::{self, Debug, Display, Formatter};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
const PCG_DEFAULT_MULTIPLIER_128: u128 = 47026247687942121848144207491837523525;
/// An instance of the Pcg64 random number generator.
/// Seeded from the system entropy generator when available.
/// **This generator is _NOT_ cryptographically secure.**
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
#[cfg_attr(feature = "zeroize", zeroize(drop))]
pub struct Pcg64 {
seed: u128,
state: u128,
inc: u128,
}
impl Pcg64 {
/// Create a new [`Pcg64`] instance, seeding from the system's default source of entropy.
#[cfg(feature = "std")]
#[must_use]
pub fn new() -> Self {
let mut entropy: [u8; core::mem::size_of::<u128>()] = Default::default();
crate::entropy::system(&mut entropy);
Self {
seed: u128::from_ne_bytes(entropy),
inc: 0,
state: 0,
}
}
/// Create a new [`Pcg64`] instance, using a provided seed.
#[must_use]
pub const fn new_seed(seed: u128) -> Self {
Self {
seed,
inc: 0,
state: 0,
}
}
fn step(&mut self) {
self.state = self
.state
.wrapping_mul(PCG_DEFAULT_MULTIPLIER_128)
.wrapping_add(self.inc);
}
fn rand128(&mut self) -> u64 {
self.state = 0;
self.inc = self.seed.wrapping_shl(1) | 1;
self.step();
self.state = self.state.wrapping_add(self.seed);
self.step();
self.step();
self.state.wrapping_shr(64) as u64 ^ self.state as u64
}
}
#[cfg(feature = "std")]
impl Default for Pcg64 {
/// Create a new [`Pcg64`] instance, seeding from the system's default source of entropy.
fn default() -> Self {
let mut entropy: [u8; core::mem::size_of::<u128>()] = Default::default();
crate::entropy::system(&mut entropy);
Self {
seed: u128::from_ne_bytes(entropy),
inc: 0,
state: 0,
}
}
}
impl Rng<8> for Pcg64 {
fn rand(&mut self) -> [u8; 8] {
let ret = self.rand128();
self.seed = self.state ^ (ret as u128).wrapping_shr(64);
ret.to_ne_bytes()
}
}
impl SeedableRng<16, 8> for Pcg64 {
fn reseed(&mut self, seed: [u8; 16]) {
self.seed = u128::from_ne_bytes(seed);
}
}
impl Clone for Pcg64 {
fn clone(&self) -> Self {
Self {
seed: self.seed,
inc: self.inc,
state: self.state,
}
}
}
impl Display for Pcg64 {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "Pcg64 ({:p})", self)
}
}
impl Debug for Pcg64 {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("Pcg64")
.field("seed", &format_args!("0x{:x}", self.seed))
.field("state", &format_args!("0x{:x}", self.state))
.field("inc", &format_args!("0x{:x}", self.inc))
.finish()
}
}

View File

@@ -0,0 +1,75 @@
// Based off lemire's wyrand C++ code at https://github.com/lemire/testingRNG/blob/master/source/wyrand.h
use crate::rand::{Rng, SeedableRng};
use core::fmt::{self, Debug, Display, Formatter};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// An instance of the WyRand random number generator.
/// Seeded from the system entropy generator when available.
/// **This generator is _NOT_ cryptographically secure.**
#[cfg_attr(feature = "zeroize", derive(Zeroize))]
#[cfg_attr(feature = "zeroize", zeroize(drop))]
pub struct WyRand {
seed: u64,
}
impl WyRand {
/// Create a new [`WyRand`] instance, seeding from the system's default source of entropy.
#[must_use]
pub fn new() -> Self {
Self::default()
}
/// Create a new [`WyRand`] instance, using a provided seed.
#[must_use]
pub const fn new_seed(seed: u64) -> Self {
Self { seed }
}
}
impl Default for WyRand {
/// Create a new [`WyRand`] instance, seeding from the system's default source of entropy.
fn default() -> Self {
let mut entropy: [u8; core::mem::size_of::<u64>()] = Default::default();
crate::entropy::system(&mut entropy);
Self {
seed: u64::from_ne_bytes(entropy),
}
}
}
impl Rng<8> for WyRand {
fn rand(&mut self) -> [u8; 8] {
self.seed = self.seed.wrapping_add(0xa0761d6478bd642f);
let t: u128 = (self.seed as u128).wrapping_mul((self.seed ^ 0xe7037ed1a0b428db) as u128);
let ret = (t.wrapping_shr(64) ^ t) as u64;
ret.to_ne_bytes()
}
}
impl Clone for WyRand {
fn clone(&self) -> Self {
Self { seed: self.seed }
}
}
impl Display for WyRand {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "WyRand ({:p})", self)
}
}
impl Debug for WyRand {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
f.debug_struct("WyRand")
.field("seed", &format_args!("0x{:x}", self.seed))
.finish()
}
}
impl SeedableRng<8, 8> for WyRand {
fn reseed(&mut self, seed: [u8; 8]) {
self.seed = u64::from_ne_bytes(seed);
}
}

View File

@@ -0,0 +1,42 @@
use crate::rand::{wyrand::WyRand, Rng, SeedableRng};
use std::{cell::RefCell, rc::Rc};
thread_local! {
static WYRAND: Rc<RefCell<WyRand>> = Rc::new(RefCell::new(WyRand::new()));
}
#[derive(Clone)]
#[doc(hidden)]
pub struct TlsWyRand(Rc<RefCell<WyRand>>);
impl Rng<8> for TlsWyRand {
fn rand(&mut self) -> [u8; 8] {
self.0.borrow_mut().rand()
}
}
impl SeedableRng<8, 8> for TlsWyRand {
fn reseed(&mut self, seed: [u8; 8]) {
self.0.borrow_mut().reseed(seed);
}
}
/// Fetch a thread-local [`WyRand`]
/// ```rust
/// use nanorand::Rng;
///
/// let mut rng = nanorand::tls_rng();
/// println!("Random number: {}", rng.generate::<u64>());
/// ```
/// This cannot be passed to another thread, as something like this will fail to compile:
/// ```compile_fail
/// use nanorand::Rng;
///
/// let mut rng = nanorand::tls_rng();
/// std::thread::spawn(move || {
/// println!("Random number: {}", rng.generate::<u64>());
/// });
/// ```
pub fn tls_rng() -> TlsWyRand {
WYRAND.with(|tls| TlsWyRand(tls.clone()))
}