├── fuzz ├── .gitignore ├── fuzz_targets │ └── fuzz_target_1.rs └── Cargo.toml ├── dryoc.png ├── .gitignore ├── src ├── poly1305 │ ├── mod.rs │ └── poly1305_soft.rs ├── blake2b │ └── mod.rs ├── rng.rs ├── scalarmult_curve25519.rs ├── classic │ ├── crypto_secretbox_impl.rs │ ├── crypto_hash.rs │ ├── crypto_box_impl.rs │ ├── generichash_blake2b.rs │ ├── crypto_shorthash.rs │ ├── crypto_kdf.rs │ ├── crypto_auth.rs │ ├── crypto_kx.rs │ ├── crypto_onetimeauth.rs │ ├── crypto_generichash.rs │ ├── crypto_secretbox.rs │ └── crypto_sign.rs ├── error.rs ├── sha512.rs ├── utils.rs ├── kdf.rs ├── siphash24.rs ├── precalc.rs ├── auth.rs ├── onetimeauth.rs ├── bytes_serde.rs ├── lib.rs ├── kx.rs ├── constants.rs └── generichash.rs ├── .github ├── FUNDING.yml ├── dependabot.yml ├── ISSUE_TEMPLATE │ ├── config.yml │ └── bug_report.yml └── workflows │ ├── coverage.yml │ ├── publish.yml │ └── build-and-test.yml ├── netlify.toml ├── .rustfmt.toml ├── LICENSE ├── Cargo.toml └── README.md /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | target 3 | corpus 4 | artifacts 5 | -------------------------------------------------------------------------------- /dryoc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brndnmtthws/dryoc/HEAD/dryoc.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | 4 | # Ignore IDE stuff 5 | .vscode/ 6 | -------------------------------------------------------------------------------- /src/poly1305/mod.rs: -------------------------------------------------------------------------------- 1 | pub(crate) mod poly1305_soft; 2 | pub(crate) use poly1305_soft::*; 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: brndnmtthws 4 | -------------------------------------------------------------------------------- /netlify.toml: -------------------------------------------------------------------------------- 1 | [build] 2 | publish = "target/doc" 3 | command = 'rustup toolchain install nightly && cargo +nightly doc --features nightly,serde,base64 && echo "/ /dryoc" >> target/doc/_redirects' 4 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_target_1.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | use libfuzzer_sys::fuzz_target; 3 | 4 | use dryoc::generichash::{GenericHash, Key}; 5 | 6 | fuzz_target!(|data: &[u8]| { 7 | GenericHash::hash_with_defaults_to_vec::<_, Key>(data, None).ok(); 8 | }); 9 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "github-actions" 4 | directory: "/" 5 | schedule: 6 | interval: "weekly" 7 | - package-ecosystem: "cargo" 8 | directory: "/" 9 | schedule: 10 | interval: "weekly" 11 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | enum_discrim_align_threshold = 60 2 | format_code_in_doc_comments = true 3 | format_macro_matchers = true 4 | format_strings = true 5 | group_imports = "StdExternalCrate" 6 | imports_granularity = "Module" 7 | # normalize_comments = true 8 | reorder_impl_items = true 9 | unstable_features = true 10 | use_field_init_shorthand = true 11 | style_edition = "2024" 12 | wrap_comments = true 13 | -------------------------------------------------------------------------------- /src/blake2b/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(all(feature = "simd_backend", feature = "nightly"))] 2 | pub(crate) mod blake2b_simd; 3 | #[cfg(all(feature = "simd_backend", feature = "nightly"))] 4 | pub(crate) use blake2b_simd::*; 5 | 6 | #[cfg(not(all(feature = "simd_backend", feature = "nightly")))] 7 | pub(crate) mod blake2b_soft; 8 | #[cfg(not(all(feature = "simd_backend", feature = "nightly")))] 9 | pub(crate) use blake2b_soft::*; 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: 🙏 General questions & help 4 | url: https://github.com/brndnmtthws/dryoc/discussions/categories/q-a 5 | about: Please ask and answer general questions using GitHub Discussions 6 | - name: 💡 New ideas and feature requests 7 | url: https://github.com/brndnmtthws/dryoc/discussions/categories/ideas 8 | about: Request and discuss enhancements using GitHub Discussions 9 | -------------------------------------------------------------------------------- /fuzz/Cargo.toml: -------------------------------------------------------------------------------- 1 | 2 | [package] 3 | name = "dryoc-fuzz" 4 | version = "0.0.0" 5 | authors = ["Automatically generated"] 6 | publish = false 7 | edition = "2018" 8 | 9 | [package.metadata] 10 | cargo-fuzz = true 11 | 12 | [dependencies] 13 | libfuzzer-sys = "0.4" 14 | 15 | [dependencies.dryoc] 16 | path = ".." 17 | 18 | # Prevent this from interfering with workspaces 19 | [workspace] 20 | members = ["."] 21 | 22 | [[bin]] 23 | name = "fuzz_target_1" 24 | path = "fuzz_targets/fuzz_target_1.rs" 25 | test = false 26 | doc = false 27 | -------------------------------------------------------------------------------- /src/rng.rs: -------------------------------------------------------------------------------- 1 | /// Provides random data up to `len` from the OS's random number generator. 2 | pub fn randombytes_buf(len: usize) -> Vec { 3 | use rand_core::{OsRng, TryRngCore}; 4 | 5 | let mut r: Vec = vec![0; len]; 6 | OsRng 7 | .try_fill_bytes(r.as_mut_slice()) 8 | .expect("failed to fill random bytes"); 9 | 10 | r 11 | } 12 | 13 | /// Provides random data up to length of `data` from the OS's random number 14 | /// generator. 15 | pub fn copy_randombytes(dest: &mut [u8]) { 16 | use rand_core::{OsRng, TryRngCore}; 17 | 18 | OsRng 19 | .try_fill_bytes(dest) 20 | .expect("failed to fill random bytes"); 21 | } 22 | -------------------------------------------------------------------------------- /.github/workflows/coverage.yml: -------------------------------------------------------------------------------- 1 | name: Coverage 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | concurrency: 10 | group: ${{ github.workflow }}-${{ github.ref }} 11 | cancel-in-progress: true 12 | 13 | jobs: 14 | test: 15 | name: coverage 16 | runs-on: ubuntu-24.04 17 | steps: 18 | - name: Checkout repository 19 | uses: actions/checkout@v4 20 | - name: Setup nightly Rust toolchain with caching 21 | uses: brndnmtthws/rust-action@v1 22 | with: 23 | toolchain: nightly 24 | - run: cargo tarpaulin --features serde,nightly --out Xml 25 | - name: Upload to codecov.io 26 | uses: codecov/codecov-action@v4 27 | if: github.repository == 'brndnmtthws/dryoc' 28 | with: 29 | fail_ci_if_error: true 30 | token: ${{ secrets.CODECOV_TOKEN }} 31 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: Publish to crates.io 2 | 3 | on: 4 | push: 5 | tags: 6 | - v* 7 | env: 8 | CARGO_TERM_COLOR: always 9 | 10 | permissions: 11 | contents: write 12 | discussions: write 13 | 14 | jobs: 15 | build-test-publish: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v4 19 | - uses: brndnmtthws/rust-action@v1 20 | with: 21 | toolchain: stable 22 | - run: cargo build 23 | - run: cargo test 24 | - run: cargo login ${{ secrets.CRATES_IO_TOKEN }} 25 | - run: cargo publish 26 | - name: Create Release 27 | id: create_release 28 | uses: softprops/action-gh-release@v1 29 | if: startsWith(github.ref, 'refs/tags/') 30 | with: 31 | draft: false 32 | prerelease: false 33 | discussion_category_name: General 34 | generate_release_notes: true 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 Brenden Matthews 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /src/scalarmult_curve25519.rs: -------------------------------------------------------------------------------- 1 | use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE; 2 | use curve25519_dalek::montgomery::MontgomeryPoint; 3 | use curve25519_dalek::scalar::Scalar; 4 | 5 | use crate::constants::{ 6 | CRYPTO_SCALARMULT_CURVE25519_BYTES, CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES, 7 | }; 8 | 9 | fn clamp( 10 | n: &[u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES], 11 | ) -> [u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES] { 12 | let mut s = *n; 13 | s[0] &= 248; 14 | s[31] &= 127; 15 | s[31] |= 64; 16 | s 17 | } 18 | 19 | pub(crate) fn crypto_scalarmult_curve25519_base( 20 | q: &mut [u8; CRYPTO_SCALARMULT_CURVE25519_BYTES], 21 | n: &[u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES], 22 | ) { 23 | let sk = Scalar::from_bytes_mod_order(clamp(n)); 24 | let pk = (ED25519_BASEPOINT_TABLE * &sk).to_montgomery(); 25 | 26 | q.copy_from_slice(pk.as_bytes()); 27 | } 28 | 29 | pub(crate) fn crypto_scalarmult_curve25519( 30 | q: &mut [u8; CRYPTO_SCALARMULT_CURVE25519_BYTES], 31 | n: &[u8; CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES], 32 | p: &[u8; CRYPTO_SCALARMULT_CURVE25519_BYTES], 33 | ) { 34 | let sk = Scalar::from_bytes_mod_order(clamp(n)); 35 | let pk = MontgomeryPoint(*p); 36 | let shared_secret = sk * pk; 37 | 38 | q.copy_from_slice(shared_secret.as_bytes()); 39 | } 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: 🐞 Bug Report 2 | description: "File a bug report: crashes, unexpected behaviour, and other errors go here." 3 | title: "[Bug]: " 4 | labels: ["bug", "triage"] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | If you haven't done so already, please take a moment to search the [existing issues](https://github.com/brndnmtthws/dryoc/issues) to see if this has been previously reported. If not, carry on. 10 | 11 | Thanks for taking the time to fill out this bug report! 12 | - type: textarea 13 | id: what-happened 14 | attributes: 15 | label: What happened? 16 | description: > 17 | Describe the problem, what you expected to happen, and any other 18 | relevant info. You can sample code below. 19 | placeholder: Here's what happened 20 | validations: 21 | required: true 22 | - type: input 23 | id: version 24 | attributes: 25 | label: Version 26 | description: What version of dryoc are you using? 27 | validations: 28 | required: true 29 | - type: dropdown 30 | id: os 31 | validations: 32 | required: true 33 | attributes: 34 | label: Which OS are you seeing the problem on? 35 | options: 36 | - Linux 37 | - macOS 38 | - Windows 39 | - type: textarea 40 | id: code 41 | attributes: 42 | label: Test code 43 | description: If possible, please provide a minimal code sample that produces the problem. 44 | render: Rust 45 | -------------------------------------------------------------------------------- /src/classic/crypto_secretbox_impl.rs: -------------------------------------------------------------------------------- 1 | use generic_array::GenericArray; 2 | use salsa20::XSalsa20; 3 | use salsa20::cipher::{KeyIvInit, StreamCipher}; 4 | use subtle::ConstantTimeEq; 5 | use zeroize::Zeroize; 6 | 7 | use crate::classic::crypto_secretbox::{Key, Mac, Nonce}; 8 | use crate::error::Error; 9 | use crate::poly1305::Poly1305; 10 | 11 | pub(crate) fn crypto_secretbox_detached_inplace( 12 | data: &mut [u8], 13 | mac: &mut Mac, 14 | nonce: &Nonce, 15 | key: &Key, 16 | ) { 17 | let mut cipher = XSalsa20::new( 18 | GenericArray::from_slice(key), 19 | GenericArray::from_slice(nonce), 20 | ); 21 | 22 | let mut mac_key = crate::poly1305::Key::new(); 23 | cipher.apply_keystream(&mut mac_key); 24 | 25 | let mut computed_mac = Poly1305::new(&mac_key); 26 | 27 | mac_key.zeroize(); 28 | 29 | cipher.apply_keystream(data); 30 | 31 | computed_mac.update(data); 32 | computed_mac.finalize(mac); 33 | } 34 | 35 | pub(crate) fn crypto_secretbox_open_detached_inplace( 36 | data: &mut [u8], 37 | mac: &Mac, 38 | nonce: &Nonce, 39 | key: &Key, 40 | ) -> Result<(), Error> { 41 | let mut cipher = XSalsa20::new( 42 | GenericArray::from_slice(key), 43 | GenericArray::from_slice(nonce), 44 | ); 45 | 46 | let mut mac_key = crate::poly1305::Key::new(); 47 | cipher.apply_keystream(&mut mac_key); 48 | 49 | let mut computed_mac = Poly1305::new(&mac_key); 50 | mac_key.zeroize(); 51 | 52 | computed_mac.update(data); 53 | let computed_mac = computed_mac.finalize_to_array(); 54 | 55 | cipher.apply_keystream(data); 56 | 57 | if mac.ct_eq(&computed_mac).unwrap_u8() == 1 { 58 | Ok(()) 59 | } else { 60 | Err(dryoc_error!("decryption error (authentication failure)")) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: Build & test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 12 | 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | build: 19 | strategy: 20 | matrix: 21 | rust: 22 | - stable 23 | # - beta 24 | - nightly 25 | features: 26 | - serde 27 | - base64 28 | - simd_backend 29 | - default 30 | os: 31 | - ubuntu-latest 32 | - macos-latest 33 | - windows-latest 34 | exclude: 35 | - rust: stable 36 | features: simd_backend 37 | - rust: beta 38 | features: simd_backend 39 | - os: windows-latest 40 | features: simd_backend 41 | runs-on: ${{ matrix.os }} 42 | env: 43 | FEATURES: ${{ matrix.rust != 'nightly' && matrix.features || format('{0},nightly', matrix.features) }} 44 | steps: 45 | - uses: actions/checkout@v4 46 | - name: Setup ${{ matrix.rust }} Rust toolchain with caching 47 | uses: brndnmtthws/rust-action@v1 48 | with: 49 | toolchain: ${{ matrix.rust }} 50 | cargo-packages: | 51 | cargo-nextest 52 | cargo-tarpaulin 53 | - run: cargo build --features ${{ env.FEATURES }} 54 | - run: cargo nextest run --features ${{ env.FEATURES }} 55 | env: 56 | RUST_BACKTRACE: 1 57 | - run: cargo fmt --all -- --check 58 | if: ${{ matrix.rust == 'nightly' && matrix.os == 'ubuntu-latest' }} 59 | - run: cargo clippy --features ${{ env.FEATURES }} -- -D warnings 60 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | authors = ["Brenden Matthews "] 3 | categories = ["cryptography", "algorithms"] 4 | description = "Don't Roll Your Own Crypto: pure-Rust, hard to misuse cryptography library" 5 | documentation = "https://docs.rs/dryoc" 6 | edition = "2021" 7 | keywords = ["cryptography", "nacl", "libsodium", "curve25519", "crypto_box"] 8 | license = "MIT" 9 | name = "dryoc" 10 | readme = "README.md" 11 | repository = "https://github.com/brndnmtthws/dryoc" 12 | rust-version = "1.56" 13 | version = "0.7.1" 14 | 15 | [dependencies] 16 | base64 = { version = "0.21", optional = true } 17 | bitflags = "2.3" 18 | chacha20 = { version = "0.9", features = ["zeroize"] } 19 | curve25519-dalek = "4.1.3" 20 | generic-array = "0.14" 21 | lazy_static = "1" 22 | rand_core = { version = "0.9", features = ["os_rng"] } 23 | salsa20 = { version = "0.10", features = ["zeroize"] } 24 | serde = { version = "1.0", optional = true, features = ["derive"] } 25 | sha2 = "0.10" 26 | subtle = "2.4" 27 | zeroize = { version = "1.6", features = ["zeroize_derive"] } 28 | 29 | [target.'cfg(windows)'.dependencies] 30 | winapi = { version = "0.3", features = [ 31 | "impl-default", 32 | "memoryapi", 33 | "minwindef", 34 | "std", 35 | "sysinfoapi", 36 | "winnt", 37 | ] } 38 | 39 | [target.'cfg(unix)'.dependencies] 40 | libc = "0.2" 41 | 42 | [dev-dependencies] 43 | base64 = "0.21" 44 | bincode = "1" 45 | hex = "0.4" 46 | libc = "0.2" 47 | libsodium-sys = "0.2" 48 | serde = { version = "1.0", features = ["derive"] } 49 | serde_json = "1" 50 | sodiumoxide = "0.2" 51 | static_assertions = "1.1" 52 | num-bigint = { version = "0.4", features = ["rand"] } 53 | rand = "0.9" 54 | 55 | [features] 56 | default = ["u64_backend"] 57 | nightly = [] 58 | simd_backend = ["sha2/asm"] 59 | u64_backend = [] 60 | 61 | [package.metadata.docs.rs] 62 | # docs.rs uses nightly, enable feature flag to get all the juicy docs 63 | features = ["nightly", "serde", "base64"] 64 | -------------------------------------------------------------------------------- /src/classic/crypto_hash.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::CRYPTO_HASH_SHA512_BYTES; 2 | use crate::sha512::*; 3 | 4 | /// Type alias for SHA512 digest output. 5 | pub type Digest = [u8; CRYPTO_HASH_SHA512_BYTES]; 6 | 7 | /// Computes a SHA-512 hash from `input`. 8 | pub fn crypto_hash_sha512(output: &mut Digest, input: &[u8]) { 9 | let mut state = crypto_hash_sha512_init(); 10 | crypto_hash_sha512_update(&mut state, input); 11 | crypto_hash_sha512_final(state, output); 12 | } 13 | 14 | /// Internal state for `crypto_hash_*` functions. 15 | #[derive(Default)] 16 | pub struct Sha512State { 17 | pub(super) hasher: Sha512, 18 | } 19 | 20 | /// Initializes a SHA-512 hasher. 21 | pub fn crypto_hash_sha512_init() -> Sha512State { 22 | Sha512State::default() 23 | } 24 | 25 | /// Updates `state` of SHA-512 hasher with `input`. 26 | pub fn crypto_hash_sha512_update(state: &mut Sha512State, input: &[u8]) { 27 | state.hasher.update(input); 28 | } 29 | 30 | /// Finalizes `state` of SHA-512, and writes the digest to `output` consuming 31 | /// `state`. 32 | pub fn crypto_hash_sha512_final(state: Sha512State, output: &mut Digest) { 33 | state.hasher.finalize_into_bytes(output) 34 | } 35 | 36 | #[cfg(test)] 37 | mod tests { 38 | use super::*; 39 | 40 | #[test] 41 | fn test_crypto_hash_sha512() { 42 | use sodiumoxide::crypto::hash; 43 | 44 | use crate::rng::randombytes_buf; 45 | 46 | let r = randombytes_buf(64); 47 | 48 | let their_digest = hash::hash(&r); 49 | let mut our_digest = [0u8; CRYPTO_HASH_SHA512_BYTES]; 50 | crypto_hash_sha512(&mut our_digest, &r); 51 | 52 | assert_eq!(their_digest.as_ref(), our_digest); 53 | } 54 | 55 | #[test] 56 | fn test_crypto_hash_sha512_update() { 57 | use sodiumoxide::crypto::hash; 58 | 59 | use crate::rng::randombytes_buf; 60 | 61 | let mut their_state = hash::State::new(); 62 | let mut our_state = crypto_hash_sha512_init(); 63 | 64 | for _ in 0..10 { 65 | let r = randombytes_buf(64); 66 | their_state.update(&r); 67 | crypto_hash_sha512_update(&mut our_state, &r); 68 | } 69 | 70 | let their_digest = their_state.finalize(); 71 | let mut our_digest = [0u8; CRYPTO_HASH_SHA512_BYTES]; 72 | crypto_hash_sha512_final(our_state, &mut our_digest); 73 | 74 | assert_eq!(their_digest.as_ref(), our_digest); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | /// Errors generated by Dryoc. 4 | /// 5 | /// Most errors just contain a message as to what went wrong. 6 | /// I/O errors are forwarded through. 7 | #[derive(Debug)] 8 | pub enum Error { 9 | /// An internal Dryoc error. 10 | Message(String), 11 | 12 | /// Some I/O problem occurred. 13 | Io(std::io::Error), 14 | 15 | /// Unable to convert data from slice. 16 | FromSlice(core::array::TryFromSliceError), 17 | } 18 | 19 | impl From for Error { 20 | fn from(message: String) -> Self { 21 | Error::Message(message) 22 | } 23 | } 24 | 25 | impl From<&str> for Error { 26 | fn from(message: &str) -> Self { 27 | Error::Message(message.into()) 28 | } 29 | } 30 | 31 | impl From for Error { 32 | fn from(error: std::io::Error) -> Self { 33 | Error::Io(error) 34 | } 35 | } 36 | 37 | impl From for Error { 38 | fn from(error: core::array::TryFromSliceError) -> Self { 39 | Error::FromSlice(error) 40 | } 41 | } 42 | 43 | impl Display for Error { 44 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 45 | match self { 46 | Error::Message(message) => f.write_str(message), 47 | Error::Io(err) => write!(f, "I/O error: {}", err), 48 | Error::FromSlice(err) => write!(f, "From slice error: {}", err), 49 | } 50 | } 51 | } 52 | 53 | impl std::error::Error for Error { 54 | fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { 55 | match self { 56 | Error::Message(_) => None, 57 | Error::Io(err) => Some(err), 58 | Error::FromSlice(err) => Some(err), 59 | } 60 | } 61 | } 62 | 63 | macro_rules! dryoc_error { 64 | ($msg:expr) => {{ crate::error::Error::from(format!("{}, from {}:{}", $msg, file!(), line!())) }}; 65 | } 66 | 67 | macro_rules! validate { 68 | ($min:expr, $max:expr, $value:expr, $name:literal) => { 69 | if $value < $min { 70 | return Err(dryoc_error!(format!( 71 | "{} value of {} less than minimum {}", 72 | $name, $value, $min 73 | ))); 74 | } else if $value > $max { 75 | return Err(dryoc_error!(format!( 76 | "{} value of {} greater than minimum {}", 77 | $name, $value, $max 78 | ))); 79 | } 80 | }; 81 | } 82 | -------------------------------------------------------------------------------- /src/classic/crypto_box_impl.rs: -------------------------------------------------------------------------------- 1 | use zeroize::Zeroize; 2 | 3 | use super::crypto_core::crypto_scalarmult; 4 | use crate::classic::crypto_box::{PublicKey, SecretKey}; 5 | use crate::classic::crypto_core::crypto_core_hsalsa20; 6 | use crate::classic::crypto_hash::crypto_hash_sha512; 7 | use crate::classic::crypto_secretbox::Key; 8 | use crate::constants::{ 9 | CRYPTO_BOX_SEEDBYTES, CRYPTO_CORE_HSALSA20_INPUTBYTES, CRYPTO_CORE_HSALSA20_OUTPUTBYTES, 10 | CRYPTO_HASH_SHA512_BYTES, CRYPTO_SCALARMULT_BYTES, 11 | }; 12 | use crate::dryocstream::ByteArray; 13 | use crate::rng::copy_randombytes; 14 | use crate::scalarmult_curve25519::*; 15 | 16 | pub(crate) fn crypto_box_curve25519xsalsa20poly1305_beforenm( 17 | public_key: &PublicKey, 18 | secret_key: &SecretKey, 19 | ) -> Key { 20 | let mut s = [0u8; CRYPTO_SCALARMULT_BYTES]; 21 | crypto_scalarmult(&mut s, secret_key.as_array(), public_key.as_array()); 22 | 23 | let mut hash = [0u8; CRYPTO_CORE_HSALSA20_OUTPUTBYTES]; 24 | crypto_core_hsalsa20(&mut hash, &[0u8; CRYPTO_CORE_HSALSA20_INPUTBYTES], &s, None); 25 | 26 | hash 27 | } 28 | 29 | #[inline] 30 | pub(crate) fn crypto_box_curve25519xsalsa20poly1305_keypair_inplace( 31 | public_key: &mut PublicKey, 32 | secret_key: &mut SecretKey, 33 | ) { 34 | copy_randombytes(secret_key); 35 | crypto_scalarmult_curve25519_base(public_key, secret_key); 36 | } 37 | 38 | #[inline] 39 | pub(crate) fn crypto_box_curve25519xsalsa20poly1305_seed_keypair_inplace( 40 | public_key: &mut PublicKey, 41 | secret_key: &mut SecretKey, 42 | seed: &[u8], 43 | ) { 44 | let mut hash = [0u8; CRYPTO_HASH_SHA512_BYTES]; 45 | crypto_hash_sha512(&mut hash, seed); 46 | 47 | secret_key.copy_from_slice(&hash[0..CRYPTO_BOX_SEEDBYTES]); 48 | 49 | hash.zeroize(); 50 | 51 | crypto_scalarmult_curve25519_base(public_key, secret_key); 52 | } 53 | pub(crate) fn crypto_box_curve25519xsalsa20poly1305_keypair() -> (PublicKey, SecretKey) { 54 | let mut secret_key = SecretKey::default(); 55 | let mut public_key = PublicKey::default(); 56 | 57 | crypto_box_curve25519xsalsa20poly1305_keypair_inplace(&mut public_key, &mut secret_key); 58 | 59 | (public_key, secret_key) 60 | } 61 | 62 | pub(crate) fn crypto_box_curve25519xsalsa20poly1305_seed_keypair( 63 | seed: &[u8], 64 | ) -> (PublicKey, SecretKey) { 65 | let mut secret_key = [0u8; CRYPTO_BOX_SEEDBYTES]; 66 | let mut public_key = [0u8; CRYPTO_BOX_SEEDBYTES]; 67 | 68 | crypto_box_curve25519xsalsa20poly1305_seed_keypair_inplace( 69 | &mut public_key, 70 | &mut secret_key, 71 | seed, 72 | ); 73 | 74 | (public_key, secret_key) 75 | } 76 | -------------------------------------------------------------------------------- /src/classic/generichash_blake2b.rs: -------------------------------------------------------------------------------- 1 | use crate::blake2b; 2 | use crate::constants::{ 3 | CRYPTO_GENERICHASH_BLAKE2B_BYTES_MAX, CRYPTO_GENERICHASH_BLAKE2B_BYTES_MIN, 4 | CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MAX, CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MIN, 5 | CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES, CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES, 6 | }; 7 | use crate::error::Error; 8 | 9 | #[inline] 10 | pub(crate) fn crypto_generichash_blake2b_validate_key(key: Option<&[u8]>) -> Result<(), Error> { 11 | match key { 12 | Some(key) => { 13 | if key.len() < CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MIN 14 | || key.len() > CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MAX 15 | { 16 | return Err(dryoc_error!(format!( 17 | "key length is {}, should be at least {} and less than {} bytes", 18 | key.len(), 19 | CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MIN, 20 | CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MAX 21 | ))); 22 | } 23 | Ok(()) 24 | } 25 | None => Ok(()), 26 | } 27 | } 28 | 29 | #[inline] 30 | pub(crate) fn crypto_generichash_blake2b_validate_outlen(outlen: usize) -> Result<(), Error> { 31 | if !(CRYPTO_GENERICHASH_BLAKE2B_BYTES_MIN..=CRYPTO_GENERICHASH_BLAKE2B_BYTES_MAX) 32 | .contains(&outlen) 33 | { 34 | return Err(dryoc_error!(format!( 35 | "output length is {}, expected at least {} and less than {} bytes", 36 | outlen, CRYPTO_GENERICHASH_BLAKE2B_BYTES_MIN, CRYPTO_GENERICHASH_BLAKE2B_BYTES_MAX 37 | ))); 38 | } 39 | Ok(()) 40 | } 41 | 42 | #[inline] 43 | pub(crate) fn crypto_generichash_blake2b( 44 | output: &mut [u8], 45 | input: &[u8], 46 | key: Option<&[u8]>, 47 | ) -> Result<(), Error> { 48 | crypto_generichash_blake2b_validate_outlen(output.len())?; 49 | crypto_generichash_blake2b_validate_key(key)?; 50 | 51 | blake2b::hash(output, input, key) 52 | } 53 | 54 | #[inline] 55 | pub(crate) fn crypto_generichash_blake2b_init( 56 | key: Option<&[u8]>, 57 | outlen: usize, 58 | salt: Option<&[u8; CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES]>, 59 | personal: Option<&[u8; CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES]>, 60 | ) -> Result { 61 | crypto_generichash_blake2b_validate_outlen(outlen)?; 62 | crypto_generichash_blake2b_validate_key(key)?; 63 | 64 | blake2b::State::init(outlen as u8, key, salt, personal) 65 | } 66 | 67 | #[inline] 68 | pub(crate) fn crypto_generichash_blake2b_update(state: &mut blake2b::State, input: &[u8]) { 69 | state.update(input) 70 | } 71 | 72 | #[inline] 73 | pub(crate) fn crypto_generichash_blake2b_final( 74 | state: blake2b::State, 75 | output: &mut [u8], 76 | ) -> Result<(), Error> { 77 | state.finalize(output) 78 | } 79 | -------------------------------------------------------------------------------- /src/classic/crypto_shorthash.rs: -------------------------------------------------------------------------------- 1 | //! # Short-input hashing 2 | //! 3 | //! This module implements libsodium's short input hashing, based on 4 | //! SipHash-2-4. 5 | //! 6 | //! You may want to use short input hashing when: 7 | //! 8 | //! * you need to construct hash tables in a fashion that is collision resistant 9 | //! (i.e., it's hard for other parties to guess when there may be a hash key 10 | //! collision, which could lead to DoS or timing attacks) 11 | //! * you want to construct probabilistic data structures, such as bloom filters 12 | //! * you want to perform basic integrity checks on data 13 | //! * you have relatively short inputs 14 | //! 15 | //! The key used with this function should be treated as a secret. If used for 16 | //! constructing hash tables, it's recommended the table size be a prime number 17 | //! to ensure all bits from the output are used. 18 | //! 19 | //! For details, refer to [libsodium docs](https://libsodium.gitbook.io/doc/hashing/short-input_hashing). 20 | //! 21 | //! ## Classic API example 22 | //! 23 | //! ``` 24 | //! use dryoc::classic::crypto_shorthash::*; 25 | //! use dryoc::rng::copy_randombytes; 26 | //! 27 | //! // Generate a random key 28 | //! let key = crypto_shorthash_keygen(); 29 | //! 30 | //! // Generate some random input data 31 | //! let mut input = vec![0u8; 69]; 32 | //! copy_randombytes(&mut input); 33 | //! 34 | //! // Compute the hash, put result into `output` 35 | //! let mut output = Hash::default(); 36 | //! crypto_shorthash(&mut output, &input, &key); 37 | //! ``` 38 | use crate::constants::{CRYPTO_SHORTHASH_BYTES, CRYPTO_SHORTHASH_KEYBYTES}; 39 | use crate::rng::copy_randombytes; 40 | use crate::siphash24::siphash24; 41 | 42 | /// Hash type alias for short input hashing. 43 | pub type Hash = [u8; CRYPTO_SHORTHASH_BYTES]; 44 | /// Key type alias for short input hashing. 45 | pub type Key = [u8; CRYPTO_SHORTHASH_KEYBYTES]; 46 | 47 | /// Generates a random key for short input hashing. 48 | pub fn crypto_shorthash_keygen() -> Key { 49 | let mut key = Key::default(); 50 | copy_randombytes(&mut key); 51 | key 52 | } 53 | 54 | /// Computes a short input hash for `input` and `key`, placing the result into 55 | /// `output`, using SipHash-2-4. 56 | pub fn crypto_shorthash(output: &mut Hash, input: &[u8], key: &Key) { 57 | siphash24(output, input, key) 58 | } 59 | 60 | #[cfg(test)] 61 | mod tests { 62 | use rand::TryRngCore; 63 | 64 | use super::*; 65 | 66 | #[test] 67 | fn test_shorthash() { 68 | use rand_core::OsRng; 69 | use sodiumoxide::crypto::shorthash; 70 | 71 | for _ in 0..20 { 72 | let key = crypto_shorthash_keygen(); 73 | let mut input = vec![0u8; (OsRng.try_next_u32().unwrap() % 69) as usize]; 74 | copy_randombytes(&mut input); 75 | let mut output = Hash::default(); 76 | 77 | crypto_shorthash(&mut output, &input, &key); 78 | 79 | let so_output = shorthash::shorthash( 80 | &input, 81 | &shorthash::Key::from_slice(&key).expect("so key failed"), 82 | ); 83 | 84 | assert_eq!(output, so_output.0); 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/sha512.rs: -------------------------------------------------------------------------------- 1 | //! # SHA-512 hash algorithm 2 | //! 3 | //! Provides an implementation of the SHA-512 hash algorithm. 4 | //! 5 | //! ## Example 6 | //! 7 | //! ``` 8 | //! use dryoc::sha512::Sha512; 9 | //! 10 | //! let mut state = Sha512::new(); 11 | //! state.update(b"bytes"); 12 | //! let hash = state.finalize_to_vec(); 13 | //! ``` 14 | use generic_array::GenericArray; 15 | use generic_array::typenum::U64; 16 | use sha2::{Digest as DigestImpl, Sha512 as Sha512Impl}; 17 | 18 | use crate::constants::CRYPTO_HASH_SHA512_BYTES; 19 | use crate::types::*; 20 | 21 | /// Type alias for SHA512 digest, provided for convience. 22 | pub type Digest = StackByteArray; 23 | 24 | /// SHA-512 wrapper, provided for convience. 25 | pub struct Sha512 { 26 | hasher: Sha512Impl, 27 | } 28 | 29 | impl Sha512 { 30 | /// Returns a new SHA-512 hasher instance. 31 | pub fn new() -> Self { 32 | Self { 33 | hasher: Sha512Impl::new(), 34 | } 35 | } 36 | 37 | /// One-time interface to compute SHA-512 digest for `input`, copying result 38 | /// into `output`. 39 | pub fn compute_into_bytes< 40 | Input: Bytes + ?Sized, 41 | Output: MutByteArray, 42 | >( 43 | output: &mut Output, 44 | input: &Input, 45 | ) { 46 | let mut hasher = Self::new(); 47 | hasher.update(input); 48 | hasher.finalize_into_bytes(output) 49 | } 50 | 51 | /// One-time interface to compute SHA-512 digest for `input`. 52 | pub fn compute>( 53 | input: &Input, 54 | ) -> Output { 55 | let mut hasher = Self::new(); 56 | hasher.update(input); 57 | hasher.finalize() 58 | } 59 | 60 | /// Wrapper around [`Sha512::compute`], returning a [`Vec`]. Provided for 61 | /// convenience. 62 | pub fn compute_to_vec(input: &Input) -> Vec { 63 | Self::compute(input) 64 | } 65 | 66 | /// Updates SHA-512 hash state with `input`. 67 | pub fn update(&mut self, input: &Input) { 68 | self.hasher.update(input.as_slice()) 69 | } 70 | 71 | /// Consumes hasher and return final computed hash. 72 | pub fn finalize>(self) -> Output { 73 | let mut hash = Output::new_byte_array(); 74 | self.finalize_into_bytes(&mut hash); 75 | hash 76 | } 77 | 78 | /// Consumes hasher and writes final computed hash into `output`. 79 | pub fn finalize_into_bytes>( 80 | mut self, 81 | output: &mut Output, 82 | ) { 83 | let arr = GenericArray::<_, U64>::from_mut_slice(output.as_mut_slice()); 84 | self.hasher.finalize_into_reset(arr); 85 | } 86 | 87 | /// Consumes hasher and returns final computed hash as a [`Vec`]. 88 | pub fn finalize_to_vec(self) -> Vec { 89 | self.finalize() 90 | } 91 | } 92 | 93 | impl Default for Sha512 { 94 | fn default() -> Self { 95 | Self::new() 96 | } 97 | } 98 | 99 | #[cfg(test)] 100 | mod tests { 101 | use super::*; 102 | 103 | #[test] 104 | fn test_sha512() { 105 | use sodiumoxide::crypto::hash; 106 | 107 | use crate::rng::randombytes_buf; 108 | 109 | let mut their_state = hash::State::new(); 110 | let mut our_state = Sha512::new(); 111 | 112 | for _ in 0..10 { 113 | let r = randombytes_buf(64); 114 | their_state.update(&r); 115 | our_state.update(&r); 116 | } 117 | 118 | let their_digest = their_state.finalize(); 119 | let our_digest = our_state.finalize_to_vec(); 120 | 121 | assert_eq!(their_digest.as_ref(), our_digest); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/classic/crypto_kdf.rs: -------------------------------------------------------------------------------- 1 | //! # Key derivation function 2 | //! 3 | //! Implements libsodium's key derivation functions (`crypto_kdf_*`). 4 | //! 5 | //! For details, refer to [libsodium docs](https://doc.libsodium.org/key_derivation). 6 | //! 7 | //! # Classic API example 8 | //! 9 | //! ``` 10 | //! use base64::Engine as _; 11 | //! use base64::engine::general_purpose; 12 | //! use dryoc::classic::crypto_kdf::*; 13 | //! 14 | //! // Generate a random main key 15 | //! let main_key = crypto_kdf_keygen(); 16 | //! // Provide 8 bytes of context data, can be any data 17 | //! let context = b"hello123"; 18 | //! 19 | //! // Derive 20 subkeys 20 | //! for i in 0..20 { 21 | //! let mut key = Key::default(); 22 | //! crypto_kdf_derive_from_key(&mut key, i, context, &main_key).expect("kdf failed"); 23 | //! println!("Subkey {}: {}", i, general_purpose::STANDARD.encode(&key)); 24 | //! } 25 | //! ``` 26 | 27 | use crate::blake2b; 28 | use crate::constants::{ 29 | CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES, CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES, 30 | CRYPTO_KDF_BLAKE2B_BYTES_MAX, CRYPTO_KDF_BLAKE2B_BYTES_MIN, CRYPTO_KDF_CONTEXTBYTES, 31 | CRYPTO_KDF_KEYBYTES, 32 | }; 33 | use crate::error::Error; 34 | 35 | /// Key type for the main key used for deriving subkeys. 36 | pub type Key = [u8; CRYPTO_KDF_KEYBYTES]; 37 | /// Context for key derivation. 38 | pub type Context = [u8; CRYPTO_KDF_CONTEXTBYTES]; 39 | 40 | /// Generates a random key, suitable for use as a main key with 41 | /// [`crypto_kdf_derive_from_key`]. 42 | pub fn crypto_kdf_keygen() -> Key { 43 | use crate::rng::copy_randombytes; 44 | let mut key = Key::default(); 45 | copy_randombytes(&mut key); 46 | key 47 | } 48 | 49 | /// Derives `subkey` from `main_key`, using `context` and `subkey_id` such that 50 | /// `subkey` will always be the same for the given set of inputs, but `main_key` 51 | /// cannot be derived from `subkey`. 52 | pub fn crypto_kdf_derive_from_key( 53 | subkey: &mut [u8], 54 | subkey_id: u64, 55 | context: &Context, 56 | main_key: &Key, 57 | ) -> Result<(), Error> { 58 | if subkey.len() < CRYPTO_KDF_BLAKE2B_BYTES_MIN || subkey.len() > CRYPTO_KDF_BLAKE2B_BYTES_MAX { 59 | Err(dryoc_error!(format!( 60 | "invalid subkey length {}, should be at least {} and no more than {}", 61 | subkey.len(), 62 | CRYPTO_KDF_BLAKE2B_BYTES_MIN, 63 | CRYPTO_KDF_BLAKE2B_BYTES_MAX 64 | ))) 65 | } else { 66 | let mut ctx_padded = [0u8; CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES]; 67 | let mut salt = [0u8; CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES]; 68 | 69 | ctx_padded[..CRYPTO_KDF_CONTEXTBYTES].copy_from_slice(context); 70 | salt[..8].copy_from_slice(&subkey_id.to_le_bytes()); 71 | 72 | let state = blake2b::State::init( 73 | CRYPTO_KDF_KEYBYTES as u8, 74 | Some(main_key), 75 | Some(&salt), 76 | Some(&ctx_padded), 77 | )?; 78 | state.finalize(subkey) 79 | } 80 | } 81 | 82 | #[cfg(test)] 83 | mod tests { 84 | use super::*; 85 | 86 | #[test] 87 | fn test_derive_key() { 88 | use sodiumoxide::crypto::{kdf, secretbox}; 89 | let main_key = crypto_kdf_keygen(); 90 | let context = b"hello123"; 91 | 92 | for i in 0..20 { 93 | let mut key = Key::default(); 94 | crypto_kdf_derive_from_key(&mut key, i, context, &main_key).expect("kdf failed"); 95 | 96 | let mut so_key = secretbox::Key([0; secretbox::KEYBYTES]); 97 | kdf::derive_from_key( 98 | &mut so_key.0[..], 99 | i, 100 | *context, 101 | &kdf::blake2b::Key::from_slice(&main_key).expect("key failed"), 102 | ) 103 | .expect("so kdf failed"); 104 | 105 | assert_eq!(so_key.0, key); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/utils.rs: -------------------------------------------------------------------------------- 1 | /// Increments `bytes` in constant time, representing a large little-endian 2 | /// integer; equivalent to `sodium_increment`. 3 | #[inline] 4 | pub fn increment_bytes(bytes: &mut [u8]) { 5 | let mut carry: u16 = 1; 6 | for b in bytes { 7 | carry += *b as u16; 8 | *b = (carry & 0xff) as u8; 9 | carry >>= 8; 10 | } 11 | } 12 | 13 | /// Convenience wrapper for [`increment_bytes`]. Functionally equivalent to 14 | /// `sodium_increment`. 15 | pub fn sodium_increment(bytes: &mut [u8]) { 16 | increment_bytes(bytes) 17 | } 18 | 19 | #[inline] 20 | pub(crate) fn xor_buf(out: &mut [u8], in_: &[u8]) { 21 | let len = std::cmp::min(out.len(), in_.len()); 22 | for i in 0..len { 23 | out[i] ^= in_[i]; 24 | } 25 | } 26 | 27 | #[inline] 28 | pub(crate) fn load_u64_le(bytes: &[u8]) -> u64 { 29 | (bytes[0] as u64) 30 | | ((bytes[1] as u64) << 8) 31 | | ((bytes[2] as u64) << 16) 32 | | ((bytes[3] as u64) << 24) 33 | | ((bytes[4] as u64) << 32) 34 | | ((bytes[5] as u64) << 40) 35 | | ((bytes[6] as u64) << 48) 36 | | ((bytes[7] as u64) << 56) 37 | } 38 | 39 | #[inline] 40 | pub(crate) fn load_u32_le(bytes: &[u8]) -> u32 { 41 | (bytes[0] as u32) 42 | | ((bytes[1] as u32) << 8) 43 | | ((bytes[2] as u32) << 16) 44 | | ((bytes[3] as u32) << 24) 45 | } 46 | 47 | // #[inline] 48 | // pub(crate) fn load_i32_le(bytes: &[u8]) -> i32 { 49 | // (bytes[0] as i32) | (bytes[1] as i32) << 8 | (bytes[2] as i32) << 16 | 50 | // (bytes[3] as i32) << 24 } 51 | 52 | #[inline] 53 | pub(crate) fn rotr64(x: u64, b: u64) -> u64 { 54 | (x >> b) | (x << (64 - b)) 55 | } 56 | 57 | #[inline] 58 | pub(crate) fn pad16(n: usize) -> usize { 59 | (0x10 - (n % 16)) & 0xf 60 | } 61 | 62 | #[cfg(test)] 63 | mod tests { 64 | use rand::TryRngCore; 65 | 66 | use super::*; 67 | 68 | #[test] 69 | fn test_increment_bytes() { 70 | let mut b = [0]; 71 | 72 | increment_bytes(&mut b); 73 | assert_eq!(b, [1]); 74 | increment_bytes(&mut b); 75 | assert_eq!(b, [2]); 76 | 77 | let mut b = [0xff]; 78 | 79 | increment_bytes(&mut b); 80 | assert_eq!(b, [0]); 81 | increment_bytes(&mut b); 82 | assert_eq!(b, [1]); 83 | 84 | let mut b = [0xff, 0]; 85 | 86 | increment_bytes(&mut b); 87 | assert_eq!(b, [0, 1]); 88 | increment_bytes(&mut b); 89 | assert_eq!(b, [1, 1]); 90 | increment_bytes(&mut b); 91 | assert_eq!(b, [2, 1]); 92 | } 93 | 94 | #[test] 95 | fn test_xor_buf() { 96 | let mut a = [0]; 97 | let b = [0]; 98 | 99 | xor_buf(&mut a, &b); 100 | assert_eq!([0], a); 101 | 102 | let mut a = [1]; 103 | let b = [0]; 104 | 105 | xor_buf(&mut a, &b); 106 | assert_eq!([1], a); 107 | 108 | let mut a = [1, 1, 1]; 109 | let b = [0]; 110 | 111 | xor_buf(&mut a, &b); 112 | assert_eq!([1, 1, 1], a); 113 | 114 | let mut a = [1, 1, 1]; 115 | let b = [0]; 116 | 117 | xor_buf(&mut a, &b); 118 | assert_eq!([1, 1, 1], a); 119 | 120 | let mut a = [1, 1, 1]; 121 | let b = [0, 1, 1]; 122 | 123 | xor_buf(&mut a, &b); 124 | assert_eq!([1, 0, 0], a); 125 | } 126 | 127 | #[test] 128 | fn test_sodium_increment() { 129 | use libsodium_sys::sodium_increment as so_sodium_increment; 130 | use rand_core::OsRng; 131 | 132 | use crate::rng::copy_randombytes; 133 | 134 | for _ in 0..20 { 135 | let rand_usize = (OsRng.try_next_u32().unwrap() % 1000) as usize; 136 | let mut data = vec![0u8; rand_usize]; 137 | copy_randombytes(&mut data); 138 | 139 | let mut data_copy = data.clone(); 140 | 141 | sodium_increment(&mut data); 142 | 143 | unsafe { so_sodium_increment(data_copy.as_mut_ptr(), data_copy.len()) }; 144 | 145 | assert_eq!(data, data_copy); 146 | } 147 | } 148 | 149 | #[test] 150 | fn test_pad16() { 151 | assert_eq!(pad16(0), 0); 152 | assert_eq!(pad16(1), 15); 153 | assert_eq!(pad16(2), 14); 154 | assert_eq!(pad16(15), 1); 155 | assert_eq!(pad16(16), 0); 156 | assert_eq!(pad16(17), 15); 157 | assert_eq!(pad16(32), 0); 158 | assert_eq!(pad16(33), 15); 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/kdf.rs: -------------------------------------------------------------------------------- 1 | //! # Key derivation functions 2 | //! 3 | //! [`Kdf`] implements libsodium's key derivation functions, based on the 4 | //! Blake2b hash function. 5 | //! 6 | //! You should use [`Kdf`] when you want to: 7 | //! 8 | //! * create many subkeys from a main key, without having to risk leaking the 9 | //! main key 10 | //! * ensure that if a subkey were to become compromised, one could not derive 11 | //! the main key 12 | //! 13 | //! # Rustaceous API example 14 | //! 15 | //! ``` 16 | //! use base64::Engine as _; 17 | //! use base64::engine::general_purpose; 18 | //! use dryoc::kdf::*; 19 | //! 20 | //! // Randomly generate a main key and context, using the default stack-allocated 21 | //! // types 22 | //! let key = Kdf::gen_with_defaults(); 23 | //! let subkey_id = 0; 24 | //! 25 | //! let subkey = key.derive_subkey_to_vec(subkey_id).expect("derive failed"); 26 | //! println!( 27 | //! "Subkey {}: {}", 28 | //! subkey_id, 29 | //! general_purpose::STANDARD.encode(&subkey) 30 | //! ); 31 | //! ``` 32 | //! 33 | //! ## Additional resources 34 | //! 35 | //! * See for additional details on 36 | //! key derivation 37 | 38 | #[cfg(feature = "serde")] 39 | use serde::{Deserialize, Serialize}; 40 | use zeroize::Zeroize; 41 | 42 | use crate::classic::crypto_kdf::crypto_kdf_derive_from_key; 43 | use crate::constants::{CRYPTO_KDF_CONTEXTBYTES, CRYPTO_KDF_KEYBYTES}; 44 | use crate::error::Error; 45 | use crate::types::*; 46 | 47 | /// Stack-allocated key type alias for key derivation with [`Kdf`]. 48 | pub type Key = StackByteArray; 49 | /// Stack-allocated context type alias for key derivation with [`Kdf`]. 50 | pub type Context = StackByteArray; 51 | 52 | #[cfg_attr( 53 | feature = "serde", 54 | derive(Zeroize, Clone, Debug, Serialize, Deserialize) 55 | )] 56 | #[cfg_attr(not(feature = "serde"), derive(Zeroize, Clone, Debug))] 57 | /// Key derivation implementation based on Blake2b, compatible with libsodium's 58 | /// `crypto_kdf_*` functions. 59 | pub struct Kdf< 60 | Key: ByteArray + Zeroize, 61 | Context: ByteArray + Zeroize, 62 | > { 63 | main_key: Key, 64 | context: Context, 65 | } 66 | 67 | /// Stack-allocated type alias for [`Kdf`]. Provided for convenience. 68 | pub type StackKdf = Kdf; 69 | 70 | #[cfg(any(feature = "nightly", all(doc, not(doctest))))] 71 | #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))] 72 | pub mod protected { 73 | //! # Protected memory type aliases for [`Kdf`] 74 | //! 75 | //! This mod provides re-exports of type aliases for protected memory usage 76 | //! with [`Kdf`]. These type aliases are provided for 77 | //! convenience. 78 | //! 79 | //! ## Example 80 | //! 81 | //! ``` 82 | //! use base64::Engine as _; 83 | //! use base64::engine::general_purpose; 84 | //! use dryoc::kdf::Kdf; 85 | //! use dryoc::kdf::protected::*; 86 | //! 87 | //! // Randomly generate a main key and context, using locked memory 88 | //! let key: LockedKdf = Kdf::gen(); 89 | //! let subkey_id = 0; 90 | //! 91 | //! let subkey: Locked = key.derive_subkey(subkey_id).expect("derive failed"); 92 | //! println!( 93 | //! "Subkey {}: {}", 94 | //! subkey_id, 95 | //! general_purpose::STANDARD.encode(&subkey) 96 | //! ); 97 | //! ``` 98 | use super::*; 99 | pub use crate::protected::*; 100 | 101 | /// Heap-allocated, page-aligned key type alias for key derivation with 102 | /// [`Kdf`]. 103 | pub type Key = HeapByteArray; 104 | /// Heap-allocated, page-aligned context type alias for key derivation with 105 | /// [`Kdf`]. 106 | pub type Context = HeapByteArray; 107 | 108 | /// Locked [`Kdf`], provided as a type alias for convenience. 109 | pub type LockedKdf = Kdf, Locked>; 110 | } 111 | 112 | impl< 113 | Key: NewByteArray + Zeroize, 114 | Context: NewByteArray + Zeroize, 115 | > Kdf 116 | { 117 | /// Randomly generates a new pair of main key and context. 118 | pub fn gen() -> Self { 119 | Self { 120 | main_key: Key::gen(), 121 | context: Context::gen(), 122 | } 123 | } 124 | } 125 | 126 | impl< 127 | Key: ByteArray + Zeroize, 128 | Context: ByteArray + Zeroize, 129 | > Kdf 130 | { 131 | /// Derives a subkey for `subkey_id`, returning it. 132 | pub fn derive_subkey>( 133 | &self, 134 | subkey_id: u64, 135 | ) -> Result { 136 | let mut subkey = Subkey::new_byte_array(); 137 | crypto_kdf_derive_from_key( 138 | subkey.as_mut_array(), 139 | subkey_id, 140 | self.context.as_array(), 141 | self.main_key.as_array(), 142 | )?; 143 | Ok(subkey) 144 | } 145 | 146 | /// Derives a subkey for `subkey_id`, returning it as a [`Vec`]. Provided 147 | /// for convenience. 148 | pub fn derive_subkey_to_vec(&self, subkey_id: u64) -> Result, Error> { 149 | self.derive_subkey(subkey_id) 150 | } 151 | 152 | /// Constructs a new instance from `key` and `context`, consuming them both. 153 | pub fn from_parts(main_key: Key, context: Context) -> Self { 154 | Self { main_key, context } 155 | } 156 | 157 | /// Moves the key and context out of this instance, returning them as a 158 | /// tuple. 159 | pub fn into_parts(self) -> (Key, Context) { 160 | (self.main_key, self.context) 161 | } 162 | } 163 | 164 | impl Kdf { 165 | /// Randomly generates a new pair of main key and context. 166 | pub fn gen_with_defaults() -> Self { 167 | Self { 168 | main_key: Key::gen(), 169 | context: Context::gen(), 170 | } 171 | } 172 | } 173 | 174 | #[cfg(test)] 175 | mod tests { 176 | use super::*; 177 | 178 | #[test] 179 | fn test_kdf() { 180 | let key = StackKdf::gen(); 181 | 182 | let _subkey = key.derive_subkey_to_vec(0).expect("derive failed"); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/siphash24.rs: -------------------------------------------------------------------------------- 1 | use crate::constants::{CRYPTO_SHORTHASH_SIPHASH24_BYTES, CRYPTO_SHORTHASH_SIPHASH24_KEYBYTES}; 2 | use crate::utils::load_u64_le; 3 | 4 | pub(crate) type Hash = [u8; CRYPTO_SHORTHASH_SIPHASH24_BYTES]; 5 | pub(crate) type Key = [u8; CRYPTO_SHORTHASH_SIPHASH24_KEYBYTES]; 6 | 7 | fn rotl64(x: u64, b: u64) -> u64 { 8 | (x << b) | (x >> (64 - b)) 9 | } 10 | 11 | pub(crate) fn siphash24(output: &mut Hash, input: &[u8], key: &Key) { 12 | // "somepseudorandomlygeneratedbytes" 13 | let mut v0 = 0x736f6d6570736575u64; 14 | let mut v1 = 0x646f72616e646f6du64; 15 | let mut v2 = 0x6c7967656e657261u64; 16 | let mut v3 = 0x7465646279746573u64; 17 | 18 | let k0 = load_u64_le(&key[..8]); 19 | let k1 = load_u64_le(&key[8..]); 20 | 21 | v3 ^= k1; 22 | v2 ^= k0; 23 | v1 ^= k1; 24 | v0 ^= k0; 25 | 26 | let round = |v0: &mut u64, v1: &mut u64, v2: &mut u64, v3: &mut u64| { 27 | *v0 = v0.wrapping_add(*v1); 28 | *v1 = rotl64(*v1, 13); 29 | *v1 ^= *v0; 30 | *v0 = rotl64(*v0, 32); 31 | *v2 = v2.wrapping_add(*v3); 32 | *v3 = rotl64(*v3, 16); 33 | *v3 ^= *v2; 34 | *v0 = v0.wrapping_add(*v3); 35 | *v3 = rotl64(*v3, 21); 36 | *v3 ^= *v0; 37 | *v2 = v2.wrapping_add(*v1); 38 | *v1 = rotl64(*v1, 17); 39 | *v1 ^= *v2; 40 | *v2 = rotl64(*v2, 32); 41 | }; 42 | 43 | for chunk in input.chunks_exact(8) { 44 | let m = load_u64_le(chunk); 45 | v3 ^= m; 46 | round(&mut v0, &mut v1, &mut v2, &mut v3); 47 | round(&mut v0, &mut v1, &mut v2, &mut v3); 48 | v0 ^= m; 49 | } 50 | 51 | let mut b = (input.len() as u64) << 56; 52 | 53 | let remainder = input.chunks_exact(8).remainder(); 54 | 55 | for i in (0..remainder.len()).rev() { 56 | b |= (remainder[i] as u64) << (i * 8); 57 | } 58 | 59 | v3 ^= b; 60 | round(&mut v0, &mut v1, &mut v2, &mut v3); 61 | round(&mut v0, &mut v1, &mut v2, &mut v3); 62 | v0 ^= b; 63 | v2 ^= 0xff; 64 | round(&mut v0, &mut v1, &mut v2, &mut v3); 65 | round(&mut v0, &mut v1, &mut v2, &mut v3); 66 | round(&mut v0, &mut v1, &mut v2, &mut v3); 67 | round(&mut v0, &mut v1, &mut v2, &mut v3); 68 | b = v0 ^ v1 ^ v2 ^ v3; 69 | 70 | output.copy_from_slice(&b.to_le_bytes()); 71 | } 72 | 73 | #[cfg(test)] 74 | mod tests { 75 | use super::*; 76 | 77 | #[test] 78 | fn test_siphash24() { 79 | let vectors: [[u8; 8]; 64] = [ 80 | [0x31, 0x0e, 0x0e, 0xdd, 0x47, 0xdb, 0x6f, 0x72], 81 | [0xfd, 0x67, 0xdc, 0x93, 0xc5, 0x39, 0xf8, 0x74], 82 | [0x5a, 0x4f, 0xa9, 0xd9, 0x09, 0x80, 0x6c, 0x0d], 83 | [0x2d, 0x7e, 0xfb, 0xd7, 0x96, 0x66, 0x67, 0x85], 84 | [0xb7, 0x87, 0x71, 0x27, 0xe0, 0x94, 0x27, 0xcf], 85 | [0x8d, 0xa6, 0x99, 0xcd, 0x64, 0x55, 0x76, 0x18], 86 | [0xce, 0xe3, 0xfe, 0x58, 0x6e, 0x46, 0xc9, 0xcb], 87 | [0x37, 0xd1, 0x01, 0x8b, 0xf5, 0x00, 0x02, 0xab], 88 | [0x62, 0x24, 0x93, 0x9a, 0x79, 0xf5, 0xf5, 0x93], 89 | [0xb0, 0xe4, 0xa9, 0x0b, 0xdf, 0x82, 0x00, 0x9e], 90 | [0xf3, 0xb9, 0xdd, 0x94, 0xc5, 0xbb, 0x5d, 0x7a], 91 | [0xa7, 0xad, 0x6b, 0x22, 0x46, 0x2f, 0xb3, 0xf4], 92 | [0xfb, 0xe5, 0x0e, 0x86, 0xbc, 0x8f, 0x1e, 0x75], 93 | [0x90, 0x3d, 0x84, 0xc0, 0x27, 0x56, 0xea, 0x14], 94 | [0xee, 0xf2, 0x7a, 0x8e, 0x90, 0xca, 0x23, 0xf7], 95 | [0xe5, 0x45, 0xbe, 0x49, 0x61, 0xca, 0x29, 0xa1], 96 | [0xdb, 0x9b, 0xc2, 0x57, 0x7f, 0xcc, 0x2a, 0x3f], 97 | [0x94, 0x47, 0xbe, 0x2c, 0xf5, 0xe9, 0x9a, 0x69], 98 | [0x9c, 0xd3, 0x8d, 0x96, 0xf0, 0xb3, 0xc1, 0x4b], 99 | [0xbd, 0x61, 0x79, 0xa7, 0x1d, 0xc9, 0x6d, 0xbb], 100 | [0x98, 0xee, 0xa2, 0x1a, 0xf2, 0x5c, 0xd6, 0xbe], 101 | [0xc7, 0x67, 0x3b, 0x2e, 0xb0, 0xcb, 0xf2, 0xd0], 102 | [0x88, 0x3e, 0xa3, 0xe3, 0x95, 0x67, 0x53, 0x93], 103 | [0xc8, 0xce, 0x5c, 0xcd, 0x8c, 0x03, 0x0c, 0xa8], 104 | [0x94, 0xaf, 0x49, 0xf6, 0xc6, 0x50, 0xad, 0xb8], 105 | [0xea, 0xb8, 0x85, 0x8a, 0xde, 0x92, 0xe1, 0xbc], 106 | [0xf3, 0x15, 0xbb, 0x5b, 0xb8, 0x35, 0xd8, 0x17], 107 | [0xad, 0xcf, 0x6b, 0x07, 0x63, 0x61, 0x2e, 0x2f], 108 | [0xa5, 0xc9, 0x1d, 0xa7, 0xac, 0xaa, 0x4d, 0xde], 109 | [0x71, 0x65, 0x95, 0x87, 0x66, 0x50, 0xa2, 0xa6], 110 | [0x28, 0xef, 0x49, 0x5c, 0x53, 0xa3, 0x87, 0xad], 111 | [0x42, 0xc3, 0x41, 0xd8, 0xfa, 0x92, 0xd8, 0x32], 112 | [0xce, 0x7c, 0xf2, 0x72, 0x2f, 0x51, 0x27, 0x71], 113 | [0xe3, 0x78, 0x59, 0xf9, 0x46, 0x23, 0xf3, 0xa7], 114 | [0x38, 0x12, 0x05, 0xbb, 0x1a, 0xb0, 0xe0, 0x12], 115 | [0xae, 0x97, 0xa1, 0x0f, 0xd4, 0x34, 0xe0, 0x15], 116 | [0xb4, 0xa3, 0x15, 0x08, 0xbe, 0xff, 0x4d, 0x31], 117 | [0x81, 0x39, 0x62, 0x29, 0xf0, 0x90, 0x79, 0x02], 118 | [0x4d, 0x0c, 0xf4, 0x9e, 0xe5, 0xd4, 0xdc, 0xca], 119 | [0x5c, 0x73, 0x33, 0x6a, 0x76, 0xd8, 0xbf, 0x9a], 120 | [0xd0, 0xa7, 0x04, 0x53, 0x6b, 0xa9, 0x3e, 0x0e], 121 | [0x92, 0x59, 0x58, 0xfc, 0xd6, 0x42, 0x0c, 0xad], 122 | [0xa9, 0x15, 0xc2, 0x9b, 0xc8, 0x06, 0x73, 0x18], 123 | [0x95, 0x2b, 0x79, 0xf3, 0xbc, 0x0a, 0xa6, 0xd4], 124 | [0xf2, 0x1d, 0xf2, 0xe4, 0x1d, 0x45, 0x35, 0xf9], 125 | [0x87, 0x57, 0x75, 0x19, 0x04, 0x8f, 0x53, 0xa9], 126 | [0x10, 0xa5, 0x6c, 0xf5, 0xdf, 0xcd, 0x9a, 0xdb], 127 | [0xeb, 0x75, 0x09, 0x5c, 0xcd, 0x98, 0x6c, 0xd0], 128 | [0x51, 0xa9, 0xcb, 0x9e, 0xcb, 0xa3, 0x12, 0xe6], 129 | [0x96, 0xaf, 0xad, 0xfc, 0x2c, 0xe6, 0x66, 0xc7], 130 | [0x72, 0xfe, 0x52, 0x97, 0x5a, 0x43, 0x64, 0xee], 131 | [0x5a, 0x16, 0x45, 0xb2, 0x76, 0xd5, 0x92, 0xa1], 132 | [0xb2, 0x74, 0xcb, 0x8e, 0xbf, 0x87, 0x87, 0x0a], 133 | [0x6f, 0x9b, 0xb4, 0x20, 0x3d, 0xe7, 0xb3, 0x81], 134 | [0xea, 0xec, 0xb2, 0xa3, 0x0b, 0x22, 0xa8, 0x7f], 135 | [0x99, 0x24, 0xa4, 0x3c, 0xc1, 0x31, 0x57, 0x24], 136 | [0xbd, 0x83, 0x8d, 0x3a, 0xaf, 0xbf, 0x8d, 0xb7], 137 | [0x0b, 0x1a, 0x2a, 0x32, 0x65, 0xd5, 0x1a, 0xea], 138 | [0x13, 0x50, 0x79, 0xa3, 0x23, 0x1c, 0xe6, 0x60], 139 | [0x93, 0x2b, 0x28, 0x46, 0xe4, 0xd7, 0x06, 0x66], 140 | [0xe1, 0x91, 0x5f, 0x5c, 0xb1, 0xec, 0xa4, 0x6c], 141 | [0xf3, 0x25, 0x96, 0x5c, 0xa1, 0x6d, 0x62, 0x9f], 142 | [0x57, 0x5f, 0xf2, 0x8e, 0x60, 0x38, 0x1b, 0xe5], 143 | [0x72, 0x45, 0x06, 0xeb, 0x4c, 0x32, 0x8a, 0x95], 144 | ]; 145 | 146 | let mut key = Key::default(); 147 | for (i, item) in key.iter_mut().enumerate() { 148 | *item = i as u8; 149 | } 150 | 151 | for (i, item) in vectors.iter().enumerate() { 152 | let mut input = vec![0u8; i]; 153 | for (j, item) in input.iter_mut().enumerate().take(i) { 154 | *item = j as u8; 155 | } 156 | 157 | let mut output = Hash::default(); 158 | 159 | siphash24(&mut output, &input, &key); 160 | 161 | assert_eq!(output, *item); 162 | } 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /src/classic/crypto_auth.rs: -------------------------------------------------------------------------------- 1 | //! # Secret-key authentication 2 | //! 3 | //! Implements secret-key authentication using HMAC-SHA512-256, compatible 4 | //! with libsodium's `crypto_auth_*` functions. 5 | //! 6 | //! # Classic API single-part example 7 | //! 8 | //! ``` 9 | //! use dryoc::classic::crypto_auth::{Mac, crypto_auth, crypto_auth_keygen, crypto_auth_verify}; 10 | //! 11 | //! let key = crypto_auth_keygen(); 12 | //! let mut mac = Mac::default(); 13 | //! 14 | //! crypto_auth(&mut mac, b"Data to authenticate", &key); 15 | //! 16 | //! // This should be valid 17 | //! crypto_auth_verify(&mac, b"Data to authenticate", &key).expect("failed to authenticate"); 18 | //! 19 | //! // This should not be valid 20 | //! crypto_auth_verify(&mac, b"Invalid data", &key).expect_err("should not authenticate"); 21 | //! ``` 22 | //! 23 | //! # Classic API multi-part example 24 | //! 25 | //! ``` 26 | //! use dryoc::classic::crypto_auth::{ 27 | //! Mac, crypto_auth_final, crypto_auth_init, crypto_auth_keygen, crypto_auth_update, 28 | //! crypto_auth_verify, 29 | //! }; 30 | //! 31 | //! let key = crypto_auth_keygen(); 32 | //! let mut mac = Mac::default(); 33 | //! 34 | //! let mut state = crypto_auth_init(&key); 35 | //! crypto_auth_update(&mut state, b"Multi-part"); 36 | //! crypto_auth_update(&mut state, b"data"); 37 | //! crypto_auth_final(state, &mut mac); 38 | //! 39 | //! // This should be valid 40 | //! crypto_auth_verify(&mac, b"Multi-partdata", &key).expect("failed to authenticate"); 41 | //! 42 | //! // This should not be valid 43 | //! crypto_auth_verify(&mac, b"Invalid data", &key).expect_err("should not authenticate"); 44 | //! ``` 45 | use subtle::ConstantTimeEq; 46 | 47 | use crate::constants::{CRYPTO_AUTH_BYTES, CRYPTO_AUTH_HMACSHA512256_BYTES, CRYPTO_AUTH_KEYBYTES}; 48 | use crate::error::Error; 49 | use crate::sha512::Sha512; 50 | use crate::types::*; 51 | 52 | struct HmacSha512State { 53 | octx: Sha512, 54 | ictx: Sha512, 55 | } 56 | 57 | /// Key for secret-key message authentication. 58 | pub type Key = [u8; CRYPTO_AUTH_KEYBYTES]; 59 | /// Message authentication code type for use with secret-key authentication. 60 | pub type Mac = [u8; CRYPTO_AUTH_BYTES]; 61 | 62 | fn crypto_auth_hmacsha512256(output: &mut Mac, message: &[u8], key: &Key) { 63 | let mut state = crypto_auth_hmacsha512256_init(key); 64 | crypto_auth_hmacsha512256_update(&mut state, message); 65 | crypto_auth_hmacsha512256_final(state, output); 66 | } 67 | 68 | fn crypto_auth_hmacsha512256_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> { 69 | let mut computed_mac = Mac::default(); 70 | crypto_auth_hmacsha512256(&mut computed_mac, input, key); 71 | if mac.ct_eq(&computed_mac).unwrap_u8() == 1 { 72 | Ok(()) 73 | } else { 74 | Err(dryoc_error!("authentication codes do not match")) 75 | } 76 | } 77 | 78 | fn crypto_auth_hmacsha512256_init(key: &[u8]) -> HmacSha512State { 79 | let mut pad = [0x36u8; 128]; 80 | let mut khash = [0u8; 64]; 81 | let keylen = key.len(); 82 | 83 | let key = if keylen > 128 { 84 | Sha512::compute_into_bytes(&mut khash, key); 85 | &khash 86 | } else { 87 | key 88 | }; 89 | 90 | let mut ictx = Sha512::new(); 91 | for i in 0..keylen { 92 | pad[i] ^= key[i] 93 | } 94 | ictx.update(&pad); 95 | 96 | let mut octx = Sha512::new(); 97 | pad.fill(0x5c); 98 | for i in 0..keylen { 99 | pad[i] ^= key[i] 100 | } 101 | octx.update(&pad); 102 | 103 | HmacSha512State { octx, ictx } 104 | } 105 | 106 | fn crypto_auth_hmacsha512256_update(state: &mut HmacSha512State, input: &[u8]) { 107 | state.ictx.update(input) 108 | } 109 | fn crypto_auth_hmacsha512256_final( 110 | mut state: HmacSha512State, 111 | output: &mut [u8; CRYPTO_AUTH_HMACSHA512256_BYTES], 112 | ) { 113 | let mut ihash = [0u8; 64]; 114 | state.ictx.finalize_into_bytes(&mut ihash); 115 | state.octx.update(&ihash); 116 | state.octx.finalize_into_bytes(&mut ihash); 117 | output.copy_from_slice(&ihash[..CRYPTO_AUTH_HMACSHA512256_BYTES]) 118 | } 119 | 120 | /// Authenticates `message` using `key`, and places the result into 121 | /// `mac`. 122 | /// 123 | /// Equivalent to libsodium's `crypto_auth`. 124 | pub fn crypto_auth(mac: &mut Mac, message: &[u8], key: &Key) { 125 | crypto_auth_hmacsha512256(mac, message, key) 126 | } 127 | 128 | /// Verifies that `mac` is the correct authenticator for `message` using `key`. 129 | /// Returns `Ok(())` if the message authentication code is valid. 130 | /// 131 | /// Equivalent to libsodium's `crypto_auth_verify`. 132 | pub fn crypto_auth_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> { 133 | crypto_auth_hmacsha512256_verify(mac, input, key) 134 | } 135 | 136 | /// Internal state for [`crypto_auth`]. 137 | pub struct AuthState { 138 | state: HmacSha512State, 139 | } 140 | 141 | /// Generates a random key using 142 | /// [`copy_randombytes`](crate::rng::copy_randombytes), suitable for use with 143 | /// [`crypto_auth_init`] and [`crypto_auth`]. 144 | /// 145 | /// Equivalent to libsodium's `crypto_auth_keygen`. 146 | pub fn crypto_auth_keygen() -> Key { 147 | Key::gen() 148 | } 149 | 150 | /// Initialize the incremental interface for HMAC-SHA512-256 secret-key. 151 | /// 152 | /// Initializes the incremental interface for HMAC-SHA512-256 secret-key 153 | /// authentication, using `key`. Returns a state struct which is required for 154 | /// subsequent calls to [`crypto_auth_update`] and 155 | /// [`crypto_auth_final`]. 156 | pub fn crypto_auth_init(key: &Key) -> AuthState { 157 | AuthState { 158 | state: crypto_auth_hmacsha512256_init(key), 159 | } 160 | } 161 | 162 | /// Updates `state` for the secret-key authentication function, based on 163 | /// `input`. 164 | pub fn crypto_auth_update(state: &mut AuthState, input: &[u8]) { 165 | crypto_auth_hmacsha512256_update(&mut state.state, input) 166 | } 167 | 168 | /// Finalizes the message authentication code for `state`, and places the result 169 | /// into `output`. 170 | pub fn crypto_auth_final(state: AuthState, output: &mut [u8; CRYPTO_AUTH_BYTES]) { 171 | crypto_auth_hmacsha512256_final(state.state, output) 172 | } 173 | 174 | #[cfg(test)] 175 | mod tests { 176 | use rand::TryRngCore; 177 | 178 | use super::*; 179 | 180 | #[test] 181 | fn test_crypto_auth() { 182 | use rand_core::OsRng; 183 | use sodiumoxide::crypto::auth; 184 | use sodiumoxide::crypto::auth::Key as SOKey; 185 | 186 | use crate::rng::copy_randombytes; 187 | 188 | for _ in 0..20 { 189 | let mlen = (OsRng.try_next_u32().unwrap() % 5000) as usize; 190 | let mut message = vec![0u8; mlen]; 191 | copy_randombytes(&mut message); 192 | let key = crypto_auth_keygen(); 193 | 194 | let so_tag = 195 | auth::authenticate(&message, &SOKey::from_slice(&key).expect("key failed")); 196 | 197 | let mut mac = Mac::new_byte_array(); 198 | crypto_auth(&mut mac, &message, &key); 199 | 200 | assert_eq!(mac, so_tag.0); 201 | 202 | crypto_auth_verify(&mac, &message, &key).expect("verify failed"); 203 | crypto_auth_verify(&mac, b"invalid message", &key) 204 | .expect_err("verify should have failed"); 205 | } 206 | } 207 | } 208 | -------------------------------------------------------------------------------- /src/classic/crypto_kx.rs: -------------------------------------------------------------------------------- 1 | //! # Key exchange 2 | //! 3 | //! This module implements libsodium's key exchange functions, which uses a 4 | //! combination of Curve25519, Diffie-Hellman, and Blake2b to generate shared 5 | //! session keys. 6 | //! 7 | //! ## Classic API example 8 | //! 9 | //! ``` 10 | //! use dryoc::classic::crypto_kx::*; 11 | //! 12 | //! // Generate random client & server keypairs 13 | //! let (client_pk, client_sk) = crypto_kx_keypair(); 14 | //! let (server_pk, server_sk) = crypto_kx_keypair(); 15 | //! 16 | //! // Variables for client & server rx/tx session keys 17 | //! let (mut crx, mut ctx, mut srx, mut stx) = ( 18 | //! SessionKey::default(), 19 | //! SessionKey::default(), 20 | //! SessionKey::default(), 21 | //! SessionKey::default(), 22 | //! ); 23 | //! 24 | //! // Calculate the client Rx & Tx keys 25 | //! crypto_kx_client_session_keys(&mut crx, &mut ctx, &client_pk, &client_sk, &server_pk) 26 | //! .expect("client kx failed"); 27 | //! 28 | //! // Calculate the server Rx & Tx keys 29 | //! crypto_kx_server_session_keys(&mut srx, &mut stx, &server_pk, &server_sk, &client_pk) 30 | //! .expect("server kx failed"); 31 | //! 32 | //! assert_eq!(crx, stx); 33 | //! assert_eq!(ctx, srx); 34 | //! ``` 35 | 36 | use zeroize::Zeroize; 37 | 38 | use super::crypto_core::{crypto_scalarmult, crypto_scalarmult_base}; 39 | use super::crypto_generichash::{ 40 | crypto_generichash, crypto_generichash_final, crypto_generichash_init, 41 | crypto_generichash_update, 42 | }; 43 | use crate::constants::{ 44 | CRYPTO_KX_PUBLICKEYBYTES, CRYPTO_KX_SECRETKEYBYTES, CRYPTO_KX_SEEDBYTES, 45 | CRYPTO_KX_SESSIONKEYBYTES, CRYPTO_SCALARMULT_BYTES, 46 | }; 47 | use crate::error::Error; 48 | use crate::types::*; 49 | 50 | /// Public key type for key exchange 51 | pub type PublicKey = [u8; CRYPTO_KX_PUBLICKEYBYTES]; 52 | /// Secret key type for key exchange 53 | pub type SecretKey = [u8; CRYPTO_KX_SECRETKEYBYTES]; 54 | /// Session data type for key exchange 55 | pub type SessionKey = [u8; CRYPTO_KX_SESSIONKEYBYTES]; 56 | 57 | /// Computes and returns a keypair of `(PublicKey, SecretKey)` based on `seed` 58 | /// upon success. Uses the Blake2b function to derive a secret from `seed`. 59 | /// 60 | /// Compatible with libsodium's `crypto_kx_seed_keypair`. 61 | pub fn crypto_kx_seed_keypair( 62 | seed: &[u8; CRYPTO_KX_SEEDBYTES], 63 | ) -> Result<(PublicKey, SecretKey), Error> { 64 | let mut sk = SecretKey::default(); 65 | let mut pk = PublicKey::default(); 66 | 67 | crypto_generichash(&mut sk, seed, None)?; 68 | 69 | crypto_scalarmult_base(&mut pk, &sk); 70 | 71 | Ok((pk, sk)) 72 | } 73 | 74 | /// Returns a randomly generated keypair, suitable for use with key exchange. 75 | /// 76 | /// Equivalent to libsodium's `crypto_kx_keypair`. 77 | pub fn crypto_kx_keypair() -> (PublicKey, SecretKey) { 78 | let sk = SecretKey::gen(); 79 | let mut pk = PublicKey::default(); 80 | 81 | crypto_scalarmult_base(&mut pk, &sk); 82 | 83 | (pk, sk) 84 | } 85 | 86 | fn crypto_kx( 87 | x1: &mut SessionKey, 88 | x2: &mut SessionKey, 89 | client_pk: &PublicKey, 90 | server_pk: &PublicKey, 91 | mut shared_secret: [u8; CRYPTO_SCALARMULT_BYTES], 92 | ) -> Result<(), Error> { 93 | let mut keys = [0u8; 2 * CRYPTO_KX_SESSIONKEYBYTES]; 94 | 95 | let mut hasher = crypto_generichash_init(None, 2 * CRYPTO_KX_SESSIONKEYBYTES)?; 96 | crypto_generichash_update(&mut hasher, &shared_secret); 97 | shared_secret.zeroize(); 98 | crypto_generichash_update(&mut hasher, client_pk); 99 | crypto_generichash_update(&mut hasher, server_pk); 100 | crypto_generichash_final(hasher, &mut keys)?; 101 | 102 | x1.copy_from_slice(&keys[..CRYPTO_KX_SESSIONKEYBYTES]); 103 | x2.copy_from_slice(&keys[CRYPTO_KX_SESSIONKEYBYTES..]); 104 | 105 | keys.zeroize(); 106 | 107 | Ok(()) 108 | } 109 | 110 | /// Computes client session keys for `rx` and `tx`, using `client_pk`, 111 | /// `client_sk`, and `server_pk`. Returns unit `()` upon success. 112 | /// 113 | /// Compatible with libsodium's `crypto_kx_client_session_keys`. 114 | pub fn crypto_kx_client_session_keys( 115 | rx: &mut SessionKey, 116 | tx: &mut SessionKey, 117 | client_pk: &PublicKey, 118 | client_sk: &SecretKey, 119 | server_pk: &PublicKey, 120 | ) -> Result<(), Error> { 121 | let mut shared_secret = [0u8; CRYPTO_SCALARMULT_BYTES]; 122 | 123 | crypto_scalarmult(&mut shared_secret, client_sk, server_pk); 124 | 125 | crypto_kx(rx, tx, client_pk, server_pk, shared_secret) 126 | } 127 | 128 | /// Computes server session keys for `rx` and `tx`, using `client_pk`, 129 | /// `client_sk`, and `server_pk`. Returns unit `()` upon success. 130 | /// 131 | /// Compatible with libsodium's `crypto_kx_server_session_keys`. 132 | pub fn crypto_kx_server_session_keys( 133 | rx: &mut SessionKey, 134 | tx: &mut SessionKey, 135 | server_pk: &PublicKey, 136 | server_sk: &SecretKey, 137 | client_pk: &PublicKey, 138 | ) -> Result<(), Error> { 139 | let mut shared_secret = [0u8; CRYPTO_SCALARMULT_BYTES]; 140 | 141 | crypto_scalarmult(&mut shared_secret, server_sk, client_pk); 142 | 143 | crypto_kx(tx, rx, client_pk, server_pk, shared_secret) 144 | } 145 | 146 | #[cfg(test)] 147 | mod tests { 148 | use super::*; 149 | 150 | #[test] 151 | fn test_kx() { 152 | for _ in 0..20 { 153 | let (client_pk, client_sk) = crypto_kx_keypair(); 154 | let (server_pk, server_sk) = crypto_kx_keypair(); 155 | 156 | let (mut crx, mut ctx, mut srx, mut stx) = ( 157 | SessionKey::default(), 158 | SessionKey::default(), 159 | SessionKey::default(), 160 | SessionKey::default(), 161 | ); 162 | 163 | crypto_kx_client_session_keys(&mut crx, &mut ctx, &client_pk, &client_sk, &server_pk) 164 | .expect("client kx failed"); 165 | 166 | crypto_kx_server_session_keys(&mut srx, &mut stx, &server_pk, &server_sk, &client_pk) 167 | .expect("server kx failed"); 168 | 169 | assert_eq!(crx, stx); 170 | assert_eq!(ctx, srx); 171 | 172 | use sodiumoxide::crypto::kx; 173 | 174 | let client_pk = kx::PublicKey::from_slice(&client_pk).expect("client pk failed"); 175 | let client_sk = kx::SecretKey::from_slice(&client_sk).expect("client sk failed"); 176 | let server_pk = kx::PublicKey::from_slice(&server_pk).expect("server pk failed"); 177 | let server_sk = kx::SecretKey::from_slice(&server_sk).expect("server sk failed"); 178 | 179 | let (rx1, tx1) = match kx::client_session_keys(&client_pk, &client_sk, &server_pk) { 180 | Ok((rx, tx)) => (rx, tx), 181 | Err(()) => panic!("bad server signature"), 182 | }; 183 | 184 | // server performs the same operation 185 | let (rx2, tx2) = match kx::server_session_keys(&server_pk, &server_sk, &client_pk) { 186 | Ok((rx, tx)) => (rx, tx), 187 | Err(()) => panic!("bad client signature"), 188 | }; 189 | 190 | assert_eq!(rx1.as_ref(), crx); 191 | assert_eq!(rx2.as_ref(), srx); 192 | assert_eq!(tx1.as_ref(), ctx); 193 | assert_eq!(tx2.as_ref(), stx); 194 | } 195 | } 196 | } 197 | -------------------------------------------------------------------------------- /src/classic/crypto_onetimeauth.rs: -------------------------------------------------------------------------------- 1 | //! # One-time authentication 2 | //! 3 | //! Implements one-time authentication using the Poly1305 algorithm, compatible 4 | //! with libsodium's `crypto_onetimeauth_*` functions. 5 | //! 6 | //! # Classic API single-part example 7 | //! 8 | //! ``` 9 | //! use base64::Engine as _; 10 | //! use base64::engine::general_purpose; 11 | //! use dryoc::classic::crypto_onetimeauth::{ 12 | //! Mac, crypto_onetimeauth, crypto_onetimeauth_keygen, crypto_onetimeauth_verify, 13 | //! }; 14 | //! 15 | //! let key = crypto_onetimeauth_keygen(); 16 | //! let mut mac = Mac::default(); 17 | //! 18 | //! crypto_onetimeauth(&mut mac, b"Data to authenticate", &key); 19 | //! 20 | //! // This should be valid 21 | //! crypto_onetimeauth_verify(&mac, b"Data to authenticate", &key).expect("failed to authenticate"); 22 | //! 23 | //! // This should not be valid 24 | //! crypto_onetimeauth_verify(&mac, b"Invalid data", &key).expect_err("should not authenticate"); 25 | //! ``` 26 | //! 27 | //! # Classic API multi-part example 28 | //! 29 | //! ``` 30 | //! use base64::Engine as _; 31 | //! use base64::engine::general_purpose; 32 | //! use dryoc::classic::crypto_onetimeauth::{ 33 | //! Mac, crypto_onetimeauth_final, crypto_onetimeauth_init, crypto_onetimeauth_keygen, 34 | //! crypto_onetimeauth_update, crypto_onetimeauth_verify, 35 | //! }; 36 | //! 37 | //! let key = crypto_onetimeauth_keygen(); 38 | //! let mut mac = Mac::default(); 39 | //! 40 | //! let mut state = crypto_onetimeauth_init(&key); 41 | //! crypto_onetimeauth_update(&mut state, b"Multi-part"); 42 | //! crypto_onetimeauth_update(&mut state, b"data"); 43 | //! crypto_onetimeauth_final(state, &mut mac); 44 | //! 45 | //! // This should be valid 46 | //! crypto_onetimeauth_verify(&mac, b"Multi-partdata", &key).expect("failed to authenticate"); 47 | //! 48 | //! // This should not be valid 49 | //! crypto_onetimeauth_verify(&mac, b"Invalid data", &key).expect_err("should not authenticate"); 50 | //! ``` 51 | use subtle::ConstantTimeEq; 52 | 53 | use crate::constants::{ 54 | CRYPTO_ONETIMEAUTH_BYTES, CRYPTO_ONETIMEAUTH_KEYBYTES, CRYPTO_ONETIMEAUTH_POLY1305_BYTES, 55 | CRYPTO_ONETIMEAUTH_POLY1305_KEYBYTES, 56 | }; 57 | use crate::error::Error; 58 | use crate::poly1305::Poly1305; 59 | use crate::types::*; 60 | struct OnetimeauthPoly1305State { 61 | mac: Poly1305, 62 | } 63 | 64 | /// Key type for use with one-time authentication. 65 | pub type Key = [u8; CRYPTO_ONETIMEAUTH_POLY1305_KEYBYTES]; 66 | /// Message authentication code type for use with one-time authentication. 67 | pub type Mac = [u8; CRYPTO_ONETIMEAUTH_POLY1305_BYTES]; 68 | 69 | fn crypto_onetimeauth_poly1305(output: &mut Mac, message: &[u8], key: &Key) { 70 | let mut poly1305 = Poly1305::new(key); 71 | poly1305.update(message); 72 | poly1305.finalize(output) 73 | } 74 | fn crypto_onetimeauth_poly1305_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> { 75 | let mut poly1305 = Poly1305::new(key); 76 | poly1305.update(input); 77 | let computed_mac = poly1305.finalize_to_array(); 78 | 79 | if mac.ct_eq(&computed_mac).unwrap_u8() == 1 { 80 | Ok(()) 81 | } else { 82 | Err(dryoc_error!("authentication codes do not match")) 83 | } 84 | } 85 | 86 | fn crypto_onetimeauth_poly1305_init(key: &Key) -> OnetimeauthPoly1305State { 87 | OnetimeauthPoly1305State { 88 | mac: Poly1305::new(key), 89 | } 90 | } 91 | 92 | fn crypto_onetimeauth_poly1305_update(state: &mut OnetimeauthPoly1305State, input: &[u8]) { 93 | state.mac.update(input) 94 | } 95 | fn crypto_onetimeauth_poly1305_final( 96 | mut state: OnetimeauthPoly1305State, 97 | output: &mut [u8; CRYPTO_ONETIMEAUTH_POLY1305_BYTES], 98 | ) { 99 | state.mac.finalize(output) 100 | } 101 | 102 | /// Authenticates `message` using `key`, and places the result into 103 | /// `mac`. `key` should only be used once. 104 | /// 105 | /// Equivalent to libsodium's `crypto_onetimeauth`. 106 | pub fn crypto_onetimeauth(mac: &mut Mac, message: &[u8], key: &Key) { 107 | crypto_onetimeauth_poly1305(mac, message, key) 108 | } 109 | 110 | /// Verifies that `mac` is the correct authenticator for `message` using `key`. 111 | /// Returns `Ok(())` if the message authentication code is valid. 112 | /// 113 | /// Equivalent to libsodium's `crypto_onetimeauth_verify`. 114 | pub fn crypto_onetimeauth_verify(mac: &Mac, input: &[u8], key: &Key) -> Result<(), Error> { 115 | crypto_onetimeauth_poly1305_verify(mac, input, key) 116 | } 117 | 118 | /// Internal state for [`crypto_onetimeauth`]. 119 | pub struct OnetimeauthState { 120 | state: OnetimeauthPoly1305State, 121 | } 122 | 123 | /// Generates a random key using 124 | /// [`copy_randombytes`](crate::rng::copy_randombytes), suitable for use with 125 | /// [`crypto_onetimeauth_init`] and [`crypto_onetimeauth`]. The key should only 126 | /// be used once. 127 | /// 128 | /// Equivalent to libsodium's `crypto_onetimeauth_keygen`. 129 | pub fn crypto_onetimeauth_keygen() -> Key { 130 | Key::gen() 131 | } 132 | 133 | /// Initializes the incremental Poly1305-based one-time authentication. 134 | /// 135 | /// Initialize the incremental interface for Poly1305-based one-time 136 | /// authentication, using `key`. Returns a state struct which is required for 137 | /// subsequent calls to [`crypto_onetimeauth_update`] and 138 | /// [`crypto_onetimeauth_final`]. The key should only be used once. 139 | /// 140 | /// Equivalent to libsodium's `crypto_onetimeauth_init`. 141 | pub fn crypto_onetimeauth_init(key: &[u8; CRYPTO_ONETIMEAUTH_KEYBYTES]) -> OnetimeauthState { 142 | OnetimeauthState { 143 | state: crypto_onetimeauth_poly1305_init(key), 144 | } 145 | } 146 | 147 | /// Updates `state` for the one-time authentication function, based on `input`. 148 | /// 149 | /// Equivalent to libsodium's `crypto_onetimeauth_update`. 150 | pub fn crypto_onetimeauth_update(state: &mut OnetimeauthState, input: &[u8]) { 151 | crypto_onetimeauth_poly1305_update(&mut state.state, input) 152 | } 153 | 154 | /// Finalizes the message authentication code for `state`, and places the result 155 | /// into `output`. 156 | /// 157 | /// Equivalent to libsodium's `crypto_onetimeauth_final`. 158 | pub fn crypto_onetimeauth_final( 159 | state: OnetimeauthState, 160 | output: &mut [u8; CRYPTO_ONETIMEAUTH_BYTES], 161 | ) { 162 | crypto_onetimeauth_poly1305_final(state.state, output) 163 | } 164 | 165 | #[cfg(test)] 166 | mod tests { 167 | use super::*; 168 | 169 | #[test] 170 | fn test_onetimeauth() { 171 | use sodiumoxide::crypto::onetimeauth; 172 | 173 | use crate::rng::copy_randombytes; 174 | 175 | for _ in 0..20 { 176 | let mut key = [0u8; 32]; 177 | copy_randombytes(&mut key); 178 | let mut input = [0u8; 1024]; 179 | copy_randombytes(&mut input); 180 | 181 | let so_mac = onetimeauth::authenticate( 182 | &input, 183 | &onetimeauth::poly1305::Key::from_slice(&key).expect("so key failed"), 184 | ); 185 | 186 | let mut mac = [0u8; CRYPTO_ONETIMEAUTH_BYTES]; 187 | crypto_onetimeauth(&mut mac, &input, &key); 188 | 189 | assert_eq!(so_mac.0, mac); 190 | 191 | crypto_onetimeauth_verify(&mac, &input, &key).expect("verify failed"); 192 | } 193 | } 194 | 195 | #[test] 196 | fn test_onetimeauth_incremental() { 197 | use sodiumoxide::crypto::onetimeauth; 198 | 199 | use crate::rng::copy_randombytes; 200 | 201 | for _ in 0..20 { 202 | let mut key = [0u8; 32]; 203 | copy_randombytes(&mut key); 204 | let mut input = [0u8; 1024]; 205 | copy_randombytes(&mut input); 206 | 207 | let so_mac = onetimeauth::authenticate( 208 | &input, 209 | &onetimeauth::poly1305::Key::from_slice(&key).expect("so key failed"), 210 | ); 211 | 212 | let mut mac = [0u8; CRYPTO_ONETIMEAUTH_BYTES]; 213 | let mut state = crypto_onetimeauth_init(&key); 214 | crypto_onetimeauth_update(&mut state, &input); 215 | crypto_onetimeauth_final(state, &mut mac); 216 | 217 | assert_eq!(so_mac.0, mac); 218 | 219 | crypto_onetimeauth_verify(&mac, &input, &key).expect("verify failed"); 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/precalc.rs: -------------------------------------------------------------------------------- 1 | //! Precalculated secret key for use with `precalc_*` functions in 2 | //! [`crate::dryocbox::DryocBox`] 3 | //! 4 | //! You may want to use `precalc_*` functions if you need to 5 | //! encrypt/decrypt multiple messages between the same sender and receiver. 6 | use zeroize::{Zeroize, ZeroizeOnDrop}; 7 | 8 | use crate::constants::{ 9 | CRYPTO_BOX_BEFORENMBYTES, CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES, 10 | }; 11 | use crate::types::{ByteArray, Bytes, MutByteArray, MutBytes, StackByteArray}; 12 | 13 | type InnerKey = StackByteArray; 14 | 15 | /// Precalculated secret key for use with `precalc_*` functions in 16 | /// [`crate::dryocbox::DryocBox`]. 17 | /// 18 | /// You probably want to use `precalc_*` functions if you need to 19 | /// encrypt/decrypt multiple messages between the same sender and receiver. 20 | /// These functions save computation time by using [`PrecalcSecretKey`] 21 | /// instead of computing the shared secret every time. 22 | /// 23 | /// Using precalculated secret keys is compatible with libsodium's 24 | /// `crypto_box_beforenm`. 25 | #[derive(Zeroize, ZeroizeOnDrop, Debug, PartialEq, Eq, Clone)] 26 | pub struct PrecalcSecretKey + Zeroize>(InnerKey); 27 | 28 | impl + Bytes + Zeroize> Bytes 29 | for PrecalcSecretKey 30 | { 31 | #[inline] 32 | fn as_slice(&self) -> &[u8] { 33 | self.0.as_slice() 34 | } 35 | 36 | #[inline] 37 | fn is_empty(&self) -> bool { 38 | self.0.is_empty() 39 | } 40 | 41 | #[inline] 42 | fn len(&self) -> usize { 43 | self.0.len() 44 | } 45 | } 46 | 47 | impl + Zeroize> ByteArray 48 | for PrecalcSecretKey 49 | { 50 | #[inline] 51 | fn as_array(&self) -> &[u8; CRYPTO_BOX_BEFORENMBYTES] { 52 | self.0.as_array() 53 | } 54 | } 55 | 56 | impl + Zeroize + MutBytes> MutBytes 57 | for PrecalcSecretKey 58 | { 59 | #[inline] 60 | fn as_mut_slice(&mut self) -> &mut [u8] { 61 | self.0.as_mut_slice() 62 | } 63 | 64 | #[inline] 65 | fn copy_from_slice(&mut self, other: &[u8]) { 66 | self.0.copy_from_slice(other); 67 | } 68 | } 69 | 70 | impl + Zeroize> 71 | MutByteArray for PrecalcSecretKey 72 | { 73 | #[inline] 74 | fn as_mut_array(&mut self) -> &mut [u8; CRYPTO_BOX_BEFORENMBYTES] { 75 | self.0.as_mut_array() 76 | } 77 | } 78 | 79 | impl PrecalcSecretKey { 80 | /// Computes a stack-allocated shared secret key for the given 81 | /// `third_party_public_key` and `secret_key`. 82 | /// 83 | /// Compatible with libsodium's `crypto_box_beforenm`. 84 | #[inline] 85 | pub fn precalculate< 86 | ThirdPartyPublicKey: ByteArray, 87 | SecretKey: ByteArray, 88 | >( 89 | third_party_public_key: &ThirdPartyPublicKey, 90 | secret_key: &SecretKey, 91 | ) -> Self { 92 | use crate::classic::crypto_box::crypto_box_beforenm; 93 | 94 | Self(crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array()).into()) 95 | } 96 | } 97 | 98 | #[cfg(any(feature = "nightly", all(doc, not(doctest))))] 99 | #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))] 100 | pub mod protected { 101 | //! # Protected memory for [`PrecalcSecretKey`] 102 | use super::*; 103 | pub use crate::protected::*; 104 | 105 | type InnerKey = HeapByteArray; 106 | 107 | impl PrecalcSecretKey> { 108 | /// Computes a heap-allocated, page-aligned, locked shared secret key 109 | /// for the given `third_party_public_key` and `secret_key`. 110 | /// 111 | /// Compatible with libsodium's `crypto_box_beforenm`. 112 | pub fn precalculate_locked< 113 | ThirdPartyPublicKey: ByteArray, 114 | SecretKey: ByteArray, 115 | >( 116 | third_party_public_key: &ThirdPartyPublicKey, 117 | secret_key: &SecretKey, 118 | ) -> Result { 119 | use crate::classic::crypto_box::crypto_box_beforenm; 120 | 121 | let mut precalc = HeapByteArray::::new_locked()?; 122 | let mut key = 123 | crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array()); 124 | 125 | precalc.copy_from_slice(&key); 126 | key.zeroize(); 127 | 128 | Ok(PrecalcSecretKey(precalc)) 129 | } 130 | } 131 | 132 | impl PrecalcSecretKey> { 133 | /// Computes a heap-allocated, page-aligned, locked, read-only shared 134 | /// secret key for the given `third_party_public_key` and 135 | /// `secret_key`. 136 | /// 137 | /// Compatible with libsodium's `crypto_box_beforenm`. 138 | pub fn precalculate_readonly_locked< 139 | ThirdPartyPublicKey: ByteArray, 140 | SecretKey: ByteArray, 141 | >( 142 | third_party_public_key: &ThirdPartyPublicKey, 143 | secret_key: &SecretKey, 144 | ) -> Result { 145 | use crate::classic::crypto_box::crypto_box_beforenm; 146 | 147 | let mut precalc = HeapByteArray::::new_locked()?; 148 | let mut key = 149 | crypto_box_beforenm(third_party_public_key.as_array(), secret_key.as_array()); 150 | 151 | precalc.copy_from_slice(&key); 152 | key.zeroize(); 153 | 154 | Ok(PrecalcSecretKey(precalc.mprotect_readonly()?)) 155 | } 156 | } 157 | } 158 | 159 | impl + Zeroize> std::ops::Deref 160 | for PrecalcSecretKey 161 | { 162 | type Target = InnerKey; 163 | 164 | fn deref(&self) -> &Self::Target { 165 | &self.0 166 | } 167 | } 168 | 169 | impl + Zeroize> std::ops::DerefMut 170 | for PrecalcSecretKey 171 | { 172 | fn deref_mut(&mut self) -> &mut Self::Target { 173 | &mut self.0 174 | } 175 | } 176 | #[cfg(test)] 177 | mod tests { 178 | use super::*; 179 | use crate::constants::{CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES}; 180 | 181 | #[test] 182 | fn test_precalculate() { 183 | let public_key = StackByteArray::::default(); 184 | let secret_key = StackByteArray::::default(); 185 | let precalc_key = PrecalcSecretKey::precalculate(&public_key, &secret_key); 186 | assert!(!precalc_key.is_empty()); 187 | assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES); 188 | } 189 | 190 | #[cfg(feature = "nightly")] 191 | #[test] 192 | fn test_precalculate_locked() { 193 | let public_key = StackByteArray::::default(); 194 | let secret_key = StackByteArray::::default(); 195 | let mut precalc_key = 196 | PrecalcSecretKey::precalculate_locked(&public_key, &secret_key).unwrap(); 197 | assert!(!precalc_key.is_empty()); 198 | assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES); 199 | 200 | // should be able to write now without blowing up 201 | precalc_key.as_mut_slice()[0] = 0; 202 | precalc_key.as_mut_array()[0] = 1; 203 | precalc_key.copy_from_slice(&precalc_key.as_slice().to_owned()); 204 | } 205 | 206 | #[cfg(feature = "nightly")] 207 | #[test] 208 | fn test_precalculate_readonly_locked() { 209 | let public_key = StackByteArray::::default(); 210 | let secret_key = StackByteArray::::default(); 211 | let precalc_key = 212 | PrecalcSecretKey::precalculate_readonly_locked(&public_key, &secret_key).unwrap(); 213 | assert!(!precalc_key.is_empty()); 214 | assert_eq!(precalc_key.len(), CRYPTO_BOX_BEFORENMBYTES); 215 | } 216 | } 217 | -------------------------------------------------------------------------------- /src/classic/crypto_generichash.rs: -------------------------------------------------------------------------------- 1 | //! # Generic hashing 2 | //! 3 | //! Implements libsodium's generic hashing functions, based on blake2b. Can also 4 | //! be used as an HMAC function, if a key is provided. 5 | //! 6 | //! For details, refer to [libsodium docs](https://libsodium.gitbook.io/doc/hashing/generic_hashing). 7 | //! 8 | //! # Classic API example, one-time interface 9 | //! 10 | //! ``` 11 | //! use base64::Engine as _; 12 | //! use base64::engine::general_purpose; 13 | //! use dryoc::classic::crypto_generichash::*; 14 | //! use dryoc::constants::CRYPTO_GENERICHASH_BYTES; 15 | //! 16 | //! // Use the default hash length 17 | //! let mut output = [0u8; CRYPTO_GENERICHASH_BYTES]; 18 | //! // Compute the hash using the one-time interface 19 | //! crypto_generichash(&mut output, b"a string of bytes", None).ok(); 20 | //! 21 | //! assert_eq!( 22 | //! general_purpose::STANDARD.encode(output), 23 | //! "GdztjR9nU/rLh8VJt8e74+/seKTUnHgBexhGSpxLau0=" 24 | //! ); 25 | //! ``` 26 | //! 27 | //! # Classic API example, incremental interface 28 | //! 29 | //! ``` 30 | //! use base64::Engine as _; 31 | //! use base64::engine::general_purpose; 32 | //! use dryoc::classic::crypto_generichash::*; 33 | //! use dryoc::constants::CRYPTO_GENERICHASH_BYTES; 34 | //! 35 | //! // Use the default hash length 36 | //! let mut output = [0u8; CRYPTO_GENERICHASH_BYTES]; 37 | //! // Initialize the state for the incremental interface 38 | //! let mut state = crypto_generichash_init(None, CRYPTO_GENERICHASH_BYTES).expect("state"); 39 | //! // Update the hash 40 | //! crypto_generichash_update(&mut state, b"a string of bytes"); 41 | //! // Finalize, compute the hash and copy it into `output` 42 | //! crypto_generichash_final(state, &mut output).expect("final failed"); 43 | //! 44 | //! assert_eq!( 45 | //! general_purpose::STANDARD.encode(output), 46 | //! "GdztjR9nU/rLh8VJt8e74+/seKTUnHgBexhGSpxLau0=" 47 | //! ); 48 | //! ``` 49 | use super::generichash_blake2b::*; 50 | use crate::blake2b; 51 | use crate::constants::CRYPTO_GENERICHASH_KEYBYTES; 52 | use crate::error::Error; 53 | 54 | /** 55 | Computes a hash from `input` and `key`, copying the result into `output`. 56 | 57 | | Parameter | Typical length | Minimum length | Maximum length | 58 | |-|-|-|-| 59 | | `output` | [`CRYPTO_GENERICHASH_BYTES`](crate::constants::CRYPTO_GENERICHASH_BYTES) | [`CRYPTO_GENERICHASH_BYTES_MIN`](crate::constants::CRYPTO_GENERICHASH_BYTES_MIN) | [ `CRYPTO_GENERICHASH_BYTES_MAX`](crate::constants::CRYPTO_GENERICHASH_BYTES_MAX) | 60 | | `key` | [`CRYPTO_GENERICHASH_KEYBYTES`] | [`CRYPTO_GENERICHASH_KEYBYTES_MIN`](crate::constants::CRYPTO_GENERICHASH_KEYBYTES_MIN) | [ `CRYPTO_GENERICHASH_KEYBYTES_MAX`](crate::constants::CRYPTO_GENERICHASH_KEYBYTES_MAX) | 61 | 62 | Compatible with libsodium's `crypto_generichash_final` 63 | */ 64 | #[inline] 65 | pub fn crypto_generichash( 66 | output: &mut [u8], 67 | input: &[u8], 68 | key: Option<&[u8]>, 69 | ) -> Result<(), Error> { 70 | crypto_generichash_blake2b(output, input, key) 71 | } 72 | 73 | /// State struct for the generic hash algorithm, based on BLAKE2B. 74 | pub struct GenericHashState { 75 | state: blake2b::State, 76 | } 77 | 78 | /** 79 | Initializes the state for the generic hash function using `outlen` for the expected hash output length, and optional `key`, returning it upon success. 80 | 81 | | Parameter | Typical length | Minimum length | Maximum length | 82 | |-|-|-|-| 83 | | `outlen` | [`CRYPTO_GENERICHASH_BYTES`](crate::constants::CRYPTO_GENERICHASH_BYTES) | [`CRYPTO_GENERICHASH_BYTES_MIN`](crate::constants::CRYPTO_GENERICHASH_BYTES_MIN) | [`CRYPTO_GENERICHASH_BYTES_MAX`](crate::constants::CRYPTO_GENERICHASH_BYTES_MAX) | 84 | | `key` | [`CRYPTO_GENERICHASH_KEYBYTES`] | [`CRYPTO_GENERICHASH_KEYBYTES_MIN`](crate::constants::CRYPTO_GENERICHASH_KEYBYTES_MIN) | [ `CRYPTO_GENERICHASH_KEYBYTES_MAX`](crate::constants::CRYPTO_GENERICHASH_KEYBYTES_MAX) | 85 | 86 | Equivalent to libsodium's `crypto_generichash_final` 87 | */ 88 | #[inline] 89 | pub fn crypto_generichash_init( 90 | key: Option<&[u8]>, 91 | outlen: usize, 92 | ) -> Result { 93 | let state = crypto_generichash_blake2b_init(key, outlen, None, None)?; 94 | Ok(GenericHashState { state }) 95 | } 96 | 97 | /// Updates the internal hash state with `input`. 98 | /// 99 | /// Equivalent to libsodium's `crypto_generichash_final` 100 | #[inline] 101 | pub fn crypto_generichash_update(state: &mut GenericHashState, input: &[u8]) { 102 | crypto_generichash_blake2b_update(&mut state.state, input) 103 | } 104 | 105 | /// Finalizes the hash computation, copying the result into `output`. The length 106 | /// of `output` should match `outlen` from the call to 107 | /// [`crypto_generichash_init`]. 108 | /// 109 | /// Equivalent to libsodium's `crypto_generichash_final` 110 | #[inline] 111 | pub fn crypto_generichash_final(state: GenericHashState, output: &mut [u8]) -> Result<(), Error> { 112 | crypto_generichash_blake2b_final(state.state, output) 113 | } 114 | 115 | /// Generates a random hash key using the OS's random number source. 116 | /// 117 | /// Equivalent to libsodium's `crypto_generichash_keygen` 118 | pub fn crypto_generichash_keygen() -> [u8; CRYPTO_GENERICHASH_KEYBYTES] { 119 | let mut key = [0u8; CRYPTO_GENERICHASH_KEYBYTES]; 120 | crate::rng::copy_randombytes(&mut key); 121 | key 122 | } 123 | 124 | #[cfg(test)] 125 | mod tests { 126 | use rand::TryRngCore; 127 | 128 | use super::*; 129 | 130 | #[test] 131 | fn test_generichash() { 132 | use libsodium_sys::crypto_generichash as so_crypto_generichash; 133 | use rand_core::OsRng; 134 | 135 | use crate::constants::{CRYPTO_GENERICHASH_BYTES_MAX, CRYPTO_GENERICHASH_BYTES_MIN}; 136 | use crate::rng::copy_randombytes; 137 | 138 | for _ in 0..20 { 139 | let outlen = CRYPTO_GENERICHASH_BYTES_MIN 140 | + (OsRng.try_next_u32().unwrap() as usize 141 | % (CRYPTO_GENERICHASH_BYTES_MAX - CRYPTO_GENERICHASH_BYTES_MIN)); 142 | let mut output = vec![0u8; outlen]; 143 | 144 | let mut input = vec![0u8; (OsRng.try_next_u32().unwrap() % 5000) as usize]; 145 | 146 | copy_randombytes(&mut input); 147 | 148 | let mut so_output = output.clone(); 149 | 150 | crypto_generichash(&mut output, &input, None).ok(); 151 | 152 | unsafe { 153 | so_crypto_generichash( 154 | so_output.as_mut_ptr(), 155 | so_output.len(), 156 | input.as_ptr(), 157 | input.len() as u64, 158 | std::ptr::null(), 159 | 0, 160 | ); 161 | } 162 | 163 | assert_eq!(output, so_output); 164 | } 165 | } 166 | 167 | #[test] 168 | fn test_generichash_key() { 169 | use libsodium_sys::crypto_generichash as so_crypto_generichash; 170 | use rand_core::OsRng; 171 | 172 | use crate::constants::{ 173 | CRYPTO_GENERICHASH_BYTES_MAX, CRYPTO_GENERICHASH_BYTES_MIN, 174 | CRYPTO_GENERICHASH_KEYBYTES_MAX, CRYPTO_GENERICHASH_KEYBYTES_MIN, 175 | }; 176 | use crate::rng::copy_randombytes; 177 | 178 | for _ in 0..20 { 179 | let outlen = CRYPTO_GENERICHASH_BYTES_MIN 180 | + (OsRng.try_next_u32().unwrap() as usize 181 | % (CRYPTO_GENERICHASH_BYTES_MAX - CRYPTO_GENERICHASH_BYTES_MIN)); 182 | let mut output = vec![0u8; outlen]; 183 | 184 | let mut input = vec![0u8; (OsRng.try_next_u32().unwrap() % 5000) as usize]; 185 | 186 | let keylen = CRYPTO_GENERICHASH_KEYBYTES_MIN 187 | + (OsRng.try_next_u32().unwrap() as usize 188 | % (CRYPTO_GENERICHASH_KEYBYTES_MAX - CRYPTO_GENERICHASH_KEYBYTES_MIN)); 189 | let mut key = vec![0u8; keylen]; 190 | 191 | copy_randombytes(&mut input); 192 | copy_randombytes(&mut key); 193 | 194 | let mut so_output = output.clone(); 195 | 196 | crypto_generichash(&mut output, &input, Some(&key)).ok(); 197 | 198 | unsafe { 199 | so_crypto_generichash( 200 | so_output.as_mut_ptr(), 201 | so_output.len(), 202 | input.as_ptr(), 203 | input.len() as u64, 204 | key.as_ptr(), 205 | key.len(), 206 | ); 207 | } 208 | 209 | assert_eq!(output, so_output); 210 | } 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /src/auth.rs: -------------------------------------------------------------------------------- 1 | //! # Secret-key message authentication 2 | //! 3 | //! [`Auth`] implements libsodium's secret-key authentication, based on 4 | //! HMAC-SHA512-256. 5 | //! 6 | //! Use [`Auth`] to authenticate messages when: 7 | //! 8 | //! * you want to authenticate arbitrary messages 9 | //! * you have a pre-shared key between both parties 10 | //! * (optionally) you want to share the authentication tag publicly 11 | //! 12 | //! # Rustaceous API example, one-time interface 13 | //! 14 | //! ``` 15 | //! use dryoc::auth::*; 16 | //! use dryoc::types::*; 17 | //! 18 | //! // Generate a random key 19 | //! let key = Key::gen(); 20 | //! 21 | //! // Compute the mac in one shot. Here we clone the key for the purpose of this 22 | //! // example, but normally you would not do this as you never want to re-use a 23 | //! // key. 24 | //! let mac = Auth::compute_to_vec(key.clone(), b"Data to authenticate"); 25 | //! 26 | //! // Verify the mac 27 | //! Auth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed"); 28 | //! ``` 29 | //! 30 | //! # Rustaceous API example, incremental interface 31 | //! 32 | //! ``` 33 | //! use dryoc::auth::*; 34 | //! use dryoc::types::*; 35 | //! 36 | //! // Generate a random key 37 | //! let key = Key::gen(); 38 | //! 39 | //! // Initialize the MAC, clone the key (don't do this) 40 | //! let mut mac = Auth::new(key.clone()); 41 | //! mac.update(b"Multi-part"); 42 | //! mac.update(b"data"); 43 | //! let mac = mac.finalize_to_vec(); 44 | //! 45 | //! // Verify it's correct, clone the key (don't do this) 46 | //! let mut verify_mac = Auth::new(key.clone()); 47 | //! verify_mac.update(b"Multi-part"); 48 | //! verify_mac.update(b"data"); 49 | //! verify_mac.verify(&mac).expect("verify failed"); 50 | //! 51 | //! // Check that invalid data fails, consume the key 52 | //! let mut verify_mac = Auth::new(key); 53 | //! verify_mac.update(b"Multi-part"); 54 | //! verify_mac.update(b"bad data"); 55 | //! verify_mac 56 | //! .verify(&mac) 57 | //! .expect_err("verify should have failed"); 58 | //! ``` 59 | 60 | use subtle::ConstantTimeEq; 61 | 62 | use crate::classic::crypto_auth::{ 63 | AuthState, crypto_auth, crypto_auth_final, crypto_auth_init, crypto_auth_update, 64 | crypto_auth_verify, 65 | }; 66 | use crate::constants::{CRYPTO_AUTH_BYTES, CRYPTO_AUTH_KEYBYTES}; 67 | use crate::error::Error; 68 | use crate::types::*; 69 | 70 | /// Stack-allocated key for secret-key authentication. 71 | pub type Key = StackByteArray; 72 | /// Stack-allocated message authentication code for secret-key authentication. 73 | pub type Mac = StackByteArray; 74 | 75 | #[cfg(any(feature = "nightly", all(doc, not(doctest))))] 76 | #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))] 77 | pub mod protected { 78 | //! # Protected memory type aliases for [`Auth`] 79 | //! 80 | //! This mod provides re-exports of type aliases for protected memory usage 81 | //! with [`Auth`]. These type aliases are provided for 82 | //! convenience. 83 | //! 84 | //! ## Example 85 | //! 86 | //! ``` 87 | //! use dryoc::auth::Auth; 88 | //! use dryoc::auth::protected::*; 89 | //! 90 | //! // Create a randomly generated key, lock it, protect it as read-only 91 | //! let key = Key::gen_readonly_locked().expect("gen failed"); 92 | //! let input = 93 | //! HeapBytes::from_slice_into_readonly_locked(b"super secret input").expect("input failed"); 94 | //! // Compute the message authentication code, consuming the key. 95 | //! let mac: Locked = Auth::compute(key, &input); 96 | //! ``` 97 | use super::*; 98 | pub use crate::protected::*; 99 | 100 | /// Heap-allocated, page-aligned secret key for the generic hash algorithm, 101 | /// for use with protected memory. 102 | pub type Key = HeapByteArray; 103 | /// Heap-allocated, page-aligned hash output for the generic hash algorithm, 104 | /// for use with protected memory. 105 | pub type Mac = HeapByteArray; 106 | } 107 | 108 | /// secret-key authentication implementation based on Poly1305, compatible with 109 | /// libsodium's `crypto_Auth_*` functions. 110 | pub struct Auth { 111 | state: AuthState, 112 | } 113 | 114 | impl Auth { 115 | /// Single-part interface for [`Auth`]. Computes (and returns) the 116 | /// message authentication code for `input` using `key`. The `key` is 117 | /// consumed to prevent accidental re-use of the same key. 118 | pub fn compute< 119 | Key: ByteArray, 120 | Input: Bytes, 121 | Output: NewByteArray, 122 | >( 123 | key: Key, 124 | input: &Input, 125 | ) -> Output { 126 | let mut output = Output::new_byte_array(); 127 | crypto_auth(output.as_mut_array(), input.as_slice(), key.as_array()); 128 | output 129 | } 130 | 131 | /// Convience wrapper around [`Auth::compute`]. Returns the message 132 | /// authentication code as a [`Vec`]. The `key` is 133 | /// consumed to prevent accidental re-use of the same key. 134 | pub fn compute_to_vec, Input: Bytes>( 135 | key: Key, 136 | input: &Input, 137 | ) -> Vec { 138 | Self::compute(key, input) 139 | } 140 | 141 | /// Verifies the message authentication code `other_mac` matches the 142 | /// expected code for `key` and `input`. The `key` is 143 | /// consumed to prevent accidental re-use of the same key. 144 | pub fn compute_and_verify< 145 | OtherMac: ByteArray, 146 | Key: ByteArray, 147 | Input: Bytes, 148 | >( 149 | other_mac: &OtherMac, 150 | key: Key, 151 | input: &Input, 152 | ) -> Result<(), Error> { 153 | crypto_auth_verify(other_mac.as_array(), input.as_slice(), key.as_array()) 154 | } 155 | 156 | /// Returns a new secret-key authenticator for `key`. The `key` is 157 | /// consumed to prevent accidental re-use of the same key. 158 | pub fn new>(key: Key) -> Self { 159 | Self { 160 | state: crypto_auth_init(key.as_array()), 161 | } 162 | } 163 | 164 | /// Updates the secret-key authenticator at `self` with `input`. 165 | pub fn update(&mut self, input: &Input) { 166 | crypto_auth_update(&mut self.state, input.as_slice()) 167 | } 168 | 169 | /// Finalizes this secret-key authenticator, returning the message 170 | /// authentication code. 171 | pub fn finalize>(self) -> Output { 172 | let mut output = Output::new_byte_array(); 173 | crypto_auth_final(self.state, output.as_mut_array()); 174 | output 175 | } 176 | 177 | /// Finalizes this secret-key authenticator, returning the message 178 | /// authentication code as a [`Vec`]. Convenience wrapper around 179 | /// [`Auth::finalize`]. 180 | pub fn finalize_to_vec(self) -> Vec { 181 | self.finalize() 182 | } 183 | 184 | /// Finalizes this authenticator, and verifies that the computed code 185 | /// matches `other_mac` using a constant-time comparison. 186 | pub fn verify>( 187 | self, 188 | other_mac: &OtherMac, 189 | ) -> Result<(), Error> { 190 | let computed_mac: Mac = self.finalize(); 191 | 192 | if other_mac 193 | .as_array() 194 | .ct_eq(computed_mac.as_array()) 195 | .unwrap_u8() 196 | == 1 197 | { 198 | Ok(()) 199 | } else { 200 | Err(dryoc_error!("authentication codes do not match")) 201 | } 202 | } 203 | } 204 | 205 | #[cfg(test)] 206 | mod tests { 207 | use super::*; 208 | 209 | #[test] 210 | fn test_single_part() { 211 | let key = Key::gen(); 212 | let mac = Auth::compute_to_vec(key.clone(), b"Data to authenticate"); 213 | 214 | Auth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed"); 215 | } 216 | 217 | #[test] 218 | fn test_multi_part() { 219 | let key = Key::gen(); 220 | 221 | let mut mac = Auth::new(key.clone()); 222 | mac.update(b"Multi-part"); 223 | mac.update(b"data"); 224 | let mac = mac.finalize_to_vec(); 225 | 226 | let mut verify_mac = Auth::new(key.clone()); 227 | verify_mac.update(b"Multi-part"); 228 | verify_mac.update(b"data"); 229 | verify_mac.verify(&mac).expect("verify failed"); 230 | 231 | let mut verify_mac = Auth::new(key); 232 | verify_mac.update(b"Multi-part"); 233 | verify_mac.update(b"bad data"); 234 | verify_mac 235 | .verify(&mac) 236 | .expect_err("verify should have failed"); 237 | } 238 | } 239 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Docs](https://docs.rs/dryoc/badge.svg)](https://docs.rs/dryoc) [![Crates.io](https://img.shields.io/crates/v/dryoc)](https://crates.io/crates/dryoc) [![Build & test](https://github.com/brndnmtthws/dryoc/actions/workflows/build-and-test.yml/badge.svg)](https://github.com/brndnmtthws/dryoc/actions/workflows/build-and-test.yml) [![Codecov](https://img.shields.io/codecov/c/github/brndnmtthws/dryoc)](https://app.codecov.io/gh/brndnmtthws/dryoc/) 2 | 3 | [💬 Join the Matrix chat](https://matrix.to/#/#dryoc:frens.io) 4 | 5 | # dryoc: Don't Roll Your Own Crypto™[^1] 6 | 7 | dryoc is a pure-Rust, general-purpose cryptography library that's hard to misuse. It's based on the excellent 8 | [libsodium](https://github.com/jedisct1/libsodium) library, but in _pure_ Rust. It 9 | also includes protected memory features throughout, which makes it dead simple 10 | to build secure, robust, and safe cryptographic software. The original goal of this library was to provide a pure-Rust alternative to libsodium. 11 | 12 | ![Granny says no](dryoc.png) 13 | 14 | The purpose of this project is to provide a pure-Rust, mostly drop-in 15 | replacement for libsodium. This library has nearly the same ergonomics as 16 | libsodium (referred to in dryoc as the _Classic_ API), such that people 17 | familiar with libsodium can use this library nearly interchangeably. While 18 | the API is not 100% identical to libsodium, most functions have the same or 19 | very similar signatures. 20 | 21 | In addition to the Classic API, there's a _Rustaceous_ API which aims to bring 22 | an idiomatic Rust implementation of libsodium's core features: public and 23 | secret key authenticated cryptography and general-purpose cryptography tools. 24 | 25 | Not all features from libsodium are implemented here, either because there 26 | exist better implementations in other crates, or because they aren't 27 | necessary as part of this crate. 28 | 29 | Additionally, this crate provides exceptionally safe cryptography thanks to 30 | Rust's safety features. The Rustaceous API is designed designed to make it 31 | difficult to shoot yourself in the foot. It's worth noting, however, you 32 | certainly can still shoot yourself if you choose (either by leaking private 33 | data, using insecure hardware, OPSEC issues, etc). 34 | 35 | For example usage, refer to the 36 | [official docs](https://docs.rs/dryoc/latest/dryoc/) or the 37 | [integration tests](/tests/integration_tests.rs). 38 | 39 | ## Features 40 | 41 | * 100% pure Rust, no hidden C libraries 42 | * mostly free of unsafe code[^2] 43 | * Hard to misuse, helping you avoid common costly cryptography mistakes 44 | * Many libsodium features implemented with both Classic and Rustaceous API 45 | * Protected memory handling (`mprotect()` + `mlock()`, along with Windows equivalents) 46 | * [Serde](https://serde.rs/) support (with `features = ["serde"]`) 47 | * [_Portable_ SIMD](https://doc.rust-lang.org/std/simd/index.html) implementation for Blake2b (used by generic hashing, password hashing, and key derivation) on nightly, with `features = ["simd_backend", "nightly"]` 48 | * SIMD backend for Curve25519 (used by public/private key functions) on nightly with `features = ["simd_backend", "nightly"]` 49 | * [SHA2](https://github.com/RustCrypto/hashes/tree/master/sha2) (used by sealed boxes) includes SIMD implementation for AVX2 50 | * [ChaCha20](https://github.com/RustCrypto/stream-ciphers/tree/master/chacha20) (used by streaming interface) includes SIMD implementations for Neon, AVX2, and SSE2 51 | 52 | To enable all the SIMD backends through 3rd party crates, you'll need to also 53 | set `RUSTFLAGS`: 54 | * For AVX2 set `RUSTFLAGS=-Ctarget-cpu=haswell -Ctarget-feature=+avx2` 55 | * For SSE2 set `RUSTFLAGS=-Ctarget-feature=+sse2` 56 | * For Neon set `RUSTFLAGS=-Ctarget-feature=+neon` 57 | 58 | _Note that eventually this project will converge on portable SIMD implementations 59 | for all the core algos which will work across all platforms supported by LLVM, 60 | rather than relying on hand-coded assembly or intrinsics, but this is a work in 61 | progress_. 62 | 63 | ## Project status 64 | 65 | The following libsodium features are currently implemented, or awaiting 66 | implementation: 67 | 68 | * [x] [Public-key cryptography](https://docs.rs/dryoc/latest/dryoc/dryocbox/index.html) (`crypto_box_*`) [libsodium link](https://doc.libsodium.org/public-key_cryptography) 69 | * [x] [Secret-key cryptography](https://docs.rs/dryoc/latest/dryoc/dryocsecretbox/index.html) (`crypto_secretbox_*`) [libsodium link](https://doc.libsodium.org/secret-key_cryptography) 70 | * [x] [Point*scalar multiplication](https://docs.rs/dryoc/latest/dryoc/classic/crypto_core/index.html) (`crypto_scalarmult*`) [libsodium link](https://doc.libsodium.org/advanced/scalar_multiplication) 71 | * [x] Zeroing memory (`sodium_memzero`) with [zeroize](https://crates.io/crates/zeroize) [libsodium link](https://doc.libsodium.org/memory_management) 72 | * [x] [Generating random data](https://docs.rs/dryoc/latest/dryoc/rng/index.html) (`randombytes_buf`) [libsodium link](https://doc.libsodium.org/generating_random_data) 73 | * [x] [Encrypted streams](https://docs.rs/dryoc/latest/dryoc/dryocstream/index.html) (`crypto_secretstream_*`) [libsodium link](https://doc.libsodium.org/secret-key_cryptography/secretstream) 74 | * [x] [Memory locking](https://docs.rs/dryoc/latest/dryoc/protected/index.html) (`sodium_mlock`, `sodium_munlock`, `sodium_mprotect_*`) [libsodium link](https://doc.libsodium.org/memory_management) 75 | * [x] [Encrypting related messages](https://docs.rs/dryoc/latest/dryoc/utils/fn.increment_bytes.html) (`sodium_increment`) [libsodium link](https://doc.libsodium.org/secret-key_cryptography/encrypted-messages) 76 | * [x] [Generic hashing](https://docs.rs/dryoc/latest/dryoc/generichash/index.html) (`crypto_generichash_*`) [libsodium link](https://doc.libsodium.org/hashing/generic_hashing) 77 | * [x] [Secret-key authentication](https://docs.rs/dryoc/latest/dryoc/auth/index.html) (`crypto_auth*`) [libsodium link](https://doc.libsodium.org/secret-key_cryptography/secret-key_authentication) 78 | * [x] [One-time authentication](https://docs.rs/dryoc/latest/dryoc/onetimeauth/index.html) (`crypto_onetimeauth_*`) [libsodium link](https://doc.libsodium.org/advanced/poly1305) 79 | * [x] [Sealed boxes](https://docs.rs/dryoc/latest/dryoc/dryocbox/struct.DryocBox.html#method.seal) (`crypto_box_seal*`) [libsodium link](https://doc.libsodium.org/public-key_cryptography/sealed_boxes) 80 | * [x] [Key derivation](https://docs.rs/dryoc/latest/dryoc/kdf/index.html) (`crypto_kdf_*`) [libsodium link](https://doc.libsodium.org/key_derivation) 81 | * [x] [Key exchange](https://docs.rs/dryoc/latest/dryoc/kx/index.html) (`crypto_kx_*`) [libsodium link](https://doc.libsodium.org/key_exchange) 82 | * [x] [Public-key signatures](https://docs.rs/dryoc/latest/dryoc/sign/index.html) (`crypto_sign_*`) [libsodium link](https://doc.libsodium.org/public-key_cryptography/public-key_signatures) 83 | * [x] [Ed25519 to Curve25519](https://docs.rs/dryoc/latest/dryoc/classic/crypto_sign_ed25519/index.html) (`crypto_sign_ed25519_*`) [libsodium link](https://doc.libsodium.org/advanced/ed25519-curve25519) 84 | * [x] [Short-input hashing](https://docs.rs/dryoc/latest/dryoc/classic/crypto_shorthash/index.html) (`crypto_shorthash`) [libsodium link](https://doc.libsodium.org/hashing/short-input_hashing) 85 | * [x] [Password hashing](https://docs.rs/dryoc/latest/dryoc/pwhash/index.html) (`crypto_pwhash_*`) [libsodium link](https://doc.libsodium.org/password_hashing/default_phf) 86 | 87 | The following libsodium features are either incomplete, not exposed as public 88 | APIs, or not implemented; you may find equivalent functionality in other 89 | crates: 90 | 91 | * [Stream ciphers](https://doc.libsodium.org/advanced/stream_ciphers) (use [salsa20](https://crates.io/crates/salsa20) crate directly instead) 92 | * [Helpers](https://doc.libsodium.org/helpers) and [padding](https://doc.libsodium.org/padding) utilities 93 | * [Advanced features](https://doc.libsodium.org/advanced): 94 | * [Scrypt](https://doc.libsodium.org/advanced/scrypt) (use [scrypt](https://crates.io/crates/scrypt) crate directly instead) 95 | * [Finite field arithmetic](https://doc.libsodium.org/advanced/point-arithmetic) (try the [curve25519-dalek](https://crates.io/crates/curve25519-dalek) crate) 96 | 97 | ## Stargazers over time 98 | 99 | [![Stargazers over time](https://starchart.cc/brndnmtthws/dryoc.svg)](https://starchart.cc/brndnmtthws/dryoc) 100 | 101 | ## Other NaCl-related Rust implementations worth mentioning 102 | 103 | * [sodiumoxide](https://crates.io/crates/sodiumoxide) 104 | * [crypto_box](https://crates.io/crates/crypto_box) 105 | 106 | [^1]: Not actually trademarked. 107 | 108 | [^2]: The protected memory features described in the [protected] mod require 109 | custom memory allocation, system calls, and pointer arithmetic, which are unsafe 110 | in Rust. Some of the 3rd party libraries used by this crate, such as those with 111 | SIMD, may contain unsafe code. In particular, most SIMD implementations are 112 | considered "unsafe" due to their use of assembly or intrinsics, however without 113 | SIMD-based cryptography you may be exposed to timing attacks. 114 | -------------------------------------------------------------------------------- /src/onetimeauth.rs: -------------------------------------------------------------------------------- 1 | //! # One-time authentication 2 | //! 3 | //! [`OnetimeAuth`] implements libsodium's one-time authentication, based on the 4 | //! Poly1305 message authentication code. 5 | //! 6 | //! Use [`OnetimeAuth`] to authenticate messages when: 7 | //! 8 | //! * you want to exchange many small messages, such as in an online protocol 9 | //! * you can generate a unique key for each message you're authenticating, 10 | //! i.e., using a key and a nonce 11 | //! 12 | //! Do not reuse the same key for difference messages with [`OnetimeAuth`], as 13 | //! it provides an opportunity for an attacker to discover the key. 14 | //! 15 | //! 16 | //! # Rustaceous API example, one-time interface 17 | //! 18 | //! ``` 19 | //! use dryoc::onetimeauth::*; 20 | //! use dryoc::types::*; 21 | //! 22 | //! // Generate a random key 23 | //! let key = Key::gen(); 24 | //! 25 | //! // Compute the mac in one shot. Here we clone the key for the purpose of this 26 | //! // example, but normally you would not do this as you never want to re-use a 27 | //! // key. 28 | //! let mac = OnetimeAuth::compute_to_vec(key.clone(), b"Data to authenticate"); 29 | //! 30 | //! // Verify the mac 31 | //! OnetimeAuth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed"); 32 | //! ``` 33 | //! 34 | //! # Rustaceous API example, incremental interface 35 | //! 36 | //! ``` 37 | //! use dryoc::onetimeauth::*; 38 | //! use dryoc::types::*; 39 | //! 40 | //! // Generate a random key 41 | //! let key = Key::gen(); 42 | //! 43 | //! // Initialize the MAC, clone the key (don't do this) 44 | //! let mut mac = OnetimeAuth::new(key.clone()); 45 | //! mac.update(b"Multi-part"); 46 | //! mac.update(b"data"); 47 | //! let mac = mac.finalize_to_vec(); 48 | //! 49 | //! // Verify it's correct, clone the key (don't do this) 50 | //! let mut verify_mac = OnetimeAuth::new(key.clone()); 51 | //! verify_mac.update(b"Multi-part"); 52 | //! verify_mac.update(b"data"); 53 | //! verify_mac.verify(&mac).expect("verify failed"); 54 | //! 55 | //! // Check that invalid data fails, consume the key 56 | //! let mut verify_mac = OnetimeAuth::new(key); 57 | //! verify_mac.update(b"Multi-part"); 58 | //! verify_mac.update(b"bad data"); 59 | //! verify_mac 60 | //! .verify(&mac) 61 | //! .expect_err("verify should have failed"); 62 | //! ``` 63 | 64 | use subtle::ConstantTimeEq; 65 | 66 | use crate::classic::crypto_onetimeauth::{ 67 | OnetimeauthState, crypto_onetimeauth, crypto_onetimeauth_final, crypto_onetimeauth_init, 68 | crypto_onetimeauth_update, crypto_onetimeauth_verify, 69 | }; 70 | use crate::constants::{CRYPTO_ONETIMEAUTH_BYTES, CRYPTO_ONETIMEAUTH_KEYBYTES}; 71 | use crate::error::Error; 72 | use crate::types::*; 73 | 74 | /// Stack-allocated key for one-time authentication. 75 | pub type Key = StackByteArray; 76 | /// Stack-allocated message authentication code for one-time authentication. 77 | pub type Mac = StackByteArray; 78 | 79 | #[cfg(any(feature = "nightly", all(doc, not(doctest))))] 80 | #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))] 81 | pub mod protected { 82 | //! # Protected memory type aliases for [`OnetimeAuth`] 83 | //! 84 | //! This mod provides re-exports of type aliases for protected memory usage 85 | //! with [`OnetimeAuth`]. These type aliases are provided for 86 | //! convenience. 87 | //! 88 | //! ## Example 89 | //! 90 | //! ``` 91 | //! use dryoc::onetimeauth::OnetimeAuth; 92 | //! use dryoc::onetimeauth::protected::*; 93 | //! 94 | //! // Create a randomly generated key, lock it, protect it as read-only 95 | //! let key = Key::gen_readonly_locked().expect("gen failed"); 96 | //! let input = 97 | //! HeapBytes::from_slice_into_readonly_locked(b"super secret input").expect("input failed"); 98 | //! // Compute the message authentication code, consuming the key. 99 | //! let mac: Locked = OnetimeAuth::compute(key, &input); 100 | //! ``` 101 | use super::*; 102 | pub use crate::protected::*; 103 | 104 | /// Heap-allocated, page-aligned secret key for the generic hash algorithm, 105 | /// for use with protected memory. 106 | pub type Key = HeapByteArray; 107 | /// Heap-allocated, page-aligned hash output for the generic hash algorithm, 108 | /// for use with protected memory. 109 | pub type Mac = HeapByteArray; 110 | } 111 | 112 | /// One-time authentication implementation based on Poly1305, compatible with 113 | /// libsodium's `crypto_onetimeauth_*` functions. 114 | pub struct OnetimeAuth { 115 | state: OnetimeauthState, 116 | } 117 | 118 | impl OnetimeAuth { 119 | /// Single-part interface for [`OnetimeAuth`]. Computes (and returns) the 120 | /// message authentication code for `input` using `key`. The `key` is 121 | /// consumed to prevent accidental re-use of the same key. 122 | pub fn compute< 123 | Key: ByteArray, 124 | Input: Bytes, 125 | Output: NewByteArray, 126 | >( 127 | key: Key, 128 | input: &Input, 129 | ) -> Output { 130 | let mut output = Output::new_byte_array(); 131 | crypto_onetimeauth(output.as_mut_array(), input.as_slice(), key.as_array()); 132 | output 133 | } 134 | 135 | /// Convience wrapper around [`OnetimeAuth::compute`]. Returns the message 136 | /// authentication code as a [`Vec`]. The `key` is 137 | /// consumed to prevent accidental re-use of the same key. 138 | pub fn compute_to_vec, Input: Bytes>( 139 | key: Key, 140 | input: &Input, 141 | ) -> Vec { 142 | Self::compute(key, input) 143 | } 144 | 145 | /// Verifies the message authentication code `other_mac` matches the 146 | /// expected code for `key` and `input`. The `key` is 147 | /// consumed to prevent accidental re-use of the same key. 148 | pub fn compute_and_verify< 149 | OtherMac: ByteArray, 150 | Key: ByteArray, 151 | Input: Bytes, 152 | >( 153 | other_mac: &OtherMac, 154 | key: Key, 155 | input: &Input, 156 | ) -> Result<(), Error> { 157 | crypto_onetimeauth_verify(other_mac.as_array(), input.as_slice(), key.as_array()) 158 | } 159 | 160 | /// Returns a new one-time authenticator for `key`. The `key` is 161 | /// consumed to prevent accidental re-use of the same key. 162 | pub fn new>(key: Key) -> Self { 163 | Self { 164 | state: crypto_onetimeauth_init(key.as_array()), 165 | } 166 | } 167 | 168 | /// Updates the one-time authenticator at `self` with `input`. 169 | pub fn update(&mut self, input: &Input) { 170 | crypto_onetimeauth_update(&mut self.state, input.as_slice()) 171 | } 172 | 173 | /// Finalizes this one-time authenticator, returning the message 174 | /// authentication code. 175 | pub fn finalize>(self) -> Output { 176 | let mut output = Output::new_byte_array(); 177 | crypto_onetimeauth_final(self.state, output.as_mut_array()); 178 | output 179 | } 180 | 181 | /// Finalizes this one-time authenticator, returning the message 182 | /// authentication code as a [`Vec`]. Convenience wrapper around 183 | /// [`OnetimeAuth::finalize`]. 184 | pub fn finalize_to_vec(self) -> Vec { 185 | self.finalize() 186 | } 187 | 188 | /// Finalizes this authenticator, and verifies that the computed code 189 | /// matches `other_mac` using a constant-time comparison. 190 | pub fn verify>( 191 | self, 192 | other_mac: &OtherMac, 193 | ) -> Result<(), Error> { 194 | let computed_mac: Mac = self.finalize(); 195 | 196 | if other_mac 197 | .as_array() 198 | .ct_eq(computed_mac.as_array()) 199 | .unwrap_u8() 200 | == 1 201 | { 202 | Ok(()) 203 | } else { 204 | Err(dryoc_error!("authentication codes do not match")) 205 | } 206 | } 207 | } 208 | 209 | #[cfg(test)] 210 | mod tests { 211 | use super::*; 212 | 213 | #[test] 214 | fn test_single_part() { 215 | let key = Key::gen(); 216 | let mac = OnetimeAuth::compute_to_vec(key.clone(), b"Data to authenticate"); 217 | 218 | OnetimeAuth::compute_and_verify(&mac, key, b"Data to authenticate").expect("verify failed"); 219 | } 220 | 221 | #[test] 222 | fn test_multi_part() { 223 | let key = Key::gen(); 224 | 225 | let mut mac = OnetimeAuth::new(key.clone()); 226 | mac.update(b"Multi-part"); 227 | mac.update(b"data"); 228 | let mac = mac.finalize_to_vec(); 229 | 230 | let mut verify_mac = OnetimeAuth::new(key.clone()); 231 | verify_mac.update(b"Multi-part"); 232 | verify_mac.update(b"data"); 233 | verify_mac.verify(&mac).expect("verify failed"); 234 | 235 | let mut verify_mac = OnetimeAuth::new(key); 236 | verify_mac.update(b"Multi-part"); 237 | verify_mac.update(b"bad data"); 238 | verify_mac 239 | .verify(&mac) 240 | .expect_err("verify should have failed"); 241 | } 242 | } 243 | -------------------------------------------------------------------------------- /src/bytes_serde.rs: -------------------------------------------------------------------------------- 1 | use serde::de::{Error, SeqAccess, Visitor}; 2 | use serde::{Deserialize, Deserializer, Serialize, Serializer}; 3 | 4 | use crate::types::*; 5 | 6 | impl Serialize for StackByteArray { 7 | fn serialize(&self, serializer: S) -> Result 8 | where 9 | S: Serializer, 10 | { 11 | serializer.serialize_bytes(self.as_slice()) 12 | } 13 | } 14 | 15 | impl<'de, const LENGTH: usize> Deserialize<'de> for StackByteArray { 16 | fn deserialize(deserializer: D) -> Result 17 | where 18 | D: Deserializer<'de>, 19 | { 20 | struct ByteArrayVisitor; 21 | 22 | impl<'de, const LENGTH: usize> Visitor<'de> for ByteArrayVisitor { 23 | type Value = StackByteArray; 24 | 25 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 26 | write!(formatter, "bytes") 27 | } 28 | 29 | fn visit_seq(self, mut seq: A) -> Result 30 | where 31 | A: SeqAccess<'de>, 32 | { 33 | let mut arr = StackByteArray::::new(); 34 | let mut idx: usize = 0; 35 | 36 | while let Some(elem) = seq.next_element()? { 37 | if idx < LENGTH { 38 | arr[idx] = elem; 39 | idx += 1; 40 | } else { 41 | break; 42 | } 43 | } 44 | 45 | Ok(arr) 46 | } 47 | 48 | fn visit_bytes(self, v: &[u8]) -> Result 49 | where 50 | E: Error, 51 | { 52 | if v.len() != LENGTH { 53 | return Err(Error::invalid_length(v.len(), &stringify!(LENGTH))); 54 | } 55 | let mut arr = StackByteArray::::new(); 56 | arr.copy_from_slice(v); 57 | Ok(arr) 58 | } 59 | } 60 | 61 | deserializer.deserialize_bytes(ByteArrayVisitor::) 62 | } 63 | } 64 | 65 | #[cfg(any(feature = "nightly", all(doc, not(doctest))))] 66 | mod protected { 67 | use super::*; 68 | use crate::protected::*; 69 | 70 | impl Serialize for HeapByteArray { 71 | fn serialize(&self, serializer: S) -> Result 72 | where 73 | S: Serializer, 74 | { 75 | serializer.serialize_bytes(self.as_slice()) 76 | } 77 | } 78 | 79 | impl Serialize for Locked> { 80 | fn serialize(&self, serializer: S) -> Result 81 | where 82 | S: Serializer, 83 | { 84 | serializer.serialize_bytes(self.as_slice()) 85 | } 86 | } 87 | 88 | impl Serialize for HeapBytes { 89 | fn serialize(&self, serializer: S) -> Result 90 | where 91 | S: Serializer, 92 | { 93 | serializer.serialize_bytes(self.as_slice()) 94 | } 95 | } 96 | 97 | impl Serialize for LockedBytes { 98 | fn serialize(&self, serializer: S) -> Result 99 | where 100 | S: Serializer, 101 | { 102 | serializer.serialize_bytes(self.as_slice()) 103 | } 104 | } 105 | 106 | impl Serialize for LockedRO { 107 | fn serialize(&self, serializer: S) -> Result 108 | where 109 | S: Serializer, 110 | { 111 | serializer.serialize_bytes(self.as_slice()) 112 | } 113 | } 114 | 115 | impl<'de> Deserialize<'de> for HeapBytes { 116 | fn deserialize(deserializer: D) -> Result 117 | where 118 | D: Deserializer<'de>, 119 | { 120 | struct BytesVisitor; 121 | 122 | impl<'de> Visitor<'de> for BytesVisitor { 123 | type Value = HeapBytes; 124 | 125 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 126 | write!(formatter, "bytes") 127 | } 128 | 129 | fn visit_seq(self, mut seq: A) -> Result 130 | where 131 | A: SeqAccess<'de>, 132 | { 133 | let mut arr = HeapBytes::default(); 134 | let mut idx: usize = 0; 135 | let size_hint = seq.size_hint().unwrap_or(1); 136 | arr.resize(size_hint, 0); 137 | 138 | while let Some(elem) = seq.next_element()? { 139 | if idx > arr.len() { 140 | arr.resize(idx, 0); 141 | } 142 | arr[idx] = elem; 143 | idx += 1; 144 | } 145 | 146 | Ok(arr) 147 | } 148 | 149 | fn visit_bytes(self, v: &[u8]) -> Result 150 | where 151 | E: Error, 152 | { 153 | Ok(HeapBytes::from(v)) 154 | } 155 | } 156 | 157 | deserializer.deserialize_bytes(BytesVisitor) 158 | } 159 | } 160 | 161 | impl<'de> Deserialize<'de> for LockedBytes { 162 | fn deserialize(deserializer: D) -> Result 163 | where 164 | D: Deserializer<'de>, 165 | { 166 | struct BytesVisitor; 167 | 168 | impl<'de> Visitor<'de> for BytesVisitor { 169 | type Value = LockedBytes; 170 | 171 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 172 | write!(formatter, "bytes") 173 | } 174 | 175 | fn visit_seq(self, mut seq: A) -> Result 176 | where 177 | A: SeqAccess<'de>, 178 | { 179 | let mut arr = HeapBytes::gen_locked().expect("couldn't create locked bytes"); 180 | let mut idx: usize = 0; 181 | let size_hint = seq.size_hint().unwrap_or(1); 182 | arr.resize(size_hint, 0); 183 | 184 | while let Some(elem) = seq.next_element()? { 185 | if idx > arr.len() { 186 | arr.resize(idx, 0); 187 | } 188 | arr[idx] = elem; 189 | idx += 1; 190 | } 191 | 192 | Ok(arr) 193 | } 194 | 195 | fn visit_bytes(self, v: &[u8]) -> Result 196 | where 197 | E: Error, 198 | { 199 | Ok(HeapBytes::from_slice_into_locked(v) 200 | .expect("couldn't copy slice into locked bytes")) 201 | } 202 | } 203 | 204 | deserializer.deserialize_bytes(BytesVisitor) 205 | } 206 | } 207 | 208 | impl<'de, const LENGTH: usize> Deserialize<'de> for Locked> { 209 | fn deserialize(deserializer: D) -> Result 210 | where 211 | D: Deserializer<'de>, 212 | { 213 | struct BytesVisitor; 214 | 215 | impl<'de, const LENGTH: usize> Visitor<'de> for BytesVisitor { 216 | type Value = Locked>; 217 | 218 | fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { 219 | write!(formatter, "bytes") 220 | } 221 | 222 | fn visit_seq(self, mut seq: A) -> Result 223 | where 224 | A: SeqAccess<'de>, 225 | { 226 | let mut arr = HeapByteArray::::gen_locked() 227 | .expect("couldn't create locked bytes"); 228 | let mut idx: usize = 0; 229 | let size_hint = seq.size_hint().unwrap_or(0); 230 | if size_hint != LENGTH { 231 | Err(Error::invalid_length(size_hint, &stringify!(LENGTH))) 232 | } else { 233 | while let Some(elem) = seq.next_element()? { 234 | arr[idx] = elem; 235 | idx += 1; 236 | } 237 | 238 | Ok(arr) 239 | } 240 | } 241 | 242 | fn visit_bytes(self, v: &[u8]) -> Result 243 | where 244 | E: Error, 245 | { 246 | if v.len() != LENGTH { 247 | Err(Error::invalid_length(v.len(), &stringify!(LENGTH))) 248 | } else { 249 | Ok(HeapByteArray::::from_slice_into_locked(v) 250 | .expect("couldn't copy slice into locked bytes")) 251 | } 252 | } 253 | } 254 | 255 | deserializer.deserialize_bytes(BytesVisitor) 256 | } 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /src/classic/crypto_secretbox.rs: -------------------------------------------------------------------------------- 1 | //! # Authenticated encryption functions 2 | //! 3 | //! Implements libsodium's secret-key authenticated crypto boxes. 4 | //! 5 | //! For details, refer to [libsodium docs](https://libsodium.gitbook.io/doc/secret-key_cryptography/secretbox). 6 | //! 7 | //! ## Classic API example 8 | //! 9 | //! ``` 10 | //! use dryoc::classic::crypto_secretbox::{ 11 | //! Key, Nonce, crypto_secretbox_easy, crypto_secretbox_keygen, crypto_secretbox_open_easy, 12 | //! }; 13 | //! use dryoc::constants::{CRYPTO_SECRETBOX_MACBYTES, CRYPTO_SECRETBOX_NONCEBYTES}; 14 | //! use dryoc::rng::randombytes_buf; 15 | //! use dryoc::types::*; 16 | //! 17 | //! let key: Key = crypto_secretbox_keygen(); 18 | //! let nonce = Nonce::gen(); 19 | //! 20 | //! let message = "I Love Doge!"; 21 | //! 22 | //! // Encrypt 23 | //! let mut ciphertext = vec![0u8; message.len() + CRYPTO_SECRETBOX_MACBYTES]; 24 | //! crypto_secretbox_easy(&mut ciphertext, message.as_bytes(), &nonce, &key) 25 | //! .expect("encrypt failed"); 26 | //! 27 | //! // Decrypt 28 | //! let mut decrypted = vec![0u8; ciphertext.len() - CRYPTO_SECRETBOX_MACBYTES]; 29 | //! crypto_secretbox_open_easy(&mut decrypted, &ciphertext, &nonce, &key).expect("decrypt failed"); 30 | //! 31 | //! assert_eq!(decrypted, message.as_bytes()); 32 | //! ``` 33 | 34 | use crate::classic::crypto_secretbox_impl::*; 35 | use crate::constants::{ 36 | CRYPTO_SECRETBOX_KEYBYTES, CRYPTO_SECRETBOX_MACBYTES, CRYPTO_SECRETBOX_NONCEBYTES, 37 | }; 38 | use crate::error::Error; 39 | use crate::rng::copy_randombytes; 40 | use crate::types::*; 41 | 42 | /// Secret box message authentication code. 43 | pub type Mac = [u8; CRYPTO_SECRETBOX_MACBYTES]; 44 | /// Nonce for secret key authenticated boxes. 45 | pub type Nonce = [u8; CRYPTO_SECRETBOX_NONCEBYTES]; 46 | /// Key (or secret) for secret key authenticated boxes. 47 | pub type Key = [u8; CRYPTO_SECRETBOX_KEYBYTES]; 48 | 49 | /// In-place variant of [`crypto_secretbox_keygen`] 50 | pub fn crypto_secretbox_keygen_inplace(key: &mut Key) { 51 | copy_randombytes(key) 52 | } 53 | 54 | /// Generates a random key using 55 | /// [`copy_randombytes`]. 56 | pub fn crypto_secretbox_keygen() -> Key { 57 | Key::gen() 58 | } 59 | 60 | /// Detached version of [`crypto_secretbox_easy`]. 61 | /// 62 | /// Compatible with libsodium's `crypto_secretbox_detached`. 63 | pub fn crypto_secretbox_detached( 64 | ciphertext: &mut [u8], 65 | mac: &mut Mac, 66 | message: &[u8], 67 | nonce: &Nonce, 68 | key: &Key, 69 | ) { 70 | ciphertext[..message.len()].copy_from_slice(message); 71 | crypto_secretbox_detached_inplace(ciphertext, mac, nonce, key); 72 | } 73 | 74 | /// Detached version of [`crypto_secretbox_open_easy`]. 75 | /// 76 | /// Compatible with libsodium's `crypto_secretbox_open_detached`. 77 | pub fn crypto_secretbox_open_detached( 78 | message: &mut [u8], 79 | mac: &Mac, 80 | ciphertext: &[u8], 81 | nonce: &Nonce, 82 | key: &Key, 83 | ) -> Result<(), Error> { 84 | let c_len = ciphertext.len(); 85 | message[..c_len].copy_from_slice(ciphertext); 86 | crypto_secretbox_open_detached_inplace(message, mac, nonce, key) 87 | } 88 | 89 | /// Encrypts `message` with `nonce` and `key`. 90 | /// 91 | /// Compatible with libsodium's `crypto_secretbox_easy`. 92 | pub fn crypto_secretbox_easy( 93 | ciphertext: &mut [u8], 94 | message: &[u8], 95 | nonce: &Nonce, 96 | key: &Key, 97 | ) -> Result<(), Error> { 98 | let mut mac = Mac::default(); 99 | crypto_secretbox_detached( 100 | &mut ciphertext[CRYPTO_SECRETBOX_MACBYTES..], 101 | &mut mac, 102 | message, 103 | nonce, 104 | key, 105 | ); 106 | 107 | ciphertext[..CRYPTO_SECRETBOX_MACBYTES].copy_from_slice(&mac); 108 | 109 | Ok(()) 110 | } 111 | 112 | /// Decrypts `ciphertext` with `nonce` and `key`. 113 | /// 114 | /// Compatible with libsodium's `crypto_secretbox_open_easy`. 115 | pub fn crypto_secretbox_open_easy( 116 | message: &mut [u8], 117 | ciphertext: &[u8], 118 | nonce: &Nonce, 119 | key: &Key, 120 | ) -> Result<(), Error> { 121 | if ciphertext.len() < CRYPTO_SECRETBOX_MACBYTES { 122 | Err(dryoc_error!(format!( 123 | "Impossibly small box ({} < {}", 124 | ciphertext.len(), 125 | CRYPTO_SECRETBOX_MACBYTES 126 | ))) 127 | } else { 128 | let (mac, ciphertext) = ciphertext.split_at(CRYPTO_SECRETBOX_MACBYTES); 129 | let mac = ByteArray::as_array(mac); 130 | crypto_secretbox_open_detached(message, mac, ciphertext, nonce, key) 131 | } 132 | } 133 | 134 | /// Encrypts `message` with `nonce` and `key` in-place, without allocating 135 | /// additional memory for the ciphertext. 136 | pub fn crypto_secretbox_easy_inplace( 137 | data: &mut [u8], 138 | nonce: &Nonce, 139 | key: &Key, 140 | ) -> Result<(), Error> { 141 | data.rotate_right(CRYPTO_SECRETBOX_MACBYTES); 142 | let (mac, data) = data.split_at_mut(CRYPTO_SECRETBOX_MACBYTES); 143 | let mac = MutByteArray::as_mut_array(mac); 144 | 145 | crypto_secretbox_detached_inplace(data, mac, nonce, key); 146 | 147 | Ok(()) 148 | } 149 | 150 | /// Decrypts `ciphertext` with `nonce` and `key` in-place, without allocating 151 | /// additional memory for the message. 152 | pub fn crypto_secretbox_open_easy_inplace( 153 | ciphertext: &mut [u8], 154 | nonce: &Nonce, 155 | key: &Key, 156 | ) -> Result<(), Error> { 157 | if ciphertext.len() < CRYPTO_SECRETBOX_MACBYTES { 158 | Err(dryoc_error!(format!( 159 | "Impossibly small box ({} < {}", 160 | ciphertext.len(), 161 | CRYPTO_SECRETBOX_MACBYTES 162 | ))) 163 | } else { 164 | let (mac, data) = ciphertext.split_at_mut(CRYPTO_SECRETBOX_MACBYTES); 165 | let mac = ByteArray::as_array(mac); 166 | 167 | crypto_secretbox_open_detached_inplace(data, mac, nonce, key)?; 168 | 169 | ciphertext.rotate_left(CRYPTO_SECRETBOX_MACBYTES); 170 | 171 | Ok(()) 172 | } 173 | } 174 | 175 | #[cfg(test)] 176 | mod tests { 177 | use super::*; 178 | 179 | #[test] 180 | fn test_crypto_secretbox_easy() { 181 | for i in 0..20 { 182 | use base64::Engine as _; 183 | use base64::engine::general_purpose; 184 | use sodiumoxide::crypto::secretbox; 185 | use sodiumoxide::crypto::secretbox::{Key as SOKey, Nonce as SONonce}; 186 | 187 | let key: Key = crypto_secretbox_keygen(); 188 | let nonce = Nonce::gen(); 189 | 190 | let words = vec!["love Doge".to_string(); i]; 191 | let message = words.join(" <3 "); 192 | 193 | let mut ciphertext = vec![0u8; message.len() + CRYPTO_SECRETBOX_MACBYTES]; 194 | crypto_secretbox_easy(&mut ciphertext, message.as_bytes(), &nonce, &key) 195 | .expect("encrypt failed"); 196 | let so_ciphertext = secretbox::seal( 197 | message.as_bytes(), 198 | &SONonce::from_slice(&nonce).unwrap(), 199 | &SOKey::from_slice(&key).unwrap(), 200 | ); 201 | assert_eq!( 202 | general_purpose::STANDARD.encode(&ciphertext), 203 | general_purpose::STANDARD.encode(&so_ciphertext) 204 | ); 205 | 206 | let mut decrypted = vec![0u8; message.len()]; 207 | crypto_secretbox_open_easy(&mut decrypted, &ciphertext, &nonce, &key) 208 | .expect("decrypt failed"); 209 | let so_decrypted = secretbox::open( 210 | &ciphertext, 211 | &SONonce::from_slice(&nonce).unwrap(), 212 | &SOKey::from_slice(&key).unwrap(), 213 | ) 214 | .unwrap(); 215 | 216 | assert_eq!(decrypted, message.as_bytes()); 217 | assert_eq!(decrypted, so_decrypted); 218 | } 219 | } 220 | 221 | #[test] 222 | fn test_crypto_secretbox_easy_inplace() { 223 | for i in 0..20 { 224 | use base64::Engine as _; 225 | use base64::engine::general_purpose; 226 | use sodiumoxide::crypto::secretbox; 227 | use sodiumoxide::crypto::secretbox::{Key as SOKey, Nonce as SONonce}; 228 | 229 | let key = crypto_secretbox_keygen(); 230 | let nonce = Nonce::gen(); 231 | 232 | let words = vec!["love Doge".to_string(); i]; 233 | let message: Vec = words.join(" <3 ").into(); 234 | let message_copy = message.clone(); 235 | 236 | let mut ciphertext = message.clone(); 237 | ciphertext.resize(message.len() + CRYPTO_SECRETBOX_MACBYTES, 0); 238 | crypto_secretbox_easy_inplace(&mut ciphertext, &nonce, &key).expect("encrypt failed"); 239 | let so_ciphertext = secretbox::seal( 240 | &message_copy, 241 | &SONonce::from_slice(&nonce).unwrap(), 242 | &SOKey::from_slice(&key).unwrap(), 243 | ); 244 | assert_eq!( 245 | general_purpose::STANDARD.encode(&ciphertext), 246 | general_purpose::STANDARD.encode(&so_ciphertext) 247 | ); 248 | 249 | let mut decrypted = ciphertext.clone(); 250 | crypto_secretbox_open_easy_inplace(&mut decrypted, &nonce, &key) 251 | .expect("decrypt failed"); 252 | decrypted.resize(ciphertext.len() - CRYPTO_SECRETBOX_MACBYTES, 0); 253 | let so_decrypted = secretbox::open( 254 | &ciphertext, 255 | &SONonce::from_slice(&nonce).unwrap(), 256 | &SOKey::from_slice(&key).unwrap(), 257 | ) 258 | .expect("decrypt failed"); 259 | 260 | assert_eq!(&decrypted, &message_copy); 261 | assert_eq!(decrypted, so_decrypted); 262 | } 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # dryoc: Don't Roll Your Own Crypto™[^1] 2 | //! 3 | //! dryoc is a pure-Rust, general-purpose cryptography library. It's also an 4 | //! implementation of [libsodium](https://libsodium.gitbook.io/doc/), and 5 | //! designed to be 100% compatible with, and interchangeable with, libsodium's 6 | //! API. 7 | //! 8 | //! Doing cryptography properly is _hard_. While no human is infallible, 9 | //! computers are pretty good at following instructions. Humans are bad at 10 | //! following instructions, but they do a decent job of giving instructions, 11 | //! provided they can effectively communicate intent. Thus, if the instructions 12 | //! humans give the computer are correct, we can be reasonably assured 13 | //! that the operations the computer does are correct too. 14 | //! 15 | //! This library tries to make it easy to give the computer the correct 16 | //! instructions, and it does so by providing well-known implementations of 17 | //! general-purpose cryptography functions, in an API that's relatively easy to 18 | //! use, type safe, and hard to use incorrectly. 19 | //! 20 | //! As the name of this library implies, one should avoid trying to "roll their 21 | //! own crypto", as it often results in avoidable mistakes. In the context of 22 | //! cryptography, mistakes can be very costly. 23 | //! 24 | //! The minimum supported Rust version (MSRV) for this crate is **Rust 1.51** or 25 | //! newer. 26 | //! 27 | //! ## Features 28 | //! 29 | //! * 100% pure Rust, no hidden C libraries 30 | //! * mostly free of unsafe code[^2] 31 | //! * Hard to misuse, helping you avoid common costly cryptography mistakes 32 | //! * Many libsodium features implemented with both Classic and Rustaceous API 33 | //! * Protected memory handling (`mprotect()` + `mlock()`, along with Windows 34 | //! equivalents) 35 | //! * [Serde](https://serde.rs/) support (with `features = ["serde"]`) 36 | //! * [_Portable_ SIMD](https://doc.rust-lang.org/std/simd/index.html) 37 | //! implementation for Blake2b (used by generic hashing, password hashing, and 38 | //! key derivation) on nightly, with `features = ["simd_backend", "nightly"]` 39 | //! * SIMD backend for Curve25519 (used by public/private key functions) on 40 | //! nightly with `features = ["simd_backend", "nightly"]` 41 | //! * [SHA2](https://github.com/RustCrypto/hashes/tree/master/sha2) (used by 42 | //! sealed boxes) includes SIMD implementation for AVX2 43 | //! * [ChaCha20](https://github.com/RustCrypto/stream-ciphers/tree/master/chacha20) 44 | //! (used by streaming interface) includes SIMD implementations for Neon, 45 | //! AVX2, and SSE2 46 | //! 47 | //! To enable all the SIMD backends through 3rd party crates, you'll need to 48 | //! also set `RUSTFLAGS`: 49 | //! * For AVX2 set `RUSTFLAGS=-Ctarget-cpu=haswell -Ctarget-feature=+avx2` 50 | //! * For SSE2 set `RUSTFLAGS=-Ctarget-feature=+sse2` 51 | //! * For Neon set `RUSTFLAGS=-Ctarget-feature=+neon` 52 | //! 53 | //! _Note that eventually this project will converge on portable SIMD 54 | //! implementations for all the core algos which will work across all platforms 55 | //! supported by LLVM, rather than relying on hand-coded assembly or intrinsics, 56 | //! but his is a work in progress_. 57 | //! 58 | //! ## APIs 59 | //! 60 | //! This library includes both a _Classic_ API, which is very similar to the 61 | //! original libsodium API, and _Rustaceous_ API with Rust-specific features. 62 | //! Both APIs can be used together interchangeably, according to your 63 | //! preferences. The Rustaceous API is a wrapper around the underlying classic 64 | //! API. 65 | //! 66 | //! It's recommended that you use the Rustaceous API unless you have strong 67 | //! feelings about using the Classic API. The Classic API includes some pitfalls 68 | //! and traps that are also present in the original libsodium API, and unless 69 | //! you're extra careful you could make mistakes. With the Rustaceous API, it's 70 | //! harder to make mistakes thanks to strict type and safety features. 71 | //! 72 | //! The Rustaceous API is, arguably, somewhat trickier to use, especially if 73 | //! you're new to Rust. The Rustaceous API requires knowing and specifying the 74 | //! desired type in many cases. For your convenience, type aliases are provided 75 | //! for common types within each module. The Classic API only uses base types 76 | //! (fixed length byte arrays and byte slices). 77 | //! 78 | //! | Feature | Rustaceous API | Classic API | Libsodium Docs | 79 | //! |-|-|-|-| 80 | //! | Public-key authenticated boxes | [`DryocBox`](dryocbox) | [`crypto_box`](classic::crypto_box) | [Link](https://libsodium.gitbook.io/doc/public-key_cryptography/authenticated_encryption) | 81 | //! | Secret-key authenticated boxes | [`DryocSecretBox`](dryocsecretbox) | [`crypto_secretbox`](classic::crypto_secretbox) | [Link](https://libsodium.gitbook.io/doc/secret-key_cryptography/secretbox) | 82 | //! | Streaming encryption | [`DryocStream`](dryocstream) | [`crypto_secretstream_xchacha20poly1305`](classic::crypto_secretstream_xchacha20poly1305) | [Link](https://libsodium.gitbook.io/doc/secret-key_cryptography/secretstream) | 83 | //! | Generic hashing, HMAC | [`GenericHash`](generichash) | [`crypto_generichash`](classic::crypto_generichash) | [Link](https://doc.libsodium.org/hashing/generic_hashing) | 84 | //! | Secret-key authentication | [`Auth`](auth) | [`crypto_auth`](classic::crypto_auth) | [Link](https://doc.libsodium.org/secret-key_cryptography/secret-key_authentication) | 85 | //! | One-time authentication | [`OnetimeAuth`](onetimeauth) | [`crypto_onetimeauth`](classic::crypto_onetimeauth) | [Link](https://doc.libsodium.org/advanced/poly1305) | 86 | //! | Key derivation | [`Kdf`](kdf) | [`crypto_kdf`](classic::crypto_kdf) | [Link](https://doc.libsodium.org/key_derivation) | 87 | //! | Key exchange | [`Session`](kx) | [`crypto_kx`](classic::crypto_kx) | [Link](https://doc.libsodium.org/key_exchange) | 88 | //! | Public-key signatures | [`SigningKeyPair`](sign) | [`crypto_sign`](classic::crypto_sign) | [Link](https://libsodium.gitbook.io/doc/public-key_cryptography/public-key_signatures) | 89 | //! | Password hashing | [`PwHash`](pwhash) | [`crypto_pwhash`](classic::crypto_pwhash) | [Link](https://libsodium.gitbook.io/doc/password_hashing/default_phf) | 90 | //! | Protected memory[^4] | [protected] | N/A | [Link](https://doc.libsodium.org/memory_management) | 91 | //! | Short-input hashing | N/A | [`crypto_shorthash`](classic::crypto_shorthash) | [Link](https://libsodium.gitbook.io/doc/hashing/short-input_hashing) | 92 | //! 93 | //! ## Using Serde 94 | //! 95 | //! This crate includes optional [Serde](https://serde.rs/) support which can be 96 | //! enabled with the `serde` feature flag. When enabled, the 97 | //! [`Serialize`](serde::ser::Serialize) and 98 | //! [`Deserialize`](serde::de::Deserialize) traits are provided for data 99 | //! structures. 100 | //! 101 | //! ## Security notes 102 | //! 103 | //! This crate has not been audited by any 3rd parties. It uses well-known 104 | //! implementations of the underlying algorithms which have been previously 105 | //! verified as using constant-time operations. 106 | //! 107 | //! With that out of the way, the deterministic nature of cryptography and 108 | //! extensive testing used in this crate means it's relatively safe to use, 109 | //! provided the underlying algorithms remain safe. Arguably, this crate is 110 | //! _incredibly_ safe (as far as cryptography libraries go) thanks to the 111 | //! features provided by the API of this crate, and those provided by the Rust 112 | //! language itself. 113 | //! 114 | //! ## Acknowledgements 115 | //! 116 | //! Big ups to the authors and contributors of [NaCl](https://nacl.cr.yp.to/) and [libsodium](https://github.com/jedisct1/libsodium) for paving the 117 | //! way toward better cryptography libraries. 118 | //! 119 | //! [^1]: Not actually trademarked. 120 | //! 121 | //! [^2]: The protected memory features described in the [protected] mod require 122 | //! custom memory allocation, system calls, and pointer arithmetic, which are 123 | //! unsafe in Rust. Some of the 3rd party libraries used by this crate, such as 124 | //! those with SIMD, may contain unsafe code. In particular, most SIMD 125 | //! implementations are considered "unsafe" due to their use of assembly or 126 | //! intrinsics, however without SIMD-based cryptography you may be exposed to 127 | //! timing attacks. 128 | //! 129 | //! [^3]: The Rustaceous API is designed to protect users of this library from 130 | //! making mistakes, however the Classic API allows one to do as one pleases. 131 | //! 132 | //! [^4]: Currently only available on nightly Rust, with the `nightly` feature 133 | //! flag enabled. 134 | 135 | #![cfg_attr( 136 | any(feature = "nightly", all(feature = "nightly", doc)), 137 | feature(allocator_api, doc_cfg) 138 | )] 139 | #![cfg_attr( 140 | all(feature = "simd_backend", feature = "nightly"), 141 | feature(portable_simd) 142 | )] 143 | #![cfg_attr(feature = "nightly", feature(test))] 144 | #[macro_use] 145 | mod error; 146 | #[cfg(any(feature = "nightly", all(doc, not(doctest))))] 147 | #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))] 148 | #[macro_use] 149 | pub mod protected; 150 | 151 | mod argon2; 152 | mod blake2b; 153 | #[cfg(feature = "serde")] 154 | mod bytes_serde; 155 | mod poly1305; 156 | mod scalarmult_curve25519; 157 | mod siphash24; 158 | 159 | pub mod classic { 160 | //! # Classic API 161 | //! 162 | //! The Classic API contains functions designed to match the interface of 163 | //! libsodium as closely as possible. It's provided to make it easier to 164 | //! switch code from using libsodium directly over to dryoc, and also to 165 | //! provide a familiar interface for anyone already comfortable with 166 | //! libsodium. 167 | mod crypto_box_impl; 168 | mod crypto_secretbox_impl; 169 | mod generichash_blake2b; 170 | 171 | pub mod crypto_auth; 172 | pub mod crypto_box; 173 | /// # Core cryptography functions 174 | pub mod crypto_core; 175 | pub mod crypto_generichash; 176 | /// Hash functions 177 | pub mod crypto_hash; 178 | pub mod crypto_kdf; 179 | pub mod crypto_kx; 180 | pub mod crypto_onetimeauth; 181 | pub mod crypto_pwhash; 182 | pub mod crypto_secretbox; 183 | pub mod crypto_secretstream_xchacha20poly1305; 184 | pub mod crypto_shorthash; 185 | pub mod crypto_sign; 186 | pub mod crypto_sign_ed25519; 187 | } 188 | 189 | pub mod auth; 190 | /// # Constant value definitions 191 | pub mod constants; 192 | pub mod dryocbox; 193 | pub mod dryocsecretbox; 194 | pub mod dryocstream; 195 | pub mod generichash; 196 | pub mod kdf; 197 | pub mod keypair; 198 | pub mod kx; 199 | pub mod onetimeauth; 200 | pub mod precalc; 201 | pub mod pwhash; 202 | /// # Random number generation utilities 203 | pub mod rng; 204 | pub mod sha512; 205 | pub mod sign; 206 | /// # Base type definitions 207 | pub mod types; 208 | /// # Various utility functions 209 | pub mod utils; 210 | 211 | pub use error::Error; 212 | 213 | #[cfg(test)] 214 | mod tests { 215 | 216 | #[test] 217 | fn test_randombytes_buf() { 218 | use crate::rng::*; 219 | let r = randombytes_buf(5); 220 | assert_eq!(r.len(), 5); 221 | let sum = r.into_iter().fold(0u64, |acc, n| acc + n as u64); 222 | assert_ne!(sum, 0); 223 | } 224 | } 225 | -------------------------------------------------------------------------------- /src/kx.rs: -------------------------------------------------------------------------------- 1 | //! # Key exchange functions 2 | //! 3 | //! [`Session`] implements libsodium's key exchange functions, which use a 4 | //! combination of Curve25519, Diffie-Hellman, and Blake2b to generate shared 5 | //! session keys between two parties who know each other's public keys. 6 | //! 7 | //! You should use [`Session`] when you want to: 8 | //! 9 | //! * derive shared secrets between two parties 10 | //! * use public-key cryptography, but do so with another cipher that only 11 | //! supports pre-shared secrets 12 | //! * create a session key or token that can't be used to derive the original 13 | //! inputs should it become compromised 14 | //! 15 | //! # Rustaceous API example 16 | //! 17 | //! ``` 18 | //! use dryoc::kx::*; 19 | //! 20 | //! // Generate random client/server keypairs 21 | //! let client_keypair = KeyPair::gen(); 22 | //! let server_keypair = KeyPair::gen(); 23 | //! 24 | //! // Compute client session keys, into default stack-allocated byte array 25 | //! let client_session_keys = 26 | //! Session::new_client_with_defaults(&client_keypair, &server_keypair.public_key) 27 | //! .expect("compute client failed"); 28 | //! 29 | //! // Compute server session keys, into default stack-allocated byte array 30 | //! let server_session_keys = 31 | //! Session::new_server_with_defaults(&server_keypair, &client_keypair.public_key) 32 | //! .expect("compute client failed"); 33 | //! 34 | //! let (client_rx, client_tx) = client_session_keys.into_parts(); 35 | //! let (server_rx, server_tx) = server_session_keys.into_parts(); 36 | //! 37 | //! // Client Rx should match server Tx keys 38 | //! assert_eq!(client_rx, server_tx); 39 | //! // Client Tx should match server Rx keys 40 | //! assert_eq!(client_tx, server_rx); 41 | //! ``` 42 | //! 43 | //! ## Additional resources 44 | //! 45 | //! * See for additional details on key 46 | //! exchange 47 | 48 | #[cfg(feature = "serde")] 49 | use serde::{Deserialize, Serialize}; 50 | use zeroize::Zeroize; 51 | 52 | use crate::classic::crypto_kx::{crypto_kx_client_session_keys, crypto_kx_server_session_keys}; 53 | use crate::constants::{ 54 | CRYPTO_KX_PUBLICKEYBYTES, CRYPTO_KX_SECRETKEYBYTES, CRYPTO_KX_SESSIONKEYBYTES, 55 | }; 56 | use crate::error::Error; 57 | use crate::types::*; 58 | 59 | /// Stack-allocated session key type alias 60 | pub type SessionKey = StackByteArray; 61 | /// Stack-allocated public key type alias 62 | pub type PublicKey = StackByteArray; 63 | /// Stack-allocated secret key type alias 64 | pub type SecretKey = StackByteArray; 65 | /// Stack-allocated keypair type alias 66 | pub type KeyPair = crate::keypair::KeyPair; 67 | 68 | #[cfg_attr( 69 | feature = "serde", 70 | derive(Zeroize, Clone, Debug, Serialize, Deserialize) 71 | )] 72 | #[cfg_attr(not(feature = "serde"), derive(Zeroize, Clone, Debug))] 73 | /// Key derivation implemantation based on Curve25519, Diffie-Hellman, and 74 | /// Blake2b. Compatible with libsodium's `crypto_kx_*` functions. 75 | pub struct Session + Zeroize> { 76 | rx_key: SessionKey, 77 | tx_key: SessionKey, 78 | } 79 | 80 | /// Stack-allocated type alias for [`Session`]. Provided for convenience. 81 | pub type StackSession = Session; 82 | 83 | #[cfg(any(feature = "nightly", all(doc, not(doctest))))] 84 | #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))] 85 | pub mod protected { 86 | //! # Protected memory type aliases for [`Session`] 87 | //! 88 | //! This mod provides re-exports of type aliases for protected memory usage 89 | //! with [`Session`]. These type aliases are provided for 90 | //! convenience. 91 | //! 92 | //! ## Example 93 | //! 94 | //! ``` 95 | //! use dryoc::kx::Session; 96 | //! use dryoc::kx::protected::*; 97 | //! 98 | //! // Generate random client/server keypairs 99 | //! let client_keypair = 100 | //! LockedROKeyPair::gen_readonly_locked_keypair().expect("couldn't generate client keypair"); 101 | //! let server_keypair = 102 | //! LockedROKeyPair::gen_readonly_locked_keypair().expect("couldn't generate server keypair"); 103 | //! 104 | //! // Compute client session keys, into default stack-allocated byte array 105 | //! let client_session_keys: LockedSession = 106 | //! Session::new_client(&client_keypair, &server_keypair.public_key) 107 | //! .expect("compute client failed"); 108 | //! 109 | //! // Compute server session keys, into default stack-allocated byte array 110 | //! let server_session_keys: LockedSession = 111 | //! Session::new_server(&server_keypair, &client_keypair.public_key) 112 | //! .expect("compute client failed"); 113 | //! 114 | //! let (client_rx, client_tx) = client_session_keys.into_parts(); 115 | //! let (server_rx, server_tx) = server_session_keys.into_parts(); 116 | //! 117 | //! // Client Rx should match server Tx keys 118 | //! assert_eq!(client_rx.as_slice(), server_tx.as_slice()); 119 | //! // Client Tx should match server Rx keys 120 | //! assert_eq!(client_tx.as_slice(), server_rx.as_slice()); 121 | //! ``` 122 | use super::*; 123 | pub use crate::keypair::protected::*; 124 | 125 | /// Heap-allocated, paged-aligned session key type alias for use with 126 | /// protected memory 127 | pub type SessionKey = HeapByteArray; 128 | /// Heap-allocated, paged-aligned public key type alias for use with 129 | /// protected memory 130 | pub type PublicKey = HeapByteArray; 131 | /// Heap-allocated, paged-aligned secret key type alias for use with 132 | /// protected memory 133 | pub type SecretKey = HeapByteArray; 134 | 135 | /// Heap-allocated, paged-aligned keypair type alias for use with 136 | /// protected memory 137 | pub type LockedKeyPair = crate::keypair::KeyPair, Locked>; 138 | /// Heap-allocated, paged-aligned keypair type alias for use with 139 | /// protected memory 140 | pub type LockedROKeyPair = crate::keypair::KeyPair, LockedRO>; 141 | /// Locked session keys type alias, for use with protected memory 142 | pub type LockedSession = Session>; 143 | } 144 | 145 | impl + Zeroize> Session { 146 | /// Computes client session keys, given `client_keypair` and 147 | /// `server_public_key`, returning a new session upon success. 148 | pub fn new_client< 149 | PublicKey: ByteArray + Zeroize, 150 | SecretKey: ByteArray + Zeroize, 151 | >( 152 | client_keypair: &crate::keypair::KeyPair, 153 | server_public_key: &PublicKey, 154 | ) -> Result { 155 | let mut rx_key = SessionKey::new_byte_array(); 156 | let mut tx_key = SessionKey::new_byte_array(); 157 | 158 | crypto_kx_client_session_keys( 159 | rx_key.as_mut_array(), 160 | tx_key.as_mut_array(), 161 | client_keypair.public_key.as_array(), 162 | client_keypair.secret_key.as_array(), 163 | server_public_key.as_array(), 164 | )?; 165 | 166 | Ok(Self { rx_key, tx_key }) 167 | } 168 | 169 | /// Computes server session keys, given `server_keypair` and 170 | /// `client_public_key`, returning a new session upon success. 171 | pub fn new_server< 172 | PublicKey: ByteArray + Zeroize, 173 | SecretKey: ByteArray + Zeroize, 174 | >( 175 | server_keypair: &crate::keypair::KeyPair, 176 | client_public_key: &PublicKey, 177 | ) -> Result { 178 | let mut rx_key = SessionKey::new_byte_array(); 179 | let mut tx_key = SessionKey::new_byte_array(); 180 | 181 | crypto_kx_server_session_keys( 182 | rx_key.as_mut_array(), 183 | tx_key.as_mut_array(), 184 | server_keypair.public_key.as_array(), 185 | server_keypair.secret_key.as_array(), 186 | client_public_key.as_array(), 187 | )?; 188 | 189 | Ok(Self { rx_key, tx_key }) 190 | } 191 | } 192 | 193 | impl Session { 194 | /// Returns a new client session upon success using the default types for 195 | /// the given `client_keypair` and `server_public_key`. Wraps 196 | /// [`Session::new_client`], provided for convenience. 197 | pub fn new_client_with_defaults< 198 | PublicKey: ByteArray + Zeroize, 199 | SecretKey: ByteArray + Zeroize, 200 | >( 201 | client_keypair: &crate::keypair::KeyPair, 202 | server_public_key: &PublicKey, 203 | ) -> Result { 204 | Self::new_client(client_keypair, server_public_key) 205 | } 206 | 207 | /// Returns a new server session upon success using the default types for 208 | /// the given `server_keypair` and `client_public_key`. Wraps 209 | /// [`Session::new_server`], provided for convenience. 210 | pub fn new_server_with_defaults< 211 | PublicKey: ByteArray + Zeroize, 212 | SecretKey: ByteArray + Zeroize, 213 | >( 214 | server_keypair: &crate::keypair::KeyPair, 215 | client_public_key: &PublicKey, 216 | ) -> Result { 217 | Self::new_server(server_keypair, client_public_key) 218 | } 219 | } 220 | 221 | impl + Zeroize> Session { 222 | /// Moves the rx_key and tx_key out of this instance, returning them as a 223 | /// tuple with `(rx_key, tx_key)`. 224 | pub fn into_parts(self) -> (SessionKey, SessionKey) { 225 | (self.rx_key, self.tx_key) 226 | } 227 | 228 | /// Returns a reference to a slice of the Rx session key. 229 | #[inline] 230 | pub fn rx_as_slice(&self) -> &[u8] { 231 | self.rx_key.as_slice() 232 | } 233 | 234 | /// Returns a reference to a slice of the Tx session key. 235 | #[inline] 236 | pub fn tx_as_slice(&self) -> &[u8] { 237 | self.tx_key.as_slice() 238 | } 239 | 240 | /// Returns a reference to an array of the Rx session key. 241 | #[inline] 242 | pub fn rx_as_array(&self) -> &[u8; CRYPTO_KX_SESSIONKEYBYTES] { 243 | self.rx_key.as_array() 244 | } 245 | 246 | /// Returns a reference to an array of the Tx session key. 247 | #[inline] 248 | pub fn tx_as_array(&self) -> &[u8; CRYPTO_KX_SESSIONKEYBYTES] { 249 | self.tx_key.as_array() 250 | } 251 | } 252 | 253 | #[cfg(test)] 254 | mod tests { 255 | use super::*; 256 | 257 | #[test] 258 | fn test_kx() { 259 | let client_keypair = KeyPair::gen(); 260 | let server_keypair = KeyPair::gen(); 261 | 262 | let client_session_keys = 263 | Session::new_client_with_defaults(&client_keypair, &server_keypair.public_key) 264 | .expect("compute client failed"); 265 | 266 | let server_session_keys = 267 | Session::new_server_with_defaults(&server_keypair, &client_keypair.public_key) 268 | .expect("compute client failed"); 269 | 270 | let (client_rx, client_tx) = client_session_keys.into_parts(); 271 | let (server_rx, server_tx) = server_session_keys.into_parts(); 272 | 273 | assert_eq!(client_rx, server_tx); 274 | assert_eq!(client_tx, server_rx); 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /src/classic/crypto_sign.rs: -------------------------------------------------------------------------------- 1 | //! # Public-key signatures 2 | //! 3 | //! This module implements libsodium's public-key signatures, based on Ed25519. 4 | //! 5 | //! ## Classic API example 6 | //! 7 | //! ``` 8 | //! use dryoc::classic::crypto_sign::*; 9 | //! use dryoc::constants::CRYPTO_SIGN_BYTES; 10 | //! 11 | //! // Generate a random signing keypair 12 | //! let (public_key, secret_key) = crypto_sign_keypair(); 13 | //! let message = b"These violent delights have violent ends..."; 14 | //! 15 | //! // Signed message buffer needs to be correct length 16 | //! let mut signed_message = vec![0u8; message.len() + CRYPTO_SIGN_BYTES]; 17 | //! 18 | //! // Sign the message, placing the result into `signed_message` 19 | //! crypto_sign(&mut signed_message, message, &secret_key).expect("sign failed"); 20 | //! 21 | //! // Allocate a new buffer for opening the message 22 | //! let mut opened_message = vec![0u8; message.len()]; 23 | //! 24 | //! // Open the signed message, verifying the signature 25 | //! crypto_sign_open(&mut opened_message, &signed_message, &public_key).expect("verify failed"); 26 | //! 27 | //! assert_eq!(&opened_message, message); 28 | //! 29 | //! // Create an invalid message 30 | //! let mut invalid_signed_message = signed_message.clone(); 31 | //! invalid_signed_message[5] = !invalid_signed_message[5]; 32 | //! 33 | //! // An invalid message can't be verified 34 | //! crypto_sign_open(&mut opened_message, &invalid_signed_message, &public_key) 35 | //! .expect_err("open should not succeed"); 36 | //! ``` 37 | //! 38 | //! ## Classic API example, detached mode 39 | //! 40 | //! ``` 41 | //! use dryoc::classic::crypto_sign::*; 42 | //! use dryoc::constants::CRYPTO_SIGN_BYTES; 43 | //! 44 | //! // Generate a random signing keypair 45 | //! let (public_key, secret_key) = crypto_sign_keypair(); 46 | //! let message = b"Brevity is the soul of wit."; 47 | //! let mut signature = [0u8; CRYPTO_SIGN_BYTES]; 48 | //! 49 | //! // Sign our message 50 | //! crypto_sign_detached(&mut signature, message, &secret_key).expect("sign failed"); 51 | //! 52 | //! // Verify the signature 53 | //! crypto_sign_verify_detached(&signature, message, &public_key).expect("verify failed"); 54 | //! ``` 55 | 56 | use super::crypto_sign_ed25519::*; 57 | pub use super::crypto_sign_ed25519::{PublicKey, SecretKey}; 58 | use crate::constants::CRYPTO_SIGN_BYTES; 59 | use crate::error::Error; 60 | 61 | /// In-place variant of [`crypto_sign_keypair`]. 62 | pub fn crypto_sign_keypair_inplace(public_key: &mut PublicKey, secret_key: &mut SecretKey) { 63 | crypto_sign_ed25519_keypair_inplace(public_key, secret_key) 64 | } 65 | 66 | /// In-place variant of [`crypto_sign_seed_keypair`]. 67 | pub fn crypto_sign_seed_keypair_inplace( 68 | public_key: &mut PublicKey, 69 | secret_key: &mut SecretKey, 70 | seed: &[u8; 32], 71 | ) { 72 | crypto_sign_ed25519_seed_keypair_inplace(public_key, secret_key, seed) 73 | } 74 | 75 | /// Randomly generates a new Ed25519 `(PublicKey, SecretKey)` keypair that can 76 | /// be used for message signing. 77 | pub fn crypto_sign_keypair() -> (PublicKey, SecretKey) { 78 | crypto_sign_ed25519_keypair() 79 | } 80 | 81 | /// Returns a keypair derived from `seed`, which can be used for message 82 | /// signing. 83 | pub fn crypto_sign_seed_keypair(seed: &[u8; 32]) -> (PublicKey, SecretKey) { 84 | crypto_sign_ed25519_seed_keypair(seed) 85 | } 86 | 87 | /// Signs `message`, placing the result into `signed_message`. The length of 88 | /// `signed_message` should be the length of the message plus 89 | /// [`CRYPTO_SIGN_BYTES`]. 90 | /// 91 | /// This function is compatible with libsodium`s `crypto_sign`, however the 92 | /// `ED25519_NONDETERMINISTIC` feature is not supported. 93 | pub fn crypto_sign( 94 | signed_message: &mut [u8], 95 | message: &[u8], 96 | secret_key: &SecretKey, 97 | ) -> Result<(), Error> { 98 | if signed_message.len() != message.len() + CRYPTO_SIGN_BYTES { 99 | Err(dryoc_error!(format!( 100 | "signed_message length incorrect (expect {}, got {})", 101 | message.len() + CRYPTO_SIGN_BYTES, 102 | signed_message.len() 103 | ))) 104 | } else { 105 | crypto_sign_ed25519(signed_message, message, secret_key) 106 | } 107 | } 108 | 109 | /// Verifies the signature of `signed_message`, placing the result into 110 | /// `message`. The length of `message` should be the length of the signed 111 | /// message minus [`CRYPTO_SIGN_BYTES`]. 112 | /// 113 | /// This function is compatible with libsodium`s `crypto_sign_open`, however the 114 | /// `ED25519_NONDETERMINISTIC` feature is not supported. 115 | pub fn crypto_sign_open( 116 | message: &mut [u8], 117 | signed_message: &[u8], 118 | public_key: &PublicKey, 119 | ) -> Result<(), Error> { 120 | if signed_message.len() < CRYPTO_SIGN_BYTES { 121 | Err(dryoc_error!(format!( 122 | "signed_message length invalid ({} < {})", 123 | signed_message.len(), 124 | CRYPTO_SIGN_BYTES, 125 | ))) 126 | } else if message.len() != signed_message.len() - CRYPTO_SIGN_BYTES { 127 | Err(dryoc_error!(format!( 128 | "message length incorrect (expect {}, got {})", 129 | signed_message.len() - CRYPTO_SIGN_BYTES, 130 | message.len() 131 | ))) 132 | } else { 133 | crypto_sign_ed25519_open(message, signed_message, public_key) 134 | } 135 | } 136 | 137 | /// Signs `message`, placing the signature into `signature` upon success. 138 | /// Detached variant of [`crypto_sign_open`]. 139 | /// 140 | /// This function is compatible with libsodium`s `crypto_sign_detached`, however 141 | /// the `ED25519_NONDETERMINISTIC` feature is not supported. 142 | pub fn crypto_sign_detached( 143 | signature: &mut Signature, 144 | message: &[u8], 145 | secret_key: &SecretKey, 146 | ) -> Result<(), Error> { 147 | crypto_sign_ed25519_detached(signature, message, secret_key) 148 | } 149 | 150 | /// Verifies that `signature` is a valid signature for `message` using the given 151 | /// `public_key`. 152 | /// 153 | /// This function is compatible with libsodium`s `crypto_sign_verify_detached`, 154 | /// however the `ED25519_NONDETERMINISTIC` feature is not supported. 155 | pub fn crypto_sign_verify_detached( 156 | signature: &Signature, 157 | message: &[u8], 158 | public_key: &PublicKey, 159 | ) -> Result<(), Error> { 160 | crypto_sign_ed25519_verify_detached(signature, message, public_key) 161 | } 162 | 163 | /// State for incremental signing interface. 164 | pub struct SignerState { 165 | state: Ed25519SignerState, 166 | } 167 | 168 | /// Initializes the incremental signing interface. 169 | pub fn crypto_sign_init() -> SignerState { 170 | SignerState { 171 | state: crypto_sign_ed25519ph_init(), 172 | } 173 | } 174 | 175 | /// Updates the signature for `state` with `message`. 176 | pub fn crypto_sign_update(state: &mut SignerState, message: &[u8]) { 177 | crypto_sign_ed25519ph_update(&mut state.state, message) 178 | } 179 | 180 | /// Finalizes the incremental signature for `state`, using `secret_key`, copying 181 | /// the result into `signature` upon success, and consuming the state. 182 | pub fn crypto_sign_final_create( 183 | state: SignerState, 184 | signature: &mut Signature, 185 | secret_key: &SecretKey, 186 | ) -> Result<(), Error> { 187 | crypto_sign_ed25519ph_final_create(state.state, signature, secret_key) 188 | } 189 | 190 | /// Verifies the computed signature for `state` and `public_key` matches 191 | /// `signature`, consuming the state. 192 | pub fn crypto_sign_final_verify( 193 | state: SignerState, 194 | signature: &Signature, 195 | public_key: &PublicKey, 196 | ) -> Result<(), Error> { 197 | crypto_sign_ed25519ph_final_verify(state.state, signature, public_key) 198 | } 199 | 200 | #[cfg(test)] 201 | mod tests { 202 | use super::*; 203 | 204 | #[test] 205 | fn test_crypto_sign() { 206 | use base64::Engine as _; 207 | use base64::engine::general_purpose; 208 | use sodiumoxide::crypto::sign; 209 | 210 | for _ in 0..10 { 211 | let (public_key, secret_key) = crypto_sign_keypair(); 212 | let message = b"important message"; 213 | let mut signed_message = vec![0u8; message.len() + CRYPTO_SIGN_BYTES]; 214 | crypto_sign(&mut signed_message, message, &secret_key).expect("sign failed"); 215 | 216 | let so_signed_message = sign::sign( 217 | message, 218 | &sign::SecretKey::from_slice(&secret_key).expect("secret key failed"), 219 | ); 220 | 221 | assert_eq!( 222 | general_purpose::STANDARD.encode(&signed_message), 223 | general_purpose::STANDARD.encode(&so_signed_message) 224 | ); 225 | 226 | let so_m = sign::verify( 227 | &signed_message, 228 | &sign::PublicKey::from_slice(&public_key).expect("public key failed"), 229 | ) 230 | .expect("verify failed"); 231 | 232 | assert_eq!(so_m, message); 233 | } 234 | } 235 | 236 | #[test] 237 | fn test_crypto_sign_open() { 238 | use base64::Engine as _; 239 | use base64::engine::general_purpose; 240 | use sodiumoxide::crypto::sign; 241 | 242 | for _ in 0..10 { 243 | let (public_key, secret_key) = crypto_sign_keypair(); 244 | let message = b"important message"; 245 | let mut signed_message = vec![0u8; message.len() + CRYPTO_SIGN_BYTES]; 246 | crypto_sign(&mut signed_message, message, &secret_key).expect("sign failed"); 247 | 248 | let so_signed_message = sign::sign( 249 | message, 250 | &sign::SecretKey::from_slice(&secret_key).expect("secret key failed"), 251 | ); 252 | 253 | assert_eq!( 254 | general_purpose::STANDARD.encode(&signed_message), 255 | general_purpose::STANDARD.encode(&so_signed_message) 256 | ); 257 | 258 | let so_m = sign::verify( 259 | &signed_message, 260 | &sign::PublicKey::from_slice(&public_key).expect("public key failed"), 261 | ) 262 | .expect("verify failed"); 263 | 264 | assert_eq!(so_m, message); 265 | 266 | let mut opened_message = vec![0u8; message.len()]; 267 | 268 | crypto_sign_open(&mut opened_message, &signed_message, &public_key) 269 | .expect("verify failed"); 270 | 271 | assert_eq!(opened_message, message); 272 | } 273 | } 274 | 275 | #[test] 276 | fn test_crypto_sign_detached() { 277 | use sodiumoxide::crypto::sign; 278 | 279 | for _ in 0..10 { 280 | let (public_key, secret_key) = crypto_sign_keypair(); 281 | let message = b"important message"; 282 | let mut signature = [0u8; CRYPTO_SIGN_BYTES]; 283 | crypto_sign_detached(&mut signature, message, &secret_key).expect("sign failed"); 284 | 285 | assert!(sign::verify_detached( 286 | &sign::ed25519::Signature::from_bytes(&signature).expect("secret key failed"), 287 | message, 288 | &sign::PublicKey::from_slice(&public_key).expect("public key failed"), 289 | )); 290 | 291 | crypto_sign_verify_detached(&signature, message, &public_key).expect("verify failed"); 292 | } 293 | } 294 | 295 | #[test] 296 | fn test_crypto_sign_incremental() { 297 | use sodiumoxide::crypto::sign; 298 | 299 | use crate::rng::copy_randombytes; 300 | 301 | for _ in 0..10 { 302 | let (public_key, secret_key) = crypto_sign_keypair(); 303 | let mut signer = crypto_sign_init(); 304 | let mut verifier = crypto_sign_init(); 305 | 306 | let mut so_signer = sign::State::init(); 307 | let mut so_verifier = sign::State::init(); 308 | 309 | for _ in 0..3 { 310 | let mut randos = vec![0u8; 100]; 311 | copy_randombytes(&mut randos); 312 | 313 | crypto_sign_update(&mut signer, &randos); 314 | crypto_sign_update(&mut verifier, &randos); 315 | 316 | so_signer.update(&randos); 317 | so_verifier.update(&randos); 318 | } 319 | 320 | let mut signature = [0u8; CRYPTO_SIGN_BYTES]; 321 | crypto_sign_final_create(signer, &mut signature, &secret_key) 322 | .expect("final create failed"); 323 | 324 | let so_signature = so_signer 325 | .finalize(&sign::SecretKey::from_slice(&secret_key).expect("secret key failed")); 326 | 327 | assert_eq!(signature, so_signature.to_bytes()); 328 | 329 | crypto_sign_final_verify(verifier, &so_signature.to_bytes(), &public_key) 330 | .expect("verify failed"); 331 | 332 | assert!(so_signer.verify( 333 | &sign::ed25519::Signature::from_bytes(&signature).expect("secret key failed"), 334 | &sign::PublicKey::from_slice(&public_key).expect("public key failed"), 335 | )); 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /src/constants.rs: -------------------------------------------------------------------------------- 1 | #![allow(missing_docs)] 2 | 3 | const fn min(a: usize, b: usize) -> usize { 4 | [a, b][(a > b) as usize] 5 | } 6 | const fn max(a: usize, b: usize) -> usize { 7 | [a, b][(a < b) as usize] 8 | } 9 | 10 | const SODIUM_SIZE_MAX: usize = min(usize::MAX, u64::MAX as usize); 11 | 12 | pub const CRYPTO_SCALARMULT_CURVE25519_BYTES: usize = 32; 13 | pub const CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES: usize = 32; 14 | 15 | pub const CRYPTO_SCALARMULT_BYTES: usize = CRYPTO_SCALARMULT_CURVE25519_BYTES; 16 | pub const CRYPTO_SCALARMULT_SCALARBYTES: usize = CRYPTO_SCALARMULT_CURVE25519_SCALARBYTES; 17 | 18 | const CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES: usize = 32; 19 | const CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES: usize = 32; 20 | const CRYPTO_BOX_CURVE25519XSALSA20POLY1305_MACBYTES: usize = 16; 21 | const CRYPTO_BOX_CURVE25519XSALSA20POLY1305_NONCEBYTES: usize = 24; 22 | const CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SEEDBYTES: usize = 32; 23 | const CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BEFORENMBYTES: usize = 32; 24 | 25 | const CRYPTO_STREAM_XSALSA20_MESSAGEBYTES_MAX: usize = SODIUM_SIZE_MAX; 26 | 27 | pub const CRYPTO_BOX_PUBLICKEYBYTES: usize = CRYPTO_BOX_CURVE25519XSALSA20POLY1305_PUBLICKEYBYTES; 28 | pub const CRYPTO_BOX_SECRETKEYBYTES: usize = CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SECRETKEYBYTES; 29 | pub const CRYPTO_BOX_MACBYTES: usize = CRYPTO_BOX_CURVE25519XSALSA20POLY1305_MACBYTES; 30 | pub const CRYPTO_BOX_NONCEBYTES: usize = CRYPTO_BOX_CURVE25519XSALSA20POLY1305_NONCEBYTES; 31 | pub const CRYPTO_BOX_SEEDBYTES: usize = CRYPTO_BOX_CURVE25519XSALSA20POLY1305_SEEDBYTES; 32 | pub const CRYPTO_BOX_BEFORENMBYTES: usize = CRYPTO_BOX_CURVE25519XSALSA20POLY1305_BEFORENMBYTES; 33 | pub const CRYPTO_BOX_SEALBYTES: usize = CRYPTO_BOX_PUBLICKEYBYTES + CRYPTO_BOX_MACBYTES; 34 | pub const CRYPTO_BOX_MESSAGEBYTES_MAX: usize = 35 | CRYPTO_STREAM_XSALSA20_MESSAGEBYTES_MAX - CRYPTO_BOX_CURVE25519XSALSA20POLY1305_MACBYTES; 36 | 37 | pub const CRYPTO_SECRETBOX_XSALSA20POLY1305_KEYBYTES: usize = 32; 38 | pub const CRYPTO_SECRETBOX_XSALSA20POLY1305_NONCEBYTES: usize = 24; 39 | pub const CRYPTO_SECRETBOX_XSALSA20POLY1305_MACBYTES: usize = 16; 40 | pub const CRYPTO_SECRETBOX_XSALSA20POLY1305_MESSAGEBYTES_MAX: usize = SODIUM_SIZE_MAX; 41 | 42 | pub const CRYPTO_SECRETBOX_KEYBYTES: usize = CRYPTO_SECRETBOX_XSALSA20POLY1305_KEYBYTES; 43 | pub const CRYPTO_SECRETBOX_NONCEBYTES: usize = CRYPTO_SECRETBOX_XSALSA20POLY1305_NONCEBYTES; 44 | pub const CRYPTO_SECRETBOX_MACBYTES: usize = CRYPTO_SECRETBOX_XSALSA20POLY1305_MACBYTES; 45 | pub const CRYPTO_SECRETBOX_PRIMITIVE: &str = "xsalsa20poly1305"; 46 | pub const CRYPTO_SECRETBOX_MESSAGEBYTES_MAX: usize = 47 | CRYPTO_SECRETBOX_XSALSA20POLY1305_MESSAGEBYTES_MAX; 48 | 49 | pub const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES: usize = 32; 50 | pub const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES: usize = 24; 51 | pub const CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES: usize = 16; 52 | pub const CRYPTO_AEAD_CHACHA20POLY1305_IETF_MESSAGEBYTES_MAX: usize = 53 | (64u64 * ((1u64 << 32) - 1u64)) as usize; 54 | 55 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_KEYBYTES: usize = 56 | CRYPTO_AEAD_XCHACHA20POLY1305_IETF_KEYBYTES; 57 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_HEADERBYTES: usize = 58 | CRYPTO_AEAD_XCHACHA20POLY1305_IETF_NPUBBYTES; 59 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_INONCEBYTES: usize = 8; 60 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_COUNTERBYTES: usize = 4; 61 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES: usize = 62 | 1 + CRYPTO_AEAD_XCHACHA20POLY1305_IETF_ABYTES; 63 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_MESSAGEBYTES_MAX: usize = min( 64 | SODIUM_SIZE_MAX - CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_ABYTES, 65 | (64u64 * ((1u64 << 32) - 2u64)) as usize, 66 | ); 67 | 68 | pub const CRYPTO_STREAM_CHACHA20_IETF_KEYBYTES: usize = 32; 69 | pub const CRYPTO_STREAM_CHACHA20_IETF_NONCEBYTES: usize = 12; 70 | 71 | pub const CRYPTO_CORE_HCHACHA20_INPUTBYTES: usize = 16; 72 | pub const CRYPTO_CORE_HCHACHA20_OUTPUTBYTES: usize = 32; 73 | pub const CRYPTO_CORE_HCHACHA20_KEYBYTES: usize = 32; 74 | 75 | pub const CRYPTO_CORE_HSALSA20_OUTPUTBYTES: usize = 32; 76 | pub const CRYPTO_CORE_HSALSA20_INPUTBYTES: usize = 16; 77 | pub const CRYPTO_CORE_HSALSA20_KEYBYTES: usize = 32; 78 | pub const CRYPTO_CORE_HSALSA20_CONSTBYTES: usize = 16; 79 | 80 | pub const CRYPTO_SECRETSTREAM_PADBYTES: usize = 8; 81 | 82 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE: u8 = 0x00; 83 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH: u8 = 0x01; 84 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY: u8 = 0x02; 85 | pub const CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL: u8 = 86 | CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH 87 | | CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY; 88 | 89 | pub const CRYPTO_GENERICHASH_BLAKE2B_BYTES_MIN: usize = 16; 90 | pub const CRYPTO_GENERICHASH_BLAKE2B_BYTES_MAX: usize = 64; 91 | pub const CRYPTO_GENERICHASH_BLAKE2B_BYTES: usize = 32; 92 | pub const CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MIN: usize = 16; 93 | pub const CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MAX: usize = 64; 94 | pub const CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES: usize = 32; 95 | pub const CRYPTO_GENERICHASH_BLAKE2B_SALTBYTES: usize = 16; 96 | pub const CRYPTO_GENERICHASH_BLAKE2B_PERSONALBYTES: usize = 16; 97 | 98 | pub const CRYPTO_GENERICHASH_BYTES: usize = CRYPTO_GENERICHASH_BLAKE2B_BYTES; 99 | pub const CRYPTO_GENERICHASH_KEYBYTES: usize = CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES; 100 | pub const CRYPTO_GENERICHASH_BYTES_MIN: usize = CRYPTO_GENERICHASH_BLAKE2B_BYTES_MIN; 101 | pub const CRYPTO_GENERICHASH_BYTES_MAX: usize = CRYPTO_GENERICHASH_BLAKE2B_BYTES_MAX; 102 | pub const CRYPTO_GENERICHASH_KEYBYTES_MIN: usize = CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MIN; 103 | pub const CRYPTO_GENERICHASH_KEYBYTES_MAX: usize = CRYPTO_GENERICHASH_BLAKE2B_KEYBYTES_MAX; 104 | 105 | pub const CRYPTO_ONETIMEAUTH_POLY1305_BYTES: usize = 16; 106 | pub const CRYPTO_ONETIMEAUTH_POLY1305_KEYBYTES: usize = 32; 107 | 108 | pub const CRYPTO_ONETIMEAUTH_BYTES: usize = CRYPTO_ONETIMEAUTH_POLY1305_BYTES; 109 | pub const CRYPTO_ONETIMEAUTH_KEYBYTES: usize = CRYPTO_ONETIMEAUTH_POLY1305_KEYBYTES; 110 | 111 | pub const CRYPTO_AUTH_HMACSHA512256_BYTES: usize = 32; 112 | pub const CRYPTO_AUTH_HMACSHA512256_KEYBYTES: usize = 32; 113 | 114 | pub const CRYPTO_AUTH_BYTES: usize = CRYPTO_AUTH_HMACSHA512256_BYTES; 115 | pub const CRYPTO_AUTH_KEYBYTES: usize = CRYPTO_AUTH_HMACSHA512256_KEYBYTES; 116 | 117 | pub const CRYPTO_HASH_SHA512_BYTES: usize = 64; 118 | 119 | pub const CRYPTO_KDF_BLAKE2B_KEYBYTES: usize = 32; 120 | pub const CRYPTO_KDF_BLAKE2B_CONTEXTBYTES: usize = 8; 121 | pub const CRYPTO_KDF_BLAKE2B_BYTES_MIN: usize = 16; 122 | pub const CRYPTO_KDF_BLAKE2B_BYTES_MAX: usize = 64; 123 | 124 | pub const CRYPTO_KDF_KEYBYTES: usize = CRYPTO_KDF_BLAKE2B_KEYBYTES; 125 | pub const CRYPTO_KDF_CONTEXTBYTES: usize = CRYPTO_KDF_BLAKE2B_CONTEXTBYTES; 126 | 127 | pub const CRYPTO_KX_PUBLICKEYBYTES: usize = 32; 128 | pub const CRYPTO_KX_SECRETKEYBYTES: usize = 32; 129 | pub const CRYPTO_KX_SEEDBYTES: usize = 32; 130 | pub const CRYPTO_KX_SESSIONKEYBYTES: usize = 32; 131 | 132 | pub const CRYPTO_SIGN_ED25519_PUBLICKEYBYTES: usize = 32; 133 | pub const CRYPTO_SIGN_ED25519_SECRETKEYBYTES: usize = 32 + 32; 134 | pub const CRYPTO_SIGN_ED25519_BYTES: usize = 64; 135 | pub const CRYPTO_SIGN_ED25519_SEEDBYTES: usize = 32; 136 | pub const CRYPTO_SIGN_ED25519_MESSAGEBYTES_MAX: usize = SODIUM_SIZE_MAX - CRYPTO_SIGN_ED25519_BYTES; 137 | pub const CRYPTO_CORE_ED25519_BYTES: usize = 32; 138 | 139 | pub const CRYPTO_SIGN_BYTES: usize = CRYPTO_SIGN_ED25519_BYTES; 140 | pub const CRYPTO_SIGN_SEEDBYTES: usize = CRYPTO_SIGN_ED25519_SEEDBYTES; 141 | pub const CRYPTO_SIGN_PUBLICKEYBYTES: usize = CRYPTO_SIGN_ED25519_PUBLICKEYBYTES; 142 | pub const CRYPTO_SIGN_SECRETKEYBYTES: usize = CRYPTO_SIGN_ED25519_SECRETKEYBYTES; 143 | pub const CRYPTO_SIGN_MESSAGEBYTES_MAX: usize = CRYPTO_SIGN_ED25519_MESSAGEBYTES_MAX; 144 | 145 | pub const CRYPTO_SHORTHASH_SIPHASH24_BYTES: usize = 8; 146 | pub const CRYPTO_SHORTHASH_SIPHASH24_KEYBYTES: usize = 16; 147 | 148 | pub const CRYPTO_SHORTHASH_BYTES: usize = CRYPTO_SHORTHASH_SIPHASH24_BYTES; 149 | pub const CRYPTO_SHORTHASH_KEYBYTES: usize = CRYPTO_SHORTHASH_SIPHASH24_KEYBYTES; 150 | 151 | pub const CRYPTO_PWHASH_ARGON2I_ALG_ARGON2I13: usize = 1; 152 | pub const CRYPTO_PWHASH_ARGON2I_BYTES_MAX: usize = min(SODIUM_SIZE_MAX, 4294967295); 153 | pub const CRYPTO_PWHASH_ARGON2I_BYTES_MIN: usize = 16; 154 | pub const CRYPTO_PWHASH_ARGON2I_MEMLIMIT_INTERACTIVE: usize = 33554432; 155 | pub const CRYPTO_PWHASH_ARGON2I_MEMLIMIT_MAX: usize = max( 156 | min(SODIUM_SIZE_MAX, 4398046510080), 157 | max( 158 | min(SODIUM_SIZE_MAX, 2147483648), 159 | min(SODIUM_SIZE_MAX, 32768), 160 | ), 161 | ); 162 | pub const CRYPTO_PWHASH_ARGON2I_MEMLIMIT_MIN: usize = 8192; 163 | pub const CRYPTO_PWHASH_ARGON2I_MEMLIMIT_MODERATE: usize = 134217728; 164 | pub const CRYPTO_PWHASH_ARGON2I_MEMLIMIT_SENSITIVE: usize = 536870912; 165 | pub const CRYPTO_PWHASH_ARGON2I_OPSLIMIT_INTERACTIVE: u64 = 4; 166 | pub const CRYPTO_PWHASH_ARGON2I_OPSLIMIT_MAX: u64 = 4294967295; 167 | pub const CRYPTO_PWHASH_ARGON2I_OPSLIMIT_MIN: u64 = 3; 168 | pub const CRYPTO_PWHASH_ARGON2I_OPSLIMIT_MODERATE: u64 = 6; 169 | pub const CRYPTO_PWHASH_ARGON2I_OPSLIMIT_SENSITIVE: u64 = 8; 170 | pub const CRYPTO_PWHASH_ARGON2I_PASSWD_MAX: usize = 4294967295; 171 | pub const CRYPTO_PWHASH_ARGON2I_PASSWD_MIN: usize = 0; 172 | pub const CRYPTO_PWHASH_ARGON2I_SALTBYTES_MAX: usize = 0xFFFFFFFF; 173 | pub const CRYPTO_PWHASH_ARGON2I_SALTBYTES_MIN: usize = 8; 174 | pub const CRYPTO_PWHASH_ARGON2I_SALTBYTES: usize = 16; 175 | pub const CRYPTO_PWHASH_ARGON2I_STRBYTES: usize = 128; 176 | pub const CRYPTO_PWHASH_ARGON2I_STRPREFIX: &str = "$argon2i$"; 177 | 178 | pub const CRYPTO_PWHASH_ARGON2ID_ALG_ARGON2ID13: usize = 2; 179 | pub const CRYPTO_PWHASH_ARGON2ID_BYTES_MAX: usize = min(SODIUM_SIZE_MAX, 4294967295); 180 | pub const CRYPTO_PWHASH_ARGON2ID_BYTES_MIN: usize = 16; 181 | pub const CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_INTERACTIVE: usize = 67108864; 182 | pub const CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_MIN: usize = 8192; 183 | pub const CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_MAX: usize = max( 184 | min(SODIUM_SIZE_MAX, 4398046510080), 185 | max( 186 | min(SODIUM_SIZE_MAX, 2147483648), 187 | min(SODIUM_SIZE_MAX, 32768), 188 | ), 189 | ); 190 | pub const CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_MODERATE: usize = 268435456; 191 | pub const CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_SENSITIVE: usize = 1073741824; 192 | pub const CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_INTERACTIVE: u64 = 2; 193 | pub const CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_MAX: u64 = 4294967295; 194 | pub const CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_MIN: u64 = 1; 195 | pub const CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_MODERATE: u64 = 3; 196 | pub const CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_SENSITIVE: u64 = 4; 197 | pub const CRYPTO_PWHASH_ARGON2ID_PASSWD_MAX: usize = 4294967295; 198 | pub const CRYPTO_PWHASH_ARGON2ID_PASSWD_MIN: usize = 0; 199 | pub const CRYPTO_PWHASH_ARGON2ID_SALTBYTES_MAX: usize = 0xFFFFFFFF; 200 | pub const CRYPTO_PWHASH_ARGON2ID_SALTBYTES_MIN: usize = 8; 201 | pub const CRYPTO_PWHASH_ARGON2ID_SALTBYTES: usize = 16; 202 | pub const CRYPTO_PWHASH_ARGON2ID_STRBYTES: usize = 128; 203 | pub const CRYPTO_PWHASH_ARGON2ID_STRPREFIX: &str = "$argon2id$"; 204 | 205 | pub const CRYPTO_PWHASH_ALG_ARGON2I13: usize = CRYPTO_PWHASH_ARGON2I_ALG_ARGON2I13; 206 | pub const CRYPTO_PWHASH_ALG_ARGON2ID13: usize = CRYPTO_PWHASH_ARGON2ID_ALG_ARGON2ID13; 207 | pub const CRYPTO_PWHASH_ALG_DEFAULT: usize = CRYPTO_PWHASH_ALG_ARGON2ID13; 208 | pub const CRYPTO_PWHASH_BYTES_MAX: usize = CRYPTO_PWHASH_ARGON2ID_BYTES_MAX; 209 | pub const CRYPTO_PWHASH_BYTES_MIN: usize = CRYPTO_PWHASH_ARGON2ID_BYTES_MIN; 210 | pub const CRYPTO_PWHASH_MEMLIMIT_INTERACTIVE: usize = CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_INTERACTIVE; 211 | pub const CRYPTO_PWHASH_MEMLIMIT_MAX: usize = CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_MAX; 212 | pub const CRYPTO_PWHASH_MEMLIMIT_MIN: usize = CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_MIN; 213 | pub const CRYPTO_PWHASH_MEMLIMIT_MODERATE: usize = CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_MODERATE; 214 | pub const CRYPTO_PWHASH_MEMLIMIT_SENSITIVE: usize = CRYPTO_PWHASH_ARGON2ID_MEMLIMIT_SENSITIVE; 215 | pub const CRYPTO_PWHASH_OPSLIMIT_INTERACTIVE: u64 = CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_INTERACTIVE; 216 | pub const CRYPTO_PWHASH_OPSLIMIT_MAX: u64 = CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_MAX; 217 | pub const CRYPTO_PWHASH_OPSLIMIT_MIN: u64 = CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_MIN; 218 | pub const CRYPTO_PWHASH_OPSLIMIT_MODERATE: u64 = CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_MODERATE; 219 | pub const CRYPTO_PWHASH_OPSLIMIT_SENSITIVE: u64 = CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_SENSITIVE; 220 | pub const CRYPTO_PWHASH_PASSWD_MAX: usize = CRYPTO_PWHASH_ARGON2ID_PASSWD_MAX; 221 | pub const CRYPTO_PWHASH_PASSWD_MIN: usize = CRYPTO_PWHASH_ARGON2ID_PASSWD_MIN; 222 | pub const CRYPTO_PWHASH_SALTBYTES_MAX: usize = CRYPTO_PWHASH_ARGON2ID_SALTBYTES_MAX; 223 | pub const CRYPTO_PWHASH_SALTBYTES_MIN: usize = CRYPTO_PWHASH_ARGON2ID_SALTBYTES_MIN; 224 | pub const CRYPTO_PWHASH_SALTBYTES: usize = CRYPTO_PWHASH_ARGON2ID_SALTBYTES; 225 | pub const CRYPTO_PWHASH_STRBYTES: usize = CRYPTO_PWHASH_ARGON2ID_STRBYTES; 226 | pub const CRYPTO_PWHASH_STRPREFIX: &str = CRYPTO_PWHASH_ARGON2ID_STRPREFIX; 227 | -------------------------------------------------------------------------------- /src/poly1305/poly1305_soft.rs: -------------------------------------------------------------------------------- 1 | use zeroize::Zeroize; 2 | 3 | use crate::types::*; 4 | use crate::utils::load_u64_le; 5 | 6 | const BLOCK_SIZE: usize = 16; 7 | 8 | #[derive(Default, Zeroize)] 9 | pub struct Poly1305 { 10 | r: [u64; 3], 11 | h: [u64; 3], 12 | pad: [u64; 2], 13 | buffer: Vec, 14 | } 15 | 16 | #[inline] 17 | fn mul(x: u64, y: u64) -> u128 { 18 | u128::from(x) * u128::from(y) 19 | } 20 | 21 | #[inline] 22 | fn shr(in_: u128, shift: u64) -> u64 { 23 | (in_ >> shift) as u64 24 | } 25 | 26 | #[inline] 27 | fn lo(in_: u128) -> u64 { 28 | in_ as u64 29 | } 30 | 31 | pub type Key = StackByteArray<32>; 32 | 33 | impl Poly1305 { 34 | pub fn new(key: &K) -> Self 35 | where 36 | K: ByteArray<32>, 37 | { 38 | let mut state = Poly1305::default(); 39 | 40 | let (t0, t1) = ( 41 | load_u64_le(&key.as_array()[0..8]), 42 | load_u64_le(&key.as_array()[8..16]), 43 | ); 44 | 45 | // wiped after finalization 46 | state.r[0] = t0 & 0xffc0fffffff; 47 | state.r[1] = ((t0 >> 44) | (t1 << 20)) & 0xfffffc0ffff; 48 | state.r[2] = (t1 >> 24) & 0x00ffffffc0f; 49 | 50 | // h = 0 51 | state.h.fill(0); 52 | 53 | // save pad for later 54 | state.pad[0] = load_u64_le(&key.as_array()[16..24]); 55 | state.pad[1] = load_u64_le(&key.as_array()[24..32]); 56 | 57 | state 58 | } 59 | 60 | pub fn update(&mut self, input: &[u8]) { 61 | let mut m = input; 62 | if !self.buffer.is_empty() { 63 | let input_block_end = std::cmp::min(BLOCK_SIZE - self.buffer.len(), input.len()); 64 | // copy start of incoming block into previous block 65 | self.buffer.extend_from_slice(&m[..input_block_end]); 66 | 67 | if self.buffer.len() < BLOCK_SIZE { 68 | // don't have enough data yet, do nothing 69 | return; 70 | } 71 | 72 | // process block 73 | let b = self.buffer.clone(); 74 | self.blocks(&b, false); 75 | self.buffer.clear(); 76 | 77 | m = &m[input_block_end..] 78 | } 79 | 80 | // process all full blocks 81 | let full_blocks_end = m.len() - (m.len() % BLOCK_SIZE); 82 | self.blocks(&m[..full_blocks_end], false); 83 | 84 | if full_blocks_end < m.len() { 85 | // copy leftover into buffer 86 | self.buffer.extend_from_slice(&m[full_blocks_end..]); 87 | } 88 | } 89 | 90 | fn blocks(&mut self, input: &[u8], partial: bool) { 91 | let hibit = if partial { 92 | 0u64 93 | } else { 94 | // 1 << 128 95 | 1u64 << 40 96 | }; 97 | 98 | let r0 = self.r[0]; 99 | let r1 = self.r[1]; 100 | let r2 = self.r[2]; 101 | 102 | let mut h0 = self.h[0]; 103 | let mut h1 = self.h[1]; 104 | let mut h2 = self.h[2]; 105 | 106 | let s1 = r1 * (5 << 2); 107 | let s2 = r2 * (5 << 2); 108 | 109 | for m in input.chunks(BLOCK_SIZE) { 110 | // h += m[i] 111 | let t0 = load_u64_le(&m[0..8]); 112 | let t1 = load_u64_le(&m[8..]); 113 | 114 | h0 = h0.wrapping_add(t0 & 0xfffffffffff); 115 | h1 = h1.wrapping_add(((t0 >> 44) | (t1 << 20)) & 0xfffffffffff); 116 | h2 = h2.wrapping_add(((t1 >> 24) & 0x3ffffffffff) | hibit); 117 | 118 | self.h[0] = h0; 119 | self.h[1] = h1; 120 | self.h[2] = h2; 121 | 122 | // h *= r 123 | let d0 = mul(h0, r0) + mul(h1, s2) + mul(h2, s1); 124 | let mut d1 = mul(h0, r1) + mul(h1, r0) + mul(h2, s2); 125 | let mut d2 = mul(h0, r2) + mul(h1, r1) + mul(h2, r0); 126 | 127 | self.h[0] = h0; 128 | self.h[1] = h1; 129 | self.h[2] = h2; 130 | 131 | // (partial) h %= p 132 | let mut c = shr(d0, 44); 133 | h0 = lo(d0) & 0xfffffffffff; 134 | d1 += c as u128; 135 | c = shr(d1, 44); 136 | h1 = lo(d1) & 0xfffffffffff; 137 | d2 += c as u128; 138 | c = shr(d2, 42); 139 | h2 = lo(d2) & 0x3ffffffffff; 140 | h0 += c * 5; 141 | c = h0 >> 44; 142 | h0 &= 0xfffffffffff; 143 | h1 += c; 144 | 145 | self.h[0] = h0; 146 | self.h[1] = h1; 147 | self.h[2] = h2; 148 | } 149 | 150 | self.h[0] = h0; 151 | self.h[1] = h1; 152 | self.h[2] = h2; 153 | } 154 | 155 | pub fn finalize_to_array(&mut self) -> [u8; BLOCK_SIZE] { 156 | let mut mac = [0u8; 16]; 157 | 158 | self.finalize(&mut mac); 159 | 160 | mac 161 | } 162 | 163 | pub fn finalize(&mut self, output: &mut [u8]) { 164 | // process any remaining block 165 | if !self.buffer.is_empty() { 166 | self.buffer.push(1); 167 | if self.buffer.len() % BLOCK_SIZE != 0 { 168 | self.buffer.resize( 169 | self.buffer.len() + (BLOCK_SIZE - self.buffer.len() % BLOCK_SIZE), 170 | 0, 171 | ); 172 | } 173 | 174 | self.blocks(&self.buffer.clone(), true); 175 | } 176 | 177 | // fully carry h 178 | let mut h0 = self.h[0]; 179 | let mut h1 = self.h[1]; 180 | let mut h2 = self.h[2]; 181 | 182 | let mut c = h1 >> 44; 183 | h1 &= 0xfffffffffff; 184 | h2 += c; 185 | c = h2 >> 42; 186 | h2 &= 0x3ffffffffff; 187 | h0 += c * 5; 188 | c = h0 >> 44; 189 | h0 &= 0xfffffffffff; 190 | h1 += c; 191 | c = h1 >> 44; 192 | h1 &= 0xfffffffffff; 193 | h2 += c; 194 | c = h2 >> 42; 195 | h2 &= 0x3ffffffffff; 196 | h0 += c * 5; 197 | c = h0 >> 44; 198 | h0 &= 0xfffffffffff; 199 | h1 += c; 200 | 201 | // compute h + -p 202 | let mut g0 = h0.wrapping_add(5); 203 | c = g0 >> 44; 204 | g0 &= 0xfffffffffff; 205 | let mut g1 = h1.wrapping_add(c); 206 | c = g1 >> 44; 207 | g1 &= 0xfffffffffff; 208 | let mut g2 = (h2.wrapping_add(c)).wrapping_sub(1u64 << 42); 209 | 210 | // select h if h < p, or h + -p if h >= p 211 | let mut mask = (g2 >> ((8 * 8) - 1)).wrapping_sub(1); 212 | g0 &= mask; 213 | g1 &= mask; 214 | g2 &= mask; 215 | mask = !mask; 216 | h0 = (h0 & mask) | g0; 217 | h1 = (h1 & mask) | g1; 218 | h2 = (h2 & mask) | g2; 219 | 220 | // h = (h + pad) 221 | let t0 = self.pad[0]; 222 | let t1 = self.pad[1]; 223 | 224 | h0 = h0.wrapping_add(t0 & 0xfffffffffff); 225 | c = h0 >> 44; 226 | h0 &= 0xfffffffffff; 227 | h1 = h1.wrapping_add((((t0 >> 44) | (t1 << 20)) & 0xfffffffffff).wrapping_add(c)); 228 | c = h1 >> 44; 229 | h1 &= 0xfffffffffff; 230 | h2 = h2.wrapping_add(((t1 >> 24) & 0x3ffffffffff).wrapping_add(c)); 231 | h2 &= 0x3ffffffffff; 232 | 233 | // mac = h % (2^128) 234 | h0 |= h1 << 44; 235 | h1 = (h1 >> 20) | (h2 << 24); 236 | 237 | output[0..8].copy_from_slice(&h0.to_le_bytes()); 238 | output[8..16].copy_from_slice(&h1.to_le_bytes()); 239 | 240 | // zero out the state 241 | self.zeroize(); 242 | } 243 | } 244 | 245 | #[cfg(test)] 246 | mod tests { 247 | use rand::TryRngCore; 248 | 249 | use super::*; 250 | 251 | #[test] 252 | fn test_example_vector() { 253 | // from https://tools.ietf.org/html/rfc7539#section-2.5.2 254 | let key = Key::from(&[ 255 | 0x85, 0xd6, 0xbe, 0x78, 0x57, 0x55, 0x6d, 0x33, 0x7f, 0x44, 0x52, 0xfe, 0x42, 0xd5, 256 | 0x06, 0xa8, 0x01, 0x03, 0x80, 0x8a, 0xfb, 0x0d, 0xb2, 0xfd, 0x4a, 0xbf, 0xf6, 0xaf, 257 | 0x41, 0x49, 0xf5, 0x1b, 258 | ]); 259 | let text = b"Cryptographic Forum Research Group"; 260 | 261 | let mut mac = Poly1305::new(&key); 262 | mac.update(text); 263 | let mac = mac.finalize_to_array(); 264 | 265 | use sodiumoxide::crypto::onetimeauth::poly1305::{Key as SOKey, authenticate}; 266 | let so_key = SOKey::from_slice(&key).expect("key"); 267 | let so_mac = authenticate(text, &so_key); 268 | assert_eq!(mac, so_mac.as_ref()); 269 | assert_eq!( 270 | mac, 271 | [ 272 | 0xa8, 0x06, 0x1d, 0xc1, 0x30, 0x51, 0x36, 0xc6, 0xc2, 0x2b, 0x8b, 0xaf, 0x0c, 0x01, 273 | 0x27, 0xa9, 274 | ] 275 | ); 276 | } 277 | 278 | #[test] 279 | fn test_vector_1() { 280 | // from https://tools.ietf.org/html/rfc7539#appendix-A.3 281 | let key = Key::new(); 282 | let text = [0u8; 64]; 283 | 284 | let mut mac = Poly1305::new(&key); 285 | mac.update(&text); 286 | let mac = mac.finalize_to_array(); 287 | 288 | assert_eq!(mac, [0u8; 16]); 289 | } 290 | 291 | #[test] 292 | fn test_vector_2() { 293 | // from https://tools.ietf.org/html/rfc7539#appendix-A.3 294 | let key = Key::from(&[ 295 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 296 | 0x00, 0x00, 0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70, 0xf0, 0xef, 0xca, 0x96, 297 | 0x22, 0x7a, 0x86, 0x3e, 298 | ]); 299 | let text = b"Any submission to the IETF intended by the Contributor for publication as all or part of an IETF Internet-Draft or RFC and any statement made within the context of an IETF activity is considered an \"IETF Contribution\". Such statements include oral statements in IETF sessions, as well as written and electronic communications made at any time or place, which are addressed to"; 300 | 301 | let mut mac = Poly1305::new(&key); 302 | mac.update(text); 303 | let mac = mac.finalize_to_array(); 304 | 305 | assert_eq!( 306 | mac, 307 | [ 308 | 0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70, 0xf0, 0xef, 0xca, 0x96, 0x22, 0x7a, 309 | 0x86, 0x3e, 310 | ] 311 | ); 312 | } 313 | 314 | #[test] 315 | fn test_vector_3() { 316 | // from https://tools.ietf.org/html/rfc7539#appendix-A.3 317 | let key = Key::from(&[ 318 | 0x36, 0xe5, 0xf6, 0xb5, 0xc5, 0xe0, 0x60, 0x70, 0xf0, 0xef, 0xca, 0x96, 0x22, 0x7a, 319 | 0x86, 0x3e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 320 | 0x00, 0x00, 0x00, 0x00, 321 | ]); 322 | let text = b"Any submission to the IETF intended by the Contributor for publication as all or part of an IETF Internet-Draft or RFC and any statement made within the context of an IETF activity is considered an \"IETF Contribution\". Such statements include oral statements in IETF sessions, as well as written and electronic communications made at any time or place, which are addressed to"; 323 | 324 | let mut mac = Poly1305::new(&key); 325 | mac.update(text); 326 | let mac = mac.finalize_to_array(); 327 | 328 | assert_eq!( 329 | mac, 330 | [ 331 | 0xf3, 0x47, 0x7e, 0x7c, 0xd9, 0x54, 0x17, 0xaf, 0x89, 0xa6, 0xb8, 0x79, 0x4c, 0x31, 332 | 0x0c, 0xf0, 333 | ] 334 | ); 335 | } 336 | 337 | #[test] 338 | fn test_vector_4() { 339 | // from https://tools.ietf.org/html/rfc7539#appendix-A.3 340 | let key = Key::from(&[ 341 | 0x1c, 0x92, 0x40, 0xa5, 0xeb, 0x55, 0xd3, 0x8a, 0xf3, 0x33, 0x88, 0x86, 0x04, 0xf6, 342 | 0xb5, 0xf0, 0x47, 0x39, 0x17, 0xc1, 0x40, 0x2b, 0x80, 0x09, 0x9d, 0xca, 0x5c, 0xbc, 343 | 0x20, 0x70, 0x75, 0xc0, 344 | ]); 345 | let text = [ 346 | 0x27u8, 0x54u8, 0x77u8, 0x61u8, 0x73u8, 0x20u8, 0x62u8, 0x72u8, 0x69u8, 0x6cu8, 0x6cu8, 347 | 0x69u8, 0x67u8, 0x2cu8, 0x20u8, 0x61u8, 0x6eu8, 0x64u8, 0x20u8, 0x74u8, 0x68u8, 0x65u8, 348 | 0x20u8, 0x73u8, 0x6cu8, 0x69u8, 0x74u8, 0x68u8, 0x79u8, 0x20u8, 0x74u8, 0x6fu8, 0x76u8, 349 | 0x65u8, 0x73u8, 0x0au8, 0x44u8, 0x69u8, 0x64u8, 0x20u8, 0x67u8, 0x79u8, 0x72u8, 0x65u8, 350 | 0x20u8, 0x61u8, 0x6eu8, 0x64u8, 0x20u8, 0x67u8, 0x69u8, 0x6du8, 0x62u8, 0x6cu8, 0x65u8, 351 | 0x20u8, 0x69u8, 0x6eu8, 0x20u8, 0x74u8, 0x68u8, 0x65u8, 0x20u8, 0x77u8, 0x61u8, 0x62u8, 352 | 0x65u8, 0x3au8, 0x0au8, 0x41u8, 0x6cu8, 0x6cu8, 0x20u8, 0x6du8, 0x69u8, 0x6du8, 0x73u8, 353 | 0x79u8, 0x20u8, 0x77u8, 0x65u8, 0x72u8, 0x65u8, 0x20u8, 0x74u8, 0x68u8, 0x65u8, 0x20u8, 354 | 0x62u8, 0x6fu8, 0x72u8, 0x6fu8, 0x67u8, 0x6fu8, 0x76u8, 0x65u8, 0x73u8, 0x2cu8, 0x0au8, 355 | 0x41u8, 0x6eu8, 0x64u8, 0x20u8, 0x74u8, 0x68u8, 0x65u8, 0x20u8, 0x6du8, 0x6fu8, 0x6du8, 356 | 0x65u8, 0x20u8, 0x72u8, 0x61u8, 0x74u8, 0x68u8, 0x73u8, 0x20u8, 0x6fu8, 0x75u8, 0x74u8, 357 | 0x67u8, 0x72u8, 0x61u8, 0x62u8, 0x65u8, 0x2eu8, 358 | ]; 359 | 360 | let mut mac = Poly1305::new(&key); 361 | mac.update(&text); 362 | let mac = mac.finalize_to_array(); 363 | 364 | assert_eq!( 365 | mac, 366 | [ 367 | 0x45, 0x41, 0x66, 0x9a, 0x7e, 0xaa, 0xee, 0x61, 0xe7, 0x08, 0xdc, 0x7c, 0xbc, 0xc5, 368 | 0xeb, 0x62, 369 | ] 370 | ); 371 | } 372 | 373 | #[test] 374 | fn test_libsodium() { 375 | use rand_core::OsRng; 376 | use sodiumoxide::crypto::onetimeauth::poly1305::{Key as SOKey, authenticate}; 377 | 378 | use crate::rng::copy_randombytes; 379 | 380 | let key = Key::gen(); 381 | 382 | let so_key = SOKey::from_slice(&key).unwrap(); 383 | 384 | for _ in 0..20 { 385 | let rand_usize = (OsRng.try_next_u32().unwrap() % 1000) as usize; 386 | let mut data = vec![0u8; rand_usize]; 387 | copy_randombytes(&mut data); 388 | 389 | let mut mac = Poly1305::new(&key); 390 | mac.update(&data); 391 | let mac = mac.finalize_to_array(); 392 | 393 | let so_mac = authenticate(&data, &so_key); 394 | 395 | assert_eq!(mac, so_mac.as_ref()); 396 | } 397 | } 398 | } 399 | -------------------------------------------------------------------------------- /src/generichash.rs: -------------------------------------------------------------------------------- 1 | //! # Generic hashing 2 | //! 3 | //! [`GenericHash`] implements libsodium's generic hashing, based on the Blake2b 4 | //! algorithm. Can also be used as an HMAC function, if a key is provided. 5 | //! 6 | //! # Rustaceous API example, one-time interface 7 | //! 8 | //! ``` 9 | //! use base64::Engine as _; 10 | //! use base64::engine::general_purpose; 11 | //! use dryoc::generichash::{GenericHash, Key}; 12 | //! 13 | //! // NOTE: The type for `key` param must be specified, the compiler cannot infer it when 14 | //! // we pass `None`. 15 | //! let hash = 16 | //! GenericHash::hash_with_defaults_to_vec::<_, Key>(b"hello", None).expect("hash failed"); 17 | //! 18 | //! assert_eq!( 19 | //! general_purpose::STANDARD.encode(&hash), 20 | //! "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8=" 21 | //! ); 22 | //! ``` 23 | //! 24 | //! # Rustaceous API example, incremental interface 25 | //! 26 | //! ``` 27 | //! use base64::Engine as _; 28 | //! use base64::engine::general_purpose; 29 | //! use dryoc::generichash::{GenericHash, Key}; 30 | //! 31 | //! // The compiler cannot infer the `Key` type, so we pass it below. 32 | //! let mut hasher = GenericHash::new_with_defaults::(None).expect("new failed"); 33 | //! hasher.update(b"hello"); 34 | //! let hash = hasher.finalize_to_vec().expect("finalize failed"); 35 | //! 36 | //! assert_eq!( 37 | //! general_purpose::STANDARD.encode(&hash), 38 | //! "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8=" 39 | //! ); 40 | //! ``` 41 | 42 | use crate::classic::crypto_generichash::{ 43 | GenericHashState, crypto_generichash, crypto_generichash_final, crypto_generichash_init, 44 | crypto_generichash_update, 45 | }; 46 | use crate::constants::{CRYPTO_GENERICHASH_BYTES, CRYPTO_GENERICHASH_KEYBYTES}; 47 | use crate::error::Error; 48 | pub use crate::types::*; 49 | 50 | /// Stack-allocated hash output of the recommended output length. 51 | pub type Hash = StackByteArray; 52 | /// Stack-allocated secret key for use with the generic hash algorithm. 53 | pub type Key = StackByteArray; 54 | 55 | #[cfg(any(feature = "nightly", all(doc, not(doctest))))] 56 | #[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))] 57 | pub mod protected { 58 | //! # Protected memory type aliases for [`GenericHash`] 59 | //! 60 | //! This mod provides re-exports of type aliases for protected memory usage 61 | //! with [`GenericHash`]. These type aliases are provided for 62 | //! convenience. 63 | //! 64 | //! ## Example 65 | //! 66 | //! ``` 67 | //! use dryoc::generichash::GenericHash; 68 | //! use dryoc::generichash::protected::*; 69 | //! 70 | //! // Create a randomly generated key, lock it, protect it as read-only 71 | //! let key = Key::gen_readonly_locked().expect("gen failed"); 72 | //! let input = 73 | //! HeapBytes::from_slice_into_readonly_locked(b"super secret input").expect("input failed"); 74 | //! let hash: Locked = GenericHash::hash(&input, Some(&key)).expect("hash failed"); 75 | //! ``` 76 | use super::*; 77 | pub use crate::protected::*; 78 | 79 | /// Heap-allocated, page-aligned secret key for the generic hash algorithm, 80 | /// for use with protected memory. 81 | pub type Key = HeapByteArray; 82 | /// Heap-allocated, page-aligned hash output for the generic hash algorithm, 83 | /// for use with protected memory. 84 | pub type Hash = HeapByteArray; 85 | } 86 | 87 | /// Provides a generic hash function implementation based on Blake2b. Compatible 88 | /// with libsodium's generic hash. 89 | pub struct GenericHash { 90 | state: GenericHashState, 91 | } 92 | 93 | impl GenericHash { 94 | /// Returns a new hasher instance, with `key`. 95 | pub fn new>(key: Option<&Key>) -> Result { 96 | Ok(Self { 97 | state: crypto_generichash_init(key.map(|k| k.as_slice()), OUTPUT_LENGTH)?, 98 | }) 99 | } 100 | 101 | /// Updates the hasher state from `input`. 102 | pub fn update(&mut self, input: &Input) { 103 | crypto_generichash_update(&mut self.state, input.as_slice()) 104 | } 105 | 106 | /// Computes and returns the final hash value. 107 | pub fn finalize>(self) -> Result { 108 | let mut output = Output::new_byte_array(); 109 | 110 | crypto_generichash_final(self.state, output.as_mut_slice())?; 111 | 112 | Ok(output) 113 | } 114 | 115 | /// Computes and returns the final hash value as a [`Vec`]. Provided for 116 | /// convenience. 117 | pub fn finalize_to_vec(self) -> Result, Error> { 118 | self.finalize() 119 | } 120 | 121 | /// Onet-time interface for the generic hash function. Computes the hash for 122 | /// `input` with optional `key`. The output length is determined by the type 123 | /// signature of `Output`. 124 | /// 125 | /// # Example 126 | /// 127 | /// ``` 128 | /// use base64::Engine as _; 129 | /// use base64::engine::general_purpose; 130 | /// use dryoc::generichash::{GenericHash, Hash}; 131 | /// 132 | /// let output: Hash = 133 | /// GenericHash::hash(b"hello", Some(b"a very secret key")).expect("hash failed"); 134 | /// 135 | /// assert_eq!( 136 | /// general_purpose::STANDARD.encode(&output), 137 | /// "AECDe+XJsB6nOkbCsbS/OPXdzpcRm3AolW/Bg1LFY9A=" 138 | /// ); 139 | /// ``` 140 | pub fn hash< 141 | Input: Bytes + ?Sized, 142 | Key: ByteArray, 143 | Output: NewByteArray, 144 | >( 145 | input: &Input, 146 | key: Option<&Key>, 147 | ) -> Result { 148 | let mut output = Output::new_byte_array(); 149 | crypto_generichash( 150 | output.as_mut_slice(), 151 | input.as_slice(), 152 | key.map(|k| k.as_slice()), 153 | )?; 154 | Ok(output) 155 | } 156 | 157 | /// Convenience wrapper for [`GenericHash::hash`]. 158 | pub fn hash_to_vec>( 159 | input: &Input, 160 | key: Option<&Key>, 161 | ) -> Result, Error> { 162 | Self::hash(input, key) 163 | } 164 | } 165 | 166 | impl GenericHash { 167 | /// Returns an instance of [`GenericHash`] with the default output and key 168 | /// length parameters. 169 | pub fn new_with_defaults>( 170 | key: Option<&Key>, 171 | ) -> Result { 172 | Ok(Self { 173 | state: crypto_generichash_init(key.map(|k| k.as_slice()), CRYPTO_GENERICHASH_BYTES)?, 174 | }) 175 | } 176 | 177 | /// Hashes `input` using `key`, with the default length parameters. Provided 178 | /// for convenience. 179 | pub fn hash_with_defaults< 180 | Input: Bytes + ?Sized, 181 | Key: ByteArray, 182 | Output: NewByteArray, 183 | >( 184 | input: &Input, 185 | key: Option<&Key>, 186 | ) -> Result { 187 | Self::hash(input, key) 188 | } 189 | 190 | /// Hashes `input` using `key`, with the default length parameters, 191 | /// returning a [`Vec`]. Provided for convenience. 192 | pub fn hash_with_defaults_to_vec< 193 | Input: Bytes + ?Sized, 194 | Key: ByteArray, 195 | >( 196 | input: &Input, 197 | key: Option<&Key>, 198 | ) -> Result, Error> { 199 | Self::hash(input, key) 200 | } 201 | } 202 | 203 | #[cfg(test)] 204 | mod tests { 205 | use super::*; 206 | 207 | #[test] 208 | fn test_generichash() { 209 | use base64::Engine as _; 210 | use base64::engine::general_purpose; 211 | 212 | let mut hasher = GenericHash::new_with_defaults::(None).expect("new hash failed"); 213 | hasher.update(b"hello"); 214 | 215 | let output: Vec = hasher.finalize().expect("finalize failed"); 216 | 217 | assert_eq!( 218 | general_purpose::STANDARD.encode(output), 219 | "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8=" 220 | ); 221 | 222 | let mut hasher = GenericHash::new_with_defaults::(None).expect("new hash failed"); 223 | hasher.update(b"hello"); 224 | 225 | let output = hasher.finalize_to_vec().expect("finalize failed"); 226 | 227 | assert_eq!( 228 | general_purpose::STANDARD.encode(output), 229 | "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8=" 230 | ); 231 | } 232 | 233 | #[test] 234 | fn test_generichash_onetime() { 235 | use base64::Engine as _; 236 | use base64::engine::general_purpose; 237 | 238 | let output: Hash = 239 | GenericHash::hash(b"hello", Some(b"a very secret key")).expect("hash failed"); 240 | 241 | assert_eq!( 242 | general_purpose::STANDARD.encode(&output), 243 | "AECDe+XJsB6nOkbCsbS/OPXdzpcRm3AolW/Bg1LFY9A=" 244 | ); 245 | 246 | let output: Vec = 247 | GenericHash::hash_with_defaults::<_, Key, _>(b"hello", None).expect("hash failed"); 248 | 249 | assert_eq!( 250 | general_purpose::STANDARD.encode(output), 251 | "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8=" 252 | ); 253 | 254 | let output = 255 | GenericHash::hash_with_defaults_to_vec::<_, Key>(b"hello", None).expect("hash failed"); 256 | 257 | assert_eq!( 258 | general_purpose::STANDARD.encode(output), 259 | "Mk3PAn3UowqTLEQfNlol6GsXPe+kuOWJSCU0cbgbcs8=" 260 | ); 261 | } 262 | #[test] 263 | fn test_generichash_onetime_empty() { 264 | use base64::Engine as _; 265 | use base64::engine::general_purpose; 266 | 267 | let output = 268 | GenericHash::hash_with_defaults_to_vec::<_, Key>(&[], None).expect("hash failed"); 269 | 270 | assert_eq!( 271 | general_purpose::STANDARD.encode(output), 272 | "DldRwCblQ7Loqy6wYJnaodHl30d3j3eH+qtFzfEv46g=" 273 | ); 274 | } 275 | 276 | #[test] 277 | fn test_vectors() { 278 | let test_vec = |input, key, hash| { 279 | let input = hex::decode(input).expect("decode input"); 280 | let key = hex::decode(key).expect("decode key"); 281 | let expected_hash = hex::decode(hash).expect("decode hash"); 282 | 283 | let hash: Vec = 284 | GenericHash::<64, 64>::hash(&input, Some(&key)).expect("hash failed"); 285 | 286 | assert_eq!(expected_hash, hash); 287 | }; 288 | 289 | test_vec("", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "10ebb67700b1868efb4417987acf4690ae9d972fb7a590c2f02871799aaa4786b5e996e8f0f4eb981fc214b005f42d2ff4233499391653df7aefcbc13fc51568"); 290 | test_vec("00", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "961f6dd1e4dd30f63901690c512e78e4b45e4742ed197c3c5e45c549fd25f2e4187b0bc9fe30492b16b0d0bc4ef9b0f34c7003fac09a5ef1532e69430234cebd"); 291 | test_vec("0001", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "da2cfbe2d8409a0f38026113884f84b50156371ae304c4430173d08a99d9fb1b983164a3770706d537f49e0c916d9f32b95cc37a95b99d857436f0232c88a965"); 292 | test_vec("000102", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "33d0825dddf7ada99b0e7e307104ad07ca9cfd9692214f1561356315e784f3e5a17e364ae9dbb14cb2036df932b77f4b292761365fb328de7afdc6d8998f5fc1"); 293 | test_vec("00010203", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "beaa5a3d08f3807143cf621d95cd690514d0b49efff9c91d24b59241ec0eefa5f60196d407048bba8d2146828ebcb0488d8842fd56bb4f6df8e19c4b4daab8ac"); 294 | test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfc", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "a6213743568e3b3158b9184301f3690847554c68457cb40fc9a4b8cfd8d4a118c301a07737aeda0f929c68913c5f51c80394f53bff1c3e83b2e40ca97eba9e15"); 295 | test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfd", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "d444bfa2362a96df213d070e33fa841f51334e4e76866b8139e8af3bb3398be2dfaddcbc56b9146de9f68118dc5829e74b0c28d7711907b121f9161cb92b69a9"); 296 | test_vec("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeafb0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfe", "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f", "142709d62e28fcccd0af97fad0f8465b971e82201dc51070faa0372aa43e92484be1c1e73ba10906d5d1853db6a4106e0a7bf9800d373d6dee2d46d62ef2a461"); 297 | } 298 | } 299 | --------------------------------------------------------------------------------