diff --git a/.github/workflows/block-padding.yml b/.github/workflows/block-padding.yml index 68e43f40..6d5e38a9 100644 --- a/.github/workflows/block-padding.yml +++ b/.github/workflows/block-padding.yml @@ -22,7 +22,7 @@ jobs: strategy: matrix: rust: - - 1.41.0 # MSRV + - 1.56.0 # MSRV - stable target: - thumbv7em-none-eabi @@ -35,13 +35,13 @@ jobs: toolchain: ${{ matrix.rust }} target: ${{ matrix.target }} override: true - - run: cargo build --release --target ${{ matrix.target }} + - run: cargo build --target ${{ matrix.target }} test: runs-on: ubuntu-latest strategy: matrix: rust: - - 1.41.0 # MSRV + - 1.56.0 # MSRV - stable steps: - uses: actions/checkout@v1 @@ -50,4 +50,4 @@ jobs: profile: minimal toolchain: ${{ matrix.rust }} override: true - - run: cargo test --release + - run: cargo test diff --git a/.github/workflows/inout.yml b/.github/workflows/inout.yml new file mode 100644 index 00000000..ccd5cbb5 --- /dev/null +++ b/.github/workflows/inout.yml @@ -0,0 +1,56 @@ +name: inout + +on: + pull_request: + paths: + - "inout/**" + - "Cargo.*" + push: + branches: master + +defaults: + run: + working-directory: inout + +env: + CARGO_INCREMENTAL: 0 + RUSTFLAGS: "-Dwarnings" + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + target: + - thumbv7em-none-eabi + - wasm32-unknown-unknown + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + target: ${{ matrix.target }} + override: true + - run: cargo build --target ${{ matrix.target }} + - run: cargo build --features block-padding --target ${{ matrix.target }} + test: + runs-on: ubuntu-latest + strategy: + matrix: + rust: + - 1.56.0 # MSRV + - stable + steps: + - uses: actions/checkout@v1 + - uses: actions-rs/toolchain@v1 + with: + profile: minimal + toolchain: ${{ matrix.rust }} + override: true + - run: cargo test + - run: cargo test --features block-padding + - run: cargo test --all-features diff --git a/Cargo.lock b/Cargo.lock index fd7387c7..a4b8026f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,13 +16,6 @@ dependencies = [ "generic-array", ] -[[package]] -name = "block-padding" -version = "0.3.0-pre" -dependencies = [ - "generic-array", -] - [[package]] name = "collectable" version = "0.0.2" @@ -43,9 +36,9 @@ dependencies = [ [[package]] name = "generic-array" -version = "0.14.4" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817" +checksum = "fd48d33ec7f05fbfa152300fdad764757cbded343c1aa1cff2fbaf4134851803" dependencies = [ "typenum", "version_check", @@ -69,9 +62,9 @@ checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35" [[package]] name = "libc" -version = "0.2.109" +version = "0.2.117" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98a04dce437184842841303488f70d0188c5f51437d2a834dc097eafa909a01" +checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c" [[package]] name = "opaque-debug" @@ -88,33 +81,33 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.11" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3dff9853339d4a5b75094c8edd65c3a0e81da8281855332d79559ddd8591b236" +checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145" dependencies = [ "proc-macro2", ] [[package]] name = "ryu" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c9613b5a66ab9ba26415184cfc41156594925a9cf3a2057e57f31ff145f6568" +checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f" [[package]] name = "serde" -version = "1.0.130" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12d06de37cf59146fbdecab66aa99f9fe4f78722e3607577a5375d66bd0c913" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.130" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7bc1a1ab1961464eae040d96713baa5a724a8152c1222492465b54322ec508b" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -123,9 +116,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.73" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcbd0344bc6533bc7ec56df11d42fb70f1b912351c0825ccb7211b59d8af7cf5" +checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085" dependencies = [ "itoa", "ryu", @@ -134,9 +127,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.82" +version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8daf5dd0bb60cbd4137b1b587d2fc0ae729bc07cf01cd70b36a1ed5ade3b9d59" +checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b" dependencies = [ "proc-macro2", "quote", @@ -157,9 +150,9 @@ dependencies = [ [[package]] name = "typenum" -version = "1.14.0" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987" [[package]] name = "unicode-xid" @@ -169,9 +162,9 @@ checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" [[package]] name = "version_check" -version = "0.9.3" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" +checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" [[package]] name = "wycheproof2blb" diff --git a/Cargo.toml b/Cargo.toml index 057f7e76..9c2e9066 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "blobby", "block-buffer", - "block-padding", "collectable", "cpufeatures", "dbl", diff --git a/README.md b/README.md index ed775809..f0ce6120 100644 --- a/README.md +++ b/README.md @@ -1,23 +1,26 @@ # RustCrypto: Utilities -[![Project Chat][chat-image]][chat-link] [![dependency status][deps-image]][deps-link] ![Apache2/MIT licensed][license-image] +[![Project Chat][chat-image]][chat-link] +[![dependency status][deps-image]][deps-link] +![Apache2/MIT licensed][license-image] This repository contains various utility crates used in the RustCrypto project. ## Crates -| Name | crates.io | Docs | Description | -|------|:---------:|:----:|--------------| -| [`blobby`] | [![crates.io](https://img.shields.io/crates/v/blobby.svg)](https://crates.io/crates/blobby) | [![Documentation](https://docs.rs/blobby/badge.svg)](https://docs.rs/blobby) | Decoder of the simple de-duplicated binary blob storage format | -| [`block-buffer`] | [![crates.io](https://img.shields.io/crates/v/block-buffer.svg)](https://crates.io/crates/block-buffer) | [![Documentation](https://docs.rs/block-buffer/badge.svg)](https://docs.rs/block-buffer) | Fixed size buffer for block processing of data | -| [`block‑padding`] | [![crates.io](https://img.shields.io/crates/v/block-padding.svg)](https://crates.io/crates/block-padding) | [![Documentation](https://docs.rs/block-padding/badge.svg)](https://docs.rs/block-padding) | Padding and unpadding of messages divided into blocks | -| [`collectable`] | [![crates.io](https://img.shields.io/crates/v/collectable.svg)](https://crates.io/crates/collectable) | [![Documentation](https://docs.rs/collectable/badge.svg)](https://docs.rs/collectable) | Fallible, `no_std`-friendly collection traits | -| [`cpufeatures`] | [![crates.io](https://img.shields.io/crates/v/cpufeatures.svg)](https://crates.io/crates/cpufeatures) | [![Documentation](https://docs.rs/cpufeatures/badge.svg)](https://docs.rs/cpufeatures) | Lightweight and efficient alternative to the `is_x86_feature_detected!` macro | -| [`dbl`] | [![crates.io](https://img.shields.io/crates/v/dbl.svg)](https://crates.io/crates/dbl) | [![Documentation](https://docs.rs/dbl/badge.svg)](https://docs.rs/dbl) | Double operation in Galois Field (GF) | -| [`hex-literal`] | [![crates.io](https://img.shields.io/crates/v/hex-literal.svg)](https://crates.io/crates/hex-literal) | [![Documentation](https://docs.rs/hex-literal/badge.svg)](https://docs.rs/hex-literal) | Procedural macro for converting hexadecimal string to byte array at compile time | -| [`opaque-debug`] | [![crates.io](https://img.shields.io/crates/v/opaque-debug.svg)](https://crates.io/crates/opaque-debug) | [![Documentation](https://docs.rs/opaque-debug/badge.svg)](https://docs.rs/opaque-debug) | Macro for opaque `Debug` trait implementation | -| [`wycheproof2blb`] | | | Utility for converting [Wycheproof] test vectors to the blobby format | -| [`zeroize`] | [![crates.io](https://img.shields.io/crates/v/zeroize.svg)](https://crates.io/crates/zeroize) | [![Documentation](https://docs.rs/zeroize/badge.svg)](https://docs.rs/zeroize) | Securely zero memory while avoiding compiler optimizations | +| Name | crates.io | Docs | MSRV | Description | +|------|:---------:|:----:|:----:|-------------| +| [`blobby`] | [![crates.io](https://img.shields.io/crates/v/blobby.svg)](https://crates.io/crates/blobby) | [![Documentation](https://docs.rs/blobby/badge.svg)](https://docs.rs/blobby) | ![MSRV 1.39][msrv-1.39] | Decoder of the simple de-duplicated binary blob storage format | +| [`block-buffer`] | [![crates.io](https://img.shields.io/crates/v/block-buffer.svg)](https://crates.io/crates/block-buffer) | [![Documentation](https://docs.rs/block-buffer/badge.svg)](https://docs.rs/block-buffer) | ![MSRV 1.41][msrv-1.41] | Fixed size buffer for block processing of data | +| [`block‑padding`] | [![crates.io](https://img.shields.io/crates/v/block-padding.svg)](https://crates.io/crates/block-padding) | [![Documentation](https://docs.rs/block-padding/badge.svg)](https://docs.rs/block-padding) | ![MSRV 1.56][msrv-1.56] | Padding and unpadding of messages divided into blocks | +| [`collectable`] | [![crates.io](https://img.shields.io/crates/v/collectable.svg)](https://crates.io/crates/collectable) | [![Documentation](https://docs.rs/collectable/badge.svg)](https://docs.rs/collectable) | ![MSRV 1.41][msrv-1.41] | Fallible, `no_std`-friendly collection traits | +| [`cpufeatures`] | [![crates.io](https://img.shields.io/crates/v/cpufeatures.svg)](https://crates.io/crates/cpufeatures) | [![Documentation](https://docs.rs/cpufeatures/badge.svg)](https://docs.rs/cpufeatures) | ![MSRV 1.40][msrv-1.40] | Lightweight and efficient alternative to the `is_x86_feature_detected!` macro | +| [`dbl`] | [![crates.io](https://img.shields.io/crates/v/dbl.svg)](https://crates.io/crates/dbl) | [![Documentation](https://docs.rs/dbl/badge.svg)](https://docs.rs/dbl) | ![MSRV 1.41][msrv-1.41] | Double operation in Galois Field (GF) | +| [`hex-literal`] | [![crates.io](https://img.shields.io/crates/v/hex-literal.svg)](https://crates.io/crates/hex-literal) | [![Documentation](https://docs.rs/hex-literal/badge.svg)](https://docs.rs/hex-literal) | ![MSRV 1.45][msrv-1.45] | Procedural macro for converting hexadecimal string to byte array at compile time | +| [`inout`] | [![crates.io](https://img.shields.io/crates/v/inout.svg)](https://crates.io/crates/inout) | [![Documentation](https://docs.rs/inout/badge.svg)](https://docs.rs/inout) | ![MSRV 1.56][msrv-1.56] | Custom reference types for code generic over in-place and buffer-to-buffer modes of operation. | +| [`opaque-debug`] | [![crates.io](https://img.shields.io/crates/v/opaque-debug.svg)](https://crates.io/crates/opaque-debug) | [![Documentation](https://docs.rs/opaque-debug/badge.svg)](https://docs.rs/opaque-debug) | ![MSRV 1.41][msrv-1.41] | Macro for opaque `Debug` trait implementation | +| [`wycheproof2blb`] | | | | Utility for converting [Wycheproof] test vectors to the blobby format | +| [`zeroize`] | [![crates.io](https://img.shields.io/crates/v/zeroize.svg)](https://crates.io/crates/zeroize) | [![Documentation](https://docs.rs/zeroize/badge.svg)](https://docs.rs/zeroize) | ![MSRV 1.51][msrv-1.51] | Securely zero memory while avoiding compiler optimizations | ## License @@ -40,6 +43,13 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [deps-image]: https://deps.rs/repo/github/RustCrypto/utils/status.svg [deps-link]: https://deps.rs/repo/github/RustCrypto/utils +[msrv-1.39]: https://img.shields.io/badge/rustc-1.39.0+-blue.svg +[msrv-1.40]: https://img.shields.io/badge/rustc-1.40.0+-blue.svg +[msrv-1.41]: https://img.shields.io/badge/rustc-1.41.0+-blue.svg +[msrv-1.45]: https://img.shields.io/badge/rustc-1.45.0+-blue.svg +[msrv-1.51]: https://img.shields.io/badge/rustc-1.51.0+-blue.svg +[msrv-1.56]: https://img.shields.io/badge/rustc-1.56.0+-blue.svg + [//]: # (crates) [`blobby`]: ./blobby @@ -49,6 +59,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [`cpufeatures`]: ./cpufeatures [`dbl`]: ./dbl [`hex-literal`]: ./hex-literal +[`inout`]: ./inout [`opaque-debug`]: ./opaque-debug [`wycheproof2blb`]: ./wycheproof2blb [`zeroize`]: ./zeroize diff --git a/block-padding/CHANGELOG.md b/block-padding/CHANGELOG.md index 95275577..604d994d 100644 --- a/block-padding/CHANGELOG.md +++ b/block-padding/CHANGELOG.md @@ -4,15 +4,18 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [UNRELEASED] +## 0.3.0 (2022-02-10) ### Added - `Iso10126` padding algorithm ([#643]) +- `PadType` enum, `Padding::TYPE` associated constant, and `Padding::unpad_blocks` method ([#675]) ### Changed - The `Padding` trait methods now work with blocks instead of byte slices. ([#113]) +- Bump MSRV to 1.56 and edition to 2021 ([#675]) [#113]: https://github.com/RustCrypto/utils/pull/113 [#643]: https://github.com/RustCrypto/utils/pull/643 +[#675]: https://github.com/RustCrypto/utils/pull/675 ## 0.2.1 (2020-08-14) ### Added diff --git a/block-padding/Cargo.toml b/block-padding/Cargo.toml index 4b7051b8..9c7672ad 100644 --- a/block-padding/Cargo.toml +++ b/block-padding/Cargo.toml @@ -1,17 +1,26 @@ [package] name = "block-padding" -version = "0.3.0-pre" # Also update html_root_url in lib.rs when bumping this +version = "0.3.0" # Also update html_root_url in lib.rs when bumping this +description = "Padding and unpadding of messages divided into blocks." authors = ["RustCrypto Developers"] license = "MIT OR Apache-2.0" -description = "Padding and unpadding of messages divided into blocks." +edition = "2021" +rust-version = "1.56" documentation = "https://docs.rs/block-padding" repository = "https://github.com/RustCrypto/utils" keywords = ["padding", "pkcs7", "ansix923", "iso7816"] categories = ["cryptography", "no-std"] -edition = "2018" + +# Hack to allow this crate to coexist with pre-2021 edition crates +[workspace] +members = ["."] [dependencies] generic-array = "0.14" [features] std = [] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/block-padding/src/lib.rs b/block-padding/src/lib.rs index 8be43a3d..aa5468b6 100644 --- a/block-padding/src/lib.rs +++ b/block-padding/src/lib.rs @@ -9,7 +9,6 @@ html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", html_root_url = "https://docs.rs/block-padding/0.3.0" )] -#![forbid(unsafe_code)] #![warn(missing_docs, rust_2018_idioms)] #[cfg(feature = "std")] @@ -19,11 +18,25 @@ use core::fmt; pub use generic_array; use generic_array::{ArrayLength, GenericArray}; +/// Padding types +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum PadType { + /// Reversible padding + Reversible, + /// Ambiguous padding + Ambiguous, + /// No padding, message must be mutliple of block size + NoPadding, +} + /// Block size. pub type Block = GenericArray; /// Trait for padding messages divided into blocks pub trait Padding> { + /// Padding type + const TYPE: PadType; + /// Pads `block` filled with data up to `pos` (i.e length of a message /// stored in the block is equal to `pos`). /// @@ -36,6 +49,27 @@ pub trait Padding> { /// /// Returns `Err(UnpadError)` if the block containts malformed padding. fn unpad(block: &Block) -> Result<&[u8], UnpadError>; + + /// Unpad data in the `blocks`. + /// + /// Returns `Err(UnpadError)` if the block containts malformed padding. + fn unpad_blocks(blocks: &[Block]) -> Result<&[u8], UnpadError> { + let bs = BlockSize::USIZE; + let res_len = match (blocks.last(), Self::TYPE) { + (_, PadType::NoPadding) => bs * blocks.len(), + (Some(last_block), _) => { + let n = Self::unpad(last_block)?.len(); + n + bs * (blocks.len() - 1) + } + (None, PadType::Ambiguous) => 0, + (None, PadType::Reversible) => return Err(UnpadError), + }; + // SAFETY: `res_len` is always smaller or equal to `bs * blocks.len()` + Ok(unsafe { + let p = blocks.as_ptr() as *const u8; + core::slice::from_raw_parts(p, res_len) + }) + } } /// Pad block with zeros. @@ -60,6 +94,8 @@ pub trait Padding> { pub struct ZeroPadding; impl> Padding for ZeroPadding { + const TYPE: PadType = PadType::Ambiguous; + #[inline] fn pad(block: &mut Block, pos: usize) { if pos > B::USIZE { @@ -122,6 +158,8 @@ impl Pkcs7 { } impl> Padding for Pkcs7 { + const TYPE: PadType = PadType::Reversible; + #[inline] fn pad(block: &mut Block, pos: usize) { // TODO: use bounds to check it at compile time @@ -164,6 +202,8 @@ impl> Padding for Pkcs7 { pub struct Iso10126; impl> Padding for Iso10126 { + const TYPE: PadType = PadType::Reversible; + #[inline] fn pad(block: &mut Block, pos: usize) { // Instead of generating random bytes as specified by Iso10126 we @@ -197,6 +237,8 @@ impl> Padding for Iso10126 { pub struct AnsiX923; impl> Padding for AnsiX923 { + const TYPE: PadType = PadType::Reversible; + #[inline] fn pad(block: &mut Block, pos: usize) { // TODO: use bounds to check it at compile time @@ -251,6 +293,8 @@ impl> Padding for AnsiX923 { pub struct Iso7816; impl> Padding for Iso7816 { + const TYPE: PadType = PadType::Reversible; + #[inline] fn pad(block: &mut Block, pos: usize) { if pos >= B::USIZE { @@ -300,6 +344,8 @@ impl> Padding for Iso7816 { pub struct NoPadding; impl> Padding for NoPadding { + const TYPE: PadType = PadType::NoPadding; + #[inline] fn pad(_block: &mut Block, pos: usize) { if pos > B::USIZE { @@ -324,4 +370,5 @@ impl fmt::Display for UnpadError { } #[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] impl std::error::Error for UnpadError {} diff --git a/inout/CHANGELOG.md b/inout/CHANGELOG.md new file mode 100644 index 00000000..4ba000bf --- /dev/null +++ b/inout/CHANGELOG.md @@ -0,0 +1,10 @@ +# Changelog +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## 0.1.0 (2022-02-10) +- Initial release ([#675]) + +[#675]: https://github.com/RustCrypto/utils/pull/675 diff --git a/inout/Cargo.toml b/inout/Cargo.toml new file mode 100644 index 00000000..3d2feac6 --- /dev/null +++ b/inout/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "inout" +version = "0.1.0" # Also update html_root_url in lib.rs when bumping this +description = "Custom reference types for code generic over in-place and buffer-to-buffer modes of operation." +authors = ["RustCrypto Developers"] +license = "MIT OR Apache-2.0" +edition = "2021" +rust-version = "1.56" +documentation = "https://docs.rs/inout" +repository = "https://github.com/RustCrypto/utils" +keywords = ["custom-reference"] + +# Hack to allow this crate to coexist with pre-2021 edition crates +[workspace] +members = ["."] + +[dependencies] +generic-array = "0.14" +block-padding = { version = "0.3", path = "../block-padding", optional = true } + +[features] +std = ["block-padding/std"] + +[package.metadata.docs.rs] +all-features = true +rustdoc-args = ["--cfg", "docsrs"] diff --git a/inout/LICENSE-APACHE b/inout/LICENSE-APACHE new file mode 100644 index 00000000..78173fa2 --- /dev/null +++ b/inout/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + +3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + +4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + +5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + +6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + +7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + +8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + +9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + +END OF TERMS AND CONDITIONS + +APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + +Copyright [yyyy] [name of copyright owner] + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. diff --git a/inout/LICENSE-MIT b/inout/LICENSE-MIT new file mode 100644 index 00000000..26af9406 --- /dev/null +++ b/inout/LICENSE-MIT @@ -0,0 +1,26 @@ +Copyright (c) 2022 The RustCrypto Project Developers +Copyright (c) 2022 Artyom Pavlov + +Permission is hereby granted, free of charge, to any +person obtaining a copy of this software and associated +documentation files (the "Software"), to deal in the +Software without restriction, including without +limitation the rights to use, copy, modify, merge, +publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software +is furnished to do so, subject to the following +conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions +of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF +ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED +TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT +SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR +IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/inout/src/errors.rs b/inout/src/errors.rs new file mode 100644 index 00000000..be61a780 --- /dev/null +++ b/inout/src/errors.rs @@ -0,0 +1,62 @@ +use core::fmt; + +/// The error returned when slice can not be converted into array. +#[derive(Copy, Clone, Debug)] +pub struct IntoArrayError; + +impl fmt::Display for IntoArrayError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Failed to convert into array.") + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for IntoArrayError {} + +/// The error returned when input and output slices have different length +/// and thus can not be converted to `InOutBuf`. +#[derive(Copy, Clone, Debug)] +pub struct NotEqualError; + +impl fmt::Display for NotEqualError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Length of input slices is not equal to each other") + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for NotEqualError {} + +/// Padding error. Usually emitted when size of output buffer is insufficient. +#[cfg(feature = "block-padding")] +#[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] +#[derive(Clone, Copy, Debug)] +pub struct PadError; + +#[cfg(feature = "block-padding")] +impl fmt::Display for PadError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Padding error") + } +} + +#[cfg(feature = "block-padding")] +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for PadError {} + +/// Output buffer is smaller than input buffer. +#[derive(Clone, Copy, Debug)] +pub struct OutIsTooSmallError; + +impl fmt::Display for OutIsTooSmallError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + f.write_str("Output buffer is smaller than input") + } +} + +#[cfg(feature = "std")] +#[cfg_attr(docsrs, doc(cfg(feature = "std")))] +impl std::error::Error for OutIsTooSmallError {} diff --git a/inout/src/inout.rs b/inout/src/inout.rs new file mode 100644 index 00000000..8e798865 --- /dev/null +++ b/inout/src/inout.rs @@ -0,0 +1,174 @@ +use crate::InOutBuf; +use core::{marker::PhantomData, ptr}; +use generic_array::{ArrayLength, GenericArray}; + +/// Custom pointer type which contains one immutable (input) and one mutable +/// (output) pointer, which are either equal or non-overlapping. +pub struct InOut<'inp, 'out, T> { + pub(crate) in_ptr: *const T, + pub(crate) out_ptr: *mut T, + pub(crate) _pd: PhantomData<(&'inp T, &'out mut T)>, +} + +impl<'inp, 'out, T> InOut<'inp, 'out, T> { + /// Reborrow `self`. + #[inline(always)] + pub fn reborrow<'a>(&'a mut self) -> InOut<'a, 'a, T> { + Self { + in_ptr: self.in_ptr, + out_ptr: self.out_ptr, + _pd: PhantomData, + } + } + + /// Get immutable reference to the input value. + #[inline(always)] + pub fn get_in<'a>(&'a self) -> &'a T { + unsafe { &*self.in_ptr } + } + + /// Get mutable reference to the output value. + #[inline(always)] + pub fn get_out<'a>(&'a mut self) -> &'a mut T { + unsafe { &mut *self.out_ptr } + } + + /// Convert `self` to a pair of raw input and output pointers. + #[inline(always)] + pub fn into_raw(self) -> (*const T, *mut T) { + (self.in_ptr, self.out_ptr) + } + + /// Create `InOut` from raw input and output pointers. + /// + /// # Safety + /// Behavior is undefined if any of the following conditions are violated: + /// - `in_ptr` must point to a properly initialized value of type `T` and + /// must be valid for reads. + /// - `out_ptr` must point to a properly initialized value of type `T` and + /// must be valid for both reads and writes. + /// - `in_ptr` and `out_ptr` must be either equal or non-overlapping. + /// - If `in_ptr` and `out_ptr` are equal, then the memory referenced by + /// them must not be accessed through any other pointer (not derived from + /// the return value) for the duration of lifetime 'a. Both read and write + /// accesses are forbidden. + /// - If `in_ptr` and `out_ptr` are not equal, then the memory referenced by + /// `out_ptr` must not be accessed through any other pointer (not derived from + /// the return value) for the duration of lifetime `'a`. Both read and write + /// accesses are forbidden. The memory referenced by `in_ptr` must not be + /// mutated for the duration of lifetime `'a`, except inside an `UnsafeCell`. + #[inline(always)] + pub unsafe fn from_raw(in_ptr: *const T, out_ptr: *mut T) -> InOut<'inp, 'out, T> { + Self { + in_ptr, + out_ptr, + _pd: PhantomData, + } + } +} + +impl<'inp, 'out, T: Clone> InOut<'inp, 'out, T> { + /// Clone input value and return it. + #[inline(always)] + pub fn clone_in(&self) -> T { + unsafe { (&*self.in_ptr).clone() } + } +} + +impl<'a, T> From<&'a mut T> for InOut<'a, 'a, T> { + #[inline(always)] + fn from(val: &'a mut T) -> Self { + let out_ptr = val as *mut T; + Self { + in_ptr: out_ptr as *const T, + out_ptr, + _pd: PhantomData, + } + } +} + +impl<'inp, 'out, T> From<(&'inp T, &'out mut T)> for InOut<'inp, 'out, T> { + #[inline(always)] + fn from((in_val, out_val): (&'inp T, &'out mut T)) -> Self { + Self { + in_ptr: in_val as *const T, + out_ptr: out_val as *mut T, + _pd: Default::default(), + } + } +} + +impl<'inp, 'out, T, N: ArrayLength> InOut<'inp, 'out, GenericArray> { + /// Returns `InOut` for the given position. + /// + /// # Panics + /// If `pos` greater or equal to array length. + #[inline(always)] + pub fn get<'a>(&'a mut self, pos: usize) -> InOut<'a, 'a, T> { + assert!(pos < N::USIZE); + unsafe { + InOut { + in_ptr: (self.in_ptr as *const T).add(pos), + out_ptr: (self.out_ptr as *mut T).add(pos), + _pd: PhantomData, + } + } + } + + /// Convert `InOut` array to `InOutBuf`. + #[inline(always)] + pub fn into_buf(self) -> InOutBuf<'inp, 'out, T> { + InOutBuf { + in_ptr: self.in_ptr as *const T, + out_ptr: self.out_ptr as *mut T, + len: N::USIZE, + _pd: PhantomData, + } + } +} + +impl<'inp, 'out, N: ArrayLength> InOut<'inp, 'out, GenericArray> { + /// XOR `data` with values behind the input slice and write + /// result to the output slice. + /// + /// # Panics + /// If `data` length is not equal to the buffer length. + #[inline(always)] + #[allow(clippy::needless_range_loop)] + pub fn xor_in2out(&mut self, data: &GenericArray) { + unsafe { + let input = ptr::read(self.in_ptr); + let mut temp = GenericArray::::default(); + for i in 0..N::USIZE { + temp[i] = input[i] ^ data[i]; + } + ptr::write(self.out_ptr, temp); + } + } +} + +impl<'inp, 'out, N, M> InOut<'inp, 'out, GenericArray, M>> +where + N: ArrayLength, + M: ArrayLength>, +{ + /// XOR `data` with values behind the input slice and write + /// result to the output slice. + /// + /// # Panics + /// If `data` length is not equal to the buffer length. + #[inline(always)] + #[allow(clippy::needless_range_loop)] + pub fn xor_in2out(&mut self, data: &GenericArray, M>) { + unsafe { + let input = ptr::read(self.in_ptr); + let mut temp = GenericArray::, M>::default(); + for i in 0..M::USIZE { + for j in 0..N::USIZE { + temp[i][j] = input[i][j] ^ data[i][j]; + } + } + ptr::write(self.out_ptr, temp); + } + } +} diff --git a/inout/src/inout_buf.rs b/inout/src/inout_buf.rs new file mode 100644 index 00000000..0ab1b8dc --- /dev/null +++ b/inout/src/inout_buf.rs @@ -0,0 +1,302 @@ +use crate::{ + errors::{IntoArrayError, NotEqualError}, + InOut, +}; +use core::{marker::PhantomData, slice}; +use generic_array::{ArrayLength, GenericArray}; + +/// Custom slice type which references one immutable (input) slice and one +/// mutable (output) slice of equal length. Input and output slices are +/// either the same or do not overlap. +pub struct InOutBuf<'inp, 'out, T> { + pub(crate) in_ptr: *const T, + pub(crate) out_ptr: *mut T, + pub(crate) len: usize, + pub(crate) _pd: PhantomData<(&'inp T, &'out mut T)>, +} + +impl<'a, T> From<&'a mut [T]> for InOutBuf<'a, 'a, T> { + #[inline(always)] + fn from(buf: &'a mut [T]) -> Self { + Self { + in_ptr: buf.as_ptr(), + out_ptr: buf.as_mut_ptr(), + len: buf.len(), + _pd: PhantomData, + } + } +} + +impl<'a, T> InOutBuf<'a, 'a, T> { + /// Create `InOutBuf` from a single mutable reference. + #[inline(always)] + pub fn from_mut(val: &'a mut T) -> InOutBuf<'a, 'a, T> { + let out_ptr = val as *mut T; + Self { + in_ptr: out_ptr as *const T, + out_ptr, + len: 1, + _pd: PhantomData, + } + } +} + +impl<'inp, 'out, T> IntoIterator for InOutBuf<'inp, 'out, T> { + type Item = InOut<'inp, 'out, T>; + type IntoIter = InOutBufIter<'inp, 'out, T>; + + #[inline(always)] + fn into_iter(self) -> Self::IntoIter { + InOutBufIter { buf: self, pos: 0 } + } +} + +impl<'inp, 'out, T> InOutBuf<'inp, 'out, T> { + /// Create `InOutBuf` from a pair of immutable and mutable references. + #[inline(always)] + pub fn from_ref_mut(in_val: &'inp T, out_val: &'out mut T) -> Self { + Self { + in_ptr: in_val as *const T, + out_ptr: out_val as *mut T, + len: 1, + _pd: PhantomData, + } + } + + /// Create `InOutBuf` from immutable and mutable slices. + /// + /// Returns an error if length of slices is not equal to each other. + #[inline(always)] + pub fn new(in_buf: &'inp [T], out_buf: &'out mut [T]) -> Result { + if in_buf.len() != out_buf.len() { + Err(NotEqualError) + } else { + Ok(Self { + in_ptr: in_buf.as_ptr(), + out_ptr: out_buf.as_mut_ptr(), + len: in_buf.len(), + _pd: Default::default(), + }) + } + } + + /// Get length of the inner buffers. + #[inline(always)] + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if the buffer has a length of 0. + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns `InOut` for given position. + /// + /// # Panics + /// If `pos` greater or equal to buffer length. + #[inline(always)] + pub fn get<'a>(&'a mut self, pos: usize) -> InOut<'a, 'a, T> { + assert!(pos < self.len); + unsafe { + InOut { + in_ptr: self.in_ptr.add(pos), + out_ptr: self.out_ptr.add(pos), + _pd: PhantomData, + } + } + } + + /// Get input slice. + #[inline(always)] + pub fn get_in<'a>(&'a self) -> &'a [T] { + unsafe { slice::from_raw_parts(self.in_ptr, self.len) } + } + + /// Get output slice. + #[inline(always)] + pub fn get_out<'a>(&'a mut self) -> &'a mut [T] { + unsafe { slice::from_raw_parts_mut(self.out_ptr, self.len) } + } + + /// Consume self and return output slice with lifetime `'a`. + #[inline(always)] + pub fn into_out(self) -> &'out mut [T] { + unsafe { slice::from_raw_parts_mut(self.out_ptr, self.len) } + } + + /// Get raw input and output pointers. + #[inline(always)] + pub fn into_raw(self) -> (*const T, *mut T) { + (self.in_ptr, self.out_ptr) + } + + /// Reborrow `self`. + #[inline(always)] + pub fn reborrow<'a>(&'a mut self) -> InOutBuf<'a, 'a, T> { + Self { + in_ptr: self.in_ptr, + out_ptr: self.out_ptr, + len: self.len, + _pd: PhantomData, + } + } + + /// Create [`InOutBuf`] from raw input and output pointers. + /// + /// # Safety + /// Behavior is undefined if any of the following conditions are violated: + /// - `in_ptr` must point to a properly initialized value of type `T` and + /// must be valid for reads for `len * mem::size_of::()` many bytes. + /// - `out_ptr` must point to a properly initialized value of type `T` and + /// must be valid for both reads and writes for `len * mem::size_of::()` + /// many bytes. + /// - `in_ptr` and `out_ptr` must be either equal or non-overlapping. + /// - If `in_ptr` and `out_ptr` are equal, then the memory referenced by + /// them must not be accessed through any other pointer (not derived from + /// the return value) for the duration of lifetime 'a. Both read and write + /// accesses are forbidden. + /// - If `in_ptr` and `out_ptr` are not equal, then the memory referenced by + /// `out_ptr` must not be accessed through any other pointer (not derived from + /// the return value) for the duration of lifetime 'a. Both read and write + /// accesses are forbidden. The memory referenced by `in_ptr` must not be + /// mutated for the duration of lifetime `'a`, except inside an `UnsafeCell`. + /// - The total size `len * mem::size_of::()` must be no larger than `isize::MAX`. + #[inline(always)] + pub unsafe fn from_raw( + in_ptr: *const T, + out_ptr: *mut T, + len: usize, + ) -> InOutBuf<'inp, 'out, T> { + Self { + in_ptr, + out_ptr, + len, + _pd: PhantomData, + } + } + + /// Divides one buffer into two at `mid` index. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `mid > len`. + #[inline(always)] + pub fn split_at(self, mid: usize) -> (InOutBuf<'inp, 'out, T>, InOutBuf<'inp, 'out, T>) { + assert!(mid <= self.len); + let (tail_in_ptr, tail_out_ptr) = unsafe { (self.in_ptr.add(mid), self.out_ptr.add(mid)) }; + ( + InOutBuf { + in_ptr: self.in_ptr, + out_ptr: self.out_ptr, + len: mid, + _pd: PhantomData, + }, + InOutBuf { + in_ptr: tail_in_ptr, + out_ptr: tail_out_ptr, + len: self.len() - mid, + _pd: PhantomData, + }, + ) + } + + /// Partition buffer into 2 parts: buffer of arrays and tail. + #[inline(always)] + pub fn into_chunks>( + self, + ) -> ( + InOutBuf<'inp, 'out, GenericArray>, + InOutBuf<'inp, 'out, T>, + ) { + let chunks = self.len() / N::USIZE; + let tail_pos = N::USIZE * chunks; + let tail_len = self.len() - tail_pos; + unsafe { + let chunks = InOutBuf { + in_ptr: self.in_ptr as *const GenericArray, + out_ptr: self.out_ptr as *mut GenericArray, + len: chunks, + _pd: PhantomData, + }; + let tail = InOutBuf { + in_ptr: self.in_ptr.add(tail_pos), + out_ptr: self.out_ptr.add(tail_pos), + len: tail_len, + _pd: PhantomData, + }; + (chunks, tail) + } + } +} + +impl<'inp, 'out> InOutBuf<'inp, 'out, u8> { + /// XORs `data` with values behind the input slice and write + /// result to the output slice. + /// + /// # Panics + /// If `data` length is not equal to the buffer length. + #[inline(always)] + #[allow(clippy::needless_range_loop)] + pub fn xor_in2out(&mut self, data: &[u8]) { + assert_eq!(self.len(), data.len()); + unsafe { + for i in 0..data.len() { + let in_ptr = self.in_ptr.add(i); + let out_ptr = self.out_ptr.add(i); + *out_ptr = *in_ptr ^ data[i]; + } + } + } +} + +impl<'inp, 'out, T, N> TryInto>> for InOutBuf<'inp, 'out, T> +where + N: ArrayLength, +{ + type Error = IntoArrayError; + + #[inline(always)] + fn try_into(self) -> Result>, Self::Error> { + if self.len() == N::USIZE { + Ok(InOut { + in_ptr: self.in_ptr as *const _, + out_ptr: self.out_ptr as *mut _, + _pd: PhantomData, + }) + } else { + Err(IntoArrayError) + } + } +} + +/// Iterator over [`InOutBuf`]. +pub struct InOutBufIter<'inp, 'out, T> { + buf: InOutBuf<'inp, 'out, T>, + pos: usize, +} + +impl<'inp, 'out, T> Iterator for InOutBufIter<'inp, 'out, T> { + type Item = InOut<'inp, 'out, T>; + + #[inline(always)] + fn next(&mut self) -> Option { + if self.buf.len() == self.pos { + return None; + } + let res = unsafe { + InOut { + in_ptr: self.buf.in_ptr.add(self.pos), + out_ptr: self.buf.out_ptr.add(self.pos), + _pd: PhantomData, + } + }; + self.pos += 1; + Some(res) + } +} diff --git a/inout/src/lib.rs b/inout/src/lib.rs new file mode 100644 index 00000000..870b014e --- /dev/null +++ b/inout/src/lib.rs @@ -0,0 +1,25 @@ +//! Collection of custom reference types for code generic over in-place and +//! buffer-to-buffer modes of operation. + +#![no_std] +#![doc( + html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", + html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg", + html_root_url = "https://docs.rs/inout/0.1.0" +)] +#![allow(clippy::needless_lifetimes)] +#![warn(missing_docs, rust_2018_idioms)] + +#[cfg(feature = "std")] +extern crate std; + +#[cfg(feature = "block-padding")] +#[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] +pub use block_padding; + +mod errors; +mod inout; +mod inout_buf; +mod reserved; + +pub use crate::{errors::*, inout::*, inout_buf::*, reserved::*}; diff --git a/inout/src/reserved.rs b/inout/src/reserved.rs new file mode 100644 index 00000000..940a3138 --- /dev/null +++ b/inout/src/reserved.rs @@ -0,0 +1,226 @@ +use crate::errors::OutIsTooSmallError; +use core::{marker::PhantomData, slice}; + +#[cfg(feature = "block-padding")] +use crate::errors::PadError; +#[cfg(feature = "block-padding")] +use crate::{InOut, InOutBuf}; +#[cfg(feature = "block-padding")] +use block_padding::{PadType, Padding}; +#[cfg(feature = "block-padding")] +use generic_array::{ArrayLength, GenericArray}; + +/// Custom slice type which references one immutable (input) slice and one +/// mutable (output) slice. Input and output slices are either the same or +/// do not overlap. Length of the output slice is always equal or bigger than +/// length of the input slice. +pub struct InOutBufReserved<'inp, 'out, T> { + in_ptr: *const T, + out_ptr: *mut T, + in_len: usize, + out_len: usize, + _pd: PhantomData<(&'inp T, &'out mut T)>, +} + +impl<'a, T> InOutBufReserved<'a, 'a, T> { + /// Crate [`InOutBufReserved`] from a single mutable slice. + pub fn from_mut_slice(buf: &'a mut [T], msg_len: usize) -> Result { + if msg_len > buf.len() { + return Err(OutIsTooSmallError); + } + let out_ptr = buf.as_mut_ptr(); + let out_len = buf.len(); + Ok(Self { + in_ptr: out_ptr as *const T, + out_ptr, + in_len: msg_len, + out_len, + _pd: PhantomData, + }) + } + + /// Create [`InOutBufReserved`] from raw input and output pointers. + /// + /// # Safety + /// Behavior is undefined if any of the following conditions are violated: + /// - `in_ptr` must point to a properly initialized value of type `T` and + /// must be valid for reads for `in_len * mem::size_of::()` many bytes. + /// - `out_ptr` must point to a properly initialized value of type `T` and + /// must be valid for both reads and writes for `out_len * mem::size_of::()` + /// many bytes. + /// - `in_ptr` and `out_ptr` must be either equal or non-overlapping. + /// - If `in_ptr` and `out_ptr` are equal, then the memory referenced by + /// them must not be accessed through any other pointer (not derived from + /// the return value) for the duration of lifetime 'a. Both read and write + /// accesses are forbidden. + /// - If `in_ptr` and `out_ptr` are not equal, then the memory referenced by + /// `out_ptr` must not be accessed through any other pointer (not derived from + /// the return value) for the duration of lifetime 'a. Both read and write + /// accesses are forbidden. The memory referenced by `in_ptr` must not be + /// mutated for the duration of lifetime `'a`, except inside an `UnsafeCell`. + /// - The total size `in_len * mem::size_of::()` and + /// `out_len * mem::size_of::()` must be no larger than `isize::MAX`. + #[inline(always)] + pub unsafe fn from_raw( + in_ptr: *const T, + in_len: usize, + out_ptr: *mut T, + out_len: usize, + ) -> Self { + Self { + in_ptr, + out_ptr, + in_len, + out_len, + _pd: PhantomData, + } + } + + /// Get raw input and output pointers. + #[inline(always)] + pub fn into_raw(self) -> (*const T, *mut T) { + (self.in_ptr, self.out_ptr) + } + + /// Get input buffer length. + #[inline(always)] + pub fn get_in_len(self) -> usize { + self.in_len + } + + /// Get output buffer length. + #[inline(always)] + pub fn get_out_len(self) -> usize { + self.in_len + } +} + +impl<'inp, 'out, T> InOutBufReserved<'inp, 'out, T> { + /// Crate [`InOutBufReserved`] from two separate slices. + pub fn from_slices( + in_buf: &'inp [T], + out_buf: &'out mut [T], + ) -> Result { + if in_buf.len() > out_buf.len() { + return Err(OutIsTooSmallError); + } + Ok(Self { + in_ptr: in_buf.as_ptr(), + out_ptr: out_buf.as_mut_ptr(), + in_len: in_buf.len(), + out_len: out_buf.len(), + _pd: PhantomData, + }) + } + + /// Get input slice. + #[inline(always)] + pub fn get_in<'a>(&'a self) -> &'a [T] { + unsafe { slice::from_raw_parts(self.in_ptr, self.in_len) } + } + + /// Get output slice. + #[inline(always)] + pub fn get_out<'a>(&'a mut self) -> &'a mut [T] { + unsafe { slice::from_raw_parts_mut(self.out_ptr, self.out_len) } + } +} + +impl<'inp, 'out> InOutBufReserved<'inp, 'out, u8> { + /// Transform buffer into [`PaddedInOutBuf`] using padding algorithm `P`. + #[cfg(feature = "block-padding")] + #[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] + #[inline(always)] + pub fn into_padded_blocks(self) -> Result, PadError> + where + P: Padding, + BS: ArrayLength, + { + let bs = BS::USIZE; + let blocks_len = self.in_len / bs; + let tail_len = self.in_len - bs * blocks_len; + let blocks = unsafe { + InOutBuf::from_raw( + self.in_ptr as *const GenericArray, + self.out_ptr as *mut GenericArray, + blocks_len, + ) + }; + let mut tail_in = GenericArray::::default(); + let tail_out = match P::TYPE { + PadType::NoPadding | PadType::Ambiguous if tail_len == 0 => None, + PadType::NoPadding => return Err(PadError), + PadType::Reversible | PadType::Ambiguous => { + let blen = bs * blocks_len; + let res_len = blen + bs; + if res_len > self.out_len { + return Err(PadError); + } + // SAFETY: `in_ptr + blen..in_ptr + blen + tail_len` + // is valid region for reads and `tail_len` is smaller than `BS`. + // we have verified that `blen + bs <= out_len`, in other words, + // `out_ptr + blen..out_ptr + blen + bs` is valid region + // for writes. + let out_block = unsafe { + core::ptr::copy_nonoverlapping( + self.in_ptr.add(blen), + tail_in.as_mut_ptr(), + tail_len, + ); + &mut *(self.out_ptr.add(blen) as *mut GenericArray) + }; + P::pad(&mut tail_in, tail_len); + Some(out_block) + } + }; + Ok(PaddedInOutBuf { + blocks, + tail_in, + tail_out, + }) + } +} + +/// Variant of [`InOutBuf`] with optional padded tail block. +#[cfg(feature = "block-padding")] +#[cfg_attr(docsrs, doc(cfg(feature = "block-padding")))] +pub struct PaddedInOutBuf<'inp, 'out, BS: ArrayLength> { + blocks: InOutBuf<'inp, 'out, GenericArray>, + tail_in: GenericArray, + tail_out: Option<&'out mut GenericArray>, +} + +#[cfg(feature = "block-padding")] +impl<'inp, 'out, BS: ArrayLength> PaddedInOutBuf<'inp, 'out, BS> { + /// Get full blocks. + #[inline(always)] + pub fn get_blocks<'a>(&'a mut self) -> InOutBuf<'a, 'a, GenericArray> { + self.blocks.reborrow() + } + + /// Get padded tail block. + /// + /// For paddings with `P::TYPE = PadType::Reversible` it always returns `Some`. + #[inline(always)] + pub fn get_tail_block<'a>(&'a mut self) -> Option>> { + match self.tail_out.as_deref_mut() { + Some(out_block) => Some((&self.tail_in, out_block).into()), + None => None, + } + } + + /// Convert buffer into output slice. + #[inline(always)] + pub fn into_out(self) -> &'out [u8] { + let total_blocks = if self.tail_out.is_some() { + self.blocks.len() + 1 + } else { + self.blocks.len() + }; + let res_len = BS::USIZE * total_blocks; + let (_, out_ptr) = self.blocks.into_raw(); + // SAFETY: `res_len` is always valid for the output buffer since + // it's checked during type construction + unsafe { slice::from_raw_parts(out_ptr as *const u8, res_len) } + } +}