pax_global_header00006660000000000000000000000064142117737650014527gustar00rootroot0000000000000052 comment=f7c67967cb78dde9629f78e4d8ece384089c2446 nanorand-rs-0.7.0/000077500000000000000000000000001421177376500137555ustar00rootroot00000000000000nanorand-rs-0.7.0/.cargo/000077500000000000000000000000001421177376500151265ustar00rootroot00000000000000nanorand-rs-0.7.0/.cargo/config000066400000000000000000000001551421177376500163170ustar00rootroot00000000000000[target.x86_64-apple-darwin] rustflags = [ "-C", "link-arg=-undefined", "-C", "link-arg=dynamic_lookup", ] nanorand-rs-0.7.0/.editorconfig000066400000000000000000000002211421177376500164250ustar00rootroot00000000000000[*] indent_style = tab indent_size = 4 charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true [*.yml] indent_style = space nanorand-rs-0.7.0/.gitattributes000066400000000000000000000002251421177376500166470ustar00rootroot00000000000000* text=auto eol=lf # Guaranteed to be text *.rs text=auto eol=lf *.txt text=auto eol=lf *.md text=auto eol=lf *.toml text=auto eol=lfnanorand-rs-0.7.0/.github/000077500000000000000000000000001421177376500153155ustar00rootroot00000000000000nanorand-rs-0.7.0/.github/workflows/000077500000000000000000000000001421177376500173525ustar00rootroot00000000000000nanorand-rs-0.7.0/.github/workflows/lint.yml000066400000000000000000000015721421177376500210500ustar00rootroot00000000000000name: "Linting" on: push: paths: - '**/*.rs' - '.github/workflows/*.yml' pull_request: paths: - '**/*.rs' - '.github/workflows/*.yml' jobs: lint: name: "Linting" runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: "Install stable Rust toolchain" uses: actions-rs/toolchain@v1 with: override: true profile: minimal toolchain: "stable" components: rustfmt, clippy - name: Run rustfmt uses: actions-rs/cargo@v1 with: args: "--all -- --check" command: fmt - name: Run 'cargo check' uses: actions-rs/cargo@v1 with: args: "--workspace" command: check - name: Run clippy uses: actions-rs/cargo@v1 with: args: "--workspace" command: clippy nanorand-rs-0.7.0/.github/workflows/smallcrush.yml000066400000000000000000000024561421177376500222610ustar00rootroot00000000000000name: "Run SmallCrush" on: push: paths: - '**/*.rs' - '**/*.h' - '**/*.c' - '.github/workflows/*.yml' pull_request: paths: - '**/*.rs' - '**/*.h' - '**/*.c' - '.github/workflows/*.yml' jobs: smallcrush: name: "Run SmallCrush" runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install TestU01 and Clang run: sudo apt-get update && sudo apt-get install -y libtestu01-0-dev libtestu01-0-dev-common libtestu01-0 llvm-10 clang-10 - name: "Install stable Rust toolchain" uses: actions-rs/toolchain@v1 with: override: true profile: minimal toolchain: "stable" - name: Build nanorand-abi uses: actions-rs/cargo@v1 with: args: "--release --package nanorand-abi" command: build - name: Build nanorand SmallCrush example run: clang -O3 -pipe -I$GITHUB_WORKSPACE/nanorand-abi nanorand-abi/examples/smallcrush.c target/release/libnanorand_abi.so -o nanorand-smallcrush -ltestu01 -ltestu01probdist -ltestu01mylib -lm - name: Run nanorand SmallCrush run: ./nanorand-smallcrush nanorand-rs-0.7.0/.github/workflows/tests.yml000066400000000000000000000072071421177376500212450ustar00rootroot00000000000000name: "Run Tests" on: push: paths: - '**/*.rs' - '.github/workflows/*.yml' pull_request: paths: - '**/*.rs' - '.github/workflows/*.yml' jobs: version-tests: name: Test on Rust ${{ matrix.toolchain }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install stable Rust toolchain uses: actions-rs/toolchain@v1 with: override: true profile: minimal toolchain: ${{ matrix.toolchain }} - name: Run 'cargo check' uses: actions-rs/cargo@v1 with: command: check args: --all-features - name: Run 'cargo test' uses: actions-rs/cargo@v1 with: command: test args: --all-features strategy: matrix: toolchain: - 1.56.0 - stable - beta - nightly linux-tests: name: Test on ${{ matrix.target }} runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Install stable Rust toolchain uses: actions-rs/toolchain@v1 with: override: true profile: minimal toolchain: stable - name: Run 'cargo check' uses: actions-rs/cargo@v1 with: command: check args: --all-features - name: Run 'cargo check --no-default-features' uses: actions-rs/cargo@v1 with: command: check args: --no-default-features - name: Run 'cross test' uses: actions-rs/cargo@v1 with: use-cross: true command: test args: --all-features --target ${{ matrix.target }} strategy: matrix: target: - x86_64-unknown-linux-gnu - aarch64-unknown-linux-gnu - arm-unknown-linux-gnueabihf - armv5te-unknown-linux-gnueabi - armv7-unknown-linux-gnueabihf - mips64-unknown-linux-gnuabi64 - mips64el-unknown-linux-gnuabi64 - powerpc-unknown-linux-gnu - powerpc64le-unknown-linux-gnu - riscv64gc-unknown-linux-gnu windows-tests: name: Test on Windows runs-on: windows-latest steps: - uses: actions/checkout@v2 - name: Install stable Rust toolchain uses: actions-rs/toolchain@v1 with: override: true profile: minimal toolchain: stable - name: Run 'cargo check' uses: actions-rs/cargo@v1 with: command: check args: --all-features - name: Run 'cargo check --no-default-features' uses: actions-rs/cargo@v1 with: command: check args: --no-default-features - name: Run 'cargo test' uses: actions-rs/cargo@v1 with: command: test args: --all-features macos-tests: name: Test on macOS runs-on: macos-latest steps: - uses: actions/checkout@v2 - name: Install stable Rust toolchain uses: actions-rs/toolchain@v1 with: override: true profile: minimal toolchain: stable - name: Run 'cargo check' uses: actions-rs/cargo@v1 with: command: check args: --all-features - name: Run 'cargo check --no-default-features' uses: actions-rs/cargo@v1 with: command: check args: --no-default-features - name: Run 'cargo test' uses: actions-rs/cargo@v1 with: command: test args: --all-features nanorand-rs-0.7.0/.gitignore000066400000000000000000000052471421177376500157550ustar00rootroot00000000000000# RUST gitignore generated by Blindfold # Generated by Cargo # will have compiled files and executables target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html Cargo.lock # VISUALSTUDIOCODE gitignore generated by Blindfold .vscode/* !.vscode/tasks.json !.vscode/launch.json *.code-workspace # JETBRAINS gitignore generated by Blindfold # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 # User-specific stuff .idea # Gradle and Maven with auto-import # When using Gradle or Maven with auto-import, you should exclude module files, # since they will be recreated, and may cause churn. Uncomment if using # auto-import. # .idea/artifacts # .idea/compiler.xml # .idea/jarRepositories.xml # .idea/modules.xml # .idea/*.iml # .idea/modules # *.iml # *.ipr # CMake cmake-build-*/ # Mongo Explorer plugin .idea/**/mongoSettings.xml # File-based project format *.iws # IntelliJ out/ # mpeltonen/sbt-idea plugin .idea_modules/ # JIRA plugin atlassian-ide-plugin.xml # Cursive Clojure plugin .idea/replstate.xml # Crashlytics plugin (for Android Studio and IntelliJ) com_crashlytics_export_strings.xml crashlytics.properties crashlytics-build.properties fabric.properties # Editor-based Rest Client .idea/httpRequests # Android studio 3.1+ serialized cache file .idea/caches/build_file_checksums.ser # WINDOWS gitignore generated by Blindfold # Windows thumbnail cache files Thumbs.db Thumbs.db:encryptable ehthumbs.db ehthumbs_vista.db # Dump file *.stackdump # Folder config file [Dd]esktop.ini # Recycle Bin used on file shares $RECYCLE.BIN/ # Windows Installer files *.cab *.msi *.msix *.msm *.msp # Windows shortcuts *.lnk # MACOS gitignore generated by Blindfold # General .DS_Store .AppleDouble .LSOverride # Icon must end with two \r Icon # Thumbnails ._* # Files that might appear in the root of a volume .DocumentRevisions-V100 .fseventsd .Spotlight-V100 .TemporaryItems .Trashes .VolumeIcon.icns .com.apple.timemachine.donotpresent # Directories potentially created on remote AFP share .AppleDB .AppleDesktop Network Trash Folder Temporary Items .apdisk # LINUX gitignore generated by Blindfold *~ # temporary files which can be created if a process still has a handle open of a deleted file .fuse_hidden* # KDE directory preferences .directory # Linux trash folder which might appear on any partition or disk .Trash-* # .nfs files are created when an open file is removed but is still being accessed .nfs* nanorand-rs-0.7.0/.rustfmt.toml000066400000000000000000000000201421177376500164240ustar00rootroot00000000000000hard_tabs = truenanorand-rs-0.7.0/.rusty-hook.toml000066400000000000000000000002401421177376500170500ustar00rootroot00000000000000[hooks] commit-msg = "python3 enforce-conventional-commits.py \"%rh!\"" pre-commit = "cargo clippy && cargo test && cargo deny check" [logging] verbose = true nanorand-rs-0.7.0/Cargo.toml000066400000000000000000000017261421177376500157130ustar00rootroot00000000000000[package] name = "nanorand" description = "A tiny, fast, zero-dep library for random number generation." repository = "https://github.com/Absolucy/nanorand-rs" keywords = ["rand", "random", "no-std", "entropy"] categories = ["algorithms", "no-std"] readme = "README.md" version = "0.7.0" authors = ["Lucy "] edition = "2021" license = "Zlib" [features] default = ["std", "tls", "wyrand", "pcg64", "chacha"] alloc = [] std = ["alloc"] tls = ["std", "wyrand"] wyrand = [] pcg64 = [] chacha = [] rdseed = ["std"] [dependencies] zeroize = { version = "1.5.3", optional = true, features = ["zeroize_derive"] } getrandom = { version = "0.2.5", optional = true, features = ["rdrand", "js"] } [dev-dependencies] hex = "0.4.3" [package.metadata.docs.rs] all-features = true default-target = "x86_64-unknown-linux-gnu" targets = ["x86_64-pc-windows-msvc"] [workspace] members = ["nanorand-abi", "nanorand-bench"] default-members = ["."] [profile.bench] lto = "thin" nanorand-rs-0.7.0/LICENSE.md000066400000000000000000000016051421177376500153630ustar00rootroot00000000000000The 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. nanorand-rs-0.7.0/README.md000066400000000000000000000155051421177376500152420ustar00rootroot00000000000000[![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::()); ``` ### Generating a number with a thread-local RNG ```rust use nanorand::Rng; let mut rng = nanorand::tls_rng(); println!("Random number: {}", rng.generate::()); ``` ### 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**1|**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) 1. Speed benchmarked on an M1 Macbook Air ### 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 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. nanorand-rs-0.7.0/README.tpl000066400000000000000000000036061421177376500154400ustar00rootroot00000000000000[![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 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. nanorand-rs-0.7.0/deny.toml000066400000000000000000000021051421177376500156070ustar00rootroot00000000000000[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 = [] nanorand-rs-0.7.0/enforce-conventional-commits.py000077500000000000000000000006451421177376500221260ustar00rootroot00000000000000#!/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() nanorand-rs-0.7.0/nanorand-abi/000077500000000000000000000000001421177376500163065ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-abi/.gitignore000066400000000000000000000014741421177376500203040ustar00rootroot00000000000000# C gitignore generated by Blindfold # Prerequisites *.d # Object files *.o *.ko *.obj *.elf # Linker output *.ilk *.map *.exp # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex # Debug files *.dSYM/ *.su *.idb *.pdb # Kernel Module Compile Results *.mod* *.cmd .tmp_versions/ modules.order Module.symvers Mkfile.old dkms.conf # C++ gitignore generated by Blindfold # Prerequisites *.d # Compiled Object files *.slo *.lo *.o *.obj # Precompiled Headers *.gch *.pch # Linker files *.ilk # Debugger Files *.pdb # Compiled Dynamic libraries *.so *.dylib *.dll # Fortran module files *.mod *.smod # Compiled Static libraries *.lai *.la *.a *.lib # Executables *.exe *.out *.app nanorand-rs-0.7.0/nanorand-abi/Cargo.toml000066400000000000000000000010011421177376500202260ustar00rootroot00000000000000[package] name = "nanorand-abi" description = "A stable C-compatible ABI for nanorand" repository = "https://github.com/Absolucy/nanorand-rs" keywords = ["rng", "wyrand", "pcg", "chacha", "random", "pcg64"] version = "0.7.0" authors = ["Lucy "] edition = "2021" license = "Zlib" publish = false [lib] crate-type = ["cdylib", "staticlib"] [dependencies] paste = "1.0.6" [dependencies.nanorand] path = ".." default-features = false features = ["std", "wyrand", "pcg64", "chacha", "getrandom"] nanorand-rs-0.7.0/nanorand-abi/examples/000077500000000000000000000000001421177376500201245ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-abi/examples/smallcrush.c000066400000000000000000000023121421177376500224430ustar00rootroot00000000000000#include "nanorand.h" #include static BufferedChaCha20 *chacha_rng; static BufferedWyRand *wyrand_rng; static BufferedPcg64 *pcg_rng; unsigned int gen_chacha(void) { unsigned int ret; chacha20_buffered_fill(chacha_rng, &ret, sizeof(ret)); return ret; } unsigned int gen_wyrand(void) { unsigned int ret; wyrand_buffered_fill(wyrand_rng, &ret, sizeof(ret)); return ret; } unsigned int gen_pcg(void) { unsigned int ret; pcg64_buffered_fill(pcg_rng, &ret, sizeof(ret)); return ret; } int main() { chacha_rng = chacha20_buffered(new_chacha20()); wyrand_rng = wyrand_buffered(new_wyrand()); pcg_rng = pcg64_buffered(new_pcg64()); unif01_Gen *chacha_testu01 = unif01_CreateExternGenBits("nanorand chacha20", gen_chacha); bbattery_SmallCrush(chacha_testu01); unif01_DeleteExternGenBits(chacha_testu01); unif01_Gen *wyrand_testu01 = unif01_CreateExternGenBits("nanorand wyrand", gen_wyrand); bbattery_SmallCrush(wyrand_testu01); unif01_DeleteExternGenBits(wyrand_testu01); unif01_Gen *pcg_testu01 = unif01_CreateExternGenBits("nanorand pcg64", gen_pcg); bbattery_SmallCrush(pcg_testu01); unif01_DeleteExternGenBits(pcg_testu01); } nanorand-rs-0.7.0/nanorand-abi/examples/test.cpp000066400000000000000000000002501421177376500216040ustar00rootroot00000000000000#include "nanorand.h" #include int main() { nanorand::ChaCha rng = nanorand::new_chacha20(); std::cout << nanorand::chacha_next_u16(&rng) << std::endl; } nanorand-rs-0.7.0/nanorand-abi/nanorand.h000066400000000000000000000103501421177376500202560ustar00rootroot00000000000000/* * Copyright (c) 2022 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. */ #ifndef NANORAND_H #define NANORAND_H #define DEFINE_NANORAND_BASE(type, buffered_type, name) \ type *new_##name(void); \ void free_##name(type *rng); \ void name##_fill(type *rng, void *buffer, usize length); \ buffered_type *name##_buffered(type *rng); \ void free_##name##_buffered(buffered_type *rng); \ void name##_buffered_fill(buffered_type *rng, void *buffer, usize length); #define DEFINE_NANORAND_RANGES(type, name) \ u8 name##_range_u8(type *rng, u8 lower, u8 upper); \ u16 name##_range_u16(type *rng, u16 lower, u16 upper); \ u32 name##_range_u32(type *rng, u32 lower, u32 upper); \ u64 name##_range_u64(type *rng, u64 lower, u64 upper); \ usize name##_range_usize(type *rng, usize lower, usize upper); \ i8 name##_range_i8(type *rng, i8 lower, i8 upper); \ i16 name##_range_i16(type *rng, i16 lower, i16 upper); \ i32 name##_range_i32(type *rng, i32 lower, i32 upper); \ i64 name##_range_i64(type *rng, i64 lower, i64 upper); \ isize name##_range_isize(type *rng, isize lower, isize upper); #include typedef uint8_t u8; typedef uint16_t u16; typedef uint32_t u32; typedef uint64_t u64; typedef uintptr_t usize; typedef int8_t i8; typedef int16_t i16; typedef int32_t i32; typedef int64_t i64; typedef intptr_t isize; #ifdef __cplusplus extern "C" { #endif typedef struct _WyRand WyRand; typedef struct _BufferedWyRand BufferedWyRand; DEFINE_NANORAND_BASE(WyRand, BufferedWyRand, wyrand); DEFINE_NANORAND_RANGES(WyRand, wyrand); typedef struct _Pcg64 Pcg64; typedef struct _BufferedPcg64 BufferedPcg64; DEFINE_NANORAND_BASE(Pcg64, BufferedPcg64, pcg64); DEFINE_NANORAND_RANGES(Pcg64, pcg64); typedef struct _ChaCha8 ChaCha8; typedef struct _BufferedChaCha8 BufferedChaCha8; DEFINE_NANORAND_BASE(ChaCha8, BufferedChaCha8, chacha8); DEFINE_NANORAND_RANGES(ChaCha8, chacha8); typedef struct _ChaCha12 ChaCha12; typedef struct _BufferedChaCha12 BufferedChaCha12; DEFINE_NANORAND_BASE(ChaCha12, BufferedChaCha12, chacha12); DEFINE_NANORAND_RANGES(ChaCha12, chacha12); typedef struct _ChaCha20 ChaCha20; typedef struct _BufferedChaCha20 BufferedChaCha20; DEFINE_NANORAND_BASE(ChaCha20, BufferedChaCha20, chacha20); DEFINE_NANORAND_RANGES(ChaCha20, chacha20); #ifdef __cplusplus } #endif #endif nanorand-rs-0.7.0/nanorand-abi/src/000077500000000000000000000000001421177376500170755ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-abi/src/chacha/000077500000000000000000000000001421177376500203045ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-abi/src/chacha/chacha12.rs000066400000000000000000000001011421177376500222140ustar00rootroot00000000000000use crate::define_c_api; define_c_api!(chacha12, ChaCha12, 64); nanorand-rs-0.7.0/nanorand-abi/src/chacha/chacha20.rs000066400000000000000000000001011421177376500222130ustar00rootroot00000000000000use crate::define_c_api; define_c_api!(chacha20, ChaCha20, 64); nanorand-rs-0.7.0/nanorand-abi/src/chacha/chacha8.rs000066400000000000000000000000771421177376500221550ustar00rootroot00000000000000use crate::define_c_api; define_c_api!(chacha8, ChaCha8, 64); nanorand-rs-0.7.0/nanorand-abi/src/chacha/mod.rs000066400000000000000000000000651421177376500214320ustar00rootroot00000000000000pub mod chacha12; pub mod chacha20; pub mod chacha8; nanorand-rs-0.7.0/nanorand-abi/src/lib.rs000066400000000000000000000056241421177376500202200ustar00rootroot00000000000000#![deny(clippy::complexity, clippy::correctness, clippy::perf, clippy::style)] #![allow(improper_ctypes_definitions, clippy::missing_safety_doc)] pub mod chacha; pub mod pcg64; pub mod wyrand; #[macro_export] macro_rules! define_c_api { (range, $rng_name:ident, $rng_type:ty, $num_type:ty) => { paste::paste! { #[no_mangle] pub unsafe extern "C" fn [<$rng_name _range_ $num_type>](rng: *mut $rng_type, lower: $num_type, upper: $num_type) -> $num_type { let rng = &mut *rng; rng.generate_range(lower..=upper) } } }; ($name:ident, $rng_type:ty, $size:expr) => { paste::paste! { use nanorand::{BufferedRng, $rng_type, Rng}; /// Create a new $type RNG, using system-provided entropy. /// This must be freed later with `free_$name(ptr)`! #[no_mangle] pub extern "C" fn []() -> *mut $rng_type { Box::into_raw(Box::new(<$rng_type>::new())) } /// Free a $type RNG. #[no_mangle] pub unsafe extern "C" fn [](ptr: *mut $rng_type) { std::mem::drop(Box::from_raw(ptr)); } /// Get the raw 64-bit output from the provided RNG. #[no_mangle] pub unsafe extern "C" fn [<$name _next>](rng: *mut $rng_type) -> [u8; $size] { (*rng).rand() } /// Fill the provided buffer with random bytes. #[no_mangle] pub unsafe extern "C" fn [<$name _fill>]( rng: *mut $rng_type, buffer: *mut u8, buffer_length: usize, ) { let rng = &mut *rng; let buffer = std::slice::from_raw_parts_mut(buffer, buffer_length); rng.fill_bytes(buffer); } define_c_api!(range, $name, $rng_type, u8); define_c_api!(range, $name, $rng_type, u16); define_c_api!(range, $name, $rng_type, u32); define_c_api!(range, $name, $rng_type, u64); define_c_api!(range, $name, $rng_type, usize); define_c_api!(range, $name, $rng_type, i8); define_c_api!(range, $name, $rng_type, i16); define_c_api!(range, $name, $rng_type, i32); define_c_api!(range, $name, $rng_type, i64); define_c_api!(range, $name, $rng_type, isize); /// Wrap a $type RNG in a buffer. /// This consumes the RNG, and must be freed later with `free_$name_buffered(ptr)`! #[no_mangle] pub unsafe extern "C" fn [<$name _buffered>](rng: *mut $rng_type) -> *mut BufferedRng<$rng_type, $size> { let rng = *Box::from_raw(rng); let buffered = BufferedRng::new(rng); Box::into_raw(Box::new(buffered)) } /// Free a buffer-wrapped $type RNG. #[no_mangle] pub unsafe extern "C" fn [](ptr: *mut BufferedRng<$rng_type, $size>) { std::mem::drop(Box::from_raw(ptr)); } /// Fill the provided buffer with random bytes. #[no_mangle] pub unsafe extern "C" fn [<$name _buffered_fill>]( rng: *mut BufferedRng<$rng_type, $size>, buffer: *mut u8, buffer_length: usize ) { let rng = &mut *rng; let buffer = std::slice::from_raw_parts_mut(buffer, buffer_length); rng.fill_bytes(buffer); } } }; } nanorand-rs-0.7.0/nanorand-abi/src/pcg64.rs000066400000000000000000000000721421177376500203650ustar00rootroot00000000000000use crate::define_c_api; define_c_api!(pcg64, Pcg64, 8); nanorand-rs-0.7.0/nanorand-abi/src/wyrand.rs000066400000000000000000000000741421177376500207500ustar00rootroot00000000000000use crate::define_c_api; define_c_api!(wyrand, WyRand, 8); nanorand-rs-0.7.0/nanorand-bench/000077500000000000000000000000001421177376500166325ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-bench/Cargo.toml000066400000000000000000000006261421177376500205660ustar00rootroot00000000000000[package] name = "nanorand-bench" version = "0.0.0" authors = ["Lucy "] edition = "2021" publish = false [dependencies] criterion = "0.3.5" fastrand = "1.7.0" hex = "0.4.3" nanorand = { path = ".." } oorandom = "11.1.3" rand_chacha = "0.3.1" rand_core = "0.6.3" rand_pcg = "0.3.1" random-fast-rng = "0.1.1" randomize = "3.0.1" wyhash = "0.5.0" [[bench]] name = "speed" harness = false nanorand-rs-0.7.0/nanorand-bench/benches/000077500000000000000000000000001421177376500202415ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-bench/benches/speed.rs000066400000000000000000000103261421177376500217110ustar00rootroot00000000000000use criterion::{black_box, criterion_group, criterion_main, Criterion, Throughput}; use nanorand::rand::Rng; use rand_core::RngCore; use random_fast_rng::Random; fn criterion_benchmark(c: &mut Criterion) { let mut entropy_group = c.benchmark_group("entropy"); entropy_group.throughput(Throughput::Bytes(std::mem::size_of::() as u64 * 4096)); entropy_group.bench_function("system entropy", |b| { let mut out = [0u8; std::mem::size_of::() * 4096]; b.iter(|| { nanorand::entropy::system(&mut out); }) }); entropy_group.finish(); let mut rng_group = c.benchmark_group("rngs"); rng_group.throughput(Throughput::Bytes(std::mem::size_of::() as u64 * 1024)); rng_group.bench_function("wyrand", |b| { let mut rng = nanorand::rand::WyRand::new(); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.generate()); } black_box(n); }) }); rng_group.bench_function("pcg64", |b| { let mut rng = nanorand::rand::Pcg64::new(); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.generate()); } black_box(n); }) }); rng_group.bench_function("chacha8", |b| { let mut rng = nanorand::rand::ChaCha8::new(); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.generate()); } black_box(n); }) }); rng_group.bench_function("chacha20", |b| { let mut rng = nanorand::rand::ChaCha20::new(); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.generate()); } black_box(n); }) }); rng_group.finish(); let mut other_rngs = c.benchmark_group("other-rng-crates"); other_rngs.throughput(Throughput::Bytes(std::mem::size_of::() as u64 * 1024)); other_rngs.bench_function("wyhash wyrand", |b| { let mut seed = 42_u64; b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(wyhash::wyrng(&mut seed)); } black_box(n); }) }); other_rngs.bench_function("oorandom pcg32", |b| { let mut rng = oorandom::Rand32::new(42); b.iter(|| { let mut n: u32 = u32::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.rand_u32()); } black_box(n); }) }); other_rngs.bench_function("oorandom pcg64", |b| { let mut rng = oorandom::Rand64::new(42); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.rand_u64()); } black_box(n); }) }); other_rngs.bench_function("randomize pcg32", |b| { let mut rng = randomize::PCG32::seed(42, 0); b.iter(|| { let mut n: u32 = u32::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.next_u32()); } black_box(n); }) }); other_rngs.bench_function("randomize pcg64", |b| { let mut rng = randomize::PCG64::seed(42, 0); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.next_u64()); } black_box(n); }) }); other_rngs.bench_function("rand pcg32", |b| { let mut rng = rand_pcg::Pcg32::new(0xcafef00dd15ea5e5, 0xa02bdbf7bb3c0a7); b.iter(|| { let mut n: u32 = u32::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.next_u32()); } black_box(n); }) }); other_rngs.bench_function("rand pcg64", |b| { let mut rng = rand_pcg::Pcg64::new(0xcafef00dd15ea5e5, 0xa02bdbf7bb3c0a7ac28fa16a64abf96); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.next_u64()); } black_box(n); }) }); other_rngs.bench_function("random-fast-rng pcg32", |b| { let mut rng = random_fast_rng::FastRng::new(); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.gen()); } black_box(n); }) }); other_rngs.bench_function("fastrand pcg32", |b| { let rng = fastrand::Rng::with_seed(42); b.iter(|| { let mut n: u32 = u32::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.u32(u32::MIN..u32::MAX)); } black_box(n); }) }); other_rngs.bench_function("fastrand pcg64", |b| { let rng = fastrand::Rng::with_seed(42); b.iter(|| { let mut n: u64 = u64::MIN; for _ in 0..1024 { n = n.wrapping_add(rng.u64(u64::MIN..u64::MAX)); } black_box(n); }) }); other_rngs.finish(); } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); nanorand-rs-0.7.0/nanorand-bench/src/000077500000000000000000000000001421177376500174215ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-bench/src/main.rs000066400000000000000000000001101421177376500207030ustar00rootroot00000000000000fn main() { println!("This is meant to be ran with `cargo bench`!"); } nanorand-rs-0.7.0/nanorand-fuzz/000077500000000000000000000000001421177376500165515ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-fuzz/.gitignore000066400000000000000000000000311421177376500205330ustar00rootroot00000000000000 target corpus artifacts nanorand-rs-0.7.0/nanorand-fuzz/Cargo.toml000066400000000000000000000007531421177376500205060ustar00rootroot00000000000000 [package] name = "nanorand-fuzz" version = "0.0.0" authors = ["Automatically generated"] publish = false edition = "2021" [package.metadata] cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4.1" nanorand = { path = ".." } c2-chacha = "0.3.1" # Prevent this from interfering with workspaces [workspace] members = ["."] [[bin]] name = "chacha20" path = "fuzz_targets/chacha20.rs" test = false doc = false [[bin]] name = "range" path = "fuzz_targets/range.rs" test = false doc = false nanorand-rs-0.7.0/nanorand-fuzz/fuzz_targets/000077500000000000000000000000001421177376500213005ustar00rootroot00000000000000nanorand-rs-0.7.0/nanorand-fuzz/fuzz_targets/chacha20.rs000066400000000000000000000017631421177376500232260ustar00rootroot00000000000000#![no_main] use c2_chacha::{ stream_cipher::{generic_array::GenericArray, NewStreamCipher, SyncStreamCipher}, ChaCha20, }; use libfuzzer_sys::fuzz_target; use nanorand::crypto::chacha; const KEY_LEN: usize = 32; const NONCE_LEN: usize = 8; fuzz_target!(|data: ([u8; KEY_LEN], [u8; NONCE_LEN])| { let (key, nonce) = data; let reference_keystream = { let mut state = ChaCha20::new( GenericArray::from_slice(&key), GenericArray::from_slice(&nonce), ); let mut keystream = [0u8; 256]; state.apply_keystream(&mut keystream); keystream }; let our_keystream = { let mut state = chacha::chacha_init(key, nonce); let mut keystream: Vec = Vec::with_capacity(reference_keystream.len()); while reference_keystream.len() > keystream.len() { chacha::chacha_block(20, state) .iter() .for_each(|packed| keystream.extend_from_slice(&packed.to_le_bytes())); chacha::chacha_increment_counter(&mut state); } keystream }; assert_eq!(our_keystream, reference_keystream); }); nanorand-rs-0.7.0/nanorand-fuzz/fuzz_targets/range.rs000066400000000000000000000020671421177376500227470ustar00rootroot00000000000000#![no_main] use libfuzzer_sys::fuzz_target; use nanorand::{tls_rng, Rng}; fuzz_target!(|data: (u16, u16, bool)| { let (a, b, range_type) = data; let (lower, upper) = if a > b { (b, a) } else { (a, b) }; if range_type { let number = tls_rng().generate_range(lower..=upper); if upper == lower { assert_eq!( number, upper, "{} was outside of range {}=={}", number, lower, upper ); } else { assert!( number >= lower, "{} was bigger than range {}..={}", number, lower, upper ); assert!( number <= upper, "{} was smaller than range {}..={}", number, lower, upper ); } } else { let number = tls_rng().generate_range(lower..upper); if upper == lower { assert_eq!( number, upper, "{} was outside of range {}=={}", number, lower, upper ); } else { assert!( number >= lower, "{} was bigger than range {}..{}", number, lower, upper ); assert!( number < upper, "{} was smaller than range {}..{}", number, lower, upper ); } }; }); nanorand-rs-0.7.0/src/000077500000000000000000000000001421177376500145445ustar00rootroot00000000000000nanorand-rs-0.7.0/src/buffer.rs000066400000000000000000000060171421177376500163670ustar00rootroot00000000000000use 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, const OUTPUT: usize> { rng: InternalGenerator, buffer: Vec, } impl, const OUTPUT: usize> BufferedRng { /// 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, const OUTPUT: usize> Rng for BufferedRng { fn rand(&mut self) -> [u8; OUTPUT] { let mut out = [0_u8; OUTPUT]; self.fill_bytes(&mut out); out } fn fill_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, const OUTPUT: usize> std::io::Read for BufferedRng { fn read(&mut self, output: &mut [u8]) -> std::io::Result { self.fill_bytes(&mut *output); Ok(output.len()) } fn read_to_end(&mut self, buf: &mut Vec) -> std::io::Result { buf.extend_from_slice(&self.buffer); Ok(self.buffer.drain(..).count()) } fn read_to_string(&mut self, _buf: &mut String) -> std::io::Result { panic!("attempted to read an rng into a string") } } impl< InternalGenerator: SeedableRng, const OUTPUT: usize, const SEED_SIZE: usize, > SeedableRng for BufferedRng { fn reseed(&mut self, seed: [u8; SEED_SIZE]) { self.rng.reseed(seed); } } impl + Default, const OUTPUT: usize> Default for BufferedRng { fn default() -> Self { Self::new(InternalGenerator::default()) } } nanorand-rs-0.7.0/src/crypto.rs000066400000000000000000000002231421177376500164270ustar00rootroot00000000000000/// Implementation of the ChaCha cryptographic primitives. /// More details can be seen at https://en.wikipedia.org/wiki/Salsa20 pub mod chacha; nanorand-rs-0.7.0/src/crypto/000077500000000000000000000000001421177376500160645ustar00rootroot00000000000000nanorand-rs-0.7.0/src/crypto/chacha.rs000066400000000000000000000122031421177376500176370ustar00rootroot00000000000000const 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(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 = hex::decode($keystream_hex).unwrap(); let mut state = chacha_init(key, nonce); let mut keystream: Vec = 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" ); } } nanorand-rs-0.7.0/src/entropy.rs000066400000000000000000000073231421177376500166170ustar00rootroot00000000000000#[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 { #[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 { 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::() - 1) / core::mem::size_of::()).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::() * 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 { 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!") } nanorand-rs-0.7.0/src/entropy/000077500000000000000000000000001421177376500162445ustar00rootroot00000000000000nanorand-rs-0.7.0/src/entropy/darwin.rs000066400000000000000000000005141421177376500200760ustar00rootroot00000000000000use 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 } } nanorand-rs-0.7.0/src/entropy/linux.rs000066400000000000000000000003411421177376500177470ustar00rootroot00000000000000extern "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 } } nanorand-rs-0.7.0/src/entropy/windows.rs000066400000000000000000000004451421177376500203070ustar00rootroot00000000000000extern "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 } } nanorand-rs-0.7.0/src/entropy/windows_uwp.rs000066400000000000000000000007701421177376500212030ustar00rootroot00000000000000use 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 } } nanorand-rs-0.7.0/src/gen.rs000066400000000000000000000134051421177376500156660ustar00rootroot00000000000000use crate::Rng; use core::ops::{Bound, RangeBounds}; macro_rules! gen { ($($type:ty),+) => { $( impl, const OUTPUT: usize> RandomGen 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, const OUTPUT: usize> RandomRange for $type { fn random_range>(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, const OUTPUT: usize> RandomRange for $signed { fn random_range>(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, 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, const OUTPUT: usize>: RandomGen { /// 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>(nng: &mut Generator, range: Bounds) -> Self; } impl, const OUTPUT: usize> RandomGen for bool { fn random(rng: &mut Generator) -> Self { u8::random(rng) < 0b10000000 } } impl, const OUTPUT: usize> RandomGen for f32 { fn random(rng: &mut Generator) -> Self { (u32::random(rng) as f32) / (u32::MAX as f32) } } impl, const OUTPUT: usize> RandomGen 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::(); assert!(1.0 >= number, "{} was bigger than 1.0", number); assert!(number >= 0.0, "0 was bigger than {}", number); let number = rng.generate::(); 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); } } nanorand-rs-0.7.0/src/lib.rs000066400000000000000000000142721421177376500156660ustar00rootroot00000000000000#![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::()); //! ``` //! ## Generating a number with a thread-local RNG //! ```rust //! use nanorand::Rng; //! //! let mut rng = nanorand::tls_rng(); //! println!("Random number: {}", rng.generate::()); //! ``` //! ## 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**1|**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) //! //! 1. Speed benchmarked on an M1 Macbook Air //! //! ## 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; nanorand-rs-0.7.0/src/rand.rs000066400000000000000000000052261421177376500160430ustar00rootroot00000000000000#[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 #[cfg(feature = "wyrand")] pub mod wyrand; /// Implementation of the Pcg64 PRNG algorithm. /// More details can be seen at #[cfg(feature = "pcg64")] pub mod pcg64; /// Implementation of the ChaCha CSPRNG algorithm. /// More details can be seen at #[cfg(feature = "chacha")] pub mod chacha; /// A trait that represents a random number generator. pub trait Rng: 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(&mut self) -> Generated where Generated: RandomGen, { Generated::random(self) } /// Fill an array of bytes with randomness. fn fill_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(&mut self, mut target: Array) where Contents: RandomGen, 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(&mut self, range: Bounds) -> Number where Number: RandomRange, Bounds: RangeBounds, { Number::random_range(self, range) } /// Shuffle a slice, using the RNG. fn shuffle(&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: Rng { /// Re-seed the RNG with the specified bytes. fn reseed(&mut self, seed: [u8; SEED_SIZE]); } nanorand-rs-0.7.0/src/rand/000077500000000000000000000000001421177376500154705ustar00rootroot00000000000000nanorand-rs-0.7.0/src/rand/chacha.rs000066400000000000000000000062151421177376500172510ustar00rootroot00000000000000use 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 { state: [u32; 16], } impl ChaCha { /// 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 Default for ChaCha { 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 Rng<64> for ChaCha { fn rand(&mut self) -> [u8; 64] { let block = chacha::chacha_block::(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 Clone for ChaCha { fn clone(&self) -> Self { Self { state: self.state } } } impl Display for ChaCha { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "ChaCha ({:p}, {} rounds)", self, ROUNDS) } } impl SeedableRng<40, 64> for ChaCha { 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 Debug for ChaCha { 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() } } nanorand-rs-0.7.0/src/rand/pcg64.rs000066400000000000000000000052741421177376500167710ustar00rootroot00000000000000// 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::()] = 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::()] = 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() } } nanorand-rs-0.7.0/src/rand/wyrand.rs000066400000000000000000000036401421177376500173450ustar00rootroot00000000000000// 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::()] = 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); } } nanorand-rs-0.7.0/src/tls.rs000066400000000000000000000017731421177376500157240ustar00rootroot00000000000000use crate::rand::{wyrand::WyRand, Rng, SeedableRng}; use std::{cell::RefCell, rc::Rc}; thread_local! { static WYRAND: Rc> = Rc::new(RefCell::new(WyRand::new())); } #[derive(Clone)] #[doc(hidden)] pub struct TlsWyRand(Rc>); 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::()); /// ``` /// 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::()); /// }); /// ``` pub fn tls_rng() -> TlsWyRand { WYRAND.with(|tls| TlsWyRand(tls.clone())) }