diff --git a/.cargo/config.toml b/.cargo/config.toml
index 9768afc5..ddbe09f3 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,67 +1,2 @@
-# Linux x86_64
-[target.x86_64-unknown-linux-gnu]
-rustflags = ["-C", "target-cpu=native"]
-
-# Linux ARM64
-[target.aarch64-unknown-linux-gnu]
-rustflags = ["-C", "target-feature=+lse", "-C", "target-cpu=native"]
-
-# Windows x86_64
-[target.x86_64-pc-windows-msvc]
-
-# Windows ARM64
-[target.aarch64-pc-windows-msvc]
-rustflags = ["-C", "target-feature=+lse"]
-
-# Tier B
-# Linux x86_64 MUSL
-[target.x86_64-unknown-linux-musl]
-
-# Linux ARM64 MUSL
-[target.aarch64-unknown-linux-musl]
-rustflags = ["-C", "target-feature=+lse"]
-
-# Tier C - WASM
-# WASM - Browser
-[target.wasm32-unknown-unknown]
-
-# WASM/WASI - Server
-[target.wasm32-wasip1]
-
-# Tier D - Enterprise
-# IBM s390x (Z mainframes)
-[target.s390x-unknown-linux-gnu]
-
-# IBM PowerPC64 (little-endian, modern POWER systems)
-[target.powerpc64le-unknown-linux-gnu]
-
-# RISC-V 64-bit Linux (RV64GC: general + compressed)
-[target.riscv64gc-unknown-linux-gnu]
-
-# LoongArch 64-bit Linux
-[target.loongarch64-unknown-linux-gnu]
-
-# Tier E - no_std Bare Metal Sentinels
-# Cortex-M0/M0+ (minimal no_std sentinel - no FPU, no DSP)
-# If it compiles here, it compiles everywhere
-[target.thumbv6m-none-eabi]
-
-# ARM64 bare metal (modern no_std sentinel - hypervisors, TEEs)
-# No LSE: target hardware capabilities vary
-[target.aarch64-unknown-none]
-
-# RISC-V 64-bit bare metal (RV64IMAFDC)
-[target.riscv64gc-unknown-none-elf]
-
-# RISC-V 32-bit bare metal (RV32IMAC)
-[target.riscv32imac-unknown-none-elf]
-
-# x86_64 bare metal (kernels, bootloaders, hypervisors)
-[target.x86_64-unknown-none]
-
-# Build Configuration
[build]
rustdocflags = ["-D", "warnings"]
-
-[env]
-RUST_LOG = "info"
diff --git a/.changes/add-debug-output-for-4cec.md b/.changes/add-debug-output-for-4cec.md
new file mode 100644
index 00000000..7f386f2c
--- /dev/null
+++ b/.changes/add-debug-output-for-4cec.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Add Debug output for Ed25519 verification diagnostic snapshots.
diff --git a/.changes/add-debug-output-for-6a2e.md b/.changes/add-debug-output-for-6a2e.md
new file mode 100644
index 00000000..52f38010
--- /dev/null
+++ b/.changes/add-debug-output-for-6a2e.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Add Debug output for AES-GCM nonce-counter state.
diff --git a/.changes/add-redacted-debug-output-4c0e.md b/.changes/add-redacted-debug-output-4c0e.md
new file mode 100644
index 00000000..5a8a312e
--- /dev/null
+++ b/.changes/add-redacted-debug-output-4c0e.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "major"
+---
+
+Group caller-supplied RSA blinding inputs in `RsaBlindingPair` and add redacted `Debug` output for borrowed RSA private-key inputs.
diff --git a/.changes/document-checksum-and-digest-e536.md b/.changes/document-checksum-and-digest-e536.md
new file mode 100644
index 00000000..35942df7
--- /dev/null
+++ b/.changes/document-checksum-and-digest-e536.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Document checksum and digest I/O adapter state and ownership methods.
diff --git a/.changes/document-shake128-and-shake256-2928.md b/.changes/document-shake128-and-shake256-2928.md
new file mode 100644
index 00000000..a6c482da
--- /dev/null
+++ b/.changes/document-shake128-and-shake256-2928.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Document SHAKE128 and SHAKE256 state and output-reader APIs.
diff --git a/.changes/harden-aegis256-aesni-a37b.md b/.changes/harden-aegis256-aesni-a37b.md
new file mode 100644
index 00000000..46d4b9b3
--- /dev/null
+++ b/.changes/harden-aegis256-aesni-a37b.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Harden the x86 and POWER8 AEGIS-256 backends against out-of-bounds pointer formation while preserving oracle-compatible ciphertext and tags.
diff --git a/.changes/harden-blake2b-length-handling-4ab7.md b/.changes/harden-blake2b-length-handling-4ab7.md
new file mode 100644
index 00000000..3c2dfee8
--- /dev/null
+++ b/.changes/harden-blake2b-length-handling-4ab7.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Harden Blake2b length handling, state cleanup, and portable diagnostics.
diff --git a/.changes/harden-blake2s-length-handling-454e.md b/.changes/harden-blake2s-length-handling-454e.md
new file mode 100644
index 00000000..694dba0a
--- /dev/null
+++ b/.changes/harden-blake2s-length-handling-454e.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Harden Blake2s length handling, state cleanup, and portable diagnostics.
diff --git a/.changes/harden-blake3-tree-arithmetic-32c1.md b/.changes/harden-blake3-tree-arithmetic-32c1.md
new file mode 100644
index 00000000..193fb694
--- /dev/null
+++ b/.changes/harden-blake3-tree-arithmetic-32c1.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Harden BLAKE3 tree and SIMD kernel arithmetic, bounds, and assembly ABI validation.
diff --git a/.changes/harden-ecdsa-limb-boundaries-6503.md b/.changes/harden-ecdsa-limb-boundaries-6503.md
new file mode 100644
index 00000000..6dcdd27f
--- /dev/null
+++ b/.changes/harden-ecdsa-limb-boundaries-6503.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Harden ECDSA limb arithmetic, endian conversion, diagnostic documentation, and fixed-work RISC-V and s390x multiplication while preserving P-256 and P-384 signature semantics.
diff --git a/.changes/harden-ed25519-simd-boundaries-de52.md b/.changes/harden-ed25519-simd-boundaries-de52.md
new file mode 100644
index 00000000..221f8db8
--- /dev/null
+++ b/.changes/harden-ed25519-simd-boundaries-de52.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Harden Ed25519 scalar encoding, fixed-base table validation, table selection, and AVX2/AVX-512 IFMA safety boundaries while preserving signature and verification results.
diff --git a/.changes/harden-ml-kem-encoding-2483.md b/.changes/harden-ml-kem-encoding-2483.md
new file mode 100644
index 00000000..4c082bbe
--- /dev/null
+++ b/.changes/harden-ml-kem-encoding-2483.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Harden ML-KEM encoding, arithmetic bounds, and key-layout validation.
diff --git a/.changes/harden-pbkdf2-block-indexing-54fe.md b/.changes/harden-pbkdf2-block-indexing-54fe.md
new file mode 100644
index 00000000..2074cb96
--- /dev/null
+++ b/.changes/harden-pbkdf2-block-indexing-54fe.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Harden PBKDF2 block indexing and document portable verification diagnostics.
diff --git a/.changes/remove-unused-riscv-crc64-surfac-b7f9.md b/.changes/remove-unused-riscv-crc64-surfac-b7f9.md
new file mode 100644
index 00000000..b18285c7
--- /dev/null
+++ b/.changes/remove-unused-riscv-crc64-surfac-b7f9.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "major"
+---
+
+Remove the unused RISC-V CRC-64 Zbc/Zvbc force modes and dead accelerated backend; RISC-V CRC-64 continues to use the portable slice-by-16 implementation until target evidence justifies acceleration.
diff --git a/.changes/report-unavailable-argon2-backe-8b41.md b/.changes/report-unavailable-argon2-backe-8b41.md
new file mode 100644
index 00000000..7d02e777
--- /dev/null
+++ b/.changes/report-unavailable-argon2-backe-8b41.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "patch"
+---
+
+Return `Argon2Error::BackendUnavailable` when a forced diagnostic backend is unsupported.
diff --git a/.changes/unsafe-chacha20-diagnostic-backe-51d5.md b/.changes/unsafe-chacha20-diagnostic-backe-51d5.md
new file mode 100644
index 00000000..2c8fa02f
--- /dev/null
+++ b/.changes/unsafe-chacha20-diagnostic-backe-51d5.md
@@ -0,0 +1,5 @@
+---
+"rscrypto" = "major"
+---
+
+Make the forced hardware ChaCha20 diagnostic functions `unsafe`; callers must establish the documented CPU capabilities before invoking them.
diff --git a/.config/ci-tool-archives.tsv b/.config/ci-tool-archives.tsv
index 75987b9d..fcda12a9 100644
--- a/.config/ci-tool-archives.tsv
+++ b/.config/ci-tool-archives.tsv
@@ -1,6 +1,6 @@
# tool version os architecture filename url sha256
-wasmtime v46.0.1 linux x86_64 wasmtime-v46.0.1-x86_64-linux.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v46.0.1/wasmtime-v46.0.1-x86_64-linux.tar.xz 9ae0b17ea298bcc52277a8208d6ab7fae8e1a89579672f9d82f9d86c116edb62
-wasmtime v46.0.1 linux aarch64 wasmtime-v46.0.1-aarch64-linux.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v46.0.1/wasmtime-v46.0.1-aarch64-linux.tar.xz 071c4def2a08f0ebc95c52dfd4f2886eb697ba495804217cf76e13b09d70a1be
-wasmtime v46.0.1 macos x86_64 wasmtime-v46.0.1-x86_64-macos.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v46.0.1/wasmtime-v46.0.1-x86_64-macos.tar.xz 0513db67e7089c7e5f743a01427782bc4def83854222f4bc9b1d75f0b925240b
-wasmtime v46.0.1 macos aarch64 wasmtime-v46.0.1-aarch64-macos.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v46.0.1/wasmtime-v46.0.1-aarch64-macos.tar.xz acee50be70dbe90b0ab2ac7db1321fc44715153a1b1cc58291c97b6d7cffc558
+wasmtime v47.0.3 linux x86_64 wasmtime-v47.0.3-x86_64-linux.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-linux.tar.xz ca1fc56d1afc40c8782e96c297fd182a0da162f9a8f52a1e7b094e1dd648e178
+wasmtime v47.0.3 linux aarch64 wasmtime-v47.0.3-aarch64-linux.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-aarch64-linux.tar.xz 497b518db00ae585f04390758eaa99ad555bee50612dce7d102602778fb46ff0
+wasmtime v47.0.3 macos x86_64 wasmtime-v47.0.3-x86_64-macos.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-x86_64-macos.tar.xz 424a50f76a9dcf4d02dab326b2374be1ad404030576ee915866e4af106058b35
+wasmtime v47.0.3 macos aarch64 wasmtime-v47.0.3-aarch64-macos.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v47.0.3/wasmtime-v47.0.3-aarch64-macos.tar.xz c2684249e5d9ef9351942cf2d315982cf201fe0300f05d63bc1527446f0cd37f
codecov v11.3.1 linux x86_64 codecov https://cli.codecov.io/v11.3.1/linux/codecov ca1d64196d2d34771084afe76ea657d581bf628e31d993ff8e52ea09cc88a56d
diff --git a/.config/nextest.toml b/.config/nextest.toml
index 2635257e..f139574f 100644
--- a/.config/nextest.toml
+++ b/.config/nextest.toml
@@ -1,3 +1,5 @@
+nextest-version = { required = "0.9.143", recommended = "0.9.143" }
+
[profile.default]
status-level = "pass"
success-output = "never"
@@ -15,6 +17,10 @@ slow-timeout = { period = "60s", terminate-after = 8 }
filter = 'binary(=rsa_wycheproof) & test(=wycheproof_rsaes_pkcs1v15_3072_decrypt_vectors_match_expected_results)'
slow-timeout = { period = "60s", terminate-after = 8 }
+[[profile.default.overrides]]
+filter = 'binary(=rsa_wycheproof) & (test(=wycheproof_oaep_mgf1sha1_vectors_are_rejected_by_sha2_mgf1sha2_profiles) | test(=wycheproof_pkcs1v15_sha2_sig_gen_vectors_match_expected_signatures))'
+slow-timeout = { period = "60s", terminate-after = 4 }
+
[profile.commit]
status-level = "fail"
success-output = "never"
@@ -22,7 +28,7 @@ failure-output = "immediate-final"
fail-fast = false
test-threads = "num-cpus"
slow-timeout = { period = "60s", terminate-after = 4 }
-retries = { backoff = "exponential", count = 2, delay = "1s", jitter = true }
+retries = 0
[profile.commit.junit]
path = "junit.xml"
diff --git a/.config/rail.toml b/.config/rail.toml
index 41f90f4e..ef09c902 100644
--- a/.config/rail.toml
+++ b/.config/rail.toml
@@ -4,23 +4,21 @@
# Targets
targets = [
"aarch64-apple-darwin",
+ "aarch64-pc-windows-msvc",
"aarch64-unknown-linux-gnu",
- "x86_64-apple-darwin",
- "x86_64-pc-windows-msvc",
- "x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
- "wasm32-unknown-unknown",
- "wasm32-wasip1",
- "x86_64-unknown-linux-musl",
- "aarch64-pc-windows-msvc",
"aarch64-unknown-none",
- "loongarch64-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"riscv32imac-unknown-none-elf",
"riscv64gc-unknown-linux-gnu",
- "riscv64gc-unknown-none-elf",
"s390x-unknown-linux-gnu",
"thumbv6m-none-eabi",
+ "wasm32-unknown-unknown",
+ "wasm32-wasip1",
+ "x86_64-apple-darwin",
+ "x86_64-pc-windows-msvc",
+ "x86_64-unknown-linux-gnu",
+ "x86_64-unknown-linux-musl",
"x86_64-unknown-none",
]
diff --git a/.config/target-matrix.json b/.config/target-matrix.json
index 938a59ca..9294bc28 100644
--- a/.config/target-matrix.json
+++ b/.config/target-matrix.json
@@ -1,28 +1,28 @@
{
"groups": {
"win": [
- "x86_64-pc-windows-msvc",
- "aarch64-pc-windows-msvc"
+ "aarch64-pc-windows-msvc",
+ "x86_64-pc-windows-msvc"
],
"macos": [
"aarch64-apple-darwin",
"x86_64-apple-darwin"
],
"linux": [
- "x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-gnu",
+ "aarch64-unknown-linux-musl",
"riscv64gc-unknown-linux-gnu",
- "x86_64-unknown-linux-musl",
- "aarch64-unknown-linux-musl"
+ "x86_64-unknown-linux-gnu",
+ "x86_64-unknown-linux-musl"
],
"ibm": [
- "s390x-unknown-linux-gnu",
- "powerpc64le-unknown-linux-gnu"
+ "powerpc64le-unknown-linux-gnu",
+ "s390x-unknown-linux-gnu"
],
"no_std": [
- "thumbv6m-none-eabi",
- "riscv32imac-unknown-none-elf",
"aarch64-unknown-none",
+ "riscv32imac-unknown-none-elf",
+ "thumbv6m-none-eabi",
"x86_64-unknown-none"
],
"wasm": [
@@ -31,9 +31,9 @@
]
},
"ci": [
- { "name": "x86_64-unknown-linux-gnu", "type": "runson", "pool": "linux-x64-ci" },
+ { "name": "aarch64-pc-windows-msvc", "type": "gha", "runner": "windows-11-arm" },
{ "name": "aarch64-unknown-linux-gnu", "type": "runson", "pool": "linux-arm64-ci" },
{ "name": "x86_64-pc-windows-msvc", "type": "gha", "runner": "windows-latest" },
- { "name": "aarch64-pc-windows-msvc", "type": "gha", "runner": "windows-11-arm" }
+ { "name": "x86_64-unknown-linux-gnu", "type": "runson", "pool": "linux-x64-ci" }
]
}
diff --git a/.config/toolchains.toml b/.config/toolchains.toml
new file mode 100644
index 00000000..034d6d27
--- /dev/null
+++ b/.config/toolchains.toml
@@ -0,0 +1 @@
+nightly = "nightly-2026-08-12"
diff --git a/.github/actions/setup-toolchain/action.yaml b/.github/actions/setup-toolchain/action.yaml
index 9f3f968c..67e7ae40 100644
--- a/.github/actions/setup-toolchain/action.yaml
+++ b/.github/actions/setup-toolchain/action.yaml
@@ -1,9 +1,12 @@
-name: Setup Rust Toolchain (Dynamic)
+name: Setup Rust Toolchain
description: |
- Install Rust toolchain reading version from rust-toolchain.toml.
- Single source of truth for toolchain version.
+ Install the stable development toolchain, MSRV, or pinned nightly contract.
inputs:
+ contract:
+ description: Toolchain contract to install (development, msrv, or nightly)
+ required: false
+ default: "development"
components:
description: Comma-separated list of components to install
required: false
@@ -11,7 +14,7 @@ inputs:
outputs:
toolchain:
- description: The toolchain version that was installed
+ description: Exact installed toolchain version
value: ${{ steps.read-toolchain.outputs.toolchain }}
runs:
@@ -20,8 +23,15 @@ runs:
- name: Read Toolchain Version
id: read-toolchain
shell: bash
+ env:
+ TOOLCHAIN_CONTRACT: ${{ inputs.contract }}
run: |
- TOOLCHAIN=$(bash scripts/lib/toolchain.sh)
+ case "$TOOLCHAIN_CONTRACT" in
+ development) TOOLCHAIN=$(bash scripts/lib/toolchain.sh) ;;
+ msrv) TOOLCHAIN=$(bash scripts/lib/toolchain.sh --msrv) ;;
+ nightly) TOOLCHAIN=$(bash scripts/lib/toolchain.sh --nightly) ;;
+ *) echo "Unsupported toolchain contract: $TOOLCHAIN_CONTRACT" >&2; exit 2 ;;
+ esac
echo "toolchain=$TOOLCHAIN" >> "$GITHUB_OUTPUT"
echo "Using toolchain: $TOOLCHAIN"
@@ -30,4 +40,6 @@ runs:
env:
TOOLCHAIN: ${{ steps.read-toolchain.outputs.toolchain }}
TOOLCHAIN_COMPONENTS: ${{ inputs.components }}
- run: scripts/ci/setup-toolchain.sh "$TOOLCHAIN" "$TOOLCHAIN_COMPONENTS"
+ run: |
+ scripts/ci/setup-toolchain.sh "$TOOLCHAIN" "$TOOLCHAIN_COMPONENTS" "$GITHUB_ENV"
+ RUSTUP_TOOLCHAIN="$TOOLCHAIN" rustc --version --verbose
diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml
index a9f05709..99a2b35d 100644
--- a/.github/actions/setup/action.yaml
+++ b/.github/actions/setup/action.yaml
@@ -4,6 +4,10 @@ description: |
Unified host setup for GHA + runs-on.com + IBM/RISC-V runners.
inputs:
+ toolchain-contract:
+ description: Toolchain contract to install (development, msrv, or nightly).
+ required: false
+ default: "development"
cache-key:
description: Suffix for the rust-cache key (workflow + target).
required: false
@@ -31,11 +35,12 @@ runs:
# runs-on.com S3-backed cache intercept. No-op on non-runs-on pools.
- name: Setup MagicCache
if: inputs.enable-magic-cache == 'true'
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc # v2.2.0
+ uses: runs-on/action@46910bf61b41721b0579f237e186afb35477007a # v2.3.0
- name: Setup Toolchain
uses: ./.github/actions/setup-toolchain
with:
+ contract: ${{ inputs.toolchain-contract }}
components: ${{ inputs.toolchain-components }}
- name: Install Cargo Tools
@@ -47,7 +52,7 @@ runs:
- name: Setup Rust Cache
if: inputs.enable-rust-cache == 'true'
- uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1
+ uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
with:
shared-key: "rscrypto-v1-${{ inputs.cache-key }}"
cache-directories: ${{ inputs.tools-mode == 'rail' && 'target/cargo-rail/cache' || '' }}
diff --git a/.github/workflows/_ci-suite.yaml b/.github/workflows/_ci-suite.yaml
index aecc3dd7..f4c7357c 100644
--- a/.github/workflows/_ci-suite.yaml
+++ b/.github/workflows/_ci-suite.yaml
@@ -67,6 +67,20 @@ jobs:
enable_rust_cache: true
operation: quality
+ msrv:
+ name: MSRV
+ uses: ./.github/workflows/_rust-job.yaml
+ with:
+ runner: runs-on=${{ github.run_id }}/runner=linux-x64-ci
+ timeout_minutes: 30
+ cache_key: ${{ inputs.cache_key_prefix }}-msrv
+ tools_mode: none
+ toolchain_contract: msrv
+ toolchain_components: ""
+ enable_magic_cache: true
+ enable_rust_cache: true
+ operation: msrv
+
cargo-graph:
if: ${{ inputs.include_cargo_graph }}
name: Cargo Graph Assurance
@@ -126,6 +140,7 @@ jobs:
timeout_minutes: 120
cache_key: ${{ inputs.cache_key_prefix }}-ibm-s390x
tools_mode: none
+ toolchain_contract: nightly
toolchain_components: "clippy, rustfmt"
enable_magic_cache: false
enable_rust_cache: true
@@ -140,6 +155,7 @@ jobs:
timeout_minutes: 120
cache_key: ${{ inputs.cache_key_prefix }}-ibm-power10
tools_mode: none
+ toolchain_contract: nightly
toolchain_components: "clippy, rustfmt"
enable_magic_cache: false
enable_rust_cache: true
@@ -154,6 +170,7 @@ jobs:
timeout_minutes: 30
cache_key: ${{ inputs.cache_key_prefix }}-platform-amx-test-nodebug
tools_mode: none
+ toolchain_contract: nightly
toolchain_components: "rustfmt"
enable_magic_cache: true
enable_rust_cache: true
@@ -167,6 +184,7 @@ jobs:
timeout_minutes: 90
cache_key: ${{ inputs.cache_key_prefix }}-cross-targets
tools_mode: none
+ toolchain_contract: nightly
toolchain_components: "clippy, rust-src"
enable_magic_cache: true
enable_rust_cache: true
diff --git a/.github/workflows/_rust-job.yaml b/.github/workflows/_rust-job.yaml
index 8508c2de..e0ef686c 100644
--- a/.github/workflows/_rust-job.yaml
+++ b/.github/workflows/_rust-job.yaml
@@ -25,6 +25,10 @@ on:
required: false
type: string
default: standard
+ toolchain_contract:
+ required: false
+ type: string
+ default: development
toolchain_components:
required: false
type: string
@@ -128,6 +132,7 @@ jobs:
- name: Setup
uses: ./.github/actions/setup
with:
+ toolchain-contract: ${{ inputs.toolchain_contract }}
cache-key: ${{ inputs.cache_key }}
tools-mode: ${{ inputs.tools_mode }}
toolchain-components: ${{ inputs.toolchain_components }}
diff --git a/.github/workflows/bench.yaml b/.github/workflows/bench.yaml
index 26417e10..a3ebd8fe 100644
--- a/.github/workflows/bench.yaml
+++ b/.github/workflows/bench.yaml
@@ -71,6 +71,7 @@ jobs:
timeout_minutes: ${{ matrix.timeout_minutes }}
cache_key: bench-${{ matrix.artifact_suffix }}
tools_mode: ${{ matrix.tools_mode }}
+ toolchain_contract: ${{ (matrix.platform == 'ibm-s390x' || matrix.platform == 'ibm-power10') && 'nightly' || 'development' }}
toolchain_components: ${{ matrix.toolchain_components }}
enable_magic_cache: false
enable_rust_cache: true
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index 6d00ab7f..6e890908 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -39,7 +39,7 @@ jobs:
id: rail
uses: loadingalias/cargo-rail-action@ebc8ef2740efefe2cd5c42826344a8f1213113ae # v6.1.2
with:
- version: "0.20.0"
+ version: "0.21.0"
checksum: required
since: ${{ github.event.pull_request.base.sha }}
diff --git a/.github/workflows/ct.yaml b/.github/workflows/ct.yaml
index 569c55ea..0c7fabbf 100644
--- a/.github/workflows/ct.yaml
+++ b/.github/workflows/ct.yaml
@@ -175,6 +175,7 @@ jobs:
timeout_minutes: ${{ matrix.timeout_minutes }}
cache_key: ct-${{ matrix.artifact_suffix }}
tools_mode: ${{ matrix.tools_mode }}
+ toolchain_contract: ${{ (matrix.platform == 'ibm-s390x' || matrix.platform == 'ibm-power10' || matrix.platform == 'rise-riscv') && 'nightly' || 'development' }}
toolchain_components: ${{ matrix.toolchain_components }}
enable_magic_cache: ${{ matrix.enable_magic_cache }}
enable_rust_cache: ${{ matrix.enable_rust_cache }}
diff --git a/.github/workflows/riscv.yaml b/.github/workflows/riscv.yaml
index f6ac5a5d..3f8ae671 100644
--- a/.github/workflows/riscv.yaml
+++ b/.github/workflows/riscv.yaml
@@ -52,6 +52,7 @@ jobs:
timeout_minutes: 240
cache_key: weekly-riscv64
tools_mode: none
+ toolchain_contract: nightly
toolchain_components: "clippy, rustfmt"
enable_magic_cache: false
enable_rust_cache: true
@@ -83,6 +84,7 @@ jobs:
timeout_minutes: 240
cache_key: bench-rise-riscv
tools_mode: ibm
+ toolchain_contract: nightly
toolchain_components: "clippy, rustfmt"
enable_magic_cache: false
enable_rust_cache: true
diff --git a/.github/workflows/rsa.yaml b/.github/workflows/rsa.yaml
index 4bf3fa1b..b3b0d714 100644
--- a/.github/workflows/rsa.yaml
+++ b/.github/workflows/rsa.yaml
@@ -38,6 +38,7 @@ jobs:
timeout_minutes: 60
cache_key: rsa-miri-linux-x64
tools_mode: minimal
+ toolchain_contract: nightly
toolchain_components: "miri, rust-src"
enable_magic_cache: true
enable_rust_cache: true
diff --git a/.github/workflows/weekly.yaml b/.github/workflows/weekly.yaml
index 29b95b1b..cb9de23e 100644
--- a/.github/workflows/weekly.yaml
+++ b/.github/workflows/weekly.yaml
@@ -90,6 +90,7 @@ jobs:
timeout_minutes: 60
cache_key: weekly-miri-${{ matrix.target.name }}
tools_mode: minimal
+ toolchain_contract: nightly
toolchain_components: "miri, rust-src"
enable_magic_cache: true
enable_rust_cache: true
@@ -104,6 +105,7 @@ jobs:
timeout_minutes: 60
cache_key: weekly-miri-tree-borrows
tools_mode: minimal
+ toolchain_contract: nightly
toolchain_components: "miri, rust-src"
enable_magic_cache: true
enable_rust_cache: true
@@ -128,6 +130,7 @@ jobs:
timeout_minutes: 120
cache_key: weekly-fuzz-${{ matrix.target.name }}
tools_mode: fuzz
+ toolchain_contract: nightly
enable_magic_cache: true
enable_rust_cache: true
operation: fuzz
@@ -155,6 +158,7 @@ jobs:
timeout_minutes: 120
cache_key: weekly-asan-fuzz-${{ matrix.target.name }}
tools_mode: fuzz
+ toolchain_contract: nightly
toolchain_components: "rust-src"
enable_magic_cache: true
enable_rust_cache: true
diff --git a/.gitignore b/.gitignore
index d9aa2a11..cac10004 100644
--- a/.gitignore
+++ b/.gitignore
@@ -16,7 +16,9 @@ Thumbs.db
AGENTS.md
# Zed/IDE
-.zed
+.zed/*
+!.zed/settings.json
+!.zed/tasks.json
# Keys/Creds
*.pem
diff --git a/.zed/settings.json b/.zed/settings.json
new file mode 100644
index 00000000..e68e209b
--- /dev/null
+++ b/.zed/settings.json
@@ -0,0 +1,13 @@
+{
+ "lsp": {
+ "rust-analyzer": {
+ "enable_lsp_tasks": false,
+ "initialization_options": {
+ "cargo": {
+ "extraArgs": ["--locked"],
+ "features": "all"
+ }
+ }
+ }
+ }
+}
diff --git a/.zed/tasks.json b/.zed/tasks.json
new file mode 100644
index 00000000..048cc3df
--- /dev/null
+++ b/.zed/tasks.json
@@ -0,0 +1,18 @@
+[
+ {
+ "label": "rscrypto: check",
+ "command": "just",
+ "args": ["check"],
+ "cwd": "$ZED_WORKTREE_ROOT",
+ "allow_concurrent_runs": false,
+ "save": "all"
+ },
+ {
+ "label": "rscrypto: test",
+ "command": "just",
+ "args": ["test"],
+ "cwd": "$ZED_WORKTREE_ROOT",
+ "allow_concurrent_runs": false,
+ "save": "all"
+ }
+]
diff --git a/Cargo.lock b/Cargo.lock
index 133c9ad3..46569232 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,16 +2,6 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "aead"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
-dependencies = [
- "crypto-common 0.1.7",
- "generic-array",
-]
-
[[package]]
name = "aead"
version = "0.6.1"
@@ -19,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99"
dependencies = [
"crypto-common 0.2.2",
- "inout 0.2.2",
+ "inout",
]
[[package]]
@@ -32,24 +22,13 @@ dependencies = [
"softaes",
]
-[[package]]
-name = "aes"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
-dependencies = [
- "cfg-if",
- "cipher 0.4.4",
- "cpufeatures 0.2.17",
-]
-
[[package]]
name = "aes"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58"
dependencies = [
- "cipher 0.5.2",
+ "cipher",
"cpubits",
"cpufeatures 0.3.0",
]
@@ -60,34 +39,33 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdf011db2e21ce0d575593d749db5554b47fed37aff429e4dc50bc91ac93a028"
dependencies = [
- "aead 0.6.1",
- "aes 0.9.2",
- "cipher 0.5.2",
- "ctr 0.10.1",
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
"ghash",
"subtle",
]
[[package]]
name = "aes-gcm-siv"
-version = "0.11.1"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
+checksum = "1f437e5b075722bda3f54039e95d60d1c142f140e6c45b718e4f9fca3e5a1514"
dependencies = [
- "aead 0.5.2",
- "aes 0.8.4",
- "cipher 0.4.4",
- "ctr 0.9.2",
- "polyval 0.6.2",
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "polyval",
"subtle",
- "zeroize",
]
[[package]]
name = "aho-corasick"
-version = "1.1.4"
+version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
+checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba"
dependencies = [
"memchr",
]
@@ -115,13 +93,13 @@ checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
[[package]]
name = "argon2"
-version = "0.6.0-rc.8"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5"
+checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
- "cpufeatures 0.3.0",
+ "cpufeatures 0.2.17",
"password-hash",
]
@@ -143,9 +121,9 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cba922132fed390c20be89a16fa713999c46b2ce84ac4b0ecee0f3586a39d88"
dependencies = [
- "aead 0.6.1",
+ "aead",
"ascon-core",
- "inout 0.2.2",
+ "inout",
"subtle",
]
@@ -173,32 +151,20 @@ checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
[[package]]
name = "aws-lc-rs"
-version = "1.17.3"
+version = "1.18.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1"
+checksum = "ce2b2dcc879c3bae0d371e77c99f2238400ef24ec001394befa67b6e543add9e"
dependencies = [
- "aws-lc-sys 0.43.0",
+ "aws-lc-sys",
"untrusted 0.7.1",
"zeroize",
]
[[package]]
name = "aws-lc-sys"
-version = "0.41.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4"
-dependencies = [
- "cc",
- "cmake",
- "dunce",
- "fs_extra",
-]
-
-[[package]]
-name = "aws-lc-sys"
-version = "0.43.0"
+version = "0.44.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c"
+checksum = "f09fae7be8bb3174e05c6afdb34199e6dc0c7c04ba9fa237b1967adfbde27483"
dependencies = [
"cc",
"cmake",
@@ -207,12 +173,6 @@ dependencies = [
"pkg-config",
]
-[[package]]
-name = "base16ct"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
-
[[package]]
name = "base16ct"
version = "1.0.0"
@@ -268,18 +228,18 @@ checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da"
[[package]]
name = "blake2"
-version = "0.11.0-rc.6"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
- "digest 0.11.3",
+ "digest 0.10.7",
]
[[package]]
name = "blake3"
-version = "1.8.5"
+version = "1.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce"
+checksum = "76ae7bad254120e9e4c63bafc385310756f90c484eac0e36b8317cf09cb92a77"
dependencies = [
"arrayref",
"arrayvec",
@@ -329,9 +289,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -361,7 +321,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
dependencies = [
"cfg-if",
- "cipher 0.5.2",
+ "cipher",
"cpufeatures 0.3.0",
"rand_core 0.10.1",
"zeroize",
@@ -373,9 +333,9 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b89e1c441e926b9c82a8d023f6e1b7ae0adcfaa7d621814e4d60789bac751cb"
dependencies = [
- "aead 0.6.1",
+ "aead",
"chacha20",
- "cipher 0.5.2",
+ "cipher",
"poly1305",
]
@@ -406,16 +366,6 @@ dependencies = [
"half",
]
-[[package]]
-name = "cipher"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
-dependencies = [
- "crypto-common 0.1.7",
- "inout 0.1.4",
-]
-
[[package]]
name = "cipher"
version = "0.5.2"
@@ -424,7 +374,7 @@ checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [
"block-buffer 0.12.1",
"crypto-common 0.2.2",
- "inout 0.2.2",
+ "inout",
"zeroize",
]
@@ -441,18 +391,18 @@ dependencies = [
[[package]]
name = "clap"
-version = "4.6.5"
+version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "301b56658598e48f3648647ac6fc887be7e7108eddfa4e9b63fcf3ec58c0cadf"
+checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca"
dependencies = [
"clap_builder",
]
[[package]]
name = "clap_builder"
-version = "4.6.5"
+version = "4.6.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94a65403d1a1bd28f7dc68eb8506e8874808ee5eecb59298de588e2e1407a078"
+checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889"
dependencies = [
"anstyle",
"clap_lex",
@@ -657,18 +607,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
-[[package]]
-name = "crypto-bigint"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
-dependencies = [
- "generic-array",
- "rand_core 0.6.4",
- "subtle",
- "zeroize",
-]
-
[[package]]
name = "crypto-bigint"
version = "0.7.5"
@@ -692,7 +630,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
- "rand_core 0.6.4",
"typenum",
]
@@ -714,26 +651,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6250a2d96a09edbe8e75ed29c87d05512ee2cbb24c7e8c684657f7930ffd3c6"
dependencies = [
"digest 0.11.3",
- "keccak 0.2.0",
+ "keccak 0.2.1",
"sponge-cursor",
]
-[[package]]
-name = "ctr"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
-dependencies = [
- "cipher 0.4.4",
-]
-
[[package]]
name = "ctr"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21"
dependencies = [
- "cipher 0.5.2",
+ "cipher",
]
[[package]]
@@ -755,8 +683,23 @@ dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"curve25519-dalek-derive",
- "digest 0.10.7",
- "fiat-crypto",
+ "fiat-crypto 0.2.9",
+ "rustc_version",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "curve25519-dalek"
+version = "5.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "curve25519-dalek-derive",
+ "digest 0.11.3",
+ "fiat-crypto 0.3.0",
"rustc_version",
"subtle",
"zeroize",
@@ -816,22 +759,23 @@ dependencies = [
"const-oid 0.10.2",
"crypto-common 0.2.2",
"ctutils",
+ "zeroize",
]
[[package]]
name = "dryoc"
-version = "0.8.0"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d09fac1c1cf30622ccd33105965abb8ef17dd1c3aae34a489350dd0b8c3fae4"
+checksum = "2a9268c01fc6ec1f2a104ae9e40343c255fbcafea97a48f334237206c96838b2"
dependencies = [
- "bitflags",
"chacha20",
- "curve25519-dalek",
- "lazy_static",
+ "curve25519-dalek 4.1.3",
+ "getrandom 0.4.3",
"libc",
"rand 0.10.2",
"salsa20",
"sha2 0.11.0",
+ "sha3 0.12.0",
"subtle",
"winapi",
"zeroize",
@@ -843,20 +787,6 @@ version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
-[[package]]
-name = "ecdsa"
-version = "0.16.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
-dependencies = [
- "der 0.7.10",
- "digest 0.10.7",
- "elliptic-curve 0.13.8",
- "rfc6979 0.4.0",
- "signature 2.2.0",
- "spki 0.7.3",
-]
-
[[package]]
name = "ecdsa"
version = "0.17.0"
@@ -865,8 +795,8 @@ checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0"
dependencies = [
"der 0.8.1",
"digest 0.11.3",
- "elliptic-curve 0.14.1",
- "rfc6979 0.6.0",
+ "elliptic-curve",
+ "rfc6979",
"signature 3.0.0",
"spki 0.8.0",
"zeroize",
@@ -874,24 +804,22 @@ dependencies = [
[[package]]
name = "ed25519"
-version = "2.2.3"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a"
dependencies = [
- "pkcs8 0.10.2",
- "signature 2.2.0",
+ "signature 3.0.0",
]
[[package]]
name = "ed25519-dalek"
-version = "2.2.0"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
+checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de"
dependencies = [
- "curve25519-dalek",
+ "curve25519-dalek 5.0.0",
"ed25519",
- "serde",
- "sha2 0.10.9",
+ "sha2 0.11.0",
"subtle",
"zeroize",
]
@@ -902,41 +830,22 @@ version = "1.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d"
-[[package]]
-name = "elliptic-curve"
-version = "0.13.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
-dependencies = [
- "base16ct 0.2.0",
- "crypto-bigint 0.5.5",
- "digest 0.10.7",
- "ff 0.13.1",
- "generic-array",
- "group 0.13.0",
- "pkcs8 0.10.2",
- "rand_core 0.6.4",
- "sec1 0.7.3",
- "subtle",
- "zeroize",
-]
-
[[package]]
name = "elliptic-curve"
version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65"
dependencies = [
- "base16ct 1.0.0",
- "crypto-bigint 0.7.5",
+ "base16ct",
+ "crypto-bigint",
"crypto-common 0.2.2",
"digest 0.11.3",
- "ff 0.14.0",
- "group 0.14.0",
+ "ff",
+ "group",
"hybrid-array",
"pkcs8 0.11.0",
"rand_core 0.10.1",
- "sec1 0.8.1",
+ "sec1",
"subtle",
"zeroize",
]
@@ -957,16 +866,6 @@ version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223"
-[[package]]
-name = "ff"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
-dependencies = [
- "rand_core 0.6.4",
- "subtle",
-]
-
[[package]]
name = "ff"
version = "0.14.0"
@@ -983,11 +882,17 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+[[package]]
+name = "fiat-crypto"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24"
+
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "fips203"
@@ -1015,21 +920,21 @@ checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c"
[[package]]
name = "futures-core"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7"
+checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e"
[[package]]
name = "futures-task"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109"
+checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd"
[[package]]
name = "futures-util"
-version = "0.3.33"
+version = "0.3.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa"
+checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc"
dependencies = [
"futures-core",
"futures-task",
@@ -1045,7 +950,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
- "zeroize",
]
[[package]]
@@ -1078,9 +982,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi 6.0.0",
"rand_core 0.10.1",
+ "wasm-bindgen",
]
[[package]]
@@ -1089,7 +995,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5"
dependencies = [
- "polyval 0.7.3",
+ "polyval",
]
[[package]]
@@ -1098,24 +1004,13 @@ version = "0.3.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b"
-[[package]]
-name = "group"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
-dependencies = [
- "ff 0.13.1",
- "rand_core 0.6.4",
- "subtle",
-]
-
[[package]]
name = "group"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4"
dependencies = [
- "ff 0.14.0",
+ "ff",
"rand_core 0.10.1",
"subtle",
]
@@ -1174,16 +1069,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018"
dependencies = [
- "hmac 0.13.0",
-]
-
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest 0.10.7",
+ "hmac",
]
[[package]]
@@ -1207,15 +1093,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "inout"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
-dependencies = [
- "generic-array",
-]
-
[[package]]
name = "inout"
version = "0.2.2"
@@ -1252,9 +1129,9 @@ dependencies = [
[[package]]
name = "js-sys"
-version = "0.3.103"
+version = "0.3.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
+checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a"
dependencies = [
"cfg-if",
"futures-util",
@@ -1272,9 +1149,9 @@ dependencies = [
[[package]]
name = "keccak"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"
+checksum = "ffd9697dc4a9a62e2da93389f34400b77a28f0287711263cabb203b3ccb9c0e4"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
@@ -1472,9 +1349,9 @@ dependencies = [
[[package]]
name = "num-integer"
-version = "0.1.46"
+version = "0.1.47"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b"
dependencies = [
"num-traits",
]
@@ -1511,35 +1388,31 @@ version = "11.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6790f58c7ff633d8771f42965289203411a5e5c68388703c06e14f24770b41e"
-[[package]]
-name = "opaque-debug"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
-
[[package]]
name = "p256"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a"
dependencies = [
- "ecdsa 0.17.0",
- "elliptic-curve 0.14.1",
+ "ecdsa",
+ "elliptic-curve",
"primefield",
- "primeorder 0.14.0",
+ "primeorder",
"sha2 0.11.0",
]
[[package]]
name = "p384"
-version = "0.13.1"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
+checksum = "d17b851e6b3e378ab4ecb07fa2ed23f4d15f075735f8fec9fa1e7bdce5f8301f"
dependencies = [
- "ecdsa 0.16.9",
- "elliptic-curve 0.13.8",
- "primeorder 0.13.6",
- "sha2 0.10.9",
+ "ecdsa",
+ "elliptic-curve",
+ "fiat-crypto 0.3.0",
+ "primefield",
+ "primeorder",
+ "sha2 0.11.0",
]
[[package]]
@@ -1554,11 +1427,13 @@ dependencies = [
[[package]]
name = "password-hash"
-version = "0.6.1"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
- "phc",
+ "base64ct",
+ "rand_core 0.6.4",
+ "subtle",
]
[[package]]
@@ -1574,17 +1449,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629"
dependencies = [
"digest 0.11.3",
- "hmac 0.13.0",
-]
-
-[[package]]
-name = "phc"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892"
-dependencies = [
- "base64ct",
- "ctutils",
+ "hmac",
]
[[package]]
@@ -1665,19 +1530,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c"
dependencies = [
"cpufeatures 0.3.0",
- "universal-hash 0.6.1",
-]
-
-[[package]]
-name = "polyval"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.2.17",
- "opaque-debug",
- "universal-hash 0.5.1",
+ "universal-hash",
]
[[package]]
@@ -1688,7 +1541,7 @@ checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd"
dependencies = [
"cpubits",
"cpufeatures 0.3.0",
- "universal-hash 0.6.1",
+ "universal-hash",
]
[[package]]
@@ -1716,30 +1569,21 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4"
dependencies = [
- "crypto-bigint 0.7.5",
+ "crypto-bigint",
"crypto-common 0.2.2",
- "ff 0.14.0",
+ "ff",
"rand_core 0.10.1",
"subtle",
"zeroize",
]
-[[package]]
-name = "primeorder"
-version = "0.13.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
-dependencies = [
- "elliptic-curve 0.13.8",
-]
-
[[package]]
name = "primeorder"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06"
dependencies = [
- "elliptic-curve 0.14.1",
+ "elliptic-curve",
"once_cell",
"primefield",
"serdect",
@@ -1950,9 +1794,9 @@ dependencies = [
[[package]]
name = "regex-automata"
-version = "0.4.16"
+version = "0.4.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad"
+checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2"
dependencies = [
"aho-corasick",
"memchr",
@@ -1965,24 +1809,14 @@ version = "0.8.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4"
-[[package]]
-name = "rfc6979"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
-dependencies = [
- "hmac 0.12.1",
- "subtle",
-]
-
[[package]]
name = "rfc6979"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b"
dependencies = [
- "crypto-bigint 0.7.5",
- "hmac 0.13.0",
+ "crypto-bigint",
+ "hmac",
]
[[package]]
@@ -2031,7 +1865,7 @@ dependencies = [
"ascon-aead",
"ascon-hash",
"aws-lc-rs",
- "aws-lc-sys 0.41.0",
+ "aws-lc-sys",
"blake2",
"blake3",
"chacha20poly1305",
@@ -2048,8 +1882,8 @@ dependencies = [
"fips203",
"getrandom 0.4.3",
"hkdf",
- "hmac 0.13.0",
- "keccak 0.2.0",
+ "hmac",
+ "keccak 0.2.1",
"libcrux-ml-kem",
"ml-kem",
"p256",
@@ -2124,7 +1958,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f874456e72520ff1375a06c588eaf074b0f01f9e9e1aada45bd9b7954a6e42c"
dependencies = [
"cfg-if",
- "cipher 0.5.2",
+ "cipher",
]
[[package]]
@@ -2148,27 +1982,13 @@ dependencies = [
"sha2 0.11.0",
]
-[[package]]
-name = "sec1"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
-dependencies = [
- "base16ct 0.2.0",
- "der 0.7.10",
- "generic-array",
- "pkcs8 0.10.2",
- "subtle",
- "zeroize",
-]
-
[[package]]
name = "sec1"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d"
dependencies = [
- "base16ct 1.0.0",
+ "base16ct",
"ctutils",
"der 0.8.1",
"hybrid-array",
@@ -2231,7 +2051,7 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e"
dependencies = [
- "base16ct 1.0.0",
+ "base16ct",
"serde",
]
@@ -2274,7 +2094,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "be176f1a57ce4e3d31c1a166222d9768de5954f811601fb7ca06fc8203905ce1"
dependencies = [
"digest 0.11.3",
- "keccak 0.2.0",
+ "keccak 0.2.1",
]
[[package]]
@@ -2284,7 +2104,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc9bad02c26382724b2d2692c6f179285e4b54eeecd7968f52a50059c3c11759"
dependencies = [
"digest 0.11.3",
- "keccak 0.2.0",
+ "keccak 0.2.1",
"sponge-cursor",
]
@@ -2375,6 +2195,9 @@ name = "sponge-cursor"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3a0219bd7d979d58245a4f41f695e1ac9f8befdffadd7f61f1bae9e39abc6620"
+dependencies = [
+ "zeroize",
+]
[[package]]
name = "subtle"
@@ -2454,16 +2277,6 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
-[[package]]
-name = "universal-hash"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
-dependencies = [
- "crypto-common 0.1.7",
- "subtle",
-]
-
[[package]]
name = "universal-hash"
version = "0.6.1"
@@ -2539,9 +2352,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen"
-version = "0.2.126"
+version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
+checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70"
dependencies = [
"cfg-if",
"once_cell",
@@ -2552,9 +2365,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro"
-version = "0.2.126"
+version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
+checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1"
dependencies = [
"quote",
"wasm-bindgen-macro-support",
@@ -2562,9 +2375,9 @@ dependencies = [
[[package]]
name = "wasm-bindgen-macro-support"
-version = "0.2.126"
+version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
+checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284"
dependencies = [
"bumpalo",
"proc-macro2",
@@ -2575,18 +2388,18 @@ dependencies = [
[[package]]
name = "wasm-bindgen-shared"
-version = "0.2.126"
+version = "0.2.127"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
+checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf"
dependencies = [
"unicode-ident",
]
[[package]]
name = "web-sys"
-version = "0.3.103"
+version = "0.3.104"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141"
+checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30"
dependencies = [
"js-sys",
"wasm-bindgen",
@@ -2723,20 +2536,19 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1"
dependencies = [
- "ff 0.14.0",
- "group 0.14.0",
+ "ff",
+ "group",
"hybrid-array",
]
[[package]]
name = "x25519-dalek"
-version = "2.0.1"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
+checksum = "e7e8131a03190127fb2263afc72b322ecadae46b6ff8c6f399ff5d02f5559af6"
dependencies = [
- "curve25519-dalek",
- "rand_core 0.6.4",
- "serde",
+ "curve25519-dalek 5.0.0",
+ "rand_core 0.10.1",
"zeroize",
]
@@ -2748,18 +2560,18 @@ checksum = "aee1b19627c7c60102ab80d3a9cbe18de90bfe03bfa6c3715447681f0e8c8af6"
[[package]]
name = "zerocopy"
-version = "0.8.55"
+version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb"
+checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
-version = "0.8.55"
+version = "0.8.56"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb"
+checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1"
dependencies = [
"proc-macro2",
"quote",
diff --git a/Cargo.toml b/Cargo.toml
index 2d718ff7..108993d4 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -47,36 +47,101 @@ rustdoc-args = ["--cfg", "docsrs"]
resolver = "3"
members = ["."]
-[workspace.lints.rust]
-unsafe_code = "allow"
+[lints.rust]
+warnings = { level = "deny", priority = -1 }
+ambiguous_negative_literals = "deny"
unsafe_op_in_unsafe_fn = "deny"
-dangling_pointers_from_locals = "deny"
-integer_to_ptr_transmutes = "deny"
-unexpected_cfgs = { level = "warn", check-cfg = [
+ffi_unwind_calls = "deny"
+let_underscore_drop = "deny"
+unreachable_pub = "deny"
+unexpected_cfgs = { level = "deny", check-cfg = [
'cfg(miri)',
'cfg(fuzzing)',
'cfg(rscrypto_internal_fuzzing)',
'cfg(target_feature, values("movdiri", "movdir64b", "serialize"))',
] }
-[workspace.lints.clippy]
-correctness = { level = "deny", priority = -1 }
-suspicious = { level = "deny", priority = -1 }
-perf = { level = "deny", priority = -1 }
+[lints.clippy]
+all = { level = "deny", priority = -1 }
+
+non_send_fields_in_send_ty = "deny"
+significant_drop_in_scrutinee = "deny"
+
+panic = "deny"
+panic_in_result_fn = "deny"
+string_slice = "deny"
+todo = "deny"
+unchecked_time_subtraction = "deny"
+unimplemented = "deny"
+unreachable = "deny"
+unwrap_used = "deny"
+
+assertions_on_result_states = "deny"
+let_underscore_must_use = "deny"
+unused_result_ok = "deny"
+
+cast_possible_truncation = "deny"
+cast_possible_wrap = "deny"
+cast_sign_loss = "deny"
+checked_conversions = "deny"
+fn_to_numeric_cast_any = "deny"
+invalid_upcast_comparisons = "deny"
+lossy_float_literal = "deny"
+
+allow_attributes = "deny"
+allow_attributes_without_reason = "deny"
+dbg_macro = "deny"
+enum_glob_use = "deny"
+expl_impl_clone_on_copy = "deny"
+iter_not_returning_iterator = "deny"
+same_functions_in_if_condition = "deny"
+
+negative_feature_names = "deny"
+wildcard_dependencies = "deny"
+
+coerce_container_to_any = "deny"
+collection_is_never_read = "deny"
+debug_assert_with_mut_call = "deny"
+default_union_representation = "deny"
+fallible_impl_from = "deny"
+ignore_without_reason = "deny"
+literal_string_with_formatting_args = "deny"
+mixed_read_write_in_expression = "deny"
+needless_collect = "deny"
+or_fun_call = "deny"
+path_buf_push_overwrite = "deny"
+read_zero_byte_vec = "deny"
+redundant_clone = "deny"
+should_panic_without_expect = "deny"
+suspicious_operation_groupings = "deny"
+trait_duplication_in_bounds = "deny"
+transmute_undefined_repr = "deny"
+uninhabited_references = "deny"
+unsafe_derive_deserialize = "deny"
+volatile_composites = "deny"
+
+as_ptr_cast_mut = "deny"
+as_pointer_underscore = "deny"
+borrow_as_ptr = "deny"
+cast_ptr_alignment = "deny"
+mem_forget = "deny"
+ptr_as_ptr = "deny"
+ptr_cast_constness = "deny"
+ref_as_ptr = "deny"
+undocumented_unsafe_blocks = "deny"
+unnecessary_safety_comment = "deny"
+unnecessary_safety_doc = "deny"
-std_instead_of_core = "warn"
-std_instead_of_alloc = "warn"
-unwrap_used = "allow"
-expect_used = "allow"
-indexing_slicing = "allow"
+alloc_instead_of_core = "deny"
+std_instead_of_alloc = "deny"
+std_instead_of_core = "deny"
-undocumented_unsafe_blocks = "deny"
-missing_safety_doc = "deny"
-chunks_exact_to_as_chunks = "allow"
+arithmetic_side_effects = "deny"
+cargo_common_metadata = "deny"
+cfg_not_test = "deny"
-[workspace.lints.rustdoc]
-broken_intra_doc_links = "deny"
-private_intra_doc_links = "warn"
+[lints.rustdoc]
+all = "deny"
[features]
default = ["std"]
@@ -164,36 +229,36 @@ crc32c = "0.6.8"
crc32fast = "1.5.0"
crc64fast = "1.1.0"
ascon-hash = "0.4.0"
-argon2 = { version = "0.6.0-rc.8", default-features = false, features = ["alloc"] }
-blake3 = { version = "1.8.5", default-features = false, features = ["std", "rayon"] }
-blake2 = "0.11.0-rc.6"
+argon2 = { version = "0.5.3", default-features = false, features = ["alloc"] }
+blake3 = { version = "1.8.6", default-features = false, features = ["std", "rayon"] }
+blake2 = "0.10.6"
pbkdf2 = "0.13.0"
scrypt = { version = "0.12.0", default-features = false }
sha2 = "0.11.0"
sha2_010 = { package = "sha2", version = "0.10.9" }
sha3 = "0.12.0"
-keccak = "0.2.0"
+keccak = "0.2.1"
xxhash-rust = { version = "0.8.18", features = ["xxh3"] }
rapidhash = "=4.5.1"
tiny-keccak = { version = "2.0.2", features = ["kmac", "shake"] }
digest = { version = "0.11.3", features = ["dev"] }
p256 = { version = "0.14.0", default-features = false, features = ["ecdsa", "pkcs8", "std"] }
-p384 = { version = "0.13.1", default-features = false, features = ["ecdsa", "pkcs8", "std"] }
-ed25519-dalek = "2.2.0"
-x25519-dalek = { version = "2.0.1", features = ["static_secrets"] }
+p384 = { version = "0.14.0", default-features = false, features = ["ecdsa", "pkcs8", "std"] }
+ed25519-dalek = "3.0.0"
+x25519-dalek = { version = "3.0.0", features = ["static_secrets"] }
fips203 = { version = "0.4.3", default-features = false, features = ["ml-kem-512", "ml-kem-768", "ml-kem-1024"] }
rustcrypto-ml-kem = { package = "ml-kem", version = "0.3.2", default-features = false, features = ["hazmat"] }
hmac = "0.13.0"
hkdf = "0.13.0"
chacha20poly1305 = "0.11.0"
aes-gcm = "0.11.0"
-aes-gcm-siv = "0.11.1"
+aes-gcm-siv = "0.12.0"
aegis = "0.9.15"
ascon-aead = { version = "0.6", default-features = false, features = ["alloc"] }
serde_json = "1"
ring = "0.17.14"
rsa = { version = "0.9.10", default-features = false, features = ["std", "u64_digit", "sha2"] }
-dryoc = "0.8.0"
+dryoc = "1.0.0"
[target.'cfg(any(target_arch = "x86", target_arch = "x86_64"))'.dev-dependencies]
libcrux-ml-kem = { version = "0.0.10", default-features = false, features = [
@@ -219,8 +284,8 @@ libcrux-ml-kem = { version = "0.0.10", default-features = false, features = [
] }
[target.'cfg(all(any(unix, windows), not(target_arch = "wasm32"), not(any(target_arch = "s390x", target_arch = "powerpc64"))))'.dev-dependencies]
-aws-lc-rs = "1.17.3"
-aws-lc-sys = "0.41.0"
+aws-lc-rs = "1.18.0"
+aws-lc-sys = "0.44.0"
[lib]
bench = false
@@ -334,9 +399,6 @@ name = "kmac_cshake"
harness = false
required-features = ["sha3", "kmac"]
-[lints]
-workspace = true
-
[profile.dev]
opt-level = 0
debug = true
diff --git a/README.md b/README.md
index d7271097..26b1cb72 100644
--- a/README.md
+++ b/README.md
@@ -39,14 +39,14 @@ Minimal `no_std` SHA-2 build:
```toml
[dependencies]
-rscrypto = { version = "0.8.0", default-features = false, features = ["sha2"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["sha2"] }
```
Full primitive stack with OS randomness enabled:
```toml
[dependencies]
-rscrypto = { version = "0.8.0", features = ["full", "getrandom"] }
+rscrypto = { version = "0.8.1", features = ["full", "getrandom"] }
```
Use `default-features = false` for `no_std` builds. Enable `getrandom` only for
@@ -156,24 +156,28 @@ code from a binary.
## Performance
-The published 2026-07-04 benchmark snapshot is historical. Its aggregate
-geomeans are not equivalent-work performance claims because the historical
-RustCrypto HMAC-SHA-256 rows included key setup while the compared rscrypto,
-`ring`, and AWS-LC rows reused keyed state. The benchmark source now aligns
-that setup, but a new aggregate requires a complete regenerated artifact.
+The 2026-08-18 snapshot covers eight Linux CI runners at commit `7eb44e9`. The
+RustCrypto HMAC-SHA-256 key setup is now hoisted out of the timed loop, matching
+the reusable keyed state given to rscrypto, `ring`, and AWS-LC, so these
+aggregates are equivalent-work claims. Ratios are `external / rscrypto`; higher
+is better.
-
-Historical 2026-07-04 scorecard (not an equivalent-work aggregate)
-
-
+
+3,780 of 6,144 fastest-external comparisons are wins and 5,475 are wins or ties,
+for a 1.62x Linux geomean. Known losses: ECDSA P-256/P-384 regressed sharply on
+IBM z16/s390x in this run and drags every ECDSA aggregate below parity (0.87x
+across 128 rows; 1.19x-1.53x excluding that one runner), and
+`rapidhash-stream/one-write` trails the `rapidhash` crate at 0.87x, mostly on
+x86_64.
Use individual shape-compatible rows for investigation and benchmark the
-deployment workload on its target hardware. The correction, raw results,
-methodology, and known losses are in
+deployment workload on its target hardware. Raw results, methodology, and the
+full loss list are in
[`benchmark_results/OVERVIEW.md`](benchmark_results/OVERVIEW.md) and
[`docs/benchmarking.md`](docs/benchmarking.md).
@@ -195,9 +199,10 @@ methodology, and known losses are in
The minimum supported Rust version is **1.91.0**.
-The pinned development nightly in
-[`rust-toolchain.toml`](rust-toolchain.toml) is separate from the MSRV and is
-used for Miri, fuzzing, and architecture-specific checks.
+The pinned stable development toolchain in
+[`rust-toolchain.toml`](rust-toolchain.toml) is separate from the MSRV.
+Nightly-only Miri, fuzzing, and architecture checks use the dated exception in
+[`toolchains.toml`](.config/toolchains.toml).
## License
diff --git a/assets/readme/perf.svg b/assets/readme/perf.svg
index 1030bba0..40a68c56 100644
--- a/assets/readme/perf.svg
+++ b/assets/readme/perf.svg
@@ -2,4 +2,4 @@
text.sans { font-family: ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; }
text.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, 'Liberation Mono', monospace; }
text.hero { letter-spacing: -2px; paint-order: stroke fill; stroke: #0090FF; stroke-width: 0.7px; }
-rscryptoGeomean speedups vs fastest matched competitors. Higher is better.Linux1.59x4,052 wins | 6,101 wins/ties | 6,750 casesApple Silicon1.37x382 wins | 708 wins/ties | 774 casesChecksums5.18x- Competitor Crates/Libs: crc-fast, crc, crc32fast, crc32c, crc64fastPrimitive GeomeansAEAD1.56xRSA1.55xECDSA1.45xHashes/MAC/XOF1.35xAuth/KDF1.25xPassword Hashing1.07xML-KEM1.49x- Linux Runners: AMD Zen 4/5; Intel Sapphire Rapids/Ice Lake; AWS Graviton 3/4; IBM POWER 10 and IBM Z16 (s390x); Rise RISC-V- macOS: MBP M1 10-Core, 16GB RAM - Local Dev Box- Fastest External: aws-lc-rs, ring, RustCrypto, BLAKE3, libcrux, crc-fast, etc.
\ No newline at end of file
+rscryptoGeomean speedups vs fastest matched competitors. Higher is better.Linux1.62x3,780 wins | 5,475 wins/ties | 6,144 casesApple Silicon1.37x382 wins | 708 wins/ties | 774 casesChecksums6.18x- Competitor Crates/Libs: crc-fast, crc, crc32fast, crc32c, crc64fastPrimitive GeomeansAEAD1.61xRSA1.65xECDSA0.87xHashes/MAC/XOF1.35xAuth/KDF1.28xPassword Hashing1.07xML-KEM1.55x1.00x- Linux Runners: AMD Zen 4/5; Intel Sapphire Rapids/Ice Lake; AWS Graviton 3/4; IBM POWER 10 and IBM Z16 (s390x)- macOS: MBP M1 10-Core, 16GB RAM - Local Dev Box- Fastest External: aws-lc-rs, ring, RustCrypto, BLAKE3, libcrux, crc-fast, etc.
\ No newline at end of file
diff --git a/benches/aead.rs b/benches/aead.rs
index 8b40ff6c..743c9dc0 100644
--- a/benches/aead.rs
+++ b/benches/aead.rs
@@ -9,7 +9,6 @@ mod common;
use core::hint::black_box;
use aes_gcm::aead::{AeadInOut as _, KeyInit as _};
-use aes_gcm_siv::aead::{AeadInPlace as _, KeyInit as _};
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
use rscrypto::aead::expert::AeadWithNonce;
@@ -78,7 +77,7 @@ fn xchacha20_poly1305_encrypt(c: &mut Criterion) {
black_box(AAD),
black_box(buf.as_mut_slice().into()),
)
- .unwrap(),
+ .expect("valid AEAD benchmark operation must succeed"),
)
})
});
@@ -100,13 +99,15 @@ fn xchacha20_poly1305_decrypt(c: &mut Criterion) {
// Pre-encrypt with rscrypto to get valid ciphertext + tag.
let mut ciphertext = data.clone();
- let tag_rs = cipher_rs.encrypt_in_place(&nonce_rs, AAD, &mut ciphertext).unwrap();
+ let tag_rs = cipher_rs
+ .encrypt_in_place(&nonce_rs, AAD, &mut ciphertext)
+ .expect("valid AEAD benchmark operation must succeed");
// Pre-encrypt with RustCrypto to get its tag format.
let mut ct_rc = data.clone();
let tag_rc = cipher_rc
.encrypt_inout_detached(&nonce_rc, AAD, ct_rc.as_mut_slice().into())
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
let mut buf = ciphertext.clone();
@@ -120,7 +121,7 @@ fn xchacha20_poly1305_decrypt(c: &mut Criterion) {
black_box(&mut buf),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf);
})
});
@@ -137,7 +138,7 @@ fn xchacha20_poly1305_decrypt(c: &mut Criterion) {
black_box(buf_rc.as_mut_slice().into()),
black_box(&tag_rc),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_rc);
})
});
@@ -161,10 +162,12 @@ fn chacha20_poly1305_encrypt(c: &mut Criterion) {
let nonce_rc = chacha20poly1305::Nonce::from(NONCE_12);
aws_lc_bench! {
let aws_key =
- aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::CHACHA20_POLY1305, &KEY_32).unwrap());
+ aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::CHACHA20_POLY1305, &KEY_32).expect("valid AEAD benchmark operation must succeed"));
}
- let ring_key =
- ring_aead::LessSafeKey::new(ring_aead::UnboundKey::new(&ring_aead::CHACHA20_POLY1305, &KEY_32).unwrap());
+ let ring_key = ring_aead::LessSafeKey::new(
+ ring_aead::UnboundKey::new(&ring_aead::CHACHA20_POLY1305, &KEY_32)
+ .expect("valid AEAD benchmark operation must succeed"),
+ );
let mut g = c.benchmark_group("chacha20-poly1305/encrypt");
for (len, data) in &inputs {
@@ -174,7 +177,7 @@ fn chacha20_poly1305_encrypt(c: &mut Criterion) {
let mut buf_owned = data.clone();
#[cfg(all(feature = "diag", target_arch = "x86_64", target_os = "linux"))]
let mut buf_x86_asm = data.clone();
- let mut buf_combined: Vec = Vec::with_capacity(data.len() + 16);
+ let mut buf_combined: Vec = Vec::with_capacity(data.len().strict_add(16));
g.bench_with_input(BenchmarkId::new("rscrypto", len), data, |b, d| {
b.iter(|| {
@@ -224,7 +227,7 @@ fn chacha20_poly1305_encrypt(c: &mut Criterion) {
black_box(AAD),
black_box(buf.as_mut_slice().into()),
)
- .unwrap(),
+ .expect("valid AEAD benchmark operation must succeed"),
)
})
});
@@ -240,7 +243,7 @@ fn chacha20_poly1305_encrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_combined),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_combined);
})
});
@@ -256,7 +259,7 @@ fn chacha20_poly1305_encrypt(c: &mut Criterion) {
ring_aead::Aad::from(AAD),
black_box(&mut buf_combined),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_combined);
})
});
@@ -278,22 +281,26 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
let nonce_rc = chacha20poly1305::Nonce::from(NONCE_12);
aws_lc_bench! {
let aws_key =
- aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::CHACHA20_POLY1305, &KEY_32).unwrap());
+ aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::CHACHA20_POLY1305, &KEY_32).expect("valid AEAD benchmark operation must succeed"));
}
- let ring_key =
- ring_aead::LessSafeKey::new(ring_aead::UnboundKey::new(&ring_aead::CHACHA20_POLY1305, &KEY_32).unwrap());
+ let ring_key = ring_aead::LessSafeKey::new(
+ ring_aead::UnboundKey::new(&ring_aead::CHACHA20_POLY1305, &KEY_32)
+ .expect("valid AEAD benchmark operation must succeed"),
+ );
let mut g = c.benchmark_group("chacha20-poly1305/decrypt");
for (len, data) in &inputs {
common::set_throughput(&mut g, *len);
let mut ciphertext = data.clone();
- let tag_rs = cipher_rs.encrypt_in_place(&nonce_rs, AAD, &mut ciphertext).unwrap();
+ let tag_rs = cipher_rs
+ .encrypt_in_place(&nonce_rs, AAD, &mut ciphertext)
+ .expect("valid AEAD benchmark operation must succeed");
let mut ct_rc = data.clone();
let tag_rc = cipher_rc
.encrypt_inout_detached(&nonce_rc, AAD, ct_rc.as_mut_slice().into())
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
aws_lc_bench! {
let mut ct_aws: Vec = data.clone();
@@ -303,7 +310,7 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
&mut ct_aws,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
}
let mut ct_ring: Vec = data.clone();
@@ -313,7 +320,7 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
ring_aead::Aad::from(AAD),
&mut ct_ring,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
let mut buf = ciphertext.clone();
#[cfg(feature = "diag")]
@@ -331,7 +338,7 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
black_box(&mut buf),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf);
})
});
@@ -347,7 +354,7 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
black_box(&mut buf_owned),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_owned);
})
});
@@ -365,7 +372,7 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
black_box(&tag_rs),
)
.expect("x86 asm path must apply to benchmarked non-empty sizes")
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_x86_asm);
})
});
@@ -383,7 +390,7 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
black_box(buf_rc.as_mut_slice().into()),
black_box(&tag_rc),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_rc);
})
});
@@ -400,7 +407,7 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_aws);
})
});
@@ -417,7 +424,7 @@ fn chacha20_poly1305_decrypt(c: &mut Criterion) {
ring_aead::Aad::from(AAD),
black_box(&mut buf_ring),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_ring);
})
});
@@ -437,10 +444,10 @@ fn aes256_gcm_siv_encrypt(c: &mut Criterion) {
let nonce_rs = rscrypto::aead::Nonce96::from_bytes(NONCE_12);
let cipher_rs = rscrypto::Aes256GcmSiv::new(&rscrypto::Aes256GcmSivKey::from_bytes(KEY_32));
let cipher_rc = aes_gcm_siv::Aes256GcmSiv::new(&KEY_32.into());
- let nonce_rc = aes_gcm_siv::Nonce::from_slice(&NONCE_12);
+ let nonce_rc = aes_gcm_siv::Nonce::from(NONCE_12);
aws_lc_bench! {
let aws_key =
- aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_256_GCM_SIV, &KEY_32).unwrap());
+ aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_256_GCM_SIV, &KEY_32).expect("valid AEAD benchmark operation must succeed"));
}
let mut g = c.benchmark_group("aes-256-gcm-siv/encrypt");
@@ -460,8 +467,12 @@ fn aes256_gcm_siv_encrypt(c: &mut Criterion) {
buf.copy_from_slice(d);
black_box(
cipher_rc
- .encrypt_in_place_detached(black_box(nonce_rc), black_box(AAD), black_box(&mut buf))
- .unwrap(),
+ .encrypt_inout_detached(
+ black_box(&nonce_rc),
+ black_box(AAD),
+ black_box(buf.as_mut_slice()).into(),
+ )
+ .expect("valid AEAD benchmark operation must succeed"),
)
})
});
@@ -477,7 +488,7 @@ fn aes256_gcm_siv_encrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(tag.as_ref());
black_box(&buf_aws);
})
@@ -497,10 +508,10 @@ fn aes256_gcm_siv_decrypt(c: &mut Criterion) {
let nonce_rs = rscrypto::aead::Nonce96::from_bytes(NONCE_12);
let cipher_rs = rscrypto::Aes256GcmSiv::new(&rscrypto::Aes256GcmSivKey::from_bytes(KEY_32));
let cipher_rc = aes_gcm_siv::Aes256GcmSiv::new(&KEY_32.into());
- let nonce_rc = aes_gcm_siv::Nonce::from_slice(&NONCE_12);
+ let nonce_rc = aes_gcm_siv::Nonce::from(NONCE_12);
aws_lc_bench! {
let aws_key =
- aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_256_GCM_SIV, &KEY_32).unwrap());
+ aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_256_GCM_SIV, &KEY_32).expect("valid AEAD benchmark operation must succeed"));
}
let mut g = c.benchmark_group("aes-256-gcm-siv/decrypt");
@@ -508,10 +519,14 @@ fn aes256_gcm_siv_decrypt(c: &mut Criterion) {
common::set_throughput(&mut g, *len);
let mut ciphertext = data.clone();
- let tag_rs = cipher_rs.encrypt_in_place(&nonce_rs, AAD, &mut ciphertext).unwrap();
+ let tag_rs = cipher_rs
+ .encrypt_in_place(&nonce_rs, AAD, &mut ciphertext)
+ .expect("valid AEAD benchmark operation must succeed");
let mut ct_rc = data.clone();
- let tag_rc = cipher_rc.encrypt_in_place_detached(nonce_rc, AAD, &mut ct_rc).unwrap();
+ let tag_rc = cipher_rc
+ .encrypt_inout_detached(&nonce_rc, AAD, ct_rc.as_mut_slice().into())
+ .expect("valid AEAD benchmark operation must succeed");
aws_lc_bench! {
let mut ct_aws = data.clone();
@@ -521,7 +536,7 @@ fn aes256_gcm_siv_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
&mut ct_aws,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
// AWS-LC exposes detached tags for seal, but its in-place open API takes ct||tag.
ct_aws.extend_from_slice(tag_aws.as_ref());
}
@@ -538,7 +553,7 @@ fn aes256_gcm_siv_decrypt(c: &mut Criterion) {
black_box(&mut buf),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf);
})
});
@@ -549,13 +564,13 @@ fn aes256_gcm_siv_decrypt(c: &mut Criterion) {
b.iter(|| {
buf_rc.copy_from_slice(ct);
cipher_rc
- .decrypt_in_place_detached(
- black_box(nonce_rc),
+ .decrypt_inout_detached(
+ black_box(&nonce_rc),
black_box(AAD),
- black_box(&mut buf_rc),
+ black_box(buf_rc.as_mut_slice()).into(),
black_box(&tag_rc),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_rc);
})
});
@@ -572,7 +587,7 @@ fn aes256_gcm_siv_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_aws);
})
});
@@ -593,10 +608,10 @@ fn aes128_gcm_siv_encrypt(c: &mut Criterion) {
let nonce_rs = rscrypto::aead::Nonce96::from_bytes(NONCE_12);
let cipher_rs = rscrypto::Aes128GcmSiv::new(&rscrypto::Aes128GcmSivKey::from_bytes(KEY_16));
let cipher_rc = aes_gcm_siv::Aes128GcmSiv::new(&KEY_16.into());
- let nonce_rc = aes_gcm_siv::Nonce::from_slice(&NONCE_12);
+ let nonce_rc = aes_gcm_siv::Nonce::from(NONCE_12);
aws_lc_bench! {
let aws_key =
- aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_128_GCM_SIV, &KEY_16).unwrap());
+ aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_128_GCM_SIV, &KEY_16).expect("valid AEAD benchmark operation must succeed"));
}
let mut g = c.benchmark_group("aes-128-gcm-siv/encrypt");
@@ -616,8 +631,12 @@ fn aes128_gcm_siv_encrypt(c: &mut Criterion) {
buf.copy_from_slice(d);
black_box(
cipher_rc
- .encrypt_in_place_detached(black_box(nonce_rc), black_box(AAD), black_box(&mut buf))
- .unwrap(),
+ .encrypt_inout_detached(
+ black_box(&nonce_rc),
+ black_box(AAD),
+ black_box(buf.as_mut_slice()).into(),
+ )
+ .expect("valid AEAD benchmark operation must succeed"),
)
})
});
@@ -633,7 +652,7 @@ fn aes128_gcm_siv_encrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(tag.as_ref());
black_box(&buf_aws);
})
@@ -653,10 +672,10 @@ fn aes128_gcm_siv_decrypt(c: &mut Criterion) {
let nonce_rs = rscrypto::aead::Nonce96::from_bytes(NONCE_12);
let cipher_rs = rscrypto::Aes128GcmSiv::new(&rscrypto::Aes128GcmSivKey::from_bytes(KEY_16));
let cipher_rc = aes_gcm_siv::Aes128GcmSiv::new(&KEY_16.into());
- let nonce_rc = aes_gcm_siv::Nonce::from_slice(&NONCE_12);
+ let nonce_rc = aes_gcm_siv::Nonce::from(NONCE_12);
aws_lc_bench! {
let aws_key =
- aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_128_GCM_SIV, &KEY_16).unwrap());
+ aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_128_GCM_SIV, &KEY_16).expect("valid AEAD benchmark operation must succeed"));
}
let mut g = c.benchmark_group("aes-128-gcm-siv/decrypt");
@@ -664,10 +683,14 @@ fn aes128_gcm_siv_decrypt(c: &mut Criterion) {
common::set_throughput(&mut g, *len);
let mut ciphertext = data.clone();
- let tag_rs = cipher_rs.encrypt_in_place(&nonce_rs, AAD, &mut ciphertext).unwrap();
+ let tag_rs = cipher_rs
+ .encrypt_in_place(&nonce_rs, AAD, &mut ciphertext)
+ .expect("valid AEAD benchmark operation must succeed");
let mut ct_rc = data.clone();
- let tag_rc = cipher_rc.encrypt_in_place_detached(nonce_rc, AAD, &mut ct_rc).unwrap();
+ let tag_rc = cipher_rc
+ .encrypt_inout_detached(&nonce_rc, AAD, ct_rc.as_mut_slice().into())
+ .expect("valid AEAD benchmark operation must succeed");
aws_lc_bench! {
let mut ct_aws = data.clone();
@@ -677,7 +700,7 @@ fn aes128_gcm_siv_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
&mut ct_aws,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
// AWS-LC exposes detached tags for seal, but its in-place open API takes ct||tag.
ct_aws.extend_from_slice(tag_aws.as_ref());
}
@@ -694,7 +717,7 @@ fn aes128_gcm_siv_decrypt(c: &mut Criterion) {
black_box(&mut buf),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf);
})
});
@@ -705,13 +728,13 @@ fn aes128_gcm_siv_decrypt(c: &mut Criterion) {
b.iter(|| {
buf_rc.copy_from_slice(ct);
cipher_rc
- .decrypt_in_place_detached(
- black_box(nonce_rc),
+ .decrypt_inout_detached(
+ black_box(&nonce_rc),
black_box(AAD),
- black_box(&mut buf_rc),
+ black_box(buf_rc.as_mut_slice()).into(),
black_box(&tag_rc),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_rc);
})
});
@@ -728,7 +751,7 @@ fn aes128_gcm_siv_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_aws);
})
});
@@ -752,9 +775,11 @@ fn aes256_gcm_encrypt(c: &mut Criterion) {
let cipher_rc = aes_gcm::Aes256Gcm::new(&KEY_32.into());
let nonce_rc = aes_gcm::Nonce::from(NONCE_12);
aws_lc_bench! {
- let aws_key = aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_256_GCM, &KEY_32).unwrap());
+ let aws_key = aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_256_GCM, &KEY_32).expect("valid AEAD benchmark operation must succeed"));
}
- let ring_key = ring_aead::LessSafeKey::new(ring_aead::UnboundKey::new(&ring_aead::AES_256_GCM, &KEY_32).unwrap());
+ let ring_key = ring_aead::LessSafeKey::new(
+ ring_aead::UnboundKey::new(&ring_aead::AES_256_GCM, &KEY_32).expect("valid AEAD benchmark operation must succeed"),
+ );
let mut g = c.benchmark_group("aes-256-gcm/encrypt");
for (len, data) in &inputs {
@@ -778,7 +803,7 @@ fn aes256_gcm_encrypt(c: &mut Criterion) {
black_box(AAD),
black_box(buf.as_mut_slice().into()),
)
- .unwrap(),
+ .expect("valid AEAD benchmark operation must succeed"),
)
})
});
@@ -794,7 +819,7 @@ fn aes256_gcm_encrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(tag.as_ref());
black_box(&buf_aws);
})
@@ -811,7 +836,7 @@ fn aes256_gcm_encrypt(c: &mut Criterion) {
ring_aead::Aad::from(AAD),
black_box(&mut buf_ring),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(tag.as_ref());
black_box(&buf_ring);
})
@@ -833,21 +858,25 @@ fn aes256_gcm_decrypt(c: &mut Criterion) {
let cipher_rc = aes_gcm::Aes256Gcm::new(&KEY_32.into());
let nonce_rc = aes_gcm::Nonce::from(NONCE_12);
aws_lc_bench! {
- let aws_key = aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_256_GCM, &KEY_32).unwrap());
+ let aws_key = aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_256_GCM, &KEY_32).expect("valid AEAD benchmark operation must succeed"));
}
- let ring_key = ring_aead::LessSafeKey::new(ring_aead::UnboundKey::new(&ring_aead::AES_256_GCM, &KEY_32).unwrap());
+ let ring_key = ring_aead::LessSafeKey::new(
+ ring_aead::UnboundKey::new(&ring_aead::AES_256_GCM, &KEY_32).expect("valid AEAD benchmark operation must succeed"),
+ );
let mut g = c.benchmark_group("aes-256-gcm/decrypt");
for (len, data) in &inputs {
common::set_throughput(&mut g, *len);
let mut ciphertext = data.clone();
- let tag_rs = cipher_rs.encrypt_in_place(&nonce_rs, AAD, &mut ciphertext).unwrap();
+ let tag_rs = cipher_rs
+ .encrypt_in_place(&nonce_rs, AAD, &mut ciphertext)
+ .expect("valid AEAD benchmark operation must succeed");
let mut ct_rc = data.clone();
let tag_rc = cipher_rc
.encrypt_inout_detached(&nonce_rc, AAD, ct_rc.as_mut_slice().into())
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
aws_lc_bench! {
let mut ct_aws = data.clone();
@@ -857,7 +886,7 @@ fn aes256_gcm_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
&mut ct_aws,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
// AWS-LC exposes detached tags for seal, but its in-place open API takes ct||tag.
ct_aws.extend_from_slice(tag_aws.as_ref());
}
@@ -869,7 +898,7 @@ fn aes256_gcm_decrypt(c: &mut Criterion) {
ring_aead::Aad::from(AAD),
&mut ct_ring,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
let mut buf = ciphertext.clone();
@@ -883,7 +912,7 @@ fn aes256_gcm_decrypt(c: &mut Criterion) {
black_box(&mut buf),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf);
})
});
@@ -900,7 +929,7 @@ fn aes256_gcm_decrypt(c: &mut Criterion) {
black_box(buf_rc.as_mut_slice().into()),
black_box(&tag_rc),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_rc);
})
});
@@ -917,7 +946,7 @@ fn aes256_gcm_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_aws);
})
});
@@ -936,7 +965,7 @@ fn aes256_gcm_decrypt(c: &mut Criterion) {
black_box(&mut buf_ring),
0..,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_ring);
})
});
@@ -959,9 +988,11 @@ fn aes128_gcm_encrypt(c: &mut Criterion) {
let cipher_rc = aes_gcm::Aes128Gcm::new(&KEY_16.into());
let nonce_rc = aes_gcm::Nonce::from(NONCE_12);
aws_lc_bench! {
- let aws_key = aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_128_GCM, &KEY_16).unwrap());
+ let aws_key = aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_128_GCM, &KEY_16).expect("valid AEAD benchmark operation must succeed"));
}
- let ring_key = ring_aead::LessSafeKey::new(ring_aead::UnboundKey::new(&ring_aead::AES_128_GCM, &KEY_16).unwrap());
+ let ring_key = ring_aead::LessSafeKey::new(
+ ring_aead::UnboundKey::new(&ring_aead::AES_128_GCM, &KEY_16).expect("valid AEAD benchmark operation must succeed"),
+ );
let mut g = c.benchmark_group("aes-128-gcm/encrypt");
for (len, data) in &inputs {
@@ -985,7 +1016,7 @@ fn aes128_gcm_encrypt(c: &mut Criterion) {
black_box(AAD),
black_box(buf.as_mut_slice().into()),
)
- .unwrap(),
+ .expect("valid AEAD benchmark operation must succeed"),
)
})
});
@@ -1001,7 +1032,7 @@ fn aes128_gcm_encrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(tag.as_ref());
black_box(&buf_aws);
})
@@ -1018,7 +1049,7 @@ fn aes128_gcm_encrypt(c: &mut Criterion) {
ring_aead::Aad::from(AAD),
black_box(&mut buf_ring),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(tag.as_ref());
black_box(&buf_ring);
})
@@ -1040,21 +1071,25 @@ fn aes128_gcm_decrypt(c: &mut Criterion) {
let cipher_rc = aes_gcm::Aes128Gcm::new(&KEY_16.into());
let nonce_rc = aes_gcm::Nonce::from(NONCE_12);
aws_lc_bench! {
- let aws_key = aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_128_GCM, &KEY_16).unwrap());
+ let aws_key = aws_aead::LessSafeKey::new(aws_aead::UnboundKey::new(&aws_aead::AES_128_GCM, &KEY_16).expect("valid AEAD benchmark operation must succeed"));
}
- let ring_key = ring_aead::LessSafeKey::new(ring_aead::UnboundKey::new(&ring_aead::AES_128_GCM, &KEY_16).unwrap());
+ let ring_key = ring_aead::LessSafeKey::new(
+ ring_aead::UnboundKey::new(&ring_aead::AES_128_GCM, &KEY_16).expect("valid AEAD benchmark operation must succeed"),
+ );
let mut g = c.benchmark_group("aes-128-gcm/decrypt");
for (len, data) in &inputs {
common::set_throughput(&mut g, *len);
let mut ciphertext = data.clone();
- let tag_rs = cipher_rs.encrypt_in_place(&nonce_rs, AAD, &mut ciphertext).unwrap();
+ let tag_rs = cipher_rs
+ .encrypt_in_place(&nonce_rs, AAD, &mut ciphertext)
+ .expect("valid AEAD benchmark operation must succeed");
let mut ct_rc = data.clone();
let tag_rc = cipher_rc
.encrypt_inout_detached(&nonce_rc, AAD, ct_rc.as_mut_slice().into())
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
aws_lc_bench! {
let mut ct_aws = data.clone();
@@ -1064,7 +1099,7 @@ fn aes128_gcm_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
&mut ct_aws,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
// AWS-LC exposes detached tags for seal, but its in-place open API takes ct||tag.
ct_aws.extend_from_slice(tag_aws.as_ref());
}
@@ -1076,7 +1111,7 @@ fn aes128_gcm_decrypt(c: &mut Criterion) {
ring_aead::Aad::from(AAD),
&mut ct_ring,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
let mut buf = ciphertext.clone();
@@ -1090,7 +1125,7 @@ fn aes128_gcm_decrypt(c: &mut Criterion) {
black_box(&mut buf),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf);
})
});
@@ -1107,7 +1142,7 @@ fn aes128_gcm_decrypt(c: &mut Criterion) {
black_box(buf_rc.as_mut_slice().into()),
black_box(&tag_rc),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_rc);
})
});
@@ -1124,7 +1159,7 @@ fn aes128_gcm_decrypt(c: &mut Criterion) {
aws_aead::Aad::from(AAD),
black_box(&mut buf_aws),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_aws);
})
});
@@ -1143,7 +1178,7 @@ fn aes128_gcm_decrypt(c: &mut Criterion) {
black_box(&mut buf_ring),
0..,
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_ring);
})
});
@@ -1194,7 +1229,9 @@ fn aegis256_decrypt(c: &mut Criterion) {
// Pre-encrypt with rscrypto to get valid ciphertext + tag.
let mut ciphertext = data.clone();
- let tag_rs = cipher_rs.encrypt_in_place(&nonce_rs, AAD, &mut ciphertext).unwrap();
+ let tag_rs = cipher_rs
+ .encrypt_in_place(&nonce_rs, AAD, &mut ciphertext)
+ .expect("valid AEAD benchmark operation must succeed");
// Pre-encrypt with aegis crate to get its tag format.
let mut ct_ac = data.clone();
@@ -1213,7 +1250,7 @@ fn aegis256_decrypt(c: &mut Criterion) {
black_box(&mut buf),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf);
})
});
@@ -1225,7 +1262,7 @@ fn aegis256_decrypt(c: &mut Criterion) {
buf_ac.copy_from_slice(ct);
cipher_ac
.decrypt_in_place(black_box(&mut buf_ac), black_box(&tag_ac), black_box(AAD))
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_ac);
})
});
@@ -1267,7 +1304,7 @@ fn ascon_aead128_encrypt(c: &mut Criterion) {
black_box(AAD),
black_box(buf.as_mut_slice().into()),
)
- .unwrap(),
+ .expect("valid AEAD benchmark operation must succeed"),
)
})
});
@@ -1290,12 +1327,14 @@ fn ascon_aead128_decrypt(c: &mut Criterion) {
common::set_throughput(&mut g, *len);
let mut ciphertext = data.clone();
- let tag_rs = cipher_rs.encrypt_in_place(&nonce_rs, AAD, &mut ciphertext).unwrap();
+ let tag_rs = cipher_rs
+ .encrypt_in_place(&nonce_rs, AAD, &mut ciphertext)
+ .expect("valid AEAD benchmark operation must succeed");
let mut ct_ac = data.clone();
let tag_ac = cipher_ac
.encrypt_inout_detached(&nonce_ac, AAD, ct_ac.as_mut_slice().into())
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
let mut buf = ciphertext.clone();
@@ -1309,7 +1348,7 @@ fn ascon_aead128_decrypt(c: &mut Criterion) {
black_box(&mut buf),
black_box(&tag_rs),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf);
})
});
@@ -1326,7 +1365,7 @@ fn ascon_aead128_decrypt(c: &mut Criterion) {
black_box(buf_ac.as_mut_slice().into()),
black_box(&tag_ac),
)
- .unwrap();
+ .expect("valid AEAD benchmark operation must succeed");
black_box(&buf_ac);
})
});
diff --git a/benches/aead_kernels.rs b/benches/aead_kernels.rs
index 4ecc827f..17108f67 100644
--- a/benches/aead_kernels.rs
+++ b/benches/aead_kernels.rs
@@ -31,12 +31,16 @@ fn chacha20_xor_kernel(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("aarch64-neon", len), data, |b, d| {
b.iter(|| {
buf.copy_from_slice(d);
- rscrypto::aead::diag_chacha20_xor_keystream_aarch64_neon(
- black_box(&KEY_32),
- black_box(1),
- black_box(&NONCE_12),
- black_box(&mut buf),
- );
+ // SAFETY: AArch64 NEON is part of this target's compile-time baseline. `comp_sizes` tops out at 1,048,576
+ // bytes (16,384 blocks), which fits the `u32` counter range starting at 1.
+ unsafe {
+ rscrypto::aead::diag_chacha20_xor_keystream_aarch64_neon(
+ black_box(&KEY_32),
+ black_box(1),
+ black_box(&NONCE_12),
+ black_box(&mut buf),
+ );
+ }
black_box(buf.as_ptr())
})
});
@@ -59,7 +63,7 @@ fn poly1305_auth_kernel(c: &mut Criterion) {
b.iter(|| {
black_box(
rscrypto::aead::diag_chacha20poly1305_authenticate_aead(black_box(AAD), black_box(d), black_box(&POLY_KEY))
- .unwrap(),
+ .expect("selected AEAD benchmark kernel must be available"),
)
})
});
@@ -73,7 +77,7 @@ fn poly1305_auth_kernel(c: &mut Criterion) {
black_box(d),
black_box(&POLY_KEY),
)
- .unwrap(),
+ .expect("selected AEAD benchmark kernel must be available"),
)
})
});
diff --git a/benches/ascon.rs b/benches/ascon.rs
index 12de7ea3..bdf789a6 100644
--- a/benches/ascon.rs
+++ b/benches/ascon.rs
@@ -140,8 +140,8 @@ fn ascon_xof128_many(c: &mut Criterion) {
let mut hasher = rscrypto::AsconXof::new();
hasher.update(black_box(input));
let mut reader = hasher.finalize_xof();
- let base = index * OUT_LEN;
- reader.squeeze(&mut out[base..base + OUT_LEN]);
+ let base = index.strict_mul(OUT_LEN);
+ reader.squeeze(&mut out[base..base.strict_add(OUT_LEN)]);
}
black_box(out[0])
})
@@ -163,7 +163,8 @@ fn ascon_cxof128(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("rscrypto", len), data, |b, d| {
b.iter(|| {
let mut out = [0u8; OUT_LEN];
- rscrypto::AsconCxof128::hash_into(black_box(CUSTOMIZATION), black_box(d), &mut out).unwrap();
+ rscrypto::AsconCxof128::hash_into(black_box(CUSTOMIZATION), black_box(d), &mut out)
+ .expect("valid Ascon benchmark operation must succeed");
black_box(out)
})
});
diff --git a/benches/auth.rs b/benches/auth.rs
index 9a37ac57..d16956c4 100644
--- a/benches/auth.rs
+++ b/benches/auth.rs
@@ -43,7 +43,8 @@ fn array_from_slice(slice: &[u8]) -> [u8; N] {
fn deterministic_bytes(offset: u8) -> [u8; N] {
let mut out = [0u8; N];
for (i, byte) in out.iter_mut().enumerate() {
- *byte = offset.wrapping_add(i as u8);
+ let low_index = u8::try_from(i & usize::from(u8::MAX)).expect("masked deterministic-byte index must fit u8");
+ *byte = offset.wrapping_add(low_index);
}
out
}
@@ -147,7 +148,8 @@ fn hmac_sha256(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("rustcrypto", len), data, |b, d| {
- let base_mac = RustCryptoHmacSha256::new_from_slice(&key).unwrap();
+ let base_mac =
+ RustCryptoHmacSha256::new_from_slice(&key).expect("valid authentication benchmark operation must succeed");
b.iter(|| {
use hmac::Mac as _;
@@ -196,7 +198,8 @@ fn hmac_sha384(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("rustcrypto", len), data, |b, d| {
- let base_mac = RustCryptoHmacSha384::new_from_slice(&key).unwrap();
+ let base_mac =
+ RustCryptoHmacSha384::new_from_slice(&key).expect("valid authentication benchmark operation must succeed");
b.iter(|| {
use hmac::Mac as _;
@@ -245,7 +248,8 @@ fn hmac_sha512(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("rustcrypto", len), data, |b, d| {
- let base_mac = RustCryptoHmacSha512::new_from_slice(&key).unwrap();
+ let base_mac =
+ RustCryptoHmacSha512::new_from_slice(&key).expect("valid authentication benchmark operation must succeed");
b.iter(|| {
use hmac::Mac as _;
@@ -292,7 +296,8 @@ fn hmac_sha256_streaming(c: &mut Criterion) {
b.iter(|| {
use hmac::Mac as _;
- let mut mac = RustCryptoHmacSha256::new_from_slice(&key).unwrap();
+ let mut mac =
+ RustCryptoHmacSha256::new_from_slice(&key).expect("valid authentication benchmark operation must succeed");
for chunk in data.chunks(chunk_size) {
mac.update(black_box(chunk));
}
@@ -345,7 +350,8 @@ fn hmac_sha256_internal(c: &mut Criterion) {
b.iter(|| {
use hmac::Mac as _;
- let mut mac = RustCryptoHmacSha256::new_from_slice(black_box(&key)).unwrap();
+ let mut mac = RustCryptoHmacSha256::new_from_slice(black_box(&key))
+ .expect("valid authentication benchmark operation must succeed");
mac.update(black_box(d));
black_box(mac.finalize().into_bytes())
})
@@ -384,7 +390,9 @@ fn hkdf_sha256_expand(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("rscrypto", out_len), &out_len, |b, &len| {
let mut out = vec![0u8; len];
b.iter(|| {
- hkdf.expand(black_box(&info), black_box(&mut out)).unwrap();
+ hkdf
+ .expand(black_box(&info), black_box(&mut out))
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -392,7 +400,9 @@ fn hkdf_sha256_expand(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("rustcrypto", out_len), &out_len, |b, &len| {
let mut out = vec![0u8; len];
b.iter(|| {
- rustcrypto.expand(black_box(&info), black_box(&mut out)).unwrap();
+ rustcrypto
+ .expand(black_box(&info), black_box(&mut out))
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -403,9 +413,9 @@ fn hkdf_sha256_expand(c: &mut Criterion) {
b.iter(|| {
aws_prk
.expand(&[black_box(&info)], AwsHkdfLen(len))
- .unwrap()
+ .expect("valid authentication benchmark operation must succeed")
.fill(black_box(&mut out))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -416,9 +426,9 @@ fn hkdf_sha256_expand(c: &mut Criterion) {
b.iter(|| {
ring_prk
.expand(&[black_box(&info)], RingHkdfLen(len))
- .unwrap()
+ .expect("valid authentication benchmark operation must succeed")
.fill(black_box(&mut out))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -446,7 +456,9 @@ fn hkdf_sha384_expand(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("rscrypto", out_len), &out_len, |b, &len| {
let mut out = vec![0u8; len];
b.iter(|| {
- hkdf.expand(black_box(&info), black_box(&mut out)).unwrap();
+ hkdf
+ .expand(black_box(&info), black_box(&mut out))
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -454,7 +466,9 @@ fn hkdf_sha384_expand(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("rustcrypto", out_len), &out_len, |b, &len| {
let mut out = vec![0u8; len];
b.iter(|| {
- rustcrypto.expand(black_box(&info), black_box(&mut out)).unwrap();
+ rustcrypto
+ .expand(black_box(&info), black_box(&mut out))
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -465,9 +479,9 @@ fn hkdf_sha384_expand(c: &mut Criterion) {
b.iter(|| {
aws_prk
.expand(&[black_box(&info)], AwsHkdfLen(len))
- .unwrap()
+ .expect("valid authentication benchmark operation must succeed")
.fill(black_box(&mut out))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -478,9 +492,9 @@ fn hkdf_sha384_expand(c: &mut Criterion) {
b.iter(|| {
ring_prk
.expand(&[black_box(&info)], RingHkdfLen(len))
- .unwrap()
+ .expect("valid authentication benchmark operation must succeed")
.fill(black_box(&mut out))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -497,7 +511,8 @@ fn pbkdf2_sha256_derive(c: &mut Criterion) {
let state = Pbkdf2Sha256::new(&password);
for &iterations in &[1u32, 100, 1000] {
- let nz_iters = core::num::NonZeroU32::new(iterations).unwrap();
+ let nz_iters =
+ core::num::NonZeroU32::new(iterations).expect("valid authentication benchmark operation must succeed");
let mut g = c.benchmark_group(format!("pbkdf2-sha256/iters={iterations}"));
for &out_len in &[32usize, 64] {
@@ -507,7 +522,7 @@ fn pbkdf2_sha256_derive(c: &mut Criterion) {
let mut out = vec![0u8; len];
b.iter(|| {
Pbkdf2Sha256::derive_key_primitive(black_box(&password), black_box(&salt), iterations, black_box(&mut out))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -559,7 +574,9 @@ fn pbkdf2_sha256_derive(c: &mut Criterion) {
g_state.bench_with_input(BenchmarkId::new("rscrypto", out_len), &out_len, |b, &len| {
let mut out = vec![0u8; len];
b.iter(|| {
- state.derive(black_box(&salt), iterations, black_box(&mut out)).unwrap();
+ state
+ .derive(black_box(&salt), iterations, black_box(&mut out))
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -576,7 +593,8 @@ fn pbkdf2_sha256_internal(c: &mut Criterion) {
let state = Pbkdf2Sha256::new(&password);
for &iterations in &[1u32, 100, 1000] {
- let nz_iters = core::num::NonZeroU32::new(iterations).unwrap();
+ let nz_iters =
+ core::num::NonZeroU32::new(iterations).expect("valid authentication benchmark operation must succeed");
let mut g = c.benchmark_group(format!("pbkdf2-sha256/internal/iters={iterations}"));
for &out_len in &[32usize, 64] {
@@ -586,7 +604,7 @@ fn pbkdf2_sha256_internal(c: &mut Criterion) {
let mut out = vec![0u8; len];
b.iter(|| {
Pbkdf2Sha256::derive_key_primitive(black_box(&password), black_box(&salt), iterations, black_box(&mut out))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -594,7 +612,9 @@ fn pbkdf2_sha256_internal(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("rscrypto-state", out_len), &out_len, |b, &len| {
let mut out = vec![0u8; len];
b.iter(|| {
- state.derive(black_box(&salt), iterations, black_box(&mut out)).unwrap();
+ state
+ .derive(black_box(&salt), iterations, black_box(&mut out))
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -650,7 +670,8 @@ fn pbkdf2_sha512_derive(c: &mut Criterion) {
let state = Pbkdf2Sha512::new(&password);
for &iterations in &[1u32, 100, 1000] {
- let nz_iters = core::num::NonZeroU32::new(iterations).unwrap();
+ let nz_iters =
+ core::num::NonZeroU32::new(iterations).expect("valid authentication benchmark operation must succeed");
let mut g = c.benchmark_group(format!("pbkdf2-sha512/iters={iterations}"));
for &out_len in &[64usize, 128] {
@@ -660,7 +681,7 @@ fn pbkdf2_sha512_derive(c: &mut Criterion) {
let mut out = vec![0u8; len];
b.iter(|| {
Pbkdf2Sha512::derive_key_primitive(black_box(&password), black_box(&salt), iterations, black_box(&mut out))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -712,7 +733,9 @@ fn pbkdf2_sha512_derive(c: &mut Criterion) {
g_state.bench_with_input(BenchmarkId::new("rscrypto", out_len), &out_len, |b, &len| {
let mut out = vec![0u8; len];
b.iter(|| {
- state.derive(black_box(&salt), iterations, black_box(&mut out)).unwrap();
+ state
+ .derive(black_box(&salt), iterations, black_box(&mut out))
+ .expect("valid authentication benchmark operation must succeed");
black_box(out[0])
})
});
@@ -744,13 +767,15 @@ fn ed25519_public_key(c: &mut Criterion) {
fn ecdsa_p256_verify(c: &mut Criterion) {
let secret_bytes = [0x11u8; 32];
- let signing_key = P256OracleSigningKey::from_slice(&secret_bytes).unwrap();
+ let signing_key =
+ P256OracleSigningKey::from_slice(&secret_bytes).expect("valid authentication benchmark operation must succeed");
let verifying_key = signing_key.verifying_key();
let sec1 = EcdsaP256SecretKey::from_bytes(secret_bytes)
- .unwrap()
+ .expect("valid authentication benchmark operation must succeed")
.public_key()
.to_sec1_bytes();
- let public = EcdsaP256PublicKey::from_sec1_bytes(sec1.as_slice()).unwrap();
+ let public = EcdsaP256PublicKey::from_sec1_bytes(sec1.as_slice())
+ .expect("valid authentication benchmark operation must succeed");
let ring_upk = ring::signature::UnparsedPublicKey::new(&ring::signature::ECDSA_P256_SHA256_FIXED, sec1.as_slice());
aws_lc_bench! {
let aws_upk =
@@ -766,11 +791,14 @@ fn ecdsa_p256_verify(c: &mut Criterion) {
for (len, data) in &inputs {
common::set_throughput(&mut g, *len);
let oracle_signature: P256OracleSignature = p256::ecdsa::signature::Signer::sign(&signing_key, data);
- let signature = EcdsaP256Signature::from_bytes(array_from_slice(oracle_signature.to_bytes().as_ref())).unwrap();
+ let signature = EcdsaP256Signature::from_bytes(array_from_slice(oracle_signature.to_bytes().as_ref()))
+ .expect("valid authentication benchmark operation must succeed");
g.bench_with_input(BenchmarkId::new("rscrypto", len), data, |b, d| {
b.iter(|| {
- black_box(&public).verify(black_box(d), black_box(&signature)).unwrap();
+ black_box(&public)
+ .verify(black_box(d), black_box(&signature))
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -778,14 +806,16 @@ fn ecdsa_p256_verify(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("rustcrypto-p256", len), data, |b, d| {
b.iter(|| {
p256::ecdsa::signature::Verifier::verify(black_box(verifying_key), black_box(d), black_box(&oracle_signature))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
g.bench_with_input(BenchmarkId::new("ring", len), data, |b, d| {
b.iter(|| {
- ring_upk.verify(black_box(d), black_box(signature.as_bytes())).unwrap();
+ ring_upk
+ .verify(black_box(d), black_box(signature.as_bytes()))
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -793,7 +823,7 @@ fn ecdsa_p256_verify(c: &mut Criterion) {
aws_lc_bench! {
g.bench_with_input(BenchmarkId::new("aws-lc-rs", len), data, |b, d| {
b.iter(|| {
- aws_upk.verify(black_box(d), black_box(signature.as_bytes())).unwrap();
+ aws_upk.verify(black_box(d), black_box(signature.as_bytes())).expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -805,10 +835,12 @@ fn ecdsa_p256_verify(c: &mut Criterion) {
fn ecdsa_p256_sign(c: &mut Criterion) {
let secret_bytes = [0x11u8; 32];
- let secret = EcdsaP256SecretKey::from_bytes(secret_bytes).unwrap();
+ let secret =
+ EcdsaP256SecretKey::from_bytes(secret_bytes).expect("valid authentication benchmark operation must succeed");
let keypair = EcdsaP256Keypair::from_secret_key(secret);
let blind = [0x5cu8; 64];
- let signing_key = P256OracleSigningKey::from_slice(&secret_bytes).unwrap();
+ let signing_key =
+ P256OracleSigningKey::from_slice(&secret_bytes).expect("valid authentication benchmark operation must succeed");
let sec1 = keypair.public_key().to_sec1_bytes();
let ring_rng = ring::rand::SystemRandom::new();
let ring_key = ring::signature::EcdsaKeyPair::from_private_key_and_public_key(
@@ -817,7 +849,7 @@ fn ecdsa_p256_sign(c: &mut Criterion) {
&sec1,
&ring_rng,
)
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
aws_lc_bench! {
let aws_rng = aws_lc_rs::rand::SystemRandom::new();
let aws_key = aws_lc_rs::signature::EcdsaKeyPair::from_private_key_and_public_key(
@@ -825,7 +857,7 @@ fn ecdsa_p256_sign(c: &mut Criterion) {
&secret_bytes,
&sec1,
)
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
}
let inputs = [0usize, 32, 1024, 16384]
@@ -838,7 +870,13 @@ fn ecdsa_p256_sign(c: &mut Criterion) {
common::set_throughput(&mut g, *len);
g.bench_with_input(BenchmarkId::new("rscrypto-deterministic", len), data, |b, d| {
- b.iter(|| black_box(black_box(&keypair).try_sign(black_box(d)).unwrap()))
+ b.iter(|| {
+ black_box(
+ black_box(&keypair)
+ .try_sign(black_box(d))
+ .expect("valid authentication benchmark operation must succeed"),
+ )
+ })
});
g.bench_with_input(BenchmarkId::new("rscrypto-blinded", len), data, |b, d| {
@@ -846,7 +884,7 @@ fn ecdsa_p256_sign(c: &mut Criterion) {
black_box(
black_box(&keypair)
.try_sign_blinded(black_box(d), |out| out.copy_from_slice(black_box(&blind)))
- .unwrap(),
+ .expect("valid authentication benchmark operation must succeed"),
)
})
});
@@ -860,12 +898,18 @@ fn ecdsa_p256_sign(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("ring", len), data, |b, d| {
- b.iter(|| black_box(ring_key.sign(&ring_rng, black_box(d)).unwrap()))
+ b.iter(|| {
+ black_box(
+ ring_key
+ .sign(&ring_rng, black_box(d))
+ .expect("valid authentication benchmark operation must succeed"),
+ )
+ })
});
aws_lc_bench! {
g.bench_with_input(BenchmarkId::new("aws-lc-rs", len), data, |b, d| {
- b.iter(|| black_box(aws_key.sign(&aws_rng, black_box(d)).unwrap()))
+ b.iter(|| black_box(aws_key.sign(&aws_rng, black_box(d)).expect("valid authentication benchmark operation must succeed")))
});
}
}
@@ -875,13 +919,15 @@ fn ecdsa_p256_sign(c: &mut Criterion) {
fn ecdsa_p384_verify(c: &mut Criterion) {
let secret_bytes = [0x31u8; 48];
- let signing_key = P384OracleSigningKey::from_slice(&secret_bytes).unwrap();
+ let signing_key =
+ P384OracleSigningKey::from_slice(&secret_bytes).expect("valid authentication benchmark operation must succeed");
let verifying_key = signing_key.verifying_key();
let sec1 = EcdsaP384SecretKey::from_bytes(secret_bytes)
- .unwrap()
+ .expect("valid authentication benchmark operation must succeed")
.public_key()
.to_sec1_bytes();
- let public = EcdsaP384PublicKey::from_sec1_bytes(sec1.as_slice()).unwrap();
+ let public = EcdsaP384PublicKey::from_sec1_bytes(sec1.as_slice())
+ .expect("valid authentication benchmark operation must succeed");
let ring_upk = ring::signature::UnparsedPublicKey::new(&ring::signature::ECDSA_P384_SHA384_FIXED, sec1.as_slice());
aws_lc_bench! {
let aws_upk =
@@ -897,11 +943,14 @@ fn ecdsa_p384_verify(c: &mut Criterion) {
for (len, data) in &inputs {
common::set_throughput(&mut g, *len);
let oracle_signature: P384OracleSignature = p384::ecdsa::signature::Signer::sign(&signing_key, data);
- let signature = EcdsaP384Signature::from_bytes(array_from_slice(oracle_signature.to_bytes().as_ref())).unwrap();
+ let signature = EcdsaP384Signature::from_bytes(array_from_slice(oracle_signature.to_bytes().as_ref()))
+ .expect("valid authentication benchmark operation must succeed");
g.bench_with_input(BenchmarkId::new("rscrypto", len), data, |b, d| {
b.iter(|| {
- black_box(&public).verify(black_box(d), black_box(&signature)).unwrap();
+ black_box(&public)
+ .verify(black_box(d), black_box(&signature))
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -909,14 +958,16 @@ fn ecdsa_p384_verify(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("rustcrypto-p384", len), data, |b, d| {
b.iter(|| {
p384::ecdsa::signature::Verifier::verify(black_box(verifying_key), black_box(d), black_box(&oracle_signature))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
g.bench_with_input(BenchmarkId::new("ring", len), data, |b, d| {
b.iter(|| {
- ring_upk.verify(black_box(d), black_box(signature.as_bytes())).unwrap();
+ ring_upk
+ .verify(black_box(d), black_box(signature.as_bytes()))
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -924,7 +975,7 @@ fn ecdsa_p384_verify(c: &mut Criterion) {
aws_lc_bench! {
g.bench_with_input(BenchmarkId::new("aws-lc-rs", len), data, |b, d| {
b.iter(|| {
- aws_upk.verify(black_box(d), black_box(signature.as_bytes())).unwrap();
+ aws_upk.verify(black_box(d), black_box(signature.as_bytes())).expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -936,10 +987,12 @@ fn ecdsa_p384_verify(c: &mut Criterion) {
fn ecdsa_p384_sign(c: &mut Criterion) {
let secret_bytes = [0x31u8; 48];
- let secret = EcdsaP384SecretKey::from_bytes(secret_bytes).unwrap();
+ let secret =
+ EcdsaP384SecretKey::from_bytes(secret_bytes).expect("valid authentication benchmark operation must succeed");
let keypair = EcdsaP384Keypair::from_secret_key(secret);
let blind = [0xa3u8; 96];
- let signing_key = P384OracleSigningKey::from_slice(&secret_bytes).unwrap();
+ let signing_key =
+ P384OracleSigningKey::from_slice(&secret_bytes).expect("valid authentication benchmark operation must succeed");
let sec1 = keypair.public_key().to_sec1_bytes();
let ring_rng = ring::rand::SystemRandom::new();
let ring_key = ring::signature::EcdsaKeyPair::from_private_key_and_public_key(
@@ -948,7 +1001,7 @@ fn ecdsa_p384_sign(c: &mut Criterion) {
&sec1,
&ring_rng,
)
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
aws_lc_bench! {
let aws_rng = aws_lc_rs::rand::SystemRandom::new();
let aws_key = aws_lc_rs::signature::EcdsaKeyPair::from_private_key_and_public_key(
@@ -956,7 +1009,7 @@ fn ecdsa_p384_sign(c: &mut Criterion) {
&secret_bytes,
&sec1,
)
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
}
let inputs = [0usize, 32, 1024, 16384]
@@ -969,7 +1022,13 @@ fn ecdsa_p384_sign(c: &mut Criterion) {
common::set_throughput(&mut g, *len);
g.bench_with_input(BenchmarkId::new("rscrypto-deterministic", len), data, |b, d| {
- b.iter(|| black_box(black_box(&keypair).try_sign(black_box(d)).unwrap()))
+ b.iter(|| {
+ black_box(
+ black_box(&keypair)
+ .try_sign(black_box(d))
+ .expect("valid authentication benchmark operation must succeed"),
+ )
+ })
});
g.bench_with_input(BenchmarkId::new("rscrypto-blinded", len), data, |b, d| {
@@ -977,7 +1036,7 @@ fn ecdsa_p384_sign(c: &mut Criterion) {
black_box(
black_box(&keypair)
.try_sign_blinded(black_box(d), |out| out.copy_from_slice(black_box(&blind)))
- .unwrap(),
+ .expect("valid authentication benchmark operation must succeed"),
)
})
});
@@ -991,12 +1050,18 @@ fn ecdsa_p384_sign(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("ring", len), data, |b, d| {
- b.iter(|| black_box(ring_key.sign(&ring_rng, black_box(d)).unwrap()))
+ b.iter(|| {
+ black_box(
+ ring_key
+ .sign(&ring_rng, black_box(d))
+ .expect("valid authentication benchmark operation must succeed"),
+ )
+ })
});
aws_lc_bench! {
g.bench_with_input(BenchmarkId::new("aws-lc-rs", len), data, |b, d| {
- b.iter(|| black_box(aws_key.sign(&aws_rng, black_box(d)).unwrap()))
+ b.iter(|| black_box(aws_key.sign(&aws_rng, black_box(d)).expect("valid authentication benchmark operation must succeed")))
});
}
}
@@ -1118,9 +1183,10 @@ fn ed25519_sign(c: &mut Criterion) {
let keypair = Ed25519Keypair::from_secret_key(secret.duplicate_secret());
let signing_key = SigningKey::from_bytes(&secret_bytes);
aws_lc_bench! {
- let aws_kp = aws_lc_rs::signature::Ed25519KeyPair::from_seed_unchecked(&secret_bytes).unwrap();
+ let aws_kp = aws_lc_rs::signature::Ed25519KeyPair::from_seed_unchecked(&secret_bytes).expect("valid authentication benchmark operation must succeed");
}
- let ring_kp = ring::signature::Ed25519KeyPair::from_seed_unchecked(&secret_bytes).unwrap();
+ let ring_kp = ring::signature::Ed25519KeyPair::from_seed_unchecked(&secret_bytes)
+ .expect("valid authentication benchmark operation must succeed");
let (_dryoc_pk, dryoc_sk) = crypto_sign_seed_keypair(&secret_bytes);
let mut dryoc_sig: [u8; 64] = [0u8; 64];
let inputs = [0usize, 32, 1024, 16384]
@@ -1156,7 +1222,8 @@ fn ed25519_sign(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("dryoc", len), data, |b, d| {
b.iter(|| {
- crypto_sign_detached(&mut dryoc_sig, black_box(d), &dryoc_sk).unwrap();
+ crypto_sign_detached(&mut dryoc_sig, black_box(d), &dryoc_sk)
+ .expect("valid authentication benchmark operation must succeed");
black_box(&dryoc_sig);
})
});
@@ -1179,11 +1246,12 @@ fn ed25519_verify(c: &mut Criterion) {
let signing_key = SigningKey::from_bytes(&secret_bytes);
let verifying_key = signing_key.verifying_key();
aws_lc_bench! {
- let aws_kp = aws_lc_rs::signature::Ed25519KeyPair::from_seed_unchecked(&secret_bytes).unwrap();
+ let aws_kp = aws_lc_rs::signature::Ed25519KeyPair::from_seed_unchecked(&secret_bytes).expect("valid authentication benchmark operation must succeed");
let aws_pubkey: Vec = aws_kp.public_key().as_ref().to_vec();
let aws_upk = aws_lc_rs::signature::UnparsedPublicKey::new(&aws_lc_rs::signature::ED25519, aws_pubkey);
}
- let ring_kp = ring::signature::Ed25519KeyPair::from_seed_unchecked(&secret_bytes).unwrap();
+ let ring_kp = ring::signature::Ed25519KeyPair::from_seed_unchecked(&secret_bytes)
+ .expect("valid authentication benchmark operation must succeed");
let ring_pubkey: Vec = ring_kp.public_key().as_ref().to_vec();
let ring_upk = ring::signature::UnparsedPublicKey::new(&ring::signature::ED25519, ring_pubkey);
let (dryoc_pk, dryoc_sk) = crypto_sign_seed_keypair(&secret_bytes);
@@ -1202,11 +1270,14 @@ fn ed25519_verify(c: &mut Criterion) {
}
let ring_sig = ring_kp.sign(data);
let mut dryoc_sig: [u8; 64] = [0u8; 64];
- crypto_sign_detached(&mut dryoc_sig, data, &dryoc_sk).unwrap();
+ crypto_sign_detached(&mut dryoc_sig, data, &dryoc_sk)
+ .expect("valid authentication benchmark operation must succeed");
g.bench_with_input(BenchmarkId::new("rscrypto", len), data, |b, d| {
b.iter(|| {
- black_box(&public).verify(black_box(d), black_box(&ours)).unwrap();
+ black_box(&public)
+ .verify(black_box(d), black_box(&ours))
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -1215,7 +1286,7 @@ fn ed25519_verify(c: &mut Criterion) {
b.iter(|| {
black_box(&verifying_key)
.verify_strict(black_box(d), black_box(&dalek))
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -1223,7 +1294,7 @@ fn ed25519_verify(c: &mut Criterion) {
aws_lc_bench! {
g.bench_with_input(BenchmarkId::new("aws-lc-rs", len), data, |b, d| {
b.iter(|| {
- aws_upk.verify(black_box(d), aws_sig.as_ref()).unwrap();
+ aws_upk.verify(black_box(d), aws_sig.as_ref()).expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -1231,14 +1302,17 @@ fn ed25519_verify(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("ring", len), data, |b, d| {
b.iter(|| {
- ring_upk.verify(black_box(d), ring_sig.as_ref()).unwrap();
+ ring_upk
+ .verify(black_box(d), ring_sig.as_ref())
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
g.bench_with_input(BenchmarkId::new("dryoc", len), data, |b, d| {
b.iter(|| {
- crypto_sign_verify_detached(&dryoc_sig, black_box(d), &dryoc_pk).unwrap();
+ crypto_sign_verify_detached(&dryoc_sig, black_box(d), &dryoc_pk)
+ .expect("valid authentication benchmark operation must succeed");
black_box(())
})
});
@@ -1269,7 +1343,8 @@ fn ed25519_verify_phase(c: &mut Criterion) {
for (len, data) in &inputs {
common::set_throughput(&mut g, *len);
let signature = keypair.sign(data);
- let scalars = diag_ed25519_verify_scalars(&public, &signature, data).unwrap();
+ let scalars = diag_ed25519_verify_scalars(&public, &signature, data)
+ .expect("valid authentication benchmark operation must succeed");
g.bench_with_input(BenchmarkId::new("challenge-reduce", len), data, |b, d| {
b.iter(|| {
@@ -1352,8 +1427,8 @@ fn x25519_public_key(c: &mut Criterion) {
b.iter(|| {
let priv_key =
aws_lc_rs::agreement::PrivateKey::from_private_key(&aws_lc_rs::agreement::X25519, black_box(&secret_bytes))
- .unwrap();
- black_box(priv_key.compute_public_key().unwrap())
+ .expect("valid authentication benchmark operation must succeed");
+ black_box(priv_key.compute_public_key().expect("valid authentication benchmark operation must succeed"))
})
});
}
@@ -1381,11 +1456,11 @@ fn x25519_diffie_hellman(c: &mut Criterion) {
let dalek_bob_public = DalekX25519PublicKey::from(&DalekX25519Secret::from(bob_bytes));
aws_lc_bench! {
let aws_alice =
- aws_lc_rs::agreement::PrivateKey::from_private_key(&aws_lc_rs::agreement::X25519, &alice_bytes).unwrap();
+ aws_lc_rs::agreement::PrivateKey::from_private_key(&aws_lc_rs::agreement::X25519, &alice_bytes).expect("valid authentication benchmark operation must succeed");
let aws_bob_pub_bytes: [u8; 32] = {
let bob_priv =
- aws_lc_rs::agreement::PrivateKey::from_private_key(&aws_lc_rs::agreement::X25519, &bob_bytes).unwrap();
- let pk = bob_priv.compute_public_key().unwrap();
+ aws_lc_rs::agreement::PrivateKey::from_private_key(&aws_lc_rs::agreement::X25519, &bob_bytes).expect("valid authentication benchmark operation must succeed");
+ let pk = bob_priv.compute_public_key().expect("valid authentication benchmark operation must succeed");
let mut out = [0u8; 32];
out.copy_from_slice(pk.as_ref());
out
@@ -1398,7 +1473,13 @@ fn x25519_diffie_hellman(c: &mut Criterion) {
let mut g = c.benchmark_group("x25519/diffie-hellman");
g.bench_function("rscrypto", |b| {
- b.iter(|| black_box(black_box(&alice).diffie_hellman(black_box(&bob_public)).unwrap()))
+ b.iter(|| {
+ black_box(
+ black_box(&alice)
+ .diffie_hellman(black_box(&bob_public))
+ .expect("valid authentication benchmark operation must succeed"),
+ )
+ })
});
g.bench_function("dalek", |b| {
@@ -1413,7 +1494,7 @@ fn x25519_diffie_hellman(c: &mut Criterion) {
out.copy_from_slice(bytes);
Ok::<[u8; 32], ()>(out)
})
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
black_box(shared)
})
});
@@ -1422,7 +1503,8 @@ fn x25519_diffie_hellman(c: &mut Criterion) {
g.bench_function("dryoc", |b| {
let mut shared = [0u8; 32];
b.iter(|| {
- crypto_scalarmult(&mut shared, black_box(&alice_bytes), black_box(&dryoc_bob_pub));
+ crypto_scalarmult(&mut shared, black_box(&alice_bytes), black_box(&dryoc_bob_pub))
+ .expect("valid authentication benchmark operation must succeed");
black_box(shared)
})
});
@@ -1454,7 +1536,7 @@ macro_rules! mlkem_profile_benches {
out.copy_from_slice(black_box(&key_random));
Ok::<(), MlKemError>(())
})
- .unwrap()
+ .expect("valid authentication benchmark operation must succeed")
})
});
@@ -1464,7 +1546,7 @@ macro_rules! mlkem_profile_benches {
aws_lc_bench! {
g.bench_function("aws-lc-rs", |b| {
- b.iter(|| black_box(AwsMlKemDecapsulationKey::generate(&$aws_algorithm).unwrap()))
+ b.iter(|| black_box(AwsMlKemDecapsulationKey::generate(&$aws_algorithm).expect("valid authentication benchmark operation must succeed")))
});
}
@@ -1490,8 +1572,8 @@ macro_rules! mlkem_profile_benches {
out.copy_from_slice(&key_random);
Ok::<(), MlKemError>(())
})
- .unwrap();
- let prepared_ek = ek.prepare().unwrap();
+ .expect("valid authentication benchmark operation must succeed");
+ let prepared_ek = ek.prepare().expect("valid authentication benchmark operation must succeed");
let (fips_ek, _) = $fips::KG::keygen_from_seed(
array_from_slice::<32>(&key_random[..32]),
array_from_slice::<32>(&key_random[32..]),
@@ -1502,8 +1584,8 @@ macro_rules! mlkem_profile_benches {
let libcrux_keypair = $libcrux::generate_key_pair(key_random);
let libcrux_ek = libcrux_keypair.public_key().clone();
aws_lc_bench! {
- let aws_dk = AwsMlKemDecapsulationKey::generate(&$aws_algorithm).unwrap();
- let aws_ek = aws_dk.encapsulation_key().unwrap();
+ let aws_dk = AwsMlKemDecapsulationKey::generate(&$aws_algorithm).expect("valid authentication benchmark operation must succeed");
+ let aws_ek = aws_dk.encapsulation_key().expect("valid authentication benchmark operation must succeed");
}
let mut g = c.benchmark_group(concat!($group, "/encapsulate"));
@@ -1514,7 +1596,7 @@ macro_rules! mlkem_profile_benches {
out.copy_from_slice(black_box(&encaps_random));
Ok::<(), MlKemError>(())
})
- .unwrap()
+ .expect("valid authentication benchmark operation must succeed")
})
});
@@ -1524,7 +1606,7 @@ macro_rules! mlkem_profile_benches {
aws_lc_bench! {
g.bench_function("aws-lc-rs", |b| {
- b.iter(|| black_box(aws_ek.encapsulate().unwrap()))
+ b.iter(|| black_box(aws_ek.encapsulate().expect("valid authentication benchmark operation must succeed")))
});
}
@@ -1548,15 +1630,15 @@ macro_rules! mlkem_profile_benches {
out.copy_from_slice(&key_random);
Ok::<(), MlKemError>(())
})
- .unwrap();
- let prepared_ek = ek.prepare().unwrap();
- let prepared_dk = dk.prepare().unwrap();
+ .expect("valid authentication benchmark operation must succeed");
+ let prepared_ek = ek.prepare().expect("valid authentication benchmark operation must succeed");
+ let prepared_dk = dk.prepare().expect("valid authentication benchmark operation must succeed");
let (ciphertext, _) = prepared_ek
.encapsulate(|out| {
out.copy_from_slice(&encaps_random);
Ok::<(), MlKemError>(())
})
- .unwrap();
+ .expect("valid authentication benchmark operation must succeed");
let (fips_ek, fips_dk) = $fips::KG::keygen_from_seed(
array_from_slice::<32>(&key_random[..32]),
array_from_slice::<32>(&key_random[32..]),
@@ -1572,14 +1654,14 @@ macro_rules! mlkem_profile_benches {
let libcrux_dk = libcrux_keypair.private_key().clone();
let (libcrux_ciphertext, _) = $libcrux::encapsulate(&libcrux_ek, encaps_random);
aws_lc_bench! {
- let aws_dk = AwsMlKemDecapsulationKey::generate(&$aws_algorithm).unwrap();
- let aws_ek = aws_dk.encapsulation_key().unwrap();
- let (aws_ciphertext, _) = aws_ek.encapsulate().unwrap();
+ let aws_dk = AwsMlKemDecapsulationKey::generate(&$aws_algorithm).expect("valid authentication benchmark operation must succeed");
+ let aws_ek = aws_dk.encapsulation_key().expect("valid authentication benchmark operation must succeed");
+ let (aws_ciphertext, _) = aws_ek.encapsulate().expect("valid authentication benchmark operation must succeed");
}
let mut g = c.benchmark_group(concat!($group, "/decapsulate"));
g.bench_function("rscrypto", |b| {
- b.iter(|| black_box(&prepared_dk).decapsulate(black_box(&ciphertext)).unwrap())
+ b.iter(|| black_box(&prepared_dk).decapsulate(black_box(&ciphertext)).expect("valid authentication benchmark operation must succeed"))
});
g.bench_function("libcrux", |b| {
@@ -1597,14 +1679,14 @@ macro_rules! mlkem_profile_benches {
black_box(
aws_dk
.decapsulate(AwsMlKemCiphertext::from(black_box(aws_ciphertext.as_ref())))
- .unwrap(),
+ .expect("valid authentication benchmark operation must succeed"),
)
})
});
}
g.bench_function("fips203", |b| {
- b.iter(|| black_box(fips_dk.try_decaps(black_box(&fips_ciphertext)).unwrap()))
+ b.iter(|| black_box(fips_dk.try_decaps(black_box(&fips_ciphertext)).expect("valid authentication benchmark operation must succeed")))
});
g.bench_function("rustcrypto", |b| {
diff --git a/benches/blake2.rs b/benches/blake2.rs
index 7e851d9e..bf31d82b 100644
--- a/benches/blake2.rs
+++ b/benches/blake2.rs
@@ -5,12 +5,14 @@ mod common;
use core::hint::black_box;
use blake2::{
- Blake2b256 as RustCryptoBlake2b256, Blake2b512 as RustCryptoBlake2b512, Blake2bMac,
- Blake2s128 as RustCryptoBlake2s128, Blake2s256 as RustCryptoBlake2s256, Blake2sMac, Digest as _,
+ Blake2b as RustCryptoBlake2b, Blake2b512 as RustCryptoBlake2b512, Blake2bMac, Blake2s as RustCryptoBlake2s,
+ Blake2s256 as RustCryptoBlake2s256, Blake2sMac,
+ digest::{
+ Digest as _, Mac as _,
+ consts::{U16, U32, U64},
+ },
};
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
-use digest::typenum::{U16, U32, U64};
-use hmac::{Mac as _, digest::KeyInit};
use rscrypto::{
Blake2b256, Blake2b512, Blake2bKey, Blake2bParams, Blake2s128, Blake2s256, Blake2sKey, Blake2sParams, Digest,
};
@@ -19,6 +21,8 @@ type RustCryptoBlake2bMac256 = Blake2bMac;
type RustCryptoBlake2bMac512 = Blake2bMac;
type RustCryptoBlake2sMac128 = Blake2sMac;
type RustCryptoBlake2sMac256 = Blake2sMac;
+type RustCryptoBlake2b256 = RustCryptoBlake2b;
+type RustCryptoBlake2s128 = RustCryptoBlake2s;
fn oneshot(c: &mut Criterion) {
use dryoc::classic::crypto_generichash::crypto_generichash;
@@ -38,7 +42,8 @@ fn oneshot(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("dryoc/blake2b256", len), data, |b, d| {
let mut out = [0u8; 32];
b.iter(|| {
- crypto_generichash(black_box(&mut out), black_box(d), None).unwrap();
+ crypto_generichash(black_box(&mut out), black_box(d), None)
+ .expect("valid BLAKE2 benchmark operation must succeed");
black_box(out)
})
});
@@ -52,7 +57,8 @@ fn oneshot(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("dryoc/blake2b512", len), data, |b, d| {
let mut out = [0u8; 64];
b.iter(|| {
- crypto_generichash(black_box(&mut out), black_box(d), None).unwrap();
+ crypto_generichash(black_box(&mut out), black_box(d), None)
+ .expect("valid BLAKE2 benchmark operation must succeed");
black_box(out)
})
});
@@ -86,8 +92,8 @@ fn host_overhead(c: &mut Criterion) {
let inputs = tiny_inputs();
let key_b = [0x42u8; 64];
let key_s = [0x24u8; 32];
- let key_b_typed = Blake2bKey::new(black_box(&key_b[..32])).unwrap();
- let key_s_typed = Blake2sKey::new(black_box(&key_s)).unwrap();
+ let key_b_typed = Blake2bKey::new(black_box(&key_b[..32])).expect("valid BLAKE2 benchmark operation must succeed");
+ let key_s_typed = Blake2sKey::new(black_box(&key_s)).expect("valid BLAKE2 benchmark operation must succeed");
let mut oneshot = c.benchmark_group("blake2/host-overhead");
for (len, data) in &inputs {
@@ -118,7 +124,8 @@ fn host_overhead(c: &mut Criterion) {
});
keyed.bench_with_input(BenchmarkId::new("rustcrypto/blake2b256", len), data, |b, d| {
b.iter(|| {
- let mut mac = RustCryptoBlake2bMac256::new_from_slice(black_box(&key_b[..32])).unwrap();
+ let mut mac = RustCryptoBlake2bMac256::new_from_slice(black_box(&key_b[..32]))
+ .expect("valid BLAKE2 benchmark operation must succeed");
mac.update(black_box(d));
black_box(mac.finalize().into_bytes())
})
@@ -129,7 +136,8 @@ fn host_overhead(c: &mut Criterion) {
});
keyed.bench_with_input(BenchmarkId::new("rustcrypto/blake2s256", len), data, |b, d| {
b.iter(|| {
- let mut mac = RustCryptoBlake2sMac256::new_from_slice(black_box(&key_s)).unwrap();
+ let mut mac = RustCryptoBlake2sMac256::new_from_slice(black_box(&key_s))
+ .expect("valid BLAKE2 benchmark operation must succeed");
mac.update(black_box(d));
black_box(mac.finalize().into_bytes())
})
@@ -180,10 +188,10 @@ fn keyed(c: &mut Criterion) {
let inputs = common::comp_sizes();
let key_b = [0x42u8; 64];
let key_s = [0x24u8; 32];
- let key_b_256 = Blake2bKey::new(black_box(&key_b[..32])).unwrap();
- let key_b_512 = Blake2bKey::new(black_box(&key_b)).unwrap();
- let key_s_128 = Blake2sKey::new(black_box(&key_s[..16])).unwrap();
- let key_s_256 = Blake2sKey::new(black_box(&key_s)).unwrap();
+ let key_b_256 = Blake2bKey::new(black_box(&key_b[..32])).expect("valid BLAKE2 benchmark operation must succeed");
+ let key_b_512 = Blake2bKey::new(black_box(&key_b)).expect("valid BLAKE2 benchmark operation must succeed");
+ let key_s_128 = Blake2sKey::new(black_box(&key_s[..16])).expect("valid BLAKE2 benchmark operation must succeed");
+ let key_s_256 = Blake2sKey::new(black_box(&key_s)).expect("valid BLAKE2 benchmark operation must succeed");
let mut g = c.benchmark_group("blake2/keyed");
for (len, data) in &inputs {
@@ -194,7 +202,8 @@ fn keyed(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("rustcrypto/blake2b256", len), data, |b, d| {
b.iter(|| {
- let mut mac = RustCryptoBlake2bMac256::new_from_slice(black_box(&key_b[..32])).unwrap();
+ let mut mac = RustCryptoBlake2bMac256::new_from_slice(black_box(&key_b[..32]))
+ .expect("valid BLAKE2 benchmark operation must succeed");
mac.update(black_box(d));
black_box(mac.finalize().into_bytes())
})
@@ -202,7 +211,8 @@ fn keyed(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("dryoc/blake2b256", len), data, |b, d| {
let mut out = [0u8; 32];
b.iter(|| {
- crypto_generichash(black_box(&mut out), black_box(d), Some(black_box(&key_b[..32]))).unwrap();
+ crypto_generichash(black_box(&mut out), black_box(d), Some(black_box(&key_b[..32])))
+ .expect("valid BLAKE2 benchmark operation must succeed");
black_box(out)
})
});
@@ -212,7 +222,8 @@ fn keyed(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("rustcrypto/blake2b512", len), data, |b, d| {
b.iter(|| {
- let mut mac = RustCryptoBlake2bMac512::new_from_slice(black_box(&key_b)).unwrap();
+ let mut mac = RustCryptoBlake2bMac512::new_from_slice(black_box(&key_b))
+ .expect("valid BLAKE2 benchmark operation must succeed");
mac.update(black_box(d));
black_box(mac.finalize().into_bytes())
})
@@ -220,7 +231,8 @@ fn keyed(c: &mut Criterion) {
g.bench_with_input(BenchmarkId::new("dryoc/blake2b512", len), data, |b, d| {
let mut out = [0u8; 64];
b.iter(|| {
- crypto_generichash(black_box(&mut out), black_box(d), Some(black_box(&key_b[..]))).unwrap();
+ crypto_generichash(black_box(&mut out), black_box(d), Some(black_box(&key_b[..])))
+ .expect("valid BLAKE2 benchmark operation must succeed");
black_box(out)
})
});
@@ -230,7 +242,8 @@ fn keyed(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("rustcrypto/blake2s128", len), data, |b, d| {
b.iter(|| {
- let mut mac = RustCryptoBlake2sMac128::new_from_slice(black_box(&key_s[..16])).unwrap();
+ let mut mac = RustCryptoBlake2sMac128::new_from_slice(black_box(&key_s[..16]))
+ .expect("valid BLAKE2 benchmark operation must succeed");
mac.update(black_box(d));
black_box(mac.finalize().into_bytes())
})
@@ -241,7 +254,8 @@ fn keyed(c: &mut Criterion) {
});
g.bench_with_input(BenchmarkId::new("rustcrypto/blake2s256", len), data, |b, d| {
b.iter(|| {
- let mut mac = RustCryptoBlake2sMac256::new_from_slice(black_box(&key_s)).unwrap();
+ let mut mac = RustCryptoBlake2sMac256::new_from_slice(black_box(&key_s))
+ .expect("valid BLAKE2 benchmark operation must succeed");
mac.update(black_box(d));
black_box(mac.finalize().into_bytes())
})
@@ -281,12 +295,12 @@ fn streaming(c: &mut Criterion) {
});
g.bench_function(format!("dryoc/blake2b256/{chunk_size}B"), |b| {
b.iter(|| {
- let mut state = crypto_generichash_init(None, 32).unwrap();
+ let mut state = crypto_generichash_init(None, 32).expect("valid BLAKE2 benchmark operation must succeed");
for chunk in data.chunks(chunk_size) {
crypto_generichash_update(&mut state, black_box(chunk));
}
let mut out = [0u8; 32];
- crypto_generichash_final(state, &mut out).unwrap();
+ crypto_generichash_final(state, &mut out).expect("valid BLAKE2 benchmark operation must succeed");
black_box(out)
})
});
diff --git a/benches/blake3.rs b/benches/blake3.rs
index 34d87f48..0cc81a25 100644
--- a/benches/blake3.rs
+++ b/benches/blake3.rs
@@ -132,7 +132,14 @@ fn oneshot(c: &mut Criterion) {
g.bench_with_input(
BenchmarkId::new(format!("rscrypto-{}", kernel.label()), len),
data,
- |b, d| b.iter(|| black_box(diag_blake3_digest_with_kernel(kernel, black_box(d)).unwrap())),
+ |b, d| {
+ b.iter(|| {
+ black_box(
+ diag_blake3_digest_with_kernel(kernel, black_box(d))
+ .expect("selected BLAKE3 benchmark kernel must be available"),
+ )
+ })
+ },
);
}
@@ -167,7 +174,12 @@ fn keyed(c: &mut Criterion) {
BenchmarkId::new(format!("rscrypto-{}", kernel.label()), len),
data,
|b, d| {
- b.iter(|| black_box(diag_blake3_keyed_digest_with_kernel(kernel, black_box(&key), black_box(d)).unwrap()))
+ b.iter(|| {
+ black_box(
+ diag_blake3_keyed_digest_with_kernel(kernel, black_box(&key), black_box(d))
+ .expect("selected BLAKE3 benchmark kernel must be available"),
+ )
+ })
},
);
}
@@ -228,7 +240,12 @@ fn streaming(c: &mut Criterion) {
continue;
}
g.bench_function(format!("rscrypto-{}/{chunk_size}B", kernel.label()), |b| {
- b.iter(|| black_box(diag_blake3_streaming_digest_with_kernel(kernel, black_box(&data), chunk_size).unwrap()))
+ b.iter(|| {
+ black_box(
+ diag_blake3_streaming_digest_with_kernel(kernel, black_box(&data), chunk_size)
+ .expect("selected BLAKE3 benchmark kernel must be available"),
+ )
+ })
});
}
@@ -279,7 +296,8 @@ fn xof(c: &mut Criterion) {
|b, d| {
b.iter(|| {
let mut out = [0u8; OUT_LEN];
- diag_blake3_xof_with_kernel(kernel, black_box(d), &mut out).unwrap();
+ diag_blake3_xof_with_kernel(kernel, black_box(d), &mut out)
+ .expect("selected BLAKE3 benchmark kernel must be available");
black_box(out)
})
},
@@ -335,7 +353,8 @@ fn xof_output(c: &mut Criterion) {
|b, &len| {
let mut out = vec![0u8; len];
b.iter(|| {
- diag_blake3_xof_with_kernel(kernel, black_box(&data), black_box(out.as_mut_slice())).unwrap();
+ diag_blake3_xof_with_kernel(kernel, black_box(&data), black_box(out.as_mut_slice()))
+ .expect("selected BLAKE3 benchmark kernel must be available");
black_box(out[0])
})
},
@@ -368,7 +387,7 @@ fn tail_diagnostics(c: &mut Criterion) {
let mut digest_group = c.benchmark_group("blake3/chunk-tail-digest");
for chunks in tail_counts.iter().copied() {
- let data = common::random_bytes(chunks * BLAKE3_CHUNK_LEN);
+ let data = common::random_bytes(chunks.strict_mul(BLAKE3_CHUNK_LEN));
common::set_throughput(&mut digest_group, data.len());
digest_group.bench_with_input(BenchmarkId::new("rscrypto", chunks), &data, |b, d| {
@@ -382,7 +401,14 @@ fn tail_diagnostics(c: &mut Criterion) {
digest_group.bench_with_input(
BenchmarkId::new(format!("rscrypto-{}", kernel.label()), chunks),
&data,
- |b, d| b.iter(|| black_box(diag_blake3_digest_with_kernel(kernel, black_box(d)).unwrap())),
+ |b, d| {
+ b.iter(|| {
+ black_box(
+ diag_blake3_digest_with_kernel(kernel, black_box(d))
+ .expect("selected BLAKE3 benchmark kernel must be available"),
+ )
+ })
+ },
);
}
@@ -394,20 +420,21 @@ fn tail_diagnostics(c: &mut Criterion) {
let mut chunk_group = c.benchmark_group("blake3/chunk-tail-cvs");
for chunks in tail_counts.iter().copied() {
- let data = common::random_bytes(chunks * BLAKE3_CHUNK_LEN);
+ let data = common::random_bytes(chunks.strict_mul(BLAKE3_CHUNK_LEN));
common::set_throughput(&mut chunk_group, data.len());
for &kernel in chunk_tail_diag_kernels() {
if !diag_blake3_kernel_available(kernel) {
continue;
}
- let mut out = vec![0u8; chunks * BLAKE3_OUT_LEN];
+ let mut out = vec![0u8; chunks.strict_mul(BLAKE3_OUT_LEN)];
chunk_group.bench_with_input(
BenchmarkId::new(format!("rscrypto-{}", kernel.label()), chunks),
&data,
|b, d| {
b.iter(|| {
- diag_blake3_chunk_cvs_with_kernel(kernel, black_box(d), black_box(out.as_mut_slice())).unwrap();
+ diag_blake3_chunk_cvs_with_kernel(kernel, black_box(d), black_box(out.as_mut_slice()))
+ .expect("selected BLAKE3 benchmark kernel must be available");
black_box(out[0])
})
},
@@ -418,20 +445,21 @@ fn tail_diagnostics(c: &mut Criterion) {
let mut parent_group = c.benchmark_group("blake3/parent-tail-cvs");
for parents in tail_counts {
- let children = common::random_bytes(parents * 2 * BLAKE3_OUT_LEN);
+ let children = common::random_bytes(parents.strict_mul(2).strict_mul(BLAKE3_OUT_LEN));
common::set_throughput(&mut parent_group, children.len());
for &kernel in parent_tail_diag_kernels() {
if !diag_blake3_kernel_available(kernel) {
continue;
}
- let mut out = vec![0u8; parents * BLAKE3_OUT_LEN];
+ let mut out = vec![0u8; parents.strict_mul(BLAKE3_OUT_LEN)];
parent_group.bench_with_input(
BenchmarkId::new(format!("rscrypto-{}", kernel.label()), parents),
&children,
|b, d| {
b.iter(|| {
- diag_blake3_parent_cvs_with_kernel(kernel, black_box(d), black_box(out.as_mut_slice())).unwrap();
+ diag_blake3_parent_cvs_with_kernel(kernel, black_box(d), black_box(out.as_mut_slice()))
+ .expect("selected BLAKE3 benchmark kernel must be available");
black_box(out[0])
})
},
diff --git a/benches/common/mod.rs b/benches/common/mod.rs
index 1acda7aa..2cb1011e 100644
--- a/benches/common/mod.rs
+++ b/benches/common/mod.rs
@@ -1,7 +1,7 @@
use criterion::{BenchmarkGroup, Throughput, measurement::WallTime};
/// Deterministic pseudo-random bytes for reproducible benchmarks.
-pub fn random_bytes(len: usize) -> Vec {
+pub(crate) fn random_bytes(len: usize) -> Vec {
let mut state: u64 = (len as u64) ^ 0x517c_c1b7_2722_0a95;
(0..len)
.map(|_| {
@@ -19,7 +19,7 @@ pub fn random_bytes(len: usize) -> Vec {
/// Covers: overhead (0B, 1B), small (32B), block boundary (64B),
/// medium (256B, 1 KiB), page-aligned (4 KiB, 16 KiB), in-cache
/// (64 KiB, 256 KiB), throughput (1 MiB).
-pub fn comp_sizes() -> Vec<(usize, Vec)> {
+pub(crate) fn comp_sizes() -> Vec<(usize, Vec)> {
[0, 1, 32, 64, 256, 1024, 4096, 16384, 65536, 262144, 1048576]
.into_iter()
.map(|len| (len, random_bytes(len)))
@@ -27,7 +27,7 @@ pub fn comp_sizes() -> Vec<(usize, Vec)> {
}
/// Set criterion throughput for a benchmark group.
-pub fn set_throughput(group: &mut BenchmarkGroup<'_, WallTime>, len: usize) {
+pub(crate) fn set_throughput(group: &mut BenchmarkGroup<'_, WallTime>, len: usize) {
if len > 0 {
group.throughput(Throughput::Bytes(len as u64));
}
diff --git a/benches/password_hashing.rs b/benches/password_hashing.rs
index c6037893..8b7ea8e2 100644
--- a/benches/password_hashing.rs
+++ b/benches/password_hashing.rs
@@ -4,12 +4,13 @@
//! Organised by cost-parameter classes so CI can run the fast groups on
//! every push and reserve the OWASP-scale group for dedicated perf runs.
-#![allow(clippy::unwrap_used)]
-
use core::{hint::black_box, time::Duration};
use criterion::{BenchmarkId, Criterion, criterion_group, criterion_main};
-use dryoc::classic::crypto_pwhash::{PasswordHashAlgorithm, crypto_pwhash};
+use dryoc::{
+ classic::crypto_pwhash::{PasswordHashAlgorithm, crypto_pwhash},
+ constants::{CRYPTO_PWHASH_ARGON2I_OPSLIMIT_MIN, CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_MIN},
+};
use rscrypto::{
Argon2Error, Argon2Params, Argon2d, Argon2i, Argon2id, Argon2idPassword, Scrypt, ScryptParams, ScryptPassword,
};
@@ -22,12 +23,13 @@ const SALT: &[u8] = b"rscrypto-bench-salt-16bytes!";
/// Build rscrypto params.
fn rs_params(m_kib: u32, t: u32, p: u32, _out_len: u32) -> Argon2Params {
- Argon2Params::new(m_kib, t, p).unwrap()
+ Argon2Params::new(m_kib, t, p).expect("supported password-hashing benchmark parameters must succeed")
}
/// Build RustCrypto oracle context.
fn oracle_ctx(algo: argon2::Algorithm, m_kib: u32, t: u32, p: u32, out_len: usize) -> argon2::Argon2<'static> {
- let params = argon2::Params::new(m_kib, t, p, Some(out_len)).unwrap();
+ let params = argon2::Params::new(m_kib, t, p, Some(out_len))
+ .expect("supported password-hashing benchmark parameters must succeed");
argon2::Argon2::new(algo, argon2::Version::V0x13, params)
}
@@ -40,6 +42,15 @@ const SMALL_MATRIX: &[(u32, u32, u32)] = &[
(64, 3, 2),
];
+fn dryoc_supports_small_row(algorithm: PasswordHashAlgorithm, time_cost: u32, parallelism: u32) -> bool {
+ let minimum_time_cost = match algorithm {
+ PasswordHashAlgorithm::Argon2i13 => CRYPTO_PWHASH_ARGON2I_OPSLIMIT_MIN,
+ PasswordHashAlgorithm::Argon2id13 => CRYPTO_PWHASH_ARGON2ID_OPSLIMIT_MIN,
+ };
+
+ parallelism == 1 && u64::from(time_cost) >= minimum_time_cost
+}
+
fn bench_small_variant(
c: &mut Criterion,
group_name: &str,
@@ -53,7 +64,12 @@ fn bench_small_variant(
for &(m, t, p) in SMALL_MATRIX {
let out_len = 32usize;
let param_id = format!("m={m}_t={t}_p={p}");
- let rs_params = rs_params(m, t, p, out_len as u32);
+ let rs_params = rs_params(
+ m,
+ t,
+ p,
+ u32::try_from(out_len).expect("benchmark output length must fit u32"),
+ );
let oracle = oracle_ctx(oracle_algo, m, t, p, out_len);
g.bench_with_input(BenchmarkId::new("rscrypto", ¶m_id), &rs_params, |b, params| {
@@ -65,7 +81,7 @@ fn bench_small_variant(
black_box(SALT),
black_box(&mut out),
)
- .unwrap()
+ .expect("supported password-hashing benchmark parameters must succeed")
});
});
@@ -74,28 +90,26 @@ fn bench_small_variant(
b.iter(|| {
ctx
.hash_password_into(black_box(PASSWORD), black_box(SALT), black_box(&mut out))
- .unwrap();
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
- // dryoc wraps libsodium's `crypto_pwhash`, which fixes parallelism at p=1.
- // Skip the row whenever the matrix asks for p>1; that case is rscrypto+rustcrypto only.
- if p == 1
- && let Some(ref alg) = dryoc_algo
- {
- let memlimit_bytes = (m as usize).saturating_mul(1024);
- g.bench_with_input(BenchmarkId::new("dryoc", ¶m_id), alg, |b, algorithm| {
+ if let Some(algorithm) = dryoc_algo.filter(|&algorithm| dryoc_supports_small_row(algorithm, t, p)) {
+ let memlimit_bytes = usize::try_from(m)
+ .expect("benchmark memory cost must fit usize")
+ .strict_mul(1024);
+ g.bench_with_input(BenchmarkId::new("dryoc", ¶m_id), &algorithm, |b, algorithm| {
let mut out = [0u8; 32];
b.iter(|| {
crypto_pwhash(
black_box(&mut out),
black_box(PASSWORD),
black_box(&SALT[..16]),
- t as u64,
+ u64::from(t),
memlimit_bytes,
- algorithm.clone(),
+ *algorithm,
)
- .unwrap();
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
}
@@ -139,7 +153,12 @@ fn argon2id_owasp(c: &mut Criterion) {
let out_len = 32usize;
// OWASP 2024: m=19MiB, t=2, p=1
- let rs_params = rs_params(19 * 1024, 2, 1, out_len as u32);
+ let rs_params = rs_params(
+ 19 * 1024,
+ 2,
+ 1,
+ u32::try_from(out_len).expect("benchmark output length must fit u32"),
+ );
let oracle = oracle_ctx(argon2::Algorithm::Argon2id, 19 * 1024, 2, 1, out_len);
g.bench_function(BenchmarkId::new("rscrypto", "m=19MiB_t=2_p=1"), |b| {
@@ -151,7 +170,7 @@ fn argon2id_owasp(c: &mut Criterion) {
black_box(SALT),
black_box(&mut out),
)
- .unwrap()
+ .expect("supported password-hashing benchmark parameters must succeed")
});
});
@@ -160,12 +179,12 @@ fn argon2id_owasp(c: &mut Criterion) {
b.iter(|| {
oracle
.hash_password_into(black_box(PASSWORD), black_box(SALT), black_box(&mut out))
- .unwrap();
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
// dryoc / libsodium-classic Argon2id at OWASP parameters (memlimit in bytes).
- let dryoc_memlimit = (19 * 1024usize).saturating_mul(1024);
+ let dryoc_memlimit = 19usize.strict_mul(1024).strict_mul(1024);
g.bench_function(BenchmarkId::new("dryoc", "m=19MiB_t=2_p=1"), |b| {
let mut out = [0u8; 32];
b.iter(|| {
@@ -177,7 +196,7 @@ fn argon2id_owasp(c: &mut Criterion) {
dryoc_memlimit,
PasswordHashAlgorithm::Argon2id13,
)
- .unwrap();
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
@@ -186,12 +205,12 @@ fn argon2id_owasp(c: &mut Criterion) {
/// Build rscrypto scrypt params.
fn rs_scrypt_params(log_n: u8, r: u32, p: u32, _out_len: u32) -> ScryptParams {
- ScryptParams::new(log_n, r, p).unwrap()
+ ScryptParams::new(log_n, r, p).expect("supported password-hashing benchmark parameters must succeed")
}
/// Build RustCrypto scrypt oracle params.
fn oracle_scrypt_params(log_n: u8, r: u32, p: u32, _out_len: usize) -> scrypt::Params {
- scrypt::Params::new(log_n, r, p).unwrap()
+ scrypt::Params::new(log_n, r, p).expect("supported password-hashing benchmark parameters must succeed")
}
/// Small / CI-friendly scrypt matrix: (log_n, r, p).
@@ -204,7 +223,12 @@ fn scrypt_small(c: &mut Criterion) {
for &(log_n, r, p) in SCRYPT_SMALL_MATRIX {
let out_len = 32usize;
let id = format!("log_n={log_n}_r={r}_p={p}");
- let rs = rs_scrypt_params(log_n, r, p, out_len as u32);
+ let rs = rs_scrypt_params(
+ log_n,
+ r,
+ p,
+ u32::try_from(out_len).expect("benchmark output length must fit u32"),
+ );
let oracle = oracle_scrypt_params(log_n, r, p, out_len);
g.bench_with_input(BenchmarkId::new("rscrypto", &id), &rs, |b, params| {
@@ -216,14 +240,15 @@ fn scrypt_small(c: &mut Criterion) {
black_box(SALT),
black_box(&mut out),
)
- .unwrap();
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
g.bench_with_input(BenchmarkId::new("rustcrypto", &id), &oracle, |b, params| {
let mut out = [0u8; 32];
b.iter(|| {
- scrypt::scrypt(black_box(PASSWORD), black_box(SALT), params, black_box(&mut out)).unwrap();
+ scrypt::scrypt(black_box(PASSWORD), black_box(SALT), params, black_box(&mut out))
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
}
@@ -238,7 +263,12 @@ fn scrypt_owasp(c: &mut Criterion) {
g.measurement_time(Duration::from_secs(30));
let out_len = 32usize;
- let rs = rs_scrypt_params(17, 8, 1, out_len as u32);
+ let rs = rs_scrypt_params(
+ 17,
+ 8,
+ 1,
+ u32::try_from(out_len).expect("benchmark output length must fit u32"),
+ );
let oracle = oracle_scrypt_params(17, 8, 1, out_len);
g.bench_function(BenchmarkId::new("rscrypto", "log_n=17_r=8_p=1"), |b| {
@@ -250,14 +280,15 @@ fn scrypt_owasp(c: &mut Criterion) {
black_box(SALT),
black_box(&mut out),
)
- .unwrap();
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
g.bench_function(BenchmarkId::new("rustcrypto", "log_n=17_r=8_p=1"), |b| {
let mut out = [0u8; 32];
b.iter(|| {
- scrypt::scrypt(black_box(PASSWORD), black_box(SALT), &oracle, black_box(&mut out)).unwrap();
+ scrypt::scrypt(black_box(PASSWORD), black_box(SALT), &oracle, black_box(&mut out))
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
@@ -270,17 +301,23 @@ fn scrypt_phc_roundtrip(c: &mut Criterion) {
g.sample_size(20);
let params = rs_scrypt_params(10, 8, 1, 32);
- let password = ScryptPassword::new(params).unwrap();
+ let password = ScryptPassword::new(params).expect("supported password-hashing benchmark parameters must succeed");
g.bench_function("hash_password", |b| {
- b.iter(|| password.hash_password(black_box(PASSWORD)).unwrap());
+ b.iter(|| {
+ password
+ .hash_password(black_box(PASSWORD))
+ .expect("supported password-hashing benchmark parameters must succeed")
+ });
});
- let encoded = password.hash_password(PASSWORD).unwrap();
+ let encoded = password
+ .hash_password(PASSWORD)
+ .expect("supported password-hashing benchmark parameters must succeed");
g.bench_function("verify_password", |b| {
b.iter(|| {
password
.verify_password(black_box(PASSWORD), black_box(&encoded))
- .unwrap()
+ .expect("supported password-hashing benchmark parameters must succeed")
});
});
@@ -298,17 +335,23 @@ fn argon2id_phc_roundtrip(c: &mut Criterion) {
g.sample_size(30);
let params = rs_params(32, 2, 1, 32);
- let password = Argon2idPassword::new(params).unwrap();
+ let password = Argon2idPassword::new(params).expect("supported password-hashing benchmark parameters must succeed");
g.bench_function("hash_password", |b| {
- b.iter(|| password.hash_password(black_box(PASSWORD)).unwrap());
+ b.iter(|| {
+ password
+ .hash_password(black_box(PASSWORD))
+ .expect("supported password-hashing benchmark parameters must succeed")
+ });
});
- let encoded = password.hash_password(PASSWORD).unwrap();
+ let encoded = password
+ .hash_password(PASSWORD)
+ .expect("supported password-hashing benchmark parameters must succeed");
g.bench_function("verify_password", |b| {
b.iter(|| {
password
.verify_password(black_box(PASSWORD), black_box(&encoded))
- .unwrap()
+ .expect("supported password-hashing benchmark parameters must succeed")
});
});
@@ -336,7 +379,12 @@ fn argon2id_parallel_scaling(c: &mut Criterion) {
for &p in &[1u32, 4, 8, 16] {
let id = format!("p={p}");
- let params = rs_params(m_kib, t, p, out_len as u32);
+ let params = rs_params(
+ m_kib,
+ t,
+ p,
+ u32::try_from(out_len).expect("benchmark output length must fit u32"),
+ );
g.bench_with_input(BenchmarkId::new("rscrypto", &id), ¶ms, |b, params| {
let mut out = [0u8; 32];
@@ -347,7 +395,7 @@ fn argon2id_parallel_scaling(c: &mut Criterion) {
black_box(SALT),
black_box(&mut out),
)
- .unwrap();
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
}
@@ -369,7 +417,12 @@ fn argon2id_parallel_owasp(c: &mut Criterion) {
for &p in &[1u32, 4, 8, 16] {
let id = format!("p={p}");
- let params = rs_params(m_kib, t, p, out_len as u32);
+ let params = rs_params(
+ m_kib,
+ t,
+ p,
+ u32::try_from(out_len).expect("benchmark output length must fit u32"),
+ );
g.bench_with_input(BenchmarkId::new("rscrypto", &id), ¶ms, |b, params| {
let mut out = [0u8; 32];
@@ -380,7 +433,7 @@ fn argon2id_parallel_owasp(c: &mut Criterion) {
black_box(SALT),
black_box(&mut out),
)
- .unwrap();
+ .expect("supported password-hashing benchmark parameters must succeed");
});
});
}
diff --git a/benches/rsa.rs b/benches/rsa.rs
index 14ec2cd5..831f52b7 100644
--- a/benches/rsa.rs
+++ b/benches/rsa.rs
@@ -61,38 +61,45 @@ const RSA8192_PKCS1V15_SHA256: &[u8] = include_bytes!("rsa_fixtures/rsa8192_pkcs
fn hex_to_vec(hex: &str) -> Vec {
let mut out = Vec::with_capacity(hex.len() / 2);
for chunk in hex.as_bytes().chunks_exact(2) {
- out.push((hex_value(chunk[0]) << 4) | hex_value(chunk[1]));
+ let high = hex_value(chunk[0]).expect("RSA benchmark fixture must contain hexadecimal digits");
+ let low = hex_value(chunk[1]).expect("RSA benchmark fixture must contain hexadecimal digits");
+ out.push((high << 4) | low);
}
out
}
-fn hex_value(byte: u8) -> u8 {
+fn hex_value(byte: u8) -> Option {
match byte {
- b'0'..=b'9' => byte - b'0',
- b'a'..=b'f' => byte - b'a' + 10,
- b'A'..=b'F' => byte - b'A' + 10,
- _ => panic!("invalid hex digit"),
+ b'0'..=b'9' => Some(byte.strict_sub(b'0')),
+ b'a'..=b'f' => Some(byte.strict_sub(b'a').strict_add(10)),
+ b'A'..=b'F' => Some(byte.strict_sub(b'A').strict_add(10)),
+ _ => None,
}
}
fn der_len(len: usize) -> Vec {
if len < 128 {
- return vec![len as u8];
+ return vec![u8::try_from(len).expect("short DER length must fit in one byte")];
}
let bytes = len.to_be_bytes();
- let first_nonzero = bytes.iter().position(|&byte| byte != 0).unwrap();
+ let first_nonzero = bytes
+ .iter()
+ .position(|&byte| byte != 0)
+ .expect("long DER length must contain a non-zero byte");
let len_bytes = &bytes[first_nonzero..];
- let mut out = Vec::with_capacity(1 + len_bytes.len());
- out.push(0x80 | len_bytes.len() as u8);
+ let mut out = Vec::with_capacity(1usize.strict_add(len_bytes.len()));
+ out.push(0x80 | u8::try_from(len_bytes.len()).expect("DER length-of-length must fit in one byte"));
out.extend_from_slice(len_bytes);
out
}
fn tlv(tag: u8, value: &[u8]) -> Vec {
- let mut out = Vec::with_capacity(1 + der_len(value.len()).len() + value.len());
+ let encoded_len = der_len(value.len());
+ let capacity = 1usize.strict_add(encoded_len.len()).strict_add(value.len());
+ let mut out = Vec::with_capacity(capacity);
out.push(tag);
- out.extend_from_slice(&der_len(value.len()));
+ out.extend_from_slice(&encoded_len);
out.extend_from_slice(value);
out
}
@@ -104,7 +111,7 @@ fn sequence(value: &[u8]) -> Vec {
fn integer_unsigned(value: &[u8]) -> Vec {
let first_nonzero = value.iter().position(|&byte| byte != 0);
let value = first_nonzero.map_or(&[0u8][..], |index| &value[index..]);
- let mut encoded = Vec::with_capacity(value.len() + usize::from(value[0] & 0x80 != 0));
+ let mut encoded = Vec::with_capacity(value.len().strict_add(usize::from(value[0] & 0x80 != 0)));
if value[0] & 0x80 != 0 {
encoded.push(0);
}
@@ -114,7 +121,10 @@ fn integer_unsigned(value: &[u8]) -> Vec {
fn exponent_bytes(exponent: u64) -> Vec {
let bytes = exponent.to_be_bytes();
- let first_nonzero = bytes.iter().position(|&byte| byte != 0).unwrap_or(bytes.len() - 1);
+ let first_nonzero = bytes
+ .iter()
+ .position(|&byte| byte != 0)
+ .unwrap_or_else(|| bytes.len().strict_sub(1));
bytes[first_nonzero..].to_vec()
}
@@ -240,9 +250,11 @@ fn rsa_components_for_size(
pkcs1_sig: &[u8],
import_policy: &RsaPublicKeyPolicy,
) {
- let pss_key = RsaPublicKey::from_spki_der_with_policy(pss_spki, import_policy).unwrap();
+ let pss_key =
+ RsaPublicKey::from_spki_der_with_policy(pss_spki, import_policy).expect("valid RSA benchmark fixture must succeed");
let mut pss_scratch = pss_key.public_scratch();
- let pkcs1_key = RsaPublicKey::from_spki_der_with_policy(pkcs1_spki, import_policy).unwrap();
+ let pkcs1_key = RsaPublicKey::from_spki_der_with_policy(pkcs1_spki, import_policy)
+ .expect("valid RSA benchmark fixture must succeed");
let mut pkcs1_scratch = pkcs1_key.public_scratch();
let pss_pkcs1 = pkcs1_der_from_key(&pss_key);
let pkcs1_pkcs1 = pkcs1_der_from_key(&pkcs1_key);
@@ -254,7 +266,7 @@ fn rsa_components_for_size(
let mut pss_encoded = vec![0u8; pss_key.modulus().len()];
pss_key
.public_operation_with_scratch(pss_sig, &mut pss_encoded, &mut pss_scratch)
- .unwrap();
+ .expect("valid RSA benchmark fixture must succeed");
let pss_em_bits = pss_key.modulus_bits().strict_sub(1);
let pss_em_len = pss_em_bits.strict_add(7) / 8;
let leading = pss_encoded.len().strict_sub(pss_em_len);
@@ -263,7 +275,7 @@ fn rsa_components_for_size(
let mut pkcs1_encoded = vec![0u8; pkcs1_key.modulus().len()];
pkcs1_key
.public_operation_with_scratch(pkcs1_sig, &mut pkcs1_encoded, &mut pkcs1_scratch)
- .unwrap();
+ .expect("valid RSA benchmark fixture must succeed");
(
pss_encoded,
@@ -286,20 +298,31 @@ fn rsa_components_for_size(
let rustcrypto_pss_key = RustCryptoRsaPublicKey::from_public_key_der(pss_spki)
.ok()
.map(RustCryptoPssVerifyingKey::::new);
- let rustcrypto_pss_sig = RustCryptoPssSignature::try_from(pss_sig).unwrap();
+ let rustcrypto_pss_sig = RustCryptoPssSignature::try_from(pss_sig).expect("valid RSA benchmark fixture must succeed");
let rustcrypto_pkcs1_key = RustCryptoRsaPublicKey::from_public_key_der(pkcs1_spki)
.ok()
.map(RustCryptoPkcs1v15VerifyingKey::::new);
- let rustcrypto_pkcs1_sig = RustCryptoPkcs1v15Signature::try_from(pkcs1_sig).unwrap();
+ let rustcrypto_pkcs1_sig =
+ RustCryptoPkcs1v15Signature::try_from(pkcs1_sig).expect("valid RSA benchmark fixture must succeed");
let mut group = c.benchmark_group(name);
group.bench_function("parse-spki-rscrypto", |b| {
- b.iter(|| black_box(RsaPublicKey::from_spki_der_with_policy(black_box(pss_spki), import_policy).unwrap()))
+ b.iter(|| {
+ black_box(
+ RsaPublicKey::from_spki_der_with_policy(black_box(pss_spki), import_policy)
+ .expect("valid RSA benchmark fixture must succeed"),
+ )
+ })
});
if rustcrypto_pss_key.is_some() {
group.bench_function("parse-spki-rustcrypto-rsa", |b| {
- b.iter(|| black_box(RustCryptoRsaPublicKey::from_public_key_der(black_box(pss_spki)).unwrap()))
+ b.iter(|| {
+ black_box(
+ RustCryptoRsaPublicKey::from_public_key_der(black_box(pss_spki))
+ .expect("valid RSA benchmark fixture must succeed"),
+ )
+ })
});
}
group.bench_function("scratch-setup-rscrypto", |b| {
@@ -313,7 +336,7 @@ fn rsa_components_for_size(
black_box(&mut out),
black_box(&mut pss_scratch),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -328,7 +351,7 @@ fn rsa_components_for_size(
black_box(&mut out),
black_box(&mut product_scratch),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("public-op-e65537-cios-candidate", |b| {
@@ -339,7 +362,7 @@ fn rsa_components_for_size(
black_box(&mut out),
black_box(&mut cios_scratch),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
}
@@ -354,7 +377,7 @@ fn rsa_components_for_size(
black_box(&mut pss_db),
black_box(&mut pss_db_mask),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -365,7 +388,7 @@ fn rsa_components_for_size(
black_box(MESSAGE_PKCS1V15),
black_box(&pkcs1_encoded),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("verify-pss-sha256-rscrypto", |b| {
@@ -377,22 +400,22 @@ fn rsa_components_for_size(
black_box(pss_sig),
black_box(&mut pss_scratch),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("verify-pss-sha256-rscrypto-oneshot", |b| {
b.iter(|| {
pss_key
.verify_pss(RsaPssProfile::Sha256, black_box(MESSAGE_PSS), black_box(pss_sig))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("verify-pss-sha256-rscrypto-cold", |b| {
b.iter(|| {
RsaPublicKey::from_spki_der_with_policy(black_box(pss_spki), import_policy)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
.verify_pss(RsaPssProfile::Sha256, black_box(MESSAGE_PSS), black_box(pss_sig))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
if let Some(rustcrypto_pss_key) = rustcrypto_pss_key {
@@ -400,16 +423,20 @@ fn rsa_components_for_size(
b.iter(|| {
rustcrypto_pss_key
.verify(black_box(MESSAGE_PSS), black_box(&rustcrypto_pss_sig))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
}
group.bench_function("verify-pss-sha256-ring", |b| {
- b.iter(|| ring_pss_key.verify(black_box(MESSAGE_PSS), black_box(pss_sig)).unwrap())
+ b.iter(|| {
+ ring_pss_key
+ .verify(black_box(MESSAGE_PSS), black_box(pss_sig))
+ .expect("valid RSA benchmark fixture must succeed")
+ })
});
aws_lc_bench! {
group.bench_function("verify-pss-sha256-aws-lc-rs", |b| {
- b.iter(|| aws_pss_key.verify(black_box(MESSAGE_PSS), black_box(pss_sig)).unwrap())
+ b.iter(|| aws_pss_key.verify(black_box(MESSAGE_PSS), black_box(pss_sig)).expect("valid RSA benchmark fixture must succeed"))
});
}
group.bench_function("verify-pkcs1v15-sha256-rscrypto", |b| {
@@ -421,7 +448,7 @@ fn rsa_components_for_size(
black_box(pkcs1_sig),
black_box(&mut pkcs1_scratch),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("verify-pkcs1v15-sha256-rscrypto-oneshot", |b| {
@@ -432,19 +459,19 @@ fn rsa_components_for_size(
black_box(MESSAGE_PKCS1V15),
black_box(pkcs1_sig),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("verify-pkcs1v15-sha256-rscrypto-cold", |b| {
b.iter(|| {
RsaPublicKey::from_spki_der_with_policy(black_box(pkcs1_spki), import_policy)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
.verify_pkcs1v15(
RsaPkcs1v15Profile::Sha256,
black_box(MESSAGE_PKCS1V15),
black_box(pkcs1_sig),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
if let Some(rustcrypto_pkcs1_key) = rustcrypto_pkcs1_key {
@@ -452,7 +479,7 @@ fn rsa_components_for_size(
b.iter(|| {
rustcrypto_pkcs1_key
.verify(black_box(MESSAGE_PKCS1V15), black_box(&rustcrypto_pkcs1_sig))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
}
@@ -460,7 +487,7 @@ fn rsa_components_for_size(
b.iter(|| {
ring_pkcs1_key
.verify(black_box(MESSAGE_PKCS1V15), black_box(pkcs1_sig))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
aws_lc_bench! {
@@ -468,7 +495,7 @@ fn rsa_components_for_size(
b.iter(|| {
aws_pkcs1_key
.verify(black_box(MESSAGE_PKCS1V15), black_box(pkcs1_sig))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
}
@@ -481,18 +508,18 @@ fn rsa_public_exponents(c: &mut Criterion) {
let input = legacy_exponent_input();
let policy = RsaPublicKeyPolicy::legacy_verification().allow_legacy_small_exponents();
- let key_e3 =
- RsaPublicKey::from_pkcs1_der_with_policy(&pkcs1_der_from_modulus_exponent(&modulus, &[0x03]), &policy).unwrap();
- let key_e17 =
- RsaPublicKey::from_pkcs1_der_with_policy(&pkcs1_der_from_modulus_exponent(&modulus, &[0x11]), &policy).unwrap();
+ let key_e3 = RsaPublicKey::from_pkcs1_der_with_policy(&pkcs1_der_from_modulus_exponent(&modulus, &[0x03]), &policy)
+ .expect("valid RSA benchmark fixture must succeed");
+ let key_e17 = RsaPublicKey::from_pkcs1_der_with_policy(&pkcs1_der_from_modulus_exponent(&modulus, &[0x11]), &policy)
+ .expect("valid RSA benchmark fixture must succeed");
let key_e65537 =
RsaPublicKey::from_pkcs1_der_with_policy(&pkcs1_der_from_modulus_exponent(&modulus, &[0x01, 0x00, 0x01]), &policy)
- .unwrap();
+ .expect("valid RSA benchmark fixture must succeed");
let key_generic = RsaPublicKey::from_pkcs1_der_with_policy(
&pkcs1_der_from_modulus_exponent(&modulus, &[0x49, 0xd2, 0xa1]),
&policy.allow_legacy_odd_exponents(),
)
- .unwrap();
+ .expect("valid RSA benchmark fixture must succeed");
let mut scratch_e3 = key_e3.public_scratch();
let mut scratch_e17 = key_e17.public_scratch();
@@ -527,12 +554,15 @@ fn rsa_public_exponents(c: &mut Criterion) {
b.iter(|| {
key_e3
.public_operation_with_scratch(black_box(&input), black_box(&mut out), black_box(&mut scratch_e3))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
group.bench_function("public-op-e3-bitserial-baseline", |b| {
- b.iter(|| diag_rsa_public_operation_bitserial(black_box(&key_e3), black_box(&input), black_box(&mut out)).unwrap())
+ b.iter(|| {
+ diag_rsa_public_operation_bitserial(black_box(&key_e3), black_box(&input), black_box(&mut out))
+ .expect("valid RSA benchmark fixture must succeed")
+ })
});
#[cfg(feature = "diag")]
group.bench_function("public-op-e3-product-montgomery", |b| {
@@ -543,7 +573,7 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut product_scratch_e3),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -555,7 +585,7 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut generic_scratch_e3),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -567,19 +597,22 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut cios_scratch_e3),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("public-op-e17", |b| {
b.iter(|| {
key_e17
.public_operation_with_scratch(black_box(&input), black_box(&mut out), black_box(&mut scratch_e17))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
group.bench_function("public-op-e17-bitserial-baseline", |b| {
- b.iter(|| diag_rsa_public_operation_bitserial(black_box(&key_e17), black_box(&input), black_box(&mut out)).unwrap())
+ b.iter(|| {
+ diag_rsa_public_operation_bitserial(black_box(&key_e17), black_box(&input), black_box(&mut out))
+ .expect("valid RSA benchmark fixture must succeed")
+ })
});
#[cfg(feature = "diag")]
group.bench_function("public-op-e17-product-montgomery", |b| {
@@ -590,7 +623,7 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut product_scratch_e17),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -602,7 +635,7 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut generic_scratch_e17),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -614,20 +647,21 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut cios_scratch_e17),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("public-op-e65537", |b| {
b.iter(|| {
key_e65537
.public_operation_with_scratch(black_box(&input), black_box(&mut out), black_box(&mut scratch_e65537))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
group.bench_function("public-op-e65537-bitserial-baseline", |b| {
b.iter(|| {
- diag_rsa_public_operation_bitserial(black_box(&key_e65537), black_box(&input), black_box(&mut out)).unwrap()
+ diag_rsa_public_operation_bitserial(black_box(&key_e65537), black_box(&input), black_box(&mut out))
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -639,7 +673,7 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut product_scratch_e65537),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -651,7 +685,7 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut generic_scratch_e65537),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -663,20 +697,21 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut cios_scratch_e65537),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function("public-op-e0x49d2a1-generic", |b| {
b.iter(|| {
key_generic
.public_operation_with_scratch(black_box(&input), black_box(&mut out), black_box(&mut scratch_generic))
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
group.bench_function("public-op-e0x49d2a1-bitserial-baseline", |b| {
b.iter(|| {
- diag_rsa_public_operation_bitserial(black_box(&key_generic), black_box(&input), black_box(&mut out)).unwrap()
+ diag_rsa_public_operation_bitserial(black_box(&key_generic), black_box(&input), black_box(&mut out))
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -688,7 +723,7 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut product_scratch_generic),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
#[cfg(feature = "diag")]
@@ -700,7 +735,7 @@ fn rsa_public_exponents(c: &mut Criterion) {
black_box(&mut out),
black_box(&mut cios_scratch_generic),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.finish();
@@ -748,7 +783,7 @@ fn rsa_montgomery_thresholds(c: &mut Criterion) {
&synthetic_pkcs1_der(modulus_len),
&RsaPublicKeyPolicy::legacy_verification(),
)
- .unwrap();
+ .expect("valid RSA benchmark fixture must succeed");
let input = modulus_minus_one(&key);
let mut out_auto = vec![0u8; key.modulus().len()];
let mut out_product = vec![0u8; key.modulus().len()];
@@ -767,7 +802,7 @@ fn rsa_montgomery_thresholds(c: &mut Criterion) {
black_box(&mut out_auto),
black_box(&mut scratch_auto),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function(format!("{name}/product-montgomery"), |b| {
@@ -778,7 +813,7 @@ fn rsa_montgomery_thresholds(c: &mut Criterion) {
black_box(&mut out_product),
black_box(&mut scratch_product),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function(format!("{name}/cios-candidate"), |b| {
@@ -789,7 +824,7 @@ fn rsa_montgomery_thresholds(c: &mut Criterion) {
black_box(&mut out_cios),
black_box(&mut scratch_cios),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
group.bench_function(format!("{name}/cios-portable"), |b| {
@@ -800,7 +835,7 @@ fn rsa_montgomery_thresholds(c: &mut Criterion) {
black_box(&mut out_cios_portable),
black_box(&mut scratch_cios_portable),
)
- .unwrap()
+ .expect("valid RSA benchmark fixture must succeed")
})
});
}
diff --git a/benches/sha2.rs b/benches/sha2.rs
index 293da66c..3205c8f4 100644
--- a/benches/sha2.rs
+++ b/benches/sha2.rs
@@ -200,7 +200,7 @@ fn sha256_internal(c: &mut Criterion) {
let mut g = c.benchmark_group("sha256/internal/compress");
for block_count in [1usize, 2, 16] {
- let len = block_count * 64;
+ let len = block_count.strict_mul(64);
common::set_throughput(&mut g, len);
g.bench_with_input(BenchmarkId::new("selected-kernel", format!("{len}B")), &len, |b, &n| {
let blocks = &blocks[..n];
diff --git a/benchmark_results/OVERVIEW.md b/benchmark_results/OVERVIEW.md
index a7fad017..f55021e3 100644
--- a/benchmark_results/OVERVIEW.md
+++ b/benchmark_results/OVERVIEW.md
@@ -2,25 +2,35 @@
Sources:
-- Linux benchmark CI run [#28710784737](https://github.com/loadingalias/rscrypto/actions/runs/28710784737), created 2026-07-04 15:26:12 UTC.
-- Linux commit: `596498f0e07e869eac71fd31c157aa1b22186239`.
-- Linux artifacts: nine successful `benchmark-*` artifacts extracted into `benchmark_results/2026-07-04/linux/*/results.txt`.
-- Local macOS run: `benchmark_results/2026-07-04/macos/aarch64/results.txt` at commit `596498f0e07e869eac71fd31c157aa1b22186239`.
+- Linux benchmark CI run [#32185659553](https://github.com/loadingalias/rscrypto/actions/runs/32185659553), created 2026-08-18 21:03:07 UTC.
+- Linux commit: `7eb44e9a38ef7a031d9181dc8c4c0fad38f46504`.
+- Linux artifacts: eight successful `benchmark-*` artifacts extracted into `benchmark_results/2026-08-18/linux/*/results.txt`.
+- Local macOS run: `benchmark_results/2026-07-04/macos/aarch64/results.txt` at commit `596498f0e07e869eac71fd31c157aa1b22186239`, carried forward unchanged.
- Local Ed25519 direct-secret before/after diagnostic, recorded below.
-Scope: the 2026-07-04 nine-runner Linux CI benchmark matrix for commit `596498f`. Ratios are `external_crate_time / rscrypto_time`; higher is better. Wins are `>1.05x`, ties are `0.95x..1.05x`, and losses are `<0.95x`. Fastest-external comparisons keep only the fastest external implementation for each platform, primitive, operation, and input shape. Internal kernel, scratch-buffer, padding-only, cold-path, PHC roundtrip, parallel-scaling, threshold-selection, public-overhead, and phase-attribution microbenches are parsed as raw rows but excluded from external win/loss claims. The macOS local run is listed separately and is not mixed into Linux CI claims.
+Scope: the 2026-08-18 eight-runner Linux CI benchmark matrix for commit `7eb44e9`. Ratios are `external_crate_time / rscrypto_time`; higher is better. Wins are `>1.05x`, ties are `0.95x..1.05x`, and losses are `<0.95x`. Fastest-external comparisons keep only the fastest external implementation for each platform, primitive, operation, and input shape. Internal kernel, scratch-buffer, padding-only, cold-path, PHC roundtrip, parallel-scaling, threshold-selection, public-overhead, and phase-attribution microbenches are parsed as raw rows but excluded from external win/loss claims. The macOS local run is listed separately and is not mixed into Linux CI claims.
-This is a historical snapshot of commit `596498f`, not an inventory of the
+This is a historical snapshot of commit `7eb44e9`, not an inventory of the
current public API. Primitive rows remain as measured even when a later commit
changes or removes that surface.
-Equivalence correction: the historical RustCrypto HMAC-SHA-256 rows include
-key setup inside the timed loop, while rscrypto, ring, and AWS-LC use reusable
-keyed state. Consequently, the HMAC-SHA-256 row and aggregate statistics that
-include it are archival measurements, not equivalent-work performance claims.
-The current benchmark source precomputes the RustCrypto state; publish new
-HMAC-SHA-256 and aggregate ratios only after a complete benchmark artifact is
-regenerated.
+Runner coverage change: this run has eight Linux runners. The RISE RISC-V
+runner did not execute in run #32185659553, so every aggregate below is over
+eight platforms rather than the nine in the 2026-07-04 snapshot. Row counts are
+therefore not directly comparable to that snapshot; ratios and geomeans are.
+
+Equivalence correction resolved: the historical RustCrypto HMAC-SHA-256 rows
+included key setup inside the timed loop. The current benchmark source hoists
+`RustCryptoHmacSha256::new_from_slice` out of the timed loop and clones the
+keyed state per iteration, matching the reusable-keyed-state treatment given to
+rscrypto, ring, and AWS-LC. This artifact is a complete regenerated benchmark
+pass, so the HMAC-SHA-256 rows and the aggregates that include them are
+equivalent-work performance claims.
+
+Surface change since 2026-07-04: the rapidhash benchmark surface was collapsed.
+The former `rapidhash-64`, `rapidhash-128`, and `rapidhash-v3-128` primitives no
+longer exist; `rapidhash-v3-64`, `rapidhash-stream`, `rapidhash-buildhasher`,
+`rapidhash-hash-one`, and `rapidhash-hashmap` are the current rows.
Coverage note: this is a full Linux CI public benchmark pass. It includes checksum, hash, XOF, MAC, KDF, password-hashing, BLAKE2/BLAKE3, RSA import/verification, ECDSA P-256/P-384 signing and verification, Ed25519, X25519, AEAD, and ML-KEM-512/768/1024 keygen, encapsulation, and decapsulation rows. ML-KEM phase/arithmetic microbenches are present in the raw artifacts and intentionally excluded from release-level competitor claims.
@@ -60,117 +70,121 @@ curation:
| Scope | Pairs | W/T/L | Win % | Geomean | Median |
| --- | --- | --- | --- | --- | --- |
-| Linux CI: all matched performance pairs | 10,781 | 7,542/2,470/769 | 70% | 1.76x | 1.22x |
-| Linux CI: fastest external per case | 6,750 | 4,052/2,049/649 | 60% | 1.59x | 1.11x |
+| Linux CI: all matched performance pairs | 9,674 | 6,831/2,085/758 | 71% | 1.78x | 1.24x |
+| Linux CI: fastest external per case | 6,144 | 3,780/1,695/669 | 62% | 1.62x | 1.12x |
Snapshot summary:
-- **Headline:** 4,052 of 6,750 matched Linux CI fastest-external comparisons are wins; 6,101 are wins or ties. Linux CI fastest-external geomean is 1.59x.
-- **Checksums:** 5.18x geomean across 693 fastest-external rows; W/T/L is 517/115/61.
-- **Hashes/MACs/XOFs:** 1.35x geomean across 3,726 fastest-external rows; W/T/L is 2,028/1,457/241.
-- **Auth/KDF:** 1.25x geomean across 180 fastest-external rows; W/T/L is 159/20/1.
-- **Password hashing:** 1.07x geomean across 135 fastest-external rows; W/T/L is 69/28/38.
-- **Public-key:** 1.33x geomean across 333 fastest-external rows; W/T/L is 217/73/43.
-- **RSA:** 1.55x geomean across 99 fastest-external rows; W/T/L is 89/2/8.
-- **AEAD:** 1.56x geomean across 1,584 fastest-external rows; W/T/L is 973/354/257.
-- **ML-KEM:** 1.49x geomean across 81 fastest-external rows; W/T/L is 68/4/9.
-- **ECDSA P-256/P-384:** Linux CI 1.45x geomean across 144 fastest-external rows; W/T/L is 116/7/21.
-- **Top current loss areas:** `argon2id-owasp` / `hash`: 0.97x geomean across 9 rows; W/T/L 4/1/4; pressure `rustcrypto` 4; `ed25519` / `verify`: 1.00x geomean across 36 rows; W/T/L 7/20/9; pressure `ring` 6, `dalek` 3; `mlkem1024` / `keygen`: 1.00x geomean across 9 rows; W/T/L 6/0/3; pressure `aws-lc-rs` 3; `blake2b256` / `streaming`: 1.02x geomean across 27 rows; W/T/L 10/16/1; pressure `rustcrypto` 1; `argon2id-small` / `hash`: 1.02x geomean across 27 rows; W/T/L 12/4/11; pressure `rustcrypto` 11.
+- **Headline:** 3,780 of 6,144 matched Linux CI fastest-external comparisons are wins; 5,475 are wins or ties. Linux CI fastest-external geomean is 1.62x.
+- **Checksums:** 6.18x geomean across 616 fastest-external rows; W/T/L is 476/118/22.
+- **Hashes/MACs/XOFs:** 1.35x geomean across 3,456 fastest-external rows; W/T/L is 1,926/1,181/349.
+- **Auth/KDF:** 1.28x geomean across 160 fastest-external rows; W/T/L is 140/20/0.
+- **Password hashing:** 1.07x geomean across 120 fastest-external rows; W/T/L is 55/27/38.
+- **Public-key:** 1.09x geomean across 296 fastest-external rows; W/T/L is 187/59/50.
+- **RSA:** 1.65x geomean across 88 fastest-external rows; W/T/L is 86/2/0.
+- **AEAD:** 1.61x geomean across 1,408 fastest-external rows; W/T/L is 910/288/210.
+- **ML-KEM:** 1.55x geomean across 72 fastest-external rows; W/T/L is 64/0/8.
+- **ECDSA P-256/P-384:** Linux CI 0.87x geomean across 128 fastest-external rows; W/T/L is 88/7/33.
+- **Top current loss areas:** `ecdsa-p384` / `sign`: 0.70x geomean across 32 rows; W/T/L is 12/0/20; pressure `aws-lc-rs` 16, `rustcrypto-p384` 4; `ecdsa-p256` / `verify`: 0.84x geomean across 32 rows; W/T/L is 20/7/5; pressure `rustcrypto-p256` 4, `aws-lc-rs` 1; `rapidhash-stream` / `one-write`: 0.87x geomean across 88 rows; W/T/L is 27/25/36; pressure `rapidhash` 36; `ecdsa-p256` / `sign`: 0.91x geomean across 32 rows; W/T/L is 28/0/4; pressure `ring` 4; `argon2id-owasp` / `hash`: 0.98x geomean across 8 rows; W/T/L is 3/1/4; pressure `rustcrypto` 3, `dryoc` 1.
## Coverage Matrix
| Platform | Raw Criterion rows | All pairs | Fastest rows | W/T/L | Win % | Geomean | Median |
| --- | --- | --- | --- | --- | --- | --- | --- |
-| AMD Zen4 | 2,356 | 1,251 | 750 | 513/189/48 | 68% | 1.50x | 1.15x |
-| AMD Zen5 | 2,356 | 1,251 | 750 | 434/249/67 | 58% | 1.49x | 1.09x |
-| AWS Graviton3 | 2,367 | 1,251 | 750 | 360/298/92 | 48% | 1.37x | 1.04x |
-| AWS Graviton4 | 2,367 | 1,251 | 750 | 362/332/56 | 48% | 1.37x | 1.05x |
-| IBM Power10 | 2,107 | 1,012 | 750 | 389/324/37 | 52% | 1.89x | 1.06x |
-| IBM z16/s390x | 2,107 | 1,012 | 750 | 603/87/60 | 80% | 3.15x | 2.34x |
-| Intel Ice Lake | 2,356 | 1,251 | 750 | 523/154/73 | 70% | 1.49x | 1.19x |
-| Intel Sapphire Rapids | 2,356 | 1,251 | 750 | 533/149/68 | 71% | 1.63x | 1.20x |
-| RISE RISC-V | 2,356 | 1,251 | 750 | 335/267/148 | 45% | 1.09x | 1.03x |
+| AMD Zen4 | 2,304 | 1,269 | 768 | 525/171/72 | 68% | 1.47x | 1.14x |
+| AMD Zen5 | 2,304 | 1,269 | 768 | 447/245/76 | 58% | 1.47x | 1.10x |
+| AWS Graviton3 | 2,308 | 1,269 | 768 | 367/287/114 | 48% | 1.36x | 1.04x |
+| AWS Graviton4 | 2,308 | 1,269 | 768 | 366/337/65 | 48% | 1.37x | 1.04x |
+| IBM Power10 | 2,055 | 1,030 | 768 | 400/302/66 | 52% | 1.83x | 1.06x |
+| IBM z16/s390x | 2,055 | 1,030 | 768 | 620/67/81 | 81% | 2.77x | 2.19x |
+| Intel Ice Lake | 2,304 | 1,269 | 768 | 517/137/114 | 67% | 1.45x | 1.17x |
+| Intel Sapphire Rapids | 2,304 | 1,269 | 768 | 538/149/81 | 70% | 1.60x | 1.18x |
## Category Summary
| Category | Rows | W/T/L | Win % | Geomean | Median |
| --- | --- | --- | --- | --- | --- |
-| Checksums | 693 | 517/115/61 | 75% | 5.18x | 2.44x |
-| Hashes/MACs/XOFs | 3,726 | 2,028/1,457/241 | 54% | 1.35x | 1.07x |
-| Auth/KDF | 180 | 159/20/1 | 88% | 1.25x | 1.13x |
-| Password hashing | 135 | 69/28/38 | 51% | 1.07x | 1.05x |
-| Public-key | 333 | 217/73/43 | 65% | 1.33x | 1.17x |
-| RSA | 99 | 89/2/8 | 90% | 1.55x | 1.18x |
-| AEAD | 1,584 | 973/354/257 | 61% | 1.56x | 1.15x |
+| Checksums | 616 | 476/118/22 | 77% | 6.18x | 3.17x |
+| Hashes/MACs/XOFs | 3,456 | 1,926/1,181/349 | 56% | 1.35x | 1.08x |
+| Auth/KDF | 160 | 140/20/0 | 88% | 1.28x | 1.13x |
+| Password hashing | 120 | 55/27/38 | 46% | 1.07x | 1.02x |
+| Public-key | 296 | 187/59/50 | 63% | 1.09x | 1.14x |
+| RSA | 88 | 86/2/0 | 98% | 1.65x | 1.20x |
+| AEAD | 1,408 | 910/288/210 | 65% | 1.61x | 1.21x |
## BLAKE3 Summary
-BLAKE3 rows come from Linux CI run [#28710784737](https://github.com/loadingalias/rscrypto/actions/runs/28710784737). All-pair and fastest-external BLAKE3 metrics are identical because official `blake3` is the only external implementation in this bench.
+BLAKE3 rows come from Linux CI run [#32185659553](https://github.com/loadingalias/rscrypto/actions/runs/32185659553). All-pair and fastest-external BLAKE3 metrics are identical because official `blake3` is the only external implementation in this bench.
| Scope | Rows | W/T/L | Geomean | Median |
| --- | --- | --- | --- | --- |
-| All Linux BLAKE3 rows | 432 | 234/168/30 | 1.41x | 1.08x |
-| x86_64 | 192 | 92/90/10 | 1.26x | 1.05x |
-| AArch64 | 96 | 44/46/6 | 1.44x | 1.05x |
+| All Linux BLAKE3 rows | 384 | 187/134/63 | 1.35x | 1.04x |
+| x86_64 | 192 | 79/89/24 | 1.18x | 1.02x |
+| AArch64 | 96 | 44/36/16 | 1.40x | 1.04x |
| Platform | Rows | W/T/L | Geomean | Median |
| --- | --- | --- | --- | --- |
-| AMD Zen4 | 48 | 23/23/2 | 1.34x | 1.05x |
-| AMD Zen5 | 48 | 21/25/2 | 1.32x | 1.03x |
-| AWS Graviton3 | 48 | 22/21/5 | 1.40x | 0.99x |
-| AWS Graviton4 | 48 | 22/25/1 | 1.47x | 1.05x |
-| IBM Power10 | 48 | 39/9/0 | 1.98x | 1.85x |
-| IBM z16/s390x | 48 | 36/5/7 | 1.81x | 2.10x |
-| Intel Ice Lake | 48 | 19/26/3 | 1.17x | 1.02x |
-| Intel Sapphire Rapids | 48 | 29/16/3 | 1.24x | 1.08x |
-| RISE RISC-V | 48 | 23/18/7 | 1.15x | 1.02x |
+| AMD Zen4 | 48 | 20/22/6 | 1.24x | 1.01x |
+| AMD Zen5 | 48 | 18/27/3 | 1.27x | 1.02x |
+| AWS Graviton3 | 48 | 22/15/11 | 1.36x | 0.98x |
+| AWS Graviton4 | 48 | 22/21/5 | 1.44x | 1.04x |
+| IBM Power10 | 48 | 32/6/10 | 1.76x | 1.12x |
+| IBM z16/s390x | 48 | 32/3/13 | 1.69x | 1.69x |
+| Intel Ice Lake | 48 | 19/21/8 | 1.09x | 1.00x |
+| Intel Sapphire Rapids | 48 | 22/19/7 | 1.13x | 1.03x |
| Operation | Rows | W/T/L | Geomean | Median |
| --- | --- | --- | --- | --- |
-| `oneshot` | 99 | 45/47/7 | 1.32x | 1.02x |
-| `keyed` | 99 | 46/45/8 | 1.34x | 1.03x |
-| `derive-key` | 99 | 77/20/2 | 1.76x | 1.86x |
-| `streaming` | 36 | 11/21/4 | 1.16x | 1.01x |
-| `xof` | 99 | 55/35/9 | 1.35x | 1.07x |
+| `oneshot` | 88 | 35/35/18 | 1.33x | 1.00x |
+| `keyed` | 88 | 27/21/40 | 1.20x | 0.95x |
+| `derive-key` | 88 | 65/21/2 | 1.59x | 1.53x |
+| `streaming` | 32 | 10/21/1 | 1.21x | 1.02x |
+| `xof` | 88 | 50/36/2 | 1.37x | 1.07x |
## ML-KEM Summary
-ML-KEM public coverage is complete for the CI-selected primitive set: ML-KEM-512, ML-KEM-768, and ML-KEM-1024 each include keygen, encapsulate, and decapsulate on all nine Linux platforms. POWER10 and s390x do not have `aws-lc-rs` ML-KEM rows in this artifact set, but still have rscrypto plus `libcrux`, `fips203`, and RustCrypto comparison rows for every public operation.
+ML-KEM public coverage is complete for the CI-selected primitive set: ML-KEM-512, ML-KEM-768, and ML-KEM-1024 each include keygen, encapsulate, and decapsulate on all eight Linux platforms. POWER10 and s390x do not have `aws-lc-rs` ML-KEM rows in this artifact set, but still have rscrypto plus `libcrux`, `fips203`, and RustCrypto comparison rows for every public operation.
| Platform | Raw ML-KEM rows | Fastest rows | W/T/L | Geomean | Median | Fastest external split |
| --- | --- | --- | --- | --- | --- | --- |
-| AMD Zen4 | 45 | 9 | 9/0/0 | 1.81x | 1.70x | `libcrux` 7, `aws-lc-rs` 2 |
-| AMD Zen5 | 45 | 9 | 9/0/0 | 1.93x | 1.75x | `libcrux` 9 |
-| AWS Graviton3 | 45 | 9 | 5/0/4 | 1.08x | 1.13x | `aws-lc-rs` 9 |
-| AWS Graviton4 | 45 | 9 | 5/0/4 | 1.08x | 1.14x | `aws-lc-rs` 9 |
-| IBM Power10 | 36 | 9 | 9/0/0 | 1.47x | 1.58x | `libcrux` 9 |
-| IBM z16/s390x | 36 | 9 | 9/0/0 | 1.58x | 1.64x | `libcrux` 9 |
-| Intel Ice Lake | 45 | 9 | 9/0/0 | 1.79x | 1.74x | `libcrux` 7, `aws-lc-rs` 2 |
-| Intel Sapphire Rapids | 45 | 9 | 9/0/0 | 1.85x | 1.82x | `aws-lc-rs` 5, `libcrux` 4 |
-| RISE RISC-V | 45 | 9 | 4/4/1 | 1.13x | 1.03x | `aws-lc-rs` 9 |
+| AMD Zen4 | 45 | 9 | 9/0/0 | 1.83x | 1.82x | `libcrux` 7, `aws-lc-rs` 2 |
+| AMD Zen5 | 45 | 9 | 9/0/0 | 1.95x | 1.91x | `libcrux` 9 |
+| AWS Graviton3 | 45 | 9 | 5/0/4 | 1.09x | 1.12x | `aws-lc-rs` 9 |
+| AWS Graviton4 | 45 | 9 | 5/0/4 | 1.08x | 1.18x | `aws-lc-rs` 9 |
+| IBM Power10 | 36 | 9 | 9/0/0 | 1.41x | 1.53x | `libcrux` 9 |
+| IBM z16/s390x | 36 | 9 | 9/0/0 | 1.68x | 1.74x | `libcrux` 9 |
+| Intel Ice Lake | 45 | 9 | 9/0/0 | 1.80x | 1.75x | `libcrux` 7, `aws-lc-rs` 2 |
+| Intel Sapphire Rapids | 45 | 9 | 9/0/0 | 1.84x | 1.80x | `aws-lc-rs` 7, `libcrux` 2 |
| Primitive/op | Rows | W/T/L | Win % | Geomean | Median | Pressure |
| --- | --- | --- | --- | --- | --- | --- |
-| `mlkem1024` / `decapsulate` | 9 | 9/0/0 | 100% | 1.59x | 1.67x | none |
-| `mlkem1024` / `encapsulate` | 9 | 9/0/0 | 100% | 2.39x | 1.98x | none |
-| `mlkem1024` / `keygen` | 9 | 6/0/3 | 67% | 1.00x | 1.09x | `aws-lc-rs` 3 |
-| `mlkem512` / `decapsulate` | 9 | 6/1/2 | 67% | 1.33x | 1.51x | `aws-lc-rs` 2 |
-| `mlkem512` / `encapsulate` | 9 | 9/0/0 | 100% | 1.86x | 1.70x | none |
-| `mlkem512` / `keygen` | 9 | 6/1/2 | 67% | 1.08x | 1.18x | `aws-lc-rs` 2 |
-| `mlkem768` / `decapsulate` | 9 | 8/1/0 | 89% | 1.49x | 1.70x | none |
-| `mlkem768` / `encapsulate` | 9 | 9/0/0 | 100% | 2.27x | 2.02x | none |
-| `mlkem768` / `keygen` | 9 | 6/1/2 | 67% | 1.04x | 1.12x | `aws-lc-rs` 2 |
+| `mlkem1024` / `decapsulate` | 8 | 8/0/0 | 100% | 1.70x | 1.86x | none |
+| `mlkem1024` / `encapsulate` | 8 | 8/0/0 | 100% | 2.51x | 2.63x | none |
+| `mlkem1024` / `keygen` | 8 | 6/0/2 | 75% | 1.02x | 1.13x | `aws-lc-rs` 2 |
+| `mlkem512` / `decapsulate` | 8 | 6/0/2 | 75% | 1.41x | 1.59x | `aws-lc-rs` 2 |
+| `mlkem512` / `encapsulate` | 8 | 8/0/0 | 100% | 1.94x | 2.17x | none |
+| `mlkem512` / `keygen` | 8 | 6/0/2 | 75% | 1.09x | 1.22x | `aws-lc-rs` 2 |
+| `mlkem768` / `decapsulate` | 8 | 8/0/0 | 100% | 1.58x | 1.75x | none |
+| `mlkem768` / `encapsulate` | 8 | 8/0/0 | 100% | 2.33x | 2.54x | none |
+| `mlkem768` / `keygen` | 8 | 6/0/2 | 75% | 1.06x | 1.13x | `aws-lc-rs` 2 |
## ECDSA Summary
ECDSA signing includes both deterministic and blinded rscrypto rows in raw results; aggregate fastest-external comparisons use the fastest rscrypto row for the exact case. Constant-time release evidence is tracked separately by `ct.toml` and CT workflow artifacts.
+Regression: every ECDSA aggregate in this snapshot is dominated by a single
+platform. On IBM z16/s390x, P-256 signing went from 137.10 µs (2026-07-04) to
+8,889.30 µs, and P-384 signing from 562.91 µs to 34,557.00 µs, while the
+external crates on the same runner moved by less than 1.4x. Excluding s390x, the
+seven-runner geomeans are `ecdsa-p256` / `sign` 1.33x, `ecdsa-p256` / `verify`
+1.19x, `ecdsa-p384` / `sign` 1.01x, and `ecdsa-p384` / `verify` 1.53x.
+
| Operation | Rows | W/T/L | Geomean | Median |
| --- | --- | --- | --- | --- |
-| `ecdsa-p256` / `sign` | 36 | 36/0/0 | 1.45x | 1.35x |
-| `ecdsa-p256` / `verify` | 36 | 28/7/1 | 1.59x | 1.15x |
-| `ecdsa-p384` / `sign` | 36 | 16/0/20 | 1.07x | 0.90x |
-| `ecdsa-p384` / `verify` | 36 | 36/0/0 | 1.81x | 1.39x |
+| `ecdsa-p256` / `sign` | 32 | 28/0/4 | 0.91x | 1.30x |
+| `ecdsa-p256` / `verify` | 32 | 20/7/5 | 0.84x | 1.08x |
+| `ecdsa-p384` / `sign` | 32 | 12/0/20 | 0.70x | 0.83x |
+| `ecdsa-p384` / `verify` | 32 | 28/0/4 | 1.08x | 1.36x |
## Primitive Summary
@@ -178,146 +192,149 @@ Linux CI primitives with matched exact `rscrypto` comparisons. Fastest columns a
| Primitive | Fastest rows | Fastest W/T/L | Fastest geomean | All pairs | All W/T/L | All geomean |
| --- | --- | --- | --- | --- | --- | --- |
-| `argon2id-owasp` | 9 | 4/1/4 | 0.97x | 18 | 11/2/5 | 1.27x |
-| `argon2id-small` | 27 | 12/4/11 | 1.02x | 45 | 29/5/11 | 1.35x |
-| `argon2d-small` | 27 | 13/6/8 | 1.03x | 27 | 13/6/8 | 1.03x |
-| `x25519` | 18 | 2/16/0 | 1.03x | 50 | 33/17/0 | 1.54x |
-| `argon2i-small` | 27 | 13/2/12 | 1.03x | 45 | 30/2/13 | 1.35x |
-| `rapidhash-v3-64` | 99 | 27/53/19 | 1.05x | 99 | 27/53/19 | 1.05x |
-| `blake2b256` | 225 | 120/100/5 | 1.07x | 351 | 238/108/5 | 1.31x |
-| `rapidhash-v3-128` | 99 | 34/44/21 | 1.07x | 99 | 34/44/21 | 1.07x |
-| `blake2b512` | 198 | 109/82/7 | 1.07x | 297 | 207/83/7 | 1.31x |
-| `xxh3-128` | 99 | 29/51/19 | 1.08x | 99 | 29/51/19 | 1.08x |
-| `scrypt-owasp` | 9 | 5/2/2 | 1.09x | 9 | 5/2/2 | 1.09x |
-| `xxh3-64` | 99 | 33/47/19 | 1.10x | 99 | 33/47/19 | 1.10x |
-| `rapidhash-64` | 99 | 32/51/16 | 1.10x | 99 | 32/51/16 | 1.10x |
-| `chacha20-poly1305` | 198 | 83/108/7 | 1.10x | 550 | 344/198/8 | 1.29x |
-| `blake2s256` | 225 | 117/107/1 | 1.10x | 225 | 117/107/1 | 1.10x |
-| `ed25519` | 90 | 31/46/13 | 1.11x | 290 | 204/69/17 | 1.36x |
-| `blake2s128` | 198 | 118/79/1 | 1.11x | 198 | 118/79/1 | 1.11x |
-| `rapidhash-128` | 99 | 38/56/5 | 1.14x | 99 | 38/56/5 | 1.14x |
-| `rsa-8192` | 18 | 14/2/2 | 1.19x | 32 | 28/2/2 | 1.25x |
-| `scrypt-small` | 36 | 22/13/1 | 1.21x | 36 | 22/13/1 | 1.21x |
-| `hmac-sha512` | 99 | 37/54/8 | 1.23x | 275 | 165/97/13 | 1.29x |
-| `hmac-sha384` | 99 | 38/52/9 | 1.24x | 275 | 167/94/14 | 1.29x |
-| `hkdf-sha384` | 36 | 33/2/1 | 1.24x | 100 | 96/3/1 | 1.53x |
-| `sha256` | 117 | 41/59/17 | 1.24x | 293 | 164/99/30 | 1.54x |
-| `sha512` | 117 | 45/61/11 | 1.25x | 293 | 172/104/17 | 1.27x |
-| `pbkdf2-sha512` | 54 | 47/7/0 | 1.25x | 150 | 143/7/0 | 1.33x |
-| `sha384` | 99 | 43/45/11 | 1.25x | 275 | 169/89/17 | 1.28x |
-| `pbkdf2-sha256` | 54 | 50/4/0 | 1.25x | 150 | 146/4/0 | 1.65x |
-| `hkdf-sha256` | 36 | 29/7/0 | 1.26x | 100 | 93/7/0 | 1.87x |
-| `ascon-hash256` | 99 | 54/43/2 | 1.27x | 99 | 54/43/2 | 1.27x |
-| `sha512-256` | 99 | 53/46/0 | 1.29x | 99 | 53/46/0 | 1.29x |
-| `ascon-aead128` | 198 | 148/50/0 | 1.33x | 198 | 148/50/0 | 1.33x |
-| `hmac-sha256` | 117 | 64/39/14 | 1.34x | 293 | 193/76/24 | 1.68x |
-| `aegis-256` | 198 | 89/88/21 | 1.37x | 198 | 89/88/21 | 1.37x |
-| `ascon-xof128` | 99 | 75/24/0 | 1.37x | 99 | 75/24/0 | 1.37x |
-| `mlkem512` | 27 | 21/2/4 | 1.39x | 102 | 96/2/4 | 2.67x |
-| `xchacha20-poly1305` | 198 | 182/16/0 | 1.39x | 198 | 182/16/0 | 1.39x |
-| `ecdsa-p384` | 72 | 52/0/20 | 1.39x | 200 | 180/0/20 | 3.17x |
-| `blake3` | 432 | 234/168/30 | 1.41x | 432 | 234/168/30 | 1.41x |
-| `ecdsa-p256` | 72 | 64/7/1 | 1.52x | 200 | 188/11/1 | 2.40x |
-| `mlkem768` | 27 | 23/2/2 | 1.52x | 102 | 98/2/2 | 3.11x |
-| `mlkem1024` | 27 | 24/0/3 | 1.56x | 102 | 99/0/3 | 3.27x |
-| `rsa-4096` | 27 | 25/0/2 | 1.56x | 59 | 57/0/2 | 2.52x |
-| `crc32c` | 99 | 45/37/17 | 1.61x | 198 | 140/40/18 | 2.19x |
-| `crc32` | 99 | 49/31/19 | 1.66x | 198 | 144/35/19 | 2.26x |
-| `rsa-3072` | 27 | 25/0/2 | 1.66x | 59 | 57/0/2 | 2.59x |
-| `aes-128-gcm` | 198 | 97/45/56 | 1.68x | 550 | 413/54/83 | 1.89x |
-| `rsa-2048` | 27 | 25/0/2 | 1.71x | 59 | 57/0/2 | 2.63x |
-| `aes-256-gcm` | 198 | 97/40/61 | 1.71x | 550 | 405/52/93 | 1.92x |
-| `kmac256` | 99 | 65/18/16 | 1.75x | 99 | 65/18/16 | 1.75x |
-| `cshake256` | 99 | 63/28/8 | 1.78x | 99 | 63/28/8 | 1.78x |
-| `shake128` | 99 | 69/29/1 | 1.85x | 99 | 69/29/1 | 1.85x |
-| `shake256` | 99 | 70/29/0 | 1.87x | 99 | 70/29/0 | 1.87x |
-| `sha224` | 99 | 52/46/1 | 1.87x | 99 | 52/46/1 | 1.87x |
-| `aes-128-gcm-siv` | 198 | 138/3/57 | 2.02x | 352 | 271/16/65 | 2.79x |
-| `sha3-256` | 117 | 104/13/0 | 2.11x | 117 | 104/13/0 | 2.11x |
-| `sha3-224` | 99 | 87/12/0 | 2.13x | 99 | 87/12/0 | 2.13x |
-| `crc64-nvme` | 99 | 56/33/10 | 2.14x | 99 | 56/33/10 | 2.14x |
-| `aes-256-gcm-siv` | 198 | 139/4/55 | 2.15x | 352 | 292/5/55 | 3.04x |
-| `sha3-384` | 99 | 89/10/0 | 2.16x | 99 | 89/10/0 | 2.16x |
-| `sha3-512` | 99 | 88/11/0 | 2.20x | 99 | 88/11/0 | 2.20x |
-| `crc64-xz` | 99 | 76/12/11 | 2.39x | 99 | 76/12/11 | 2.39x |
-| `crc24-openpgp` | 99 | 95/2/2 | 13.25x | 99 | 95/2/2 | 13.25x |
-| `crc16-ccitt` | 99 | 98/0/1 | 23.12x | 99 | 98/0/1 | 23.12x |
-| `crc16-ibm` | 99 | 98/0/1 | 24.01x | 99 | 98/0/1 | 24.01x |
+| `ecdsa-p384` | 64 | 40/0/24 | 0.87x | 176 | 144/0/32 | 2.27x |
+| `ecdsa-p256` | 64 | 48/7/9 | 0.87x | 176 | 148/11/17 | 1.57x |
+| `rapidhash-stream` | 176 | 61/33/82 | 0.92x | 176 | 61/33/82 | 0.92x |
+| `argon2id-owasp` | 8 | 3/1/4 | 0.98x | 16 | 7/4/5 | 1.25x |
+| `xxh3-buildhasher` | 88 | 41/12/35 | 0.99x | 88 | 41/12/35 | 0.99x |
+| `x25519` | 16 | 3/13/0 | 1.02x | 44 | 31/13/0 | 1.58x |
+| `argon2i-small` | 24 | 10/3/11 | 1.03x | 40 | 26/3/11 | 1.34x |
+| `argon2id-small` | 24 | 10/3/11 | 1.03x | 40 | 25/4/11 | 1.35x |
+| `argon2d-small` | 24 | 10/5/9 | 1.04x | 24 | 10/5/9 | 1.04x |
+| `rapidhash-v3-64` | 88 | 21/45/22 | 1.05x | 88 | 21/45/22 | 1.05x |
+| `blake2b256` | 200 | 101/99/0 | 1.07x | 312 | 204/108/0 | 1.31x |
+| `scrypt-owasp` | 8 | 4/2/2 | 1.08x | 8 | 4/2/2 | 1.08x |
+| `blake2b512` | 176 | 106/69/1 | 1.08x | 264 | 194/69/1 | 1.33x |
+| `blake2s256` | 200 | 114/86/0 | 1.11x | 200 | 114/86/0 | 1.11x |
+| `chacha20-poly1305` | 176 | 75/101/0 | 1.12x | 484 | 304/180/0 | 1.32x |
+| `xxh3-128` | 88 | 34/42/12 | 1.13x | 88 | 34/42/12 | 1.13x |
+| `xxh3-64` | 88 | 34/34/20 | 1.13x | 88 | 34/34/20 | 1.13x |
+| `blake2s128` | 176 | 113/63/0 | 1.13x | 176 | 113/63/0 | 1.13x |
+| `ed25519` | 80 | 32/39/9 | 1.14x | 256 | 194/48/14 | 1.41x |
+| `xxh3-hashmap` | 8 | 7/1/0 | 1.15x | 8 | 7/1/0 | 1.15x |
+| `scrypt-small` | 32 | 18/13/1 | 1.18x | 32 | 18/13/1 | 1.18x |
+| `rapidhash-buildhasher` | 88 | 44/29/15 | 1.19x | 88 | 44/29/15 | 1.19x |
+| `aegis-256` | 176 | 81/65/30 | 1.23x | 176 | 81/65/30 | 1.23x |
+| `hmac-sha256` | 104 | 42/36/26 | 1.24x | 258 | 144/78/36 | 1.60x |
+| `hmac-sha384` | 88 | 28/49/11 | 1.24x | 242 | 133/93/16 | 1.29x |
+| `hmac-sha512` | 88 | 32/44/12 | 1.27x | 242 | 137/88/17 | 1.31x |
+| `sha256` | 104 | 44/46/14 | 1.27x | 258 | 143/89/26 | 1.60x |
+| `hkdf-sha384` | 32 | 29/3/0 | 1.27x | 88 | 85/3/0 | 1.59x |
+| `rsa-8192` | 16 | 14/2/0 | 1.28x | 28 | 26/2/0 | 1.33x |
+| `hkdf-sha256` | 32 | 27/5/0 | 1.28x | 88 | 83/5/0 | 1.93x |
+| `pbkdf2-sha256` | 48 | 43/5/0 | 1.28x | 132 | 127/5/0 | 1.71x |
+| `pbkdf2-sha512` | 48 | 41/7/0 | 1.28x | 132 | 125/7/0 | 1.34x |
+| `sha512` | 104 | 48/51/5 | 1.29x | 258 | 160/88/10 | 1.31x |
+| `sha384` | 88 | 43/39/6 | 1.30x | 242 | 151/80/11 | 1.32x |
+| `ascon-hash256` | 88 | 56/31/1 | 1.30x | 88 | 56/31/1 | 1.30x |
+| `sha512-256` | 88 | 50/38/0 | 1.33x | 88 | 50/38/0 | 1.33x |
+| `blake3` | 384 | 187/134/63 | 1.35x | 384 | 187/134/63 | 1.35x |
+| `ascon-aead128` | 176 | 136/39/1 | 1.39x | 176 | 136/39/1 | 1.39x |
+| `ascon-xof128` | 88 | 66/20/2 | 1.39x | 88 | 66/20/2 | 1.39x |
+| `xchacha20-poly1305` | 176 | 173/3/0 | 1.43x | 176 | 173/3/0 | 1.43x |
+| `mlkem512` | 24 | 20/0/4 | 1.44x | 90 | 86/0/4 | 2.90x |
+| `rapidhash-hash-one` | 24 | 18/4/2 | 1.47x | 24 | 18/4/2 | 1.47x |
+| `mlkem768` | 24 | 22/0/2 | 1.57x | 90 | 88/0/2 | 3.38x |
+| `rapidhash-hashmap` | 24 | 24/0/0 | 1.61x | 24 | 24/0/0 | 1.61x |
+| `mlkem1024` | 24 | 22/0/2 | 1.63x | 90 | 88/0/2 | 3.60x |
+| `rsa-4096` | 24 | 24/0/0 | 1.70x | 52 | 52/0/0 | 2.69x |
+| `crc32c` | 88 | 42/38/8 | 1.73x | 176 | 130/38/8 | 2.41x |
+| `rsa-3072` | 24 | 24/0/0 | 1.75x | 52 | 52/0/0 | 2.73x |
+| `rsa-2048` | 24 | 24/0/0 | 1.79x | 52 | 52/0/0 | 2.77x |
+| `aes-128-gcm` | 176 | 96/42/38 | 1.80x | 484 | 390/50/44 | 2.01x |
+| `crc32` | 88 | 47/33/8 | 1.80x | 176 | 133/35/8 | 2.51x |
+| `aes-256-gcm` | 176 | 94/36/46 | 1.83x | 484 | 382/44/58 | 2.02x |
+| `kmac256` | 88 | 58/19/11 | 1.86x | 88 | 58/19/11 | 1.86x |
+| `cshake256` | 88 | 58/21/9 | 1.90x | 88 | 58/21/9 | 1.90x |
+| `shake128` | 88 | 58/30/0 | 1.94x | 88 | 58/30/0 | 1.94x |
+| `shake256` | 88 | 63/25/0 | 1.98x | 88 | 63/25/0 | 1.98x |
+| `sha224` | 88 | 51/37/0 | 2.01x | 88 | 51/37/0 | 2.01x |
+| `aes-128-gcm-siv` | 176 | 127/1/48 | 2.20x | 308 | 237/16/55 | 2.92x |
+| `sha3-224` | 88 | 77/11/0 | 2.27x | 88 | 77/11/0 | 2.27x |
+| `sha3-256` | 104 | 91/13/0 | 2.28x | 104 | 91/13/0 | 2.28x |
+| `aes-256-gcm-siv` | 176 | 128/1/47 | 2.34x | 308 | 259/2/47 | 3.16x |
+| `crc64-nvme` | 88 | 52/35/1 | 2.34x | 88 | 52/35/1 | 2.34x |
+| `sha3-384` | 88 | 79/9/0 | 2.35x | 88 | 79/9/0 | 2.35x |
+| `sha3-512` | 88 | 77/11/0 | 2.38x | 88 | 77/11/0 | 2.38x |
+| `crc64-xz` | 88 | 73/12/3 | 2.78x | 88 | 73/12/3 | 2.78x |
+| `crc24-openpgp` | 88 | 86/0/2 | 17.62x | 88 | 86/0/2 | 17.62x |
+| `crc16-ccitt` | 88 | 88/0/0 | 30.24x | 88 | 88/0/0 | 30.24x |
+| `crc16-ibm` | 88 | 88/0/0 | 32.07x | 88 | 88/0/0 | 32.07x |
## Linux Worst Individual Rows
| Platform | Case | Fastest external | Ratio |
| --- | --- | --- | --- |
-| RISE RISC-V | `xxh3-64 / 0` | `xxhash-rust` | 0.43x |
-| Intel Sapphire Rapids | `aes-256-gcm / encrypt / 32` | `rustcrypto` | 0.46x |
-| Intel Ice Lake | `aes-256-gcm-siv / encrypt / 32` | `aws-lc-rs` | 0.48x |
-| Intel Sapphire Rapids | `aes-128-gcm / encrypt / 32` | `rustcrypto` | 0.48x |
-| Intel Ice Lake | `aes-256-gcm-siv / encrypt / 0` | `aws-lc-rs` | 0.48x |
-| Intel Sapphire Rapids | `aes-256-gcm-siv / encrypt / 32` | `aws-lc-rs` | 0.49x |
-| Intel Sapphire Rapids | `aes-128-gcm-siv / encrypt / 0` | `aws-lc-rs` | 0.49x |
-| Intel Ice Lake | `aes-128-gcm-siv / encrypt / 32` | `aws-lc-rs` | 0.49x |
-| Intel Sapphire Rapids | `aes-128-gcm-siv / encrypt / 32` | `aws-lc-rs` | 0.49x |
-| AMD Zen4 | `aes-256-gcm / encrypt / 32` | `rustcrypto` | 0.49x |
-| Intel Ice Lake | `aes-256-gcm / encrypt / 32` | `rustcrypto` | 0.49x |
-| Intel Sapphire Rapids | `aes-256-gcm-siv / encrypt / 0` | `aws-lc-rs` | 0.49x |
+| IBM z16/s390x | `ecdsa-p256 / sign / 1024` | `ring` | 0.05x |
+| IBM z16/s390x | `ecdsa-p384 / sign / 16384` | `rustcrypto-p384` | 0.05x |
+| IBM z16/s390x | `ecdsa-p384 / sign / 1024` | `rustcrypto-p384` | 0.05x |
+| IBM z16/s390x | `ecdsa-p384 / sign / 0` | `rustcrypto-p384` | 0.06x |
+| IBM z16/s390x | `ecdsa-p384 / sign / 32` | `rustcrypto-p384` | 0.06x |
+| IBM z16/s390x | `ecdsa-p256 / sign / 0` | `ring` | 0.06x |
+| IBM z16/s390x | `ecdsa-p256 / sign / 32` | `ring` | 0.06x |
+| IBM z16/s390x | `ecdsa-p256 / verify / 32` | `rustcrypto-p256` | 0.06x |
+| IBM z16/s390x | `ecdsa-p256 / verify / 1024` | `rustcrypto-p256` | 0.07x |
+| IBM z16/s390x | `ecdsa-p256 / sign / 16384` | `ring` | 0.07x |
+| IBM z16/s390x | `ecdsa-p256 / verify / 0` | `rustcrypto-p256` | 0.07x |
+| IBM z16/s390x | `ecdsa-p384 / verify / 1024` | `rustcrypto-p384` | 0.09x |
## Linux Strongest Individual Rows
| Platform | Case | Fastest external | Ratio |
| --- | --- | --- | --- |
-| Intel Sapphire Rapids | `crc16-ccitt / 262144` | `crc` | 214.89x |
-| Intel Sapphire Rapids | `crc16-ibm / 262144` | `crc` | 209.87x |
-| Intel Sapphire Rapids | `crc16-ibm / 16384` | `crc` | 208.50x |
-| Intel Sapphire Rapids | `crc16-ccitt / 16384` | `crc` | 207.55x |
-| Intel Sapphire Rapids | `crc16-ccitt / 4096` | `crc` | 184.38x |
-| Intel Sapphire Rapids | `crc16-ccitt / 65536` | `crc` | 183.70x |
-| Intel Sapphire Rapids | `crc16-ibm / 65536` | `crc` | 179.68x |
-| Intel Sapphire Rapids | `crc16-ccitt / 1048576` | `crc` | 178.10x |
-| IBM Power10 | `crc16-ibm / 262144` | `crc` | 176.65x |
-| IBM Power10 | `crc16-ibm / 1048576` | `crc` | 176.64x |
-| IBM Power10 | `crc16-ccitt / 262144` | `crc` | 176.33x |
-| IBM Power10 | `crc16-ccitt / 1048576` | `crc` | 176.09x |
+| Intel Sapphire Rapids | `crc16-ibm / 262144` | `crc` | 212.60x |
+| Intel Sapphire Rapids | `crc16-ccitt / 262144` | `crc` | 209.27x |
+| Intel Sapphire Rapids | `crc16-ccitt / 16384` | `crc` | 206.40x |
+| Intel Sapphire Rapids | `crc16-ibm / 16384` | `crc` | 198.48x |
+| Intel Sapphire Rapids | `crc16-ibm / 1048576` | `crc` | 187.52x |
+| Intel Sapphire Rapids | `crc16-ibm / 4096` | `crc` | 178.55x |
+| Intel Sapphire Rapids | `crc16-ibm / 65536` | `crc` | 178.28x |
+| Intel Sapphire Rapids | `crc16-ccitt / 4096` | `crc` | 178.15x |
+| IBM Power10 | `crc16-ccitt / 1048576` | `crc` | 176.67x |
+| IBM Power10 | `crc16-ibm / 1048576` | `crc` | 176.60x |
+| Intel Sapphire Rapids | `crc16-ccitt / 1048576` | `crc` | 176.46x |
+| IBM Power10 | `crc16-ccitt / 262144` | `crc` | 175.61x |
## Top Five Loss Areas
-- `argon2id-owasp` / `hash`: 0.97x geomean across 9 rows; W/T/L 4/1/4; pressure `rustcrypto` 4.
-- `ed25519` / `verify`: 1.00x geomean across 36 rows; W/T/L 7/20/9; pressure `ring` 6, `dalek` 3.
-- `mlkem1024` / `keygen`: 1.00x geomean across 9 rows; W/T/L 6/0/3; pressure `aws-lc-rs` 3.
-- `blake2b256` / `streaming`: 1.02x geomean across 27 rows; W/T/L 10/16/1; pressure `rustcrypto` 1.
-- `argon2id-small` / `hash`: 1.02x geomean across 27 rows; W/T/L 12/4/11; pressure `rustcrypto` 11.
+- `ecdsa-p384` / `sign`: 0.70x geomean across 32 rows; W/T/L 12/0/20; pressure `aws-lc-rs` 16, `rustcrypto-p384` 4.
+- `ecdsa-p256` / `verify`: 0.84x geomean across 32 rows; W/T/L 20/7/5; pressure `rustcrypto-p256` 4, `aws-lc-rs` 1.
+- `rapidhash-stream` / `one-write`: 0.87x geomean across 88 rows; W/T/L 27/25/36; pressure `rapidhash` 36.
+- `ecdsa-p256` / `sign`: 0.91x geomean across 32 rows; W/T/L 28/0/4; pressure `ring` 4.
+- `argon2id-owasp` / `hash`: 0.98x geomean across 8 rows; W/T/L 3/1/4; pressure `rustcrypto` 3, `dryoc` 1.
## External Pressure
| External | Pairs | W/T/L | Win % | Geomean | Median |
| --- | --- | --- | --- | --- | --- |
-| `xxhash-rust` | 198 | 62/98/38 | 31% | 1.09x | 1.00x |
-| `rapidhash` | 396 | 131/204/61 | 33% | 1.09x | 1.01x |
-| `aws-lc-rs` | 1,673 | 1,044/384/245 | 62% | 1.25x | 1.15x |
-| `ascon-hash` | 198 | 129/67/2 | 65% | 1.32x | 1.19x |
-| `ascon-aead` | 198 | 148/50/0 | 75% | 1.33x | 1.15x |
-| `aegis-crate` | 198 | 89/88/21 | 45% | 1.37x | 1.04x |
-| `blake3` | 432 | 234/168/30 | 54% | 1.41x | 1.08x |
-| `dalek` | 108 | 85/18/5 | 79% | 1.47x | 1.27x |
-| `sha2` | 531 | 284/243/4 | 53% | 1.52x | 1.06x |
-| `ring` | 1,656 | 1,333/228/95 | 80% | 1.66x | 1.29x |
-| `rustcrypto` | 2,745 | 1,949/621/175 | 71% | 1.78x | 1.19x |
-| `libcrux` | 81 | 81/0/0 | 100% | 1.79x | 1.70x |
-| `dryoc` | 360 | 325/29/6 | 90% | 1.79x | 1.83x |
-| `tiny-keccak` | 396 | 267/104/25 | 67% | 1.81x | 1.96x |
-| `crc-fast` | 297 | 170/95/32 | 57% | 2.00x | 1.15x |
-| `sha3` | 414 | 368/46/0 | 89% | 2.15x | 2.12x |
-| `crc64fast` | 99 | 76/12/11 | 77% | 2.39x | 1.95x |
-| `crc32fast` | 99 | 81/7/11 | 82% | 2.41x | 2.01x |
-| `crc32c` | 99 | 89/6/4 | 90% | 2.71x | 2.19x |
-| `fips203` | 81 | 81/0/0 | 100% | 4.66x | 5.11x |
-| `rustcrypto-p384` | 72 | 72/0/0 | 100% | 4.79x | 5.26x |
-| `rustcrypto-p256` | 72 | 72/0/0 | 100% | 5.65x | 4.91x |
-| `rustcrypto-rsa` | 81 | 81/0/0 | 100% | 5.68x | 4.70x |
-| `crc` | 297 | 291/2/4 | 98% | 19.45x | 28.96x |
+| `rapidhash` | 400 | 168/111/121 | 42% | 1.07x | 1.01x |
+| `xxhash-rust` | 272 | 116/89/67 | 43% | 1.08x | 1.00x |
+| `aws-lc-rs` | 1,434 | 896/343/195 | 62% | 1.21x | 1.13x |
+| `aegis-crate` | 176 | 81/65/30 | 46% | 1.23x | 1.04x |
+| `ascon-hash` | 176 | 122/51/3 | 69% | 1.34x | 1.32x |
+| `blake3` | 384 | 187/134/63 | 49% | 1.35x | 1.04x |
+| `ascon-aead` | 176 | 136/39/1 | 77% | 1.39x | 1.38x |
+| `dalek` | 96 | 80/12/4 | 83% | 1.52x | 1.49x |
+| `sha2` | 472 | 276/194/2 | 58% | 1.60x | 1.07x |
+| `ring` | 1,472 | 1,154/237/81 | 78% | 1.63x | 1.28x |
+| `libcrux` | 72 | 72/0/0 | 100% | 1.79x | 1.72x |
+| `dryoc` | 320 | 293/22/5 | 92% | 1.81x | 1.85x |
+| `rustcrypto` | 2,440 | 1,783/529/128 | 73% | 1.87x | 1.21x |
+| `tiny-keccak` | 352 | 237/95/20 | 67% | 1.92x | 2.10x |
+| `crc-fast` | 264 | 153/101/10 | 58% | 2.15x | 1.20x |
+| `sha3` | 368 | 324/44/0 | 88% | 2.32x | 2.15x |
+| `crc32fast` | 88 | 79/4/5 | 90% | 2.75x | 2.06x |
+| `crc64fast` | 88 | 73/12/3 | 83% | 2.78x | 2.49x |
+| `rustcrypto-p256` | 64 | 56/0/8 | 88% | 3.03x | 3.10x |
+| `rustcrypto-p384` | 64 | 56/0/8 | 88% | 3.06x | 5.50x |
+| `crc32c` | 88 | 83/3/2 | 94% | 3.13x | 2.29x |
+| `fips203` | 72 | 72/0/0 | 100% | 5.28x | 6.07x |
+| `rustcrypto-rsa` | 72 | 72/0/0 | 100% | 6.07x | 6.50x |
+| `crc` | 264 | 262/0/2 | 99% | 25.76x | 46.98x |
## macOS Local Snapshot
-The macOS Apple Silicon run is local evidence from the 2026-07-04 full benchmark at commit `596498f`. It is useful for Apple Silicon planning but is not folded into Linux CI release claims. The ML-KEM row uses the same artifact's public ML-KEM rows.
+The macOS Apple Silicon run is local evidence from the 2026-07-04 full benchmark at commit `596498f`, carried forward unchanged in this refresh. It is useful for Apple Silicon planning but is not folded into Linux CI release claims. The ML-KEM row uses the same artifact's public ML-KEM rows.
| Scope | Pairs | W/T/L | Win % | Geomean | Median |
| --- | --- | --- | --- | --- | --- |
@@ -327,29 +344,28 @@ The macOS Apple Silicon run is local evidence from the 2026-07-04 full benchmark
## README Numbers
-- **Headline:** 4,052 of 6,750 matched Linux CI fastest-external comparisons are wins; 6,101 are wins or ties. Linux CI geomean is 1.59x.
-- **Checksums:** 5.18x geomean across 693 Linux CI fastest-external rows; W/T/L 517/115/61.
-- **Hashes/MACs/XOFs:** 1.35x geomean across 3,726 Linux CI fastest-external rows; W/T/L 2,028/1,457/241.
-- **Auth/KDF:** 1.25x geomean across 180 Linux CI fastest-external rows; W/T/L 159/20/1.
-- **Password hashing:** 1.07x geomean across 135 Linux CI fastest-external rows; W/T/L 69/28/38.
-- **Public-key:** 1.33x geomean across 333 Linux CI fastest-external rows; W/T/L 217/73/43.
-- **RSA:** 1.55x geomean across 99 Linux CI fastest-external rows; W/T/L 89/2/8.
-- **AEAD:** 1.56x geomean across 1,584 Linux CI fastest-external rows; W/T/L 973/354/257.
-- **ML-KEM:** 1.49x geomean across 81 Linux CI fastest-external rows; W/T/L 68/4/9.
-- **ECDSA P-256/P-384:** 1.45x Linux CI geomean across 144 fastest-external rows; W/T/L 116/7/21.
-- **Current top losses:** `argon2id-owasp` / `hash`: 0.97x geomean across 9 rows; W/T/L 4/1/4; pressure `rustcrypto` 4; `ed25519` / `verify`: 1.00x geomean across 36 rows; W/T/L 7/20/9; pressure `ring` 6, `dalek` 3; `mlkem1024` / `keygen`: 1.00x geomean across 9 rows; W/T/L 6/0/3; pressure `aws-lc-rs` 3; `blake2b256` / `streaming`: 1.02x geomean across 27 rows; W/T/L 10/16/1; pressure `rustcrypto` 1; `argon2id-small` / `hash`: 1.02x geomean across 27 rows; W/T/L 12/4/11; pressure `rustcrypto` 11.
+- **Headline:** 3,780 of 6,144 matched Linux CI fastest-external comparisons are wins; 5,475 are wins or ties. Linux CI geomean is 1.62x.
+- **Checksums:** 6.18x geomean across 616 Linux CI fastest-external rows; W/T/L 476/118/22.
+- **Hashes/MACs/XOFs:** 1.35x geomean across 3,456 Linux CI fastest-external rows; W/T/L 1,926/1,181/349.
+- **Auth/KDF:** 1.28x geomean across 160 Linux CI fastest-external rows; W/T/L 140/20/0.
+- **Password hashing:** 1.07x geomean across 120 Linux CI fastest-external rows; W/T/L 55/27/38.
+- **Public-key:** 1.09x geomean across 296 Linux CI fastest-external rows; W/T/L 187/59/50.
+- **RSA:** 1.65x geomean across 88 Linux CI fastest-external rows; W/T/L 86/2/0.
+- **AEAD:** 1.61x geomean across 1,408 Linux CI fastest-external rows; W/T/L 910/288/210.
+- **ML-KEM:** 1.55x geomean across 72 Linux CI fastest-external rows; W/T/L 64/0/8.
+- **ECDSA P-256/P-384:** 0.87x Linux CI geomean across 128 fastest-external rows; W/T/L 88/7/33.
+- **Current top losses:** `ecdsa-p384` / `sign`: 0.70x geomean across 32 rows; W/T/L 12/0/20; pressure `aws-lc-rs` 16, `rustcrypto-p384` 4; `ecdsa-p256` / `verify`: 0.84x geomean across 32 rows; W/T/L 20/7/5; pressure `rustcrypto-p256` 4, `aws-lc-rs` 1; `rapidhash-stream` / `one-write`: 0.87x geomean across 88 rows; W/T/L 27/25/36; pressure `rapidhash` 36; `ecdsa-p256` / `sign`: 0.91x geomean across 32 rows; W/T/L 28/0/4; pressure `ring` 4; `argon2id-owasp` / `hash`: 0.98x geomean across 8 rows; W/T/L 3/1/4; pressure `rustcrypto` 3, `dryoc` 1.
## Raw Results
| Platform | Mode | Date/time | Parsed rows | Result |
| --- | --- | --- | --- | --- |
-| AMD Zen4 | `ci` | `2026-07-04 15_26_12` | 2,356 | `benchmark_results/2026-07-04/linux/amd-zen4/results.txt` |
-| AMD Zen5 | `ci` | `2026-07-04 15_26_12` | 2,356 | `benchmark_results/2026-07-04/linux/amd-zen5/results.txt` |
-| AWS Graviton3 | `ci` | `2026-07-04 15_26_12` | 2,367 | `benchmark_results/2026-07-04/linux/graviton3/results.txt` |
-| AWS Graviton4 | `ci` | `2026-07-04 15_26_12` | 2,367 | `benchmark_results/2026-07-04/linux/graviton4/results.txt` |
-| IBM Power10 | `ci` | `2026-07-04 15_26_12` | 2,107 | `benchmark_results/2026-07-04/linux/ibm-power10/results.txt` |
-| IBM z16/s390x | `ci` | `2026-07-04 15_26_12` | 2,107 | `benchmark_results/2026-07-04/linux/ibm-s390x/results.txt` |
-| Intel Ice Lake | `ci` | `2026-07-04 15_26_12` | 2,356 | `benchmark_results/2026-07-04/linux/intel-icl/results.txt` |
-| Intel Sapphire Rapids | `ci` | `2026-07-04 15_26_12` | 2,356 | `benchmark_results/2026-07-04/linux/intel-spr/results.txt` |
-| RISE RISC-V | `ci` | `2026-07-04 15_26_12` | 2,356 | `benchmark_results/2026-07-04/linux/rise-riscv/results.txt` |
+| AMD Zen4 | `ci` | `2026-08-18 21_03_07` | 2,304 | `benchmark_results/2026-08-18/linux/amd-zen4/results.txt` |
+| AMD Zen5 | `ci` | `2026-08-18 21_03_07` | 2,304 | `benchmark_results/2026-08-18/linux/amd-zen5/results.txt` |
+| AWS Graviton3 | `ci` | `2026-08-18 21_03_07` | 2,308 | `benchmark_results/2026-08-18/linux/graviton3/results.txt` |
+| AWS Graviton4 | `ci` | `2026-08-18 21_03_07` | 2,308 | `benchmark_results/2026-08-18/linux/graviton4/results.txt` |
+| IBM Power10 | `ci` | `2026-08-18 21_03_07` | 2,055 | `benchmark_results/2026-08-18/linux/ibm-power10/results.txt` |
+| IBM z16/s390x | `ci` | `2026-08-18 21_03_07` | 2,055 | `benchmark_results/2026-08-18/linux/ibm-s390x/results.txt` |
+| Intel Ice Lake | `ci` | `2026-08-18 21_03_07` | 2,304 | `benchmark_results/2026-08-18/linux/intel-icl/results.txt` |
+| Intel Sapphire Rapids | `ci` | `2026-08-18 21_03_07` | 2,304 | `benchmark_results/2026-08-18/linux/intel-spr/results.txt` |
| macOS Apple Silicon | `local` | `2026-07-04 12_28_04` | 2,277 | `benchmark_results/2026-07-04/macos/aarch64/results.txt` |
diff --git a/clippy.toml b/clippy.toml
new file mode 100644
index 00000000..be54ebfb
--- /dev/null
+++ b/clippy.toml
@@ -0,0 +1,2 @@
+check-private-items = true
+check-incompatible-msrv-in-tests = true
diff --git a/deny.toml b/deny.toml
index 8b889930..bfe406b2 100644
--- a/deny.toml
+++ b/deny.toml
@@ -1,30 +1,27 @@
[graph]
-# Target coverage for rscrypto - canonical list for dependency analysis
-# See .cargo/config.toml for per-target rustflags
+# Dependency-analysis projection of .config/target-matrix.json.
targets = [
"aarch64-apple-darwin",
+ "aarch64-pc-windows-msvc",
"aarch64-unknown-linux-gnu",
- "x86_64-apple-darwin",
- "x86_64-pc-windows-msvc",
- "x86_64-unknown-linux-gnu",
"aarch64-unknown-linux-musl",
- "wasm32-unknown-unknown",
- "wasm32-wasip1",
- "x86_64-unknown-linux-musl",
- "aarch64-pc-windows-msvc",
"aarch64-unknown-none",
- "loongarch64-unknown-linux-gnu",
"powerpc64le-unknown-linux-gnu",
"riscv32imac-unknown-none-elf",
"riscv64gc-unknown-linux-gnu",
- "riscv64gc-unknown-none-elf",
"s390x-unknown-linux-gnu",
"thumbv6m-none-eabi",
+ "wasm32-unknown-unknown",
+ "wasm32-wasip1",
+ "x86_64-apple-darwin",
+ "x86_64-pc-windows-msvc",
+ "x86_64-unknown-linux-gnu",
+ "x86_64-unknown-linux-musl",
"x86_64-unknown-none",
]
[advisories]
-yanked = "warn"
+yanked = "deny"
# RustCrypto `rsa` is a dev-only differential oracle and benchmark baseline.
# rscrypto never exposes it in production paths; production RSA verification
# uses `src/auth/rsa.rs`. Keep this scoped to the known Marvin advisory until
diff --git a/docs/benchmarking.md b/docs/benchmarking.md
index f3235c0e..234e2bb3 100644
--- a/docs/benchmarking.md
+++ b/docs/benchmarking.md
@@ -7,11 +7,12 @@ Benchmark numbers are only meaningful with their platform, commit, feature set,
and comparison shape. Treat every headline number as a pointer to the raw
results in [`benchmark_results/`](../benchmark_results/).
-The published 2026-07-04 aggregate is archival, not an equivalent-work
-performance claim. Its RustCrypto HMAC-SHA-256 rows include key setup inside
-the timed loop while the rscrypto, `ring`, and AWS-LC rows reuse keyed state.
-The current benchmark source corrects that mismatch; publish a new aggregate
-only after regenerating the complete artifact.
+The published aggregate is the 2026-08-18 eight-runner Linux CI pass at commit
+`7eb44e9`. The earlier RustCrypto HMAC-SHA-256 mismatch—key setup inside the
+timed loop while the rscrypto, `ring`, and AWS-LC rows reused keyed state—is
+corrected in the benchmark source and in this artifact, so the aggregate is an
+equivalent-work claim. The RISE RISC-V runner did not execute in that run, so
+row counts are not comparable to the nine-runner 2026-07-04 snapshot.
## Read the numbers
@@ -26,8 +27,10 @@ Values above `1.00x` mean `rscrypto` was faster for that row. Values below
The published W/T/L summaries classify ratios above `1.05x` as wins, ratios
from `0.95x` through `1.05x` as ties, and ratios below `0.95x` as losses. Use
-individual equivalent-work rows—not the archival aggregate—when a primitive or
-message size matters to a deployment.
+individual rows—not the crate-wide aggregate—when a primitive or message size
+matters to a deployment. A single regressed runner can move a whole category
+aggregate: in the 2026-08-18 pass the s390x ECDSA regression pulls the ECDSA
+geomean from above parity to `0.87x`.
## Published sources
diff --git a/docs/features.md b/docs/features.md
index 565c2d36..e7b70b36 100644
--- a/docs/features.md
+++ b/docs/features.md
@@ -14,28 +14,28 @@ algorithm feature explicitly.
```toml
# One algorithm, no_std.
-rscrypto = { version = "0.7.8", default-features = false, features = ["sha2"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["sha2"] }
# RSA public-key import and verification, no_std + alloc.
-rscrypto = { version = "0.7.8", default-features = false, features = ["rsa"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["rsa"] }
# RSA key generation, signing, encryption, and private-operation blinding.
-rscrypto = { version = "0.7.8", default-features = false, features = ["rsa", "getrandom"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["rsa", "getrandom"] }
# ECDSA P-256/SHA-256 and P-384/SHA-384 signing and verification.
-rscrypto = { version = "0.7.8", default-features = false, features = ["ecdsa"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["ecdsa"] }
# FIPS 203 ML-KEM-512/768/1024 KEM APIs with caller-supplied randomness.
-rscrypto = { version = "0.7.8", default-features = false, features = ["ml-kem"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["ml-kem"] }
# Everything.
-rscrypto = { version = "0.7.8", features = ["full", "getrandom"] }
+rscrypto = { version = "0.8.1", features = ["full", "getrandom"] }
# Everything, with parallel BLAKE3 / Argon2 lanes via Rayon.
-rscrypto = { version = "0.7.8", features = ["full", "parallel", "getrandom"] }
+rscrypto = { version = "0.8.1", features = ["full", "parallel", "getrandom"] }
# Audit-constrained: makes runtime dispatch ignore host SIMD/ASM capabilities.
-rscrypto = { version = "0.7.8", features = ["full", "portable-only"] }
+rscrypto = { version = "0.8.1", features = ["full", "portable-only"] }
```
## Complete feature index
diff --git a/docs/migration/README.md b/docs/migration/README.md
index 01f2ecb3..e5ce58e8 100644
--- a/docs/migration/README.md
+++ b/docs/migration/README.md
@@ -29,12 +29,12 @@ For projects upgrading rscrypto itself, start with
| From | To | Status |
|---|---|---|
-| [`blake3`](blake3.md) | `Blake3` | Verified against `blake3 1.8.5` |
+| [`blake3`](blake3.md) | `Blake3` | Verified against `blake3 1.8.6` |
| [`sha2`](RustCrypto/sha2.md) (RustCrypto) | `Sha224`, `Sha256`, `Sha384`, `Sha512`, `Sha512_256` | Verified against `sha2 0.11.0` |
| [`sha3`](RustCrypto/sha3.md) (RustCrypto) | `Sha3_224`, `Sha3_256`, `Sha3_384`, `Sha3_512`, `Shake128`, `Shake256`, `Cshake128`, `Cshake256` | Verified against `sha3 0.12.0` |
-| [`blake2`](RustCrypto/blake2.md) (RustCrypto) | `Blake2b256`, `Blake2b512`, `Blake2s128`, `Blake2s256` | Verified against `blake2 0.11.0-rc.6` |
+| [`blake2`](RustCrypto/blake2.md) (RustCrypto) | `Blake2b256`, `Blake2b512`, `Blake2s128`, `Blake2s256` | Verified against `blake2 0.10.6` |
| [`ascon-hash`](RustCrypto/ascon-hash.md) (RustCrypto) | `AsconHash256`, `AsconXof`, `AsconCxof128` | Verified against `ascon-hash 0.4.0` |
-| [`xxhash-rust`](xxhash-rust.md) | `Xxh3`, `Xxh3_128`, `Xxh3Hasher`, `Xxh3_128Hasher`, `Xxh3BuildHasher` | Verified against `xxhash-rust 0.8.16` |
+| [`xxhash-rust`](xxhash-rust.md) | `Xxh3`, `Xxh3_128`, `Xxh3Hasher`, `Xxh3_128Hasher`, `Xxh3BuildHasher` | Verified against `xxhash-rust 0.8.18` |
| [`twox-hash`](twox-hash.md) | `Xxh3`, `Xxh3_128`, `Xxh3Hasher`, `Xxh3_128Hasher`, `Xxh3BuildHasher` | API migration guidance; XXH3 output covered by `xxhash-rust` oracle tests |
| [`rapidhash`](rapidhash.md) | `RapidHash64`, `RapidStreamHasher`, `RapidHasher`, `RapidSeededState`, `RapidRandomState` | Verified against `rapidhash 4.5.1` |
@@ -53,25 +53,25 @@ For projects upgrading rscrypto itself, start with
| From | To | Status |
|---|---|---|
| [`aes-gcm`](RustCrypto/aes-gcm.md) (RustCrypto) | `Aes128Gcm`, `Aes256Gcm` | Verified against `aes-gcm 0.11.0` |
-| [`aes-gcm-siv`](RustCrypto/aes-gcm-siv.md) (RustCrypto) | `Aes128GcmSiv`, `Aes256GcmSiv` | Verified against `aes-gcm-siv 0.11.1` |
+| [`aes-gcm-siv`](RustCrypto/aes-gcm-siv.md) (RustCrypto) | `Aes128GcmSiv`, `Aes256GcmSiv` | Verified against `aes-gcm-siv 0.12.0` |
| [`chacha20poly1305`](RustCrypto/chacha20poly1305.md) (RustCrypto) | `ChaCha20Poly1305`, `XChaCha20Poly1305` | Verified against `chacha20poly1305 0.11.0` |
| [`ascon-aead`](RustCrypto/ascon-aead.md) (RustCrypto) | `AsconAead128` | Verified against `ascon-aead 0.6.0` |
-| [`aegis`](aegis.md) | `Aegis256` | Verified against `aegis 0.9.12` |
+| [`aegis`](aegis.md) | `Aegis256` | Verified against `aegis 0.9.15` |
## Signatures and key exchange
| From | To | Status |
|---|---|---|
-| [`p256`](RustCrypto/p256.md) / [`p384`](RustCrypto/p384.md) (RustCrypto) | `EcdsaP256SecretKey`, `EcdsaP384SecretKey`, `EcdsaP256PublicKey`, `EcdsaP384PublicKey`, raw/DER signatures | Signing and verification tested against RustCrypto `p256 0.14.0` / `p384 0.13.1` |
-| [`ed25519-dalek`](RustCrypto/ed25519-dalek.md) | `Ed25519SecretKey`, `Ed25519PublicKey`, `Ed25519Signature`, `Ed25519Keypair` | Verified against `ed25519-dalek 2.2.0` |
+| [`p256`](RustCrypto/p256.md) / [`p384`](RustCrypto/p384.md) (RustCrypto) | `EcdsaP256SecretKey`, `EcdsaP384SecretKey`, `EcdsaP256PublicKey`, `EcdsaP384PublicKey`, raw/DER signatures | Signing and verification tested against RustCrypto `p256 0.14.0` / `p384 0.14.0` |
+| [`ed25519-dalek`](RustCrypto/ed25519-dalek.md) | `Ed25519SecretKey`, `Ed25519PublicKey`, `Ed25519Signature`, `Ed25519Keypair` | Verified against `ed25519-dalek 3.0.0` |
| [`rsa`](RustCrypto/rsa.md) (RustCrypto) | `RsaPublicKey`, `RsaPrivateKey`, RSA-PSS, RSASSA-PKCS1-v1_5, OAEP | Partial; verified through CAVP, Wycheproof, and RustCrypto/ring/OpenSSL oracles |
-| [`x25519-dalek`](RustCrypto/x25519-dalek.md) | `X25519SecretKey`, `X25519PublicKey`, `X25519SharedSecret` | Verified against `x25519-dalek 2.0.1` |
+| [`x25519-dalek`](RustCrypto/x25519-dalek.md) | `X25519SecretKey`, `X25519PublicKey`, `X25519SharedSecret` | Verified against `x25519-dalek 3.0.0` |
## Password hashing
| From | To | Status |
|---|---|---|
-| [`argon2`](RustCrypto/argon2.md) (RustCrypto) | Raw `Argon2{d,i,id}` KDFs; bounded `Argon2idPassword` records | Verified against `argon2 0.6.0-rc.8` |
+| [`argon2`](RustCrypto/argon2.md) (RustCrypto) | Raw `Argon2{d,i,id}` KDFs; bounded `Argon2idPassword` records | Verified against `argon2 0.5.3` |
| [`scrypt`](RustCrypto/scrypt.md) (RustCrypto) | Raw `Scrypt` KDF; bounded `ScryptPassword` records | Verified against `scrypt 0.12.0` |
## Stack migrations
diff --git a/docs/migration/RustCrypto/aes-gcm-siv.md b/docs/migration/RustCrypto/aes-gcm-siv.md
index b1d666aa..bff269c5 100644
--- a/docs/migration/RustCrypto/aes-gcm-siv.md
+++ b/docs/migration/RustCrypto/aes-gcm-siv.md
@@ -4,14 +4,14 @@
> `Payload { msg, aad }` with rscrypto's named types and a caller-buffer API.
> The RFC 8452 construction and combined ciphertext-and-tag bytes are unchanged.
-Verified against `aes-gcm-siv = "0.11.1"` and the `rscrypto` 0.7.8 line.
+Verified against `aes-gcm-siv = "0.12.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/aes128gcmsiv_oracle.rs`, `tests/aes256gcmsiv_oracle.rs`, and `tests/aead_wycheproof.rs`.
## TL;DR
-| | Before (`aes-gcm-siv` 0.11.x) | After (`rscrypto` 0.7.8) |
+| | Before (`aes-gcm-siv` 0.12.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `aes-gcm-siv = "0.11"` | `rscrypto = { version = "0.7.8", features = ["aes-gcm-siv"] }` |
+| Cargo dep | `aes-gcm-siv = "0.12"` | `rscrypto = { version = "0.8.1", features = ["aes-gcm-siv"] }` |
| Import | `use aes_gcm_siv::{Aes256GcmSiv, Key, Nonce, KeyInit, aead::{Aead, Payload}};` | `use rscrypto::{Aead, Aes256GcmSiv, Aes256GcmSivKey, aead::{Nonce96, expert::AeadWithNonce}};` |
| Encrypt | `cipher.encrypt(nonce, Payload { msg, aad })?` | `cipher.encrypt(&nonce, aad, msg, &mut out)?` |
@@ -20,13 +20,13 @@ Evidence: `tests/aes128gcmsiv_oracle.rs`, `tests/aes256gcmsiv_oracle.rs`, and `t
```toml
# Before
[dependencies]
-aes-gcm-siv = "0.11"
+aes-gcm-siv = "0.12"
```
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["aes-gcm-siv"] }
+rscrypto = { version = "0.8.1", features = ["aes-gcm-siv"] }
```
## Algorithm map
@@ -50,10 +50,10 @@ The migration recipe below uses `Aes256GcmSiv` throughout; substitute
use aes_gcm_siv::{Aes256GcmSiv, Key, Nonce, KeyInit};
use aes_gcm_siv::aead::{Aead, Payload};
-let key = Key::::from_slice(&[0u8; 32]);
-let cipher = Aes256GcmSiv::new(key);
-let nonce = Nonce::from_slice(&[0u8; 12]);
-let ct = cipher.encrypt(nonce, Payload { msg: plaintext, aad }).unwrap();
+let key = Key::::from([0u8; 32]);
+let cipher = Aes256GcmSiv::new(&key);
+let nonce = Nonce::from([0u8; 12]);
+let ct = cipher.encrypt(&nonce, Payload { msg: plaintext, aad }).unwrap();
```
```rust
diff --git a/docs/migration/RustCrypto/aes-gcm.md b/docs/migration/RustCrypto/aes-gcm.md
index f0698bae..f21f7304 100644
--- a/docs/migration/RustCrypto/aes-gcm.md
+++ b/docs/migration/RustCrypto/aes-gcm.md
@@ -4,14 +4,14 @@ Replace the `Aes256Gcm` / `Key` / `Nonce` /
`Payload { msg, aad }` builder with rscrypto's named types and a caller-buffer
API. The combined ciphertext-and-tag bytes remain interoperable.
-Verified against `aes-gcm = "0.11.0"` and the `rscrypto` 0.7.8 line.
+Verified against `aes-gcm = "0.11.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/aes128gcm_oracle.rs`, `tests/aes256gcm_oracle.rs`, and `tests/aead_wycheproof.rs`.
## TL;DR
-| | Before (`aes-gcm` 0.11.x) | After (`rscrypto` 0.7.8) |
+| | Before (`aes-gcm` 0.11.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `aes-gcm = "0.11"` | `rscrypto = { version = "0.7.8", features = ["aes-gcm"] }` |
+| Cargo dep | `aes-gcm = "0.11"` | `rscrypto = { version = "0.8.1", features = ["aes-gcm"] }` |
| Import | `use aes_gcm::{Aes256Gcm, Key, Nonce, KeyInit, aead::{Aead, Payload}};` | `use rscrypto::{Aead, Aes256Gcm, Aes256GcmKey, aead::{Nonce96, expert::AeadWithNonce}};` |
| Encrypt | `cipher.encrypt(nonce, Payload { msg, aad })?` (returns `Vec`) | `cipher.encrypt(&nonce, aad, msg, &mut out)?` (writes into caller buffer) |
@@ -26,7 +26,7 @@ aes-gcm = "0.11"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["aes-gcm"] }
+rscrypto = { version = "0.8.1", features = ["aes-gcm"] }
```
## Algorithm map
@@ -50,10 +50,10 @@ The migration recipe below uses `Aes256Gcm` throughout; substitute
use aes_gcm::{Aes256Gcm, Key, Nonce, KeyInit};
use aes_gcm::aead::{Aead, Payload};
-let key = Key::::from_slice(&[0u8; 32]);
-let cipher = Aes256Gcm::new(key);
-let nonce = Nonce::from_slice(&[0u8; 12]);
-let ct = cipher.encrypt(nonce, Payload { msg: plaintext, aad }).unwrap();
+let key = Key::::from([0u8; 32]);
+let cipher = Aes256Gcm::new(&key);
+let nonce = Nonce::from([0u8; 12]);
+let ct = cipher.encrypt(&nonce, Payload { msg: plaintext, aad }).unwrap();
// ct: Vec, last 16 bytes are the tag.
```
diff --git a/docs/migration/RustCrypto/argon2.md b/docs/migration/RustCrypto/argon2.md
index 911ef625..b4f0883b 100644
--- a/docs/migration/RustCrypto/argon2.md
+++ b/docs/migration/RustCrypto/argon2.md
@@ -5,16 +5,16 @@ rscrypto separates two jobs that should not share an API:
- `Argon2d`, `Argon2i`, and `Argon2id` are deterministic raw KDFs.
- `Argon2idPassword` generates canonical password records and verifies hostile PHC input under finite resource limits.
-The raw implementations are checked against RFC 9106 vectors and the RustCrypto `argon2 0.6.0-rc.8` oracle in `tests/argon2_vectors.rs`, `tests/argon2_differential.rs`, `tests/argon2_kernels.rs`, and `tests/argon2_parallel.rs`.
+The raw implementations are checked against RFC 9106 vectors and the RustCrypto `argon2 0.5.3` oracle in `tests/argon2_vectors.rs`, `tests/argon2_differential.rs`, `tests/argon2_kernels.rs`, and `tests/argon2_parallel.rs`.
## Cargo features
```toml
# Raw Argon2 KDF
-rscrypto = { version = "0.7.8", default-features = false, features = ["argon2"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["argon2"] }
# Password-record generation and verification with caller-owned entropy
-rscrypto = { version = "0.7.8", default-features = false, features = [
+rscrypto = { version = "0.8.1", default-features = false, features = [
"argon2",
"phc-strings",
] }
diff --git a/docs/migration/RustCrypto/ascon-aead.md b/docs/migration/RustCrypto/ascon-aead.md
index d7cb6374..2c8436e4 100644
--- a/docs/migration/RustCrypto/ascon-aead.md
+++ b/docs/migration/RustCrypto/ascon-aead.md
@@ -4,14 +4,14 @@
> `Payload { msg, aad }` with rscrypto's named types and a caller-buffer API.
> NIST SP 800-232 specifies a 16-byte key, nonce, and tag.
-Verified against `ascon-aead = "0.6.0"` and the `rscrypto` 0.7.8 line.
+Verified against `ascon-aead = "0.6.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/ascon_aead_oracle.rs`.
## TL;DR
-| | Before (`ascon-aead` 0.6.x) | After (`rscrypto` 0.7.8) |
+| | Before (`ascon-aead` 0.6.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `ascon-aead = "0.6"` | `rscrypto = { version = "0.7.8", features = ["ascon-aead"] }` |
+| Cargo dep | `ascon-aead = "0.6"` | `rscrypto = { version = "0.8.1", features = ["ascon-aead"] }` |
| Import | `use ascon_aead::{AsconAead128, Key, Nonce, aead::{Aead, KeyInit, Payload}};` | `use rscrypto::{Aead, AsconAead128, AsconAead128Key, aead::{Nonce128, expert::AeadWithNonce}};` |
| Encrypt | `cipher.encrypt(nonce, Payload { msg, aad })?` | `cipher.encrypt(&nonce, aad, msg, &mut out)?` |
@@ -26,7 +26,7 @@ ascon-aead = "0.6"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["ascon-aead"] }
+rscrypto = { version = "0.8.1", features = ["ascon-aead"] }
```
## Algorithm map
@@ -46,10 +46,10 @@ rscrypto = { version = "0.7.8", features = ["ascon-aead"] }
use ascon_aead::{AsconAead128, Key, Nonce};
use ascon_aead::aead::{Aead, KeyInit, Payload};
-let key = Key::::from_slice(&[0u8; 16]);
-let cipher = AsconAead128::new(key);
-let nonce = Nonce::::from_slice(&[0u8; 16]);
-let ct = cipher.encrypt(nonce, Payload { msg: plaintext, aad }).unwrap();
+let key = Key::::from([0u8; 16]);
+let cipher = AsconAead128::new(&key);
+let nonce = Nonce::::from([0u8; 16]);
+let ct = cipher.encrypt(&nonce, Payload { msg: plaintext, aad }).unwrap();
```
```rust
diff --git a/docs/migration/RustCrypto/ascon-hash.md b/docs/migration/RustCrypto/ascon-hash.md
index 51cada0a..538925b3 100644
--- a/docs/migration/RustCrypto/ascon-hash.md
+++ b/docs/migration/RustCrypto/ascon-hash.md
@@ -4,14 +4,14 @@
> the SP 800-232 Ascon-Hash256 parameter set; the output bytes, `update`, and
> `finalize` flow are unchanged.
-Verified against `ascon-hash = "0.4.0"` and the `rscrypto` 0.7.8 line.
+Verified against `ascon-hash = "0.4.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/ascon_official_vectors.rs`, `tests/ascon_hash_oracle.rs`, `tests/ascon_cxof_vectors.rs`, and `tests/ascon_differential.rs`.
## TL;DR
-| | Before (`ascon-hash` 0.4.x) | After (`rscrypto` 0.7.8) |
+| | Before (`ascon-hash` 0.4.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `ascon-hash = "0.4"` | `rscrypto = { version = "0.7.8", features = ["ascon-hash"] }` |
+| Cargo dep | `ascon-hash = "0.4"` | `rscrypto = { version = "0.8.1", features = ["ascon-hash"] }` |
| Import | `use ascon_hash::{AsconHash256, digest::Digest};` | `use rscrypto::{AsconHash256, Digest};` |
| Call | `AsconHash256::digest(data)` | `AsconHash256::digest(data)` |
@@ -26,7 +26,7 @@ ascon-hash = "0.4"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["ascon-hash"] }
+rscrypto = { version = "0.8.1", features = ["ascon-hash"] }
```
## Algorithm map
diff --git a/docs/migration/RustCrypto/blake2.md b/docs/migration/RustCrypto/blake2.md
index 740ccb12..907b5a5f 100644
--- a/docs/migration/RustCrypto/blake2.md
+++ b/docs/migration/RustCrypto/blake2.md
@@ -2,16 +2,16 @@
> Same algorithms (RFC 7693), with named convenience types replacing `Blake2b`-style generic instantiations and `Blake2b256::keyed_digest(key, data)` replacing the separate `Blake2bMac` MAC type.
-Verified against `blake2 = "0.11.0-rc.6"` and the `rscrypto` 0.7.8 line.
+Verified against `blake2 = "0.10.6"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/blake2_official_vectors.rs` and `tests/blake2_differential.rs`.
Code samples use the 0.10-style names where they remain the clearest migration
shape for existing projects.
## TL;DR
-| | Before (`blake2` 0.10.x) | After (`rscrypto` 0.7.8) |
+| | Before (`blake2` 0.10.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `blake2 = "0.10"` | `rscrypto = { version = "0.7.8", features = ["blake2b", "blake2s"] }` |
+| Cargo dep | `blake2 = "0.10"` | `rscrypto = { version = "0.8.1", features = ["blake2b", "blake2s"] }` |
| Import | `use blake2::{Blake2b512, Digest};` | `use rscrypto::{Blake2b512, Digest};` |
| Call | `Blake2b512::digest(data)` | `Blake2b512::digest(data)` |
@@ -28,7 +28,7 @@ blake2 = "0.10"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["blake2b", "blake2s"] }
+rscrypto = { version = "0.8.1", features = ["blake2b", "blake2s"] }
```
## Algorithm map
diff --git a/docs/migration/RustCrypto/chacha20poly1305.md b/docs/migration/RustCrypto/chacha20poly1305.md
index 1fb07d1c..e90e6462 100644
--- a/docs/migration/RustCrypto/chacha20poly1305.md
+++ b/docs/migration/RustCrypto/chacha20poly1305.md
@@ -5,14 +5,14 @@
> ChaCha20-Poly1305 and XChaCha20-Poly1305 operations preserve ciphertext and
> tag bytes.
-Verified against `chacha20poly1305 = "0.11.0"` and the `rscrypto` 0.7.8 line.
+Verified against `chacha20poly1305 = "0.11.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/chacha20poly1305.rs`, `tests/xchacha20poly1305.rs`, and `tests/aead_wycheproof.rs`.
## TL;DR
-| | Before (`chacha20poly1305` 0.11.x) | After (`rscrypto` 0.7.8) |
+| | Before (`chacha20poly1305` 0.11.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `chacha20poly1305 = "0.11"` | `rscrypto = { version = "0.7.8", features = ["chacha20poly1305", "xchacha20poly1305"] }` |
+| Cargo dep | `chacha20poly1305 = "0.11"` | `rscrypto = { version = "0.8.1", features = ["chacha20poly1305", "xchacha20poly1305"] }` |
| Import | `use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce, KeyInit, aead::{Aead, Payload}};` | `use rscrypto::{Aead, ChaCha20Poly1305, ChaCha20Poly1305Key, aead::{Nonce96, expert::AeadWithNonce}};` |
| Encrypt | `cipher.encrypt(nonce, Payload { msg, aad })?` | `cipher.encrypt(&nonce, aad, msg, &mut out)?` |
@@ -29,7 +29,7 @@ chacha20poly1305 = "0.11"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["chacha20poly1305", "xchacha20poly1305"] }
+rscrypto = { version = "0.8.1", features = ["chacha20poly1305", "xchacha20poly1305"] }
```
## Algorithm map
@@ -49,10 +49,10 @@ rscrypto = { version = "0.7.8", features = ["chacha20poly1305", "xchacha20poly13
use chacha20poly1305::{ChaCha20Poly1305, Key, Nonce, KeyInit};
use chacha20poly1305::aead::{Aead, Payload};
-let key = Key::from_slice(&[0u8; 32]); // Key is non-generic
-let cipher = ChaCha20Poly1305::new(key);
-let nonce = Nonce::from_slice(&[0u8; 12]);
-let ct = cipher.encrypt(nonce, Payload { msg: plaintext, aad }).unwrap();
+let key = Key::from([0u8; 32]); // Key is non-generic
+let cipher = ChaCha20Poly1305::new(&key);
+let nonce = Nonce::from([0u8; 12]);
+let ct = cipher.encrypt(&nonce, Payload { msg: plaintext, aad }).unwrap();
```
```rust
@@ -76,10 +76,10 @@ cipher.encrypt(&nonce, aad, plaintext, &mut ct)?;
use chacha20poly1305::{XChaCha20Poly1305, Key, XNonce, KeyInit};
use chacha20poly1305::aead::{Aead, Payload};
-let key = Key::from_slice(&[0u8; 32]);
-let cipher = XChaCha20Poly1305::new(key);
-let nonce = XNonce::from_slice(&[0u8; 24]);
-let ct = cipher.encrypt(nonce, Payload { msg: plaintext, aad }).unwrap();
+let key = Key::from([0u8; 32]);
+let cipher = XChaCha20Poly1305::new(&key);
+let nonce = XNonce::from([0u8; 24]);
+let ct = cipher.encrypt(&nonce, Payload { msg: plaintext, aad }).unwrap();
```
```rust
diff --git a/docs/migration/RustCrypto/ed25519-dalek.md b/docs/migration/RustCrypto/ed25519-dalek.md
index 14e28ac7..11e904e4 100644
--- a/docs/migration/RustCrypto/ed25519-dalek.md
+++ b/docs/migration/RustCrypto/ed25519-dalek.md
@@ -4,14 +4,14 @@
> `Ed25519SecretKey` / `Ed25519PublicKey` / `Ed25519Signature`. Signing
> preserves RFC 8032 output bytes, and the rscrypto verifier is always strict.
-Verified against `ed25519-dalek = "2.2.0"` and the `rscrypto` 0.7.8 line.
+Verified against `ed25519-dalek = "3.0.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/ed25519_rfc8032_vectors.rs`, `tests/ed25519_oracle.rs`, and `tests/ed25519_wycheproof.rs`.
## TL;DR
-| | Before (`ed25519-dalek` 2.x) | After (`rscrypto` 0.7.8) |
+| | Before (`ed25519-dalek` 3.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `ed25519-dalek = "2.2"` | `rscrypto = { version = "0.7.8", features = ["ed25519"] }` |
+| Cargo dep | `ed25519-dalek = "3.0"` | `rscrypto = { version = "0.8.1", features = ["ed25519"] }` |
| Import | `use ed25519_dalek::{SigningKey, Signer, Verifier};` | `use rscrypto::{Ed25519SecretKey, Ed25519PublicKey, Ed25519Signature};` |
| Sign | `signing_key.sign(msg)` | `secret.sign(msg)` |
| Verify | `verifying_key.verify_strict(msg, &sig)?` | `public_key.verify(msg, &sig)?` |
@@ -21,13 +21,13 @@ Evidence: `tests/ed25519_rfc8032_vectors.rs`, `tests/ed25519_oracle.rs`, and `te
```toml
# Before
[dependencies]
-ed25519-dalek = "2.2"
+ed25519-dalek = "3.0"
```
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["ed25519"] }
+rscrypto = { version = "0.8.1", features = ["ed25519"] }
```
The `ed25519` feature implies `sha2` (Ed25519 uses SHA-512 internally per RFC 8032).
@@ -119,7 +119,7 @@ rscrypto's `verify` is *always* strict (rejects small-order public keys, non-can
### Keypair (combined secret + public)
```rust
-// Before: no first-class Keypair type in dalek 2.x; use SigningKey directly.
+// Before: no first-class Keypair type in dalek 3.x; use SigningKey directly.
let signing_key = SigningKey::from_bytes(&seed);
let pk = signing_key.verifying_key();
```
diff --git a/docs/migration/RustCrypto/hkdf.md b/docs/migration/RustCrypto/hkdf.md
index 7fe9290f..a54b6b52 100644
--- a/docs/migration/RustCrypto/hkdf.md
+++ b/docs/migration/RustCrypto/hkdf.md
@@ -2,14 +2,14 @@
> Replace `Hkdf::::new(Some(salt), ikm)` with `HkdfSha256::new(salt, ikm)`. The `Option<&[u8]>` salt becomes a plain `&[u8]` (empty slice == "no salt"); fused one-shot `HkdfSha256::derive_array::(...)` collapses extract+expand.
-Verified against `hkdf = "0.13.0"` and the `rscrypto` 0.7.8 line.
+Verified against `hkdf = "0.13.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/hkdf_sha256_vectors.rs`, `tests/hkdf_sha384_vectors.rs`, `tests/hkdf_sha512_vectors.rs`, the HKDF proptests, and `tests/hkdf_wycheproof.rs`.
## TL;DR
-| | Before (`hkdf` 0.13.x) | After (`rscrypto` 0.7.8) |
+| | Before (`hkdf` 0.13.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `hkdf = "0.13"` + `sha2 = "0.11"` | `rscrypto = { version = "0.7.8", features = ["hkdf"] }` |
+| Cargo dep | `hkdf = "0.13"` + `sha2 = "0.11"` | `rscrypto = { version = "0.8.1", features = ["hkdf"] }` |
| Import | `use hkdf::Hkdf; use sha2::Sha256;` | `use rscrypto::HkdfSha256;` |
| Call | `Hkdf::::new(Some(salt), ikm).expand(info, &mut okm)?` | `HkdfSha256::new(salt, ikm).expand(info, &mut okm)?` |
@@ -25,7 +25,7 @@ sha2 = "0.11"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["hkdf"] }
+rscrypto = { version = "0.8.1", features = ["hkdf"] }
```
The `hkdf` feature implies `hmac` which implies `sha2`.
diff --git a/docs/migration/RustCrypto/hmac.md b/docs/migration/RustCrypto/hmac.md
index b037de9b..edc7745c 100644
--- a/docs/migration/RustCrypto/hmac.md
+++ b/docs/migration/RustCrypto/hmac.md
@@ -2,14 +2,14 @@
> Replace `Hmac::` / `Hmac::` (generic over digest) with named rscrypto types such as `HmacSha256` and `HmacSha3_256`. Key construction is infallible, `finalize()` borrows, and one-shot helpers return typed tags with sealed comparison decisions.
-Verified against `hmac = "0.13.0"` and the `rscrypto` 0.7.8 line.
+Verified against `hmac = "0.13.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/hmac_sha256_vectors.rs`, `tests/hmac_sha2_family_vectors.rs`, `tests/hmac_sha3_vectors.rs`, the HMAC proptests, and `tests/hmac_wycheproof.rs`.
## TL;DR
-| | Before (`hmac` 0.13.x) | After (`rscrypto` 0.7.8) |
+| | Before (`hmac` 0.13.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `hmac = "0.13"` + `sha2 = "0.11"` | `rscrypto = { version = "0.7.8", features = ["hmac"] }` |
+| Cargo dep | `hmac = "0.13"` + `sha2 = "0.11"` | `rscrypto = { version = "0.8.1", features = ["hmac"] }` |
| Import | `use hmac::{Hmac, Mac, KeyInit}; use sha2::Sha256;` | `use rscrypto::{HmacSha256, Mac};` |
| Call | `Hmac::::new_from_slice(key).unwrap().chain_update(data).finalize().into_bytes()` | `HmacSha256::mac(key, data)` |
@@ -25,7 +25,7 @@ sha2 = "0.11" # required as the generic parameter
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["hmac"] }
+rscrypto = { version = "0.8.1", features = ["hmac"] }
```
The `hmac` feature implies `sha2`: no second dep to manage for SHA-2 HMAC. Use `features = ["hmac-sha3"]` for HMAC-SHA3, or `features = ["macs"]` when you want both SHA-2 and SHA-3 HMAC families.
diff --git a/docs/migration/RustCrypto/p256.md b/docs/migration/RustCrypto/p256.md
index affa3d7b..74ffe26c 100644
--- a/docs/migration/RustCrypto/p256.md
+++ b/docs/migration/RustCrypto/p256.md
@@ -13,9 +13,9 @@ Evidence: `tests/ecdsa_oracle.rs`, `src/auth/ecdsa.rs` unit tests, and
## TL;DR
-| | Before (`p256` 0.14) | After (`rscrypto` 0.7.8) |
+| | Before (`p256` 0.14) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `p256 = { version = "0.14", features = ["ecdsa"] }` | `rscrypto = { version = "0.7.8", default-features = false, features = ["ecdsa-p256"] }` |
+| Cargo dep | `p256 = { version = "0.14", features = ["ecdsa"] }` | `rscrypto = { version = "0.8.1", default-features = false, features = ["ecdsa-p256"] }` |
| Import | `use p256::ecdsa::{SigningKey, Signature};` | `use rscrypto::{EcdsaP256SecretKey, EcdsaP256PublicKey, EcdsaP256Signature};` |
| Sign | `signing_key.sign(msg)` | `secret.try_sign(msg)?` |
| Verify | `verifying_key.verify(msg, &sig)?` | `public.verify(msg, &sig)?` |
@@ -27,7 +27,7 @@ Evidence: `tests/ecdsa_oracle.rs`, `src/auth/ecdsa.rs` unit tests, and
p256 = { version = "0.14", features = ["ecdsa"] }
# After
-rscrypto = { version = "0.7.8", default-features = false, features = ["ecdsa-p256"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["ecdsa-p256"] }
```
The `ecdsa-p256` feature implies `hmac`, which is used for deterministic nonce
diff --git a/docs/migration/RustCrypto/p384.md b/docs/migration/RustCrypto/p384.md
index 3032e5c0..4f1328b8 100644
--- a/docs/migration/RustCrypto/p384.md
+++ b/docs/migration/RustCrypto/p384.md
@@ -6,16 +6,16 @@ rscrypto supports the fixed P-384/SHA-384 ECDSA profile, raw `r || s`
signatures, DER signature import, SEC1/SPKI public-key import, deterministic
signing, and caller-blinded signing.
-Verified against RustCrypto `p384 0.13.1`.
+Verified against RustCrypto `p384 0.14.0`.
Evidence: `tests/ecdsa_oracle.rs`, `src/auth/ecdsa.rs` unit tests, and
`fuzz/target_impls/auth_ecdsa_verify.rs` /
`fuzz/target_impls/auth_ecdsa_sign.rs`.
## TL;DR
-| | Before (`p384` 0.13) | After (`rscrypto` 0.7.8) |
+| | Before (`p384` 0.14) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `p384 = { version = "0.13", features = ["ecdsa"] }` | `rscrypto = { version = "0.7.8", default-features = false, features = ["ecdsa-p384"] }` |
+| Cargo dep | `p384 = { version = "0.14", features = ["ecdsa"] }` | `rscrypto = { version = "0.8.1", default-features = false, features = ["ecdsa-p384"] }` |
| Import | `use p384::ecdsa::{SigningKey, Signature};` | `use rscrypto::{EcdsaP384SecretKey, EcdsaP384PublicKey, EcdsaP384Signature};` |
| Sign | `signing_key.sign(msg)` | `secret.try_sign(msg)?` |
| Verify | `verifying_key.verify(msg, &sig)?` | `public.verify(msg, &sig)?` |
@@ -24,10 +24,10 @@ Evidence: `tests/ecdsa_oracle.rs`, `src/auth/ecdsa.rs` unit tests, and
```toml
# Before
-p384 = { version = "0.13", features = ["ecdsa"] }
+p384 = { version = "0.14", features = ["ecdsa"] }
# After
-rscrypto = { version = "0.7.8", default-features = false, features = ["ecdsa-p384"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["ecdsa-p384"] }
```
The `ecdsa-p384` feature implies `hmac`, which is used for deterministic nonce
diff --git a/docs/migration/RustCrypto/pbkdf2.md b/docs/migration/RustCrypto/pbkdf2.md
index e7433ecb..4ac1a8ed 100644
--- a/docs/migration/RustCrypto/pbkdf2.md
+++ b/docs/migration/RustCrypto/pbkdf2.md
@@ -2,14 +2,14 @@
> Replace the free function `pbkdf2_hmac::(password, salt, iters, &mut out)` with `Pbkdf2Sha256::derive_key_array::(password, salt, iters)?`. The password helpers enforce the current PBKDF2 iteration and salt floors by default, while `*_primitive` APIs remain available for RFC vectors and legacy compatibility.
-Verified against `pbkdf2 = "0.13.0"` and the `rscrypto` 0.7.8 line.
+Verified against `pbkdf2 = "0.13.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/pbkdf2_kat_vectors.rs`, `tests/pbkdf2_differential.rs`, and `tests/pbkdf2_wycheproof.rs`.
## TL;DR
-| | Before (`pbkdf2` 0.13.x) | After (`rscrypto` 0.7.8) |
+| | Before (`pbkdf2` 0.13.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `pbkdf2 = "0.13"` + `sha2 = "0.11"` | `rscrypto = { version = "0.7.8", features = ["pbkdf2"] }` |
+| Cargo dep | `pbkdf2 = "0.13"` + `sha2 = "0.11"` | `rscrypto = { version = "0.8.1", features = ["pbkdf2"] }` |
| Import | `use pbkdf2::pbkdf2_hmac; use sha2::Sha256;` | `use rscrypto::Pbkdf2Sha256;` |
| Call | `pbkdf2_hmac::(pw, salt, iters, &mut okm)` | `Pbkdf2Sha256::derive_key(pw, salt, iters, &mut okm)?` |
@@ -25,7 +25,7 @@ sha2 = "0.11"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["pbkdf2"] }
+rscrypto = { version = "0.8.1", features = ["pbkdf2"] }
```
The `pbkdf2` feature implies `hmac` which implies `sha2`.
diff --git a/docs/migration/RustCrypto/rsa.md b/docs/migration/RustCrypto/rsa.md
index f565ef57..7bcf1867 100644
--- a/docs/migration/RustCrypto/rsa.md
+++ b/docs/migration/RustCrypto/rsa.md
@@ -17,10 +17,10 @@ interoperability tests used by the stack migration guides.
rsa = { version = "0.9", features = ["sha2"] }
# After
-rscrypto = { version = "0.7.8", default-features = false, features = ["rsa"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["rsa"] }
# After, when generating keys or using randomized private/encryption APIs
-rscrypto = { version = "0.7.8", default-features = false, features = ["rsa", "getrandom"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["rsa", "getrandom"] }
```
## Map
diff --git a/docs/migration/RustCrypto/scrypt.md b/docs/migration/RustCrypto/scrypt.md
index ff188461..46668b11 100644
--- a/docs/migration/RustCrypto/scrypt.md
+++ b/docs/migration/RustCrypto/scrypt.md
@@ -11,10 +11,10 @@ The implementation is checked against RFC 7914 vectors and the RustCrypto `scryp
```toml
# Raw scrypt KDF
-rscrypto = { version = "0.7.8", default-features = false, features = ["scrypt"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["scrypt"] }
# Password-record generation and verification with caller-owned entropy
-rscrypto = { version = "0.7.8", default-features = false, features = [
+rscrypto = { version = "0.8.1", default-features = false, features = [
"scrypt",
"phc-strings",
] }
diff --git a/docs/migration/RustCrypto/sha2.md b/docs/migration/RustCrypto/sha2.md
index 60f62560..116d77ff 100644
--- a/docs/migration/RustCrypto/sha2.md
+++ b/docs/migration/RustCrypto/sha2.md
@@ -2,14 +2,14 @@
> Same algorithms (FIPS 180-4), same `Sha224` / `Sha256` / `Sha384` / `Sha512` / `Sha512_256` names, same `new()` / `update()` / `finalize()` shape. The output type changes from `Output` (a `GenericArray`) to `[u8; N]`, and `finalize()` now borrows instead of consuming.
-Verified against `sha2 = "0.11.0"` and the `rscrypto` 0.7.8 line.
+Verified against `sha2 = "0.11.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/sha2_official_vectors.rs`, `tests/sha256_differential.rs`, and `tests/sha512_differential.rs`.
## TL;DR
-| | Before (`sha2` 0.11.x) | After (`rscrypto` 0.7.8) |
+| | Before (`sha2` 0.11.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `sha2 = "0.11"` | `rscrypto = { version = "0.7.8", features = ["sha2"] }` |
+| Cargo dep | `sha2 = "0.11"` | `rscrypto = { version = "0.8.1", features = ["sha2"] }` |
| Import | `use sha2::{Sha256, Digest};` | `use rscrypto::Sha256;` |
| Call | `Sha256::digest(data)` | `Sha256::digest(data)` |
@@ -24,7 +24,7 @@ sha2 = "0.11"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["sha2"] }
+rscrypto = { version = "0.8.1", features = ["sha2"] }
```
## Algorithm map
diff --git a/docs/migration/RustCrypto/sha3.md b/docs/migration/RustCrypto/sha3.md
index b2b59e0a..e27ce7f1 100644
--- a/docs/migration/RustCrypto/sha3.md
+++ b/docs/migration/RustCrypto/sha3.md
@@ -2,14 +2,14 @@
> Same algorithms (FIPS 202: SHA3-224/256/384/512, SHAKE128/256; SP 800-185: cSHAKE128/256), `[u8; N]` outputs replace `Output`, and the SHAKE/cSHAKE reader chain uses `squeeze(&mut out)`.
-Verified against `sha3 = "0.12.0"` and the `rscrypto` 0.7.8 line.
+Verified against `sha3 = "0.12.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/sha3_official_vectors.rs`, `tests/sha3_differential.rs`, `tests/shake128_differential.rs`, `tests/shake256_differential.rs`, `tests/cshake256_nist_vectors.rs`, and `tests/cshake256_differential.rs`.
## TL;DR
-| | Before (`sha3` 0.12.x) | After (`rscrypto` 0.7.8) |
+| | Before (`sha3` 0.12.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `sha3 = "0.12"` | `rscrypto = { version = "0.7.8", features = ["sha3"] }` |
+| Cargo dep | `sha3 = "0.12"` | `rscrypto = { version = "0.8.1", features = ["sha3"] }` |
| Import | `use sha3::{Sha3_256, Digest};` | `use rscrypto::{Sha3_256, Digest};` |
| Call | `Sha3_256::digest(data)` | `Sha3_256::digest(data)` |
@@ -24,7 +24,7 @@ sha3 = "0.12"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["sha3"] }
+rscrypto = { version = "0.8.1", features = ["sha3"] }
```
## Algorithm map
diff --git a/docs/migration/RustCrypto/x25519-dalek.md b/docs/migration/RustCrypto/x25519-dalek.md
index b2e21aff..5f8590b0 100644
--- a/docs/migration/RustCrypto/x25519-dalek.md
+++ b/docs/migration/RustCrypto/x25519-dalek.md
@@ -5,14 +5,14 @@
> `X25519SharedSecret`. Valid RFC 7748 inputs produce the same shared-secret
> bytes; rscrypto returns an explicit error for an all-zero result.
-Verified against `x25519-dalek = "2.0.1"` and the `rscrypto` 0.7.8 line.
+Verified against `x25519-dalek = "3.0.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/x25519_vectors.rs`, `tests/x25519_oracle.rs`, and `tests/x25519_wycheproof.rs`.
## TL;DR
-| | Before (`x25519-dalek` 2.x) | After (`rscrypto` 0.7.8) |
+| | Before (`x25519-dalek` 3.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `x25519-dalek = { version = "2.0", features = ["static_secrets"] }` | `rscrypto = { version = "0.7.8", features = ["x25519"] }` |
+| Cargo dep | `x25519-dalek = { version = "3.0", features = ["static_secrets"] }` | `rscrypto = { version = "0.8.1", features = ["x25519"] }` |
| Import | `use x25519_dalek::{StaticSecret, PublicKey};` | `use rscrypto::{X25519SecretKey, X25519PublicKey};` |
| DH | `secret.diffie_hellman(&peer_pub)` (returns `SharedSecret`) | `secret.diffie_hellman(&peer_pub)?` (returns `Result`) |
@@ -21,13 +21,13 @@ Evidence: `tests/x25519_vectors.rs`, `tests/x25519_oracle.rs`, and `tests/x25519
```toml
# Before
[dependencies]
-x25519-dalek = { version = "2.0", features = ["static_secrets"] }
+x25519-dalek = { version = "3.0", features = ["static_secrets"] }
```
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["x25519"] }
+rscrypto = { version = "0.8.1", features = ["x25519"] }
```
The `x25519` feature has no transitive dependencies: X25519 needs nothing beyond Curve25519 arithmetic.
diff --git a/docs/migration/aegis.md b/docs/migration/aegis.md
index 83796170..fd8f771c 100644
--- a/docs/migration/aegis.md
+++ b/docs/migration/aegis.md
@@ -4,14 +4,14 @@
> rscrypto's caller-buffer `encrypt(&nonce, aad, msg, &mut out)`. The mapped
> AEGIS-256 operation preserves ciphertext and tag bytes.
-Verified against `aegis = "0.9.12"` and the `rscrypto` 0.7.8 line.
+Verified against `aegis = "0.9.15"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/aegis256_oracle.rs` and `tests/aead_wycheproof.rs`.
## TL;DR
-| | Before (`aegis` 0.9.x) | After (`rscrypto` 0.7.8) |
+| | Before (`aegis` 0.9.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `aegis = "0.9"` | `rscrypto = { version = "0.7.8", features = ["aegis256"] }` |
+| Cargo dep | `aegis = "0.9"` | `rscrypto = { version = "0.8.1", features = ["aegis256"] }` |
| Import | `use aegis::aegis256::Aegis256;` | `use rscrypto::{Aead, Aegis256, Aegis256Key, aead::{Nonce256, expert::AeadWithNonce}};` |
| Encrypt | `Aegis256::<16>::new(&key, &nonce).encrypt(msg, aad) -> (Vec, [u8; 16])` | `cipher.encrypt(&nonce, aad, msg, &mut out)?` |
@@ -26,7 +26,7 @@ aegis = "0.9"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["aegis256"] }
+rscrypto = { version = "0.8.1", features = ["aegis256"] }
```
## Algorithm map
diff --git a/docs/migration/api-boundaries.md b/docs/migration/api-boundaries.md
index 5ceb929c..74e02204 100644
--- a/docs/migration/api-boundaries.md
+++ b/docs/migration/api-boundaries.md
@@ -40,6 +40,28 @@ protocol defines a shorter value.
Panicking `random()` constructors were removed. Use `try_random()` or the
type-specific `try_generate()` method and propagate entropy failures.
+## RSA caller-supplied blinding
+
+Caller-supplied RSA blinding methods now take one borrowed `RsaBlindingPair`
+instead of adjacent factor and inverse slices:
+
+```rust
+use rscrypto::{RsaBlindingPair, RsaPkcs1v15Profile};
+
+let blinding = RsaBlindingPair::new(blinding_factor, blinding_factor_inverse);
+private_key.sign_pkcs1v15_with_blinding_factor(
+ RsaPkcs1v15Profile::Sha256,
+ message,
+ blinding,
+ signature,
+)?;
+# Ok::<(), rscrypto::RsaPrivateOpError>(())
+```
+
+The pair borrows both inputs without allocation or copying their bytes. Every
+accepting operation still validates their width, range, and inverse relation
+against its RSA key. Error variants and output clearing are unchanged.
+
## AEAD nonces
Normal `Aead` sealing generates a fresh OS nonce. AES-GCM also supports the
diff --git a/docs/migration/aws-lc-rs.md b/docs/migration/aws-lc-rs.md
index a8fab39c..261b7f99 100644
--- a/docs/migration/aws-lc-rs.md
+++ b/docs/migration/aws-lc-rs.md
@@ -12,7 +12,7 @@ The direct examples below are covered by `tests/migration_aws_lc_rs.rs`.
aws-lc-rs = "1.17"
# After: enable only the primitives you use
-rscrypto = { version = "0.7.8", default-features = false, features = ["sha2", "hmac", "hkdf", "pbkdf2", "aes-gcm", "chacha20poly1305", "ecdsa", "ed25519", "x25519", "rsa"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["sha2", "hmac", "hkdf", "pbkdf2", "aes-gcm", "chacha20poly1305", "ecdsa", "ed25519", "x25519", "rsa"] }
```
## Map
diff --git a/docs/migration/blake3.md b/docs/migration/blake3.md
index 86b7e509..8e70bd1b 100644
--- a/docs/migration/blake3.md
+++ b/docs/migration/blake3.md
@@ -4,14 +4,14 @@
> Unkeyed hashes return `[u8; 32]`; keyed hashes return `Blake3KeyedHash` so
> authenticator comparison requires an explicit sealed decision.
-Verified against `blake3 = "1.8.5"` and the `rscrypto` 0.7.8 line.
+Verified against `blake3 = "1.8.6"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/blake3_official_vectors.rs` and `tests/blake3_differential.rs`.
## TL;DR
-| | Before (`blake3` 1.x) | After (`rscrypto` 0.7.8) |
+| | Before (`blake3` 1.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `blake3 = "1.8"` | `rscrypto = { version = "0.7.8", features = ["blake3"] }` |
+| Cargo dep | `blake3 = "1.8"` | `rscrypto = { version = "0.8.1", features = ["blake3"] }` |
| Import | `use blake3::Hasher;` | `use rscrypto::{Blake3, prelude::*};` |
| Call | `blake3::hash(data).as_bytes()` | `&Blake3::digest(data)` |
@@ -26,7 +26,7 @@ blake3 = "1.8"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["blake3"] }
+rscrypto = { version = "0.8.1", features = ["blake3"] }
```
## API patterns
diff --git a/docs/migration/crc-fast.md b/docs/migration/crc-fast.md
index dc0bf553..d2b791f8 100644
--- a/docs/migration/crc-fast.md
+++ b/docs/migration/crc-fast.md
@@ -4,15 +4,15 @@ Replace `crc-fast`'s enum-driven `checksum(CrcAlgorithm::*, data)` calls with
rscrypto's named CRC types. The mapped algorithms keep the same CRC parameters,
but rscrypto returns each algorithm's natural integer width instead of `u64`.
-Verified against `crc-fast = "1.10.0"` and the `rscrypto` 0.7.8 line.
+Verified against `crc-fast = "1.10.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/crc16_properties.rs`, `tests/crc32_properties.rs`, and
`tests/crc64_properties.rs`.
## TL;DR
-| | Before (`crc-fast` 1.x) | After (`rscrypto` 0.7.8) |
+| | Before (`crc-fast` 1.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `crc-fast = "1.10"` | `rscrypto = { version = "0.7.8", features = ["crc32", "crc64"] }` |
+| Cargo dep | `crc-fast = "1.10"` | `rscrypto = { version = "0.8.1", features = ["crc32", "crc64"] }` |
| Import | `use crc_fast::{checksum, CrcAlgorithm};` | `use rscrypto::checksum::{Checksum, Crc32};` |
| Call | `checksum(CrcAlgorithm::Crc32IsoHdlc, data) as u32` | `Crc32::checksum(data)` |
@@ -27,7 +27,7 @@ crc-fast = "1.10"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["crc32", "crc64"] }
+rscrypto = { version = "0.8.1", features = ["crc32", "crc64"] }
```
Add `crc16` or `crc24` only when you migrate a mapped variant from those
diff --git a/docs/migration/crc.md b/docs/migration/crc.md
index 91b06595..89102218 100644
--- a/docs/migration/crc.md
+++ b/docs/migration/crc.md
@@ -4,14 +4,14 @@ Replace supported `Crc::::new(&CRC_*)` configurations with rscrypto's named
CRC types. The mapped algorithms retain their output parameters and have a
portable fallback plus selected target-specific backends.
-Verified against `crc = "3.4.0"` and the `rscrypto` 0.7.8 line.
+Verified against `crc = "3.4.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/crc16_properties.rs`, `tests/crc24_properties.rs`, `tests/crc32_properties.rs`, and `tests/crc64_properties.rs`.
## TL;DR
-| | Before (`crc` 3.x) | After (`rscrypto` 0.7.8) |
+| | Before (`crc` 3.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `crc = "3.4"` | `rscrypto = { version = "0.7.8", features = ["checksums"] }` |
+| Cargo dep | `crc = "3.4"` | `rscrypto = { version = "0.8.1", features = ["checksums"] }` |
| Import | `use crc::{Crc, CRC_32_ISO_HDLC};` | `use rscrypto::checksum::{Checksum, Crc32};` |
| Call | `Crc::::new(&CRC_32_ISO_HDLC).checksum(data)` | `Crc32::checksum(data)` |
@@ -26,7 +26,7 @@ crc = "3.4"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["checksums"] }
+rscrypto = { version = "0.8.1", features = ["checksums"] }
```
`features = ["checksums"]` enables every CRC family. To trim the build, pick only what you use: `crc16`, `crc24`, `crc32` (covers IEEE + Castagnoli), `crc64` (covers XZ + NVME).
diff --git a/docs/migration/crc32c.md b/docs/migration/crc32c.md
index 6c65db57..8f5d3fc3 100644
--- a/docs/migration/crc32c.md
+++ b/docs/migration/crc32c.md
@@ -9,9 +9,9 @@ Output is covered by the CRC-32C oracle/property tests in `tests/crc32_propertie
## TL;DR
-| | Before (`crc32c` 0.6.x) | After (`rscrypto` 0.7.8) |
+| | Before (`crc32c` 0.6.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `crc32c = "0.6"` | `rscrypto = { version = "0.7.8", features = ["crc32"] }` |
+| Cargo dep | `crc32c = "0.6"` | `rscrypto = { version = "0.8.1", features = ["crc32"] }` |
| Import | `use crc32c::{crc32c, crc32c_append, crc32c_combine};` | `use rscrypto::checksum::{Checksum, ChecksumCombine, Crc32C};` |
| Call | `crc32c(data)` | `Crc32C::checksum(data)` |
@@ -26,7 +26,7 @@ crc32c = "0.6"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["crc32"] }
+rscrypto = { version = "0.8.1", features = ["crc32"] }
```
The `crc32` feature exposes both `Crc32` (IEEE) and `Crc32C` (Castagnoli).
diff --git a/docs/migration/crc32fast.md b/docs/migration/crc32fast.md
index 1aa5b225..e5768a69 100644
--- a/docs/migration/crc32fast.md
+++ b/docs/migration/crc32fast.md
@@ -6,9 +6,9 @@ Output is covered by the CRC-32 oracle/property tests in `tests/crc32_properties
## TL;DR
-| | Before (`crc32fast` 1.x) | After (`rscrypto` 0.7.8) |
+| | Before (`crc32fast` 1.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `crc32fast = "1.5"` | `rscrypto = { version = "0.7.8", features = ["crc32"] }` |
+| Cargo dep | `crc32fast = "1.5"` | `rscrypto = { version = "0.8.1", features = ["crc32"] }` |
| Import | `use crc32fast::Hasher;` | `use rscrypto::checksum::{Checksum, Crc32};` |
| Call | `crc32fast::hash(data)` | `Crc32::checksum(data)` |
@@ -23,7 +23,7 @@ crc32fast = "1.5"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["crc32"] }
+rscrypto = { version = "0.8.1", features = ["crc32"] }
```
`features = ["crc32"]` exposes both CRC-32/IEEE (`Crc32`) and
diff --git a/docs/migration/crc64fast.md b/docs/migration/crc64fast.md
index 67dedab3..1b3e3622 100644
--- a/docs/migration/crc64fast.md
+++ b/docs/migration/crc64fast.md
@@ -2,14 +2,14 @@
> Same CRC-64/XZ output with method renames from `Digest` / `write` / `sum64` to `Crc64` / `update` / `finalize`. The aside below also covers `crc64fast-nvme` → `Crc64Nvme`.
-Verified against `crc64fast = "1.1.0"` for `Crc64`; `Crc64Nvme` oracle coverage uses `crc-fast = "1.10.0"` and the `rscrypto` 0.7.8 line.
+Verified against `crc64fast = "1.1.0"` for `Crc64`; `Crc64Nvme` oracle coverage uses `crc-fast = "1.10.0"` and the `rscrypto` 0.8.1 line.
Evidence: `tests/crc64_properties.rs` compares one-shot, streaming, and combine output against those oracle crates.
## TL;DR
-| | Before (`crc64fast` 1.x) | After (`rscrypto` 0.7.8) |
+| | Before (`crc64fast` 1.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `crc64fast = "1.1"` | `rscrypto = { version = "0.7.8", features = ["crc64"] }` |
+| Cargo dep | `crc64fast = "1.1"` | `rscrypto = { version = "0.8.1", features = ["crc64"] }` |
| Import | `use crc64fast::Digest;` | `use rscrypto::checksum::{Checksum, Crc64};` |
| Call | `Digest::new(); .write(data); .sum64()` | `Crc64::new(); .update(data); .finalize()` |
@@ -24,7 +24,7 @@ crc64fast = "1.1"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["crc64"] }
+rscrypto = { version = "0.8.1", features = ["crc64"] }
```
`features = ["crc64"]` enables both `Crc64` (XZ / ECMA-182) and `Crc64Nvme`.
@@ -120,7 +120,7 @@ Drop both `crc64fast` and `crc64fast-nvme` from Cargo.toml; `features = ["crc64"
also matter. Do not substitute CRC-64/ISO.
- **No reset, no resume in `crc64fast`.** Build a fresh `Digest` per checksum. rscrypto adds `.reset()` and `Crc64::resume(prev)` on top of the same shape.
- **`no_std`.** `crc64fast` requires `std` for SIMD detection. rscrypto's `Crc64` is `no_std`-capable; runtime detection is gated on the `std` feature, with compile-time `target_feature` selection in `no_std` builds and a portable fallback always present.
-- **Hardware coverage.** `crc64fast` ships x86_64 (PCLMUL) and aarch64 (PMULL) backends. rscrypto adds VPCLMULQDQ (large buffers on x86_64), SVE2 PMULL (aarch64), VPMSUMD (Power), VGFM (s390x), and Zbc/Zvbc (RISC-V).
+- **Hardware coverage.** `crc64fast` ships x86_64 (PCLMUL) and aarch64 (PMULL) backends. rscrypto adds VPCLMULQDQ (large buffers on x86_64), SVE2 PMULL (aarch64), VPMSUMD (Power), and VGFM (s390x). RISC-V uses the portable slice-by-16 implementation.
- **Force a backend.** `RSCRYPTO_CRC64_FORCE=portable` selects the portable
CRC-64 runtime backend in `std` builds. The `portable-only` feature makes
runtime capability detection ignore host acceleration; see
diff --git a/docs/migration/dryoc.md b/docs/migration/dryoc.md
index a134aad4..031710dd 100644
--- a/docs/migration/dryoc.md
+++ b/docs/migration/dryoc.md
@@ -14,7 +14,7 @@ The direct BLAKE2b, Ed25519, and X25519 examples below are covered by
dryoc = "0.8"
# After: choose only the primitive features you use
-rscrypto = { version = "0.7.8", default-features = false, features = ["blake2b", "ed25519", "x25519", "argon2"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["blake2b", "ed25519", "x25519", "argon2"] }
```
## Map
diff --git a/docs/migration/openssl.md b/docs/migration/openssl.md
index efbe7671..3ca47e4e 100644
--- a/docs/migration/openssl.md
+++ b/docs/migration/openssl.md
@@ -15,7 +15,7 @@ RSA test lane.
openssl = "0.10"
# After: choose only the primitive features you use
-rscrypto = { version = "0.7.8", default-features = false, features = ["sha2", "hmac", "hkdf", "pbkdf2", "aes-gcm", "chacha20poly1305", "ed25519", "x25519", "rsa"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["sha2", "hmac", "hkdf", "pbkdf2", "aes-gcm", "chacha20poly1305", "ed25519", "x25519", "rsa"] }
```
## Map
diff --git a/docs/migration/rapidhash.md b/docs/migration/rapidhash.md
index 4c346ea2..2172298e 100644
--- a/docs/migration/rapidhash.md
+++ b/docs/migration/rapidhash.md
@@ -8,7 +8,7 @@ inherent methods can run at compile time.
```toml
[dependencies]
-rscrypto = { version = "0.7.8", features = ["rapidhash"] }
+rscrypto = { version = "0.8.1", features = ["rapidhash"] }
```
```rust
diff --git a/docs/migration/ring.md b/docs/migration/ring.md
index d6664eec..2600a697 100644
--- a/docs/migration/ring.md
+++ b/docs/migration/ring.md
@@ -13,7 +13,7 @@ The direct examples below are covered by `tests/migration_ring.rs`.
ring = "0.17"
# After: choose only the primitive features you use
-rscrypto = { version = "0.7.8", default-features = false, features = ["sha2", "hmac", "hkdf", "pbkdf2", "aes-gcm", "chacha20poly1305", "ecdsa", "ed25519", "rsa"] }
+rscrypto = { version = "0.8.1", default-features = false, features = ["sha2", "hmac", "hkdf", "pbkdf2", "aes-gcm", "chacha20poly1305", "ecdsa", "ed25519", "rsa"] }
```
## Map
diff --git a/docs/migration/sha3-kmac.md b/docs/migration/sha3-kmac.md
index 47ddf764..97a13aa2 100644
--- a/docs/migration/sha3-kmac.md
+++ b/docs/migration/sha3-kmac.md
@@ -13,9 +13,9 @@ Evidence: `tests/kmac128_nist_vectors.rs`, `tests/kmac128_differential.rs`,
## TL;DR
-| | Before (`sha3-kmac` 0.3.x) | After (`rscrypto` 0.7.8) |
+| | Before (`sha3-kmac` 0.3.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `sha3-kmac = "0.3"` | `rscrypto = { version = "0.7.8", features = ["kmac"] }` |
+| Cargo dep | `sha3-kmac = "0.3"` | `rscrypto = { version = "0.8.1", features = ["kmac"] }` |
| Import | `use sha3_kmac::Kmac256;` | `use rscrypto::Kmac256;` |
| Call | `let mut k = Kmac256::new(key, custom)?; k.update(data); k.finalize_into(&mut tag);` | `Kmac256::mac_into(key, custom, data, &mut tag);` |
@@ -30,7 +30,7 @@ sha3-kmac = "0.3"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["kmac"] }
+rscrypto = { version = "0.8.1", features = ["kmac"] }
```
The `kmac` feature implies `sha3`.
diff --git a/docs/migration/tiny-keccak.md b/docs/migration/tiny-keccak.md
index e22ab527..4f96756a 100644
--- a/docs/migration/tiny-keccak.md
+++ b/docs/migration/tiny-keccak.md
@@ -5,14 +5,14 @@ Replace fixed-output `tiny_keccak::Kmac::v128` / `Kmac::v256` and
`Kmac256` and `rscrypto::Cshake128` / `Cshake256`. KMAC construction is
infallible and adds verification helpers. `KmacXof` is not mapped.
-Verified against `tiny-keccak = "2.0.2"` (with `kmac` and `cshake` features) and the `rscrypto` 0.7.8 line.
+Verified against `tiny-keccak = "2.0.2"` (with `kmac` and `cshake` features) and the `rscrypto` 0.8.1 line.
Evidence: `tests/kmac128_differential.rs`, `tests/kmac256_differential.rs`, `tests/cshake256_differential.rs`, `tests/cshake256_nist_vectors.rs`, and `tests/kmac_wycheproof.rs`.
## TL;DR
-| | Before (`tiny-keccak` 2.x) | After (`rscrypto` 0.7.8) |
+| | Before (`tiny-keccak` 2.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `tiny-keccak = { version = "2.0", features = ["kmac", "cshake"] }` | `rscrypto = { version = "0.7.8", features = ["kmac"] }` |
+| Cargo dep | `tiny-keccak = { version = "2.0", features = ["kmac", "cshake"] }` | `rscrypto = { version = "0.8.1", features = ["kmac"] }` |
| KMAC import | `use tiny_keccak::{Hasher, Kmac};` | `use rscrypto::{Kmac128, Kmac256};` |
| KMAC call | `let mut k = Kmac::v256(key, custom); k.update(data); k.finalize(&mut tag);` | `Kmac256::mac_into(key, custom, data, &mut tag);` |
| cSHAKE import | `use tiny_keccak::{Hasher, CShake};` | `use rscrypto::{Cshake128, Cshake256, Xof};` |
@@ -29,7 +29,7 @@ tiny-keccak = { version = "2.0", features = ["kmac", "cshake"] }
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["kmac"] }
+rscrypto = { version = "0.8.1", features = ["kmac"] }
```
The `kmac` feature implies `sha3` (which provides the underlying `Cshake128` / `Cshake256` sponges for both KMAC variants and the standalone cSHAKE primitives).
diff --git a/docs/migration/twox-hash.md b/docs/migration/twox-hash.md
index 686a0d55..32fb95f4 100644
--- a/docs/migration/twox-hash.md
+++ b/docs/migration/twox-hash.md
@@ -7,9 +7,9 @@ The `twox-hash` mapping is API migration guidance for its 2.x XXH3 surface.
## TL;DR
-| | Before (`twox-hash` 2.x) | After (`rscrypto` 0.7.8) |
+| | Before (`twox-hash` 2.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `twox-hash = "2.1"` | `rscrypto = { version = "0.7.8", features = ["xxh3"] }` |
+| Cargo dep | `twox-hash = "2.1"` | `rscrypto = { version = "0.8.1", features = ["xxh3"] }` |
| Import | `use twox_hash::XxHash3_64;` | `use rscrypto::{FastHash, Xxh3};` |
| Call | `XxHash3_64::oneshot(data)` | `Xxh3::hash(data)` |
@@ -24,7 +24,7 @@ twox-hash = "2.1"
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["xxh3"] }
+rscrypto = { version = "0.8.1", features = ["xxh3"] }
```
## Algorithm map
diff --git a/docs/migration/xxhash-rust.md b/docs/migration/xxhash-rust.md
index 90097fe4..20e056ac 100644
--- a/docs/migration/xxhash-rust.md
+++ b/docs/migration/xxhash-rust.md
@@ -2,14 +2,14 @@
> Replace `xxhash_rust::xxh3::xxh3_64` with `rscrypto::Xxh3::hash` (and `xxh3_128` with `Xxh3_128::hash`). One-shot bit-equivalent. Streaming routes through `Xxh3Hasher` (a `core::hash::Hasher`).
-Verified against `xxhash-rust = "0.8.16"` (with `xxh3` feature) and the `rscrypto` 0.7.8 line.
+Verified against `xxhash-rust = "0.8.18"` (with `xxh3` feature) and the `rscrypto` 0.8.1 line.
Evidence: `tests/xxh3_differential.rs`.
## TL;DR
-| | Before (`xxhash-rust` 0.8.x) | After (`rscrypto` 0.7.8) |
+| | Before (`xxhash-rust` 0.8.x) | After (`rscrypto` 0.8.1) |
|---|---|---|
-| Cargo dep | `xxhash-rust = { version = "0.8", features = ["xxh3"] }` | `rscrypto = { version = "0.7.8", features = ["xxh3"] }` |
+| Cargo dep | `xxhash-rust = { version = "0.8", features = ["xxh3"] }` | `rscrypto = { version = "0.8.1", features = ["xxh3"] }` |
| Import | `use xxhash_rust::xxh3::xxh3_64;` | `use rscrypto::{FastHash, Xxh3};` |
| Call | `xxh3_64(data)` | `Xxh3::hash(data)` |
@@ -24,7 +24,7 @@ xxhash-rust = { version = "0.8", features = ["xxh3"] }
```toml
# After
[dependencies]
-rscrypto = { version = "0.7.8", features = ["xxh3"] }
+rscrypto = { version = "0.8.1", features = ["xxh3"] }
```
## Algorithm map
diff --git a/docs/platforms.md b/docs/platforms.md
index d672ad9a..fa140ec0 100644
--- a/docs/platforms.md
+++ b/docs/platforms.md
@@ -66,8 +66,10 @@ Build coverage does not establish a constant-time claim; use
## Per-platform benchmark evidence
-The historical 2026-07-04 per-platform results live in
-[`benchmark_results/OVERVIEW.md`](../benchmark_results/OVERVIEW.md#coverage-matrix).
-Its aggregate includes the equivalent-work limitation documented in
-[`benchmarking.md`](benchmarking.md). Benchmark the deployment workload on its
-target CPU before choosing a performance-sensitive backend or feature set.
+The 2026-08-18 per-platform results live in
+[`benchmark_results/OVERVIEW.md`](../benchmark_results/OVERVIEW.md#coverage-matrix),
+covering eight Linux CI runners; the RISE RISC-V runner did not execute in that
+run. Per-platform results diverge: the same commit measures `2.77x` on IBM
+z16/s390x and `1.36x` on AWS Graviton3, and s390x carries a large ECDSA
+regression in this snapshot. Benchmark the deployment workload on its target CPU
+before choosing a performance-sensitive backend or feature set.
diff --git a/docs/secret-ownership.md b/docs/secret-ownership.md
index 54eaf624..a9b69c34 100644
--- a/docs/secret-ownership.md
+++ b/docs/secret-ownership.md
@@ -43,7 +43,8 @@ capability for permanent retention.
| ML-KEM prepared decapsulation keys | Explicit duplicate; no `Clone` or `Copy` | Masked | `SecretBytes` export; no Serde | Inline | Reuse of validated private arithmetic without making implicit copies |
| `RsaPrivateKey` | Neither | Public key plus a redacted private-components field | PKCS#1/PKCS#8 DER into `SecretVec`; no Serde | Heap-backed big integers and Montgomery state | Standards-compatible private-key storage/export while keeping the returned allocation typed as secret |
| `RsaPrivateScratch` | Neither | Public sizing metadata only | None | Reusable heap buffers and limb vectors | Amortizes private-operation allocation while keeping intermediate ownership with the caller |
-| `RsaPrivateKeyParts<'a>` | `Clone + Copy` | No `Debug` | Borrowed import fields; no Serde | Borrowed | Pass-by-value import description; copying duplicates references, not private bytes |
+| `RsaPrivateKeyParts<'a>` | `Clone + Copy` | Modulus size and public exponent only; private fields masked | Borrowed import fields; no Serde | Borrowed | Pass-by-value import description; copying duplicates references, not private bytes |
+| `RsaBlindingPair<'a>` | `Clone + Copy` | Factor and inverse masked | Borrowed factor and inverse; no Serde | Borrowed | Couples the two caller-blinding inputs; private operations validate them against the selected key |
| `RsaSignatureSigner<'a>` | `Clone + Copy` | Signature profile only | None | Borrowed | Reusable profile-bound signing handle; copying duplicates a private-key reference, not the key |
| HMAC-SHA-2 and HMAC-SHA-3 states | Neither | Masked | None | Inline | Reuse through `reset`; independent streamed owners require explicit keyed construction rather than an implicit state copy |
| HKDF-SHA-2 states | Neither | Masked | None | Inline | Repeated expansion borrows one extracted PRK owner, which can also be shared by reference |
diff --git a/docs/test-vector-coverage.md b/docs/test-vector-coverage.md
index f905818a..5882e3cf 100644
--- a/docs/test-vector-coverage.md
+++ b/docs/test-vector-coverage.md
@@ -65,7 +65,7 @@ the concrete inputs and outputs of each cryptography API.
| XChaCha20-Poly1305 | `tests/xchacha20poly1305.rs` | `tests/aead_wycheproof.rs` covers Wycheproof 192-bit nonce open failure; unit/integration tests cover wrong nonce/tag/AAD | Current suite maps directly after nonce-size filtering |
| AEGIS-256 | `tests/aegis256_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AEGIS-256 open failure; unit/integration tests cover wrong nonce/tag/AAD | Current suite maps directly |
| Ascon-AEAD128 | Final SP 800-232 reference corpus in `tests/ascon_final_kats.rs` and `testdata/ascon/asconaead128.txt`; independent coverage in `tests/ascon_aead_oracle.rs` | Unit/integration tests cover wrong nonce/tag/AAD and oracle decrypt failure | Current Wycheproof `ASCON128` vectors do not match this crate's NIST Ascon-AEAD128 variant, so they are not vendored |
-| ECDSA P-256/P-384 signing and verification | `tests/ecdsa_oracle.rs`; `tests/ecdsa_wycheproof.rs`; `src/auth/ecdsa.rs` unit tests; RustCrypto `p256 0.14.0` / `p384 0.13.1` oracles; `fuzz/target_impls/auth_ecdsa_verify.rs`; `fuzz/target_impls/auth_ecdsa_sign.rs` | Current Wycheproof P-256/SHA-256 and P-384/SHA-384 DER suites cover valid and invalid verification cases; unit, oracle, and fuzz tests cover SEC1/SPKI/DER/raw parser boundaries, zero/out-of-range scalars and coordinates, tampered signatures, wrong messages, deterministic and blinded signing, low-S normalization, and public-key derivation | CT evidence covers blinded signing. Public verification remains public-input work unless promoted by the CT manifest. Variable-length P1363 parser cases do not map to the fixed-size raw signature APIs. |
+| ECDSA P-256/P-384 signing and verification | `tests/ecdsa_oracle.rs`; `tests/ecdsa_wycheproof.rs`; `src/auth/ecdsa.rs` unit tests; RustCrypto `p256 0.14.0` / `p384 0.14.0` oracles; `fuzz/target_impls/auth_ecdsa_verify.rs`; `fuzz/target_impls/auth_ecdsa_sign.rs` | Current Wycheproof P-256/SHA-256 and P-384/SHA-384 DER suites cover valid and invalid verification cases; unit, oracle, and fuzz tests cover SEC1/SPKI/DER/raw parser boundaries, zero/out-of-range scalars and coordinates, tampered signatures, wrong messages, deterministic and blinded signing, low-S normalization, and public-key derivation | CT evidence covers blinded signing. Public verification remains public-input work unless promoted by the CT manifest. Variable-length P1363 parser cases do not map to the fixed-size raw signature APIs. |
| Ed25519 | `tests/ed25519_rfc8032_vectors.rs`, `tests/ed25519_oracle.rs` | `tests/ed25519_wycheproof.rs` covers Wycheproof valid/invalid signatures and invalid public/signature encodings; unit tests cover small-order and non-canonical signatures | Current suite maps directly |
| X25519 | `tests/x25519_vectors.rs`, `tests/x25519_oracle.rs` | `tests/x25519_wycheproof.rs` covers Wycheproof valid/acceptable XDH vectors and rejects all-zero shared secrets; RFC low-order and non-canonical public cases remain in `tests/x25519_vectors.rs` | ASN/JWK/PEM suites do not apply to byte-array API |
| ML-KEM-512/768/1024 | `tests/mlkem_acvp.rs` covers NIST ACVP FIPS 203 keyGen, encapsulation, decapsulation, decapsulationKeyCheck, and encapsulationKeyCheck vectors for all parameter sets; `tests/mlkem_properties.rs` differentials arbitrary seeds against the `fips203` crate; `tests/mlkem_types.rs` checks FIPS 203 sizes, randomness, security categories, byte wrappers, secret redaction, public-value equality, and sealed secret-owner comparison | `tests/mlkem_ops.rs` covers non-canonical public-key rejection before randomness, prepared-key parity, prepared-key invalid material, wrong-length parsers, decapsulation-key hash mismatch, and modified-ciphertext implicit rejection; `fuzz/target_impls/auth_mlkem512.rs`, `auth_mlkem768.rs`, and `auth_mlkem1024.rs` cover round trips, parser inputs, and modified ciphertexts | No vendored Wycheproof ML-KEM suite is currently mapped; official ACVP vectors plus all-profile FIPS 203 differential/property coverage are the primary oracle set |
diff --git a/docs/types.md b/docs/types.md
index c5d913fa..5480ba5e 100644
--- a/docs/types.md
+++ b/docs/types.md
@@ -144,7 +144,7 @@ Features: `signatures` / `key-exchange` or `ecdsa` / `ed25519` / `rsa` / `x25519
| `EcdsaP256Keypair` / `EcdsaP384Keypair` | secret + public | FIPS 186-5 / SEC 1 |
| `Ed25519SecretKey` / `Ed25519PublicKey` / `Ed25519Signature` | 32/32/64B | RFC 8032 |
| `Ed25519Keypair` | -- | RFC 8032 |
-| `RsaPublicKey`, `RsaPrivateKey`, `RsaPrivateKeyParts`, `RsaX509PublicKey`, `RsaPublicScratch`, `RsaPrivateScratch` | variable | RFC 8017 / RFC 4055 |
+| `RsaPublicKey`, `RsaPrivateKey`, `RsaPrivateKeyParts`, `RsaBlindingPair`, `RsaX509PublicKey`, `RsaPublicScratch`, `RsaPrivateScratch` | variable | RFC 8017 / RFC 4055 |
| `RsaSignatureSigner`, `RsaSignatureVerifier` | profile-bound wrappers | RFC 8017 / RFC 4055 |
| `RsaJwtAlgorithm`, `RsaJwtVerifier` | verifier-owned JWT/JWS policy | RFC 7515 / RFC 8725 |
| `RsaSignatureProfile`, `RsaPssProfile`, `RsaPkcs1v15Profile`, `RsaOaepProfile`, `RsaPublicKeyPolicy`, `RsaKeyGenerationContract` | -- | RFC 8017 / RFC 4055 / FIPS 186-5 / protocol-specific profiles |
diff --git a/examples/aead_seal_open.rs b/examples/aead_seal_open.rs
index 4484efee..249756d0 100644
--- a/examples/aead_seal_open.rs
+++ b/examples/aead_seal_open.rs
@@ -11,7 +11,9 @@ fn main() -> Result<(), Box> {
let (nonce, sealed) = cipher.seal_random_to_vec(aad, plaintext)?;
let opened = cipher.decrypt_to_vec(&nonce, aad, &sealed)?;
- assert_eq!(opened, plaintext);
+ if opened != plaintext {
+ return Err(std::io::Error::other("AEAD round-trip changed the plaintext").into());
+ }
println!("ChaCha20-Poly1305 sealed {} bytes", plaintext.len());
Ok(())
}
diff --git a/examples/basic.rs b/examples/basic.rs
index b2ac356d..03c632cf 100644
--- a/examples/basic.rs
+++ b/examples/basic.rs
@@ -3,7 +3,7 @@
//!
//! Run with: `cargo run --example basic --features full,getrandom`
-use std::io::{Cursor, Read, Write};
+use std::io::{Read, Write};
use rscrypto::{
Aead, Blake3, ChaCha20Poly1305, ChaCha20Poly1305Key, Checksum, Crc32C, Digest, Ed25519Keypair, Ed25519SecretKey,
@@ -86,17 +86,19 @@ fn auth_api() -> Result<(), Box> {
let mut mac = HmacSha256::new(key);
mac.update(b"hello ");
mac.update(b"world");
- assert!(mac.verify(&tag).is_ok());
+ mac.verify(&tag)?;
mac.reset();
mac.update(data);
- assert!(mac.verify(&tag).is_ok());
+ mac.verify(&tag)?;
let hkdf = HkdfSha256::new(b"salt", b"input key material");
let mut okm = [0u8; 42];
hkdf.expand(b"context", &mut okm)?;
let oneshot = HkdfSha256::derive_array::<42>(b"salt", b"input key material", b"context")?;
- assert_eq!(okm, oneshot);
+ if okm != oneshot {
+ return Err(std::io::Error::other("HKDF streaming and one-shot outputs differ").into());
+ }
println!("HMAC-SHA256 tag size = {} bytes", tag.as_slice().len());
println!("HKDF-SHA256 output = {} bytes\n", okm.len());
@@ -116,7 +118,9 @@ fn aead_api() -> Result<(), Box> {
let mut opened = [0u8; 5];
aead.decrypt(&nonce, b"", &sealed, &mut opened)?;
- assert_eq!(&opened, b"hello");
+ if &opened != b"hello" {
+ return Err(std::io::Error::other("AEAD round-trip changed the plaintext").into());
+ }
println!("ChaCha20-Poly1305 round-trip succeeded");
println!(" nonce = {nonce}\n");
@@ -135,12 +139,16 @@ fn hex_api() -> Result<(), Box> {
println!("Debug: {nonce:?}");
let parsed: Nonce96 = "abababababababababababab".parse()?;
- assert_eq!(parsed, nonce);
+ if parsed != nonce {
+ return Err(std::io::Error::other("nonce hex round-trip changed the value").into());
+ }
println!("FromStr: round-trip succeeded");
let key = ChaCha20Poly1305Key::from_bytes([0x42; 32]);
let key_debug = format!("{key:?}");
- assert_eq!(key_debug, "ChaCha20Poly1305Key(****)");
+ if key_debug != "ChaCha20Poly1305Key(****)" {
+ return Err(std::io::Error::other("secret-key Debug output exposed an unexpected representation").into());
+ }
println!("\nSecret Debug: {key_debug}");
let ed_sk = Ed25519SecretKey::from_bytes([7u8; 32]);
@@ -215,23 +223,35 @@ fn io_api() -> std::io::Result<()> {
let data = b"stream me through adapters";
- let mut reader = Sha256::reader(Cursor::new(data.to_vec()));
+ let mut reader = Sha256::reader(data.as_slice());
let mut copied = Vec::new();
reader.read_to_end(&mut copied)?;
- assert_eq!(copied, data);
- assert_eq!(reader.digest(), Sha256::digest(data));
+ if copied != data {
+ return Err(std::io::Error::other("digest reader changed the copied data"));
+ }
+ if reader.digest() != Sha256::digest(data) {
+ return Err(std::io::Error::other("digest reader produced the wrong digest"));
+ }
let mut checksum_writer = Crc32C::writer(Vec::new());
checksum_writer.write_all(data)?;
let (written, crc) = checksum_writer.into_parts();
- assert_eq!(written, data);
- assert_eq!(crc, Crc32C::checksum(data));
+ if written != data {
+ return Err(std::io::Error::other("checksum writer changed the written data"));
+ }
+ if crc != Crc32C::checksum(data) {
+ return Err(std::io::Error::other("checksum writer produced the wrong checksum"));
+ }
let mut digest_writer = Blake3::writer(Vec::new());
digest_writer.write_all(data)?;
let (written, digest) = digest_writer.into_parts();
- assert_eq!(written, data);
- assert_eq!(digest, Blake3::digest(data));
+ if written != data {
+ return Err(std::io::Error::other("digest writer changed the written data"));
+ }
+ if digest != Blake3::digest(data) {
+ return Err(std::io::Error::other("digest writer produced the wrong digest"));
+ }
println!("reader digest matches Sha256::digest()");
println!("writer checksum matches Crc32C::checksum()");
diff --git a/examples/mlkem_encapsulation.rs b/examples/mlkem_encapsulation.rs
index 83721d4d..577a650c 100644
--- a/examples/mlkem_encapsulation.rs
+++ b/examples/mlkem_encapsulation.rs
@@ -7,7 +7,9 @@ fn main() -> Result<(), Box> {
let (ciphertext, shared_secret) = MlKem768::try_encapsulate(&encapsulation_key)?;
let decapsulated = MlKem768::decapsulate(&decapsulation_key, &ciphertext)?;
- assert!(shared_secret.ct_eq(&decapsulated).declassify());
+ if !shared_secret.ct_eq(&decapsulated).declassify() {
+ return Err(std::io::Error::other("ML-KEM encapsulation and decapsulation secrets differ").into());
+ }
println!(
"ML-KEM-768 encapsulated {} shared-secret bytes",
shared_secret.as_bytes().len()
diff --git a/examples/parallel.rs b/examples/parallel.rs
index 8f924712..f66d8aaa 100644
--- a/examples/parallel.rs
+++ b/examples/parallel.rs
@@ -63,7 +63,7 @@ fn parallel_chunks() {
println!("Parallel chunk processing\n");
// Simulate large data (in practice, this could be a memory-mapped file)
- let data: Vec = (0..1_000_000).map(|i| (i % 256) as u8).collect();
+ let data: Vec = (0usize..1_000_000).map(|i| i.to_le_bytes()[0]).collect();
let chunk_size = 250_000; // 4 chunks of 250KB each
@@ -74,12 +74,9 @@ fn parallel_chunks() {
// Parallel: compute each chunk's CRC, then combine
let chunks: Vec<_> = data.chunks(chunk_size).collect();
let chunk_crcs: Vec<_> = thread::scope(|scope| {
- let handles: Vec<_> = chunks
+ chunks
.iter()
.map(|&chunk| scope.spawn(move || Crc64::checksum(chunk)))
- .collect();
- handles
- .into_iter()
.map(|handle| handle.join().expect("thread panicked"))
.collect()
});
@@ -100,7 +97,12 @@ fn threaded_example() {
println!("Threaded processing\n");
// Generate test data
- let data: Vec = (0..4_000_000).map(|i| ((i * 17) % 256) as u8).collect();
+ let data: Vec = (0usize..4_000_000)
+ .map(|i| {
+ let value = i.strict_mul(17).strict_rem(256);
+ u8::try_from(value).expect("threaded fixture byte must fit in u8")
+ })
+ .collect();
let num_threads = 4;
let chunk_size = data.len() / num_threads;
@@ -114,12 +116,9 @@ fn threaded_example() {
// Spawn scoped threads to compute each chunk's CRC without copying chunk data.
let mut results: Vec<(usize, u64, usize)> = thread::scope(|scope| {
- let handles: Vec<_> = chunks
+ chunks
.iter()
.map(|&(idx, chunk)| scope.spawn(move || (idx, Crc64::checksum(chunk), chunk.len())))
- .collect();
- handles
- .into_iter()
.map(|handle| handle.join().expect("thread panicked"))
.collect()
});
diff --git a/examples/password_hashing.rs b/examples/password_hashing.rs
index 071cb91d..a93ad8cd 100644
--- a/examples/password_hashing.rs
+++ b/examples/password_hashing.rs
@@ -13,13 +13,17 @@ fn main() -> Result<(), Box> {
let argon2 = Argon2idPassword::default();
let argon2_phc = argon2.hash_password(password)?;
- assert!(argon2.verify_password(password, &argon2_phc).is_ok());
- assert!(argon2.verify_password(b"wrong password", &argon2_phc).is_err());
+ argon2.verify_password(password, &argon2_phc)?;
+ if argon2.verify_password(b"wrong password", &argon2_phc).is_ok() {
+ return Err(std::io::Error::other("Argon2id accepted the wrong password").into());
+ }
let scrypt = ScryptPassword::default();
let scrypt_phc = scrypt.hash_password(password)?;
- assert!(scrypt.verify_password(password, &scrypt_phc).is_ok());
- assert!(scrypt.verify_password(b"wrong password", &scrypt_phc).is_err());
+ scrypt.verify_password(password, &scrypt_phc)?;
+ if scrypt.verify_password(b"wrong password", &scrypt_phc).is_ok() {
+ return Err(std::io::Error::other("scrypt accepted the wrong password").into());
+ }
println!("{argon2_phc}");
println!("{scrypt_phc}");
diff --git a/fuzz-packages/aead-aegis256/Cargo.lock b/fuzz-packages/aead-aegis256/Cargo.lock
index 3a480232..309d2ad4 100644
--- a/fuzz-packages/aead-aegis256/Cargo.lock
+++ b/fuzz-packages/aead-aegis256/Cargo.lock
@@ -20,9 +20,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -38,9 +38,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -87,7 +87,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-aegis256"
diff --git a/fuzz-packages/aead-aes128gcm/Cargo.lock b/fuzz-packages/aead-aes128gcm/Cargo.lock
index 520c1bcb..eb550e78 100644
--- a/fuzz-packages/aead-aes128gcm/Cargo.lock
+++ b/fuzz-packages/aead-aes128gcm/Cargo.lock
@@ -54,9 +54,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -131,9 +131,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -218,7 +218,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-aes128gcm"
diff --git a/fuzz-packages/aead-aes128gcmsiv/Cargo.lock b/fuzz-packages/aead-aes128gcmsiv/Cargo.lock
index 7e8bc8ba..d374eba4 100644
--- a/fuzz-packages/aead-aes128gcmsiv/Cargo.lock
+++ b/fuzz-packages/aead-aes128gcmsiv/Cargo.lock
@@ -4,30 +4,30 @@ version = 4
[[package]]
name = "aead"
-version = "0.5.2"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99"
dependencies = [
"crypto-common",
- "generic-array",
+ "inout",
]
[[package]]
name = "aes"
-version = "0.8.4"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58"
dependencies = [
- "cfg-if",
"cipher",
+ "cpubits",
"cpufeatures",
]
[[package]]
name = "aes-gcm-siv"
-version = "0.11.1"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
+checksum = "1f437e5b075722bda3f54039e95d60d1c142f140e6c45b718e4f9fca3e5a1514"
dependencies = [
"aead",
"aes",
@@ -35,7 +35,6 @@ dependencies = [
"ctr",
"polyval",
"subtle",
- "zeroize",
]
[[package]]
@@ -44,11 +43,20 @@ version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+[[package]]
+name = "block-buffer"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
+dependencies = [
+ "hybrid-array",
+]
+
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -64,57 +72,68 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cipher"
-version = "0.4.4"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [
+ "block-buffer",
"crypto-common",
"inout",
]
+[[package]]
+name = "cmov"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
+
+[[package]]
+name = "cpubits"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
+
[[package]]
name = "cpufeatures"
-version = "0.2.17"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
-version = "0.1.7"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
- "generic-array",
- "typenum",
+ "hybrid-array",
]
[[package]]
name = "ctr"
-version = "0.9.2"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21"
dependencies = [
"cipher",
]
[[package]]
-name = "find-msvc-tools"
-version = "0.1.9"
+name = "ctutils"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
+dependencies = [
+ "cmov",
+]
[[package]]
-name = "generic-array"
-version = "0.14.7"
+name = "find-msvc-tools"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -127,13 +146,22 @@ dependencies = [
"r-efi",
]
+[[package]]
+name = "hybrid-array"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
+dependencies = [
+ "typenum",
+]
+
[[package]]
name = "inout"
-version = "0.1.4"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
+checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
- "generic-array",
+ "hybrid-array",
]
[[package]]
@@ -162,21 +190,14 @@ dependencies = [
"cc",
]
-[[package]]
-name = "opaque-debug"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
-
[[package]]
name = "polyval"
-version = "0.6.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd"
dependencies = [
- "cfg-if",
+ "cpubits",
"cpufeatures",
- "opaque-debug",
"universal-hash",
]
@@ -188,7 +209,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-aes128gcmsiv"
@@ -227,22 +248,10 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "universal-hash"
-version = "0.5.1"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96"
dependencies = [
"crypto-common",
- "subtle",
+ "ctutils",
]
-
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
-[[package]]
-name = "zeroize"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
diff --git a/fuzz-packages/aead-aes128gcmsiv/Cargo.toml b/fuzz-packages/aead-aes128gcmsiv/Cargo.toml
index 2f86b491..e9b6ce9d 100644
--- a/fuzz-packages/aead-aes128gcmsiv/Cargo.toml
+++ b/fuzz-packages/aead-aes128gcmsiv/Cargo.toml
@@ -11,7 +11,7 @@ cargo-fuzz = true
libfuzzer-sys = "0.4"
rscrypto = { path = "../..", features = ["std", "aes-gcm-siv"] }
rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] }
-aes-gcm-siv = { version = "0.11", default-features = false, features = ["alloc", "aes"] }
+aes-gcm-siv = { version = "0.12", default-features = false, features = ["alloc", "aes"] }
[workspace]
members = ["."]
diff --git a/fuzz-packages/aead-aes256gcm/Cargo.lock b/fuzz-packages/aead-aes256gcm/Cargo.lock
index 5ef7f9e0..a94e9b6d 100644
--- a/fuzz-packages/aead-aes256gcm/Cargo.lock
+++ b/fuzz-packages/aead-aes256gcm/Cargo.lock
@@ -54,9 +54,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -131,9 +131,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -218,7 +218,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-aes256gcm"
diff --git a/fuzz-packages/aead-aes256gcmsiv/Cargo.lock b/fuzz-packages/aead-aes256gcmsiv/Cargo.lock
index dfa5d6a3..0a87b8b3 100644
--- a/fuzz-packages/aead-aes256gcmsiv/Cargo.lock
+++ b/fuzz-packages/aead-aes256gcmsiv/Cargo.lock
@@ -4,30 +4,30 @@ version = 4
[[package]]
name = "aead"
-version = "0.5.2"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
+checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99"
dependencies = [
"crypto-common",
- "generic-array",
+ "inout",
]
[[package]]
name = "aes"
-version = "0.8.4"
+version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
+checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58"
dependencies = [
- "cfg-if",
"cipher",
+ "cpubits",
"cpufeatures",
]
[[package]]
name = "aes-gcm-siv"
-version = "0.11.1"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
+checksum = "1f437e5b075722bda3f54039e95d60d1c142f140e6c45b718e4f9fca3e5a1514"
dependencies = [
"aead",
"aes",
@@ -35,7 +35,6 @@ dependencies = [
"ctr",
"polyval",
"subtle",
- "zeroize",
]
[[package]]
@@ -44,11 +43,20 @@ version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
+[[package]]
+name = "block-buffer"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
+dependencies = [
+ "hybrid-array",
+]
+
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -64,57 +72,68 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cipher"
-version = "0.4.4"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
+checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [
+ "block-buffer",
"crypto-common",
"inout",
]
+[[package]]
+name = "cmov"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
+
+[[package]]
+name = "cpubits"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
+
[[package]]
name = "cpufeatures"
-version = "0.2.17"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
-version = "0.1.7"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
- "generic-array",
- "typenum",
+ "hybrid-array",
]
[[package]]
name = "ctr"
-version = "0.9.2"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
+checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21"
dependencies = [
"cipher",
]
[[package]]
-name = "find-msvc-tools"
-version = "0.1.9"
+name = "ctutils"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
+dependencies = [
+ "cmov",
+]
[[package]]
-name = "generic-array"
-version = "0.14.7"
+name = "find-msvc-tools"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -127,13 +146,22 @@ dependencies = [
"r-efi",
]
+[[package]]
+name = "hybrid-array"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
+dependencies = [
+ "typenum",
+]
+
[[package]]
name = "inout"
-version = "0.1.4"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
+checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
dependencies = [
- "generic-array",
+ "hybrid-array",
]
[[package]]
@@ -162,21 +190,14 @@ dependencies = [
"cc",
]
-[[package]]
-name = "opaque-debug"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
-
[[package]]
name = "polyval"
-version = "0.6.2"
+version = "0.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
+checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd"
dependencies = [
- "cfg-if",
+ "cpubits",
"cpufeatures",
- "opaque-debug",
"universal-hash",
]
@@ -188,7 +209,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-aes256gcmsiv"
@@ -227,22 +248,10 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
[[package]]
name = "universal-hash"
-version = "0.5.1"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
+checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96"
dependencies = [
"crypto-common",
- "subtle",
+ "ctutils",
]
-
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
-[[package]]
-name = "zeroize"
-version = "1.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
diff --git a/fuzz-packages/aead-aes256gcmsiv/Cargo.toml b/fuzz-packages/aead-aes256gcmsiv/Cargo.toml
index 7c2a09e6..aa480bc9 100644
--- a/fuzz-packages/aead-aes256gcmsiv/Cargo.toml
+++ b/fuzz-packages/aead-aes256gcmsiv/Cargo.toml
@@ -11,7 +11,7 @@ cargo-fuzz = true
libfuzzer-sys = "0.4"
rscrypto = { path = "../..", features = ["std", "aes-gcm-siv"] }
rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] }
-aes-gcm-siv = { version = "0.11", default-features = false, features = ["alloc", "aes"] }
+aes-gcm-siv = { version = "0.12", default-features = false, features = ["alloc", "aes"] }
[workspace]
members = ["."]
diff --git a/fuzz-packages/aead-ascon128/Cargo.lock b/fuzz-packages/aead-ascon128/Cargo.lock
index 15bc01d0..575ddea4 100644
--- a/fuzz-packages/aead-ascon128/Cargo.lock
+++ b/fuzz-packages/aead-ascon128/Cargo.lock
@@ -38,9 +38,9 @@ checksum = "c90c8493a0b3bf8678a82f096821a2c527dd6344bbcef6265bfcc09745f44e24"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -65,9 +65,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -132,7 +132,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-ascon128"
diff --git a/fuzz-packages/aead-chacha20poly1305/Cargo.lock b/fuzz-packages/aead-chacha20poly1305/Cargo.lock
index cd2ff2a1..fbfc0c17 100644
--- a/fuzz-packages/aead-chacha20poly1305/Cargo.lock
+++ b/fuzz-packages/aead-chacha20poly1305/Cargo.lock
@@ -29,9 +29,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -114,9 +114,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -191,7 +191,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-chacha20poly1305"
diff --git a/fuzz-packages/aead-nonce-counter/Cargo.lock b/fuzz-packages/aead-nonce-counter/Cargo.lock
index 5b5f25ce..e05e16db 100644
--- a/fuzz-packages/aead-nonce-counter/Cargo.lock
+++ b/fuzz-packages/aead-nonce-counter/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -28,9 +28,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -77,7 +77,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-nonce-counter"
diff --git a/fuzz-packages/aead-xchacha20poly1305/Cargo.lock b/fuzz-packages/aead-xchacha20poly1305/Cargo.lock
index 3b19f3f1..33393cc2 100644
--- a/fuzz-packages/aead-xchacha20poly1305/Cargo.lock
+++ b/fuzz-packages/aead-xchacha20poly1305/Cargo.lock
@@ -29,9 +29,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -114,9 +114,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -191,7 +191,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-aead-xchacha20poly1305"
diff --git a/fuzz-packages/auth-argon2/Cargo.lock b/fuzz-packages/auth-argon2/Cargo.lock
index 8fb4467c..778af033 100644
--- a/fuzz-packages/auth-argon2/Cargo.lock
+++ b/fuzz-packages/auth-argon2/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "argon2"
-version = "0.6.0-rc.8"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5"
+checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
@@ -28,27 +28,27 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "blake2"
-version = "0.11.0-rc.6"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
"digest",
]
[[package]]
name = "block-buffer"
-version = "0.12.1"
+version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
dependencies = [
- "hybrid-array",
+ "generic-array",
]
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -62,55 +62,51 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
-[[package]]
-name = "cmov"
-version = "0.5.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
-
[[package]]
name = "cpufeatures"
-version = "0.3.0"
+version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
+checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
-dependencies = [
- "hybrid-array",
-]
-
-[[package]]
-name = "ctutils"
-version = "0.4.2"
+version = "0.1.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
- "cmov",
+ "generic-array",
+ "typenum",
]
[[package]]
name = "digest"
-version = "0.11.3"
+version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer",
"crypto-common",
- "ctutils",
+ "subtle",
]
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
[[package]]
name = "getrandom"
@@ -123,15 +119,6 @@ dependencies = [
"r-efi",
]
-[[package]]
-name = "hybrid-array"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
-dependencies = [
- "typenum",
-]
-
[[package]]
name = "jobserver"
version = "0.1.35"
@@ -160,21 +147,13 @@ dependencies = [
[[package]]
name = "password-hash"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
-dependencies = [
- "phc",
-]
-
-[[package]]
-name = "phc"
-version = "0.6.1"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
"base64ct",
- "ctutils",
+ "rand_core",
+ "subtle",
]
[[package]]
@@ -183,9 +162,15 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
+[[package]]
+name = "rand_core"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-argon2"
@@ -210,8 +195,20 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
diff --git a/fuzz-packages/auth-argon2/Cargo.toml b/fuzz-packages/auth-argon2/Cargo.toml
index 979d119e..5c417308 100644
--- a/fuzz-packages/auth-argon2/Cargo.toml
+++ b/fuzz-packages/auth-argon2/Cargo.toml
@@ -11,7 +11,7 @@ cargo-fuzz = true
libfuzzer-sys = "0.4"
rscrypto = { path = "../..", features = ["std", "argon2"] }
rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support" }
-argon2 = { version = "0.6.0-rc.8", default-features = false, features = ["alloc"] }
+argon2 = { version = "0.5.3", default-features = false, features = ["alloc"] }
[workspace]
members = ["."]
diff --git a/fuzz-packages/auth-ed25519/Cargo.lock b/fuzz-packages/auth-ed25519/Cargo.lock
index 013bb2aa..12386b92 100644
--- a/fuzz-packages/auth-ed25519/Cargo.lock
+++ b/fuzz-packages/auth-ed25519/Cargo.lock
@@ -8,26 +8,20 @@ version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
-[[package]]
-name = "base64ct"
-version = "1.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
-
[[package]]
name = "block-buffer"
-version = "0.10.4"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa"
dependencies = [
- "generic-array",
+ "hybrid-array",
]
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -41,36 +35,29 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
-[[package]]
-name = "const-oid"
-version = "0.9.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
-
[[package]]
name = "cpufeatures"
-version = "0.2.17"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "crypto-common"
-version = "0.1.7"
+version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
- "generic-array",
- "typenum",
+ "hybrid-array",
]
[[package]]
name = "curve25519-dalek"
-version = "4.1.3"
+version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -90,24 +77,14 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.119",
-]
-
-[[package]]
-name = "der"
-version = "0.7.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
-dependencies = [
- "const-oid",
- "zeroize",
+ "syn",
]
[[package]]
name = "digest"
-version = "0.10.7"
+version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer",
"crypto-common",
@@ -115,23 +92,21 @@ dependencies = [
[[package]]
name = "ed25519"
-version = "2.2.3"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a"
dependencies = [
- "pkcs8",
"signature",
]
[[package]]
name = "ed25519-dalek"
-version = "2.2.0"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
+checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de"
dependencies = [
"curve25519-dalek",
"ed25519",
- "serde",
"sha2",
"subtle",
"zeroize",
@@ -139,46 +114,34 @@ dependencies = [
[[package]]
name = "fiat-crypto"
-version = "0.2.9"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
-
-[[package]]
-name = "generic-array"
-version = "0.14.7"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
-dependencies = [
- "typenum",
- "version_check",
-]
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
-version = "0.2.17"
+version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
+checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
"libc",
- "wasi",
+ "r-efi",
]
[[package]]
-name = "getrandom"
-version = "0.4.3"
+name = "hybrid-array"
+version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
+checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
dependencies = [
- "cfg-if",
- "libc",
- "r-efi",
+ "typenum",
]
[[package]]
@@ -187,7 +150,7 @@ version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
- "getrandom 0.4.3",
+ "getrandom",
"libc",
]
@@ -207,16 +170,6 @@ dependencies = [
"cc",
]
-[[package]]
-name = "pkcs8"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
-dependencies = [
- "der",
- "spki",
-]
-
[[package]]
name = "proc-macro2"
version = "1.0.107"
@@ -241,18 +194,9 @@ version = "6.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
-[[package]]
-name = "rand_core"
-version = "0.6.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom 0.2.17",
-]
-
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-ed25519"
@@ -286,40 +230,11 @@ version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
-[[package]]
-name = "serde"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
-dependencies = [
- "serde_core",
-]
-
-[[package]]
-name = "serde_core"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 3.0.3",
-]
-
[[package]]
name = "sha2"
-version = "0.10.9"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
+checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -334,22 +249,9 @@ checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "signature"
-version = "2.2.0"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
-dependencies = [
- "rand_core",
-]
-
-[[package]]
-name = "spki"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
-dependencies = [
- "base64ct",
- "der",
-]
+checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5"
[[package]]
name = "subtle"
@@ -368,17 +270,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "syn"
-version = "3.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
[[package]]
name = "typenum"
version = "1.20.1"
@@ -391,18 +282,6 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
-[[package]]
-name = "version_check"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-
-[[package]]
-name = "wasi"
-version = "0.11.1+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-
[[package]]
name = "zeroize"
version = "1.9.0"
diff --git a/fuzz-packages/auth-ed25519/Cargo.toml b/fuzz-packages/auth-ed25519/Cargo.toml
index a56bbf2c..3dcfbad7 100644
--- a/fuzz-packages/auth-ed25519/Cargo.toml
+++ b/fuzz-packages/auth-ed25519/Cargo.toml
@@ -11,7 +11,7 @@ cargo-fuzz = true
libfuzzer-sys = "0.4"
rscrypto = { path = "../..", features = ["std", "ed25519"] }
rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support" }
-ed25519-dalek = "2.2"
+ed25519-dalek = "3.0"
[workspace]
members = ["."]
diff --git a/fuzz-packages/auth-hkdf/Cargo.lock b/fuzz-packages/auth-hkdf/Cargo.lock
index c4719c32..88526fb3 100644
--- a/fuzz-packages/auth-hkdf/Cargo.lock
+++ b/fuzz-packages/auth-hkdf/Cargo.lock
@@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -88,9 +88,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -164,7 +164,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-hkdf"
diff --git a/fuzz-packages/auth-hmac/Cargo.lock b/fuzz-packages/auth-hmac/Cargo.lock
index e14dfe58..f9453d1f 100644
--- a/fuzz-packages/auth-hmac/Cargo.lock
+++ b/fuzz-packages/auth-hmac/Cargo.lock
@@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -88,9 +88,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -155,7 +155,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-hmac"
diff --git a/fuzz-packages/auth-kmac256/Cargo.lock b/fuzz-packages/auth-kmac256/Cargo.lock
index ed5bce65..3ecd0c71 100644
--- a/fuzz-packages/auth-kmac256/Cargo.lock
+++ b/fuzz-packages/auth-kmac256/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -34,9 +34,9 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -83,7 +83,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-kmac256"
diff --git a/fuzz-packages/auth-pbkdf2/Cargo.lock b/fuzz-packages/auth-pbkdf2/Cargo.lock
index a661e37c..ea2872ba 100644
--- a/fuzz-packages/auth-pbkdf2/Cargo.lock
+++ b/fuzz-packages/auth-pbkdf2/Cargo.lock
@@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -88,9 +88,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -165,7 +165,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-pbkdf2"
diff --git a/fuzz-packages/auth-phc/Cargo.lock b/fuzz-packages/auth-phc/Cargo.lock
index 0aac3f41..39a50a0e 100644
--- a/fuzz-packages/auth-phc/Cargo.lock
+++ b/fuzz-packages/auth-phc/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -28,9 +28,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -77,7 +77,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-phc"
diff --git a/fuzz-packages/auth-scrypt/Cargo.lock b/fuzz-packages/auth-scrypt/Cargo.lock
index 5a7deb35..f889a2a5 100644
--- a/fuzz-packages/auth-scrypt/Cargo.lock
+++ b/fuzz-packages/auth-scrypt/Cargo.lock
@@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -92,9 +92,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -178,7 +178,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-scrypt"
diff --git a/fuzz-packages/auth-x25519/Cargo.lock b/fuzz-packages/auth-x25519/Cargo.lock
index 4267b661..c8a42896 100644
--- a/fuzz-packages/auth-x25519/Cargo.lock
+++ b/fuzz-packages/auth-x25519/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -28,18 +28,18 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "cpufeatures"
-version = "0.2.17"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280"
+checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201"
dependencies = [
"libc",
]
[[package]]
name = "curve25519-dalek"
-version = "4.1.3"
+version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -58,20 +58,20 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3"
dependencies = [
"proc-macro2",
"quote",
- "syn 2.0.119",
+ "syn",
]
[[package]]
name = "fiat-crypto"
-version = "0.2.9"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -136,13 +136,13 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rand_core"
-version = "0.6.4"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
+checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-auth-x25519"
@@ -176,36 +176,6 @@ version = "1.0.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
-[[package]]
-name = "serde"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
-dependencies = [
- "serde_core",
- "serde_derive",
-]
-
-[[package]]
-name = "serde_core"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.229"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 3.0.3",
-]
-
[[package]]
name = "shlex"
version = "2.0.1"
@@ -229,17 +199,6 @@ dependencies = [
"unicode-ident",
]
-[[package]]
-name = "syn"
-version = "3.0.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
[[package]]
name = "unicode-ident"
version = "1.0.24"
@@ -248,13 +207,12 @@ checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
[[package]]
name = "x25519-dalek"
-version = "2.0.1"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
+checksum = "e7e8131a03190127fb2263afc72b322ecadae46b6ff8c6f399ff5d02f5559af6"
dependencies = [
"curve25519-dalek",
"rand_core",
- "serde",
"zeroize",
]
@@ -263,17 +221,3 @@ name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
-dependencies = [
- "zeroize_derive",
-]
-
-[[package]]
-name = "zeroize_derive"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.119",
-]
diff --git a/fuzz-packages/auth-x25519/Cargo.toml b/fuzz-packages/auth-x25519/Cargo.toml
index eb7ec9b6..904467b1 100644
--- a/fuzz-packages/auth-x25519/Cargo.toml
+++ b/fuzz-packages/auth-x25519/Cargo.toml
@@ -11,7 +11,7 @@ cargo-fuzz = true
libfuzzer-sys = "0.4"
rscrypto = { path = "../..", features = ["std", "x25519"] }
rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support" }
-x25519-dalek = { version = "2.0.1", features = ["static_secrets"] }
+x25519-dalek = { version = "3.0.0", features = ["static_secrets"] }
[workspace]
members = ["."]
diff --git a/fuzz-packages/checksums-crc16/Cargo.lock b/fuzz-packages/checksums-crc16/Cargo.lock
index 1c1a2e88..6c59c848 100644
--- a/fuzz-packages/checksums-crc16/Cargo.lock
+++ b/fuzz-packages/checksums-crc16/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -43,9 +43,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -92,7 +92,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-checksums-crc16"
diff --git a/fuzz-packages/checksums-crc24/Cargo.lock b/fuzz-packages/checksums-crc24/Cargo.lock
index b8a0bfa6..043526cd 100644
--- a/fuzz-packages/checksums-crc24/Cargo.lock
+++ b/fuzz-packages/checksums-crc24/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -43,9 +43,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -92,7 +92,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-checksums-crc24"
diff --git a/fuzz-packages/checksums-crc32/Cargo.lock b/fuzz-packages/checksums-crc32/Cargo.lock
index 76ed2839..eb4945bb 100644
--- a/fuzz-packages/checksums-crc32/Cargo.lock
+++ b/fuzz-packages/checksums-crc32/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -43,9 +43,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -92,7 +92,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-checksums-crc32"
diff --git a/fuzz-packages/checksums-crc64/Cargo.lock b/fuzz-packages/checksums-crc64/Cargo.lock
index c910382d..d9b51307 100644
--- a/fuzz-packages/checksums-crc64/Cargo.lock
+++ b/fuzz-packages/checksums-crc64/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -43,9 +43,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -92,7 +92,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-checksums-crc64"
diff --git a/fuzz-packages/fast-rapidhash/Cargo.lock b/fuzz-packages/fast-rapidhash/Cargo.lock
index 0f99f2e3..6026a714 100644
--- a/fuzz-packages/fast-rapidhash/Cargo.lock
+++ b/fuzz-packages/fast-rapidhash/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -28,9 +28,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -86,7 +86,7 @@ dependencies = [
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-fast-rapidhash"
diff --git a/fuzz-packages/fast-xxh3/Cargo.lock b/fuzz-packages/fast-xxh3/Cargo.lock
index 207a69f6..640364b1 100644
--- a/fuzz-packages/fast-xxh3/Cargo.lock
+++ b/fuzz-packages/fast-xxh3/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -28,9 +28,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -77,7 +77,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-fast-xxh3"
diff --git a/fuzz-packages/hash-ascon/Cargo.lock b/fuzz-packages/hash-ascon/Cargo.lock
index 946b3949..c849763e 100644
--- a/fuzz-packages/hash-ascon/Cargo.lock
+++ b/fuzz-packages/hash-ascon/Cargo.lock
@@ -35,9 +35,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -72,9 +72,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -130,7 +130,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-hash-ascon"
diff --git a/fuzz-packages/hash-blake2/Cargo.lock b/fuzz-packages/hash-blake2/Cargo.lock
index d993c23c..c2567352 100644
--- a/fuzz-packages/hash-blake2/Cargo.lock
+++ b/fuzz-packages/hash-blake2/Cargo.lock
@@ -10,11 +10,20 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "blake2"
-version = "0.11.0-rc.6"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
- "digest",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "block-buffer"
+version = "0.10.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
+dependencies = [
+ "generic-array",
]
[[package]]
@@ -28,9 +37,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -50,6 +59,16 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
+[[package]]
+name = "crypto-common"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
+dependencies = [
+ "generic-array",
+ "typenum",
+]
+
[[package]]
name = "crypto-common"
version = "0.2.2"
@@ -68,22 +87,43 @@ dependencies = [
"cmov",
]
+[[package]]
+name = "digest"
+version = "0.10.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
+dependencies = [
+ "block-buffer 0.10.4",
+ "crypto-common 0.1.7",
+ "subtle",
+]
+
[[package]]
name = "digest"
version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
- "block-buffer",
- "crypto-common",
+ "block-buffer 0.12.1",
+ "crypto-common 0.2.2",
"ctutils",
]
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
+
+[[package]]
+name = "generic-array"
+version = "0.14.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
+dependencies = [
+ "typenum",
+ "version_check",
+]
[[package]]
name = "getrandom"
@@ -102,7 +142,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
dependencies = [
- "digest",
+ "digest 0.11.3",
]
[[package]]
@@ -148,14 +188,14 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-hash-blake2"
version = "0.0.0"
dependencies = [
"blake2",
- "digest",
+ "digest 0.11.3",
"hmac",
"libfuzzer-sys",
"rscrypto",
@@ -175,8 +215,20 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
+[[package]]
+name = "subtle"
+version = "2.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
+
[[package]]
name = "typenum"
version = "1.20.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20"
+
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
diff --git a/fuzz-packages/hash-blake2/Cargo.toml b/fuzz-packages/hash-blake2/Cargo.toml
index e9db192f..b9424e7b 100644
--- a/fuzz-packages/hash-blake2/Cargo.toml
+++ b/fuzz-packages/hash-blake2/Cargo.toml
@@ -11,7 +11,7 @@ cargo-fuzz = true
libfuzzer-sys = "0.4"
rscrypto = { path = "../..", features = ["std", "blake2b", "blake2s"] }
rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support" }
-blake2 = "0.11.0-rc.6"
+blake2 = "0.10.6"
digest = "0.11"
hmac = "0.13"
diff --git a/fuzz-packages/hash-blake3/Cargo.lock b/fuzz-packages/hash-blake3/Cargo.lock
index 61858c38..fd940359 100644
--- a/fuzz-packages/hash-blake3/Cargo.lock
+++ b/fuzz-packages/hash-blake3/Cargo.lock
@@ -22,9 +22,9 @@ checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56"
[[package]]
name = "blake3"
-version = "1.8.5"
+version = "1.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce"
+checksum = "76ae7bad254120e9e4c63bafc385310756f90c484eac0e36b8317cf09cb92a77"
dependencies = [
"arrayref",
"arrayvec",
@@ -36,9 +36,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -69,9 +69,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -118,7 +118,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-hash-blake3"
diff --git a/fuzz-packages/hash-sha2/Cargo.lock b/fuzz-packages/hash-sha2/Cargo.lock
index b66776e7..3a7a8f3e 100644
--- a/fuzz-packages/hash-sha2/Cargo.lock
+++ b/fuzz-packages/hash-sha2/Cargo.lock
@@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -72,9 +72,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -130,7 +130,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-hash-sha2"
diff --git a/fuzz-packages/hash-sha3/Cargo.lock b/fuzz-packages/hash-sha3/Cargo.lock
index 23941a0e..bdfb4385 100644
--- a/fuzz-packages/hash-sha3/Cargo.lock
+++ b/fuzz-packages/hash-sha3/Cargo.lock
@@ -19,9 +19,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -89,9 +89,9 @@ dependencies = [
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -125,9 +125,9 @@ dependencies = [
[[package]]
name = "keccak"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"
+checksum = "ffd9697dc4a9a62e2da93389f34400b77a28f0287711263cabb203b3ccb9c0e4"
dependencies = [
"cfg-if",
"cpufeatures",
@@ -157,7 +157,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-hash-sha3"
diff --git a/fuzz-packages/surface-hex-parse/Cargo.lock b/fuzz-packages/surface-hex-parse/Cargo.lock
index 722ecdf9..a6214cf4 100644
--- a/fuzz-packages/surface-hex-parse/Cargo.lock
+++ b/fuzz-packages/surface-hex-parse/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -28,9 +28,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -77,7 +77,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-support"
diff --git a/fuzz-packages/traits-io/Cargo.lock b/fuzz-packages/traits-io/Cargo.lock
index 0a4d28bb..27ef473c 100644
--- a/fuzz-packages/traits-io/Cargo.lock
+++ b/fuzz-packages/traits-io/Cargo.lock
@@ -10,9 +10,9 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -28,9 +28,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "getrandom"
@@ -77,7 +77,7 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz-support"
diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock
index 73ccdeee..b438ed0e 100644
--- a/fuzz/Cargo.lock
+++ b/fuzz/Cargo.lock
@@ -2,16 +2,6 @@
# It is not intended for manual editing.
version = 4
-[[package]]
-name = "aead"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
-dependencies = [
- "crypto-common 0.1.7",
- "generic-array",
-]
-
[[package]]
name = "aead"
version = "0.6.1"
@@ -19,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99"
dependencies = [
"crypto-common 0.2.2",
- "inout 0.2.2",
+ "inout",
]
[[package]]
@@ -32,24 +22,13 @@ dependencies = [
"softaes",
]
-[[package]]
-name = "aes"
-version = "0.8.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
-dependencies = [
- "cfg-if",
- "cipher 0.4.4",
- "cpufeatures 0.2.17",
-]
-
[[package]]
name = "aes"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58"
dependencies = [
- "cipher 0.5.2",
+ "cipher",
"cpubits",
"cpufeatures 0.3.0",
]
@@ -60,27 +39,26 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdf011db2e21ce0d575593d749db5554b47fed37aff429e4dc50bc91ac93a028"
dependencies = [
- "aead 0.6.1",
- "aes 0.9.2",
- "cipher 0.5.2",
- "ctr 0.10.1",
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
"ghash",
"subtle",
]
[[package]]
name = "aes-gcm-siv"
-version = "0.11.1"
+version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae0784134ba9375416d469ec31e7c5f9fa94405049cf08c5ce5b4698be673e0d"
+checksum = "1f437e5b075722bda3f54039e95d60d1c142f140e6c45b718e4f9fca3e5a1514"
dependencies = [
- "aead 0.5.2",
- "aes 0.8.4",
- "cipher 0.4.4",
- "ctr 0.9.2",
- "polyval 0.6.2",
+ "aead",
+ "aes",
+ "cipher",
+ "ctr",
+ "polyval",
"subtle",
- "zeroize",
]
[[package]]
@@ -91,13 +69,13 @@ checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
[[package]]
name = "argon2"
-version = "0.6.0-rc.8"
+version = "0.5.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7af50940b73bf4e16c15c448a2b121c63f2d68e3e54b6a8731673cb4aa0cdff5"
+checksum = "3c3610892ee6e0cbce8ae2700349fcf8f98adb0dbfbee85aec3c9179d29cc072"
dependencies = [
"base64ct",
"blake2",
- "cpufeatures 0.3.0",
+ "cpufeatures 0.2.17",
"password-hash",
]
@@ -119,9 +97,9 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6cba922132fed390c20be89a16fa713999c46b2ce84ac4b0ecee0f3586a39d88"
dependencies = [
- "aead 0.6.1",
+ "aead",
"ascon-core",
- "inout 0.2.2",
+ "inout",
"subtle",
]
@@ -147,12 +125,6 @@ version = "1.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53"
-[[package]]
-name = "base16ct"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
-
[[package]]
name = "base16ct"
version = "1.0.0"
@@ -167,18 +139,18 @@ checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06"
[[package]]
name = "blake2"
-version = "0.11.0-rc.6"
+version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "061f1a09225e328e1ffbb378d2d49923c0ca5fee19fb5ac1cc9c1e9d52b93690"
+checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
- "digest 0.11.3",
+ "digest 0.10.7",
]
[[package]]
name = "blake3"
-version = "1.8.5"
+version = "1.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce"
+checksum = "76ae7bad254120e9e4c63bafc385310756f90c484eac0e36b8317cf09cb92a77"
dependencies = [
"arrayref",
"arrayvec",
@@ -208,9 +180,9 @@ dependencies = [
[[package]]
name = "cc"
-version = "1.4.0"
+version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5add81bb678e6cb321aff7fa0dc7689ad82b112dbc032cea19f91d6b8e3582b9"
+checksum = "5d262e149917187838d5b42777c8253bcb64500067342904e7d429499a6f277e"
dependencies = [
"find-msvc-tools",
"jobserver",
@@ -231,7 +203,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81"
dependencies = [
"cfg-if",
- "cipher 0.5.2",
+ "cipher",
"cpufeatures 0.3.0",
]
@@ -241,22 +213,12 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b89e1c441e926b9c82a8d023f6e1b7ae0adcfaa7d621814e4d60789bac751cb"
dependencies = [
- "aead 0.6.1",
+ "aead",
"chacha20",
- "cipher 0.5.2",
+ "cipher",
"poly1305",
]
-[[package]]
-name = "cipher"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
-dependencies = [
- "crypto-common 0.1.7",
- "inout 0.1.4",
-]
-
[[package]]
name = "cipher"
version = "0.5.2"
@@ -265,7 +227,7 @@ checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
dependencies = [
"block-buffer 0.12.1",
"crypto-common 0.2.2",
- "inout 0.2.2",
+ "inout",
]
[[package]]
@@ -274,12 +236,6 @@ version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
-[[package]]
-name = "const-oid"
-version = "0.9.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
-
[[package]]
name = "const-oid"
version = "0.10.2"
@@ -337,18 +293,6 @@ version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
-[[package]]
-name = "crypto-bigint"
-version = "0.5.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
-dependencies = [
- "generic-array",
- "rand_core 0.6.4",
- "subtle",
- "zeroize",
-]
-
[[package]]
name = "crypto-bigint"
version = "0.7.5"
@@ -357,7 +301,7 @@ checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271"
dependencies = [
"cpubits",
"ctutils",
- "getrandom 0.4.3",
+ "getrandom",
"hybrid-array",
"num-traits",
"rand_core 0.10.1",
@@ -381,7 +325,7 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
dependencies = [
- "getrandom 0.4.3",
+ "getrandom",
"hybrid-array",
"rand_core 0.10.1",
]
@@ -397,22 +341,13 @@ dependencies = [
"sponge-cursor",
]
-[[package]]
-name = "ctr"
-version = "0.9.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
-dependencies = [
- "cipher 0.4.4",
-]
-
[[package]]
name = "ctr"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baaca1c4b237092596f64d571e9db6ce4109c4ef9742e27590f1709594461f21"
dependencies = [
- "cipher 0.5.2",
+ "cipher",
]
[[package]]
@@ -427,14 +362,14 @@ dependencies = [
[[package]]
name = "curve25519-dalek"
-version = "4.1.3"
+version = "5.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be"
+checksum = "b5eed333089e2e1c1ac8c6c0398e5e2497b4c9926ca6d0365ed1e099afa5bc23"
dependencies = [
"cfg-if",
- "cpufeatures 0.2.17",
+ "cpufeatures 0.3.0",
"curve25519-dalek-derive",
- "digest 0.10.7",
+ "digest 0.11.3",
"fiat-crypto",
"rustc_version",
"subtle",
@@ -452,23 +387,13 @@ dependencies = [
"syn 2.0.119",
]
-[[package]]
-name = "der"
-version = "0.7.10"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
-dependencies = [
- "const-oid 0.9.6",
- "zeroize",
-]
-
[[package]]
name = "der"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d"
dependencies = [
- "const-oid 0.10.2",
+ "const-oid",
"zeroize",
]
@@ -479,7 +404,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
"block-buffer 0.10.4",
- "const-oid 0.9.6",
"crypto-common 0.1.7",
"subtle",
]
@@ -491,79 +415,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2"
dependencies = [
"block-buffer 0.12.1",
- "const-oid 0.10.2",
+ "const-oid",
"crypto-common 0.2.2",
"ctutils",
]
-[[package]]
-name = "ecdsa"
-version = "0.16.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
-dependencies = [
- "der 0.7.10",
- "digest 0.10.7",
- "elliptic-curve 0.13.8",
- "rfc6979 0.4.0",
- "signature 2.2.0",
- "spki 0.7.3",
-]
-
[[package]]
name = "ecdsa"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0"
dependencies = [
- "der 0.8.1",
+ "der",
"digest 0.11.3",
- "elliptic-curve 0.14.1",
- "rfc6979 0.6.0",
- "signature 3.0.0",
- "spki 0.8.0",
+ "elliptic-curve",
+ "rfc6979",
+ "signature",
+ "spki",
"zeroize",
]
[[package]]
name = "ed25519"
-version = "2.2.3"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "115531babc129696a58c64a4fef0a8bf9e9698629fb97e9e40767d235cfbcd53"
+checksum = "29fcf32e6c73d1079f83ab4d782de2d81620346a5f38c6237a86a22f8368980a"
dependencies = [
- "pkcs8 0.10.2",
- "signature 2.2.0",
+ "signature",
]
[[package]]
name = "ed25519-dalek"
-version = "2.2.0"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70e796c081cee67dc755e1a36a0a172b897fab85fc3f6bc48307991f64e4eca9"
+checksum = "6ebaa1a2bf1290ab3bfe5a7b771d050ebffab2711c19a81691c683a5144a25de"
dependencies = [
"curve25519-dalek",
"ed25519",
- "serde",
- "sha2 0.10.9",
- "subtle",
- "zeroize",
-]
-
-[[package]]
-name = "elliptic-curve"
-version = "0.13.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
-dependencies = [
- "base16ct 0.2.0",
- "crypto-bigint 0.5.5",
- "digest 0.10.7",
- "ff 0.13.1",
- "generic-array",
- "group 0.13.0",
- "pkcs8 0.10.2",
- "rand_core 0.6.4",
- "sec1 0.7.3",
+ "sha2",
"subtle",
"zeroize",
]
@@ -574,30 +463,20 @@ version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65"
dependencies = [
- "base16ct 1.0.0",
- "crypto-bigint 0.7.5",
+ "base16ct",
+ "crypto-bigint",
"crypto-common 0.2.2",
"digest 0.11.3",
- "ff 0.14.0",
- "group 0.14.0",
+ "ff",
+ "group",
"hybrid-array",
- "pkcs8 0.11.0",
+ "pkcs8",
"rand_core 0.10.1",
- "sec1 0.8.1",
+ "sec1",
"subtle",
"zeroize",
]
-[[package]]
-name = "ff"
-version = "0.13.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
-dependencies = [
- "rand_core 0.6.4",
- "subtle",
-]
-
[[package]]
name = "ff"
version = "0.14.0"
@@ -610,15 +489,15 @@ dependencies = [
[[package]]
name = "fiat-crypto"
-version = "0.2.9"
+version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d"
+checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24"
[[package]]
name = "find-msvc-tools"
-version = "0.1.9"
+version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
+checksum = "26b73573e6edcd2af0cdf47bd6cb58f0b3839491263c314eaad1ccf24430e1de"
[[package]]
name = "generic-array"
@@ -628,18 +507,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
- "zeroize",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
]
[[package]]
@@ -660,18 +527,7 @@ version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5"
dependencies = [
- "polyval 0.7.3",
-]
-
-[[package]]
-name = "group"
-version = "0.13.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
-dependencies = [
- "ff 0.13.1",
- "rand_core 0.6.4",
- "subtle",
+ "polyval",
]
[[package]]
@@ -680,7 +536,7 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4"
dependencies = [
- "ff 0.14.0",
+ "ff",
"rand_core 0.10.1",
"subtle",
]
@@ -691,16 +547,7 @@ version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4aaa26c720c68b866f2c96ef5c1264b3e6f473fe5d4ce61cd44bbe913e553018"
dependencies = [
- "hmac 0.13.0",
-]
-
-[[package]]
-name = "hmac"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e"
-dependencies = [
- "digest 0.10.7",
+ "hmac",
]
[[package]]
@@ -723,15 +570,6 @@ dependencies = [
"zeroize",
]
-[[package]]
-name = "inout"
-version = "0.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01"
-dependencies = [
- "generic-array",
-]
-
[[package]]
name = "inout"
version = "0.2.2"
@@ -747,15 +585,15 @@ version = "0.1.35"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3"
dependencies = [
- "getrandom 0.4.3",
+ "getrandom",
"libc",
]
[[package]]
name = "keccak"
-version = "0.2.0"
+version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9e24a010dd405bd7ed803e5253182815b41bf2e6a80cc3bfc066658e03a198aa"
+checksum = "ffd9697dc4a9a62e2da93389f34400b77a28f0287711263cabb203b3ccb9c0e4"
dependencies = [
"cfg-if",
"cpufeatures 0.3.0",
@@ -792,44 +630,42 @@ version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
-[[package]]
-name = "opaque-debug"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
-
[[package]]
name = "p256"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a"
dependencies = [
- "ecdsa 0.17.0",
- "elliptic-curve 0.14.1",
+ "ecdsa",
+ "elliptic-curve",
"primefield",
- "primeorder 0.14.0",
- "sha2 0.11.0",
+ "primeorder",
+ "sha2",
]
[[package]]
name = "p384"
-version = "0.13.1"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe42f1670a52a47d448f14b6a5c61dd78fce51856e68edaa38f7ae3a46b8d6b6"
+checksum = "d17b851e6b3e378ab4ecb07fa2ed23f4d15f075735f8fec9fa1e7bdce5f8301f"
dependencies = [
- "ecdsa 0.16.9",
- "elliptic-curve 0.13.8",
- "primeorder 0.13.6",
- "sha2 0.10.9",
+ "ecdsa",
+ "elliptic-curve",
+ "fiat-crypto",
+ "primefield",
+ "primeorder",
+ "sha2",
]
[[package]]
name = "password-hash"
-version = "0.6.1"
+version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aab41826031698d6ffcd9cff78ef56ef998e39dc7e5067cdfebe373842d4723b"
+checksum = "346f04948ba92c43e8469c1ee6736c7563d71012b17d40745260fe106aac2166"
dependencies = [
- "phc",
+ "base64ct",
+ "rand_core 0.6.4",
+ "subtle",
]
[[package]]
@@ -839,27 +675,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629"
dependencies = [
"digest 0.11.3",
- "hmac 0.13.0",
-]
-
-[[package]]
-name = "phc"
-version = "0.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44dc769b75f93afdddd8c7fa12d685292ddeff1e66f7f0f3a234cf1818afe892"
-dependencies = [
- "base64ct",
- "ctutils",
-]
-
-[[package]]
-name = "pkcs8"
-version = "0.10.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
-dependencies = [
- "der 0.7.10",
- "spki 0.7.3",
+ "hmac",
]
[[package]]
@@ -868,8 +684,8 @@ version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7"
dependencies = [
- "der 0.8.1",
- "spki 0.8.0",
+ "der",
+ "spki",
]
[[package]]
@@ -879,19 +695,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c"
dependencies = [
"cpufeatures 0.3.0",
- "universal-hash 0.6.1",
-]
-
-[[package]]
-name = "polyval"
-version = "0.6.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.2.17",
- "opaque-debug",
- "universal-hash 0.5.1",
+ "universal-hash",
]
[[package]]
@@ -902,7 +706,7 @@ checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd"
dependencies = [
"cpubits",
"cpufeatures 0.3.0",
- "universal-hash 0.6.1",
+ "universal-hash",
]
[[package]]
@@ -911,30 +715,21 @@ version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4"
dependencies = [
- "crypto-bigint 0.7.5",
+ "crypto-bigint",
"crypto-common 0.2.2",
- "ff 0.14.0",
+ "ff",
"rand_core 0.10.1",
"subtle",
"zeroize",
]
-[[package]]
-name = "primeorder"
-version = "0.13.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
-dependencies = [
- "elliptic-curve 0.13.8",
-]
-
[[package]]
name = "primeorder"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06"
dependencies = [
- "elliptic-curve 0.14.1",
+ "elliptic-curve",
"once_cell",
"primefield",
"serdect",
@@ -970,9 +765,6 @@ name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
-dependencies = [
- "getrandom 0.2.17",
-]
[[package]]
name = "rand_core"
@@ -989,29 +781,19 @@ dependencies = [
"rustversion",
]
-[[package]]
-name = "rfc6979"
-version = "0.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
-dependencies = [
- "hmac 0.12.1",
- "subtle",
-]
-
[[package]]
name = "rfc6979"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b"
dependencies = [
- "crypto-bigint 0.7.5",
- "hmac 0.13.0",
+ "crypto-bigint",
+ "hmac",
]
[[package]]
name = "rscrypto"
-version = "0.7.8"
+version = "0.8.1"
[[package]]
name = "rscrypto-fuzz"
@@ -1031,7 +813,7 @@ dependencies = [
"digest 0.11.3",
"ed25519-dalek",
"hkdf",
- "hmac 0.13.0",
+ "hmac",
"libfuzzer-sys",
"p256",
"p384",
@@ -1040,7 +822,7 @@ dependencies = [
"rscrypto",
"rscrypto-fuzz-support",
"scrypt",
- "sha2 0.11.0",
+ "sha2",
"sha3",
"tiny-keccak",
"x25519-dalek",
@@ -1076,7 +858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f874456e72520ff1375a06c588eaf074b0f01f9e9e1aada45bd9b7954a6e42c"
dependencies = [
"cfg-if",
- "cipher 0.5.2",
+ "cipher",
]
[[package]]
@@ -1088,21 +870,7 @@ dependencies = [
"cfg-if",
"pbkdf2",
"salsa20",
- "sha2 0.11.0",
-]
-
-[[package]]
-name = "sec1"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
-dependencies = [
- "base16ct 0.2.0",
- "der 0.7.10",
- "generic-array",
- "pkcs8 0.10.2",
- "subtle",
- "zeroize",
+ "sha2",
]
[[package]]
@@ -1111,9 +879,9 @@ version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d"
dependencies = [
- "base16ct 1.0.0",
+ "base16ct",
"ctutils",
- "der 0.8.1",
+ "der",
"hybrid-array",
"subtle",
"zeroize",
@@ -1132,7 +900,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
dependencies = [
"serde_core",
- "serde_derive",
]
[[package]]
@@ -1161,21 +928,10 @@ version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e"
dependencies = [
- "base16ct 1.0.0",
+ "base16ct",
"serde",
]
-[[package]]
-name = "sha2"
-version = "0.10.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
-dependencies = [
- "cfg-if",
- "cpufeatures 0.2.17",
- "digest 0.10.7",
-]
-
[[package]]
name = "sha2"
version = "0.11.0"
@@ -1204,16 +960,6 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
-[[package]]
-name = "signature"
-version = "2.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
-dependencies = [
- "digest 0.10.7",
- "rand_core 0.6.4",
-]
-
[[package]]
name = "signature"
version = "3.0.0"
@@ -1230,16 +976,6 @@ version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "45e14297decde697ddf377c25752aead0927d5cfc89c2684d2af96901a4ceeea"
-[[package]]
-name = "spki"
-version = "0.7.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
-dependencies = [
- "base64ct",
- "der 0.7.10",
-]
-
[[package]]
name = "spki"
version = "0.8.0"
@@ -1247,7 +983,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f"
dependencies = [
"base64ct",
- "der 0.8.1",
+ "der",
]
[[package]]
@@ -1305,16 +1041,6 @@ version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
-[[package]]
-name = "universal-hash"
-version = "0.5.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
-dependencies = [
- "crypto-common 0.1.7",
- "subtle",
-]
-
[[package]]
name = "universal-hash"
version = "0.6.1"
@@ -1331,32 +1057,25 @@ version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
-[[package]]
-name = "wasi"
-version = "0.11.1+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-
[[package]]
name = "wnaf"
version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1"
dependencies = [
- "ff 0.14.0",
- "group 0.14.0",
+ "ff",
+ "group",
"hybrid-array",
]
[[package]]
name = "x25519-dalek"
-version = "2.0.1"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277"
+checksum = "e7e8131a03190127fb2263afc72b322ecadae46b6ff8c6f399ff5d02f5559af6"
dependencies = [
"curve25519-dalek",
- "rand_core 0.6.4",
- "serde",
+ "rand_core 0.10.1",
"zeroize",
]
@@ -1371,17 +1090,3 @@ name = "zeroize"
version = "1.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e"
-dependencies = [
- "zeroize_derive",
-]
-
-[[package]]
-name = "zeroize_derive"
-version = "1.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.119",
-]
diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml
index ce331f94..a60b0fb3 100644
--- a/fuzz/Cargo.toml
+++ b/fuzz/Cargo.toml
@@ -8,7 +8,7 @@ edition = "2024"
cargo-fuzz = true
[lints.rust]
-unexpected_cfgs = { level = "warn", check-cfg = [
+unexpected_cfgs = { level = "deny", check-cfg = [
'cfg(fuzzing)',
'cfg(rscrypto_internal_fuzzing)',
] }
@@ -28,24 +28,24 @@ rscrypto = { path = "..", features = ["std", "checksums", "hashes", "auth", "aea
# AEAD
ascon-aead = { version = "0.6", default-features = false, features = ["alloc"] }
aes-gcm = { version = "0.11", default-features = false, features = ["alloc", "aes"] }
-aes-gcm-siv = { version = "0.11", default-features = false, features = ["alloc", "aes"] }
+aes-gcm-siv = { version = "0.12", default-features = false, features = ["alloc", "aes"] }
aegis = "0.9"
chacha20poly1305 = { version = "0.11", default-features = false, features = ["alloc"] }
# Auth
-argon2 = { version = "0.6.0-rc.8", default-features = false, features = ["alloc"] }
-ed25519-dalek = "2.2"
+argon2 = { version = "0.5.3", default-features = false, features = ["alloc"] }
+ed25519-dalek = "3.0"
hmac = "0.13"
hkdf = "0.13"
p256 = { version = "0.14.0", default-features = false, features = ["ecdsa", "pkcs8", "std"] }
-p384 = { version = "0.13.1", default-features = false, features = ["ecdsa", "pkcs8", "std"] }
+p384 = { version = "0.14.0", default-features = false, features = ["ecdsa", "pkcs8", "std"] }
pbkdf2 = "0.13"
scrypt = { version = "0.12", default-features = false }
-x25519-dalek = { version = "2.0.1", features = ["static_secrets"] }
+x25519-dalek = { version = "3.0.0", features = ["static_secrets"] }
# Hashes
ascon-hash = "0.4"
-blake2 = "0.11.0-rc.6"
+blake2 = "0.10.6"
blake3 = "1.8"
cshake = { version = "0.2.1", default-features = false }
digest = "0.11"
diff --git a/fuzz/fuzz_targets/aead_aegis256.rs b/fuzz/fuzz_targets/aead_aegis256.rs
index 44802516..09aaf1af 100644
--- a/fuzz/fuzz_targets/aead_aegis256.rs
+++ b/fuzz/fuzz_targets/aead_aegis256.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/aead_aes128gcm.rs b/fuzz/fuzz_targets/aead_aes128gcm.rs
index 30d3a805..dcbcb90f 100644
--- a/fuzz/fuzz_targets/aead_aes128gcm.rs
+++ b/fuzz/fuzz_targets/aead_aes128gcm.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/aead_aes128gcmsiv.rs b/fuzz/fuzz_targets/aead_aes128gcmsiv.rs
index 3276daf4..ea606ddd 100644
--- a/fuzz/fuzz_targets/aead_aes128gcmsiv.rs
+++ b/fuzz/fuzz_targets/aead_aes128gcmsiv.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/aead_aes256gcm.rs b/fuzz/fuzz_targets/aead_aes256gcm.rs
index 691613d5..833a768c 100644
--- a/fuzz/fuzz_targets/aead_aes256gcm.rs
+++ b/fuzz/fuzz_targets/aead_aes256gcm.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/aead_aes256gcmsiv.rs b/fuzz/fuzz_targets/aead_aes256gcmsiv.rs
index e182e649..f5aefce3 100644
--- a/fuzz/fuzz_targets/aead_aes256gcmsiv.rs
+++ b/fuzz/fuzz_targets/aead_aes256gcmsiv.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/aead_ascon128.rs b/fuzz/fuzz_targets/aead_ascon128.rs
index e60350d4..7cb5625b 100644
--- a/fuzz/fuzz_targets/aead_ascon128.rs
+++ b/fuzz/fuzz_targets/aead_ascon128.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/aead_chacha20poly1305.rs b/fuzz/fuzz_targets/aead_chacha20poly1305.rs
index 86673314..b18a79c9 100644
--- a/fuzz/fuzz_targets/aead_chacha20poly1305.rs
+++ b/fuzz/fuzz_targets/aead_chacha20poly1305.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/aead_nonce_counter.rs b/fuzz/fuzz_targets/aead_nonce_counter.rs
index 35d353da..97ef6b9b 100644
--- a/fuzz/fuzz_targets/aead_nonce_counter.rs
+++ b/fuzz/fuzz_targets/aead_nonce_counter.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/aead_xchacha20poly1305.rs b/fuzz/fuzz_targets/aead_xchacha20poly1305.rs
index 489e289e..2d0d6078 100644
--- a/fuzz/fuzz_targets/aead_xchacha20poly1305.rs
+++ b/fuzz/fuzz_targets/aead_xchacha20poly1305.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_argon2d.rs b/fuzz/fuzz_targets/auth_argon2d.rs
index a45efb2f..3d97698d 100644
--- a/fuzz/fuzz_targets/auth_argon2d.rs
+++ b/fuzz/fuzz_targets/auth_argon2d.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_argon2i.rs b/fuzz/fuzz_targets/auth_argon2i.rs
index 92a97153..06414a6f 100644
--- a/fuzz/fuzz_targets/auth_argon2i.rs
+++ b/fuzz/fuzz_targets/auth_argon2i.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_argon2id.rs b/fuzz/fuzz_targets/auth_argon2id.rs
index 7d86ce8d..82aaffa8 100644
--- a/fuzz/fuzz_targets/auth_argon2id.rs
+++ b/fuzz/fuzz_targets/auth_argon2id.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_ed25519.rs b/fuzz/fuzz_targets/auth_ed25519.rs
index 1447944c..bdc6c808 100644
--- a/fuzz/fuzz_targets/auth_ed25519.rs
+++ b/fuzz/fuzz_targets/auth_ed25519.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_ed25519_verify.rs b/fuzz/fuzz_targets/auth_ed25519_verify.rs
index 1feb01d4..a61f9316 100644
--- a/fuzz/fuzz_targets/auth_ed25519_verify.rs
+++ b/fuzz/fuzz_targets/auth_ed25519_verify.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_hkdf_sha256.rs b/fuzz/fuzz_targets/auth_hkdf_sha256.rs
index 7a356e3b..9beee577 100644
--- a/fuzz/fuzz_targets/auth_hkdf_sha256.rs
+++ b/fuzz/fuzz_targets/auth_hkdf_sha256.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_hkdf_sha384.rs b/fuzz/fuzz_targets/auth_hkdf_sha384.rs
index 41edb832..d48279d1 100644
--- a/fuzz/fuzz_targets/auth_hkdf_sha384.rs
+++ b/fuzz/fuzz_targets/auth_hkdf_sha384.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_hmac_sha256.rs b/fuzz/fuzz_targets/auth_hmac_sha256.rs
index 4249bbbf..a02999eb 100644
--- a/fuzz/fuzz_targets/auth_hmac_sha256.rs
+++ b/fuzz/fuzz_targets/auth_hmac_sha256.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_hmac_sha384.rs b/fuzz/fuzz_targets/auth_hmac_sha384.rs
index 3c867e00..e372fcb9 100644
--- a/fuzz/fuzz_targets/auth_hmac_sha384.rs
+++ b/fuzz/fuzz_targets/auth_hmac_sha384.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_hmac_sha512.rs b/fuzz/fuzz_targets/auth_hmac_sha512.rs
index 0205f50e..441a4a53 100644
--- a/fuzz/fuzz_targets/auth_hmac_sha512.rs
+++ b/fuzz/fuzz_targets/auth_hmac_sha512.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_kmac256.rs b/fuzz/fuzz_targets/auth_kmac256.rs
index c87e8e57..c33d9039 100644
--- a/fuzz/fuzz_targets/auth_kmac256.rs
+++ b/fuzz/fuzz_targets/auth_kmac256.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_pbkdf2.rs b/fuzz/fuzz_targets/auth_pbkdf2.rs
index ff7a4668..c70fea3d 100644
--- a/fuzz/fuzz_targets/auth_pbkdf2.rs
+++ b/fuzz/fuzz_targets/auth_pbkdf2.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_phc.rs b/fuzz/fuzz_targets/auth_phc.rs
index a77ff641..126149a9 100644
--- a/fuzz/fuzz_targets/auth_phc.rs
+++ b/fuzz/fuzz_targets/auth_phc.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_scrypt.rs b/fuzz/fuzz_targets/auth_scrypt.rs
index 177e0bf6..be949324 100644
--- a/fuzz/fuzz_targets/auth_scrypt.rs
+++ b/fuzz/fuzz_targets/auth_scrypt.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/auth_x25519.rs b/fuzz/fuzz_targets/auth_x25519.rs
index 04ba47b1..36703851 100644
--- a/fuzz/fuzz_targets/auth_x25519.rs
+++ b/fuzz/fuzz_targets/auth_x25519.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/checksum_crc.rs b/fuzz/fuzz_targets/checksum_crc.rs
index 8ce3e7d4..092575b6 100644
--- a/fuzz/fuzz_targets/checksum_crc.rs
+++ b/fuzz/fuzz_targets/checksum_crc.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/fast_rapidhash.rs b/fuzz/fuzz_targets/fast_rapidhash.rs
index 6826985a..212c8422 100644
--- a/fuzz/fuzz_targets/fast_rapidhash.rs
+++ b/fuzz/fuzz_targets/fast_rapidhash.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/fast_xxh3.rs b/fuzz/fuzz_targets/fast_xxh3.rs
index e1967fa2..dbd78f4d 100644
--- a/fuzz/fuzz_targets/fast_xxh3.rs
+++ b/fuzz/fuzz_targets/fast_xxh3.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_ascon.rs b/fuzz/fuzz_targets/hash_ascon.rs
index 6a652898..bd00a12d 100644
--- a/fuzz/fuzz_targets/hash_ascon.rs
+++ b/fuzz/fuzz_targets/hash_ascon.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_ascon_cxof.rs b/fuzz/fuzz_targets/hash_ascon_cxof.rs
index bb51e4f9..3d93175d 100644
--- a/fuzz/fuzz_targets/hash_ascon_cxof.rs
+++ b/fuzz/fuzz_targets/hash_ascon_cxof.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_blake2b.rs b/fuzz/fuzz_targets/hash_blake2b.rs
index d9416c39..288c2abb 100644
--- a/fuzz/fuzz_targets/hash_blake2b.rs
+++ b/fuzz/fuzz_targets/hash_blake2b.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_blake2s.rs b/fuzz/fuzz_targets/hash_blake2s.rs
index 5cee46ce..e6b431df 100644
--- a/fuzz/fuzz_targets/hash_blake2s.rs
+++ b/fuzz/fuzz_targets/hash_blake2s.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_blake3.rs b/fuzz/fuzz_targets/hash_blake3.rs
index ad8d8210..b2835b66 100644
--- a/fuzz/fuzz_targets/hash_blake3.rs
+++ b/fuzz/fuzz_targets/hash_blake3.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_blake3_derive.rs b/fuzz/fuzz_targets/hash_blake3_derive.rs
index 0d92de67..609f2c3a 100644
--- a/fuzz/fuzz_targets/hash_blake3_derive.rs
+++ b/fuzz/fuzz_targets/hash_blake3_derive.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_blake3_keyed.rs b/fuzz/fuzz_targets/hash_blake3_keyed.rs
index adbde5eb..1daf7c28 100644
--- a/fuzz/fuzz_targets/hash_blake3_keyed.rs
+++ b/fuzz/fuzz_targets/hash_blake3_keyed.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_cshake256.rs b/fuzz/fuzz_targets/hash_cshake256.rs
index 5ebd4601..52c2bc46 100644
--- a/fuzz/fuzz_targets/hash_cshake256.rs
+++ b/fuzz/fuzz_targets/hash_cshake256.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_sha2.rs b/fuzz/fuzz_targets/hash_sha2.rs
index f7397733..306888bc 100644
--- a/fuzz/fuzz_targets/hash_sha2.rs
+++ b/fuzz/fuzz_targets/hash_sha2.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hash_sha3.rs b/fuzz/fuzz_targets/hash_sha3.rs
index 7d72a358..49b9f46d 100644
--- a/fuzz/fuzz_targets/hash_sha3.rs
+++ b/fuzz/fuzz_targets/hash_sha3.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/hex_parse.rs b/fuzz/fuzz_targets/hex_parse.rs
index 45ee5985..8a947d3b 100644
--- a/fuzz/fuzz_targets/hex_parse.rs
+++ b/fuzz/fuzz_targets/hex_parse.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/fuzz_targets/traits_io.rs b/fuzz/fuzz_targets/traits_io.rs
index e741afd4..fe50fb54 100644
--- a/fuzz/fuzz_targets/traits_io.rs
+++ b/fuzz/fuzz_targets/traits_io.rs
@@ -4,5 +4,5 @@
mod target_impl;
libfuzzer_sys::fuzz_target!(|data: &[u8]| {
- target_impl::run(data);
+ target_impl::run(data);
});
diff --git a/fuzz/support/src/lib.rs b/fuzz/support/src/lib.rs
index ab1ff4f4..a2e0c9a5 100644
--- a/fuzz/support/src/lib.rs
+++ b/fuzz/support/src/lib.rs
@@ -51,12 +51,12 @@ where
);
let mut files = fs::read_dir(corpus_dir)
- .unwrap_or_else(|err| panic!("corpus replay: failed to read {}: {err}", corpus_dir.display()))
- .map(|entry| entry.unwrap_or_else(|err| panic!("corpus replay: failed to read directory entry: {err}")))
+ .expect("corpus replay directory must be readable")
+ .map(|entry| entry.expect("corpus replay directory entries must be readable"))
.filter_map(|entry| {
let file_type = entry
.file_type()
- .unwrap_or_else(|err| panic!("corpus replay: failed to inspect {}: {err}", entry.path().display()));
+ .expect("corpus replay directory entry metadata must be readable");
if file_type.is_file() || file_type.is_symlink() {
Some(entry.path())
} else {
@@ -74,7 +74,7 @@ where
let mut count = 0usize;
for path in files {
- let data = fs::read(&path).unwrap_or_else(|err| panic!("corpus replay: failed to read {}: {err}", path.display()));
+ let data = fs::read(&path).expect("corpus replay input must be readable");
run(&data);
count = count.checked_add(1).expect("corpus replay file count overflow");
}
@@ -154,7 +154,7 @@ pub fn pad_salt_to(material: &[u8], filler: u8) -> [u8; N] {
out[..N / 2].fill(filler);
} else {
for (i, slot) in out.iter_mut().enumerate() {
- *slot = material[i % material.len()];
+ *slot = material[i.rem_euclid(material.len())];
}
}
out
@@ -258,7 +258,7 @@ pub fn assert_aead_forgery(cipher: &A, nonce: &A::Nonce, aad: &[u8], pl
match target {
0 if !ct.is_empty() => {
let mut forged = ct.clone();
- let idx = seed as usize % forged.len();
+ let idx = usize::from(seed).rem_euclid(forged.len());
forged[idx] ^= 1u8 << (seed as u32 & 7);
assert!(
cipher.decrypt_in_place(nonce, aad, &mut forged, &tag).is_err(),
@@ -267,7 +267,7 @@ pub fn assert_aead_forgery(cipher: &A, nonce: &A::Nonce, aad: &[u8], pl
}
2 if !aad.is_empty() => {
let mut forged_aad = aad.to_vec();
- let idx = seed as usize % forged_aad.len();
+ let idx = usize::from(seed).rem_euclid(forged_aad.len());
forged_aad[idx] ^= 1u8 << (seed as u32 & 7);
let mut ct_copy = ct.clone();
assert!(
@@ -278,9 +278,9 @@ pub fn assert_aead_forgery(cipher: &A, nonce: &A::Nonce, aad: &[u8], pl
_ => {
let tag_ref = tag.as_ref();
let mut tag_bytes = tag_ref.to_vec();
- let idx = seed as usize % tag_bytes.len();
+ let idx = usize::from(seed).rem_euclid(tag_bytes.len());
tag_bytes[idx] ^= 1u8 << (seed as u32 & 7);
- let forged_tag = A::tag_from_slice(&tag_bytes).unwrap();
+ let forged_tag = A::tag_from_slice(&tag_bytes).expect("forged tag preserves the algorithm tag length");
let mut ct_copy = ct.clone();
assert!(
cipher.decrypt_in_place(nonce, aad, &mut ct_copy, &forged_tag).is_err(),
diff --git a/fuzz/target_impls/aead_aegis256.rs b/fuzz/target_impls/aead_aegis256.rs
index af12a9c0..4fc1f8c6 100644
--- a/fuzz/target_impls/aead_aegis256.rs
+++ b/fuzz/target_impls/aead_aegis256.rs
@@ -1,10 +1,10 @@
use rscrypto::{
- Aegis256, Aegis256Key,
- aead::{Nonce256, expert::AeadWithNonce},
+ Aegis256, Aegis256Key,
+ aead::{Nonce256, expert::AeadWithNonce},
};
use rscrypto_fuzz::{FuzzInput, assert_aead_forgery, assert_aead_roundtrip, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 32] = some_or_return!(input.bytes());
let nonce_bytes: [u8; 32] = some_or_return!(input.bytes());
@@ -26,9 +26,14 @@ pub fn run(data: &[u8]) {
let tag = cipher
.encrypt_in_place(&nonce, aad, &mut ct)
.expect("differential: rscrypto encrypt must succeed");
- let tag_arr: [u8; 16] = tag.as_ref().try_into().unwrap();
+ let tag_arr: [u8; 16] = tag
+ .as_ref()
+ .try_into()
+ .expect("AEGIS-256 produces a 16-byte authentication tag");
let oracle = OracleAegis::<16>::new(&key_bytes, &nonce_bytes);
- let pt = oracle.decrypt(&ct, &tag_arr, aad).unwrap();
+ let pt = oracle
+ .decrypt(&ct, &tag_arr, aad)
+ .expect("oracle must accept the equivalent rscrypto ciphertext");
assert_eq!(pt, plaintext, "oracle failed to decrypt our ciphertext");
// oracle encrypt → rscrypto decrypt
@@ -36,7 +41,12 @@ pub fn run(data: &[u8]) {
let (oct, otag) = oracle_enc.encrypt(plaintext, aad);
let mut buf = oct;
cipher
- .decrypt_in_place(&nonce, aad, &mut buf, &Aegis256::tag_from_slice(&otag).unwrap())
+ .decrypt_in_place(
+ &nonce,
+ aad,
+ &mut buf,
+ &Aegis256::tag_from_slice(&otag).expect("oracle produces a 16-byte authentication tag"),
+ )
.expect("we failed to decrypt oracle ciphertext");
assert_eq!(buf, plaintext, "decrypt mismatch on oracle ciphertext");
}
diff --git a/fuzz/target_impls/aead_aes128gcm.rs b/fuzz/target_impls/aead_aes128gcm.rs
index b810880f..2a7f88b9 100644
--- a/fuzz/target_impls/aead_aes128gcm.rs
+++ b/fuzz/target_impls/aead_aes128gcm.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_aead_against_oracle, assert_aead_forgery, assert_aead_roundtrip, some_or_return,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 16] = some_or_return!(input.bytes());
let nonce_bytes: [u8; 12] = some_or_return!(input.bytes());
@@ -18,14 +18,22 @@ pub fn run(data: &[u8]) {
// Differential: rscrypto ↔ aes-gcm crate.
use aes_gcm::aead::{Aead as _, KeyInit, Payload};
- let oracle = aes_gcm::Aes128Gcm::new_from_slice(&key_bytes).unwrap();
+ let oracle = aes_gcm::Aes128Gcm::new_from_slice(&key_bytes).expect("AES-128-GCM accepts a 16-byte key");
let on = aes_gcm::Nonce::from(nonce_bytes);
assert_aead_against_oracle(
&cipher,
&nonce,
aad,
plaintext,
- |pt, aad| oracle.encrypt(&on, Payload { msg: pt, aad }).unwrap(),
- |ct, aad| oracle.decrypt(&on, Payload { msg: ct, aad }).unwrap(),
+ |pt, aad| {
+ oracle
+ .encrypt(&on, Payload { msg: pt, aad })
+ .expect("oracle encryption accepts the fuzz input")
+ },
+ |ct, aad| {
+ oracle
+ .decrypt(&on, Payload { msg: ct, aad })
+ .expect("oracle must accept the equivalent rscrypto ciphertext")
+ },
);
}
diff --git a/fuzz/target_impls/aead_aes128gcmsiv.rs b/fuzz/target_impls/aead_aes128gcmsiv.rs
index 036ec8cc..3d0055bc 100644
--- a/fuzz/target_impls/aead_aes128gcmsiv.rs
+++ b/fuzz/target_impls/aead_aes128gcmsiv.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_aead_against_oracle, assert_aead_forgery, assert_aead_roundtrip, some_or_return,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 16] = some_or_return!(input.bytes());
let nonce_bytes: [u8; 12] = some_or_return!(input.bytes());
@@ -18,14 +18,22 @@ pub fn run(data: &[u8]) {
// Differential: rscrypto ↔ aes-gcm-siv crate.
use aes_gcm_siv::aead::{Aead as _, KeyInit, Payload};
- let oracle = aes_gcm_siv::Aes128GcmSiv::new_from_slice(&key_bytes).unwrap();
- let on = aes_gcm_siv::Nonce::from_slice(&nonce_bytes);
+ let oracle = aes_gcm_siv::Aes128GcmSiv::new_from_slice(&key_bytes).expect("AES-128-GCM-SIV accepts a 16-byte key");
+ let on = aes_gcm_siv::Nonce::from(nonce_bytes);
assert_aead_against_oracle(
&cipher,
&nonce,
aad,
plaintext,
- |pt, aad| oracle.encrypt(on, Payload { msg: pt, aad }).unwrap(),
- |ct, aad| oracle.decrypt(on, Payload { msg: ct, aad }).unwrap(),
+ |pt, aad| {
+ oracle
+ .encrypt(&on, Payload { msg: pt, aad })
+ .expect("oracle encryption accepts the fuzz input")
+ },
+ |ct, aad| {
+ oracle
+ .decrypt(&on, Payload { msg: ct, aad })
+ .expect("oracle must accept the equivalent rscrypto ciphertext")
+ },
);
}
diff --git a/fuzz/target_impls/aead_aes256gcm.rs b/fuzz/target_impls/aead_aes256gcm.rs
index 73e0d142..40a49538 100644
--- a/fuzz/target_impls/aead_aes256gcm.rs
+++ b/fuzz/target_impls/aead_aes256gcm.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_aead_against_oracle, assert_aead_forgery, assert_aead_roundtrip, some_or_return,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 32] = some_or_return!(input.bytes());
let nonce_bytes: [u8; 12] = some_or_return!(input.bytes());
@@ -18,14 +18,22 @@ pub fn run(data: &[u8]) {
// Differential: rscrypto ↔ aes-gcm crate.
use aes_gcm::aead::{Aead as _, KeyInit, Payload};
- let oracle = aes_gcm::Aes256Gcm::new_from_slice(&key_bytes).unwrap();
+ let oracle = aes_gcm::Aes256Gcm::new_from_slice(&key_bytes).expect("AES-256-GCM accepts a 32-byte key");
let on = aes_gcm::Nonce::from(nonce_bytes);
assert_aead_against_oracle(
&cipher,
&nonce,
aad,
plaintext,
- |pt, aad| oracle.encrypt(&on, Payload { msg: pt, aad }).unwrap(),
- |ct, aad| oracle.decrypt(&on, Payload { msg: ct, aad }).unwrap(),
+ |pt, aad| {
+ oracle
+ .encrypt(&on, Payload { msg: pt, aad })
+ .expect("oracle encryption accepts the fuzz input")
+ },
+ |ct, aad| {
+ oracle
+ .decrypt(&on, Payload { msg: ct, aad })
+ .expect("oracle must accept the equivalent rscrypto ciphertext")
+ },
);
}
diff --git a/fuzz/target_impls/aead_aes256gcmsiv.rs b/fuzz/target_impls/aead_aes256gcmsiv.rs
index 5c171acd..34ce2186 100644
--- a/fuzz/target_impls/aead_aes256gcmsiv.rs
+++ b/fuzz/target_impls/aead_aes256gcmsiv.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_aead_against_oracle, assert_aead_forgery, assert_aead_roundtrip, some_or_return,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 32] = some_or_return!(input.bytes());
let nonce_bytes: [u8; 12] = some_or_return!(input.bytes());
@@ -18,14 +18,22 @@ pub fn run(data: &[u8]) {
// Differential: rscrypto ↔ aes-gcm-siv crate.
use aes_gcm_siv::aead::{Aead as _, KeyInit, Payload};
- let oracle = aes_gcm_siv::Aes256GcmSiv::new_from_slice(&key_bytes).unwrap();
- let on = aes_gcm_siv::Nonce::from_slice(&nonce_bytes);
+ let oracle = aes_gcm_siv::Aes256GcmSiv::new_from_slice(&key_bytes).expect("AES-256-GCM-SIV accepts a 32-byte key");
+ let on = aes_gcm_siv::Nonce::from(nonce_bytes);
assert_aead_against_oracle(
&cipher,
&nonce,
aad,
plaintext,
- |pt, aad| oracle.encrypt(on, Payload { msg: pt, aad }).unwrap(),
- |ct, aad| oracle.decrypt(on, Payload { msg: ct, aad }).unwrap(),
+ |pt, aad| {
+ oracle
+ .encrypt(&on, Payload { msg: pt, aad })
+ .expect("oracle encryption accepts the fuzz input")
+ },
+ |ct, aad| {
+ oracle
+ .decrypt(&on, Payload { msg: ct, aad })
+ .expect("oracle must accept the equivalent rscrypto ciphertext")
+ },
);
}
diff --git a/fuzz/target_impls/aead_ascon128.rs b/fuzz/target_impls/aead_ascon128.rs
index d2fd430e..fa6dc6df 100644
--- a/fuzz/target_impls/aead_ascon128.rs
+++ b/fuzz/target_impls/aead_ascon128.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_aead_against_oracle, assert_aead_forgery, assert_aead_roundtrip, some_or_return,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 16] = some_or_return!(input.bytes());
let nonce_bytes: [u8; 16] = some_or_return!(input.bytes());
@@ -18,14 +18,22 @@ pub fn run(data: &[u8]) {
// Differential: rscrypto ↔ ascon-aead crate.
use ascon_aead::aead::{Aead as _, KeyInit, Payload, array::Array};
- let oracle = ascon_aead::AsconAead128::new_from_slice(&key_bytes).unwrap();
+ let oracle = ascon_aead::AsconAead128::new_from_slice(&key_bytes).expect("Ascon-AEAD128 accepts a 16-byte key");
let on = Array(nonce_bytes);
assert_aead_against_oracle(
&cipher,
&nonce,
aad,
plaintext,
- |pt, aad| oracle.encrypt(&on, Payload { msg: pt, aad }).unwrap(),
- |ct, aad| oracle.decrypt(&on, Payload { msg: ct, aad }).unwrap(),
+ |pt, aad| {
+ oracle
+ .encrypt(&on, Payload { msg: pt, aad })
+ .expect("oracle encryption accepts the fuzz input")
+ },
+ |ct, aad| {
+ oracle
+ .decrypt(&on, Payload { msg: ct, aad })
+ .expect("oracle must accept the equivalent rscrypto ciphertext")
+ },
);
}
diff --git a/fuzz/target_impls/aead_chacha20poly1305.rs b/fuzz/target_impls/aead_chacha20poly1305.rs
index 296d041c..039c0ab9 100644
--- a/fuzz/target_impls/aead_chacha20poly1305.rs
+++ b/fuzz/target_impls/aead_chacha20poly1305.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_aead_against_oracle, assert_aead_forgery, assert_aead_roundtrip, some_or_return,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 32] = some_or_return!(input.bytes());
let nonce_bytes: [u8; 12] = some_or_return!(input.bytes());
@@ -18,14 +18,23 @@ pub fn run(data: &[u8]) {
// Differential: rscrypto ↔ chacha20poly1305 crate.
use chacha20poly1305::aead::{Aead as _, KeyInit, Payload};
- let oracle = chacha20poly1305::ChaCha20Poly1305::new_from_slice(&key_bytes).unwrap();
+ let oracle =
+ chacha20poly1305::ChaCha20Poly1305::new_from_slice(&key_bytes).expect("ChaCha20-Poly1305 accepts a 32-byte key");
let on = chacha20poly1305::Nonce::from(nonce_bytes);
assert_aead_against_oracle(
&cipher,
&nonce,
aad,
plaintext,
- |pt, aad| oracle.encrypt(&on, Payload { msg: pt, aad }).unwrap(),
- |ct, aad| oracle.decrypt(&on, Payload { msg: ct, aad }).unwrap(),
+ |pt, aad| {
+ oracle
+ .encrypt(&on, Payload { msg: pt, aad })
+ .expect("oracle encryption accepts the fuzz input")
+ },
+ |ct, aad| {
+ oracle
+ .decrypt(&on, Payload { msg: ct, aad })
+ .expect("oracle must accept the equivalent rscrypto ciphertext")
+ },
);
}
diff --git a/fuzz/target_impls/aead_nonce_counter.rs b/fuzz/target_impls/aead_nonce_counter.rs
index cd681fa2..b764fc48 100644
--- a/fuzz/target_impls/aead_nonce_counter.rs
+++ b/fuzz/target_impls/aead_nonce_counter.rs
@@ -1,6 +1,6 @@
use rscrypto::{
- Aes256Gcm, Aes256GcmKey,
- aead::{NonceCounter, expert::AeadWithNonce},
+ Aes256Gcm, Aes256GcmKey,
+ aead::{NonceCounter, expert::AeadWithNonce},
};
use rscrypto_fuzz::{FuzzInput, some_or_return, split_at_ratio};
@@ -87,7 +87,7 @@ fn assert_resume_equivalence(prefix: [u8; 4], advance: u32) {
);
}
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 32] = some_or_return!(input.bytes());
let prefix: [u8; 4] = some_or_return!(input.bytes());
@@ -101,12 +101,12 @@ pub fn run(data: &[u8]) {
// are pinned by `aes_gcm_nonce_counter_exhausts_cleanly` and
// `aes_gcm_nonce_counter_with_counter_rejects_max` in src/aead/nonce_counter.rs.
let initial = u64::from_le_bytes(initial_bytes)
- % NonceCounter::::MAX_MESSAGES.strict_sub(u64::from(MAX_NONCES_PER_ITER));
- let burst = (u32::from(burst_byte) % MAX_NONCES_PER_ITER).strict_add(1);
+ .rem_euclid(NonceCounter::::MAX_MESSAGES.strict_sub(u64::from(MAX_NONCES_PER_ITER)));
+ let burst = u32::from(burst_byte).rem_euclid(MAX_NONCES_PER_ITER).strict_add(1);
// Resume-equivalence advance bounded so the fresh-counter loop runs
// ≤ MAX_NONCES_PER_ITER times — exercised every iteration rather than
// gated on a 10⁻¹² fuzzer probability.
- let resume_advance = u32::from(burst_byte) % MAX_NONCES_PER_ITER;
+ let resume_advance = u32::from(burst_byte).rem_euclid(MAX_NONCES_PER_ITER);
let cipher = Aes256Gcm::new(&Aes256GcmKey::from_bytes(key_bytes));
let (aad, plaintext) = split_at_ratio(rest, aad_split);
diff --git a/fuzz/target_impls/aead_xchacha20poly1305.rs b/fuzz/target_impls/aead_xchacha20poly1305.rs
index b0341567..5f08f3b9 100644
--- a/fuzz/target_impls/aead_xchacha20poly1305.rs
+++ b/fuzz/target_impls/aead_xchacha20poly1305.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_aead_against_oracle, assert_aead_forgery, assert_aead_roundtrip, some_or_return,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 32] = some_or_return!(input.bytes());
let nonce_bytes: [u8; 24] = some_or_return!(input.bytes());
@@ -18,14 +18,23 @@ pub fn run(data: &[u8]) {
// Differential: rscrypto ↔ chacha20poly1305 crate (XChaCha variant).
use chacha20poly1305::aead::{Aead as _, KeyInit, Payload};
- let oracle = chacha20poly1305::XChaCha20Poly1305::new_from_slice(&key_bytes).unwrap();
+ let oracle =
+ chacha20poly1305::XChaCha20Poly1305::new_from_slice(&key_bytes).expect("XChaCha20-Poly1305 accepts a 32-byte key");
let on = chacha20poly1305::XNonce::from(nonce_bytes);
assert_aead_against_oracle(
&cipher,
&nonce,
aad,
plaintext,
- |pt, aad| oracle.encrypt(&on, Payload { msg: pt, aad }).unwrap(),
- |ct, aad| oracle.decrypt(&on, Payload { msg: ct, aad }).unwrap(),
+ |pt, aad| {
+ oracle
+ .encrypt(&on, Payload { msg: pt, aad })
+ .expect("oracle encryption accepts the fuzz input")
+ },
+ |ct, aad| {
+ oracle
+ .decrypt(&on, Payload { msg: ct, aad })
+ .expect("oracle must accept the equivalent rscrypto ciphertext")
+ },
);
}
diff --git a/fuzz/target_impls/auth_argon2d.rs b/fuzz/target_impls/auth_argon2d.rs
index 0b1c083d..2c10d2b9 100644
--- a/fuzz/target_impls/auth_argon2d.rs
+++ b/fuzz/target_impls/auth_argon2d.rs
@@ -1,7 +1,7 @@
use rscrypto::{Argon2Params, Argon2d};
use rscrypto_fuzz::{FuzzInput, pad_salt_to, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let pw_salt_split: u8 = some_or_return!(input.byte());
let out_len_byte: u8 = some_or_return!(input.byte());
@@ -13,8 +13,7 @@ pub fn run(data: &[u8]) {
let t = 1u32.strict_add(u32::from(t_byte) % 4);
let out_len = 4u32.strict_add(u32::from(out_len_byte) % 29);
- let params = Argon2Params::new(m_kib, t, 1)
- .expect("params must be valid for fuzzer ranges");
+ let params = Argon2Params::new(m_kib, t, 1).expect("params must be valid for fuzzer ranges");
// See `auth_argon2id.rs` for cost-parameter rationale.
let (password, salt_material) = split_at_ratio(rest, pw_salt_split);
diff --git a/fuzz/target_impls/auth_argon2i.rs b/fuzz/target_impls/auth_argon2i.rs
index 799f2adf..0430dce9 100644
--- a/fuzz/target_impls/auth_argon2i.rs
+++ b/fuzz/target_impls/auth_argon2i.rs
@@ -1,7 +1,7 @@
use rscrypto::{Argon2Params, Argon2i};
use rscrypto_fuzz::{FuzzInput, pad_salt_to, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let pw_salt_split: u8 = some_or_return!(input.byte());
let out_len_byte: u8 = some_or_return!(input.byte());
@@ -13,8 +13,7 @@ pub fn run(data: &[u8]) {
let t = 1u32.strict_add(u32::from(t_byte) % 4);
let out_len = 4u32.strict_add(u32::from(out_len_byte) % 29);
- let params = Argon2Params::new(m_kib, t, 1)
- .expect("params must be valid for fuzzer ranges");
+ let params = Argon2Params::new(m_kib, t, 1).expect("params must be valid for fuzzer ranges");
// See `auth_argon2id.rs` for cost-parameter rationale.
let (password, salt_material) = split_at_ratio(rest, pw_salt_split);
diff --git a/fuzz/target_impls/auth_argon2id.rs b/fuzz/target_impls/auth_argon2id.rs
index 57fef803..7e0bc4a8 100644
--- a/fuzz/target_impls/auth_argon2id.rs
+++ b/fuzz/target_impls/auth_argon2id.rs
@@ -1,7 +1,7 @@
use rscrypto::{Argon2Params, Argon2id};
use rscrypto_fuzz::{FuzzInput, pad_salt_to, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let pw_salt_split: u8 = some_or_return!(input.byte());
let out_len_byte: u8 = some_or_return!(input.byte());
@@ -16,8 +16,7 @@ pub fn run(data: &[u8]) {
let t = 1u32.strict_add(u32::from(t_byte) % 4);
let out_len = 4u32.strict_add(u32::from(out_len_byte) % 29); // 4..=32 per RFC 9106 §3.1
- let params = Argon2Params::new(m_kib, t, 1)
- .expect("params must be valid for fuzzer ranges");
+ let params = Argon2Params::new(m_kib, t, 1).expect("params must be valid for fuzzer ranges");
let (password, salt_material) = split_at_ratio(rest, pw_salt_split);
let salt_buf = pad_salt_to::<16>(salt_material, pw_salt_split);
diff --git a/fuzz/target_impls/auth_ecdsa_sign.rs b/fuzz/target_impls/auth_ecdsa_sign.rs
index fb1dd2e7..f796f4bc 100644
--- a/fuzz/target_impls/auth_ecdsa_sign.rs
+++ b/fuzz/target_impls/auth_ecdsa_sign.rs
@@ -7,7 +7,7 @@ use p384::ecdsa::{
use rscrypto::{EcdsaP256Keypair, EcdsaP256SecretKey, EcdsaP384Keypair, EcdsaP384SecretKey};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let selector = some_or_return!(input.byte());
@@ -25,15 +25,21 @@ fn run_p256(input: &mut FuzzInput<'_>) {
let message = input.rest();
let public = keypair.public_key();
let signature = some_or_return!(keypair.try_sign(message).ok());
- let oracle_public = P256OracleVerifyingKey::from_sec1_bytes(&public.to_sec1_bytes()).expect("derived P-256 public key");
+ let oracle_public =
+ P256OracleVerifyingKey::from_sec1_bytes(&public.to_sec1_bytes()).expect("derived P-256 public key");
let oracle_signature = P256OracleSignature::from_slice(signature.as_bytes()).expect("derived P-256 signature");
- assert!(public.verify(message, &signature).is_ok());
- assert!(P256Verifier::verify(&oracle_public, message, &oracle_signature).is_ok());
+ public
+ .verify(message, &signature)
+ .expect("P-256 public key must verify its own signature");
+ P256Verifier::verify(&oracle_public, message, &oracle_signature)
+ .expect("P-256 oracle must verify the equivalent signature");
let mut tampered = message.to_vec();
tampered.push(0x80);
- assert!(public.verify(&tampered, &signature).is_err());
+ let _verification_error = public
+ .verify(&tampered, &signature)
+ .expect_err("P-256 verification must reject a tampered message");
}
fn run_p384(input: &mut FuzzInput<'_>) {
@@ -43,13 +49,19 @@ fn run_p384(input: &mut FuzzInput<'_>) {
let message = input.rest();
let public = keypair.public_key();
let signature = some_or_return!(keypair.try_sign(message).ok());
- let oracle_public = P384OracleVerifyingKey::from_sec1_bytes(&public.to_sec1_bytes()).expect("derived P-384 public key");
+ let oracle_public =
+ P384OracleVerifyingKey::from_sec1_bytes(&public.to_sec1_bytes()).expect("derived P-384 public key");
let oracle_signature = P384OracleSignature::from_slice(signature.as_bytes()).expect("derived P-384 signature");
- assert!(public.verify(message, &signature).is_ok());
- assert!(P384Verifier::verify(&oracle_public, message, &oracle_signature).is_ok());
+ public
+ .verify(message, &signature)
+ .expect("P-384 public key must verify its own signature");
+ P384Verifier::verify(&oracle_public, message, &oracle_signature)
+ .expect("P-384 oracle must verify the equivalent signature");
let mut tampered = message.to_vec();
tampered.push(0x80);
- assert!(public.verify(&tampered, &signature).is_err());
+ let _verification_error = public
+ .verify(&tampered, &signature)
+ .expect_err("P-384 verification must reject a tampered message");
}
diff --git a/fuzz/target_impls/auth_ecdsa_verify.rs b/fuzz/target_impls/auth_ecdsa_verify.rs
index 2c04d88f..b38245c9 100644
--- a/fuzz/target_impls/auth_ecdsa_verify.rs
+++ b/fuzz/target_impls/auth_ecdsa_verify.rs
@@ -13,7 +13,7 @@ fn array_from_slice(bytes: &[u8]) -> [u8; N] {
bytes.try_into().expect("oracle ECDSA signature length must match")
}
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let selector = some_or_return!(input.byte());
let derive_public = selector & 2 != 0;
diff --git a/fuzz/target_impls/auth_ed25519.rs b/fuzz/target_impls/auth_ed25519.rs
index fca0dbb8..fb1dae1c 100644
--- a/fuzz/target_impls/auth_ed25519.rs
+++ b/fuzz/target_impls/auth_ed25519.rs
@@ -1,7 +1,7 @@
use rscrypto::{Ed25519SecretKey, Ed25519Signature};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_bytes: [u8; 32] = some_or_return!(input.bytes());
let message = input.rest();
diff --git a/fuzz/target_impls/auth_ed25519_verify.rs b/fuzz/target_impls/auth_ed25519_verify.rs
index 5f5fc732..a1f63c87 100644
--- a/fuzz/target_impls/auth_ed25519_verify.rs
+++ b/fuzz/target_impls/auth_ed25519_verify.rs
@@ -1,7 +1,7 @@
use rscrypto::{Ed25519PublicKey, Ed25519Signature};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let public_bytes: [u8; 32] = some_or_return!(input.bytes());
let signature_bytes: [u8; 64] = some_or_return!(input.bytes());
diff --git a/fuzz/target_impls/auth_hkdf_sha256.rs b/fuzz/target_impls/auth_hkdf_sha256.rs
index 8091ec39..bcd3b119 100644
--- a/fuzz/target_impls/auth_hkdf_sha256.rs
+++ b/fuzz/target_impls/auth_hkdf_sha256.rs
@@ -1,7 +1,7 @@
use rscrypto::HkdfSha256;
use rscrypto_fuzz::{FuzzInput, assert_hkdf_against_oracle, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let salt_split: u8 = some_or_return!(input.byte());
let ikm_split: u8 = some_or_return!(input.byte());
@@ -14,7 +14,7 @@ pub fn run(data: &[u8]) {
// Exercise both valid and over-length expand requests without allocating
// unbounded buffers.
- let out_len = usize::from(u16::from_le_bytes(out_len_bytes)) % HkdfSha256::MAX_OUTPUT_SIZE.strict_add(33);
+ let out_len = usize::from(u16::from_le_bytes(out_len_bytes)).rem_euclid(HkdfSha256::MAX_OUTPUT_SIZE.strict_add(33));
let hk = HkdfSha256::new(salt, ikm);
let mut okm = vec![0u8; out_len];
diff --git a/fuzz/target_impls/auth_hkdf_sha384.rs b/fuzz/target_impls/auth_hkdf_sha384.rs
index 8ba07a37..45c6be9d 100644
--- a/fuzz/target_impls/auth_hkdf_sha384.rs
+++ b/fuzz/target_impls/auth_hkdf_sha384.rs
@@ -1,7 +1,7 @@
use rscrypto::HkdfSha384;
use rscrypto_fuzz::{FuzzInput, assert_hkdf_against_oracle, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let salt_split: u8 = some_or_return!(input.byte());
let ikm_split: u8 = some_or_return!(input.byte());
@@ -11,7 +11,7 @@ pub fn run(data: &[u8]) {
let (salt, remainder) = split_at_ratio(rest, salt_split);
let (ikm, info) = split_at_ratio(remainder, ikm_split);
- let out_len = usize::from(u16::from_le_bytes(out_len_bytes)) % HkdfSha384::MAX_OUTPUT_SIZE.strict_add(33);
+ let out_len = usize::from(u16::from_le_bytes(out_len_bytes)).rem_euclid(HkdfSha384::MAX_OUTPUT_SIZE.strict_add(33));
let hk = HkdfSha384::new(salt, ikm);
let mut okm = vec![0u8; out_len];
diff --git a/fuzz/target_impls/auth_hmac_sha256.rs b/fuzz/target_impls/auth_hmac_sha256.rs
index 14983aa3..446ddc3a 100644
--- a/fuzz/target_impls/auth_hmac_sha256.rs
+++ b/fuzz/target_impls/auth_hmac_sha256.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_mac_against_oracle, assert_mac_reset, assert_mac_streaming, some_or_return, split_at_ratio,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let key_split: u8 = some_or_return!(input.byte());
@@ -20,7 +20,7 @@ pub fn run(data: &[u8]) {
assert_mac_against_oracle::(key, message, &tag, |key, msg| {
use hmac::{Hmac, KeyInit, Mac as _};
- let mut oracle = as KeyInit>::new_from_slice(key).unwrap();
+ let mut oracle = as KeyInit>::new_from_slice(key).expect("HMAC accepts keys of every length");
oracle.update(msg);
oracle.finalize().into_bytes().to_vec()
});
diff --git a/fuzz/target_impls/auth_hmac_sha384.rs b/fuzz/target_impls/auth_hmac_sha384.rs
index 73aa94cf..3b221513 100644
--- a/fuzz/target_impls/auth_hmac_sha384.rs
+++ b/fuzz/target_impls/auth_hmac_sha384.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_mac_against_oracle, assert_mac_reset, assert_mac_streaming, some_or_return, split_at_ratio,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let key_split: u8 = some_or_return!(input.byte());
@@ -19,7 +19,7 @@ pub fn run(data: &[u8]) {
assert_mac_against_oracle::(key, message, &tag, |key, msg| {
use hmac::{Hmac, KeyInit, Mac as _};
- let mut oracle = as KeyInit>::new_from_slice(key).unwrap();
+ let mut oracle = as KeyInit>::new_from_slice(key).expect("HMAC accepts keys of every length");
oracle.update(msg);
oracle.finalize().into_bytes().to_vec()
});
diff --git a/fuzz/target_impls/auth_hmac_sha512.rs b/fuzz/target_impls/auth_hmac_sha512.rs
index 9db49b07..5c2b44a4 100644
--- a/fuzz/target_impls/auth_hmac_sha512.rs
+++ b/fuzz/target_impls/auth_hmac_sha512.rs
@@ -3,7 +3,7 @@ use rscrypto_fuzz::{
FuzzInput, assert_mac_against_oracle, assert_mac_reset, assert_mac_streaming, some_or_return, split_at_ratio,
};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let key_split: u8 = some_or_return!(input.byte());
@@ -19,7 +19,7 @@ pub fn run(data: &[u8]) {
assert_mac_against_oracle::(key, message, &tag, |key, msg| {
use hmac::{Hmac, KeyInit, Mac as _};
- let mut oracle = as KeyInit>::new_from_slice(key).unwrap();
+ let mut oracle = as KeyInit>::new_from_slice(key).expect("HMAC accepts keys of every length");
oracle.update(msg);
oracle.finalize().into_bytes().to_vec()
});
diff --git a/fuzz/target_impls/auth_kmac256.rs b/fuzz/target_impls/auth_kmac256.rs
index 31854463..c4e7c75a 100644
--- a/fuzz/target_impls/auth_kmac256.rs
+++ b/fuzz/target_impls/auth_kmac256.rs
@@ -3,7 +3,11 @@ use rscrypto_fuzz::{FuzzInput, some_or_return, split_at_ratio};
fn encoded_string_len(len: usize) -> usize {
let bits = len.strict_mul(8);
- let width = ((usize::BITS - bits.leading_zeros()) as usize).div_ceil(8).max(1);
+ let width_bits = usize::BITS.strict_sub(bits.leading_zeros());
+ let width = usize::try_from(width_bits)
+ .expect("the bit width fits usize")
+ .div_ceil(8)
+ .max(1);
1usize.strict_add(width).strict_add(len)
}
@@ -15,7 +19,7 @@ fn bytepad_is_aligned(rate: usize, segments: &[usize]) -> bool {
encoded_len.is_multiple_of(rate)
}
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let key_split: u8 = some_or_return!(input.byte());
@@ -24,7 +28,7 @@ pub fn run(data: &[u8]) {
let (key, remainder) = split_at_ratio(rest, key_split);
let (custom, message) = split_at_ratio(remainder, split);
- let out_len = (out_len_byte as usize % 128).strict_add(1);
+ let out_len = usize::from(out_len_byte).rem_euclid(128).strict_add(1);
// Property: streaming equivalence
let mut expected = vec![0u8; out_len];
diff --git a/fuzz/target_impls/auth_mlkem1024.rs b/fuzz/target_impls/auth_mlkem1024.rs
index 698c8138..fa6de78f 100644
--- a/fuzz/target_impls/auth_mlkem1024.rs
+++ b/fuzz/target_impls/auth_mlkem1024.rs
@@ -3,7 +3,7 @@ use rscrypto::{
};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_random: [u8; MlKem1024::KEY_GENERATION_RANDOM_SIZE] = some_or_return!(input.bytes());
let encaps_random: [u8; MlKem1024::ENCAPSULATION_RANDOM_SIZE] = some_or_return!(input.bytes());
@@ -27,9 +27,9 @@ pub fn run(data: &[u8]) {
);
let parse_material = input.rest();
- let _ = MlKem1024EncapsulationKey::try_from_slice(parse_material);
- let _ = MlKem1024DecapsulationKey::try_from_slice(parse_material);
- let _ = MlKem1024Ciphertext::try_from_slice(parse_material);
+ let _encapsulation_key_result = MlKem1024EncapsulationKey::try_from_slice(parse_material);
+ let _decapsulation_key_result = MlKem1024DecapsulationKey::try_from_slice(parse_material);
+ let _ciphertext_result = MlKem1024Ciphertext::try_from_slice(parse_material);
let Some(byte_idx) = input.byte() else {
return;
@@ -39,7 +39,7 @@ pub fn run(data: &[u8]) {
};
let mut modified = ciphertext.to_bytes();
- modified[byte_idx as usize % MlKem1024::CIPHERTEXT_SIZE] ^= 1u8 << (bit_idx & 7);
+ modified[usize::from(byte_idx).rem_euclid(MlKem1024::CIPHERTEXT_SIZE)] ^= 1u8.strict_shl(u32::from(bit_idx & 7));
let rejected = MlKem1024::decapsulate(&dk, &MlKem1024Ciphertext::from_bytes(modified))
.expect("ML-KEM implicit rejection returns a shared secret");
assert!(
diff --git a/fuzz/target_impls/auth_mlkem512.rs b/fuzz/target_impls/auth_mlkem512.rs
index 3722449a..23d7b56a 100644
--- a/fuzz/target_impls/auth_mlkem512.rs
+++ b/fuzz/target_impls/auth_mlkem512.rs
@@ -3,7 +3,7 @@ use rscrypto::{
};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_random: [u8; MlKem512::KEY_GENERATION_RANDOM_SIZE] = some_or_return!(input.bytes());
let encaps_random: [u8; MlKem512::ENCAPSULATION_RANDOM_SIZE] = some_or_return!(input.bytes());
@@ -27,9 +27,9 @@ pub fn run(data: &[u8]) {
);
let parse_material = input.rest();
- let _ = MlKem512EncapsulationKey::try_from_slice(parse_material);
- let _ = MlKem512DecapsulationKey::try_from_slice(parse_material);
- let _ = MlKem512Ciphertext::try_from_slice(parse_material);
+ let _encapsulation_key_result = MlKem512EncapsulationKey::try_from_slice(parse_material);
+ let _decapsulation_key_result = MlKem512DecapsulationKey::try_from_slice(parse_material);
+ let _ciphertext_result = MlKem512Ciphertext::try_from_slice(parse_material);
let Some(byte_idx) = input.byte() else {
return;
@@ -39,7 +39,7 @@ pub fn run(data: &[u8]) {
};
let mut modified = ciphertext.to_bytes();
- modified[byte_idx as usize % MlKem512::CIPHERTEXT_SIZE] ^= 1u8 << (bit_idx & 7);
+ modified[usize::from(byte_idx).rem_euclid(MlKem512::CIPHERTEXT_SIZE)] ^= 1u8.strict_shl(u32::from(bit_idx & 7));
let rejected = MlKem512::decapsulate(&dk, &MlKem512Ciphertext::from_bytes(modified))
.expect("ML-KEM implicit rejection returns a shared secret");
assert!(
diff --git a/fuzz/target_impls/auth_mlkem768.rs b/fuzz/target_impls/auth_mlkem768.rs
index e8810455..f5aabf80 100644
--- a/fuzz/target_impls/auth_mlkem768.rs
+++ b/fuzz/target_impls/auth_mlkem768.rs
@@ -3,7 +3,7 @@ use rscrypto::{
};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key_random: [u8; MlKem768::KEY_GENERATION_RANDOM_SIZE] = some_or_return!(input.bytes());
let encaps_random: [u8; MlKem768::ENCAPSULATION_RANDOM_SIZE] = some_or_return!(input.bytes());
@@ -27,9 +27,9 @@ pub fn run(data: &[u8]) {
);
let parse_material = input.rest();
- let _ = MlKem768EncapsulationKey::try_from_slice(parse_material);
- let _ = MlKem768DecapsulationKey::try_from_slice(parse_material);
- let _ = MlKem768Ciphertext::try_from_slice(parse_material);
+ let _encapsulation_key_result = MlKem768EncapsulationKey::try_from_slice(parse_material);
+ let _decapsulation_key_result = MlKem768DecapsulationKey::try_from_slice(parse_material);
+ let _ciphertext_result = MlKem768Ciphertext::try_from_slice(parse_material);
let Some(byte_idx) = input.byte() else {
return;
@@ -39,7 +39,7 @@ pub fn run(data: &[u8]) {
};
let mut modified = ciphertext.to_bytes();
- modified[byte_idx as usize % MlKem768::CIPHERTEXT_SIZE] ^= 1u8 << (bit_idx & 7);
+ modified[usize::from(byte_idx).rem_euclid(MlKem768::CIPHERTEXT_SIZE)] ^= 1u8.strict_shl(u32::from(bit_idx & 7));
let rejected = MlKem768::decapsulate(&dk, &MlKem768Ciphertext::from_bytes(modified))
.expect("ML-KEM implicit rejection returns a shared secret");
assert!(
diff --git a/fuzz/target_impls/auth_pbkdf2.rs b/fuzz/target_impls/auth_pbkdf2.rs
index dd6c921f..a02ed27c 100644
--- a/fuzz/target_impls/auth_pbkdf2.rs
+++ b/fuzz/target_impls/auth_pbkdf2.rs
@@ -1,7 +1,7 @@
use rscrypto::{Pbkdf2Sha256, Pbkdf2Sha512};
use rscrypto_fuzz::{FuzzInput, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let out_len_byte: u8 = some_or_return!(input.byte());
@@ -9,20 +9,25 @@ pub fn run(data: &[u8]) {
let data = input.rest();
let (password, salt) = split_at_ratio(data, split);
- let out_len = (out_len_byte as usize % 96).strict_add(1);
- let iterations = (u32::from(u16::from_le_bytes(iterations_bytes)) % 64).strict_add(1);
+ let out_len = usize::from(out_len_byte).rem_euclid(96).strict_add(1);
+ let iterations = u32::from(u16::from_le_bytes(iterations_bytes))
+ .rem_euclid(64)
+ .strict_add(1);
let mut ours_256 = vec![0u8; out_len];
let mut ours_256_state = vec![0u8; out_len];
- Pbkdf2Sha256::derive_key_primitive(password, salt, iterations, &mut ours_256).unwrap();
+ Pbkdf2Sha256::derive_key_primitive(password, salt, iterations, &mut ours_256)
+ .expect("the bounded PBKDF2-SHA-256 request is valid");
Pbkdf2Sha256::new(password)
.derive(salt, iterations, &mut ours_256_state)
- .unwrap();
+ .expect("the bounded PBKDF2-SHA-256 request is valid");
assert_eq!(ours_256, ours_256_state, "pbkdf2-sha256 state reuse mismatch");
- assert!(Pbkdf2Sha256::verify_password_primitive(password, salt, iterations, &ours_256).is_ok());
+ Pbkdf2Sha256::verify_password_primitive(password, salt, iterations, &ours_256)
+ .expect("PBKDF2-SHA-256 must verify its own output");
let mut wrong_256 = ours_256.clone();
wrong_256[0] ^= 1;
- assert!(Pbkdf2Sha256::verify_password_primitive(password, salt, iterations, &wrong_256).is_err());
+ let _verification_error = Pbkdf2Sha256::verify_password_primitive(password, salt, iterations, &wrong_256)
+ .expect_err("PBKDF2-SHA-256 must reject a corrupted output");
let mut oracle_256 = vec![0u8; out_len];
pbkdf2::pbkdf2_hmac::(password, salt, iterations, &mut oracle_256);
@@ -30,15 +35,18 @@ pub fn run(data: &[u8]) {
let mut ours_512 = vec![0u8; out_len];
let mut ours_512_state = vec![0u8; out_len];
- Pbkdf2Sha512::derive_key_primitive(password, salt, iterations, &mut ours_512).unwrap();
+ Pbkdf2Sha512::derive_key_primitive(password, salt, iterations, &mut ours_512)
+ .expect("the bounded PBKDF2-SHA-512 request is valid");
Pbkdf2Sha512::new(password)
.derive(salt, iterations, &mut ours_512_state)
- .unwrap();
+ .expect("the bounded PBKDF2-SHA-512 request is valid");
assert_eq!(ours_512, ours_512_state, "pbkdf2-sha512 state reuse mismatch");
- assert!(Pbkdf2Sha512::verify_password_primitive(password, salt, iterations, &ours_512).is_ok());
+ Pbkdf2Sha512::verify_password_primitive(password, salt, iterations, &ours_512)
+ .expect("PBKDF2-SHA-512 must verify its own output");
let mut wrong_512 = ours_512.clone();
wrong_512[0] ^= 1;
- assert!(Pbkdf2Sha512::verify_password_primitive(password, salt, iterations, &wrong_512).is_err());
+ let _verification_error = Pbkdf2Sha512::verify_password_primitive(password, salt, iterations, &wrong_512)
+ .expect_err("PBKDF2-SHA-512 must reject a corrupted output");
let mut oracle_512 = vec![0u8; out_len];
pbkdf2::pbkdf2_hmac::(password, salt, iterations, &mut oracle_512);
diff --git a/fuzz/target_impls/auth_phc.rs b/fuzz/target_impls/auth_phc.rs
index 0bced237..c3da30cf 100644
--- a/fuzz/target_impls/auth_phc.rs
+++ b/fuzz/target_impls/auth_phc.rs
@@ -4,24 +4,18 @@
// canonical record remains cheap while malformed or over-budget inputs
// exercise the parser and approval boundary at full throughput.
-use rscrypto::{
- Argon2Params, Argon2idPassword, ScryptParams, ScryptPassword,
-};
+use rscrypto::{Argon2Params, Argon2idPassword, ScryptParams, ScryptPassword};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let split = data.len() / 2;
let (password, encoded_bytes) = data.split_at(split);
let encoded = String::from_utf8_lossy(encoded_bytes);
- let argon2 = Argon2idPassword::new(
- Argon2Params::new(8, 1, 1).expect("fixed Argon2 fuzz profile is valid"),
- )
- .expect("fixed Argon2 fuzz profile fits the target");
- let scrypt = ScryptPassword::new(
- ScryptParams::new(1, 1, 1).expect("fixed scrypt fuzz profile is valid"),
- )
- .expect("fixed scrypt fuzz profile fits the target");
+ let argon2 = Argon2idPassword::new(Argon2Params::new(8, 1, 1).expect("fixed Argon2 fuzz profile is valid"))
+ .expect("fixed Argon2 fuzz profile fits the target");
+ let scrypt = ScryptPassword::new(ScryptParams::new(1, 1, 1).expect("fixed scrypt fuzz profile is valid"))
+ .expect("fixed scrypt fuzz profile fits the target");
- let _ = argon2.verify_password(password, &encoded);
- let _ = scrypt.verify_password(password, &encoded);
+ let _argon2_result = argon2.verify_password(password, &encoded);
+ let _scrypt_result = scrypt.verify_password(password, &encoded);
}
diff --git a/fuzz/target_impls/auth_rsa_import.rs b/fuzz/target_impls/auth_rsa_import.rs
index b63c8ca6..6e71a2a5 100644
--- a/fuzz/target_impls/auth_rsa_import.rs
+++ b/fuzz/target_impls/auth_rsa_import.rs
@@ -1,10 +1,10 @@
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
-use rscrypto_fuzz::{FuzzInput, some_or_return};
-#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
use rscrypto::auth::rsa::fuzz_rsa_import_der;
+#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
+use rscrypto_fuzz::{FuzzInput, some_or_return};
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let format = some_or_return!(input.byte());
let (der, expected) = if input.rest().first().copied() == Some(b'V') {
@@ -15,12 +15,15 @@ pub fn run(data: &[u8]) {
let accepted = fuzz_rsa_import_der(format, &der);
if let Some(expected) = expected {
- assert_eq!(accepted, expected, "generated RSA private-key import fixture expectation drifted");
+ assert_eq!(
+ accepted, expected,
+ "generated RSA private-key import fixture expectation drifted"
+ );
}
}
#[cfg(not(any(fuzzing, rscrypto_internal_fuzzing)))]
-pub fn run(_data: &[u8]) {}
+pub(super) fn run(_data: &[u8]) {}
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
fn decoded_der(input: &[u8]) -> Vec {
@@ -57,9 +60,21 @@ fn hex_value(byte: u8) -> Option {
fn valid_private_key_der(format: u8, control: &[u8]) -> (Vec, Option) {
let control = control.strip_suffix(b"\n").unwrap_or(control);
let pkcs1 = match control.first().copied() {
- Some(b'P') => pkcs1_private_key_der_with_crt(&[1], &hex_to_vec(RSA_PRIVATE_EXPONENT_Q_HEX), &hex_to_vec(RSA_PRIVATE_COEFFICIENT_HEX)),
- Some(b'Q') => pkcs1_private_key_der_with_crt(&hex_to_vec(RSA_PRIVATE_EXPONENT_P_HEX), &[1], &hex_to_vec(RSA_PRIVATE_COEFFICIENT_HEX)),
- Some(b'C') => pkcs1_private_key_der_with_crt(&hex_to_vec(RSA_PRIVATE_EXPONENT_P_HEX), &hex_to_vec(RSA_PRIVATE_EXPONENT_Q_HEX), &[1]),
+ Some(b'P') => pkcs1_private_key_der_with_crt(
+ &[1],
+ &hex_to_vec(RSA_PRIVATE_EXPONENT_Q_HEX),
+ &hex_to_vec(RSA_PRIVATE_COEFFICIENT_HEX),
+ ),
+ Some(b'Q') => pkcs1_private_key_der_with_crt(
+ &hex_to_vec(RSA_PRIVATE_EXPONENT_P_HEX),
+ &[1],
+ &hex_to_vec(RSA_PRIVATE_COEFFICIENT_HEX),
+ ),
+ Some(b'C') => pkcs1_private_key_der_with_crt(
+ &hex_to_vec(RSA_PRIVATE_EXPONENT_P_HEX),
+ &hex_to_vec(RSA_PRIVATE_EXPONENT_Q_HEX),
+ &[1],
+ ),
Some(b'N') => pkcs1_private_key_der_with_noncanonical_version(),
_ => valid_pkcs1_private_key_der(),
};
@@ -94,7 +109,7 @@ fn valid_private_key_der(format: u8, control: &[u8]) -> (Vec, Option)
}
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
-pub fn valid_pkcs1_private_key_der() -> Vec {
+pub(super) fn valid_pkcs1_private_key_der() -> Vec {
pkcs1_private_key_der_with_crt(
&hex_to_vec(RSA_PRIVATE_EXPONENT_P_HEX),
&hex_to_vec(RSA_PRIVATE_EXPONENT_Q_HEX),
@@ -143,7 +158,11 @@ fn private_key_der_for_format(format: u8, pkcs1: &[u8]) -> Vec {
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
fn valid_pkcs8_private_key_der(pkcs1: &[u8]) -> Vec {
- sequence(&[integer_unsigned(&[0]), algorithm_identifier(RSA_ENCRYPTION_OID, Some(&der_null())), tlv(0x04, pkcs1)])
+ sequence(&[
+ integer_unsigned(&[0]),
+ algorithm_identifier(RSA_ENCRYPTION_OID, Some(&der_null())),
+ tlv(0x04, pkcs1),
+ ])
}
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
diff --git a/fuzz/target_impls/auth_rsa_private_ops.rs b/fuzz/target_impls/auth_rsa_private_ops.rs
index 706422a4..6b0168c0 100644
--- a/fuzz/target_impls/auth_rsa_private_ops.rs
+++ b/fuzz/target_impls/auth_rsa_private_ops.rs
@@ -1,13 +1,12 @@
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
use rscrypto::{
- RsaEncryptionError, RsaOaepProfile, RsaPkcs1v15Profile, RsaPrivateKey, RsaPssProfile, RsaPublicKeyPolicy,
- RsaSignatureProfile,
+ RsaBlindingPair, RsaEncryptionError, RsaOaepProfile, RsaPkcs1v15Profile, RsaPrivateKey, RsaPssProfile,
+ RsaPublicKeyPolicy, RsaSignatureProfile,
};
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
use rscrypto_fuzz::{FuzzInput, some_or_return, split_at_ratio};
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
-#[allow(dead_code)]
#[path = "auth_rsa_import.rs"]
mod rsa_import_fixture;
@@ -23,7 +22,7 @@ const PSS_PROFILES: [RsaPssProfile; 3] = [RsaPssProfile::Sha256, RsaPssProfile::
const OAEP_PROFILES: [RsaOaepProfile; 3] = [RsaOaepProfile::Sha256, RsaOaepProfile::Sha384, RsaOaepProfile::Sha512];
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let mode = some_or_return!(input.byte());
let selector = some_or_return!(input.byte());
@@ -37,12 +36,12 @@ pub fn run(data: &[u8]) {
.expect("fuzz RSA private-key fixture must parse");
let (one, one_inverse) = factor_one(key.signature_len());
- match mode % 10 {
+ match mode.rem_euclid(10) {
0 => {
let profile = PKCS1_PROFILES[usize::from(selector) % PKCS1_PROFILES.len()];
let mut signature = vec![0u8; key.signature_len()];
key
- .sign_pkcs1v15_with_blinding_factor(profile, left, &one, &one_inverse, &mut signature)
+ .sign_pkcs1v15_with_blinding_factor(profile, left, RsaBlindingPair::new(&one, &one_inverse), &mut signature)
.expect("fixture RSA-PKCS1-v1_5 signing must succeed");
key
.public_key()
@@ -54,7 +53,13 @@ pub fn run(data: &[u8]) {
let salt = bounded_slice(right, profile.digest_len());
let mut signature = vec![0u8; key.signature_len()];
key
- .sign_pss_with_salt_and_blinding_factor(profile, left, salt, &one, &one_inverse, &mut signature)
+ .sign_pss_with_salt_and_blinding_factor(
+ profile,
+ left,
+ salt,
+ RsaBlindingPair::new(&one, &one_inverse),
+ &mut signature,
+ )
.expect("fixture RSA-PSS signing must succeed");
key
.public_key()
@@ -73,7 +78,13 @@ pub fn run(data: &[u8]) {
.expect("fixture RSA-OAEP encryption must succeed for bounded message");
let mut plaintext = vec![0u8; key.signature_len()];
let plaintext_len = key
- .decrypt_oaep_with_blinding_factor(profile, label, &ciphertext, &one, &one_inverse, &mut plaintext)
+ .decrypt_oaep_with_blinding_factor(
+ profile,
+ label,
+ &ciphertext,
+ RsaBlindingPair::new(&one, &one_inverse),
+ &mut plaintext,
+ )
.expect("self-produced RSA-OAEP ciphertext must decrypt");
assert_eq!(&plaintext[..plaintext_len], message);
}
@@ -84,8 +95,7 @@ pub fn run(data: &[u8]) {
.sign_pkcs1v15_with_blinding_factor(
profile.pkcs1v15_profile().expect("profile is PKCS1-v1_5"),
left,
- &one,
- &one_inverse,
+ RsaBlindingPair::new(&one, &one_inverse),
&mut signature,
)
.expect("typed fixture RSA-PKCS1-v1_5 signing must succeed");
@@ -98,30 +108,47 @@ pub fn run(data: &[u8]) {
let profile = OAEP_PROFILES[usize::from(selector) % OAEP_PROFILES.len()];
let mut plaintext = vec![0u8; key.signature_len()];
let ciphertext = full_width_candidate(left, key.signature_len());
- let _ = key.decrypt_oaep_with_blinding_factor(profile, right, &ciphertext, &one, &one_inverse, &mut plaintext);
+ let _decryption_result = key.decrypt_oaep_with_blinding_factor(
+ profile,
+ right,
+ &ciphertext,
+ RsaBlindingPair::new(&one, &one_inverse),
+ &mut plaintext,
+ );
}
5 => {
let profile = PKCS1_PROFILES[usize::from(selector) % PKCS1_PROFILES.len()];
let mut signature = vec![0u8; key.signature_len()];
let bad_factor = full_width_candidate(left, key.signature_len());
- let _ = key.sign_pkcs1v15_with_blinding_factor(profile, right, &bad_factor, &one_inverse, &mut signature);
+ let _signing_result = key.sign_pkcs1v15_with_blinding_factor(
+ profile,
+ right,
+ RsaBlindingPair::new(&bad_factor, &one_inverse),
+ &mut signature,
+ );
}
6 => {
let profile = PSS_PROFILES[usize::from(selector) % PSS_PROFILES.len()];
let mut short_signature = vec![0u8; key.signature_len().saturating_sub(1)];
- assert!(key
- .sign_pss_with_salt_and_blinding_factor(profile, left, right, &one, &one_inverse, &mut short_signature)
- .is_err());
+ let _signing_error = key
+ .sign_pss_with_salt_and_blinding_factor(
+ profile,
+ left,
+ right,
+ RsaBlindingPair::new(&one, &one_inverse),
+ &mut short_signature,
+ )
+ .expect_err("RSA-PSS signing must reject a short output buffer");
}
7 => {
let profile = OAEP_PROFILES[usize::from(selector) % OAEP_PROFILES.len()];
let mut ciphertext = vec![0u8; key.signature_len()];
- assert!(key
+ let _encryption_error = key
.public_key()
.encrypt_oaep_with_random_fill(profile, left, right, &mut ciphertext, |_| {
Err(RsaEncryptionError::EntropyUnavailable)
})
- .is_err());
+ .expect_err("RSA-OAEP encryption must propagate entropy failure");
}
8 => {
let message = bounded_slice(right, pkcs1v15_message_limit(&key));
@@ -133,21 +160,25 @@ pub fn run(data: &[u8]) {
.expect("fixture RSAES-PKCS1-v1_5 encryption must succeed for bounded message");
let mut plaintext = vec![0u8; key.signature_len()];
let plaintext_len = key
- .decrypt_pkcs1v15_with_blinding_factor(&ciphertext, &one, &one_inverse, &mut plaintext)
+ .decrypt_pkcs1v15_with_blinding_factor(&ciphertext, RsaBlindingPair::new(&one, &one_inverse), &mut plaintext)
.expect("self-produced RSAES-PKCS1-v1_5 ciphertext must decrypt");
assert_eq!(&plaintext[..plaintext_len], message);
}
9 => {
let mut plaintext = vec![0u8; key.signature_len()];
let ciphertext = full_width_candidate(left, key.signature_len());
- let _ = key.decrypt_pkcs1v15_with_blinding_factor(&ciphertext, &one, &one_inverse, &mut plaintext);
+ let _decryption_result = key.decrypt_pkcs1v15_with_blinding_factor(
+ &ciphertext,
+ RsaBlindingPair::new(&one, &one_inverse),
+ &mut plaintext,
+ );
}
- _ => unreachable!("mode modulo 10 is always in 0..10"),
+ _ => {}
}
}
#[cfg(not(any(fuzzing, rscrypto_internal_fuzzing)))]
-pub fn run(_data: &[u8]) {}
+pub(super) fn run(_data: &[u8]) {}
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
fn factor_one(len: usize) -> (Vec, Vec) {
@@ -191,7 +222,8 @@ fn fill_random_from(bytes: &[u8]) -> impl FnMut(&mut [u8]) -> Result<(), RsaEncr
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
fn oaep_message_limit(key: &RsaPrivateKey, profile: RsaOaepProfile) -> usize {
- key.signature_len()
+ key
+ .signature_len()
.saturating_sub(profile.digest_len().saturating_mul(2))
.saturating_sub(2)
}
@@ -213,10 +245,7 @@ fn pkcs1v15_message_limit(key: &RsaPrivateKey) -> usize {
#[cfg(any(fuzzing, rscrypto_internal_fuzzing))]
fn pkcs1v15_seed(key: &RsaPrivateKey, message_len: usize, selector: u8, left: &[u8], right: &[u8]) -> Vec {
- let len = key
- .signature_len()
- .saturating_sub(message_len)
- .saturating_sub(3);
+ let len = key.signature_len().saturating_sub(message_len).saturating_sub(3);
let mut seed = vec![selector.wrapping_add(1).max(1); len];
for (index, byte) in left.iter().chain(right.iter()).copied().enumerate() {
let seed_len = seed.len();
diff --git a/fuzz/target_impls/auth_rsa_protocol.rs b/fuzz/target_impls/auth_rsa_protocol.rs
index 4c51e398..152b95ff 100644
--- a/fuzz/target_impls/auth_rsa_protocol.rs
+++ b/fuzz/target_impls/auth_rsa_protocol.rs
@@ -28,8 +28,8 @@ const X509_PSS_DEFAULT_SHA1_ALGORITHM: &[u8] = &[
const X509_MALFORMED_INDEFINITE_SEQUENCE: &[u8] = &[0x30, 0x80, 0x00, 0x00];
const JWT_ALGS: [&str; 16] = [
- "PS256", "PS384", "PS512", "RS256", "RS384", "RS512", "none", "HS256", "ES256", "EdDSA", "ps256", "", "PS1",
- "RS1", "PS256\0", "RS256 ",
+ "PS256", "PS384", "PS512", "RS256", "RS384", "RS512", "none", "HS256", "ES256", "EdDSA", "ps256", "", "PS1", "RS1",
+ "PS256\0", "RS256 ",
];
const COSE_ALGORITHMS: [i64; 16] = [
-37,
@@ -50,8 +50,8 @@ const COSE_ALGORITHMS: [i64; 16] = [
i64::MAX - 1,
];
const TLS_SCHEMES: [u16; 19] = [
- 0x0804, 0x0805, 0x0806, 0x0809, 0x080a, 0x080b, 0x0401, 0x0501, 0x0601, 0x0101, 0x0201, 0x0420,
- 0x0520, 0x0620, 0x0301, 0x0203, 0x0403, 0, 0xffff,
+ 0x0804, 0x0805, 0x0806, 0x0809, 0x080a, 0x080b, 0x0401, 0x0501, 0x0601, 0x0101, 0x0201, 0x0420, 0x0520, 0x0620,
+ 0x0301, 0x0203, 0x0403, 0, 0xffff,
];
const X509_ALGORITHMS: [&[u8]; 6] = [
X509_PSS_SHA256_ALGORITHM,
@@ -69,7 +69,7 @@ fn signature_candidate(material: &[u8], len: usize) -> Vec {
}
for (index, byte) in out.iter_mut().enumerate() {
- *byte = material[index % material.len()];
+ *byte = material[index.rem_euclid(material.len())];
}
out
}
@@ -80,10 +80,12 @@ fn selected_signature<'a>(material: &'a [u8], full_width: &'a [u8], selector: u8
#[inline]
fn select(items: &[T], selector: u8) -> &T {
- &items[(selector as usize) % items.len()]
+ items
+ .get(usize::from(selector).rem_euclid(items.len()))
+ .expect("RSA protocol selector tables are nonempty")
}
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let mode = some_or_return!(input.byte());
let selector = some_or_return!(input.byte());
@@ -96,7 +98,7 @@ pub fn run(data: &[u8]) {
let signature = selected_signature(signature_material, &full_width_signature, selector);
let mut scratch = key.public_scratch();
- match mode % 14 {
+ match mode.rem_euclid(14) {
0 => {
key
.jwt_verifier(RsaJwtAlgorithm::Ps256)
@@ -134,12 +136,15 @@ pub fn run(data: &[u8]) {
.expect("valid TLS certificate rsa_pkcs1_sha256 fixture must verify");
}
5 => {
- let _ = key
- .jwt_verifier(RsaJwtAlgorithm::Ps256)
- .verify_with_scratch(select(&JWT_ALGS, selector), message, signature, &mut scratch);
+ let _verification_result = key.jwt_verifier(RsaJwtAlgorithm::Ps256).verify_with_scratch(
+ select(&JWT_ALGS, selector),
+ message,
+ signature,
+ &mut scratch,
+ );
}
6 => {
- let _ = key.verify_cose_algorithm_id_with_scratch(
+ let _verification_result = key.verify_cose_algorithm_id_with_scratch(
*select(&COSE_ALGORITHMS, selector),
message,
signature,
@@ -147,7 +152,7 @@ pub fn run(data: &[u8]) {
);
}
7 => {
- let _ = x509_key.verify_signature_from_x509_algorithm_der_with_scratch(
+ let _verification_result = x509_key.verify_signature_from_x509_algorithm_der_with_scratch(
select(&X509_ALGORITHMS, selector),
message,
signature,
@@ -155,7 +160,7 @@ pub fn run(data: &[u8]) {
);
}
8 => {
- let _ =
+ let _verification_result =
x509_key.verify_signature_from_x509_algorithm_der_with_scratch(message, MESSAGE_PSS, signature, &mut scratch);
}
9 => {
@@ -164,7 +169,8 @@ pub fn run(data: &[u8]) {
} else {
u16::from_be_bytes([selector, split])
};
- let _ = x509_key.verify_tls13_signature_scheme_with_scratch(scheme, message, signature, &mut scratch);
+ let _verification_result =
+ x509_key.verify_tls13_signature_scheme_with_scratch(scheme, message, signature, &mut scratch);
}
10 => {
let scheme = if selector & 1 == 0 {
@@ -172,18 +178,20 @@ pub fn run(data: &[u8]) {
} else {
u16::from_be_bytes([selector, split])
};
- let _ = x509_key.verify_tls_certificate_signature_scheme_with_scratch(scheme, message, signature, &mut scratch);
+ let _verification_result =
+ x509_key.verify_tls_certificate_signature_scheme_with_scratch(scheme, message, signature, &mut scratch);
}
11 => {
- let _ = RsaSignatureProfile::from_x509_signature_algorithm_der(message);
+ let _profile_result = RsaSignatureProfile::from_x509_signature_algorithm_der(message);
}
12 => {
- let _ = RsaSignatureProfile::from_cose_algorithm_id(*select(&COSE_ALGORITHMS, selector));
- let _ = RsaSignatureProfile::from_tls13_signature_scheme(u16::from_be_bytes([selector, split]));
- let _ = RsaSignatureProfile::from_tls_certificate_signature_scheme(u16::from_be_bytes([selector, split]));
+ let _cose_profile = RsaSignatureProfile::from_cose_algorithm_id(*select(&COSE_ALGORITHMS, selector));
+ let _tls13_profile = RsaSignatureProfile::from_tls13_signature_scheme(u16::from_be_bytes([selector, split]));
+ let _certificate_profile =
+ RsaSignatureProfile::from_tls_certificate_signature_scheme(u16::from_be_bytes([selector, split]));
}
_ => {
- let _ = RsaX509PublicKey::from_spki_der(message);
+ let _key_result = RsaX509PublicKey::from_spki_der(message);
}
}
}
diff --git a/fuzz/target_impls/auth_rsa_public_key.rs b/fuzz/target_impls/auth_rsa_public_key.rs
index ddd6668f..d0d48e8c 100644
--- a/fuzz/target_impls/auth_rsa_public_key.rs
+++ b/fuzz/target_impls/auth_rsa_public_key.rs
@@ -1,7 +1,7 @@
use rscrypto::{RsaPublicKey, RsaPublicKeyPolicy};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let mode = some_or_return!(input.byte());
let raw_der = input.rest();
@@ -13,7 +13,7 @@ pub fn run(data: &[u8]) {
raw_der
};
- let parsed = match mode % 4 {
+ let parsed = match mode.rem_euclid(4) {
0 => RsaPublicKey::from_pkcs1_der(der),
1 => RsaPublicKey::from_spki_der(der),
2 => RsaPublicKey::from_pkcs1_der_with_policy(der, &RsaPublicKeyPolicy::modern_verification()),
@@ -51,7 +51,7 @@ fn generated_public_key_der(mode: u8, control: &[u8]) -> Option> {
Some(der)
}
Some(b'L') => Some(tlv_with_leading_zero_long_len(&public_key_der_for_mode(mode, &pkcs1))),
- Some(b'U') if mode % 2 == 1 => Some(spki_public_key_der_with_unused_bits(&pkcs1)),
+ Some(b'U') if mode.rem_euclid(2) == 1 => Some(spki_public_key_der_with_unused_bits(&pkcs1)),
Some(b'N') if mode.is_multiple_of(2) => Some(pkcs1_public_key_der_with_noncanonical_exponent()),
_ => None,
}
@@ -122,7 +122,7 @@ fn algorithm_identifier(algorithm_oid: &[u8], params: Option<&[u8]>) -> Vec
fn integer_unsigned(value: &[u8]) -> Vec {
let first_nonzero = value.iter().position(|&byte| byte != 0);
let value = first_nonzero.map_or(&[0u8][..], |index| &value[index..]);
- let mut encoded = Vec::with_capacity(value.len() + usize::from(value[0] & 0x80 != 0));
+ let mut encoded = Vec::with_capacity(value.len().strict_add(usize::from(value[0] & 0x80 != 0)));
if value[0] & 0x80 != 0 {
encoded.push(0);
}
@@ -131,7 +131,8 @@ fn integer_unsigned(value: &[u8]) -> Vec {
}
fn tlv(tag: u8, value: &[u8]) -> Vec {
- let mut out = Vec::with_capacity(1 + der_len(value.len()).len() + value.len());
+ let encoded_len = 1usize.strict_add(der_len(value.len()).len()).strict_add(value.len());
+ let mut out = Vec::with_capacity(encoded_len);
out.push(tag);
out.extend_from_slice(&der_len(value.len()));
out.extend_from_slice(value);
@@ -146,7 +147,8 @@ fn tlv_with_leading_zero_long_len(der: &[u8]) -> Vec {
let mut out = Vec::with_capacity(der.len().strict_add(1));
out.push(tag);
- out.push(0x80 | (len_len.strict_add(1) as u8));
+ let encoded_len_len = u8::try_from(len_len.strict_add(1)).expect("DER length-of-length fits one byte");
+ out.push(0x80 | encoded_len_len);
out.push(0);
out.extend_from_slice(&der[2..]);
out
@@ -154,14 +156,18 @@ fn tlv_with_leading_zero_long_len(der: &[u8]) -> Vec {
fn der_len(len: usize) -> Vec {
if len < 128 {
- return vec![len as u8];
+ return vec![u8::try_from(len).expect("short-form DER length is below 128")];
}
let bytes = len.to_be_bytes();
- let first_nonzero = bytes.iter().position(|&byte| byte != 0).unwrap();
+ let first_nonzero = bytes
+ .iter()
+ .position(|&byte| byte != 0)
+ .expect("long-form DER length is nonzero");
let len_bytes = &bytes[first_nonzero..];
- let mut out = Vec::with_capacity(1 + len_bytes.len());
- out.push(0x80 | len_bytes.len() as u8);
+ let mut out = Vec::with_capacity(1usize.strict_add(len_bytes.len()));
+ let len_len = u8::try_from(len_bytes.len()).expect("usize DER length uses at most 16 bytes");
+ out.push(0x80 | len_len);
out.extend_from_slice(len_bytes);
out
}
@@ -171,21 +177,21 @@ fn der_null() -> Vec {
}
fn hex_to_vec(hex: &str) -> Vec {
- let mut out = Vec::with_capacity(hex.len() / 2);
+ let mut out = Vec::with_capacity(hex.len().div_euclid(2));
for chunk in hex.as_bytes().chunks_exact(2) {
- let hi = hex_value(chunk[0]);
- let lo = hex_value(chunk[1]);
- out.push((hi << 4) | lo);
+ let hi = hex_value(chunk[0]).expect("RSA modulus fixture must contain hexadecimal digits");
+ let lo = hex_value(chunk[1]).expect("RSA modulus fixture must contain hexadecimal digits");
+ out.push(hi.strict_shl(4) | lo);
}
out
}
-fn hex_value(byte: u8) -> u8 {
+fn hex_value(byte: u8) -> Option {
match byte {
- b'0'..=b'9' => byte - b'0',
- b'a'..=b'f' => byte - b'a' + 10,
- b'A'..=b'F' => byte - b'A' + 10,
- _ => 0,
+ b'0'..=b'9' => Some(byte.strict_sub(b'0')),
+ b'a'..=b'f' => Some(byte.strict_sub(b'a').strict_add(10)),
+ b'A'..=b'F' => Some(byte.strict_sub(b'A').strict_add(10)),
+ _ => None,
}
}
diff --git a/fuzz/target_impls/auth_rsa_verify.rs b/fuzz/target_impls/auth_rsa_verify.rs
index 0fb1bd70..d4dadeb2 100644
--- a/fuzz/target_impls/auth_rsa_verify.rs
+++ b/fuzz/target_impls/auth_rsa_verify.rs
@@ -14,12 +14,12 @@ fn signature_candidate(material: &[u8], len: usize) -> Vec {
}
for (index, byte) in out.iter_mut().enumerate() {
- *byte = material[index % material.len()];
+ *byte = material[index.rem_euclid(material.len())];
}
out
}
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let mode = some_or_return!(input.byte());
let split = some_or_return!(input.byte());
@@ -28,7 +28,7 @@ pub fn run(data: &[u8]) {
let key = RsaPublicKey::from_spki_der(RSA3072_SPKI).expect("fuzz RSA fixture must parse");
let mut scratch = key.public_scratch();
- match mode % 6 {
+ match mode.rem_euclid(6) {
0 => {
key
.verify_pss_with_scratch(RsaPssProfile::Sha256, MESSAGE_PSS, RSA3072_PSS_SHA256, &mut scratch)
@@ -46,17 +46,20 @@ pub fn run(data: &[u8]) {
}
2 => {
let signature = signature_candidate(signature_material, key.modulus().len());
- let _ = key.verify_pss_with_scratch(RsaPssProfile::Sha256, message, &signature, &mut scratch);
+ let _verification_result = key.verify_pss_with_scratch(RsaPssProfile::Sha256, message, &signature, &mut scratch);
}
3 => {
let signature = signature_candidate(signature_material, key.modulus().len());
- let _ = key.verify_pkcs1v15_with_scratch(RsaPkcs1v15Profile::Sha256, message, &signature, &mut scratch);
+ let _verification_result =
+ key.verify_pkcs1v15_with_scratch(RsaPkcs1v15Profile::Sha256, message, &signature, &mut scratch);
}
4 => {
- let _ = key.verify_pss_with_scratch(RsaPssProfile::Sha384, message, signature_material, &mut scratch);
+ let _verification_result =
+ key.verify_pss_with_scratch(RsaPssProfile::Sha384, message, signature_material, &mut scratch);
}
_ => {
- let _ = key.verify_pkcs1v15_with_scratch(RsaPkcs1v15Profile::Sha512, message, signature_material, &mut scratch);
+ let _verification_result =
+ key.verify_pkcs1v15_with_scratch(RsaPkcs1v15Profile::Sha512, message, signature_material, &mut scratch);
}
}
}
diff --git a/fuzz/target_impls/auth_scrypt.rs b/fuzz/target_impls/auth_scrypt.rs
index 487dbfc9..bfba115b 100644
--- a/fuzz/target_impls/auth_scrypt.rs
+++ b/fuzz/target_impls/auth_scrypt.rs
@@ -1,7 +1,7 @@
use rscrypto::{Scrypt, ScryptParams};
use rscrypto_fuzz::{FuzzInput, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let pw_salt_split: u8 = some_or_return!(input.byte());
let out_len_byte: u8 = some_or_return!(input.byte());
@@ -16,8 +16,7 @@ pub fn run(data: &[u8]) {
let r = 1u32.strict_add(u32::from(r_byte) % 4);
let out_len = 1u32.strict_add(u32::from(out_len_byte) % 64);
- let params = ScryptParams::new(log_n, r, 1)
- .expect("params must be valid for fuzzer ranges");
+ let params = ScryptParams::new(log_n, r, 1).expect("params must be valid for fuzzer ranges");
let (password, salt) = split_at_ratio(rest, pw_salt_split);
diff --git a/fuzz/target_impls/auth_x25519.rs b/fuzz/target_impls/auth_x25519.rs
index d25e0521..a0c4ae17 100644
--- a/fuzz/target_impls/auth_x25519.rs
+++ b/fuzz/target_impls/auth_x25519.rs
@@ -1,7 +1,7 @@
use rscrypto::{X25519PublicKey, X25519SecretKey, X25519SharedSecret};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let secret_bytes: [u8; 32] = some_or_return!(input.bytes());
let peer_bytes: [u8; 32] = some_or_return!(input.bytes());
@@ -25,7 +25,11 @@ pub fn run(data: &[u8]) {
match (&ours_shared, &helper_shared) {
(Ok(ours), Ok(helper)) => assert!(ours.ct_eq(helper).declassify(), "x25519 helper mismatch"),
(Err(_), Err(_)) => {}
- _ => panic!("x25519 helper result mismatch"),
+ _ => assert_eq!(
+ ours_shared.is_ok(),
+ helper_shared.is_ok(),
+ "x25519 helper result mismatch"
+ ),
}
if dalek_shared.iter().all(|&byte| byte == 0) {
diff --git a/fuzz/target_impls/checksum_crc.rs b/fuzz/target_impls/checksum_crc.rs
index 2e116839..6ddde7c2 100644
--- a/fuzz/target_impls/checksum_crc.rs
+++ b/fuzz/target_impls/checksum_crc.rs
@@ -1,7 +1,7 @@
use rscrypto::{Checksum, ChecksumCombine, Crc16Ccitt, Crc16Ibm, Crc24OpenPgp, Crc32, Crc32C, Crc64, Crc64Nvme};
use rscrypto_fuzz::{FuzzInput, assert_checksum_chunked, assert_checksum_combine, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let data = input.rest();
diff --git a/fuzz/target_impls/checksum_crc16.rs b/fuzz/target_impls/checksum_crc16.rs
index c6457f1b..1890bd6f 100644
--- a/fuzz/target_impls/checksum_crc16.rs
+++ b/fuzz/target_impls/checksum_crc16.rs
@@ -1,7 +1,7 @@
use rscrypto::{Checksum, Crc16Ccitt, Crc16Ibm};
use rscrypto_fuzz::{FuzzInput, assert_checksum_chunked, assert_checksum_combine, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let data = input.rest();
diff --git a/fuzz/target_impls/checksum_crc24.rs b/fuzz/target_impls/checksum_crc24.rs
index 74161872..bccbca3b 100644
--- a/fuzz/target_impls/checksum_crc24.rs
+++ b/fuzz/target_impls/checksum_crc24.rs
@@ -1,7 +1,7 @@
use rscrypto::{Checksum, Crc24OpenPgp};
use rscrypto_fuzz::{FuzzInput, assert_checksum_chunked, assert_checksum_combine, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let data = input.rest();
diff --git a/fuzz/target_impls/checksum_crc32.rs b/fuzz/target_impls/checksum_crc32.rs
index e91a65e3..edeaf35d 100644
--- a/fuzz/target_impls/checksum_crc32.rs
+++ b/fuzz/target_impls/checksum_crc32.rs
@@ -1,7 +1,7 @@
use rscrypto::{Checksum, Crc32, Crc32C};
use rscrypto_fuzz::{FuzzInput, assert_checksum_chunked, assert_checksum_combine, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let data = input.rest();
diff --git a/fuzz/target_impls/checksum_crc64.rs b/fuzz/target_impls/checksum_crc64.rs
index 2bfe5a3c..69d7c223 100644
--- a/fuzz/target_impls/checksum_crc64.rs
+++ b/fuzz/target_impls/checksum_crc64.rs
@@ -1,7 +1,7 @@
use rscrypto::{Checksum, ChecksumCombine, Crc64, Crc64Nvme};
use rscrypto_fuzz::{FuzzInput, assert_checksum_chunked, assert_checksum_combine, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let data = input.rest();
diff --git a/fuzz/target_impls/fast_rapidhash.rs b/fuzz/target_impls/fast_rapidhash.rs
index d3585184..c4e8bd3b 100644
--- a/fuzz/target_impls/fast_rapidhash.rs
+++ b/fuzz/target_impls/fast_rapidhash.rs
@@ -3,7 +3,7 @@ use core::hash::Hasher;
use rscrypto::{RapidHash64, RapidStreamHasher};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let seed_bytes: [u8; 8] = some_or_return!(input.bytes());
let partitions: [u8; 8] = some_or_return!(input.bytes());
diff --git a/fuzz/target_impls/fast_xxh3.rs b/fuzz/target_impls/fast_xxh3.rs
index 3a2c0cdf..12399a55 100644
--- a/fuzz/target_impls/fast_xxh3.rs
+++ b/fuzz/target_impls/fast_xxh3.rs
@@ -3,7 +3,7 @@ use core::hash::Hasher;
use rscrypto::{FastHash, Xxh3, Xxh3_128, Xxh3_128Hasher, Xxh3Hasher};
use rscrypto_fuzz::{FuzzInput, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let seed_bytes: [u8; 8] = some_or_return!(input.bytes());
let partitions: [u8; 8] = some_or_return!(input.bytes());
diff --git a/fuzz/target_impls/hash_ascon.rs b/fuzz/target_impls/hash_ascon.rs
index 3529b687..c7cbdf80 100644
--- a/fuzz/target_impls/hash_ascon.rs
+++ b/fuzz/target_impls/hash_ascon.rs
@@ -1,7 +1,7 @@
use rscrypto::{AsconHash256, AsconXof, Digest, Xof};
use rscrypto_fuzz::{FuzzInput, assert_digest_chunked, assert_digest_reset, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let out_len_byte: u8 = some_or_return!(input.byte());
diff --git a/fuzz/target_impls/hash_ascon_cxof.rs b/fuzz/target_impls/hash_ascon_cxof.rs
index 145e9a4e..a5bdbe88 100644
--- a/fuzz/target_impls/hash_ascon_cxof.rs
+++ b/fuzz/target_impls/hash_ascon_cxof.rs
@@ -18,7 +18,7 @@ fn extend_to_overlong(input: &[u8], control: u8) -> Vec {
out
}
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let customization_split: u8 = some_or_return!(input.byte());
let message_split: u8 = some_or_return!(input.byte());
diff --git a/fuzz/target_impls/hash_blake2b.rs b/fuzz/target_impls/hash_blake2b.rs
index 78266587..6e438e98 100644
--- a/fuzz/target_impls/hash_blake2b.rs
+++ b/fuzz/target_impls/hash_blake2b.rs
@@ -1,13 +1,18 @@
-use blake2::{Blake2b256 as OracleBlake2b256, Blake2b512 as OracleBlake2b512, Blake2bMac, Digest as _};
-use digest::typenum::{U32, U64};
-use hmac::{Mac as _, digest::KeyInit};
+use blake2::{
+ Blake2b as OracleBlake2b, Blake2b512 as OracleBlake2b512, Blake2bMac,
+ digest::{
+ Digest as _, Mac as _,
+ consts::{U32, U64},
+ },
+};
use rscrypto::{Blake2b256, Blake2b512, Blake2bKey, Digest};
use rscrypto_fuzz::{FuzzInput, assert_digest_chunked, assert_digest_reset, some_or_return, split_at_ratio};
type OracleBlake2bMac256 = Blake2bMac;
type OracleBlake2bMac512 = Blake2bMac;
+type OracleBlake2b256 = OracleBlake2b;
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let key_ratio: u8 = some_or_return!(input.byte());
@@ -30,7 +35,7 @@ pub fn run(data: &[u8]) {
let split_idx = split_at_ratio(data, key_ratio).0.len();
let key_len = split_idx.clamp(1, 64);
let (key, msg) = data.split_at(key_len);
- let typed_key = Blake2bKey::new(key).unwrap();
+ let typed_key = Blake2bKey::new(key).expect("the bounded BLAKE2b key length is valid");
let (msg_a, msg_b) = split_at_ratio(msg, split);
let mut ours_256_stream = Blake2b256::new_keyed(typed_key);
@@ -38,7 +43,8 @@ pub fn run(data: &[u8]) {
ours_256_stream.update(msg_b);
let ours_256_keyed = ours_256_stream.finalize();
- let mut oracle_256_mac = OracleBlake2bMac256::new_from_slice(key).unwrap();
+ let mut oracle_256_mac =
+ OracleBlake2bMac256::new_from_slice(key).expect("the bounded BLAKE2b oracle key length is valid");
oracle_256_mac.update(msg);
let oracle_256_keyed = oracle_256_mac.finalize().into_bytes();
assert_eq!(&ours_256_keyed[..], &oracle_256_keyed[..], "blake2b256 keyed mismatch");
@@ -48,7 +54,8 @@ pub fn run(data: &[u8]) {
ours_512_stream.update(msg_b);
let ours_512_keyed = ours_512_stream.finalize();
- let mut oracle_512_mac = OracleBlake2bMac512::new_from_slice(key).unwrap();
+ let mut oracle_512_mac =
+ OracleBlake2bMac512::new_from_slice(key).expect("the bounded BLAKE2b oracle key length is valid");
oracle_512_mac.update(msg);
let oracle_512_keyed = oracle_512_mac.finalize().into_bytes();
assert_eq!(&ours_512_keyed[..], &oracle_512_keyed[..], "blake2b512 keyed mismatch");
diff --git a/fuzz/target_impls/hash_blake2s.rs b/fuzz/target_impls/hash_blake2s.rs
index 8446d3c6..22215ae8 100644
--- a/fuzz/target_impls/hash_blake2s.rs
+++ b/fuzz/target_impls/hash_blake2s.rs
@@ -1,13 +1,18 @@
-use blake2::{Blake2s128 as OracleBlake2s128, Blake2s256 as OracleBlake2s256, Blake2sMac, Digest as _};
-use digest::typenum::{U16, U32};
-use hmac::{Mac as _, digest::KeyInit};
+use blake2::{
+ Blake2s as OracleBlake2s, Blake2s256 as OracleBlake2s256, Blake2sMac,
+ digest::{
+ Digest as _, Mac as _,
+ consts::{U16, U32},
+ },
+};
use rscrypto::{Blake2s128, Blake2s256, Blake2sKey, Digest};
use rscrypto_fuzz::{FuzzInput, assert_digest_chunked, assert_digest_reset, some_or_return, split_at_ratio};
type OracleBlake2sMac128 = Blake2sMac;
type OracleBlake2sMac256 = Blake2sMac;
+type OracleBlake2s128 = OracleBlake2s;
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let key_ratio: u8 = some_or_return!(input.byte());
@@ -30,7 +35,7 @@ pub fn run(data: &[u8]) {
let split_idx = split_at_ratio(data, key_ratio).0.len();
let key_len = split_idx.clamp(1, 32);
let (key, msg) = data.split_at(key_len);
- let typed_key = Blake2sKey::new(key).unwrap();
+ let typed_key = Blake2sKey::new(key).expect("the bounded BLAKE2s key length is valid");
let (msg_a, msg_b) = split_at_ratio(msg, split);
let mut ours_128_stream = Blake2s128::new_keyed(typed_key);
@@ -38,7 +43,8 @@ pub fn run(data: &[u8]) {
ours_128_stream.update(msg_b);
let ours_128_keyed = ours_128_stream.finalize();
- let mut oracle_128_mac = OracleBlake2sMac128::new_from_slice(key).unwrap();
+ let mut oracle_128_mac =
+ OracleBlake2sMac128::new_from_slice(key).expect("the bounded BLAKE2s oracle key length is valid");
oracle_128_mac.update(msg);
let oracle_128_keyed = oracle_128_mac.finalize().into_bytes();
assert_eq!(&ours_128_keyed[..], &oracle_128_keyed[..], "blake2s128 keyed mismatch");
@@ -48,7 +54,8 @@ pub fn run(data: &[u8]) {
ours_256_stream.update(msg_b);
let ours_256_keyed = ours_256_stream.finalize();
- let mut oracle_256_mac = OracleBlake2sMac256::new_from_slice(key).unwrap();
+ let mut oracle_256_mac =
+ OracleBlake2sMac256::new_from_slice(key).expect("the bounded BLAKE2s oracle key length is valid");
oracle_256_mac.update(msg);
let oracle_256_keyed = oracle_256_mac.finalize().into_bytes();
assert_eq!(&ours_256_keyed[..], &oracle_256_keyed[..], "blake2s256 keyed mismatch");
diff --git a/fuzz/target_impls/hash_blake3.rs b/fuzz/target_impls/hash_blake3.rs
index 9878e469..888c9caa 100644
--- a/fuzz/target_impls/hash_blake3.rs
+++ b/fuzz/target_impls/hash_blake3.rs
@@ -1,7 +1,7 @@
use rscrypto::{Blake3, Digest};
use rscrypto_fuzz::{FuzzInput, assert_digest_chunked, assert_digest_reset, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let out_len_byte: u8 = some_or_return!(input.byte());
diff --git a/fuzz/target_impls/hash_blake3_derive.rs b/fuzz/target_impls/hash_blake3_derive.rs
index 36aaf65b..cb1d318c 100644
--- a/fuzz/target_impls/hash_blake3_derive.rs
+++ b/fuzz/target_impls/hash_blake3_derive.rs
@@ -6,7 +6,7 @@ use rscrypto_fuzz::{FuzzInput, assert_xof_prefix, some_or_return};
// separator, not the interesting input.
const CONTEXT: &str = "rscrypto fuzz 2026-04-12 derive_key test context";
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let data = input.rest();
diff --git a/fuzz/target_impls/hash_blake3_keyed.rs b/fuzz/target_impls/hash_blake3_keyed.rs
index 6da3b9ee..dacca9bc 100644
--- a/fuzz/target_impls/hash_blake3_keyed.rs
+++ b/fuzz/target_impls/hash_blake3_keyed.rs
@@ -1,7 +1,7 @@
use rscrypto::{Blake3, Blake3KeyedHash, Digest};
use rscrypto_fuzz::{FuzzInput, assert_xof_prefix, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let key: [u8; 32] = some_or_return!(input.bytes());
let split: u8 = some_or_return!(input.byte());
diff --git a/fuzz/target_impls/hash_cshake256.rs b/fuzz/target_impls/hash_cshake256.rs
index 5ebdc605..f03b421f 100644
--- a/fuzz/target_impls/hash_cshake256.rs
+++ b/fuzz/target_impls/hash_cshake256.rs
@@ -1,7 +1,7 @@
use rscrypto::{Cshake256, Xof};
use rscrypto_fuzz::{FuzzInput, some_or_return, split_at_ratio};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let name_split: u8 = some_or_return!(input.byte());
let custom_split: u8 = some_or_return!(input.byte());
diff --git a/fuzz/target_impls/hash_sha2.rs b/fuzz/target_impls/hash_sha2.rs
index 2dfcc9e2..b0e3be79 100644
--- a/fuzz/target_impls/hash_sha2.rs
+++ b/fuzz/target_impls/hash_sha2.rs
@@ -1,7 +1,7 @@
use rscrypto::{Sha224, Sha256, Sha384, Sha512, Sha512_256};
use rscrypto_fuzz::{FuzzInput, assert_digest_chunked, assert_digest_reset, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let data = input.rest();
diff --git a/fuzz/target_impls/hash_sha3.rs b/fuzz/target_impls/hash_sha3.rs
index 2082a539..78dd50a6 100644
--- a/fuzz/target_impls/hash_sha3.rs
+++ b/fuzz/target_impls/hash_sha3.rs
@@ -1,7 +1,7 @@
use rscrypto::{Digest, Sha3_224, Sha3_256, Sha3_384, Sha3_512, Shake128, Shake256, Xof};
use rscrypto_fuzz::{FuzzInput, assert_digest_chunked, assert_digest_reset, some_or_return};
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let split: u8 = some_or_return!(input.byte());
let out_len_byte: u8 = some_or_return!(input.byte());
diff --git a/fuzz/target_impls/hex_parse.rs b/fuzz/target_impls/hex_parse.rs
index 44a9e120..4eb36930 100644
--- a/fuzz/target_impls/hex_parse.rs
+++ b/fuzz/target_impls/hex_parse.rs
@@ -23,7 +23,7 @@ fn ascii_candidate(bytes: &[u8], len: usize) -> String {
.copied()
.cycle()
.take(len)
- .map(|byte| char::from(32 + (byte % 95)))
+ .map(|byte| char::from(byte.rem_euclid(95).strict_add(32)))
.collect()
}
@@ -33,10 +33,20 @@ where
{
let lower = value.to_string();
let upper = format!("{value:X}");
- let short = &lower[..lower.len().strict_sub(1)];
+ let short = lower
+ .get(..lower.len().strict_sub(1))
+ .expect("hex rendering has an ASCII boundary before its final byte");
- assert_eq!(lower.parse::().unwrap(), value, "public lower parse mismatch");
- assert_eq!(upper.parse::().unwrap(), value, "public upper parse mismatch");
+ assert_eq!(
+ lower.parse::().expect("lowercase rendering must parse"),
+ value,
+ "public lower parse mismatch"
+ );
+ assert_eq!(
+ upper.parse::().expect("uppercase rendering must parse"),
+ value,
+ "public upper parse mismatch"
+ );
assert_eq!(
short.parse::(),
Err(InvalidHexError::InvalidLength),
@@ -44,8 +54,19 @@ where
);
if let Ok(parsed) = candidate.parse::() {
- assert_eq!(parsed.to_string().parse::().unwrap(), parsed);
- assert_eq!(format!("{parsed:X}").parse::().unwrap(), parsed);
+ assert_eq!(
+ parsed
+ .to_string()
+ .parse::()
+ .expect("parsed value must round-trip through lowercase rendering"),
+ parsed
+ );
+ assert_eq!(
+ format!("{parsed:X}")
+ .parse::()
+ .expect("parsed value must round-trip through uppercase rendering"),
+ parsed
+ );
}
}
@@ -58,14 +79,22 @@ fn exercise_sensitive_parse(
) where
T: Debug + FromStr,
{
- let short = &lower[..lower.len().strict_sub(1)];
+ let short = lower
+ .get(..lower.len().strict_sub(1))
+ .expect("hex rendering has an ASCII boundary before its final byte");
assert!(
- equivalent(&lower.parse::().unwrap(), &value),
+ equivalent(
+ &lower.parse::().expect("lowercase secret rendering must parse"),
+ &value
+ ),
"sensitive lower parse mismatch"
);
assert!(
- equivalent(&upper.parse::().unwrap(), &value),
+ equivalent(
+ &upper.parse::().expect("uppercase secret rendering must parse"),
+ &value
+ ),
"sensitive upper parse mismatch"
);
assert!(
@@ -79,7 +108,7 @@ fn exercise_sensitive_parse(
}
}
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let nonce_bytes: [u8; 12] = some_or_return!(input.bytes());
let tag_bytes: [u8; 16] = some_or_return!(input.bytes());
diff --git a/fuzz/target_impls/traits_io.rs b/fuzz/target_impls/traits_io.rs
index 786e6f7c..02400efa 100644
--- a/fuzz/target_impls/traits_io.rs
+++ b/fuzz/target_impls/traits_io.rs
@@ -44,7 +44,7 @@ fn sequential_chunks(data: &[u8], lens: [usize; 3]) -> [&[u8]; 3] {
}
fn reader_step(reader: &mut R, seen: &mut Vec, control: u8) -> io::Result<()> {
- match control % 3 {
+ match control.rem_euclid(3) {
0 => {
let mut buf = vec![0u8; scalar_len(control)];
let n = reader.read(&mut buf)?;
@@ -68,7 +68,9 @@ fn reader_step(reader: &mut R, seen: &mut Vec, control: u8) -> io::
}
_ => {
let n = reader.read(&mut [])?;
- assert_eq!(n, 0, "zero-length read must report zero");
+ if n != 0 {
+ return Err(io::Error::other("zero-length read returned a nonzero length"));
+ }
}
}
@@ -82,7 +84,7 @@ fn writer_step(
accepted: &mut Vec,
control: u8,
) -> io::Result<()> {
- match control % 4 {
+ match control.rem_euclid(4) {
0 => {
let request = scalar_len(control).min(data.len().strict_sub(*cursor));
let end = cursor.strict_add(request);
@@ -103,7 +105,9 @@ fn writer_step(
}
2 => {
let n = writer.write(&[])?;
- assert_eq!(n, 0, "zero-length write must report zero");
+ if n != 0 {
+ return Err(io::Error::other("zero-length write returned a nonzero length"));
+ }
}
_ => writer.flush()?,
}
@@ -275,7 +279,7 @@ fn fuzz_digest_writer(data: &[u8], ops: &[u8], max_per_call: usize) {
assert_eq!(digest, Blake3::digest(data), "digest writer final mismatch");
}
-pub fn run(data: &[u8]) {
+pub(super) fn run(data: &[u8]) {
let mut input = FuzzInput::new(data);
let reader_limit = usize::from(some_or_return!(input.byte()) % 32).strict_add(1);
let writer_limit = usize::from(some_or_return!(input.byte()) % 32).strict_add(1);
diff --git a/fuzz/tests/corpus_replay.rs b/fuzz/tests/corpus_replay.rs
index ad1ff5dc..c9152356 100644
--- a/fuzz/tests/corpus_replay.rs
+++ b/fuzz/tests/corpus_replay.rs
@@ -3,7 +3,7 @@ use std::path::PathBuf;
use rscrypto_fuzz::replay_corpus_dir;
fn corpus_dir(target: &str) -> PathBuf {
- PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("corpus").join(target)
+ PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("corpus").join(target)
}
#[path = "../target_impls/aead_aegis256.rs"]
@@ -149,282 +149,346 @@ mod traits_io;
#[test]
fn replay_aead_aegis256_corpus() {
- let replayed = replay_corpus_dir("aead_aegis256", corpus_dir("aead_aegis256"), aead_aegis256::run);
- assert_ne!(replayed, 0, "aead_aegis256 corpus should not be empty");
+ let replayed = replay_corpus_dir("aead_aegis256", corpus_dir("aead_aegis256"), aead_aegis256::run);
+ assert_ne!(replayed, 0, "aead_aegis256 corpus should not be empty");
}
#[test]
fn replay_aead_aes256gcm_corpus() {
- let replayed = replay_corpus_dir("aead_aes256gcm", corpus_dir("aead_aes256gcm"), aead_aes256gcm::run);
- assert_ne!(replayed, 0, "aead_aes256gcm corpus should not be empty");
+ let replayed = replay_corpus_dir("aead_aes256gcm", corpus_dir("aead_aes256gcm"), aead_aes256gcm::run);
+ assert_ne!(replayed, 0, "aead_aes256gcm corpus should not be empty");
}
#[test]
fn replay_aead_aes256gcmsiv_corpus() {
- let replayed = replay_corpus_dir("aead_aes256gcmsiv", corpus_dir("aead_aes256gcmsiv"), aead_aes256gcmsiv::run);
- assert_ne!(replayed, 0, "aead_aes256gcmsiv corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "aead_aes256gcmsiv",
+ corpus_dir("aead_aes256gcmsiv"),
+ aead_aes256gcmsiv::run,
+ );
+ assert_ne!(replayed, 0, "aead_aes256gcmsiv corpus should not be empty");
}
#[test]
fn replay_aead_ascon128_corpus() {
- let replayed = replay_corpus_dir("aead_ascon128", corpus_dir("aead_ascon128"), aead_ascon128::run);
- assert_ne!(replayed, 0, "aead_ascon128 corpus should not be empty");
+ let replayed = replay_corpus_dir("aead_ascon128", corpus_dir("aead_ascon128"), aead_ascon128::run);
+ assert_ne!(replayed, 0, "aead_ascon128 corpus should not be empty");
}
#[test]
fn replay_aead_chacha20poly1305_corpus() {
- let replayed = replay_corpus_dir("aead_chacha20poly1305", corpus_dir("aead_chacha20poly1305"), aead_chacha20poly1305::run);
- assert_ne!(replayed, 0, "aead_chacha20poly1305 corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "aead_chacha20poly1305",
+ corpus_dir("aead_chacha20poly1305"),
+ aead_chacha20poly1305::run,
+ );
+ assert_ne!(replayed, 0, "aead_chacha20poly1305 corpus should not be empty");
}
#[test]
fn replay_aead_nonce_counter_corpus() {
- let replayed = replay_corpus_dir("aead_nonce_counter", corpus_dir("aead_nonce_counter"), aead_nonce_counter::run);
- assert_ne!(replayed, 0, "aead_nonce_counter corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "aead_nonce_counter",
+ corpus_dir("aead_nonce_counter"),
+ aead_nonce_counter::run,
+ );
+ assert_ne!(replayed, 0, "aead_nonce_counter corpus should not be empty");
}
#[test]
fn replay_aead_xchacha20poly1305_corpus() {
- let replayed = replay_corpus_dir("aead_xchacha20poly1305", corpus_dir("aead_xchacha20poly1305"), aead_xchacha20poly1305::run);
- assert_ne!(replayed, 0, "aead_xchacha20poly1305 corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "aead_xchacha20poly1305",
+ corpus_dir("aead_xchacha20poly1305"),
+ aead_xchacha20poly1305::run,
+ );
+ assert_ne!(replayed, 0, "aead_xchacha20poly1305 corpus should not be empty");
}
#[test]
fn replay_auth_argon2d_corpus() {
- let replayed = replay_corpus_dir("auth_argon2d", corpus_dir("auth_argon2d"), auth_argon2d::run);
- assert_ne!(replayed, 0, "auth_argon2d corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_argon2d", corpus_dir("auth_argon2d"), auth_argon2d::run);
+ assert_ne!(replayed, 0, "auth_argon2d corpus should not be empty");
}
#[test]
fn replay_auth_argon2i_corpus() {
- let replayed = replay_corpus_dir("auth_argon2i", corpus_dir("auth_argon2i"), auth_argon2i::run);
- assert_ne!(replayed, 0, "auth_argon2i corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_argon2i", corpus_dir("auth_argon2i"), auth_argon2i::run);
+ assert_ne!(replayed, 0, "auth_argon2i corpus should not be empty");
}
#[test]
fn replay_auth_argon2id_corpus() {
- let replayed = replay_corpus_dir("auth_argon2id", corpus_dir("auth_argon2id"), auth_argon2id::run);
- assert_ne!(replayed, 0, "auth_argon2id corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_argon2id", corpus_dir("auth_argon2id"), auth_argon2id::run);
+ assert_ne!(replayed, 0, "auth_argon2id corpus should not be empty");
}
#[test]
fn replay_auth_ed25519_corpus() {
- let replayed = replay_corpus_dir("auth_ed25519", corpus_dir("auth_ed25519"), auth_ed25519::run);
- assert_ne!(replayed, 0, "auth_ed25519 corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_ed25519", corpus_dir("auth_ed25519"), auth_ed25519::run);
+ assert_ne!(replayed, 0, "auth_ed25519 corpus should not be empty");
}
#[test]
fn replay_auth_ed25519_verify_corpus() {
- let replayed = replay_corpus_dir("auth_ed25519_verify", corpus_dir("auth_ed25519_verify"), auth_ed25519_verify::run);
- assert_ne!(replayed, 0, "auth_ed25519_verify corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_ed25519_verify",
+ corpus_dir("auth_ed25519_verify"),
+ auth_ed25519_verify::run,
+ );
+ assert_ne!(replayed, 0, "auth_ed25519_verify corpus should not be empty");
}
#[test]
fn replay_auth_ecdsa_verify_corpus() {
- let replayed = replay_corpus_dir("auth_ecdsa_verify", corpus_dir("auth_ecdsa_verify"), auth_ecdsa_verify::run);
- assert_ne!(replayed, 0, "auth_ecdsa_verify corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_ecdsa_verify",
+ corpus_dir("auth_ecdsa_verify"),
+ auth_ecdsa_verify::run,
+ );
+ assert_ne!(replayed, 0, "auth_ecdsa_verify corpus should not be empty");
}
#[test]
fn replay_auth_ecdsa_sign_corpus() {
- let replayed = replay_corpus_dir("auth_ecdsa_sign", corpus_dir("auth_ecdsa_sign"), auth_ecdsa_sign::run);
- assert_ne!(replayed, 0, "auth_ecdsa_sign corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_ecdsa_sign", corpus_dir("auth_ecdsa_sign"), auth_ecdsa_sign::run);
+ assert_ne!(replayed, 0, "auth_ecdsa_sign corpus should not be empty");
}
#[test]
fn replay_auth_hkdf_sha256_corpus() {
- let replayed = replay_corpus_dir("auth_hkdf_sha256", corpus_dir("auth_hkdf_sha256"), auth_hkdf_sha256::run);
- assert_ne!(replayed, 0, "auth_hkdf_sha256 corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_hkdf_sha256",
+ corpus_dir("auth_hkdf_sha256"),
+ auth_hkdf_sha256::run,
+ );
+ assert_ne!(replayed, 0, "auth_hkdf_sha256 corpus should not be empty");
}
#[test]
fn replay_auth_hkdf_sha384_corpus() {
- let replayed = replay_corpus_dir("auth_hkdf_sha384", corpus_dir("auth_hkdf_sha384"), auth_hkdf_sha384::run);
- assert_ne!(replayed, 0, "auth_hkdf_sha384 corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_hkdf_sha384",
+ corpus_dir("auth_hkdf_sha384"),
+ auth_hkdf_sha384::run,
+ );
+ assert_ne!(replayed, 0, "auth_hkdf_sha384 corpus should not be empty");
}
#[test]
fn replay_auth_hmac_sha256_corpus() {
- let replayed = replay_corpus_dir("auth_hmac_sha256", corpus_dir("auth_hmac_sha256"), auth_hmac_sha256::run);
- assert_ne!(replayed, 0, "auth_hmac_sha256 corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_hmac_sha256",
+ corpus_dir("auth_hmac_sha256"),
+ auth_hmac_sha256::run,
+ );
+ assert_ne!(replayed, 0, "auth_hmac_sha256 corpus should not be empty");
}
#[test]
fn replay_auth_hmac_sha384_corpus() {
- let replayed = replay_corpus_dir("auth_hmac_sha384", corpus_dir("auth_hmac_sha384"), auth_hmac_sha384::run);
- assert_ne!(replayed, 0, "auth_hmac_sha384 corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_hmac_sha384",
+ corpus_dir("auth_hmac_sha384"),
+ auth_hmac_sha384::run,
+ );
+ assert_ne!(replayed, 0, "auth_hmac_sha384 corpus should not be empty");
}
#[test]
fn replay_auth_hmac_sha512_corpus() {
- let replayed = replay_corpus_dir("auth_hmac_sha512", corpus_dir("auth_hmac_sha512"), auth_hmac_sha512::run);
- assert_ne!(replayed, 0, "auth_hmac_sha512 corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_hmac_sha512",
+ corpus_dir("auth_hmac_sha512"),
+ auth_hmac_sha512::run,
+ );
+ assert_ne!(replayed, 0, "auth_hmac_sha512 corpus should not be empty");
}
#[test]
fn replay_auth_kmac256_corpus() {
- let replayed = replay_corpus_dir("auth_kmac256", corpus_dir("auth_kmac256"), auth_kmac256::run);
- assert_ne!(replayed, 0, "auth_kmac256 corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_kmac256", corpus_dir("auth_kmac256"), auth_kmac256::run);
+ assert_ne!(replayed, 0, "auth_kmac256 corpus should not be empty");
}
#[test]
fn replay_auth_mlkem512_corpus() {
- let replayed = replay_corpus_dir("auth_mlkem512", corpus_dir("auth_mlkem512"), auth_mlkem512::run);
- assert_ne!(replayed, 0, "auth_mlkem512 corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_mlkem512", corpus_dir("auth_mlkem512"), auth_mlkem512::run);
+ assert_ne!(replayed, 0, "auth_mlkem512 corpus should not be empty");
}
#[test]
fn replay_auth_mlkem768_corpus() {
- let replayed = replay_corpus_dir("auth_mlkem768", corpus_dir("auth_mlkem768"), auth_mlkem768::run);
- assert_ne!(replayed, 0, "auth_mlkem768 corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_mlkem768", corpus_dir("auth_mlkem768"), auth_mlkem768::run);
+ assert_ne!(replayed, 0, "auth_mlkem768 corpus should not be empty");
}
#[test]
fn replay_auth_mlkem1024_corpus() {
- let replayed = replay_corpus_dir("auth_mlkem1024", corpus_dir("auth_mlkem1024"), auth_mlkem1024::run);
- assert_ne!(replayed, 0, "auth_mlkem1024 corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_mlkem1024", corpus_dir("auth_mlkem1024"), auth_mlkem1024::run);
+ assert_ne!(replayed, 0, "auth_mlkem1024 corpus should not be empty");
}
#[test]
fn replay_auth_pbkdf2_corpus() {
- let replayed = replay_corpus_dir("auth_pbkdf2", corpus_dir("auth_pbkdf2"), auth_pbkdf2::run);
- assert_ne!(replayed, 0, "auth_pbkdf2 corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_pbkdf2", corpus_dir("auth_pbkdf2"), auth_pbkdf2::run);
+ assert_ne!(replayed, 0, "auth_pbkdf2 corpus should not be empty");
}
#[test]
fn replay_auth_phc_corpus() {
- let replayed = replay_corpus_dir("auth_phc", corpus_dir("auth_phc"), auth_phc::run);
- assert_ne!(replayed, 0, "auth_phc corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_phc", corpus_dir("auth_phc"), auth_phc::run);
+ assert_ne!(replayed, 0, "auth_phc corpus should not be empty");
}
#[test]
fn replay_auth_rsa_public_key_corpus() {
- let replayed = replay_corpus_dir("auth_rsa_public_key", corpus_dir("auth_rsa_public_key"), auth_rsa_public_key::run);
- assert_ne!(replayed, 0, "auth_rsa_public_key corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_rsa_public_key",
+ corpus_dir("auth_rsa_public_key"),
+ auth_rsa_public_key::run,
+ );
+ assert_ne!(replayed, 0, "auth_rsa_public_key corpus should not be empty");
}
#[test]
fn replay_auth_rsa_import_corpus() {
- let replayed = replay_corpus_dir("auth_rsa_import", corpus_dir("auth_rsa_import"), auth_rsa_import::run);
- assert_ne!(replayed, 0, "auth_rsa_import corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_rsa_import", corpus_dir("auth_rsa_import"), auth_rsa_import::run);
+ assert_ne!(replayed, 0, "auth_rsa_import corpus should not be empty");
}
#[test]
fn replay_auth_rsa_protocol_corpus() {
- let replayed = replay_corpus_dir("auth_rsa_protocol", corpus_dir("auth_rsa_protocol"), auth_rsa_protocol::run);
- assert_ne!(replayed, 0, "auth_rsa_protocol corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_rsa_protocol",
+ corpus_dir("auth_rsa_protocol"),
+ auth_rsa_protocol::run,
+ );
+ assert_ne!(replayed, 0, "auth_rsa_protocol corpus should not be empty");
}
#[test]
fn replay_auth_rsa_private_ops_corpus() {
- let replayed = replay_corpus_dir("auth_rsa_private_ops", corpus_dir("auth_rsa_private_ops"), auth_rsa_private_ops::run);
- assert_ne!(replayed, 0, "auth_rsa_private_ops corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "auth_rsa_private_ops",
+ corpus_dir("auth_rsa_private_ops"),
+ auth_rsa_private_ops::run,
+ );
+ assert_ne!(replayed, 0, "auth_rsa_private_ops corpus should not be empty");
}
#[test]
fn replay_auth_rsa_verify_corpus() {
- let replayed = replay_corpus_dir("auth_rsa_verify", corpus_dir("auth_rsa_verify"), auth_rsa_verify::run);
- assert_ne!(replayed, 0, "auth_rsa_verify corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_rsa_verify", corpus_dir("auth_rsa_verify"), auth_rsa_verify::run);
+ assert_ne!(replayed, 0, "auth_rsa_verify corpus should not be empty");
}
#[test]
fn replay_auth_scrypt_corpus() {
- let replayed = replay_corpus_dir("auth_scrypt", corpus_dir("auth_scrypt"), auth_scrypt::run);
- assert_ne!(replayed, 0, "auth_scrypt corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_scrypt", corpus_dir("auth_scrypt"), auth_scrypt::run);
+ assert_ne!(replayed, 0, "auth_scrypt corpus should not be empty");
}
#[test]
fn replay_auth_x25519_corpus() {
- let replayed = replay_corpus_dir("auth_x25519", corpus_dir("auth_x25519"), auth_x25519::run);
- assert_ne!(replayed, 0, "auth_x25519 corpus should not be empty");
+ let replayed = replay_corpus_dir("auth_x25519", corpus_dir("auth_x25519"), auth_x25519::run);
+ assert_ne!(replayed, 0, "auth_x25519 corpus should not be empty");
}
#[test]
fn replay_checksum_crc_corpus() {
- let replayed = replay_corpus_dir("checksum_crc", corpus_dir("checksum_crc"), checksum_crc::run);
- assert_ne!(replayed, 0, "checksum_crc corpus should not be empty");
+ let replayed = replay_corpus_dir("checksum_crc", corpus_dir("checksum_crc"), checksum_crc::run);
+ assert_ne!(replayed, 0, "checksum_crc corpus should not be empty");
}
#[test]
fn replay_fast_rapidhash_corpus() {
- let replayed = replay_corpus_dir("fast_rapidhash", corpus_dir("fast_rapidhash"), fast_rapidhash::run);
- assert_ne!(replayed, 0, "fast_rapidhash corpus should not be empty");
+ let replayed = replay_corpus_dir("fast_rapidhash", corpus_dir("fast_rapidhash"), fast_rapidhash::run);
+ assert_ne!(replayed, 0, "fast_rapidhash corpus should not be empty");
}
#[test]
fn replay_fast_xxh3_corpus() {
- let replayed = replay_corpus_dir("fast_xxh3", corpus_dir("fast_xxh3"), fast_xxh3::run);
- assert_ne!(replayed, 0, "fast_xxh3 corpus should not be empty");
+ let replayed = replay_corpus_dir("fast_xxh3", corpus_dir("fast_xxh3"), fast_xxh3::run);
+ assert_ne!(replayed, 0, "fast_xxh3 corpus should not be empty");
}
#[test]
fn replay_hash_ascon_corpus() {
- let replayed = replay_corpus_dir("hash_ascon", corpus_dir("hash_ascon"), hash_ascon::run);
- assert_ne!(replayed, 0, "hash_ascon corpus should not be empty");
+ let replayed = replay_corpus_dir("hash_ascon", corpus_dir("hash_ascon"), hash_ascon::run);
+ assert_ne!(replayed, 0, "hash_ascon corpus should not be empty");
}
#[test]
fn replay_hash_ascon_cxof_corpus() {
- let replayed = replay_corpus_dir("hash_ascon_cxof", corpus_dir("hash_ascon_cxof"), hash_ascon_cxof::run);
- assert_ne!(replayed, 0, "hash_ascon_cxof corpus should not be empty");
+ let replayed = replay_corpus_dir("hash_ascon_cxof", corpus_dir("hash_ascon_cxof"), hash_ascon_cxof::run);
+ assert_ne!(replayed, 0, "hash_ascon_cxof corpus should not be empty");
}
#[test]
fn replay_hash_blake2b_corpus() {
- let replayed = replay_corpus_dir("hash_blake2b", corpus_dir("hash_blake2b"), hash_blake2b::run);
- assert_ne!(replayed, 0, "hash_blake2b corpus should not be empty");
+ let replayed = replay_corpus_dir("hash_blake2b", corpus_dir("hash_blake2b"), hash_blake2b::run);
+ assert_ne!(replayed, 0, "hash_blake2b corpus should not be empty");
}
#[test]
fn replay_hash_blake2s_corpus() {
- let replayed = replay_corpus_dir("hash_blake2s", corpus_dir("hash_blake2s"), hash_blake2s::run);
- assert_ne!(replayed, 0, "hash_blake2s corpus should not be empty");
+ let replayed = replay_corpus_dir("hash_blake2s", corpus_dir("hash_blake2s"), hash_blake2s::run);
+ assert_ne!(replayed, 0, "hash_blake2s corpus should not be empty");
}
#[test]
fn replay_hash_blake3_corpus() {
- let replayed = replay_corpus_dir("hash_blake3", corpus_dir("hash_blake3"), hash_blake3::run);
- assert_ne!(replayed, 0, "hash_blake3 corpus should not be empty");
+ let replayed = replay_corpus_dir("hash_blake3", corpus_dir("hash_blake3"), hash_blake3::run);
+ assert_ne!(replayed, 0, "hash_blake3 corpus should not be empty");
}
#[test]
fn replay_hash_blake3_derive_corpus() {
- let replayed = replay_corpus_dir("hash_blake3_derive", corpus_dir("hash_blake3_derive"), hash_blake3_derive::run);
- assert_ne!(replayed, 0, "hash_blake3_derive corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "hash_blake3_derive",
+ corpus_dir("hash_blake3_derive"),
+ hash_blake3_derive::run,
+ );
+ assert_ne!(replayed, 0, "hash_blake3_derive corpus should not be empty");
}
#[test]
fn replay_hash_blake3_keyed_corpus() {
- let replayed = replay_corpus_dir("hash_blake3_keyed", corpus_dir("hash_blake3_keyed"), hash_blake3_keyed::run);
- assert_ne!(replayed, 0, "hash_blake3_keyed corpus should not be empty");
+ let replayed = replay_corpus_dir(
+ "hash_blake3_keyed",
+ corpus_dir("hash_blake3_keyed"),
+ hash_blake3_keyed::run,
+ );
+ assert_ne!(replayed, 0, "hash_blake3_keyed corpus should not be empty");
}
#[test]
fn replay_hash_cshake256_corpus() {
- let replayed = replay_corpus_dir("hash_cshake256", corpus_dir("hash_cshake256"), hash_cshake256::run);
- assert_ne!(replayed, 0, "hash_cshake256 corpus should not be empty");
+ let replayed = replay_corpus_dir("hash_cshake256", corpus_dir("hash_cshake256"), hash_cshake256::run);
+ assert_ne!(replayed, 0, "hash_cshake256 corpus should not be empty");
}
#[test]
fn replay_hash_sha2_corpus() {
- let replayed = replay_corpus_dir("hash_sha2", corpus_dir("hash_sha2"), hash_sha2::run);
- assert_ne!(replayed, 0, "hash_sha2 corpus should not be empty");
+ let replayed = replay_corpus_dir("hash_sha2", corpus_dir("hash_sha2"), hash_sha2::run);
+ assert_ne!(replayed, 0, "hash_sha2 corpus should not be empty");
}
#[test]
fn replay_hash_sha3_corpus() {
- let replayed = replay_corpus_dir("hash_sha3", corpus_dir("hash_sha3"), hash_sha3::run);
- assert_ne!(replayed, 0, "hash_sha3 corpus should not be empty");
+ let replayed = replay_corpus_dir("hash_sha3", corpus_dir("hash_sha3"), hash_sha3::run);
+ assert_ne!(replayed, 0, "hash_sha3 corpus should not be empty");
}
#[test]
fn replay_hex_parse_corpus() {
- let replayed = replay_corpus_dir("hex_parse", corpus_dir("hex_parse"), hex_parse::run);
- assert_ne!(replayed, 0, "hex_parse corpus should not be empty");
+ let replayed = replay_corpus_dir("hex_parse", corpus_dir("hex_parse"), hex_parse::run);
+ assert_ne!(replayed, 0, "hex_parse corpus should not be empty");
}
#[test]
fn replay_traits_io_corpus() {
- let replayed = replay_corpus_dir("traits_io", corpus_dir("traits_io"), traits_io::run);
- assert_ne!(replayed, 0, "traits_io corpus should not be empty");
+ let replayed = replay_corpus_dir("traits_io", corpus_dir("traits_io"), traits_io::run);
+ assert_ne!(replayed, 0, "traits_io corpus should not be empty");
}
diff --git a/justfile b/justfile
index 3da40268..a2dc6e0d 100644
--- a/justfile
+++ b/justfile
@@ -1,104 +1,49 @@
# Remote dev. Provider mechanics live in ~/dev-machines.
-ssh target:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto "{{ target }}"
+dev_machine := env_var_or_default("DEV_MACHINE_BIN", env_var("HOME") + "/dev-machines/dev-machine")
-ssh-check target:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto "{{ target }}" --check
+ssh target *args="":
+ @"{{ dev_machine }}" ssh rscrypto "{{ target }}" {{ args }}
-ssh-create target *args="":
- @"$HOME/dev-machines/dev-machine" create rscrypto "{{ target }}" {{ args }}
-
-ssh-kill target:
- @"$HOME/dev-machines/dev-machine" kill rscrypto "{{ target }}"
-
-ssh-status target="":
- @if [ -n "{{ target }}" ]; then "$HOME/dev-machines/dev-machine" status rscrypto "{{ target }}"; else "$HOME/dev-machines/dev-machine" status rscrypto; fi
-
-ssh-bootstrap target:
- @"$HOME/dev-machines/dev-machine" bootstrap rscrypto "{{ target }}"
-
-ssh-aws-linux-x64:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-x64
-
-ssh-aws-linux-arm64:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-arm64
-
-ssh-aws-windows-x64:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-windows-x64
-
-ssh-azure-linux-x64:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-linux-x64
-
-ssh-azure-linux-arm64:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-linux-arm64
-
-ssh-azure-windows-x64:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-windows-x64
+ssh-check target *args="":
+ @"{{ dev_machine }}" ssh rscrypto "{{ target }}" --check {{ args }}
-ssh-azure-windows-arm64:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-windows-arm64
+ssh-preflight target:
+ @"{{ dev_machine }}" preflight rscrypto "{{ target }}"
-ssh-aws-linux-x64-perf:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-x64-perf
-
-ssh-aws-linux-intel-gnr-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-intel-gnr-profile
-
-ssh-aws-linux-intel-spr-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-intel-spr-profile
-
-ssh-aws-linux-amd-zen5-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-amd-zen5-profile
-
-ssh-aws-linux-amd-zen4-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-amd-zen4-profile
-
-ssh-aws-linux-arm64-graviton3-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-arm64-graviton3-profile
-
-ssh-aws-linux-arm64-graviton4-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-linux-arm64-graviton4-profile
-
-ssh-azure-linux-intel-gnr-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-linux-intel-gnr-profile
-
-ssh-azure-linux-intel-emr-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-linux-intel-emr-profile
-
-ssh-azure-linux-amd-zen5-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-linux-amd-zen5-profile
+ssh-create target *args="":
+ @"{{ dev_machine }}" create rscrypto "{{ target }}" {{ args }}
-ssh-azure-linux-amd-zen4-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-linux-amd-zen4-profile
+ssh-start target:
+ @"{{ dev_machine }}" start rscrypto "{{ target }}"
-ssh-azure-linux-arm64-cobalt-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-linux-arm64-cobalt-profile
+ssh-deallocate target:
+ @"{{ dev_machine }}" deallocate rscrypto "{{ target }}"
-ssh-azure-linux-arm64-ampere-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-linux-arm64-ampere-profile
+ssh-kill target:
+ @"{{ dev_machine }}" kill rscrypto "{{ target }}"
-ssh-azure-windows-amd-zen5-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-windows-amd-zen5-profile
+ssh-status target="":
+ @if [ -n "{{ target }}" ]; then "{{ dev_machine }}" status rscrypto "{{ target }}"; else "{{ dev_machine }}" status rscrypto; fi
-ssh-azure-windows-intel-gnr-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-windows-intel-gnr-profile
+ssh-bootstrap target profile="":
+ @if [ -n "{{ profile }}" ]; then "{{ dev_machine }}" bootstrap rscrypto "{{ target }}" "{{ profile }}"; else "{{ dev_machine }}" bootstrap rscrypto "{{ target }}"; fi
-ssh-azure-windows-arm64-profile:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-windows-arm64-profile
+ssh-just target *args="":
+ @"{{ dev_machine }}" just rscrypto "{{ target }}" {{ args }}
-ssh-aws-test-rdma:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto aws-test-rdma
+ssh-collect-bench target run_id destination:
+ @"{{ dev_machine }}" collect-bench rscrypto "{{ target }}" "{{ run_id }}" "{{ destination }}"
-ssh-azure-test-rdma:
- @"$HOME/dev-machines/dev-machine" ssh rscrypto azure-test-rdma
+ssh-list:
+ @"{{ dev_machine }}" list
# Builds
build:
- cargo build --workspace --all-targets --all-features
+ cargo build --locked --workspace --all-targets --all-features
build-release:
- cargo build --workspace --all-targets --all-features --release
+ cargo build --locked --workspace --all-targets --all-features --release
# Checks
check *args="":
@@ -120,9 +65,9 @@ test-feature-matrix:
@scripts/test/test-feature-matrix.sh
test-native-api:
- cargo test --no-default-features --features 'alloc,aead,ed25519,x25519,ecdsa,ml-kem' --test api_consistency
- cargo test --features 'aead,signatures,key-exchange,getrandom' --test api_consistency
- cargo test --features 'signatures,key-exchange,getrandom' --test getrandom_smoke
+ cargo test --locked --no-default-features --features 'alloc,aead,ed25519,x25519,ecdsa,ml-kem' --test api_consistency
+ cargo test --locked --features 'aead,signatures,key-exchange,getrandom' --test api_consistency
+ cargo test --locked --features 'signatures,key-exchange,getrandom' --test getrandom_smoke
# Tests
test *crates="":
diff --git a/rust-toolchain.toml b/rust-toolchain.toml
index f3cb4461..e81aa497 100644
--- a/rust-toolchain.toml
+++ b/rust-toolchain.toml
@@ -1,26 +1,4 @@
[toolchain]
-channel = "nightly-2026-07-17"
-profile = "default"
-components = ["clippy", "rustfmt", "miri", "rust-src"]
-
-targets = [
- "aarch64-apple-darwin",
- "aarch64-unknown-linux-gnu",
- "x86_64-apple-darwin",
- "x86_64-pc-windows-msvc",
- "x86_64-unknown-linux-gnu",
- "aarch64-unknown-linux-musl",
- "wasm32-unknown-unknown",
- "wasm32-wasip1",
- "x86_64-unknown-linux-musl",
- "aarch64-pc-windows-msvc",
- "aarch64-unknown-none",
- "loongarch64-unknown-linux-gnu",
- "powerpc64le-unknown-linux-gnu",
- "riscv32imac-unknown-none-elf",
- "riscv64gc-unknown-linux-gnu",
- "riscv64gc-unknown-none-elf",
- "s390x-unknown-linux-gnu",
- "thumbv6m-none-eabi",
- "x86_64-unknown-none",
-]
+channel = "1.97.1"
+profile = "minimal"
+components = ["clippy", "rustfmt", "rust-src", "rust-analyzer"]
diff --git a/rustfmt.toml b/rustfmt.toml
index 50910770..7fd03e83 100644
--- a/rustfmt.toml
+++ b/rustfmt.toml
@@ -1,22 +1,7 @@
edition = "2024"
style_edition = "2024"
-tab_spaces = 2
max_width = 120
hard_tabs = false
-
-reorder_imports = true
-imports_granularity = "Crate"
-group_imports = "StdExternalCrate"
-
-format_code_in_doc_comments = true
-format_strings = true
-normalize_comments = true
-wrap_comments = true
-comment_width = 100
-
-use_field_init_shorthand = true
-use_try_shorthand = true
-
-error_on_line_overflow = false
-error_on_unformatted = false
+tab_spaces = 2
+newline_style = "Unix"
diff --git a/scripts/README.md b/scripts/README.md
index 3bdd2d16..bc6beb14 100644
--- a/scripts/README.md
+++ b/scripts/README.md
@@ -73,6 +73,7 @@ claim in [`docs/secret-lifecycle.md`](../docs/secret-lifecycle.md).
| `check/check-win.sh` | `scripts/check/check-all.sh` |
| `check/check-linux.sh` | `scripts/check/check-all.sh` |
| `check/check-ibm.sh` | `scripts/check/check-all.sh` |
+| `check/lint-independent-workspaces.sh` | `scripts/check/check.sh --all` |
| `check/zig-cc.sh` | `scripts/check/check-linux.sh`, `scripts/check/check-ibm.sh` |
## Bench Internals
@@ -127,10 +128,10 @@ candidate selected after the metadata refresh, verifies the installed version,
and refuses downgrades. OPAM uses exact packages from a repository pinned to a
full Git commit and verifies package source hashes from that immutable metadata.
CT formal reports bind the resulting BINSEC executable by SHA-256. Rustup
-receives only the exact toolchain declared in `rust-toolchain.toml`; runner
-images must provide rustup, which verifies component downloads against the
-exact distribution manifest, because network bootstrap installers are
-rejected.
+receives only the exact stable or nightly contract declared in
+`rust-toolchain.toml` and `.config/toolchains.toml`; runner images must provide
+rustup, which verifies component downloads against the exact distribution
+manifest, because network bootstrap installers are rejected.
## Results layout
diff --git a/scripts/check/check-all.sh b/scripts/check/check-all.sh
index 31ad9ff3..b13e6b4f 100755
--- a/scripts/check/check-all.sh
+++ b/scripts/check/check-all.sh
@@ -10,6 +10,8 @@ source "$SCRIPT_DIR/../lib/common.sh"
# shellcheck source=../lib/targets.sh
source "$SCRIPT_DIR/../lib/targets.sh"
+NIGHTLY_TOOLCHAIN=$("$SCRIPT_DIR/../lib/toolchain.sh" --nightly)
+
DEFAULT_CONSTRAINED_CRATES=(
"rscrypto"
)
@@ -77,6 +79,16 @@ crate_supports_alloc() {
[[ -f "$manifest" ]] && grep -q '^[[:space:]]*alloc[[:space:]]*=' "$manifest"
}
+cargo_for_target() {
+ local target=$1
+ shift
+ if [[ "$target" == riscv32* ]]; then
+ RUSTUP_TOOLCHAIN="$NIGHTLY_TOOLCHAIN" cargo "$@"
+ else
+ cargo "$@"
+ fi
+}
+
run_constrained_check() {
local crate=$1
local target=$2
@@ -97,7 +109,7 @@ run_constrained_check() {
args+=(--features "$feature_set")
fi
- if ! RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" cargo "${args[@]}" >>"$log_file" 2>&1; then
+ if ! RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" cargo_for_target "$target" "${args[@]}" >>"$log_file" 2>&1; then
return 1
fi
}
@@ -106,7 +118,11 @@ run_constrained_target() {
local target=$1
local log_dir=$2
- ensure_target "$target"
+ if [[ "$target" == riscv32* ]]; then
+ ensure_target "$target" "$NIGHTLY_TOOLCHAIN"
+ else
+ ensure_target "$target"
+ fi
local target_dir="target/cross-check/$target"
mkdir -p "$target_dir"
@@ -147,7 +163,7 @@ run_constrained_target() {
step "$target check (alloc)"
for crate in "${alloc_crates[@]}"; do
if ! RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" \
- cargo check --locked -p "$crate" --no-default-features --features alloc --target "$target" --lib \
+ cargo_for_target "$target" check --locked -p "$crate" --no-default-features --features alloc --target "$target" --lib \
>>"$log_file" 2>&1; then
fail
show_error "$log_file"
@@ -160,7 +176,7 @@ run_constrained_target() {
step "$target build (no features)"
for crate in "${CONSTRAINED_CRATES[@]}"; do
if ! RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" \
- cargo build --locked -p "$crate" --no-default-features --target "$target" --lib --release \
+ cargo_for_target "$target" build --locked -p "$crate" --no-default-features --target "$target" --lib --release \
>>"$log_file" 2>&1; then
fail
show_error "$log_file"
@@ -173,7 +189,7 @@ run_constrained_target() {
step "$target build (alloc)"
for crate in "${alloc_crates[@]}"; do
if ! RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" \
- cargo build --locked -p "$crate" --no-default-features --features alloc --target "$target" --lib --release \
+ cargo_for_target "$target" build --locked -p "$crate" --no-default-features --features alloc --target "$target" --lib --release \
>>"$log_file" 2>&1; then
fail
show_error "$log_file"
diff --git a/scripts/check/check-ibm.sh b/scripts/check/check-ibm.sh
index c9ce4008..140f91ff 100755
--- a/scripts/check/check-ibm.sh
+++ b/scripts/check/check-ibm.sh
@@ -21,6 +21,7 @@ fi
get_crate_flags "$@"
export ZIG_CC="$SCRIPT_DIR/zig-cc.sh"
+NIGHTLY_TOOLCHAIN=$("$SCRIPT_DIR/../lib/toolchain.sh" --nightly)
LOG_DIR=$(mktemp -d)
trap 'rm -rf "$LOG_DIR"' EXIT
@@ -33,7 +34,7 @@ if [[ ${#IBM_TARGETS[@]} -eq 0 ]]; then
fi
for target in "${IBM_TARGETS[@]}"; do
- ensure_target "$target"
+ ensure_target "$target" "$NIGHTLY_TOOLCHAIN"
done
for target in "${IBM_TARGETS[@]}"; do
@@ -53,8 +54,8 @@ for i in "${!IBM_TARGETS[@]}"; do
(
# shellcheck disable=SC2086
- if ! CC="$ZIG_CC" RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" \
- cargo clippy $CRATE_FLAGS --lib --all-features --locked --target "$target" -- -D warnings \
+ if ! CC="$ZIG_CC" RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" RUSTUP_TOOLCHAIN="$NIGHTLY_TOOLCHAIN" \
+ cargo clippy $CRATE_FLAGS --lib --all-features --locked --target "$target" \
>"$log_file" 2>&1; then
exit 1
fi
diff --git a/scripts/check/check-linux.sh b/scripts/check/check-linux.sh
index f35420d3..54ee22d4 100755
--- a/scripts/check/check-linux.sh
+++ b/scripts/check/check-linux.sh
@@ -21,6 +21,7 @@ fi
get_crate_flags "$@"
export ZIG_CC="$SCRIPT_DIR/zig-cc.sh"
+NIGHTLY_TOOLCHAIN=$("$SCRIPT_DIR/../lib/toolchain.sh" --nightly)
LOG_DIR=$(mktemp -d)
trap 'rm -rf "$LOG_DIR"' EXIT
@@ -33,7 +34,11 @@ if [[ ${#LINUX_TARGETS[@]} -eq 0 ]]; then
fi
for target in "${LINUX_TARGETS[@]}"; do
- ensure_target "$target"
+ if [[ "$target" == riscv64* ]]; then
+ ensure_target "$target" "$NIGHTLY_TOOLCHAIN"
+ else
+ ensure_target "$target"
+ fi
done
for target in "${LINUX_TARGETS[@]}"; do
@@ -52,9 +57,13 @@ for i in "${!LINUX_TARGETS[@]}"; do
targets[i]="$target"
(
+ toolchain_env=()
+ if [[ "$target" == riscv64* ]]; then
+ toolchain_env=(env "RUSTUP_TOOLCHAIN=$NIGHTLY_TOOLCHAIN")
+ fi
# shellcheck disable=SC2086
if ! CC="$ZIG_CC" RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" \
- cargo clippy $CRATE_FLAGS --lib --all-features --locked --target "$target" -- -D warnings \
+ "${toolchain_env[@]}" cargo clippy $CRATE_FLAGS --lib --all-features --locked --target "$target" \
>"$log_file" 2>&1; then
exit 1
fi
diff --git a/scripts/check/check-win.sh b/scripts/check/check-win.sh
index decebde9..33164498 100755
--- a/scripts/check/check-win.sh
+++ b/scripts/check/check-win.sh
@@ -85,7 +85,7 @@ for i in "${!WIN_TARGETS[@]}"; do
# shellcheck disable=SC2086
if ! XWIN_CACHE_DIR="$target_cache_dir" \
CARGO_TARGET_DIR="$target_dir" \
- cargo xwin clippy $CRATE_FLAGS --lib --all-features --locked --target "$target" -- -D warnings \
+ cargo xwin clippy $CRATE_FLAGS --lib --all-features --locked --target "$target" \
>"$log_file" 2>&1; then
exit 1
fi
diff --git a/scripts/check/check.sh b/scripts/check/check.sh
index c9995b5d..7dd75b74 100755
--- a/scripts/check/check.sh
+++ b/scripts/check/check.sh
@@ -145,13 +145,23 @@ fi
# Clippy
step "Linting"
# shellcheck disable=SC2086
-if ! cargo clippy $CRATE_FLAGS --all-targets --all-features --locked -- -D warnings >"$LOG_DIR/clippy.log" 2>&1; then
+if ! cargo clippy $CRATE_FLAGS --all-targets --all-features --locked >"$LOG_DIR/clippy.log" 2>&1; then
fail
show_error "$LOG_DIR/clippy.log"
exit 1
fi
ok
+if [[ "$FULL_WORKSPACE" == true ]]; then
+ step "Linting independent workspaces"
+ if ! "$SCRIPT_DIR/lint-independent-workspaces.sh" >"$LOG_DIR/independent-lints.log" 2>&1; then
+ fail
+ show_error "$LOG_DIR/independent-lints.log"
+ exit 1
+ fi
+ ok
+fi
+
# Audit/Deny (workspace only). CI owns this in the dedicated supply-chain lane.
if [[ "$FULL_WORKSPACE" == true && "${RSCRYPTO_SKIP_CHECK_SUPPLY_CHAIN:-}" != "1" ]]; then
step "Auditing deps"
diff --git a/scripts/check/lint-independent-workspaces.sh b/scripts/check/lint-independent-workspaces.sh
new file mode 100755
index 00000000..6dac5565
--- /dev/null
+++ b/scripts/check/lint-independent-workspaces.sh
@@ -0,0 +1,129 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd -P)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd -P)"
+POLICY_MANIFEST="$REPO_ROOT/Cargo.toml"
+TARGET_DIR="${RSCRYPTO_INDEPENDENT_LINT_TARGET_DIR:-$REPO_ROOT/target/independent-lints}"
+MESSAGE_FORMAT="${RSCRYPTO_LINT_MESSAGE_FORMAT:-human}"
+LINT_CAP="${RSCRYPTO_LINT_CAP:-}"
+
+case "$MESSAGE_FORMAT" in
+ human | json) ;;
+ *) echo "RSCRYPTO_LINT_MESSAGE_FORMAT must be human or json" >&2; exit 2 ;;
+esac
+case "$LINT_CAP" in
+ "" | warn) ;;
+ *) echo "RSCRYPTO_LINT_CAP must be empty or warn" >&2; exit 2 ;;
+esac
+
+if ! command -v jq >/dev/null 2>&1; then
+ echo "jq is required to resolve independent Cargo workspaces" >&2
+ exit 1
+fi
+
+manifests=()
+while IFS= read -r package_manifest; do
+ if ! metadata=$(cargo metadata \
+ --locked \
+ --no-deps \
+ --format-version 1 \
+ --manifest-path "$package_manifest"); then
+ echo "Failed to resolve Cargo workspace for $package_manifest" >&2
+ exit 1
+ fi
+ workspace_root=$(printf '%s\n' "$metadata" | jq -er '.workspace_root')
+ workspace_root=$(cd "$workspace_root" && pwd -P)
+ if [[ "$workspace_root" == "$REPO_ROOT" ]]; then
+ continue
+ fi
+
+ workspace_manifest="$workspace_root/Cargo.toml"
+ already_listed=false
+ for manifest in "${manifests[@]:+${manifests[@]}}"; do
+ if [[ "$manifest" == "$workspace_manifest" ]]; then
+ already_listed=true
+ break
+ fi
+ done
+ if [[ "$already_listed" == false ]]; then
+ manifests+=("$workspace_manifest")
+ fi
+done < <(find "$REPO_ROOT" -type d -name target -prune -o -type f -name Cargo.toml -print | sort)
+
+if [[ ${#manifests[@]} -eq 0 ]]; then
+ echo "No independent Cargo workspaces found through Cargo metadata"
+ exit 0
+fi
+
+lint_names() {
+ local namespace=$1
+ awk -v section="[lints.$namespace]" '
+ /^\[/ {
+ active = ($0 == section)
+ next
+ }
+ active {
+ line = $0
+ sub(/^[[:space:]]*/, "", line)
+ if (line ~ /^[a-z0-9_]+[[:space:]]*=/ && line ~ /"deny"/) {
+ sub(/[[:space:]]*=.*/, "", line)
+ print line
+ }
+ }
+ ' "$POLICY_MANIFEST"
+}
+
+lint_flags=()
+while IFS= read -r lint; do
+ [[ -n "$lint" ]] && lint_flags+=("-D$lint")
+done < <(lint_names rust)
+while IFS= read -r lint; do
+ [[ -n "$lint" ]] && lint_flags+=("-Dclippy::$lint")
+done < <(lint_names clippy)
+
+if [[ ${#lint_flags[@]} -eq 0 ]]; then
+ echo "No deny-level Rust or Clippy policy found in $POLICY_MANIFEST" >&2
+ exit 1
+fi
+
+check_cfg_flags=(
+ '--check-cfg=cfg(miri)'
+ '--check-cfg=cfg(fuzzing)'
+ '--check-cfg=cfg(rscrypto_internal_fuzzing)'
+ '--check-cfg=cfg(target_feature,values("movdiri","movdir64b","serialize"))'
+)
+
+failed=0
+for manifest in "${manifests[@]}"; do
+ relative_manifest=${manifest#"$REPO_ROOT/"}
+ if [[ "$MESSAGE_FORMAT" == json ]]; then
+ echo "Linting independent workspace: $relative_manifest" >&2
+ else
+ echo "Linting independent workspace: $relative_manifest"
+ fi
+
+ cargo_args=(
+ clippy
+ --locked
+ --manifest-path "$manifest"
+ --workspace
+ --all-targets
+ --all-features
+ --no-deps
+ )
+ if [[ "$MESSAGE_FORMAT" == json ]]; then
+ cargo_args+=(--message-format=json)
+ fi
+
+ compiler_flags=("${lint_flags[@]}" "${check_cfg_flags[@]}")
+ if [[ -n "$LINT_CAP" ]]; then
+ compiler_flags+=(--cap-lints "$LINT_CAP")
+ fi
+
+ if ! CARGO_TARGET_DIR="$TARGET_DIR" cargo "${cargo_args[@]}" -- "${compiler_flags[@]}"; then
+ failed=1
+ fi
+done
+
+exit "$failed"
diff --git a/scripts/check/rsa-asm-provenance.sh b/scripts/check/rsa-asm-provenance.sh
index 65bd3051..a7941399 100755
--- a/scripts/check/rsa-asm-provenance.sh
+++ b/scripts/check/rsa-asm-provenance.sh
@@ -156,9 +156,9 @@ verify_rsa_wrapper_fingerprints() {
[[ "$actual" == "$expected" ]] \
|| die "$wrapper changed without a provenance review"
done <<'EOF'
-src/auth/rsa_aarch64_asm.rs 65c059365bb76d948d2f09873870ba7e146af15e52e10560bf632d39bdd33834
-src/auth/rsa_aarch64_linux_asm.rs 2b311eb4f253ff458492c76f52f35c0a4d5ae8705774e568ae853e1f6ea1a049
-src/auth/rsa_x86_64_asm.rs 98bc241435de70963c23f0e98431024585f95eed268b773a42d3bd2983b8038e
+src/auth/rsa_aarch64_asm.rs dbcd55bc88daa1a9ab1812e964c84bfd4586ae9f6fa9b07dcd28985df6576507
+src/auth/rsa_aarch64_linux_asm.rs 69531bafa963d6d817c6a74f4a0fcaf1d98e713611874f4597768b1a6e0a0e7a
+src/auth/rsa_x86_64_asm.rs 5552b8ffa76f1ba43f6aaea1b91a28fb04a02fa167408d925bf5ccec6daefe18
EOF
}
diff --git a/scripts/ci/changed-test-planning-test.sh b/scripts/ci/changed-test-planning-test.sh
index 470aa82f..855e7902 100755
--- a/scripts/ci/changed-test-planning-test.sh
+++ b/scripts/ci/changed-test-planning-test.sh
@@ -226,7 +226,7 @@ run_test_consumer() {
assert_eq "$expected" "$actual" "test commands for $name"
}
-workspace_test='cargo test --workspace --all-features --lib --tests'
+workspace_test='cargo test --locked --workspace --all-features --lib --tests'
run_test_consumer planner-failure '' 9 "$workspace_test"
run_test_consumer empty-output '' 0 "$workspace_test"
run_test_consumer malformed-json '{' 0 "$workspace_test"
@@ -238,12 +238,13 @@ run_test_consumer malformed-crates "$empty_crate_selection" 0 "$workspace_test"
run_test_consumer malformed-surface "$malformed_surface" 0 "$workspace_test"
run_test_consumer valid-empty "$EMPTY_PLAN" 0 ''
run_test_consumer valid-workspace "$WORKSPACE_PLAN" 0 "$workspace_test"
-run_test_consumer valid-crates "$CRATES_PLAN" 0 $'cargo test -p crate-a --all-features --lib --tests\ncargo test -p crate-b --all-features --lib --tests'
+run_test_consumer valid-crates "$CRATES_PLAN" 0 $'cargo test --locked -p crate-a --all-features --lib --tests\ncargo test --locked -p crate-b --all-features --lib --tests'
run_test_consumer valid-legacy-workspace "$LEGACY_WORKSPACE_PLAN" 0 "$workspace_test"
check_fixture="$TMP_ROOT/check-repository"
mkdir -p "$check_fixture/scripts/check" "$check_fixture/scripts/lib" "$check_fixture/scripts/ct" "$check_fixture/scripts/test"
cp "$REPO_ROOT/scripts/check/check.sh" "$check_fixture/scripts/check/check.sh"
+cp "$REPO_ROOT/scripts/check/lint-independent-workspaces.sh" "$check_fixture/scripts/check/lint-independent-workspaces.sh"
cp "$REPO_ROOT/scripts/lib/common.sh" "$REPO_ROOT/scripts/lib/rail-plan.sh" "$check_fixture/scripts/lib/"
for helper in "$check_fixture/scripts/check/asm-ledger.sh" \
"$check_fixture/scripts/check/check-feature-matrix.sh" \
@@ -289,9 +290,11 @@ run_check_consumer valid-workspace "$WORKSPACE_PLAN" 0 '--workspace'
run_check_consumer valid-crates "$CRATES_PLAN" 0 '-p crate-a -p crate-b'
check_all_fixture="$TMP_ROOT/check-all-repository"
-mkdir -p "$check_all_fixture/scripts/check" "$check_all_fixture/scripts/lib"
+mkdir -p "$check_all_fixture/.config" "$check_all_fixture/scripts/check" "$check_all_fixture/scripts/lib"
cp "$REPO_ROOT/scripts/check/check-all.sh" "$check_all_fixture/scripts/check/check-all.sh"
-cp "$REPO_ROOT/scripts/lib/common.sh" "$REPO_ROOT/scripts/lib/rail-plan.sh" "$check_all_fixture/scripts/lib/"
+cp "$REPO_ROOT/scripts/lib/common.sh" "$REPO_ROOT/scripts/lib/rail-plan.sh" \
+ "$REPO_ROOT/scripts/lib/toolchain.sh" "$check_all_fixture/scripts/lib/"
+cp "$REPO_ROOT/.config/toolchains.toml" "$check_all_fixture/.config/toolchains.toml"
cat >"$check_all_fixture/scripts/lib/targets.sh" <<'EOF'
WIN_TARGETS=()
LINUX_TARGETS=()
diff --git a/scripts/ci/check-action-pins-test.sh b/scripts/ci/check-action-pins-test.sh
index 75ddc32c..6518b288 100755
--- a/scripts/ci/check-action-pins-test.sh
+++ b/scripts/ci/check-action-pins-test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
+unset BASH_ENV
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CHECKER="$SCRIPT_DIR/check-action-pins.sh"
diff --git a/scripts/ci/check-ci-ownership-test.sh b/scripts/ci/check-ci-ownership-test.sh
index 781883e6..5838cf1d 100755
--- a/scripts/ci/check-ci-ownership-test.sh
+++ b/scripts/ci/check-ci-ownership-test.sh
@@ -128,7 +128,7 @@ expect_failure "$invalid_tool_digest" "direct tool digest is malformed"
mutable_tool_url="$TMP_ROOT/mutable-tool-url"
make_fixture "$mutable_tool_url"
-sed -i.bak 's#/download/v46\.0\.1/#/download/Latest/#' \
+sed -i.bak 's#/download/v47\.0\.3/#/download/Latest/#' \
"$mutable_tool_url/.config/ci-tool-archives.tsv"
rm -f "$mutable_tool_url/.config/ci-tool-archives.tsv.bak"
expect_failure "$mutable_tool_url" "direct tool URL resolves a mutable release"
@@ -166,6 +166,13 @@ printf '\n - uses: dtolnay/rust-toolchain@e97e2d8cc328f1b50210efc529dca002889
>>"$unauthenticated_rustup/.github/actions/setup-toolchain/action.yaml"
expect_failure "$unauthenticated_rustup" "toolchain setup can run a network bootstrap installer"
+inactive_toolchain_contract="$TMP_ROOT/inactive-toolchain-contract"
+make_fixture "$inactive_toolchain_contract"
+sed -i.bak 's/ "\$GITHUB_ENV"$//' \
+ "$inactive_toolchain_contract/.github/actions/setup-toolchain/action.yaml"
+rm -f "$inactive_toolchain_contract/.github/actions/setup-toolchain/action.yaml.bak"
+expect_failure "$inactive_toolchain_contract" "toolchain contract is installed but not activated"
+
floating_rail_action="$TMP_ROOT/floating-rail-action"
make_fixture "$floating_rail_action"
yq eval '(.jobs."rail-plan".steps[] | select(.id == "rail") | .uses) = "loadingalias/cargo-rail-action@v6"' -i \
diff --git a/scripts/ci/check-ci-ownership.sh b/scripts/ci/check-ci-ownership.sh
index d83c9e17..5c102499 100755
--- a/scripts/ci/check-ci-ownership.sh
+++ b/scripts/ci/check-ci-ownership.sh
@@ -344,8 +344,15 @@ release_intent_condition=$(yq eval '.jobs."rail-plan".steps[] | select(.name ==
[[ "$release_intent_condition" == *"startsWith(github.head_ref, 'rail/release-')"* \
&& "$release_intent_condition" == *"github.event.pull_request.head.repo.full_name == github.repository"* ]] \
|| fail "only repository-owned Cargo Rail release PRs may consume change intent"
-grep -Fq 'scripts/ci/setup-toolchain.sh "$TOOLCHAIN" "$TOOLCHAIN_COMPONENTS"' "$TOOLCHAIN_ACTION" \
+grep -Fq 'scripts/ci/setup-toolchain.sh "$TOOLCHAIN" "$TOOLCHAIN_COMPONENTS" "$GITHUB_ENV"' "$TOOLCHAIN_ACTION" \
|| fail "toolchain setup must use the repository-owned rustup policy"
+grep -Fq "printf 'RUSTUP_TOOLCHAIN=%s\\n' \"\$toolchain\" >>\"\$github_env\"" "$SETUP_TOOLCHAIN" \
+ || fail "toolchain setup must activate the resolved contract for later steps"
+grep -Fq 'RUSTUP_TOOLCHAIN="$TOOLCHAIN" rustc --version --verbose' "$TOOLCHAIN_ACTION" \
+ || fail "toolchain setup must verify the activated contract without a rust-toolchain override"
+if grep -Fq 'rustup default ' "$SETUP_TOOLCHAIN"; then
+ fail "toolchain setup must not mutate a runner-global default"
+fi
if grep -Eq '[.]cargo/(bin|[.]crates)|[.]opam' "$SETUP_ACTION"; then
fail "CI tool executables and OPAM switches must not be restored from caches"
fi
@@ -686,7 +693,7 @@ group_musl=$(jq '[.groups.linux[] | select(contains("musl"))] | length' "$MANIFE
[[ "$group_musl" -eq 2 ]] || fail "the target manifest must retain both MUSL triples"
# shellcheck disable=SC2016 # `$target` is an intentional literal in the workflow contract regex.
-[[ $(count_matches 'cargo (check|clippy|build) --target "\$target"' "$CROSS_SCRIPT") -ge 3 ]] \
+[[ $(count_matches 'cargo (check|clippy|build) --locked --target "\$target"' "$CROSS_SCRIPT") -ge 3 ]] \
|| fail "MUSL evidence must pass the target triple explicitly to Cargo"
echo "CI ownership contract passed"
diff --git a/scripts/ci/check-locked-cargo-test.sh b/scripts/ci/check-locked-cargo-test.sh
new file mode 100755
index 00000000..8ee6480e
--- /dev/null
+++ b/scripts/ci/check-locked-cargo-test.sh
@@ -0,0 +1,37 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+CHECKER="$SCRIPT_DIR/check-locked-cargo.sh"
+TMP_ROOT=$(mktemp -d)
+trap 'rm -rf "$TMP_ROOT"' EXIT
+
+fail() {
+ echo "locked Cargo inventory test failure: $*" >&2
+ exit 1
+}
+
+mkdir -p "$TMP_ROOT/scripts/ci" "$TMP_ROOT/.zed"
+printf '%s\n' 'build:' ' cargo build --locked --workspace' >"$TMP_ROOT/justfile"
+"$CHECKER" --root "$TMP_ROOT" || fail "locked command was rejected"
+
+printf '%s\n' '#!/usr/bin/env bash' 'cargo test --workspace' >"$TMP_ROOT/scripts/ci/example.sh"
+if "$CHECKER" --root "$TMP_ROOT" >/dev/null 2>&1; then
+ fail "unlocked command was accepted"
+fi
+
+printf '%s\n' '#!/usr/bin/env bash' 'cargo test \' ' --locked \' ' --workspace' >"$TMP_ROOT/scripts/ci/example.sh"
+"$CHECKER" --root "$TMP_ROOT" || fail "multiline locked command was rejected"
+
+printf '%s\n' '#!/usr/bin/env bash' '# cargo check --workspace' >"$TMP_ROOT/scripts/ci/example.sh"
+"$CHECKER" --root "$TMP_ROOT" || fail "comment was treated as a command"
+
+printf '%s\n' '[{"label":"check","command":"cargo","args":["check"]}]' >"$TMP_ROOT/.zed/tasks.json"
+if "$CHECKER" --root "$TMP_ROOT" >/dev/null 2>&1; then
+ fail "unlocked Zed Cargo task was accepted"
+fi
+
+printf '%s\n' '[{"label":"check","command":"just","args":["check"]}]' >"$TMP_ROOT/.zed/tasks.json"
+"$CHECKER" --root "$TMP_ROOT" || fail "repository-front-door Zed task was rejected"
+
+echo "Locked Cargo inventory regression tests passed"
diff --git a/scripts/ci/check-locked-cargo.sh b/scripts/ci/check-locked-cargo.sh
new file mode 100755
index 00000000..ddd1db65
--- /dev/null
+++ b/scripts/ci/check-locked-cargo.sh
@@ -0,0 +1,83 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+ROOT=""
+if [[ ${1:-} == --root ]]; then
+ ROOT=${2:?missing path after --root}
+ shift 2
+fi
+if [[ $# -ne 0 ]]; then
+ echo "usage: check-locked-cargo.sh [--root PATH]" >&2
+ exit 2
+fi
+
+if [[ -z "$ROOT" ]]; then
+ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+ ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
+fi
+
+status=0
+check_statement() {
+ local file=$1
+ local line_number=$2
+ local statement=$3
+ local trimmed=${statement#"${statement%%[![:space:]]*}"}
+
+ case "$trimmed" in
+ echo\ * | printf\ * | step\ *) return ;;
+ esac
+
+ if [[ "$statement" =~ (^|[[:space:]])cargo[[:space:]]+(build|check|clippy|test|bench|rustc|run|doc|miri|nextest)($|[[:space:]]) ]] \
+ && [[ "$statement" != *"--locked"* ]]; then
+ echo "$file:$line_number: routine Cargo command must use --locked" >&2
+ echo " $statement" >&2
+ status=1
+ fi
+
+ if [[ "$statement" =~ (^|[[:space:]])cargo[[:space:]]+llvm-cov[[:space:]]+(nextest|test)($|[[:space:]]) ]] \
+ && [[ "$statement" != *"--locked"* ]]; then
+ echo "$file:$line_number: cargo-llvm-cov execution must use --locked" >&2
+ echo " $statement" >&2
+ status=1
+ fi
+}
+
+while IFS= read -r file; do
+ statement=""
+ statement_line=0
+ line_number=0
+ while IFS= read -r line || [[ -n "$line" ]]; do
+ line_number=$((line_number + 1))
+ if [[ -z "$statement" ]]; then
+ [[ "$line" =~ ^[[:space:]]*# ]] && continue
+ statement_line=$line_number
+ fi
+ statement+=" ${line%\\}"
+ if [[ "$line" == *\\ ]]; then
+ continue
+ fi
+ check_statement "${file#"$ROOT"/}" "$statement_line" "$statement"
+ statement=""
+ done <"$file"
+ if [[ -n "$statement" ]]; then
+ check_statement "${file#"$ROOT"/}" "$statement_line" "$statement"
+ fi
+done < <(
+ {
+ [[ -f "$ROOT/justfile" ]] && printf '%s\n' "$ROOT/justfile"
+ find "$ROOT/scripts" -type f -name '*.sh' \
+ ! -name '*-test.sh' ! -name '*-scheduler-test.sh' 2>/dev/null || true
+ find "$ROOT/.github" -type f \( -name '*.yaml' -o -name '*.yml' \) 2>/dev/null || true
+ } | LC_ALL=C sort
+)
+
+if [[ -f "$ROOT/.zed/tasks.json" ]]; then
+ while IFS= read -r task; do
+ command=$(jq -r '.command' <<<"$task")
+ args=$(jq -r '(.args // []) | join(" ")' <<<"$task")
+ check_statement ".zed/tasks.json" 1 "$command $args"
+ done < <(jq -c '.[] | select((.command // "") == "cargo" or ((.command // "") | startswith("cargo ")))' \
+ "$ROOT/.zed/tasks.json")
+fi
+
+exit "$status"
diff --git a/scripts/ci/check-worktree-test.sh b/scripts/ci/check-worktree-test.sh
index e05ff043..f0ff3788 100755
--- a/scripts/ci/check-worktree-test.sh
+++ b/scripts/ci/check-worktree-test.sh
@@ -17,6 +17,7 @@ fake_home="$TMP_ROOT/home"
command_log="$TMP_ROOT/commands.log"
preflight_marker="$TMP_ROOT/locked-metadata-preflight"
mkdir -p \
+ "$fixture/.config" \
"$fixture/scripts/check" \
"$fixture/scripts/ct" \
"$fixture/scripts/lib" \
@@ -31,12 +32,15 @@ cp \
"$REPO_ROOT/scripts/check/check-feature-matrix.sh" \
"$REPO_ROOT/scripts/check/check-ibm.sh" \
"$REPO_ROOT/scripts/check/check-linux.sh" \
+ "$REPO_ROOT/scripts/check/lint-independent-workspaces.sh" \
"$REPO_ROOT/scripts/check/check-win.sh" \
"$REPO_ROOT/scripts/check/check.sh" \
"$REPO_ROOT/scripts/check/zig-cc.sh" \
"$fixture/scripts/check/"
cp "$REPO_ROOT/scripts/lib/common.sh" "$REPO_ROOT/scripts/lib/rail-plan.sh" \
- "$REPO_ROOT/scripts/lib/feature-profiles.sh" "$fixture/scripts/lib/"
+ "$REPO_ROOT/scripts/lib/feature-profiles.sh" "$REPO_ROOT/scripts/lib/toolchain.sh" \
+ "$fixture/scripts/lib/"
+cp "$REPO_ROOT/.config/toolchains.toml" "$fixture/.config/toolchains.toml"
cp "$REPO_ROOT/scripts/test/test-feature-matrix.sh" "$fixture/scripts/test/test-feature-matrix.sh"
cat >"$fixture/scripts/lib/targets.sh" <<'EOF'
@@ -104,6 +108,7 @@ case "${1:-}" in
metadata)
require_locked "$@"
: >"$MOCK_PREFLIGHT_MARKER"
+ printf '{"workspace_root":"%s"}\n' "$MOCK_REPO_ROOT"
;;
rail)
if [[ "${2:-}" == "plan" ]]; then
diff --git a/scripts/ci/ci-check.sh b/scripts/ci/ci-check.sh
index 700023ba..0af74976 100755
--- a/scripts/ci/ci-check.sh
+++ b/scripts/ci/ci-check.sh
@@ -14,6 +14,9 @@ echo ""
echo "🔗 Checking workflow ownership and action pins..."
"$SCRIPT_DIR/check-ci-ownership.sh"
"$SCRIPT_DIR/check-ci-ownership-test.sh"
+"$SCRIPT_DIR/check-locked-cargo.sh"
+"$SCRIPT_DIR/check-locked-cargo-test.sh"
+"$SCRIPT_DIR/../lib/target-matrix.sh" --validate
"$SCRIPT_DIR/check-action-pins.sh"
export RSCRYPTO_SKIP_CHECK_SUPPLY_CHAIN=1
@@ -21,7 +24,7 @@ export RSCRYPTO_SKIP_CHECK_SUPPLY_CHAIN=1
echo ""
echo "🔨 Building all targets..."
-cargo build --workspace --all-targets --all-features
+cargo build --locked --workspace --all-targets --all-features
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
diff --git a/scripts/ci/cross-targets.sh b/scripts/ci/cross-targets.sh
index 044312e7..01784b29 100755
--- a/scripts/ci/cross-targets.sh
+++ b/scripts/ci/cross-targets.sh
@@ -36,11 +36,11 @@ for target in "${MUSL_TARGETS[@]}"; do
mkdir -p "$target_dir"
RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" \
- cargo check --target "$target" --no-default-features --lib
+ cargo check --locked --target "$target" --no-default-features --lib
RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" \
- cargo clippy --target "$target" --lib --all-features -- -D warnings
+ cargo clippy --locked --target "$target" --lib --all-features
RUSTC_WRAPPER="" CARGO_TARGET_DIR="$target_dir" \
- cargo build --target "$target" --no-default-features --features alloc --lib --release
+ cargo build --locked --target "$target" --no-default-features --features alloc --lib --release
done
for target in "${NOSTD_TARGETS[@]}" "${WASM_TARGETS[@]}"; do
diff --git a/scripts/ci/dependabot-smoke.sh b/scripts/ci/dependabot-smoke.sh
index 14ec2b8e..7863c393 100755
--- a/scripts/ci/dependabot-smoke.sh
+++ b/scripts/ci/dependabot-smoke.sh
@@ -39,7 +39,7 @@ run_root_smoke() {
cargo fetch --locked
cargo check --locked --workspace --all-targets --all-features
cargo test --locked --workspace --all-features --no-run
- cargo deny check advisories
+ cargo deny --locked check advisories
}
run_manifest_smoke() {
diff --git a/scripts/ci/install-tools.sh b/scripts/ci/install-tools.sh
index a5dcea6a..e354cd52 100755
--- a/scripts/ci/install-tools.sh
+++ b/scripts/ci/install-tools.sh
@@ -6,13 +6,13 @@ set -euo pipefail
MODE=${1:-standard}
-CARGO_NEXTEST_VERSION=0.9.140
+CARGO_NEXTEST_VERSION=0.9.143
CARGO_DENY_VERSION=0.20.2
CARGO_AUDIT_VERSION=0.22.2
-CARGO_RAIL_VERSION=0.20.0
+CARGO_RAIL_VERSION=0.21.0
CARGO_SEMVER_CHECKS_VERSION=0.50.0
-JUST_VERSION=1.57.0
-ZIZMOR_VERSION=1.26.1
+JUST_VERSION=1.58.0
+ZIZMOR_VERSION=1.29.0
CARGO_CRITERION_VERSION=1.1.0
CRITCMP_VERSION=0.1.8
CARGO_FUZZ_VERSION=0.13.2
diff --git a/scripts/ci/mlkem-aarch64-gate.sh b/scripts/ci/mlkem-aarch64-gate.sh
index babf284d..20f6ed36 100755
--- a/scripts/ci/mlkem-aarch64-gate.sh
+++ b/scripts/ci/mlkem-aarch64-gate.sh
@@ -60,22 +60,22 @@ else
fi
run_step "owned aarch64 NEON NTT scalar oracle" \
- cargo test --lib --features ml-kem ntt_neon -- --nocapture
+ cargo test --locked --lib --features ml-kem ntt_neon -- --nocapture
run_step "owned aarch64 basemul scalar oracle" \
- cargo test --lib --features ml-kem basemul_accumulate -- --nocapture
+ cargo test --locked --lib --features ml-kem basemul_accumulate -- --nocapture
run_step "ML-KEM ACVP FIPS 203 vectors" \
- cargo test --test mlkem_acvp --features ml-kem -- --nocapture
+ cargo test --locked --test mlkem_acvp --features ml-kem -- --nocapture
run_step "ML-KEM operation tests" \
- cargo test --test mlkem_ops --features ml-kem -- --nocapture
+ cargo test --locked --test mlkem_ops --features ml-kem -- --nocapture
run_step "ML-KEM FIPS/property tests" \
- cargo test --test mlkem_properties --features ml-kem -- --nocapture
+ cargo test --locked --test mlkem_properties --features ml-kem -- --nocapture
run_step "ML-KEM fuzz corpus replay" \
- cargo test --manifest-path fuzz/Cargo.toml --test corpus_replay replay_auth_mlkem -- --nocapture
+ cargo test --locked --manifest-path fuzz/Cargo.toml --test corpus_replay replay_auth_mlkem -- --nocapture
if [[ "$(bool_value "${RSCRYPTO_MLKEM_AARCH64_GATE_SKIP_BENCH:-false}")" == "true" ]]; then
echo "warning: ML-KEM benchmark gate skipped by RSCRYPTO_MLKEM_AARCH64_GATE_SKIP_BENCH" | tee -a "$LOG_PATH"
diff --git a/scripts/ci/native-check.sh b/scripts/ci/native-check.sh
index 13742b84..dba8f39a 100755
--- a/scripts/ci/native-check.sh
+++ b/scripts/ci/native-check.sh
@@ -23,7 +23,7 @@ echo "Native validation: $(rustc -vV | sed -n 's/^host: //p')"
echo ""
echo "Checking no-default-features library boundary..."
-cargo check --workspace --lib --no-default-features
+cargo check --locked --workspace --lib --no-default-features
TARGET_ARGS=(--lib)
if [[ "$ALL_TARGETS" == true ]]; then
@@ -32,16 +32,16 @@ fi
echo ""
echo "Checking all-feature native targets..."
-cargo check --workspace "${TARGET_ARGS[@]}" --all-features
+cargo check --locked --workspace "${TARGET_ARGS[@]}" --all-features
echo ""
echo "Linting all-feature native targets..."
-cargo clippy --workspace "${TARGET_ARGS[@]}" --all-features -- -D warnings
+cargo clippy --locked --workspace "${TARGET_ARGS[@]}" --all-features
if [[ "$ALL_TARGETS" == true ]]; then
echo ""
echo "Building all-feature native targets..."
- cargo build --workspace --all-targets --all-features
+ cargo build --locked --workspace --all-targets --all-features
fi
echo "Native validation passed"
diff --git a/scripts/ci/nostd-wasm-suite.sh b/scripts/ci/nostd-wasm-suite.sh
index 0125984f..070bc890 100755
--- a/scripts/ci/nostd-wasm-suite.sh
+++ b/scripts/ci/nostd-wasm-suite.sh
@@ -57,9 +57,9 @@ run_wasm_runtime_vectors() {
export CARGO_TARGET_WASM32_WASIP1_RUNNER="wasmtime"
local manifest="tools/wasm-runtime-vectors/Cargo.toml"
- cargo run --manifest-path "$manifest" --target "$TARGET"
+ cargo run --locked --manifest-path "$manifest" --target "$TARGET"
RUSTFLAGS="-C target-feature=+simd128" \
- cargo run --manifest-path "$manifest" --target "$TARGET"
+ cargo run --locked --manifest-path "$manifest" --target "$TARGET"
}
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
@@ -67,11 +67,11 @@ echo "Cross-compile sweep: $TARGET ($DEPTH)"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
# Always: bare no-default-features.
-cargo check --target "$TARGET" --no-default-features --lib
-cargo build --target "$TARGET" --no-default-features --lib --release
+cargo check --locked --target "$TARGET" --no-default-features --lib
+cargo build --locked --target "$TARGET" --no-default-features --lib --release
# Always: alloc.
-cargo check --target "$TARGET" --no-default-features --features alloc --lib
+cargo check --locked --target "$TARGET" --no-default-features --features alloc --lib
if [[ "$DEPTH" == "deep" ]]; then
# Union of the historical check-all facade matrix and the dedicated weekly
@@ -110,22 +110,22 @@ if [[ "$DEPTH" == "deep" ]]; then
)
for feature_set in "${FEATURE_SETS[@]}"; do
- cargo check --target "$TARGET" --no-default-features --features "$feature_set" --lib
+ cargo check --locked --target "$TARGET" --no-default-features --features "$feature_set" --lib
done
# Full no_std release build.
- cargo build --target "$TARGET" --no-default-features --features "alloc,checksums,hashes,auth,aead" --lib --release
+ cargo build --locked --target "$TARGET" --no-default-features --features "alloc,checksums,hashes,auth,aead" --lib --release
fi
# Target-specific smoke (shallow gets a token extra so each target has >0 feature coverage).
if [[ "$DEPTH" == "shallow" ]]; then
case "$TARGET" in
thumbv6m-none-eabi)
- cargo check --target "$TARGET" --no-default-features --features checksums --lib
+ cargo check --locked --target "$TARGET" --no-default-features --features checksums --lib
;;
wasm32-unknown-unknown)
- cargo check --target "$TARGET" --no-default-features --features hashes --lib
- RUSTFLAGS="-C target-feature=+simd128" cargo check --target "$TARGET" --no-default-features --features hashes --lib
+ cargo check --locked --target "$TARGET" --no-default-features --features hashes --lib
+ RUSTFLAGS="-C target-feature=+simd128" cargo check --locked --target "$TARGET" --no-default-features --features hashes --lib
;;
esac
fi
diff --git a/scripts/ci/publish-immutable-release-test.sh b/scripts/ci/publish-immutable-release-test.sh
index 57b3828a..94d7c19d 100755
--- a/scripts/ci/publish-immutable-release-test.sh
+++ b/scripts/ci/publish-immutable-release-test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
+unset BASH_ENV
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PUBLISHER="$SCRIPT_DIR/publish-immutable-release.sh"
diff --git a/scripts/ci/release-evidence-check-test.sh b/scripts/ci/release-evidence-check-test.sh
index 46bfd2f5..afc3a19a 100755
--- a/scripts/ci/release-evidence-check-test.sh
+++ b/scripts/ci/release-evidence-check-test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
+unset BASH_ENV
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
diff --git a/scripts/ci/release-identity-test.sh b/scripts/ci/release-identity-test.sh
index 2a32782d..aa43fcab 100755
--- a/scripts/ci/release-identity-test.sh
+++ b/scripts/ci/release-identity-test.sh
@@ -96,7 +96,7 @@ jq -e --arg commit "$commit" '
and (.release.tag_object | test("^[0-9a-f]{40}$"))
and .release.git_commit == $commit
and (.release.git_tree | test("^[0-9a-f]{40}$"))
- and .toolchain.channel == "nightly-2026-07-17"
+ and .toolchain.channel == "1.97.1"
and (.toolchain.manifest.sha256 | test("^[0-9a-f]{64}$"))
and .evidence.git_commit == $commit
and .evidence.mode == "exact_commit"
diff --git a/scripts/ci/release-preflight.sh b/scripts/ci/release-preflight.sh
index 853bf515..7bc2b3e1 100755
--- a/scripts/ci/release-preflight.sh
+++ b/scripts/ci/release-preflight.sh
@@ -110,7 +110,7 @@ cargo rail config validate --strict
cargo rail config migrate --check
# Exact-commit Weekly release mode owns exhaustive compiler-backed Cargo graph
# assurance. The release evidence gate verifies that named job before publication.
-cargo deny check all
+cargo deny --locked check all
# RustCrypto `rsa` is used only as a dev/test/bench oracle. Production RSA
# verification is implemented in `src/auth/rsa.rs`; keep this scoped to the
# known Marvin advisory until the oracle dependency is removed or fixed.
diff --git a/scripts/ci/repository-controls-evidence-test.sh b/scripts/ci/repository-controls-evidence-test.sh
index fbb91a30..c2d44c7a 100755
--- a/scripts/ci/repository-controls-evidence-test.sh
+++ b/scripts/ci/repository-controls-evidence-test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
+unset BASH_ENV
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
diff --git a/scripts/ci/run-bench.sh b/scripts/ci/run-bench.sh
index a693ccb3..cec94cf6 100755
--- a/scripts/ci/run-bench.sh
+++ b/scripts/ci/run-bench.sh
@@ -916,7 +916,7 @@ run_bench_cmd() {
bench_features="$(bench_features_for_target "$bench")"
cargo_bench="$(bench_binary_for_target "$bench")"
- cmd=(cargo bench --profile bench --features "$bench_features" --bench "$cargo_bench")
+ cmd=(cargo bench --locked --profile bench --features "$bench_features" --bench "$cargo_bench")
if [[ -n "$filter" || "${#CRITERION_ARGS[@]}" -gt 0 ]]; then
cmd+=(--)
if [[ -n "$filter" ]]; then
@@ -997,7 +997,7 @@ fi
GENERIC_FEATURES="$(bench_features_for_invocation "$BENCHES_INPUT")"
echo "Using features: $GENERIC_FEATURES" | tee -a "$LOG_PATH"
-cmd=(cargo bench --profile bench --features "$GENERIC_FEATURES")
+cmd=(cargo bench --locked --profile bench --features "$GENERIC_FEATURES")
if [[ "${#BENCH_FLAGS[@]}" -gt 0 ]]; then
cmd+=("${BENCH_FLAGS[@]}")
fi
diff --git a/scripts/ci/run-rust-job-test.sh b/scripts/ci/run-rust-job-test.sh
index 6b0f0769..d5c59373 100755
--- a/scripts/ci/run-rust-job-test.sh
+++ b/scripts/ci/run-rust-job-test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
+unset BASH_ENV
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
DISPATCHER="$SCRIPT_DIR/run-rust-job.sh"
@@ -292,7 +293,7 @@ grep -Fq \
"$CAPTURE/amx-cargo.args" \
|| fail "AMX integration test existence was not checked under the required permission contract"
grep -Fq \
- 'RUSTFLAGS=-A unstable-features -C target-feature=+amx-tile,+amx-bf16,+amx-int8' \
+ 'RUSTFLAGS=-C target-feature=+amx-tile,+amx-bf16,+amx-int8' \
"$CAPTURE/amx-cargo.args" \
|| fail "AMX no_std test existence was not checked with forced AMX target features"
diff --git a/scripts/ci/run-rust-job.sh b/scripts/ci/run-rust-job.sh
index 9b3eca8e..f0a25a96 100755
--- a/scripts/ci/run-rust-job.sh
+++ b/scripts/ci/run-rust-job.sh
@@ -70,6 +70,11 @@ run_quality() {
just ci-check
}
+run_msrv() {
+ cargo check --locked --workspace --lib --no-default-features
+ cargo check --locked --workspace --lib --all-features
+}
+
run_cargo_graph() {
mkdir -p target/cargo-rail
cargo rail config validate --strict
@@ -97,16 +102,16 @@ run_native() {
case "$target" in
x86_64-pc-windows-msvc)
echo "Windows x86_64 lane: compile smoke plus BLAKE3 ASM runtime vectors"
- cargo clippy --workspace --lib --all-features -- -D warnings
- cargo test --workspace --all-features --no-run
- cargo test --workspace --features blake3 \
+ cargo clippy --locked --workspace --lib --all-features
+ cargo test --locked --workspace --all-features --no-run
+ cargo test --locked --workspace --features blake3 \
--test blake3_official_vectors \
--test blake3_differential
;;
aarch64-pc-windows-msvc)
echo "Windows AArch64 lane: compile-only smoke"
- cargo clippy --workspace --lib --all-features -- -D warnings
- cargo test --workspace --all-features --no-run
+ cargo clippy --locked --workspace --lib --all-features
+ cargo test --locked --workspace --all-features --no-run
;;
x86_64-unknown-linux-gnu | aarch64-unknown-linux-gnu)
bash scripts/ci/native-check.sh --all-targets
@@ -161,7 +166,7 @@ run_platform_amx() {
# NIGHTLY: Rust target-feature names for AMX remain unstable. This lane
# deliberately forces them so the no_std permission gate is executable.
- local amx_rustflags="-A unstable-features -C target-feature=+amx-tile,+amx-bf16,+amx-int8"
+ local amx_rustflags="-C target-feature=+amx-tile,+amx-bf16,+amx-int8"
RUSTFLAGS="$amx_rustflags" \
assert_single_libtest \
platform::detect::tests::no_std_linux_x86_64_masks_compile_time_amx_without_a_permission_probe \
@@ -181,13 +186,13 @@ run_supply_chain() {
require_one_of supply_chain_mode "$mode" light full
if [[ "$mode" == "full" ]]; then
- cargo deny check all
+ cargo deny --locked check all
# RustCrypto `rsa` is used only as a dev/test/bench oracle. Production RSA
# verification is implemented in `src/auth/rsa.rs`; keep this scoped to the
# known Marvin advisory until the oracle dependency is removed or fixed.
cargo audit --ignore RUSTSEC-2023-0071
else
- cargo deny check advisories
+ cargo deny --locked check advisories
fi
}
@@ -460,6 +465,7 @@ main() {
require_nonempty operation "$operation"
case "$operation" in
quality) run_quality ;;
+ msrv) run_msrv ;;
cargo-graph) run_cargo_graph ;;
feature-contracts) run_feature_contracts ;;
native) run_native ;;
diff --git a/scripts/ci/setup-toolchain.sh b/scripts/ci/setup-toolchain.sh
index beba45ec..688675cf 100755
--- a/scripts/ci/setup-toolchain.sh
+++ b/scripts/ci/setup-toolchain.sh
@@ -1,13 +1,14 @@
#!/usr/bin/env bash
set -euo pipefail
-if [[ $# -lt 1 || $# -gt 2 ]]; then
- echo "usage: setup-toolchain.sh [comma-separated-components]" >&2
+if [[ $# -lt 1 || $# -gt 3 ]]; then
+ echo "usage: setup-toolchain.sh [comma-separated-components] [github-env]" >&2
exit 2
fi
toolchain=$1
components=${2:-}
+github_env=${3:-}
if [[ ! "$toolchain" =~ ^(nightly|beta)-[0-9]{4}-[0-9]{2}-[0-9]{2}$ \
&& ! "$toolchain" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
@@ -34,5 +35,7 @@ if [[ -n "$components" ]]; then
fi
rustup "${install_args[@]}"
-rustup default "$toolchain"
+if [[ -n "$github_env" ]]; then
+ printf 'RUSTUP_TOOLCHAIN=%s\n' "$toolchain" >>"$github_env"
+fi
rustc "+$toolchain" --version --verbose
diff --git a/scripts/ci/tool-integrity-test.sh b/scripts/ci/tool-integrity-test.sh
index 5daedf29..ef0c6418 100755
--- a/scripts/ci/tool-integrity-test.sh
+++ b/scripts/ci/tool-integrity-test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
+unset BASH_ENV
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
@@ -90,11 +91,11 @@ while [[ $# -gt 0 ]]; do
esac
done
[[ -n "$destination" ]]
-root="$destination/wasmtime-v46.0.1-x86_64-linux"
+root="$destination/wasmtime-v47.0.3-x86_64-linux"
mkdir -p "$root"
cat >"$root/wasmtime" <<'EOF'
#!/usr/bin/env bash
-printf 'wasmtime 46.0.1 (mock)\n'
+printf 'wasmtime 47.0.3 (mock)\n'
printf 'wasmtime executed\n' >>"$MOCK_EXEC_LOG"
EOF
chmod +x "$root/wasmtime"
@@ -332,6 +333,7 @@ SH
cat >"$package_bin/rustc" <<'SH'
#!/usr/bin/env bash
+printf 'rustc %s\n' "$*" >>"$MOCK_PACKAGE_LOG"
printf 'rustc 1.99.0-nightly\ncommit-date: 2026-07-16\n'
SH
@@ -537,13 +539,13 @@ if HOME="$ct_home" \
fi
for contract in \
- 'cargo-nextest =0.9.140' \
+ 'cargo-nextest =0.9.143' \
'cargo-deny =0.20.2' \
'cargo-audit =0.22.2' \
- 'cargo-rail =0.20.0' \
+ 'cargo-rail =0.21.0' \
'cargo-semver-checks =0.50.0' \
- 'just =1.57.0' \
- 'zizmor =1.26.1' \
+ 'just =1.58.0' \
+ 'zizmor =1.29.0' \
'cargo-criterion =1.1.0' \
'critcmp =0.1.8' \
'cargo-fuzz =0.13.2' \
@@ -571,12 +573,12 @@ mkdir -p "$cached_home/.cargo/bin"
cat >"$cached_home/.cargo/bin/just" <<'SH'
#!/usr/bin/env bash
printf 'forged exact-version cache executed\n' >>"$MALICIOUS_EXEC_LOG"
-printf 'just 1.57.0\n'
+printf 'just 1.58.0\n'
SH
chmod +x "$cached_home/.cargo/bin/just"
cat >"$cached_home/.cargo/.crates.toml" <<'EOF'
[v1]
-"just 1.57.0 (registry+https://github.com/rust-lang/crates.io-index)" = ["just"]
+"just 1.58.0 (registry+https://github.com/rust-lang/crates.io-index)" = ["just"]
EOF
: >"$package_log"
HOME="$cached_home" \
@@ -589,7 +591,7 @@ HOME="$cached_home" \
"$REPO_ROOT/scripts/ci/install-tools.sh" minimal >/dev/null
[[ ! -e "$malicious_exec" ]] \
|| fail "forged exact-version cached binary executed before authenticated replacement"
-grep -Fq 'cargo install --registry crates-io just --locked --version =1.57.0 --force' "$package_log" \
+grep -Fq 'cargo install --registry crates-io just --locked --version =1.58.0 --force' "$package_log" \
|| fail "forged cache did not trigger a fresh authenticated install"
trusted_bin=$(tail -n 1 "$github_path_file")
case "$trusted_bin" in
@@ -598,14 +600,21 @@ case "$trusted_bin" in
esac
: >"$package_log"
+toolchain_env="$TMP_ROOT/toolchain.env"
+: >"$toolchain_env"
MOCK_PACKAGE_LOG="$package_log" PATH="$package_bin:$PATH" \
"$REPO_ROOT/scripts/ci/setup-toolchain.sh" \
- nightly-2026-07-17 'clippy, rustfmt' >/dev/null
+ nightly-2026-08-12 'clippy, rustfmt' "$toolchain_env" >/dev/null
grep -Fq \
- 'rustup toolchain install nightly-2026-07-17 --profile minimal --no-self-update --component clippy --component rustfmt' \
+ 'rustup toolchain install nightly-2026-08-12 --profile minimal --no-self-update --component clippy --component rustfmt' \
"$package_log" || fail "rustup toolchain command was not exact"
-grep -Fq 'rustup default nightly-2026-07-17' "$package_log" \
- || fail "rustup did not select the exact toolchain"
+grep -Fq 'rustc +nightly-2026-08-12 --version --verbose' "$package_log" \
+ || fail "installed toolchain was not verified explicitly"
+grep -Fxq 'RUSTUP_TOOLCHAIN=nightly-2026-08-12' "$toolchain_env" \
+ || fail "installed toolchain was not activated for later CI steps"
+if grep -Fq 'rustup default ' "$package_log"; then
+ fail "toolchain setup mutated the runner default"
+fi
if MOCK_PACKAGE_LOG="$package_log" PATH="$package_bin:$PATH" \
"$REPO_ROOT/scripts/ci/setup-toolchain.sh" nightly clippy >/dev/null 2>&1; then
fail "mutable rustup channel was accepted"
diff --git a/scripts/ct/dudect.sh b/scripts/ct/dudect.sh
index 4c9a7a8a..cc454d62 100755
--- a/scripts/ct/dudect.sh
+++ b/scripts/ct/dudect.sh
@@ -115,7 +115,7 @@ BUILD_TARGET_DIR="$ROOT/target/ct-dudect-build/$TARGET/$PROFILE"
if [[ ! -s "$LINKER_COMMAND_PATH" ]]; then
rm -rf "$BUILD_TARGET_DIR"
fi
-CARGO_ARGS=(--locked --manifest-path "$ROOT/tools/ct-dudect/Cargo.toml" --target-dir "$BUILD_TARGET_DIR" --target "$TARGET")
+CARGO_ARGS=(--manifest-path "$ROOT/tools/ct-dudect/Cargo.toml" --target-dir "$BUILD_TARGET_DIR" --target "$TARGET")
if [[ "$PROFILE" == "release" ]]; then
CARGO_ARGS+=(--release)
elif [[ "$PROFILE" != "debug" ]]; then
@@ -124,7 +124,7 @@ elif [[ "$PROFILE" != "debug" ]]; then
fi
linker_log_candidate="$(mktemp "$OUT_DIR/.dudect-linker-command.XXXXXXXX")"
-cargo rustc "${CARGO_ARGS[@]}" --bin rscrypto-ct-dudect -- --print link-args 2>&1 | tee "$linker_log_candidate"
+cargo rustc --locked "${CARGO_ARGS[@]}" --bin rscrypto-ct-dudect -- --print link-args 2>&1 | tee "$linker_log_candidate"
link_command_count=$(grep -c '"-o"' "$linker_log_candidate" || true)
if [[ "$link_command_count" -gt 1 ]]; then
echo "expected at most one DudeCT linker command; found $link_command_count" >&2
diff --git a/scripts/lib/common.sh b/scripts/lib/common.sh
index 0387a3fc..e3b5ce49 100644
--- a/scripts/lib/common.sh
+++ b/scripts/lib/common.sh
@@ -158,7 +158,27 @@ apply_ci_resource_profile() {
ensure_target() {
local target=$1
- if ! rustup target list --installed 2>/dev/null | grep -q "^${target}$"; then
+ local toolchain=${2:-}
+ if [[ -n "$toolchain" ]]; then
+ if ! rustup target list --toolchain "$toolchain" --installed 2>/dev/null | grep -q "^${target}$"; then
+ rustup target add --toolchain "$toolchain" "$target" >/dev/null 2>&1 || true
+ fi
+ elif ! rustup target list --installed 2>/dev/null | grep -q "^${target}$"; then
rustup target add "$target" >/dev/null 2>&1 || true
fi
}
+
+activate_nightly_toolchain() {
+ local toolchain_script="$COMMON_DIR/toolchain.sh"
+ local toolchain_contracts="$COMMON_DIR/../../.config/toolchains.toml"
+ [[ -x "$toolchain_script" ]] || {
+ echo "ERROR: nightly toolchain resolver not found: $toolchain_script" >&2
+ return 1
+ }
+ [[ -f "$toolchain_contracts" ]] || {
+ echo "ERROR: nightly toolchain authority not found: $toolchain_contracts" >&2
+ return 1
+ }
+ RUSTUP_TOOLCHAIN=$("$toolchain_script" --nightly)
+ export RUSTUP_TOOLCHAIN
+}
diff --git a/scripts/lib/fuzz-packages.sh b/scripts/lib/fuzz-packages.sh
index c9bdf5dd..155a5ced 100644
--- a/scripts/lib/fuzz-packages.sh
+++ b/scripts/lib/fuzz-packages.sh
@@ -79,6 +79,9 @@ fuzz_in_package() {
(
cd "$REPO_ROOT" || exit
+ if [[ "$subcommand" == build || "$subcommand" == run ]]; then
+ cargo metadata --locked --no-deps --manifest-path "$package_dir/Cargo.toml" >/dev/null
+ fi
CARGO_TARGET_DIR="$FUZZ_SHARED_TARGET_DIR" cargo fuzz "$subcommand" --fuzz-dir "$package_dir" "$@"
)
}
diff --git a/scripts/lib/target-matrix.sh b/scripts/lib/target-matrix.sh
index 18bda859..969a9100 100755
--- a/scripts/lib/target-matrix.sh
+++ b/scripts/lib/target-matrix.sh
@@ -25,21 +25,79 @@ if ! command -v jq >/dev/null 2>&1; then
fi
usage() {
- echo "Usage: $0 --format {shell|json} [--key KEY]" >&2
+ echo "Usage: $0 --validate | --format {shell|json} [--key KEY]" >&2
exit 1
}
FORMAT=""
KEY=""
+VALIDATE=false
while [[ $# -gt 0 ]]; do
case "$1" in
+ --validate) VALIDATE=true; shift ;;
--format) FORMAT="$2"; shift 2 ;;
--key) KEY="$2"; shift 2 ;;
*) usage ;;
esac
done
+if [[ "$VALIDATE" == true && -n "$FORMAT" ]]; then
+ usage
+fi
+
+validate_manifest() {
+ jq -e '
+ . as $root |
+ ($root.groups | keys) == ["ibm", "linux", "macos", "no_std", "wasm", "win"] and
+ all($root.groups[]; type == "array" and length > 0 and . == sort and all(.[]; type == "string" and length > 0)) and
+ ([$root.groups[][]] | length) == ([$root.groups[][]] | unique | length) and
+ ($root.ci | type == "array" and length > 0) and
+ ($root.ci == ($root.ci | sort_by(.name))) and
+ ([$root.ci[].name] | length) == ([$root.ci[].name] | unique | length) and
+ all($root.ci[];
+ .name as $name |
+ any($root.groups[][]; . == $name) and
+ if .type == "runson" then
+ (keys | sort) == ["name", "pool", "type"]
+ elif .type == "gha" then
+ (keys | sort) == ["name", "runner", "type"]
+ else
+ false
+ end
+ )
+ ' "$MANIFEST" >/dev/null || {
+ echo "ERROR: invalid target matrix schema: $MANIFEST" >&2
+ return 1
+ }
+
+ local matrix_targets
+ matrix_targets="$(jq -r '.groups[][]' "$MANIFEST" | LC_ALL=C sort)"
+
+ local projection
+ for projection in "$REPO_ROOT/.config/rail.toml" "$REPO_ROOT/deny.toml"; do
+ local projected_targets
+ projected_targets="$(awk '
+ /^targets = \[$/ { in_targets = 1; next }
+ in_targets && /^\]$/ { exit }
+ in_targets && match($0, /"[^"]+"/) {
+ print substr($0, RSTART + 1, RLENGTH - 2)
+ }
+ ' "$projection" | LC_ALL=C sort)"
+ if [[ "$projected_targets" != "$matrix_targets" ]]; then
+ echo "ERROR: target projection does not match .config/target-matrix.json: $projection" >&2
+ diff -u <(printf '%s\n' "$matrix_targets") <(printf '%s\n' "$projected_targets") >&2 || true
+ return 1
+ fi
+ done
+}
+
+validate_manifest
+
+if [[ "$VALIDATE" == true ]]; then
+ exit 0
+fi
+
[[ -n "$FORMAT" ]] || usage
print_shell() {
diff --git a/scripts/lib/toolchain.sh b/scripts/lib/toolchain.sh
index 9bc20554..48b7e881 100755
--- a/scripts/lib/toolchain.sh
+++ b/scripts/lib/toolchain.sh
@@ -2,24 +2,46 @@
# Extract toolchain channel from rust-toolchain.toml.
#
# Usage:
-# scripts/lib/toolchain.sh # prints channel
-# RUST_TOOLCHAIN=$(scripts/lib/toolchain.sh)
+# scripts/lib/toolchain.sh # prints the development channel
+# scripts/lib/toolchain.sh --msrv # prints the package MSRV
+# scripts/lib/toolchain.sh --nightly # prints the pinned nightly channel
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
TOOLCHAIN_FILE="$REPO_ROOT/rust-toolchain.toml"
+CARGO_MANIFEST="$REPO_ROOT/Cargo.toml"
+TOOLCHAIN_CONTRACTS="$REPO_ROOT/.config/toolchains.toml"
-if [ ! -f "$TOOLCHAIN_FILE" ]; then
- echo "ERROR: rust-toolchain.toml not found at $TOOLCHAIN_FILE" >&2
+case "${1:-}" in
+ "")
+ SOURCE_FILE="$TOOLCHAIN_FILE"
+ FIELD_PATTERN='^channel[[:space:]]*='
+ ;;
+ --msrv)
+ SOURCE_FILE="$CARGO_MANIFEST"
+ FIELD_PATTERN='^rust-version[[:space:]]*='
+ ;;
+ --nightly)
+ SOURCE_FILE="$TOOLCHAIN_CONTRACTS"
+ FIELD_PATTERN='^nightly[[:space:]]*='
+ ;;
+ *)
+ echo "Usage: $0 [--msrv|--nightly]" >&2
+ exit 2
+ ;;
+esac
+
+if [[ ! -f "$SOURCE_FILE" ]]; then
+ echo "ERROR: toolchain authority not found: $SOURCE_FILE" >&2
exit 1
fi
-RUST_TOOLCHAIN=$(awk -F'"' '/^channel/ {print $2}' "$TOOLCHAIN_FILE")
+RUST_TOOLCHAIN=$(awk -F'"' -v pattern="$FIELD_PATTERN" '$0 ~ pattern { print $2; exit }' "$SOURCE_FILE")
-if [ -z "$RUST_TOOLCHAIN" ]; then
- echo "ERROR: Could not extract toolchain from $TOOLCHAIN_FILE" >&2
+if [[ -z "$RUST_TOOLCHAIN" ]]; then
+ echo "ERROR: could not extract toolchain from $SOURCE_FILE" >&2
exit 1
fi
diff --git a/scripts/render_perf_chart.rs b/scripts/render_perf_chart.rs
index a60efb58..56ccca6a 100644
--- a/scripts/render_perf_chart.rs
+++ b/scripts/render_perf_chart.rs
@@ -26,6 +26,7 @@ const MUTED: &str = "#7d8590";
const RULE: &str = "#30363d";
const BLUE: &str = "#0090FF";
const TRACK: &str = "#21262d";
+const LOSS: &str = "#FF7B72";
const CHART_TITLE: &str = "rscrypto";
const CHART_SUBTITLE: &str = "Geomean speedups vs fastest matched competitors. Higher is better.";
@@ -34,9 +35,10 @@ const SUMMARY_APPLE_LABEL: &str = "Apple Silicon";
const CHECKSUM_TITLE: &str = "Checksums";
const CHECKSUM_COMPETITORS: &str = "- Competitor Crates/Libs: crc-fast, crc, crc32fast, crc32c, crc64fast";
const GROUP_TITLE: &str = "Primitive Geomeans";
+const GROUP_PARITY_NOTE: &str = "1.00x";
const FOOTER_LINUX_RUNNERS: &str = concat!(
"- Linux Runners: AMD Zen 4/5; Intel Sapphire Rapids/Ice Lake; ",
- "AWS Graviton 3/4; IBM POWER 10 and IBM Z16 (s390x); Rise RISC-V",
+ "AWS Graviton 3/4; IBM POWER 10 and IBM Z16 (s390x)",
);
const FOOTER_MACOS: &str = "- macOS: MBP M1 10-Core, 16GB RAM - Local Dev Box";
const FOOTER_FASTEST_EXTERNAL: &str =
@@ -561,22 +563,51 @@ fn render_group_bars(svg: &mut String, data: &ChartData) {
let row_y = 398.0;
let row_gap = 21.0;
let bar_h = 11.0;
- let max = 1.60_f64;
+ let (lo, hi) = group_axis(data);
+ let span = hi - lo;
+ let pos = |value: f64| bar_x + ((value.clamp(lo, hi) - lo) / span) * bar_w;
+ let parity_x = pos(1.0);
for (idx, (label, value)) in data.rows.iter().enumerate() {
let y = row_y + (idx as f64) * row_gap;
- let clamped = value.clamp(1.0, max);
- let width = ((clamped - 1.0) / (max - 1.0)) * bar_w;
+ let value_x_pos = pos(*value);
text(svg, label_x, y + 10.0, 13, 700, TEXT, label);
svg.push_str(&format!(
""
));
+ // Bars grow from the 1.00x parity marker: wins extend right, losses extend left.
+ let (fill_x, fill_w, fill) = if *value >= 1.0 {
+ (parity_x, value_x_pos - parity_x, BLUE)
+ } else {
+ (value_x_pos, parity_x - value_x_pos, LOSS)
+ };
svg.push_str(&format!(
- ""
+ ""
));
mono(svg, value_x, y + 10.0, 15, 850, TITLE, &format!("{value:.2}x"));
}
+
+ let rule_top = row_y - 4.0;
+ let rule_bottom = row_y + (data.rows.len() as f64 - 1.0) * row_gap + bar_h + 4.0;
+ svg.push_str(&format!(
+ ""
+ ));
+ mono(svg, parity_x - 15.0, rule_bottom + 12.0, 9, 500, MUTED, GROUP_PARITY_NOTE);
+}
+
+/// Axis bounds for the primitive bars: always straddle parity, never clip a bar.
+fn group_axis(data: &ChartData) -> (f64, f64) {
+ let mut lo = 1.0_f64;
+ let mut hi = 1.0_f64;
+ for (_, value) in &data.rows {
+ lo = lo.min(*value);
+ hi = hi.max(*value);
+ }
+ let lo = ((lo - 0.05) * 10.0).floor() / 10.0;
+ let hi = ((hi + 0.05) * 10.0).ceil() / 10.0;
+ (lo.min(0.9), hi.max(1.6))
}
fn render_evidence_bullets(svg: &mut String) {
diff --git a/scripts/test/test-coverage.sh b/scripts/test/test-coverage.sh
index deb772ab..b00d513e 100755
--- a/scripts/test/test-coverage.sh
+++ b/scripts/test/test-coverage.sh
@@ -79,7 +79,7 @@ cargo llvm-cov clean --workspace
if [ "$RUN_NEXTEST" = true ]; then
echo "━━━ Test Suite Coverage Capture (cargo-nextest) ━━━"
- cargo llvm-cov nextest --no-report --workspace --all-features "${NEXTEST_ARGS[@]}"
+ cargo llvm-cov nextest --locked --no-report --workspace --all-features "${NEXTEST_ARGS[@]}"
fi
if [ "$RUN_FUZZ" = true ]; then
@@ -87,14 +87,14 @@ if [ "$RUN_FUZZ" = true ]; then
export RSCRYPTO_FUZZ_REPLAY_MISSING=skip
echo "Full fuzz workspace replay"
- cargo llvm-cov test --no-report \
+ cargo llvm-cov test --locked --no-report \
--manifest-path "$REPO_ROOT/fuzz/Cargo.toml" \
--all-features --test corpus_replay -- --nocapture
echo "Scoped fuzz package replay"
while IFS= read -r manifest; do
echo " -> ${manifest#"$REPO_ROOT"/}"
- cargo llvm-cov test --no-report \
+ cargo llvm-cov test --locked --no-report \
--manifest-path "$manifest" \
--all-features --test corpus_replay -- --nocapture
done < <(find "$REPO_ROOT/fuzz-packages" -mindepth 2 -maxdepth 2 -name Cargo.toml | sort)
diff --git a/scripts/test/test-fuzz-asan.sh b/scripts/test/test-fuzz-asan.sh
index 06aac483..5f786ef6 100755
--- a/scripts/test/test-fuzz-asan.sh
+++ b/scripts/test/test-fuzz-asan.sh
@@ -11,6 +11,7 @@ source "$SCRIPT_DIR/../lib/common.sh"
source "$SCRIPT_DIR/../lib/fuzz-packages.sh"
maybe_disable_sccache
+activate_nightly_toolchain
PACKAGE_SCOPE="full"
@@ -85,6 +86,7 @@ for package_dir in "${SELECTED_FUZZ_PACKAGES[@]:+${SELECTED_FUZZ_PACKAGES[@]}}";
echo "ASan corpus replay package: $(fuzz_package_label "$package_dir")"
if ! cargo test \
+ --locked \
-Zbuild-std \
--target "$(fuzz_host_target)" \
--manifest-path "$package_dir/Cargo.toml" \
diff --git a/scripts/test/test-fuzz-scheduler-test.sh b/scripts/test/test-fuzz-scheduler-test.sh
index 2d1bce45..58a4f964 100755
--- a/scripts/test/test-fuzz-scheduler-test.sh
+++ b/scripts/test/test-fuzz-scheduler-test.sh
@@ -1,5 +1,6 @@
#!/usr/bin/env bash
set -euo pipefail
+unset BASH_ENV
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
@@ -14,10 +15,12 @@ fail() {
FIXTURE="$TMP_ROOT/repo"
CAPTURE="$TMP_ROOT/capture"
BIN="$TMP_ROOT/bin"
-mkdir -p "$FIXTURE/scripts/test" "$FIXTURE/scripts/lib" "$FIXTURE/fuzz/corpus/alpha" "$CAPTURE" "$BIN"
+mkdir -p "$FIXTURE/.config" "$FIXTURE/scripts/test" "$FIXTURE/scripts/lib" "$FIXTURE/fuzz/corpus/alpha" "$CAPTURE" "$BIN"
cp "$REPO_ROOT/scripts/test/test-fuzz.sh" "$FIXTURE/scripts/test/"
cp "$REPO_ROOT/scripts/lib/common.sh" "$REPO_ROOT/scripts/lib/rail-plan.sh" \
- "$REPO_ROOT/scripts/lib/fuzz-packages.sh" "$FIXTURE/scripts/lib/"
+ "$REPO_ROOT/scripts/lib/fuzz-packages.sh" "$REPO_ROOT/scripts/lib/toolchain.sh" \
+ "$FIXTURE/scripts/lib/"
+cp "$REPO_ROOT/.config/toolchains.toml" "$FIXTURE/.config/toolchains.toml"
cat >"$FIXTURE/fuzz/Cargo.toml" <<'EOF'
[package]
diff --git a/scripts/test/test-fuzz.sh b/scripts/test/test-fuzz.sh
index 32f824c7..fab0dded 100755
--- a/scripts/test/test-fuzz.sh
+++ b/scripts/test/test-fuzz.sh
@@ -29,6 +29,7 @@ source "$SCRIPT_DIR/../lib/common.sh"
source "$SCRIPT_DIR/../lib/fuzz-packages.sh"
maybe_disable_sccache
+activate_nightly_toolchain
# Configuration (can be overridden via environment)
DURATION_SECS=${RSCRYPTO_FUZZ_DURATION_SECS:-60}
diff --git a/scripts/test/test-miri.sh b/scripts/test/test-miri.sh
index e17ddd00..a2619b37 100755
--- a/scripts/test/test-miri.sh
+++ b/scripts/test/test-miri.sh
@@ -34,6 +34,7 @@ echo "━━━━━━━━━━━━━━━━━━━━━━━━
echo ""
maybe_disable_sccache
+activate_nightly_toolchain
unset RUSTC_WRAPPER
unset CARGO_BUILD_RUSTC_WRAPPER
@@ -87,7 +88,7 @@ run_miri_lib_filter() {
echo ""
echo "━━━ $label ━━━"
- cargo miri test --lib --features "$MIRI_FEATURES" "$filter"
+ cargo miri test --locked --lib --features "$MIRI_FEATURES" "$filter"
}
run_miri_lib_filter_features() {
@@ -100,14 +101,14 @@ run_miri_lib_filter_features() {
echo ""
echo "━━━ $label ━━━"
- listing=$(cargo miri test --lib --features "$features" "$filter" -- --list)
+ listing=$(cargo miri test --locked --lib --features "$features" "$filter" -- --list)
printf '%s\n' "$listing"
count=$(printf '%s\n' "$listing" | awk '/: test$/ { count++ } END { print count + 0 }')
if [ "$count" -ne "$expected_count" ]; then
echo "Expected $expected_count Miri tests matching '$filter'; found $count" >&2
return 1
fi
- cargo miri test --lib --features "$features" "$filter"
+ cargo miri test --locked --lib --features "$features" "$filter"
}
run_miri_test_target() {
@@ -117,7 +118,7 @@ run_miri_test_target() {
echo ""
echo "━━━ $label ━━━"
- cargo miri test --test "$target" --features "$features"
+ cargo miri test --locked --test "$target" --features "$features"
}
# Run Miri Tests
@@ -152,7 +153,7 @@ case "$MIRI_SCOPE" in
;;
exhaustive)
echo "Scope: exhaustive lib tests under Miri"
- cargo miri test --lib --features "$MIRI_FEATURES"
+ cargo miri test --locked --lib --features "$MIRI_FEATURES"
;;
*)
echo "Invalid RSCRYPTO_MIRI_SCOPE: $MIRI_SCOPE"
diff --git a/scripts/test/test-rsa-leakage.sh b/scripts/test/test-rsa-leakage.sh
index 87207a34..27294421 100755
--- a/scripts/test/test-rsa-leakage.sh
+++ b/scripts/test/test-rsa-leakage.sh
@@ -21,7 +21,7 @@ echo "Welch t threshold: $RSCRYPTO_RSA_LEAKAGE_T_THRESHOLD"
echo "Warmup iterations: $RSCRYPTO_RSA_LEAKAGE_WARMUP"
echo ""
-cargo test --release --test rsa_leakage --features rsa,diag,getrandom \
+cargo test --locked --release --test rsa_leakage --features rsa,diag,getrandom \
rsa_private_operations_do_not_show_first_order_timing_leakage \
-- --ignored --nocapture
diff --git a/scripts/test/test.sh b/scripts/test/test.sh
index 1f52f7b6..593ad75f 100755
--- a/scripts/test/test.sh
+++ b/scripts/test/test.sh
@@ -57,7 +57,7 @@ run_workspace_doctests() {
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Running doctests for entire workspace"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
- cargo test --workspace --doc --all-features
+ cargo test --locked --workspace --doc --all-features
}
run_crate_doctests() {
@@ -77,7 +77,7 @@ run_crate_doctests() {
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
for crate in "${crates[@]}"; do
- cargo test -p "$crate" --doc --all-features
+ cargo test --locked -p "$crate" --doc --all-features
done
}
@@ -135,21 +135,21 @@ if [ ${#CRATES[@]} -gt 0 ]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ "$HAS_NEXTEST" = true ]; then
# shellcheck disable=SC2086
- cargo nextest run $CRATE_FLAGS -P "$PROFILE" --all-features --config-file .config/nextest.toml "${NEXTEST_THREAD_ARGS[@]:+${NEXTEST_THREAD_ARGS[@]}}"
+ cargo nextest run --locked $CRATE_FLAGS -P "$PROFILE" --all-features --config-file .config/nextest.toml "${NEXTEST_THREAD_ARGS[@]:+${NEXTEST_THREAD_ARGS[@]}}"
run_crate_doctests "${CRATES[@]}"
else
# shellcheck disable=SC2086
- cargo test $CRATE_FLAGS --all-features "${CARGO_TEST_TARGET_ARGS[@]:+${CARGO_TEST_TARGET_ARGS[@]}}"
+ cargo test --locked $CRATE_FLAGS --all-features "${CARGO_TEST_TARGET_ARGS[@]:+${CARGO_TEST_TARGET_ARGS[@]}}"
fi
elif [ "$ALL_FLAG" = true ]; then
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Testing entire workspace"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
if [ "$HAS_NEXTEST" = true ]; then
- cargo nextest run --workspace -P "$PROFILE" --all-features --config-file .config/nextest.toml "${NEXTEST_THREAD_ARGS[@]:+${NEXTEST_THREAD_ARGS[@]}}"
+ cargo nextest run --locked --workspace -P "$PROFILE" --all-features --config-file .config/nextest.toml "${NEXTEST_THREAD_ARGS[@]:+${NEXTEST_THREAD_ARGS[@]}}"
run_workspace_doctests
else
- cargo test --workspace --all-features "${CARGO_TEST_TARGET_ARGS[@]:+${CARGO_TEST_TARGET_ARGS[@]}}"
+ cargo test --locked --workspace --all-features "${CARGO_TEST_TARGET_ARGS[@]:+${CARGO_TEST_TARGET_ARGS[@]}}"
fi
else
# Rail-scoped (default): cargo-rail planner selects the affected crates.
@@ -173,11 +173,11 @@ else
for crate in $affected; do
CRATE_FLAGS+=(-p "$crate")
done
- cargo nextest run "${CRATE_FLAGS[@]}" -P "$PROFILE" --all-features --config-file .config/nextest.toml "${NEXTEST_THREAD_ARGS[@]:+${NEXTEST_THREAD_ARGS[@]}}"
+ cargo nextest run --locked "${CRATE_FLAGS[@]}" -P "$PROFILE" --all-features --config-file .config/nextest.toml "${NEXTEST_THREAD_ARGS[@]:+${NEXTEST_THREAD_ARGS[@]}}"
run_rail_scoped_doctests
else
for crate in $affected; do
- cargo test -p "$crate" --all-features "${CARGO_TEST_TARGET_ARGS[@]:+${CARGO_TEST_TARGET_ARGS[@]}}"
+ cargo test --locked -p "$crate" --all-features "${CARGO_TEST_TARGET_ARGS[@]:+${CARGO_TEST_TARGET_ARGS[@]}}"
done
fi
exit 0
@@ -186,9 +186,9 @@ else
esac
if [ "$HAS_NEXTEST" = true ]; then
- cargo nextest run --workspace -P "$PROFILE" --all-features --config-file .config/nextest.toml "${NEXTEST_THREAD_ARGS[@]:+${NEXTEST_THREAD_ARGS[@]}}"
+ cargo nextest run --locked --workspace -P "$PROFILE" --all-features --config-file .config/nextest.toml "${NEXTEST_THREAD_ARGS[@]:+${NEXTEST_THREAD_ARGS[@]}}"
run_workspace_doctests
else
- cargo test --workspace --all-features "${CARGO_TEST_TARGET_ARGS[@]:+${CARGO_TEST_TARGET_ARGS[@]}}"
+ cargo test --locked --workspace --all-features "${CARGO_TEST_TARGET_ARGS[@]:+${CARGO_TEST_TARGET_ARGS[@]}}"
fi
fi
diff --git a/src/aead/aegis256.rs b/src/aead/aegis256.rs
index 849441d5..efc6c093 100644
--- a/src/aead/aegis256.rs
+++ b/src/aead/aegis256.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::indexing_slicing)]
-
//! AEGIS-256 authenticated encryption (draft-irtf-cfrg-aegis-aead).
//!
//! AES-round-based AEAD with a 256-bit key, 256-bit nonce, and 128-bit
@@ -16,6 +14,7 @@ use core::fmt;
))]
use super::targets::AeadBackend;
#[cfg(any(
+ target_arch = "x86_64",
target_arch = "aarch64",
all(target_arch = "powerpc64", target_endian = "little"),
target_arch = "riscv64",
@@ -217,31 +216,21 @@ fn finalize(s: &mut State, ad_len: usize, msg_len: usize) -> [u8; TAG_SIZE] {
// riscv64 scalar AES backend (Zkne)
#[cfg(target_arch = "aarch64")]
-#[allow(unsafe_op_in_unsafe_fn)]
#[path = "aegis256/aarch64_ce.rs"]
mod ce;
#[cfg(target_arch = "x86_64")]
-#[allow(unsafe_op_in_unsafe_fn)]
#[path = "aegis256/x86_64_ni.rs"]
mod ni;
#[cfg(all(target_arch = "powerpc64", target_endian = "little"))]
-#[allow(unsafe_code, unsafe_op_in_unsafe_fn)]
#[path = "aegis256/powerpc64_ppc.rs"]
mod ppc;
#[cfg(target_arch = "riscv64")]
-#[allow(unsafe_code)]
-#[path = "aegis256/riscv64_vperm.rs"]
-mod rv_vperm;
-#[cfg(target_arch = "riscv64")]
-#[allow(unsafe_code)]
#[path = "aegis256/riscv64_zkne.rs"]
mod rv_zkne;
#[cfg(target_arch = "riscv64")]
-#[allow(unsafe_code)]
#[path = "aegis256/riscv64_zvkned.rs"]
mod rv_zvkned;
#[cfg(target_arch = "s390x")]
-#[allow(unsafe_code)]
#[path = "aegis256/s390x_vperm.rs"]
mod s390x_vperm;
#[cfg(any(
@@ -254,20 +243,7 @@ mod s390x_vperm;
#[inline]
fn resolve_backend() -> AeadBackend {
let caps = crate::platform::caps();
-
- #[cfg(target_arch = "x86_64")]
- {
- use crate::platform::caps::x86;
- if caps.has(x86::AESNI) && caps.has(x86::AVX) {
- return AeadBackend::X86Aesni;
- }
- AeadBackend::Portable
- }
-
- #[cfg(not(target_arch = "x86_64"))]
- {
- select_backend(AeadPrimitive::Aegis256, crate::platform::arch(), caps)
- }
+ select_backend(AeadPrimitive::Aegis256, crate::platform::arch(), caps)
}
// Key
@@ -435,6 +411,7 @@ fn encrypt_portable(key: &[u8; KEY_SIZE], nonce: &[u8; NONCE_SIZE], aad: &[u8],
}
#[cfg(feature = "diag")]
+/// Run one portable AEGIS-256 state update and return its diagnostic final tag.
#[unsafe(no_mangle)]
#[inline(never)]
#[must_use]
@@ -570,13 +547,6 @@ impl Aead for Aegis256 {
return Ok(Aegis256Tag::from_bytes(tag));
}
- #[cfg(target_arch = "riscv64")]
- if self.backend == AeadBackend::Riscv64Vperm {
- // SAFETY: backend resolution confirmed the RISC-V V extension is available.
- let tag = unsafe { rv_vperm::encrypt_fused(key, nonce, aad, buffer) };
- return Ok(Aegis256Tag::from_bytes(tag));
- }
-
#[cfg(target_arch = "s390x")]
if self.backend == AeadBackend::S390xVperm {
// SAFETY: s390x vperm backend call because:
@@ -584,7 +554,6 @@ impl Aead for Aegis256 {
// 2. `select_backend` returns `S390xVperm` only when caps include `s390x::VECTOR`.
// 3. `key`, `nonce`, `aad`, and `buffer` are valid references from the safe AEAD API.
let tag = unsafe { s390x_vperm::encrypt_fused(key, nonce, aad, buffer) };
- #[allow(clippy::needless_return)]
return Ok(Aegis256Tag::from_bytes(tag));
}
@@ -635,9 +604,6 @@ impl Aead for Aegis256 {
} else if self.backend == AeadBackend::Riscv64ScalarCrypto {
// SAFETY: backend resolution confirmed scalar AES (`zkne`) is available.
unsafe { rv_zkne::decrypt_fused(key, nonce, aad, buffer) }
- } else if self.backend == AeadBackend::Riscv64Vperm {
- // SAFETY: backend resolution confirmed the RISC-V V extension is available.
- unsafe { rv_vperm::decrypt_fused(key, nonce, aad, buffer) }
} else {
decrypt_portable(key, nonce, aad, buffer)
};
@@ -676,10 +642,12 @@ impl Aead for Aegis256 {
#[cfg(test)]
mod tests {
use alloc::{vec, vec::Vec};
- use std::eprintln;
use super::*;
- use crate::aead::expert::AeadWithNonce;
+ use crate::aead::{
+ expert::AeadWithNonce,
+ test_vectors::{hex_array, hex_vec as hex},
+ };
#[cfg(not(target_arch = "s390x"))]
#[inline(always)]
@@ -687,18 +655,8 @@ mod tests {
super::super::aes_round::aes_enc_round_portable(block, round_key)
}
- fn hex(s: &str) -> Vec {
- (0..s.len())
- .step_by(2)
- .map(|i| u8::from_str_radix(&s[i..i.strict_add(2)], 16).unwrap())
- .collect()
- }
-
fn hex_block(s: &str) -> [u8; 16] {
- let v = hex(s);
- let mut out = [0u8; 16];
- out.copy_from_slice(&v);
- out
+ hex_array(s)
}
// -- AESRound test vector (Appendix A.1) --
@@ -767,15 +725,10 @@ mod tests {
// -- Spec test vectors (Appendix A.3) --
fn spec_key() -> Aegis256Key {
- Aegis256Key::from_bytes(
- hex_block("10010000000000000000000000000000")
- .iter()
- .chain(hex_block("00000000000000000000000000000000").iter())
- .copied()
- .collect::>()
- .try_into()
- .unwrap(),
- )
+ Aegis256Key::from_bytes(hex_array(concat!(
+ "10010000000000000000000000000000",
+ "00000000000000000000000000000000"
+ )))
}
fn spec_nonce() -> Nonce256 {
@@ -793,12 +746,16 @@ mod tests {
// Encrypt.
let mut buf = msg.to_vec();
- let tag = aead.encrypt_in_place(&nonce, aad, &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, aad, &mut buf)
+ .expect("AEGIS-256 specification-vector encryption must succeed");
assert_eq!(&buf, &expected_ct, "ciphertext mismatch");
assert_eq!(tag.as_bytes(), expected_tag.as_slice(), "tag mismatch");
// Decrypt round-trip.
- aead.decrypt_in_place(&nonce, aad, &mut buf, &tag).unwrap();
+ aead
+ .decrypt_in_place(&nonce, aad, &mut buf, &tag)
+ .expect("AEGIS-256 specification-vector decryption must succeed");
assert_eq!(&buf, msg, "plaintext recovery mismatch");
}
@@ -861,8 +818,12 @@ mod tests {
let aead = Aegis256::new(&key);
let mut buf = [];
- let tag = aead.encrypt_in_place(&nonce, b"", &mut buf).unwrap();
- aead.decrypt_in_place(&nonce, b"", &mut buf, &tag).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"", &mut buf)
+ .expect("empty AEGIS-256 encryption must succeed");
+ aead
+ .decrypt_in_place(&nonce, b"", &mut buf, &tag)
+ .expect("empty AEGIS-256 decryption must succeed");
}
#[test]
@@ -873,10 +834,14 @@ mod tests {
let plaintext = b"the quick brown fox jumps over the lazy dog";
let mut buf = *plaintext;
- let tag = aead.encrypt_in_place(&nonce, b"header", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"header", &mut buf)
+ .expect("AEGIS-256 encryption with AAD must succeed");
assert_ne!(&buf[..], &plaintext[..]);
- aead.decrypt_in_place(&nonce, b"header", &mut buf, &tag).unwrap();
+ aead
+ .decrypt_in_place(&nonce, b"header", &mut buf, &tag)
+ .expect("AEGIS-256 decryption with AAD must succeed");
assert_eq!(&buf[..], &plaintext[..]);
}
@@ -889,10 +854,10 @@ mod tests {
let mut buf = [];
let tag = aead
.encrypt_in_place(&nonce, b"associated data only", &mut buf)
- .unwrap();
+ .expect("AAD-only AEGIS-256 encryption must succeed");
aead
.decrypt_in_place(&nonce, b"associated data only", &mut buf, &tag)
- .unwrap();
+ .expect("AAD-only AEGIS-256 decryption must succeed");
}
#[test]
@@ -902,14 +867,18 @@ mod tests {
let aead = Aegis256::new(&key);
let mut buf = *b"zero me on failure";
- let tag = aead.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AEGIS-256 test setup encryption must succeed");
let mut bad_tag = tag.to_bytes();
bad_tag[0] ^= 0xFF;
let bad_tag = Aegis256Tag::from_bytes(bad_tag);
- let result = aead.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag),
+ Err(OpenError::verification())
+ );
assert!(buf.iter().all(|&b| b == 0), "buffer not zeroed on auth failure");
}
@@ -920,11 +889,15 @@ mod tests {
let aead = Aegis256::new(&key);
let mut buf = *b"secret";
- let tag = aead.encrypt_in_place(&nonce, b"", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"", &mut buf)
+ .expect("AEGIS-256 test setup encryption must succeed");
buf[0] ^= 1;
- let result = aead.decrypt_in_place(&nonce, b"", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&nonce, b"", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
assert_eq!(&buf, &[0u8; 6]);
}
@@ -935,14 +908,18 @@ mod tests {
let aead = Aegis256::new(&key);
let mut buf = *b"data";
- let tag = aead.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AEGIS-256 test setup encryption must succeed");
let mut bad_tag_bytes = tag.to_bytes();
bad_tag_bytes[15] ^= 1;
let bad_tag = Aegis256Tag::from_bytes(bad_tag_bytes);
- let result = aead.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag),
+ Err(OpenError::verification())
+ );
assert_eq!(&buf, &[0u8; 4]);
}
@@ -953,10 +930,14 @@ mod tests {
let aead = Aegis256::new(&key);
let mut buf = *b"msg";
- let tag = aead.encrypt_in_place(&nonce, b"correct", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"correct", &mut buf)
+ .expect("AEGIS-256 test setup encryption must succeed");
- let result = aead.decrypt_in_place(&nonce, b"wrong", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&nonce, b"wrong", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
#[test]
@@ -966,11 +947,15 @@ mod tests {
let aead = Aegis256::new(&key);
let mut buf = *b"nonce test";
- let tag = aead.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AEGIS-256 test setup encryption must succeed");
let wrong_nonce = Nonce256::from_bytes([11; 32]);
- let result = aead.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
#[test]
@@ -981,18 +966,29 @@ mod tests {
let pt = b"combined mode";
let mut sealed = vec![0u8; pt.len().strict_add(TAG_SIZE)];
- aead.encrypt(&nonce, b"h", pt.as_slice(), &mut sealed).unwrap();
+ aead
+ .encrypt(&nonce, b"h", pt.as_slice(), &mut sealed)
+ .expect("combined AEGIS-256 encryption must succeed");
let mut opened = vec![0u8; pt.len()];
- aead.decrypt(&nonce, b"h", &sealed, &mut opened).unwrap();
+ aead
+ .decrypt(&nonce, b"h", &sealed, &mut opened)
+ .expect("combined AEGIS-256 decryption must succeed");
assert_eq!(&opened, &pt[..]);
}
#[test]
fn tag_from_slice_rejects_wrong_length() {
- assert!(Aegis256::tag_from_slice(&[0u8; 15]).is_err());
- assert!(Aegis256::tag_from_slice(&[0u8; 17]).is_err());
- assert!(Aegis256::tag_from_slice(&[0u8; 16]).is_ok());
+ assert_eq!(
+ Aegis256::tag_from_slice(&[0u8; 15]).expect_err("short AEGIS-256 tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aegis256::tag_from_slice(&[0u8; 17]).expect_err("long AEGIS-256 tag must be rejected"),
+ AeadBufferError::new()
+ );
+ let tag = Aegis256::tag_from_slice(&[0u8; 16]).expect("16-byte AEGIS-256 tag must be accepted");
+ assert_eq!(tag.as_bytes(), &[0u8; 16]);
}
#[test]
@@ -1006,7 +1002,7 @@ mod tests {
let mut buf = plaintext;
let tag = aead
.encrypt_in_place(&nonce, b"multi-block aad that is longer than one rate block", &mut buf)
- .unwrap();
+ .expect("multi-block AEGIS-256 encryption must succeed");
aead
.decrypt_in_place(
&nonce,
@@ -1014,7 +1010,7 @@ mod tests {
&mut buf,
&tag,
)
- .unwrap();
+ .expect("multi-block AEGIS-256 decryption must succeed");
assert_eq!(buf, plaintext);
}
@@ -1027,15 +1023,23 @@ mod tests {
// Exactly 16 bytes = 1 full block, 0-byte tail.
let plaintext = [0x55u8; 16];
let mut buf = plaintext;
- let tag = aead.encrypt_in_place(&nonce, b"", &mut buf).unwrap();
- aead.decrypt_in_place(&nonce, b"", &mut buf, &tag).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"", &mut buf)
+ .expect("one-block AEGIS-256 encryption must succeed");
+ aead
+ .decrypt_in_place(&nonce, b"", &mut buf, &tag)
+ .expect("one-block AEGIS-256 decryption must succeed");
assert_eq!(buf, plaintext);
// Exactly 32 bytes = 2 full blocks, 0-byte tail.
let plaintext32 = [0x66u8; 32];
let mut buf32 = plaintext32;
- let tag32 = aead.encrypt_in_place(&nonce, b"", &mut buf32).unwrap();
- aead.decrypt_in_place(&nonce, b"", &mut buf32, &tag32).unwrap();
+ let tag32 = aead
+ .encrypt_in_place(&nonce, b"", &mut buf32)
+ .expect("two-block AEGIS-256 encryption must succeed");
+ aead
+ .decrypt_in_place(&nonce, b"", &mut buf32, &tag32)
+ .expect("two-block AEGIS-256 decryption must succeed");
assert_eq!(buf32, plaintext32);
}
@@ -1047,12 +1051,16 @@ mod tests {
let aead = Aegis256::new(&key);
let aad = b"four-block-test";
- for &size in &[48, 64, 80, 96, 112, 128, 256, 1024, 4096] {
- let plaintext: Vec = (0..size).map(|i| (i & 0xFF) as u8).collect();
+ for &size in &[48usize, 64, 80, 96, 112, 128, 256, 1024, 4096] {
+ let plaintext: Vec = (0u8..=u8::MAX).cycle().take(size).collect();
let mut buf = plaintext.clone();
- let tag = aead.encrypt_in_place(&nonce, aad, &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, aad, &mut buf)
+ .expect("boundary AEGIS-256 encryption must succeed");
assert_ne!(&buf, &plaintext, "size {size}: ciphertext must differ");
- aead.decrypt_in_place(&nonce, aad, &mut buf, &tag).unwrap();
+ aead
+ .decrypt_in_place(&nonce, aad, &mut buf, &tag)
+ .expect("boundary AEGIS-256 decryption must succeed");
assert_eq!(&buf, &plaintext, "size {size}: round-trip failed");
}
}
@@ -1066,7 +1074,7 @@ mod tests {
/// Scalar simulation of vperm: table[index & 0x0F].
fn vperm_scalar(table: &[u8; 16], index: u8) -> u8 {
- table[(index & 0x0F) as usize]
+ table[usize::from(index & 0x0F)]
}
/// Scalar simulation of vperm with PSHUFB zeroing: returns 0 when bit 7 set.
@@ -1074,7 +1082,7 @@ mod tests {
if index & 0x80 != 0 {
0
} else {
- table[(index & 0x0F) as usize]
+ table[usize::from(index & 0x0F)]
}
}
@@ -1147,24 +1155,14 @@ mod tests {
0x8C, 0xA1, 0x89, 0x0D, 0xBF, 0xE6, 0x42, 0x68, 0x41, 0x99, 0x2D, 0x0F, 0xB0, 0x54, 0xBB, 0x16,
];
- let mut failures = 0u32;
- for input in 0u16..256 {
- let got = vperm_sbox_scalar(input as u8);
+ for input in u8::MIN..=u8::MAX {
+ let got = vperm_sbox_scalar(input);
// The Hamburg vperm S-box omits the AES affine constant.
// vpaes_sbox(x) = AES_sbox(x) ^ AES_AFFINE for all x.
use super::super::aes_round::AES_AFFINE;
- let expected = AES_SBOX[input as usize] ^ AES_AFFINE;
- if got != expected {
- if failures < 16 {
- eprintln!(
- "vperm S-box mismatch at input 0x{:02X}: got 0x{:02X}, expected 0x{:02X}",
- input, got, expected,
- );
- }
- failures = failures.strict_add(1);
- }
+ let expected = AES_SBOX[usize::from(input)] ^ AES_AFFINE;
+ assert_eq!(got, expected, "vperm S-box mismatch at input 0x{input:02X}");
}
- assert_eq!(failures, 0, "{failures} vperm S-box mismatches out of 256");
}
// -- Full vperm AES round validation --
@@ -1180,35 +1178,37 @@ mod tests {
// SubBytes via vperm tower field (includes affine constant compensation)
use super::super::aes_round::{AES_AFFINE, VPERM_SR as SR};
let mut sb = [0u8; 16];
- for i in 0..16 {
- sb[i] = vperm_sbox_scalar(block[i]) ^ AES_AFFINE;
+ for (substituted, &input) in sb.iter_mut().zip(block) {
+ *substituted = vperm_sbox_scalar(input) ^ AES_AFFINE;
}
// ShiftRows
let mut sr = [0u8; 16];
- for i in 0..16 {
- sr[i] = sb[SR[i] as usize];
+ for (shifted, &source) in sr.iter_mut().zip(&SR) {
+ *shifted = sb[usize::from(source)];
}
// MixColumns via xtime decomposition
fn xtime(b: u8) -> u8 {
- let r = (b as u16) << 1;
- (r ^ (if r & 0x100 != 0 { 0x1B } else { 0 })) as u8
+ let doubled = u16::from(b).strict_mul(2);
+ let reduced = if doubled & 0x100 == 0 { doubled } else { doubled ^ 0x11B };
+ u8::try_from(reduced).expect("AES polynomial reduction must produce one byte")
}
let mut mc = [0u8; 16];
- for col in 0..4 {
- let c = col * 4;
- let (b0, b1, b2, b3) = (sr[c], sr[c + 1], sr[c + 2], sr[c + 3]);
- mc[c] = xtime(b0) ^ xtime(b1) ^ b1 ^ b2 ^ b3;
- mc[c + 1] = b0 ^ xtime(b1) ^ xtime(b2) ^ b2 ^ b3;
- mc[c + 2] = b0 ^ b1 ^ xtime(b2) ^ xtime(b3) ^ b3;
- mc[c + 3] = xtime(b0) ^ b0 ^ b1 ^ b2 ^ xtime(b3);
+ for (source, mixed) in sr.as_chunks::<4>().0.iter().zip(mc.as_chunks_mut::<4>().0) {
+ let [b0, b1, b2, b3] = *source;
+ *mixed = [
+ xtime(b0) ^ xtime(b1) ^ b1 ^ b2 ^ b3,
+ b0 ^ xtime(b1) ^ xtime(b2) ^ b2 ^ b3,
+ b0 ^ b1 ^ xtime(b2) ^ xtime(b3) ^ b3,
+ xtime(b0) ^ b0 ^ b1 ^ b2 ^ xtime(b3),
+ ];
}
// AddRoundKey
let mut result = [0u8; 16];
- for i in 0..16 {
- result[i] = mc[i] ^ round_key[i];
+ for ((output, &mixed), &key_byte) in result.iter_mut().zip(&mc).zip(round_key) {
+ *output = mixed ^ key_byte;
}
result
}
@@ -1237,9 +1237,9 @@ mod tests {
assert_eq!(vperm_aes_round_scalar(&a, &b), portable_aes_round(&a, &b));
// Exhaustive: test all single-byte patterns in position 0
- for val in 0u16..256 {
+ for val in u8::MIN..=u8::MAX {
let mut block = [0u8; 16];
- block[0] = val as u8;
+ block[0] = val;
let key = [0u8; 16];
let got = vperm_aes_round_scalar(&block, &key);
let expected = portable_aes_round(&block, &key);
diff --git a/src/aead/aegis256/aarch64_ce.rs b/src/aead/aegis256/aarch64_ce.rs
index d8262b58..2809fd1d 100644
--- a/src/aead/aegis256/aarch64_ce.rs
+++ b/src/aead/aegis256/aarch64_ce.rs
@@ -4,18 +4,26 @@ use super::{BLOCK_SIZE, C0, C1, KEY_SIZE, NONCE_SIZE, TAG_SIZE};
#[inline]
/// # Safety
-///
-/// `bytes` must refer to a valid 16-byte block.
+/// Caller must ensure `neon` support is available.
unsafe fn load(bytes: &[u8; BLOCK_SIZE]) -> uint8x16_t {
- vld1q_u8(bytes.as_ptr())
+ // SAFETY: the caller guarantees NEON; `bytes` provides 16 initialized bytes for this unaligned load.
+ unsafe { vld1q_u8(bytes.as_ptr()) }
}
#[inline]
/// # Safety
-///
-/// `out` must refer to a valid writable 16-byte block.
+/// Caller must ensure `neon` support is available.
unsafe fn store(v: uint8x16_t, out: &mut [u8; BLOCK_SIZE]) {
- vst1q_u8(out.as_mut_ptr(), v);
+ // SAFETY: the caller guarantees NEON; `out` provides exclusive access to 16 bytes for this unaligned store.
+ unsafe { vst1q_u8(out.as_mut_ptr(), v) };
+}
+
+#[inline(always)]
+fn prefetch_read_l1(ptr: *const u8) {
+ // SAFETY: `prfm` is an AArch64 cache hint that does not dereference `ptr` or alter architectural state.
+ unsafe {
+ core::arch::asm!("prfm pldl1keep, [{ptr}]", ptr = in(reg) ptr, options(nostack, preserves_flags));
+ }
}
// ── Register-based helpers ──────────────────────────────────────────────
@@ -27,7 +35,6 @@ unsafe fn store(v: uint8x16_t, out: &mut [u8; BLOCK_SIZE]) {
#[target_feature(enable = "aes,neon")]
#[inline]
-#[allow(clippy::too_many_arguments)]
/// # Safety
///
/// Caller must ensure `aes` and `neon` support is available and all register
@@ -40,8 +47,8 @@ unsafe fn update_regs(
s4: &mut uint8x16_t,
s5: &mut uint8x16_t,
m: uint8x16_t,
- zero: uint8x16_t,
) {
+ let zero = vdupq_n_u8(0);
let tmp = *s5;
#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
@@ -68,10 +75,10 @@ unsafe fn update_regs(
#[inline]
/// # Safety
///
-/// The provided registers must come from a valid AEGIS-256 state on an
-/// `aes` + `neon` capable CPU.
+/// Caller must ensure `neon` support is available.
unsafe fn keystream_regs(s1: uint8x16_t, s2: uint8x16_t, s3: uint8x16_t, s4: uint8x16_t, s5: uint8x16_t) -> uint8x16_t {
- veorq_u8(veorq_u8(s1, s4), veorq_u8(s5, vandq_u8(s2, s3)))
+ // SAFETY: the caller guarantees NEON support for these register-only operations.
+ unsafe { veorq_u8(veorq_u8(s1, s4), veorq_u8(s5, vandq_u8(s2, s3))) }
}
// ── Fused encrypt/decrypt ───────────────────────────────────────────────
@@ -88,41 +95,54 @@ pub(super) unsafe fn encrypt_fused(
) -> [u8; TAG_SIZE] {
let (kh0, kh1) = super::split_halves(key);
let (nh0, nh1) = super::split_halves(nonce);
- let k0 = load(kh0);
- let k1 = load(kh1);
- let n0 = load(nh0);
- let n1 = load(nh1);
- let c0 = load(&C0);
- let c1 = load(&C1);
+ // SAFETY: the entrypoint enables NEON; `kh0` is a complete initialized block.
+ let k0 = unsafe { load(kh0) };
+ // SAFETY: the entrypoint enables NEON; `kh1` is a complete initialized block.
+ let k1 = unsafe { load(kh1) };
+ // SAFETY: the entrypoint enables NEON; `nh0` is a complete initialized block.
+ let n0 = unsafe { load(nh0) };
+ // SAFETY: the entrypoint enables NEON; `nh1` is a complete initialized block.
+ let n1 = unsafe { load(nh1) };
+ // SAFETY: the entrypoint enables NEON; `C0` is a complete initialized block.
+ let c0 = unsafe { load(&C0) };
+ // SAFETY: the entrypoint enables NEON; `C1` is a complete initialized block.
+ let c1 = unsafe { load(&C1) };
let k0_xor_n0 = veorq_u8(k0, n0);
let k1_xor_n1 = veorq_u8(k1, n1);
- let zero = vdupq_n_u8(0);
let (mut s0, mut s1, mut s2, mut s3, mut s4, mut s5) =
(k0_xor_n0, k1_xor_n1, c1, c0, veorq_u8(k0, c0), veorq_u8(k1, c1));
for _ in 0..4 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0, zero);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1, zero);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0, zero);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1, zero);
+ // SAFETY: the entrypoint establishes AES and NEON for these valid state registers.
+ unsafe {
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1);
+ }
}
let mut offset = 0usize;
while offset.strict_add(BLOCK_SIZE) <= aad.len() {
- update_regs(
- &mut s0,
- &mut s1,
- &mut s2,
- &mut s3,
- &mut s4,
- &mut s5,
- vld1q_u8(aad.as_ptr().add(offset)),
- zero,
- );
+ // SAFETY: the loop bound provides a complete readable block at `offset`; the entrypoint enables AES and NEON.
+ unsafe {
+ update_regs(
+ &mut s0,
+ &mut s1,
+ &mut s2,
+ &mut s3,
+ &mut s4,
+ &mut s5,
+ vld1q_u8(aad.as_ptr().add(offset)),
+ );
+ }
offset = offset.strict_add(BLOCK_SIZE);
}
if offset < aad.len() {
let mut pad = [0u8; BLOCK_SIZE];
pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load(&pad), zero);
+ // SAFETY: `pad` is a complete initialized block, and the entrypoint enables AES and NEON.
+ unsafe {
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load(&pad));
+ }
}
let msg_len = buffer.len();
let ptr = buffer.as_mut_ptr();
@@ -131,55 +151,71 @@ pub(super) unsafe fn encrypt_fused(
let four_blocks = BLOCK_SIZE.strict_mul(4);
let two_blocks = BLOCK_SIZE.strict_mul(2);
while offset.strict_add(four_blocks) <= len {
- core::arch::asm!("prfm pldl1keep, [{ptr}]", ptr = in(reg) ptr.add(offset.strict_add(192)), options(nostack, preserves_flags));
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let xi_a = vld1q_u8(ptr.add(offset));
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a, zero);
- vst1q_u8(ptr.add(offset), veorq_u8(xi_a, z_a));
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let xi_b = vld1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)));
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b, zero);
- vst1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)), veorq_u8(xi_b, z_b));
- let z_c = keystream_regs(s1, s2, s3, s4, s5);
- let xi_c = vld1q_u8(ptr.add(offset.strict_add(two_blocks)));
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_c, zero);
- vst1q_u8(ptr.add(offset.strict_add(two_blocks)), veorq_u8(xi_c, z_c));
- let z_d = keystream_regs(s1, s2, s3, s4, s5);
- let xi_d = vld1q_u8(ptr.add(offset.strict_add(two_blocks.strict_add(BLOCK_SIZE))));
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_d, zero);
- vst1q_u8(
- ptr.add(offset.strict_add(two_blocks.strict_add(BLOCK_SIZE))),
- veorq_u8(xi_d, z_d),
- );
+ prefetch_read_l1(ptr.wrapping_add(offset.strict_add(192)));
+ // SAFETY: the loop bound places all four 16-byte lanes inside the exclusively borrowed buffer. The entrypoint
+ // enables AES and NEON, and each plaintext lane is loaded before its ciphertext overwrites the same bytes.
+ unsafe {
+ let z_a = keystream_regs(s1, s2, s3, s4, s5);
+ let xi_a = vld1q_u8(ptr.add(offset));
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
+ vst1q_u8(ptr.add(offset), veorq_u8(xi_a, z_a));
+ let z_b = keystream_regs(s1, s2, s3, s4, s5);
+ let xi_b = vld1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)));
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
+ vst1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)), veorq_u8(xi_b, z_b));
+ let z_c = keystream_regs(s1, s2, s3, s4, s5);
+ let xi_c = vld1q_u8(ptr.add(offset.strict_add(two_blocks)));
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_c);
+ vst1q_u8(ptr.add(offset.strict_add(two_blocks)), veorq_u8(xi_c, z_c));
+ let z_d = keystream_regs(s1, s2, s3, s4, s5);
+ let xi_d = vld1q_u8(ptr.add(offset.strict_add(two_blocks.strict_add(BLOCK_SIZE))));
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_d);
+ vst1q_u8(
+ ptr.add(offset.strict_add(two_blocks.strict_add(BLOCK_SIZE))),
+ veorq_u8(xi_d, z_d),
+ );
+ }
offset = offset.strict_add(four_blocks);
}
if offset.strict_add(two_blocks) <= len {
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let xi_a = vld1q_u8(ptr.add(offset));
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a, zero);
- vst1q_u8(ptr.add(offset), veorq_u8(xi_a, z_a));
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let xi_b = vld1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)));
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b, zero);
- vst1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)), veorq_u8(xi_b, z_b));
+ // SAFETY: the branch bound places both lanes inside the exclusively borrowed buffer; AES and NEON are enabled.
+ unsafe {
+ let z_a = keystream_regs(s1, s2, s3, s4, s5);
+ let xi_a = vld1q_u8(ptr.add(offset));
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
+ vst1q_u8(ptr.add(offset), veorq_u8(xi_a, z_a));
+ let z_b = keystream_regs(s1, s2, s3, s4, s5);
+ let xi_b = vld1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)));
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
+ vst1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)), veorq_u8(xi_b, z_b));
+ }
offset = offset.strict_add(two_blocks);
}
if offset.strict_add(BLOCK_SIZE) <= len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let xi = vld1q_u8(ptr.add(offset));
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi, zero);
- vst1q_u8(ptr.add(offset), veorq_u8(xi, z));
+ // SAFETY: the branch bound provides one complete writable lane in the buffer; AES and NEON are enabled.
+ unsafe {
+ let z = keystream_regs(s1, s2, s3, s4, s5);
+ let xi = vld1q_u8(ptr.add(offset));
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
+ vst1q_u8(ptr.add(offset), veorq_u8(xi, z));
+ }
offset = offset.strict_add(BLOCK_SIZE);
}
if offset < len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
+ // SAFETY: the entrypoint enables NEON for the valid state registers.
+ let z = unsafe { keystream_regs(s1, s2, s3, s4, s5) };
let tail_len = len.strict_sub(offset);
let mut pad = [0u8; BLOCK_SIZE];
pad[..tail_len].copy_from_slice(&buffer[offset..]);
- let xi = load(&pad);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi, zero);
+ // SAFETY: `pad` is a complete initialized block, and the entrypoint enables NEON.
+ let xi = unsafe { load(&pad) };
+ // SAFETY: the entrypoint establishes AES and NEON for these valid state registers.
+ unsafe {
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
+ }
let mut ct_bytes = [0u8; BLOCK_SIZE];
- store(veorq_u8(xi, z), &mut ct_bytes);
+ // SAFETY: `ct_bytes` is a complete writable block, and the entrypoint enables NEON.
+ unsafe { store(veorq_u8(xi, z), &mut ct_bytes) };
buffer[offset..].copy_from_slice(&ct_bytes[..tail_len]);
}
let ad_bits = (aad.len() as u64).strict_mul(8);
@@ -187,13 +223,18 @@ pub(super) unsafe fn encrypt_fused(
let mut len_bytes = [0u8; BLOCK_SIZE];
len_bytes[..8].copy_from_slice(&ad_bits.to_le_bytes());
len_bytes[8..].copy_from_slice(&msg_bits.to_le_bytes());
- let t = veorq_u8(s3, load(&len_bytes));
+ // SAFETY: `len_bytes` is a complete initialized block, and the entrypoint enables NEON.
+ let t = unsafe { veorq_u8(s3, load(&len_bytes)) };
for _ in 0..7 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t, zero);
+ // SAFETY: the entrypoint establishes AES and NEON for these valid state registers.
+ unsafe {
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t);
+ }
}
let tag_vec = veorq_u8(veorq_u8(veorq_u8(s0, s1), veorq_u8(s2, s3)), veorq_u8(s4, s5));
let mut tag = [0u8; TAG_SIZE];
- store(tag_vec, &mut tag);
+ // SAFETY: `tag` is a complete writable block, and the entrypoint enables NEON.
+ unsafe { store(tag_vec, &mut tag) };
tag
}
@@ -209,41 +250,54 @@ pub(super) unsafe fn decrypt_fused(
) -> [u8; TAG_SIZE] {
let (kh0, kh1) = super::split_halves(key);
let (nh0, nh1) = super::split_halves(nonce);
- let k0 = load(kh0);
- let k1 = load(kh1);
- let n0 = load(nh0);
- let n1 = load(nh1);
- let c0 = load(&C0);
- let c1 = load(&C1);
+ // SAFETY: the entrypoint enables NEON; `kh0` is a complete initialized block.
+ let k0 = unsafe { load(kh0) };
+ // SAFETY: the entrypoint enables NEON; `kh1` is a complete initialized block.
+ let k1 = unsafe { load(kh1) };
+ // SAFETY: the entrypoint enables NEON; `nh0` is a complete initialized block.
+ let n0 = unsafe { load(nh0) };
+ // SAFETY: the entrypoint enables NEON; `nh1` is a complete initialized block.
+ let n1 = unsafe { load(nh1) };
+ // SAFETY: the entrypoint enables NEON; `C0` is a complete initialized block.
+ let c0 = unsafe { load(&C0) };
+ // SAFETY: the entrypoint enables NEON; `C1` is a complete initialized block.
+ let c1 = unsafe { load(&C1) };
let k0_xor_n0 = veorq_u8(k0, n0);
let k1_xor_n1 = veorq_u8(k1, n1);
- let zero = vdupq_n_u8(0);
let (mut s0, mut s1, mut s2, mut s3, mut s4, mut s5) =
(k0_xor_n0, k1_xor_n1, c1, c0, veorq_u8(k0, c0), veorq_u8(k1, c1));
for _ in 0..4 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0, zero);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1, zero);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0, zero);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1, zero);
+ // SAFETY: the entrypoint establishes AES and NEON for these valid state registers.
+ unsafe {
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1);
+ }
}
let mut offset = 0usize;
while offset.strict_add(BLOCK_SIZE) <= aad.len() {
- update_regs(
- &mut s0,
- &mut s1,
- &mut s2,
- &mut s3,
- &mut s4,
- &mut s5,
- vld1q_u8(aad.as_ptr().add(offset)),
- zero,
- );
+ // SAFETY: the loop bound provides a complete readable block at `offset`; the entrypoint enables AES and NEON.
+ unsafe {
+ update_regs(
+ &mut s0,
+ &mut s1,
+ &mut s2,
+ &mut s3,
+ &mut s4,
+ &mut s5,
+ vld1q_u8(aad.as_ptr().add(offset)),
+ );
+ }
offset = offset.strict_add(BLOCK_SIZE);
}
if offset < aad.len() {
let mut pad = [0u8; BLOCK_SIZE];
pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load(&pad), zero);
+ // SAFETY: `pad` is a complete initialized block, and the entrypoint enables AES and NEON.
+ unsafe {
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load(&pad));
+ }
}
let ct_len = buffer.len();
let ptr = buffer.as_mut_ptr();
@@ -252,71 +306,77 @@ pub(super) unsafe fn decrypt_fused(
let four_blocks = BLOCK_SIZE.strict_mul(4);
let two_blocks = BLOCK_SIZE.strict_mul(2);
while offset.strict_add(four_blocks) <= len {
- core::arch::asm!("prfm pldl1keep, [{ptr}]", ptr = in(reg) ptr.add(offset.strict_add(192)), options(nostack, preserves_flags));
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let ci_a = vld1q_u8(ptr.add(offset));
- let xi_a = veorq_u8(ci_a, z_a);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a, zero);
- vst1q_u8(ptr.add(offset), xi_a);
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let ci_b = vld1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)));
- let xi_b = veorq_u8(ci_b, z_b);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b, zero);
- vst1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)), xi_b);
- let z_c = keystream_regs(s1, s2, s3, s4, s5);
- let ci_c = vld1q_u8(ptr.add(offset.strict_add(two_blocks)));
- let xi_c = veorq_u8(ci_c, z_c);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_c, zero);
- vst1q_u8(ptr.add(offset.strict_add(two_blocks)), xi_c);
- let z_d = keystream_regs(s1, s2, s3, s4, s5);
- let ci_d = vld1q_u8(ptr.add(offset.strict_add(two_blocks.strict_add(BLOCK_SIZE))));
- let xi_d = veorq_u8(ci_d, z_d);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_d, zero);
- vst1q_u8(ptr.add(offset.strict_add(two_blocks.strict_add(BLOCK_SIZE))), xi_d);
+ prefetch_read_l1(ptr.wrapping_add(offset.strict_add(192)));
+ // SAFETY: the loop bound places all four 16-byte lanes inside the exclusively borrowed buffer. The entrypoint
+ // enables AES and NEON, and each ciphertext lane is loaded before its plaintext overwrites the same bytes.
+ unsafe {
+ let z_a = keystream_regs(s1, s2, s3, s4, s5);
+ let ci_a = vld1q_u8(ptr.add(offset));
+ let xi_a = veorq_u8(ci_a, z_a);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
+ vst1q_u8(ptr.add(offset), xi_a);
+ let z_b = keystream_regs(s1, s2, s3, s4, s5);
+ let ci_b = vld1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)));
+ let xi_b = veorq_u8(ci_b, z_b);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
+ vst1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)), xi_b);
+ let z_c = keystream_regs(s1, s2, s3, s4, s5);
+ let ci_c = vld1q_u8(ptr.add(offset.strict_add(two_blocks)));
+ let xi_c = veorq_u8(ci_c, z_c);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_c);
+ vst1q_u8(ptr.add(offset.strict_add(two_blocks)), xi_c);
+ let z_d = keystream_regs(s1, s2, s3, s4, s5);
+ let ci_d = vld1q_u8(ptr.add(offset.strict_add(two_blocks.strict_add(BLOCK_SIZE))));
+ let xi_d = veorq_u8(ci_d, z_d);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_d);
+ vst1q_u8(ptr.add(offset.strict_add(two_blocks.strict_add(BLOCK_SIZE))), xi_d);
+ }
offset = offset.strict_add(four_blocks);
}
if offset.strict_add(two_blocks) <= len {
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let ci_a = vld1q_u8(ptr.add(offset));
- let xi_a = veorq_u8(ci_a, z_a);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a, zero);
- vst1q_u8(ptr.add(offset), xi_a);
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let ci_b = vld1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)));
- let xi_b = veorq_u8(ci_b, z_b);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b, zero);
- vst1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)), xi_b);
+ // SAFETY: the branch bound places both lanes inside the exclusively borrowed buffer; AES and NEON are enabled.
+ unsafe {
+ let z_a = keystream_regs(s1, s2, s3, s4, s5);
+ let ci_a = vld1q_u8(ptr.add(offset));
+ let xi_a = veorq_u8(ci_a, z_a);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
+ vst1q_u8(ptr.add(offset), xi_a);
+ let z_b = keystream_regs(s1, s2, s3, s4, s5);
+ let ci_b = vld1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)));
+ let xi_b = veorq_u8(ci_b, z_b);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
+ vst1q_u8(ptr.add(offset.strict_add(BLOCK_SIZE)), xi_b);
+ }
offset = offset.strict_add(two_blocks);
}
if offset.strict_add(BLOCK_SIZE) <= len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let ci = vld1q_u8(ptr.add(offset));
- let xi = veorq_u8(ci, z);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi, zero);
- vst1q_u8(ptr.add(offset), xi);
+ // SAFETY: the branch bound provides one complete writable lane in the buffer; AES and NEON are enabled.
+ unsafe {
+ let z = keystream_regs(s1, s2, s3, s4, s5);
+ let ci = vld1q_u8(ptr.add(offset));
+ let xi = veorq_u8(ci, z);
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
+ vst1q_u8(ptr.add(offset), xi);
+ }
offset = offset.strict_add(BLOCK_SIZE);
}
if offset < len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
+ // SAFETY: the entrypoint enables NEON for the valid state registers.
+ let z = unsafe { keystream_regs(s1, s2, s3, s4, s5) };
let tail_len = len.strict_sub(offset);
let mut pad = [0u8; BLOCK_SIZE];
pad[..tail_len].copy_from_slice(&buffer[offset..]);
let mut z_bytes = [0u8; BLOCK_SIZE];
- store(z, &mut z_bytes);
+ // SAFETY: `z_bytes` is a complete writable block, and the entrypoint enables NEON.
+ unsafe { store(z, &mut z_bytes) };
let mut pt_pad = [0u8; BLOCK_SIZE];
for i in 0..tail_len {
pt_pad[i] = pad[i] ^ z_bytes[i];
}
- update_regs(
- &mut s0,
- &mut s1,
- &mut s2,
- &mut s3,
- &mut s4,
- &mut s5,
- load(&pt_pad),
- zero,
- );
+ // SAFETY: `pt_pad` is a complete initialized block, and the entrypoint enables AES and NEON.
+ unsafe {
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load(&pt_pad));
+ }
buffer[offset..].copy_from_slice(&pt_pad[..tail_len]);
}
let ad_bits = (aad.len() as u64).strict_mul(8);
@@ -324,12 +384,17 @@ pub(super) unsafe fn decrypt_fused(
let mut len_bytes = [0u8; BLOCK_SIZE];
len_bytes[..8].copy_from_slice(&ad_bits.to_le_bytes());
len_bytes[8..].copy_from_slice(&ct_bits.to_le_bytes());
- let t = veorq_u8(s3, load(&len_bytes));
+ // SAFETY: `len_bytes` is a complete initialized block, and the entrypoint enables NEON.
+ let t = unsafe { veorq_u8(s3, load(&len_bytes)) };
for _ in 0..7 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t, zero);
+ // SAFETY: the entrypoint establishes AES and NEON for these valid state registers.
+ unsafe {
+ update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t);
+ }
}
let tag_vec = veorq_u8(veorq_u8(veorq_u8(s0, s1), veorq_u8(s2, s3)), veorq_u8(s4, s5));
let mut tag = [0u8; TAG_SIZE];
- store(tag_vec, &mut tag);
+ // SAFETY: `tag` is a complete writable block, and the entrypoint enables NEON.
+ unsafe { store(tag_vec, &mut tag) };
tag
}
diff --git a/src/aead/aegis256/powerpc64_ppc.rs b/src/aead/aegis256/powerpc64_ppc.rs
index d6d2c1f0..ab3da5af 100644
--- a/src/aead/aegis256/powerpc64_ppc.rs
+++ b/src/aead/aegis256/powerpc64_ppc.rs
@@ -25,8 +25,8 @@ fn store_be(v: i64x2, out: &mut [u8; 16]) {
let arr = v.to_array();
#[cfg(target_endian = "little")]
{
- let hi = (arr[1] as u64).to_be_bytes();
- let lo = (arr[0] as u64).to_be_bytes();
+ let hi = u64::from_ne_bytes(arr[1].to_ne_bytes()).to_be_bytes();
+ let lo = u64::from_ne_bytes(arr[0].to_ne_bytes()).to_be_bytes();
out[0..8].copy_from_slice(&hi);
out[8..16].copy_from_slice(&lo);
}
@@ -43,18 +43,19 @@ fn store_be(v: i64x2, out: &mut [u8; 16]) {
/// Caller must ensure POWER8 vector crypto support is available.
unsafe fn aes_round(block: i64x2, round_key: i64x2) -> i64x2 {
let out: i64x2;
- asm!(
- "vcipher {out}, {block}, {rk}",
- out = lateout(vreg) out,
- block = in(vreg) block,
- rk = in(vreg) round_key,
- options(nomem, nostack),
- );
+ // SAFETY: the caller guarantees POWER8 vector crypto support; the instruction only reads its register operands.
+ unsafe {
+ asm!(
+ "vcipher {out}, {block}, {rk}",
+ out = lateout(vreg) out,
+ block = in(vreg) block,
+ rk = in(vreg) round_key,
+ options(nomem, nostack),
+ )
+ };
out
}
-// ── Register-based helpers ──────────────────────────────────────────────
-
#[inline(always)]
fn xor_vec(a: i64x2, b: i64x2) -> i64x2 {
let aa = a.to_array();
@@ -69,27 +70,44 @@ fn and_vec(a: i64x2, b: i64x2) -> i64x2 {
i64x2::from_array([aa[0] & ba[0], aa[1] & ba[1]])
}
-#[inline(always)]
-/// # Safety
-///
-/// Caller must ensure POWER8 vector crypto support is available and all state
-/// registers belong to a valid AEGIS-256 state.
-unsafe fn update_regs(
- s0: &mut i64x2,
- s1: &mut i64x2,
- s2: &mut i64x2,
- s3: &mut i64x2,
- s4: &mut i64x2,
- s5: &mut i64x2,
- m: i64x2,
-) {
- let tmp = *s5;
- *s5 = aes_round(*s4, *s5);
- *s4 = aes_round(*s3, *s4);
- *s3 = aes_round(*s2, *s3);
- *s2 = aes_round(*s1, *s2);
- *s1 = aes_round(*s0, *s1);
- *s0 = xor_vec(aes_round(tmp, *s0), m);
+type StateMut<'a> = (
+ &'a mut i64x2,
+ &'a mut i64x2,
+ &'a mut i64x2,
+ &'a mut i64x2,
+ &'a mut i64x2,
+ &'a mut i64x2,
+);
+
+#[derive(Clone, Copy)]
+struct Power8 {
+ _private: (),
+}
+
+impl Power8 {
+ /// Creates a POWER8 vector-crypto capability token.
+ ///
+ /// # Safety
+ ///
+ /// The current CPU must support POWER8 vector crypto for the token's entire lifetime.
+ #[inline]
+ unsafe fn new() -> Self {
+ Self { _private: () }
+ }
+
+ #[inline(always)]
+ fn update(self, (s0, s1, s2, s3, s4, s5): StateMut<'_>, message: i64x2) {
+ let old_s5 = *s5;
+ // SAFETY: this module constructs `Power8` only through `new`, whose caller guarantees POWER8 vector crypto.
+ unsafe {
+ *s5 = aes_round(*s4, *s5);
+ *s4 = aes_round(*s3, *s4);
+ *s3 = aes_round(*s2, *s3);
+ *s2 = aes_round(*s1, *s2);
+ *s1 = aes_round(*s0, *s1);
+ *s0 = xor_vec(aes_round(old_s5, *s0), message);
+ }
+ }
}
#[inline(always)]
@@ -97,8 +115,6 @@ fn keystream_regs(s1: i64x2, s2: i64x2, s3: i64x2, s4: i64x2, s5: i64x2) -> i64x
xor_vec(xor_vec(s1, s4), xor_vec(s5, and_vec(s2, s3)))
}
-// ── Fused encrypt/decrypt ───────────────────────────────────────────────
-
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
/// # Safety
///
@@ -109,6 +125,8 @@ pub(super) unsafe fn encrypt_fused(
aad: &[u8],
buffer: &mut [u8],
) -> [u8; TAG_SIZE] {
+ // SAFETY: this function's caller guarantees POWER8 vector crypto support.
+ let power8 = unsafe { Power8::new() };
let (kh0, kh1) = super::split_halves(key);
let (nh0, nh1) = super::split_halves(nonce);
let k0 = load_be(kh0);
@@ -122,118 +140,63 @@ pub(super) unsafe fn encrypt_fused(
let (mut s0, mut s1, mut s2, mut s3, mut s4, mut s5) =
(k0_xor_n0, k1_xor_n1, c1, c0, xor_vec(k0, c0), xor_vec(k1, c1));
for _ in 0..4 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k0);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k1);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k0_xor_n0);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k1_xor_n1);
}
- let mut offset = 0usize;
- while offset.strict_add(BLOCK_SIZE) <= aad.len() {
- let mut tmp = [0u8; 16];
- tmp.copy_from_slice(&aad[offset..offset.strict_add(BLOCK_SIZE)]);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load_be(&tmp));
- offset = offset.strict_add(BLOCK_SIZE);
+
+ let (aad_blocks, aad_tail) = aad.as_chunks::();
+ for block in aad_blocks {
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load_be(block));
}
- if offset < aad.len() {
+ if !aad_tail.is_empty() {
let mut pad = [0u8; BLOCK_SIZE];
- pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load_be(&pad));
+ let (pad_tail, _) = pad.split_at_mut(aad_tail.len());
+ pad_tail.copy_from_slice(aad_tail);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load_be(&pad));
}
+
let msg_len = buffer.len();
- let len = buffer.len();
- offset = 0;
- let four_blocks = BLOCK_SIZE.strict_mul(4);
- let two_blocks = BLOCK_SIZE.strict_mul(2);
- while offset.strict_add(four_blocks) <= len {
- // SAFETY: pointer arithmetic for dcbt prefetch; offset + 256 may exceed
- // the buffer but dcbt is a hint and never faults on POWER.
- asm!("dcbt 0, {ptr}", ptr = in(reg) buffer.as_ptr().add(offset.strict_add(256)), options(nostack));
- // block a
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let mut tmp_a = [0u8; 16];
- tmp_a.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
- let xi_a = load_be(&tmp_a);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
- store_be(xor_vec(xi_a, z_a), &mut tmp_a);
- buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_a);
- // block b
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let off_b = offset.strict_add(BLOCK_SIZE);
- let mut tmp_b = [0u8; 16];
- tmp_b.copy_from_slice(&buffer[off_b..off_b.strict_add(BLOCK_SIZE)]);
- let xi_b = load_be(&tmp_b);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
- store_be(xor_vec(xi_b, z_b), &mut tmp_b);
- buffer[off_b..off_b.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_b);
- // block c
- let z_c = keystream_regs(s1, s2, s3, s4, s5);
- let off_c = offset.strict_add(two_blocks);
- let mut tmp_c = [0u8; 16];
- tmp_c.copy_from_slice(&buffer[off_c..off_c.strict_add(BLOCK_SIZE)]);
- let xi_c = load_be(&tmp_c);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_c);
- store_be(xor_vec(xi_c, z_c), &mut tmp_c);
- buffer[off_c..off_c.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_c);
- // block d
- let z_d = keystream_regs(s1, s2, s3, s4, s5);
- let off_d = offset.strict_add(two_blocks.strict_add(BLOCK_SIZE));
- let mut tmp_d = [0u8; 16];
- tmp_d.copy_from_slice(&buffer[off_d..off_d.strict_add(BLOCK_SIZE)]);
- let xi_d = load_be(&tmp_d);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_d);
- store_be(xor_vec(xi_d, z_d), &mut tmp_d);
- buffer[off_d..off_d.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_d);
- offset = offset.strict_add(four_blocks);
- }
- if offset.strict_add(two_blocks) <= len {
- // block a
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let mut tmp_a = [0u8; 16];
- tmp_a.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
- let xi_a = load_be(&tmp_a);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
- store_be(xor_vec(xi_a, z_a), &mut tmp_a);
- buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_a);
- // block b
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let off_b = offset.strict_add(BLOCK_SIZE);
- let mut tmp_b = [0u8; 16];
- tmp_b.copy_from_slice(&buffer[off_b..off_b.strict_add(BLOCK_SIZE)]);
- let xi_b = load_be(&tmp_b);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
- store_be(xor_vec(xi_b, z_b), &mut tmp_b);
- buffer[off_b..off_b.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_b);
- offset = offset.strict_add(two_blocks);
+ let (quads, remainder) = buffer.as_chunks_mut::<64>();
+ for quad in quads {
+ let (blocks, _) = quad.as_chunks_mut::();
+ for block in blocks {
+ let stream = keystream_regs(s1, s2, s3, s4, s5);
+ let plaintext = load_be(block);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ store_be(xor_vec(plaintext, stream), block);
+ }
}
- if offset.strict_add(BLOCK_SIZE) <= len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let mut tmp = [0u8; 16];
- tmp.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
- let xi = load_be(&tmp);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
- store_be(xor_vec(xi, z), &mut tmp);
- buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp);
- offset = offset.strict_add(BLOCK_SIZE);
+ let (blocks, tail) = remainder.as_chunks_mut::();
+ for block in blocks {
+ let stream = keystream_regs(s1, s2, s3, s4, s5);
+ let plaintext = load_be(block);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ store_be(xor_vec(plaintext, stream), block);
}
- if offset < len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let tail_len = len.strict_sub(offset);
+ if !tail.is_empty() {
+ let stream = keystream_regs(s1, s2, s3, s4, s5);
let mut pad = [0u8; BLOCK_SIZE];
- pad[..tail_len].copy_from_slice(&buffer[offset..]);
- let xi = load_be(&pad);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
- let mut ct_bytes = [0u8; BLOCK_SIZE];
- store_be(xor_vec(xi, z), &mut ct_bytes);
- buffer[offset..].copy_from_slice(&ct_bytes[..tail_len]);
+ let (pad_tail, _) = pad.split_at_mut(tail.len());
+ pad_tail.copy_from_slice(tail);
+ let plaintext = load_be(&pad);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ let mut ciphertext = [0u8; BLOCK_SIZE];
+ store_be(xor_vec(plaintext, stream), &mut ciphertext);
+ let (ciphertext_tail, _) = ciphertext.split_at(tail.len());
+ tail.copy_from_slice(ciphertext_tail);
}
+
let ad_bits = (aad.len() as u64).strict_mul(8);
let msg_bits = (msg_len as u64).strict_mul(8);
let mut len_bytes = [0u8; BLOCK_SIZE];
- len_bytes[..8].copy_from_slice(&ad_bits.to_le_bytes());
- len_bytes[8..].copy_from_slice(&msg_bits.to_le_bytes());
+ let (ad_len_bytes, msg_len_bytes) = len_bytes.split_at_mut(8);
+ ad_len_bytes.copy_from_slice(&ad_bits.to_le_bytes());
+ msg_len_bytes.copy_from_slice(&msg_bits.to_le_bytes());
let t = xor_vec(s3, load_be(&len_bytes));
for _ in 0..7 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), t);
}
let tag_vec = xor_vec(xor_vec(xor_vec(s0, s1), xor_vec(s2, s3)), xor_vec(s4, s5));
let mut tag = [0u8; TAG_SIZE];
@@ -251,6 +214,8 @@ pub(super) unsafe fn decrypt_fused(
aad: &[u8],
buffer: &mut [u8],
) -> [u8; TAG_SIZE] {
+ // SAFETY: this function's caller guarantees POWER8 vector crypto support.
+ let power8 = unsafe { Power8::new() };
let (kh0, kh1) = super::split_halves(key);
let (nh0, nh1) = super::split_halves(nonce);
let k0 = load_be(kh0);
@@ -264,121 +229,66 @@ pub(super) unsafe fn decrypt_fused(
let (mut s0, mut s1, mut s2, mut s3, mut s4, mut s5) =
(k0_xor_n0, k1_xor_n1, c1, c0, xor_vec(k0, c0), xor_vec(k1, c1));
for _ in 0..4 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k0);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k1);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k0_xor_n0);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k1_xor_n1);
}
- let mut offset = 0usize;
- while offset.strict_add(BLOCK_SIZE) <= aad.len() {
- let mut tmp = [0u8; 16];
- tmp.copy_from_slice(&aad[offset..offset.strict_add(BLOCK_SIZE)]);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load_be(&tmp));
- offset = offset.strict_add(BLOCK_SIZE);
+
+ let (aad_blocks, aad_tail) = aad.as_chunks::();
+ for block in aad_blocks {
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load_be(block));
}
- if offset < aad.len() {
+ if !aad_tail.is_empty() {
let mut pad = [0u8; BLOCK_SIZE];
- pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load_be(&pad));
+ let (pad_tail, _) = pad.split_at_mut(aad_tail.len());
+ pad_tail.copy_from_slice(aad_tail);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load_be(&pad));
}
+
let ct_len = buffer.len();
- let len = buffer.len();
- offset = 0;
- let four_blocks = BLOCK_SIZE.strict_mul(4);
- let two_blocks = BLOCK_SIZE.strict_mul(2);
- while offset.strict_add(four_blocks) <= len {
- // SAFETY: pointer arithmetic for dcbt prefetch; offset + 256 may exceed
- // the buffer but dcbt is a hint and never faults on POWER.
- asm!("dcbt 0, {ptr}", ptr = in(reg) buffer.as_ptr().add(offset.strict_add(256)), options(nostack));
- // block a
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let mut tmp_a = [0u8; 16];
- tmp_a.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
- let xi_a = xor_vec(load_be(&tmp_a), z_a);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
- store_be(xi_a, &mut tmp_a);
- buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_a);
- // block b
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let off_b = offset.strict_add(BLOCK_SIZE);
- let mut tmp_b = [0u8; 16];
- tmp_b.copy_from_slice(&buffer[off_b..off_b.strict_add(BLOCK_SIZE)]);
- let xi_b = xor_vec(load_be(&tmp_b), z_b);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
- store_be(xi_b, &mut tmp_b);
- buffer[off_b..off_b.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_b);
- // block c
- let z_c = keystream_regs(s1, s2, s3, s4, s5);
- let off_c = offset.strict_add(two_blocks);
- let mut tmp_c = [0u8; 16];
- tmp_c.copy_from_slice(&buffer[off_c..off_c.strict_add(BLOCK_SIZE)]);
- let xi_c = xor_vec(load_be(&tmp_c), z_c);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_c);
- store_be(xi_c, &mut tmp_c);
- buffer[off_c..off_c.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_c);
- // block d
- let z_d = keystream_regs(s1, s2, s3, s4, s5);
- let off_d = offset.strict_add(two_blocks.strict_add(BLOCK_SIZE));
- let mut tmp_d = [0u8; 16];
- tmp_d.copy_from_slice(&buffer[off_d..off_d.strict_add(BLOCK_SIZE)]);
- let xi_d = xor_vec(load_be(&tmp_d), z_d);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_d);
- store_be(xi_d, &mut tmp_d);
- buffer[off_d..off_d.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_d);
- offset = offset.strict_add(four_blocks);
- }
- if offset.strict_add(two_blocks) <= len {
- // block a
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let mut tmp_a = [0u8; 16];
- tmp_a.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
- let xi_a = xor_vec(load_be(&tmp_a), z_a);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
- store_be(xi_a, &mut tmp_a);
- buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_a);
- // block b
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let off_b = offset.strict_add(BLOCK_SIZE);
- let mut tmp_b = [0u8; 16];
- tmp_b.copy_from_slice(&buffer[off_b..off_b.strict_add(BLOCK_SIZE)]);
- let xi_b = xor_vec(load_be(&tmp_b), z_b);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
- store_be(xi_b, &mut tmp_b);
- buffer[off_b..off_b.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp_b);
- offset = offset.strict_add(two_blocks);
+ let (quads, remainder) = buffer.as_chunks_mut::<64>();
+ for quad in quads {
+ let (blocks, _) = quad.as_chunks_mut::();
+ for block in blocks {
+ let stream = keystream_regs(s1, s2, s3, s4, s5);
+ let plaintext = xor_vec(load_be(block), stream);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ store_be(plaintext, block);
+ }
}
- if offset.strict_add(BLOCK_SIZE) <= len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let mut tmp = [0u8; 16];
- tmp.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
- let xi = xor_vec(load_be(&tmp), z);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
- store_be(xi, &mut tmp);
- buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp);
- offset = offset.strict_add(BLOCK_SIZE);
+ let (blocks, tail) = remainder.as_chunks_mut::();
+ for block in blocks {
+ let stream = keystream_regs(s1, s2, s3, s4, s5);
+ let plaintext = xor_vec(load_be(block), stream);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ store_be(plaintext, block);
}
- if offset < len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let tail_len = len.strict_sub(offset);
- let mut pad = [0u8; BLOCK_SIZE];
- pad[..tail_len].copy_from_slice(&buffer[offset..]);
- let mut z_bytes = [0u8; BLOCK_SIZE];
- store_be(z, &mut z_bytes);
- let mut pt_pad = [0u8; BLOCK_SIZE];
- for i in 0..tail_len {
- pt_pad[i] = pad[i] ^ z_bytes[i];
+ if !tail.is_empty() {
+ let stream = keystream_regs(s1, s2, s3, s4, s5);
+ let mut stream_bytes = [0u8; BLOCK_SIZE];
+ store_be(stream, &mut stream_bytes);
+ let mut plaintext = [0u8; BLOCK_SIZE];
+ for ((out, ciphertext), mask) in plaintext.iter_mut().zip(tail.iter()).zip(stream_bytes) {
+ *out = *ciphertext ^ mask;
}
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load_be(&pt_pad));
- buffer[offset..].copy_from_slice(&pt_pad[..tail_len]);
+ power8.update(
+ (&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5),
+ load_be(&plaintext),
+ );
+ let (plaintext_tail, _) = plaintext.split_at(tail.len());
+ tail.copy_from_slice(plaintext_tail);
}
+
let ad_bits = (aad.len() as u64).strict_mul(8);
let ct_bits = (ct_len as u64).strict_mul(8);
let mut len_bytes = [0u8; BLOCK_SIZE];
- len_bytes[..8].copy_from_slice(&ad_bits.to_le_bytes());
- len_bytes[8..].copy_from_slice(&ct_bits.to_le_bytes());
+ let (ad_len_bytes, ct_len_bytes) = len_bytes.split_at_mut(8);
+ ad_len_bytes.copy_from_slice(&ad_bits.to_le_bytes());
+ ct_len_bytes.copy_from_slice(&ct_bits.to_le_bytes());
let t = xor_vec(s3, load_be(&len_bytes));
for _ in 0..7 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t);
+ power8.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), t);
}
let tag_vec = xor_vec(xor_vec(xor_vec(s0, s1), xor_vec(s2, s3)), xor_vec(s4, s5));
let mut tag = [0u8; TAG_SIZE];
diff --git a/src/aead/aegis256/riscv64_vperm.rs b/src/aead/aegis256/riscv64_vperm.rs
deleted file mode 100644
index 4a7e4289..00000000
--- a/src/aead/aegis256/riscv64_vperm.rs
+++ /dev/null
@@ -1,368 +0,0 @@
-use core::arch::asm;
-
-use super::{BLOCK_SIZE, Block, C0, C1, KEY_SIZE, NONCE_SIZE, TAG_SIZE, and_block, split_halves, xor_block};
-use crate::aead::aes_round::{
- AES_AFFINE, MC_ROT1, MC_ROT2, VPERM_INV_HI, VPERM_INV_LO, VPERM_IPT_HI, VPERM_IPT_LO, VPERM_SBOT, VPERM_SBOU,
- VPERM_SR, XTIME_REDUCE,
-};
-
-type State = [Block; 6];
-
-/// Precomputed Hamburg vperm table block — packed contiguously for
-/// offset-based vector loads in the asm block.
-#[repr(C, align(16))]
-struct VpermTables {
- ipt_lo: [u8; 16], // offset 0
- ipt_hi: [u8; 16], // offset 16
- inv_lo: [u8; 16], // offset 32
- inv_hi: [u8; 16], // offset 48
- sbou: [u8; 16], // offset 64
- sbot: [u8; 16], // offset 80
- sr_perm: [u8; 16], // offset 96
- mc_rot1: [u8; 16], // offset 112
- mc_rot2: [u8; 16], // offset 128
- affine: [u8; 16], // offset 144
- xtime: [u8; 16], // offset 160
-}
-
-impl VpermTables {
- #[inline(always)]
- fn load() -> Self {
- Self {
- ipt_lo: VPERM_IPT_LO,
- ipt_hi: VPERM_IPT_HI,
- inv_lo: VPERM_INV_LO,
- inv_hi: VPERM_INV_HI,
- sbou: VPERM_SBOU,
- sbot: VPERM_SBOT,
- sr_perm: VPERM_SR,
- mc_rot1: MC_ROT1,
- mc_rot2: MC_ROT2,
- affine: [AES_AFFINE; 16],
- xtime: [XTIME_REDUCE; 16],
- }
- }
-}
-
-/// Single AES round via Hamburg vperm on RISC-V V: SubBytes + ShiftRows +
-/// MixColumns + AddRoundKey. Uses `vrgather.vv` for all S-box nibble lookups.
-///
-/// # Safety
-/// Requires the RISC-V V extension.
-#[target_feature(enable = "v")]
-#[inline]
-unsafe fn aes_round(block: &Block, round_key: &Block, tables: &VpermTables) -> Block {
- let mut out = [0u8; BLOCK_SIZE];
-
- // SAFETY: Caller guarantees RISC-V V extension is available.
- // The asm block loads the state, tables, and round key from memory,
- // performs all computation in vector registers, and stores the result.
- // All vrgather indices are masked to 0-15 before lookup — no secret-
- // dependent memory access.
- unsafe {
- asm!(
- // ── Setup ──────────────────────────────────────────────────────
- "vsetivli zero, 16, e8, m1, ta, ma",
-
- // Load tables from the VpermTables struct (contiguous, 16B each).
- "vle8.v v2, ({tbl})", // IPT_LO (offset 0)
- "addi {tmp}, {tbl}, 16",
- "vle8.v v3, ({tmp})", // IPT_HI (offset 16)
- "addi {tmp}, {tbl}, 32",
- "vle8.v v4, ({tmp})", // INV_LO (offset 32)
- "addi {tmp}, {tbl}, 48",
- "vle8.v v5, ({tmp})", // INV_HI (offset 48)
- "addi {tmp}, {tbl}, 64",
- "vle8.v v6, ({tmp})", // SBOU (offset 64)
- "addi {tmp}, {tbl}, 80",
- "vle8.v v7, ({tmp})", // SBOT (offset 80)
- "addi {tmp}, {tbl}, 96",
- "vle8.v v8, ({tmp})", // SR_PERM (offset 96)
- "addi {tmp}, {tbl}, 112",
- "vle8.v v9, ({tmp})", // MC_ROT1 (offset 112)
- "addi {tmp}, {tbl}, 128",
- "vle8.v v10, ({tmp})", // MC_ROT2 (offset 128)
- "addi {tmp}, {tbl}, 144",
- "vle8.v v11, ({tmp})", // 0x63 (offset 144)
- "addi {tmp}, {tbl}, 160",
- "vle8.v v12, ({tmp})", // 0x1B (offset 160)
-
- // Load state and round key.
- "vle8.v v0, ({state})",
- "vle8.v v1, ({rk})",
-
- // ── Phase 1: Nibble extraction ─────────────────────────────────
- "vand.vi v14, v0, 15", // lo_nib = state & 0x0F
- "vsrl.vi v15, v0, 4", // hi_nib = state >> 4
-
- // ── Phase 2: Input transform (AES → tower field) ──────────────
- "vrgather.vv v16, v2, v14", // ipt_l = IPT_LO[lo_nib]
- "vrgather.vv v17, v3, v15", // ipt_h = IPT_HI[hi_nib]
- "vxor.vv v14, v16, v17", // x = ipt_l ^ ipt_h
-
- // ── Phase 3: Re-extract nibbles of transformed value ───────────
- "vand.vi v15, v14, 15", // t_lo = x & 0x0F
- "vsrl.vi v16, v14, 4", // t_hi = x >> 4
-
- // ── Phase 4: GF(2^4) inverse ──────────────────────────────────
- "vrgather.vv v17, v5, v15", // ak = INV_HI[t_lo]
- "vxor.vv v18, v16, v15", // j = t_hi ^ t_lo
- "vrgather.vv v19, v4, v16", // inv_i = INV_LO[t_hi]
- "vxor.vv v20, v19, v17", // iak = inv_i ^ ak
- "vrgather.vv v21, v4, v18", // inv_j = INV_LO[j]
- "vxor.vv v22, v21, v17", // jak = inv_j ^ ak
-
- // vperm_z(INV_LO, iak): zero where bit 7 set
- "vand.vi v23, v20, 15", // iak & 0x0F
- "vrgather.vv v24, v4, v23", // INV_LO[iak & 0x0F]
- "vsra.vi v25, v20, 7", // 0xFF where bit 7 set
- "vxor.vi v26, v25, -1", // ~mask
- "vand.vv v24, v24, v26", // zero masked positions
- "vxor.vv v14, v24, v18", // io = inv_iak ^ j
-
- // vperm_z(INV_LO, jak): zero where bit 7 set
- "vand.vi v23, v22, 15",
- "vrgather.vv v24, v4, v23",
- "vsra.vi v25, v22, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v24, v24, v26",
- "vxor.vv v15, v24, v16", // jo = inv_jak ^ t_hi
-
- // ── Phase 5: Output transform (SubBytes) ──────────────────────
- // vperm_z(SBOU, io)
- "vand.vi v23, v14, 15",
- "vrgather.vv v24, v6, v23",
- "vsra.vi v25, v14, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v16, v24, v26", // su
-
- // vperm_z(SBOT, jo)
- "vand.vi v23, v15, 15",
- "vrgather.vv v24, v7, v23",
- "vsra.vi v25, v15, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v17, v24, v26", // st
-
- // sb = su ^ st ^ 0x63
- "vxor.vv v14, v16, v17",
- "vxor.vv v14, v14, v11",
-
- // ── ShiftRows ─────────────────────────────────────────────────
- "vrgather.vv v15, v14, v8", // sr = sb permuted
-
- // ── MixColumns (xtime decomposition) ──────────────────────────
- "vrgather.vv v16, v15, v9", // rot1 = column-rotate-by-1
- "vxor.vv v17, v15, v16", // pair = sr ^ rot1
- "vsll.vi v18, v17, 1", // pair << 1
- "vsra.vi v19, v17, 7", // bit-7 mask for xtime reduction
- "vand.vv v19, v19, v12", // mask & 0x1B
- "vxor.vv v18, v18, v19", // xt = xtime(pair)
- "vrgather.vv v19, v17, v10", // rot2_pair = column-rotate-by-2
- "vxor.vv v20, v17, v19", // col_sum = pair ^ rot2_pair
- "vxor.vv v14, v15, v20", // sr ^ col_sum
- "vxor.vv v14, v14, v18", // mc = sr ^ col_sum ^ xt
-
- // ── AddRoundKey ───────────────────────────────────────────────
- "vxor.vv v0, v14, v1",
-
- // ── Store result ──────────────────────────────────────────────
- "vse8.v v0, ({out})",
-
- state = in(reg) block.as_ptr(),
- rk = in(reg) round_key.as_ptr(),
- tbl = in(reg) tables as *const VpermTables as *const u8,
- out = in(reg) out.as_mut_ptr(),
- tmp = out(reg) _,
- options(nostack),
- );
- }
- out
-}
-
-// ── AEGIS-256 state operations ──────────────────────────────────────────
-
-#[target_feature(enable = "v")]
-#[inline]
-/// # Safety
-///
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-unsafe fn update(s: &mut State, m: &Block, tables: &VpermTables) {
- // SAFETY: caller guarantees the RISC-V V extension is available and the
- // state blocks are valid local buffers for the Hamburg round function.
- unsafe {
- let tmp = s[5];
- s[5] = aes_round(&s[4], &s[5], tables);
- s[4] = aes_round(&s[3], &s[4], tables);
- s[3] = aes_round(&s[2], &s[3], tables);
- s[2] = aes_round(&s[1], &s[2], tables);
- s[1] = aes_round(&s[0], &s[1], tables);
- s[0] = xor_block(&aes_round(&tmp, &s[0], tables), m);
- }
-}
-
-#[inline(always)]
-fn keystream(s: &State) -> Block {
- xor_block(&xor_block(&s[1], &s[4]), &xor_block(&s[5], &and_block(&s[2], &s[3])))
-}
-
-// ── Fused encrypt/decrypt ─────────────────────────────────────────────
-
-#[target_feature(enable = "v")]
-/// # Safety
-///
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-pub(super) unsafe fn encrypt_fused(
- key: &[u8; KEY_SIZE],
- nonce: &[u8; NONCE_SIZE],
- aad: &[u8],
- buffer: &mut [u8],
-) -> [u8; TAG_SIZE] {
- // SAFETY: caller guarantees the RISC-V V extension is available for the
- // lifetime of this fused operation and all slices are valid Rust references.
- unsafe {
- let tables = VpermTables::load();
- let (kh0, kh1) = split_halves(key);
- let (nh0, nh1) = split_halves(nonce);
- let k0_xor_n0 = xor_block(kh0, nh0);
- let k1_xor_n1 = xor_block(kh1, nh1);
- let mut s: State = [k0_xor_n0, k1_xor_n1, C1, C0, xor_block(kh0, &C0), xor_block(kh1, &C1)];
-
- for _ in 0..4 {
- update(&mut s, kh0, &tables);
- update(&mut s, kh1, &tables);
- update(&mut s, &k0_xor_n0, &tables);
- update(&mut s, &k1_xor_n1, &tables);
- }
-
- let mut offset = 0usize;
- while offset.strict_add(BLOCK_SIZE) <= aad.len() {
- let mut tmp = [0u8; BLOCK_SIZE];
- tmp.copy_from_slice(&aad[offset..offset.strict_add(BLOCK_SIZE)]);
- update(&mut s, &tmp, &tables);
- offset = offset.strict_add(BLOCK_SIZE);
- }
- if offset < aad.len() {
- let mut pad = [0u8; BLOCK_SIZE];
- pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update(&mut s, &pad, &tables);
- }
-
- let msg_len = buffer.len();
- let len = buffer.len();
- offset = 0;
- while offset.strict_add(BLOCK_SIZE) <= len {
- let z = keystream(&s);
- let mut xi = [0u8; BLOCK_SIZE];
- xi.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
- update(&mut s, &xi, &tables);
- buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&xor_block(&xi, &z));
- offset = offset.strict_add(BLOCK_SIZE);
- }
- if offset < len {
- let z = keystream(&s);
- let tail_len = len.strict_sub(offset);
- let mut pad = [0u8; BLOCK_SIZE];
- pad[..tail_len].copy_from_slice(&buffer[offset..]);
- update(&mut s, &pad, &tables);
- let ct = xor_block(&pad, &z);
- buffer[offset..].copy_from_slice(&ct[..tail_len]);
- }
-
- let ad_bits = (aad.len() as u64).strict_mul(8);
- let msg_bits = (msg_len as u64).strict_mul(8);
- let mut len_bytes = [0u8; BLOCK_SIZE];
- len_bytes[..8].copy_from_slice(&ad_bits.to_le_bytes());
- len_bytes[8..].copy_from_slice(&msg_bits.to_le_bytes());
- let t = xor_block(&s[3], &len_bytes);
- for _ in 0..7 {
- update(&mut s, &t, &tables);
- }
-
- xor_block(
- &xor_block(&xor_block(&s[0], &s[1]), &xor_block(&s[2], &s[3])),
- &xor_block(&s[4], &s[5]),
- )
- }
-}
-
-#[target_feature(enable = "v")]
-/// # Safety
-///
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-pub(super) unsafe fn decrypt_fused(
- key: &[u8; KEY_SIZE],
- nonce: &[u8; NONCE_SIZE],
- aad: &[u8],
- buffer: &mut [u8],
-) -> [u8; TAG_SIZE] {
- // SAFETY: caller guarantees the RISC-V V extension is available for the
- // lifetime of this fused operation and all slices are valid Rust references.
- unsafe {
- let tables = VpermTables::load();
- let (kh0, kh1) = split_halves(key);
- let (nh0, nh1) = split_halves(nonce);
- let k0_xor_n0 = xor_block(kh0, nh0);
- let k1_xor_n1 = xor_block(kh1, nh1);
- let mut s: State = [k0_xor_n0, k1_xor_n1, C1, C0, xor_block(kh0, &C0), xor_block(kh1, &C1)];
-
- for _ in 0..4 {
- update(&mut s, kh0, &tables);
- update(&mut s, kh1, &tables);
- update(&mut s, &k0_xor_n0, &tables);
- update(&mut s, &k1_xor_n1, &tables);
- }
-
- let mut offset = 0usize;
- while offset.strict_add(BLOCK_SIZE) <= aad.len() {
- let mut tmp = [0u8; BLOCK_SIZE];
- tmp.copy_from_slice(&aad[offset..offset.strict_add(BLOCK_SIZE)]);
- update(&mut s, &tmp, &tables);
- offset = offset.strict_add(BLOCK_SIZE);
- }
- if offset < aad.len() {
- let mut pad = [0u8; BLOCK_SIZE];
- pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update(&mut s, &pad, &tables);
- }
-
- let ct_len = buffer.len();
- let len = buffer.len();
- offset = 0;
- while offset.strict_add(BLOCK_SIZE) <= len {
- let z = keystream(&s);
- let mut ci = [0u8; BLOCK_SIZE];
- ci.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
- let xi = xor_block(&ci, &z);
- update(&mut s, &xi, &tables);
- buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&xi);
- offset = offset.strict_add(BLOCK_SIZE);
- }
- if offset < len {
- let z = keystream(&s);
- let tail_len = len.strict_sub(offset);
- let mut pad = [0u8; BLOCK_SIZE];
- pad[..tail_len].copy_from_slice(&buffer[offset..]);
- let mut pt_pad = [0u8; BLOCK_SIZE];
- for i in 0..tail_len {
- pt_pad[i] = pad[i] ^ z[i];
- }
- update(&mut s, &pt_pad, &tables);
- buffer[offset..].copy_from_slice(&pt_pad[..tail_len]);
- }
-
- let ad_bits = (aad.len() as u64).strict_mul(8);
- let ct_bits = (ct_len as u64).strict_mul(8);
- let mut len_bytes = [0u8; BLOCK_SIZE];
- len_bytes[..8].copy_from_slice(&ad_bits.to_le_bytes());
- len_bytes[8..].copy_from_slice(&ct_bits.to_le_bytes());
- let t = xor_block(&s[3], &len_bytes);
- for _ in 0..7 {
- update(&mut s, &t, &tables);
- }
-
- xor_block(
- &xor_block(&xor_block(&s[0], &s[1]), &xor_block(&s[2], &s[3])),
- &xor_block(&s[4], &s[5]),
- )
- }
-}
diff --git a/src/aead/aegis256/s390x_vperm.rs b/src/aead/aegis256/s390x_vperm.rs
index 11c7fe59..f22d1530 100644
--- a/src/aead/aegis256/s390x_vperm.rs
+++ b/src/aead/aegis256/s390x_vperm.rs
@@ -48,7 +48,7 @@ fn and_vec(a: i64x2, b: i64x2) -> i64x2 {
/// Broadcast a byte to all 16 positions of a vector.
#[inline(always)]
fn splat_byte(b: u8) -> i64x2 {
- let w = u64::from_ne_bytes([b; 8]) as i64;
+ let w = i64::from_ne_bytes([b; 8]);
i64x2::from_array([w, w])
}
@@ -297,38 +297,31 @@ impl VpermTables {
// ── AEGIS-256 state operations ──────────────────────────────────────────
#[target_feature(enable = "vector")]
-#[allow(clippy::too_many_arguments)]
#[inline]
/// # Safety
///
/// Caller must ensure the s390x vector facility is available and all state
/// registers belong to a valid AEGIS-256 state.
-unsafe fn update_regs(
- s0: &mut i64x2,
- s1: &mut i64x2,
- s2: &mut i64x2,
- s3: &mut i64x2,
- s4: &mut i64x2,
- s5: &mut i64x2,
- m: i64x2,
- tables: &VpermTables,
-) {
+unsafe fn update_regs(state: &mut [i64x2; 6], m: i64x2, tables: &VpermTables) {
// SAFETY: caller guarantees the s390x vector facility is available for
// this helper and all state registers are valid local values.
unsafe {
- let tmp = *s5;
- *s5 = aes_round(*s4, *s5, tables);
- *s4 = aes_round(*s3, *s4, tables);
- *s3 = aes_round(*s2, *s3, tables);
- *s2 = aes_round(*s1, *s2, tables);
- *s1 = aes_round(*s0, *s1, tables);
- *s0 = xor_vec(aes_round(tmp, *s0, tables), m);
+ let tmp = state[5];
+ state[5] = aes_round(state[4], state[5], tables);
+ state[4] = aes_round(state[3], state[4], tables);
+ state[3] = aes_round(state[2], state[3], tables);
+ state[2] = aes_round(state[1], state[2], tables);
+ state[1] = aes_round(state[0], state[1], tables);
+ state[0] = xor_vec(aes_round(tmp, state[0], tables), m);
}
}
#[inline(always)]
-fn keystream_regs(s1: i64x2, s2: i64x2, s3: i64x2, s4: i64x2, s5: i64x2) -> i64x2 {
- xor_vec(xor_vec(s1, s4), xor_vec(s5, and_vec(s2, s3)))
+fn keystream_regs(state: &[i64x2; 6]) -> i64x2 {
+ xor_vec(
+ xor_vec(state[1], state[4]),
+ xor_vec(state[5], and_vec(state[2], state[3])),
+ )
}
// ── Fused encrypt/decrypt ─────────────────────────────────────────────
@@ -357,64 +350,45 @@ pub(super) unsafe fn encrypt_fused(
let c1 = load_be(&C1);
let k0_xor_n0 = xor_vec(k0, n0);
let k1_xor_n1 = xor_vec(k1, n1);
- let (mut s0, mut s1, mut s2, mut s3, mut s4, mut s5) =
- (k0_xor_n0, k1_xor_n1, c1, c0, xor_vec(k0, c0), xor_vec(k1, c1));
+ let mut state = [k0_xor_n0, k1_xor_n1, c1, c0, xor_vec(k0, c0), xor_vec(k1, c1)];
for _ in 0..4 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0, &tables);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1, &tables);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0, &tables);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1, &tables);
+ update_regs(&mut state, k0, &tables);
+ update_regs(&mut state, k1, &tables);
+ update_regs(&mut state, k0_xor_n0, &tables);
+ update_regs(&mut state, k1_xor_n1, &tables);
}
let mut offset = 0usize;
while offset.strict_add(BLOCK_SIZE) <= aad.len() {
let mut tmp = [0u8; 16];
tmp.copy_from_slice(&aad[offset..offset.strict_add(BLOCK_SIZE)]);
- update_regs(
- &mut s0,
- &mut s1,
- &mut s2,
- &mut s3,
- &mut s4,
- &mut s5,
- load_be(&tmp),
- &tables,
- );
+ update_regs(&mut state, load_be(&tmp), &tables);
offset = offset.strict_add(BLOCK_SIZE);
}
if offset < aad.len() {
let mut pad = [0u8; BLOCK_SIZE];
pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update_regs(
- &mut s0,
- &mut s1,
- &mut s2,
- &mut s3,
- &mut s4,
- &mut s5,
- load_be(&pad),
- &tables,
- );
+ update_regs(&mut state, load_be(&pad), &tables);
}
let msg_len = buffer.len();
let len = buffer.len();
offset = 0;
while offset.strict_add(BLOCK_SIZE) <= len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
+ let z = keystream_regs(&state);
let mut tmp = [0u8; 16];
tmp.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
let xi = load_be(&tmp);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi, &tables);
+ update_regs(&mut state, xi, &tables);
store_be(xor_vec(xi, z), &mut tmp);
buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp);
offset = offset.strict_add(BLOCK_SIZE);
}
if offset < len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
+ let z = keystream_regs(&state);
let tail_len = len.strict_sub(offset);
let mut pad = [0u8; BLOCK_SIZE];
pad[..tail_len].copy_from_slice(&buffer[offset..]);
let xi = load_be(&pad);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi, &tables);
+ update_regs(&mut state, xi, &tables);
let mut ct_bytes = [0u8; BLOCK_SIZE];
store_be(xor_vec(xi, z), &mut ct_bytes);
buffer[offset..].copy_from_slice(&ct_bytes[..tail_len]);
@@ -424,11 +398,14 @@ pub(super) unsafe fn encrypt_fused(
let mut len_bytes = [0u8; BLOCK_SIZE];
len_bytes[..8].copy_from_slice(&ad_bits.to_le_bytes());
len_bytes[8..].copy_from_slice(&msg_bits.to_le_bytes());
- let t = xor_vec(s3, load_be(&len_bytes));
+ let t = xor_vec(state[3], load_be(&len_bytes));
for _ in 0..7 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t, &tables);
+ update_regs(&mut state, t, &tables);
}
- let tag_vec = xor_vec(xor_vec(xor_vec(s0, s1), xor_vec(s2, s3)), xor_vec(s4, s5));
+ let tag_vec = xor_vec(
+ xor_vec(xor_vec(state[0], state[1]), xor_vec(state[2], state[3])),
+ xor_vec(state[4], state[5]),
+ );
let mut tag = [0u8; TAG_SIZE];
store_be(tag_vec, &mut tag);
tag
@@ -459,59 +436,40 @@ pub(super) unsafe fn decrypt_fused(
let c1 = load_be(&C1);
let k0_xor_n0 = xor_vec(k0, n0);
let k1_xor_n1 = xor_vec(k1, n1);
- let (mut s0, mut s1, mut s2, mut s3, mut s4, mut s5) =
- (k0_xor_n0, k1_xor_n1, c1, c0, xor_vec(k0, c0), xor_vec(k1, c1));
+ let mut state = [k0_xor_n0, k1_xor_n1, c1, c0, xor_vec(k0, c0), xor_vec(k1, c1)];
for _ in 0..4 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0, &tables);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1, &tables);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0, &tables);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1, &tables);
+ update_regs(&mut state, k0, &tables);
+ update_regs(&mut state, k1, &tables);
+ update_regs(&mut state, k0_xor_n0, &tables);
+ update_regs(&mut state, k1_xor_n1, &tables);
}
let mut offset = 0usize;
while offset.strict_add(BLOCK_SIZE) <= aad.len() {
let mut tmp = [0u8; 16];
tmp.copy_from_slice(&aad[offset..offset.strict_add(BLOCK_SIZE)]);
- update_regs(
- &mut s0,
- &mut s1,
- &mut s2,
- &mut s3,
- &mut s4,
- &mut s5,
- load_be(&tmp),
- &tables,
- );
+ update_regs(&mut state, load_be(&tmp), &tables);
offset = offset.strict_add(BLOCK_SIZE);
}
if offset < aad.len() {
let mut pad = [0u8; BLOCK_SIZE];
pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update_regs(
- &mut s0,
- &mut s1,
- &mut s2,
- &mut s3,
- &mut s4,
- &mut s5,
- load_be(&pad),
- &tables,
- );
+ update_regs(&mut state, load_be(&pad), &tables);
}
let ct_len = buffer.len();
let len = buffer.len();
offset = 0;
while offset.strict_add(BLOCK_SIZE) <= len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
+ let z = keystream_regs(&state);
let mut tmp = [0u8; 16];
tmp.copy_from_slice(&buffer[offset..offset.strict_add(BLOCK_SIZE)]);
let xi = xor_vec(load_be(&tmp), z);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi, &tables);
+ update_regs(&mut state, xi, &tables);
store_be(xi, &mut tmp);
buffer[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&tmp);
offset = offset.strict_add(BLOCK_SIZE);
}
if offset < len {
- let z = keystream_regs(s1, s2, s3, s4, s5);
+ let z = keystream_regs(&state);
let tail_len = len.strict_sub(offset);
let mut pad = [0u8; BLOCK_SIZE];
pad[..tail_len].copy_from_slice(&buffer[offset..]);
@@ -521,16 +479,7 @@ pub(super) unsafe fn decrypt_fused(
for i in 0..tail_len {
pt_pad[i] = pad[i] ^ z_bytes[i];
}
- update_regs(
- &mut s0,
- &mut s1,
- &mut s2,
- &mut s3,
- &mut s4,
- &mut s5,
- load_be(&pt_pad),
- &tables,
- );
+ update_regs(&mut state, load_be(&pt_pad), &tables);
buffer[offset..].copy_from_slice(&pt_pad[..tail_len]);
}
let ad_bits = (aad.len() as u64).strict_mul(8);
@@ -538,11 +487,14 @@ pub(super) unsafe fn decrypt_fused(
let mut len_bytes = [0u8; BLOCK_SIZE];
len_bytes[..8].copy_from_slice(&ad_bits.to_le_bytes());
len_bytes[8..].copy_from_slice(&ct_bits.to_le_bytes());
- let t = xor_vec(s3, load_be(&len_bytes));
+ let t = xor_vec(state[3], load_be(&len_bytes));
for _ in 0..7 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t, &tables);
+ update_regs(&mut state, t, &tables);
}
- let tag_vec = xor_vec(xor_vec(xor_vec(s0, s1), xor_vec(s2, s3)), xor_vec(s4, s5));
+ let tag_vec = xor_vec(
+ xor_vec(xor_vec(state[0], state[1]), xor_vec(state[2], state[3])),
+ xor_vec(state[4], state[5]),
+ );
let mut tag = [0u8; TAG_SIZE];
store_be(tag_vec, &mut tag);
tag
diff --git a/src/aead/aegis256/x86_64_ni.rs b/src/aead/aegis256/x86_64_ni.rs
index 3a8c3a5b..32813c71 100644
--- a/src/aead/aegis256/x86_64_ni.rs
+++ b/src/aead/aegis256/x86_64_ni.rs
@@ -3,66 +3,62 @@ use core::arch::x86_64::*;
use super::{BLOCK_SIZE, C0, C1, KEY_SIZE, NONCE_SIZE, TAG_SIZE};
#[inline]
-/// # Safety
-///
-/// `bytes` must refer to a valid 16-byte block.
-unsafe fn load(bytes: &[u8; BLOCK_SIZE]) -> __m128i {
- _mm_loadu_si128(bytes.as_ptr().cast())
+fn load(bytes: &[u8; BLOCK_SIZE]) -> __m128i {
+ // SAFETY: SSE2 is an x86_64 baseline feature; `bytes` provides 16 readable bytes, and the load is unaligned.
+ unsafe { _mm_loadu_si128(bytes.as_ptr().cast()) }
}
#[inline]
-/// # Safety
-///
-/// `out` must refer to a valid writable 16-byte block.
-unsafe fn store(v: __m128i, out: &mut [u8; BLOCK_SIZE]) {
- _mm_storeu_si128(out.as_mut_ptr().cast(), v);
+fn store(value: __m128i, out: &mut [u8; BLOCK_SIZE]) {
+ // SAFETY: SSE2 is an x86_64 baseline feature; `out` provides 16 writable bytes, and the store is unaligned.
+ unsafe { _mm_storeu_si128(out.as_mut_ptr().cast(), value) };
}
-// ── Register-based helpers ──────────────────────────────────────────────
-//
-// All 6 AES rounds in an AEGIS-256 update read from the OLD state and are
-// mutually independent. Keeping state in 6 local `__m128i` values (rather
-// than indexing an array through `&mut State`) lets the register allocator
-// pin them to XMM registers across loop iterations, eliminating store-to-
-// load round-trips that serialize the OOO pipeline.
+type StateMut<'a> = (
+ &'a mut __m128i,
+ &'a mut __m128i,
+ &'a mut __m128i,
+ &'a mut __m128i,
+ &'a mut __m128i,
+ &'a mut __m128i,
+);
-#[inline]
-/// # Safety
-///
-/// Caller must ensure AES-NI is available and all register arguments come
-/// from a valid AEGIS-256 state.
-unsafe fn update_regs(
- s0: &mut __m128i,
- s1: &mut __m128i,
- s2: &mut __m128i,
- s3: &mut __m128i,
- s4: &mut __m128i,
- s5: &mut __m128i,
- m: __m128i,
-) {
- let tmp = *s5;
- *s5 = _mm_aesenc_si128(*s4, *s5);
- *s4 = _mm_aesenc_si128(*s3, *s4);
- *s3 = _mm_aesenc_si128(*s2, *s3);
- *s2 = _mm_aesenc_si128(*s1, *s2);
- *s1 = _mm_aesenc_si128(*s0, *s1);
- *s0 = _mm_xor_si128(_mm_aesenc_si128(tmp, *s0), m);
+#[derive(Clone, Copy)]
+struct AesNi {
+ _private: (),
}
-#[inline]
-/// # Safety
-///
-/// The provided registers must come from a valid AEGIS-256 state on an
-/// AES-NI capable CPU.
-unsafe fn keystream_regs(s1: __m128i, s2: __m128i, s3: __m128i, s4: __m128i, s5: __m128i) -> __m128i {
- _mm_xor_si128(_mm_xor_si128(s1, s4), _mm_xor_si128(s5, _mm_and_si128(s2, s3)))
+impl AesNi {
+ /// Creates an AES-NI capability token.
+ ///
+ /// # Safety
+ ///
+ /// The current CPU must support AES-NI and AVX for the token's entire lifetime.
+ #[inline]
+ unsafe fn new() -> Self {
+ Self { _private: () }
+ }
+
+ #[inline(always)]
+ fn update(self, (s0, s1, s2, s3, s4, s5): StateMut<'_>, message: __m128i) {
+ let old_s5 = *s5;
+ // SAFETY: this module constructs `AesNi` only through `new`, whose caller guarantees AES-NI and AVX support.
+ unsafe {
+ *s5 = _mm_aesenc_si128(*s4, *s5);
+ *s4 = _mm_aesenc_si128(*s3, *s4);
+ *s3 = _mm_aesenc_si128(*s2, *s3);
+ *s2 = _mm_aesenc_si128(*s1, *s2);
+ *s1 = _mm_aesenc_si128(*s0, *s1);
+ *s0 = _mm_xor_si128(_mm_aesenc_si128(old_s5, *s0), message);
+ }
+ }
}
-// ── Fused encrypt/decrypt ───────────────────────────────────────────────
-//
-// Single `#[target_feature]` entry points that keep state in XMM registers
-// from init through finalize, eliminating ~15 cycles of stack spills that
-// occur when init/aad/encrypt/finalize are separate function calls.
+#[inline]
+fn keystream(s1: __m128i, s2: __m128i, s3: __m128i, s4: __m128i, s5: __m128i) -> __m128i {
+ // SAFETY: SSE2 is part of the x86_64 baseline.
+ unsafe { _mm_xor_si128(_mm_xor_si128(s1, s4), _mm_xor_si128(s5, _mm_and_si128(s2, s3))) }
+}
#[target_feature(enable = "aes,avx")]
/// # Safety
@@ -74,7 +70,8 @@ pub(super) unsafe fn encrypt_fused(
aad: &[u8],
buffer: &mut [u8],
) -> [u8; TAG_SIZE] {
- // ── init ──
+ // SAFETY: this function's caller guarantees AES-NI and AVX support.
+ let aesni = unsafe { AesNi::new() };
let (kh0, kh1) = super::split_halves(key);
let (nh0, nh1) = super::split_halves(nonce);
let k0 = load(kh0);
@@ -94,94 +91,63 @@ pub(super) unsafe fn encrypt_fused(
_mm_xor_si128(k1, c1),
);
for _ in 0..4 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k0);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k1);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k0_xor_n0);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k1_xor_n1);
}
- // ── aad ──
- let mut offset = 0usize;
- while offset.strict_add(BLOCK_SIZE) <= aad.len() {
- let block = _mm_loadu_si128(aad.as_ptr().add(offset).cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, block);
- offset = offset.strict_add(BLOCK_SIZE);
+ let (aad_blocks, aad_tail) = aad.as_chunks::();
+ for block in aad_blocks {
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load(block));
}
- if offset < aad.len() {
+ if !aad_tail.is_empty() {
let mut pad = [0u8; BLOCK_SIZE];
- pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load(&pad));
+ let (pad_tail, _) = pad.split_at_mut(aad_tail.len());
+ pad_tail.copy_from_slice(aad_tail);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load(&pad));
}
- // ── encrypt ──
- //
- // Counted loop with raw pointer advancement: eliminates per-iteration
- // overflow-check branches (strict_add) from the hot path. The loop count
- // is pre-validated via integer division, and all intra-block offsets are
- // compile-time constants applied through ptr::add inside this unsafe block.
let msg_len = buffer.len();
- let ptr = buffer.as_mut_ptr();
- let n_quads = msg_len / 64;
- let mut p = ptr;
- for _ in 0..n_quads {
- _mm_prefetch(p.add(256).cast::(), _MM_HINT_T0);
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let xi_a = _mm_loadu_si128(p.cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
- _mm_storeu_si128(p.cast(), _mm_xor_si128(xi_a, z_a));
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let xi_b = _mm_loadu_si128(p.add(16).cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
- _mm_storeu_si128(p.add(16).cast(), _mm_xor_si128(xi_b, z_b));
- let z_c = keystream_regs(s1, s2, s3, s4, s5);
- let xi_c = _mm_loadu_si128(p.add(32).cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_c);
- _mm_storeu_si128(p.add(32).cast(), _mm_xor_si128(xi_c, z_c));
- let z_d = keystream_regs(s1, s2, s3, s4, s5);
- let xi_d = _mm_loadu_si128(p.add(48).cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_d);
- _mm_storeu_si128(p.add(48).cast(), _mm_xor_si128(xi_d, z_d));
- p = p.add(64);
- }
- let mut remaining = msg_len.strict_sub(n_quads.strict_mul(64));
- if remaining >= 32 {
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let xi_a = _mm_loadu_si128(p.cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
- _mm_storeu_si128(p.cast(), _mm_xor_si128(xi_a, z_a));
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let xi_b = _mm_loadu_si128(p.add(16).cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
- _mm_storeu_si128(p.add(16).cast(), _mm_xor_si128(xi_b, z_b));
- p = p.add(32);
- remaining = remaining.strict_sub(32);
+ let (quads, remainder) = buffer.as_chunks_mut::<64>();
+ for quad in quads {
+ let (blocks, _) = quad.as_chunks_mut::();
+ for block in blocks {
+ let stream = keystream(s1, s2, s3, s4, s5);
+ let plaintext = load(block);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ store(_mm_xor_si128(plaintext, stream), block);
+ }
}
- if remaining >= 16 {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let xi = _mm_loadu_si128(p.cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
- _mm_storeu_si128(p.cast(), _mm_xor_si128(xi, z));
- remaining = remaining.strict_sub(16);
+ let (blocks, tail) = remainder.as_chunks_mut::();
+ for block in blocks {
+ let stream = keystream(s1, s2, s3, s4, s5);
+ let plaintext = load(block);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ store(_mm_xor_si128(plaintext, stream), block);
}
- if remaining > 0 {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let tail_off = msg_len.strict_sub(remaining);
+ if !tail.is_empty() {
+ let stream = keystream(s1, s2, s3, s4, s5);
let mut pad = [0u8; BLOCK_SIZE];
- pad[..remaining].copy_from_slice(&buffer[tail_off..]);
- let xi = load(&pad);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
- let mut ct_bytes = [0u8; BLOCK_SIZE];
- store(_mm_xor_si128(xi, z), &mut ct_bytes);
- buffer[tail_off..].copy_from_slice(&ct_bytes[..remaining]);
+ let (pad_tail, _) = pad.split_at_mut(tail.len());
+ pad_tail.copy_from_slice(tail);
+ let plaintext = load(&pad);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ let mut ciphertext = [0u8; BLOCK_SIZE];
+ store(_mm_xor_si128(plaintext, stream), &mut ciphertext);
+ let (ciphertext_tail, _) = ciphertext.split_at(tail.len());
+ tail.copy_from_slice(ciphertext_tail);
}
- // ── finalize ──
let ad_bits = (aad.len() as u64).strict_mul(8);
let msg_bits = (msg_len as u64).strict_mul(8);
- let len_block = _mm_set_epi64x(msg_bits as i64, ad_bits as i64);
+ let len_block = _mm_set_epi64x(
+ i64::from_ne_bytes(msg_bits.to_ne_bytes()),
+ i64::from_ne_bytes(ad_bits.to_ne_bytes()),
+ );
let t = _mm_xor_si128(s3, len_block);
for _ in 0..7 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), t);
}
let tag_vec = _mm_xor_si128(
_mm_xor_si128(_mm_xor_si128(s0, s1), _mm_xor_si128(s2, s3)),
@@ -202,7 +168,8 @@ pub(super) unsafe fn decrypt_fused(
aad: &[u8],
buffer: &mut [u8],
) -> [u8; TAG_SIZE] {
- // ── init ──
+ // SAFETY: this function's caller guarantees AES-NI and AVX support.
+ let aesni = unsafe { AesNi::new() };
let (kh0, kh1) = super::split_halves(key);
let (nh0, nh1) = super::split_halves(nonce);
let k0 = load(kh0);
@@ -222,102 +189,63 @@ pub(super) unsafe fn decrypt_fused(
_mm_xor_si128(k1, c1),
);
for _ in 0..4 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k0_xor_n0);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, k1_xor_n1);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k0);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k1);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k0_xor_n0);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), k1_xor_n1);
}
- // ── aad ──
- let mut offset = 0usize;
- while offset.strict_add(BLOCK_SIZE) <= aad.len() {
- let block = _mm_loadu_si128(aad.as_ptr().add(offset).cast());
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, block);
- offset = offset.strict_add(BLOCK_SIZE);
+ let (aad_blocks, aad_tail) = aad.as_chunks::();
+ for block in aad_blocks {
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load(block));
}
- if offset < aad.len() {
+ if !aad_tail.is_empty() {
let mut pad = [0u8; BLOCK_SIZE];
- pad[..aad.len().strict_sub(offset)].copy_from_slice(&aad[offset..]);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load(&pad));
+ let (pad_tail, _) = pad.split_at_mut(aad_tail.len());
+ pad_tail.copy_from_slice(aad_tail);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load(&pad));
}
- // ── decrypt ──
- //
- // Same counted-loop strategy as encrypt: raw pointer advancement with
- // compile-time-constant offsets, zero overflow checks in the hot path.
let ct_len = buffer.len();
- let ptr = buffer.as_mut_ptr();
- let n_quads = ct_len / 64;
- let mut p = ptr;
- for _ in 0..n_quads {
- _mm_prefetch(p.add(256).cast::(), _MM_HINT_T0);
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let ci_a = _mm_loadu_si128(p.cast());
- let xi_a = _mm_xor_si128(ci_a, z_a);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
- _mm_storeu_si128(p.cast(), xi_a);
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let ci_b = _mm_loadu_si128(p.add(16).cast());
- let xi_b = _mm_xor_si128(ci_b, z_b);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
- _mm_storeu_si128(p.add(16).cast(), xi_b);
- let z_c = keystream_regs(s1, s2, s3, s4, s5);
- let ci_c = _mm_loadu_si128(p.add(32).cast());
- let xi_c = _mm_xor_si128(ci_c, z_c);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_c);
- _mm_storeu_si128(p.add(32).cast(), xi_c);
- let z_d = keystream_regs(s1, s2, s3, s4, s5);
- let ci_d = _mm_loadu_si128(p.add(48).cast());
- let xi_d = _mm_xor_si128(ci_d, z_d);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_d);
- _mm_storeu_si128(p.add(48).cast(), xi_d);
- p = p.add(64);
- }
- let mut remaining = ct_len.strict_sub(n_quads.strict_mul(64));
- if remaining >= 32 {
- let z_a = keystream_regs(s1, s2, s3, s4, s5);
- let ci_a = _mm_loadu_si128(p.cast());
- let xi_a = _mm_xor_si128(ci_a, z_a);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_a);
- _mm_storeu_si128(p.cast(), xi_a);
- let z_b = keystream_regs(s1, s2, s3, s4, s5);
- let ci_b = _mm_loadu_si128(p.add(16).cast());
- let xi_b = _mm_xor_si128(ci_b, z_b);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi_b);
- _mm_storeu_si128(p.add(16).cast(), xi_b);
- p = p.add(32);
- remaining = remaining.strict_sub(32);
+ let (quads, remainder) = buffer.as_chunks_mut::<64>();
+ for quad in quads {
+ let (blocks, _) = quad.as_chunks_mut::();
+ for block in blocks {
+ let stream = keystream(s1, s2, s3, s4, s5);
+ let plaintext = _mm_xor_si128(load(block), stream);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ store(plaintext, block);
+ }
}
- if remaining >= 16 {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let ci = _mm_loadu_si128(p.cast());
- let xi = _mm_xor_si128(ci, z);
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, xi);
- _mm_storeu_si128(p.cast(), xi);
- remaining = remaining.strict_sub(16);
+ let (blocks, tail) = remainder.as_chunks_mut::();
+ for block in blocks {
+ let stream = keystream(s1, s2, s3, s4, s5);
+ let plaintext = _mm_xor_si128(load(block), stream);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), plaintext);
+ store(plaintext, block);
}
- if remaining > 0 {
- let z = keystream_regs(s1, s2, s3, s4, s5);
- let tail_off = ct_len.strict_sub(remaining);
- let mut pad = [0u8; BLOCK_SIZE];
- pad[..remaining].copy_from_slice(&buffer[tail_off..]);
- let mut z_bytes = [0u8; BLOCK_SIZE];
- store(z, &mut z_bytes);
- let mut pt_pad = [0u8; BLOCK_SIZE];
- for i in 0..remaining {
- pt_pad[i] = pad[i] ^ z_bytes[i];
+ if !tail.is_empty() {
+ let stream = keystream(s1, s2, s3, s4, s5);
+ let mut stream_bytes = [0u8; BLOCK_SIZE];
+ store(stream, &mut stream_bytes);
+ let mut plaintext = [0u8; BLOCK_SIZE];
+ for ((out, ciphertext), mask) in plaintext.iter_mut().zip(tail.iter()).zip(stream_bytes) {
+ *out = *ciphertext ^ mask;
}
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, load(&pt_pad));
- buffer[tail_off..].copy_from_slice(&pt_pad[..remaining]);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), load(&plaintext));
+ let (plaintext_tail, _) = plaintext.split_at(tail.len());
+ tail.copy_from_slice(plaintext_tail);
}
- // ── finalize ──
let ad_bits = (aad.len() as u64).strict_mul(8);
let ct_bits = (ct_len as u64).strict_mul(8);
- let len_block = _mm_set_epi64x(ct_bits as i64, ad_bits as i64);
+ let len_block = _mm_set_epi64x(
+ i64::from_ne_bytes(ct_bits.to_ne_bytes()),
+ i64::from_ne_bytes(ad_bits.to_ne_bytes()),
+ );
let t = _mm_xor_si128(s3, len_block);
for _ in 0..7 {
- update_regs(&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5, t);
+ aesni.update((&mut s0, &mut s1, &mut s2, &mut s3, &mut s4, &mut s5), t);
}
let tag_vec = _mm_xor_si128(
_mm_xor_si128(_mm_xor_si128(s0, s1), _mm_xor_si128(s2, s3)),
diff --git a/src/aead/aes.rs b/src/aead/aes.rs
index e77afab3..1d8c792e 100644
--- a/src/aead/aes.rs
+++ b/src/aead/aes.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::indexing_slicing)]
-
//! Portable table-free AES block-cipher core with hardware dispatch.
//!
//! This module provides AES-128 and AES-256 key expansion and single-block
@@ -40,31 +38,23 @@ pub(crate) const EXPANDED_KEY_WORDS_128: usize = 4 * (ROUNDS_128 + 1); // 44
#[path = "aes/aarch64_ce.rs"]
mod ce;
#[cfg(target_arch = "s390x")]
-#[allow(unsafe_code)]
#[path = "aes/s390x_km.rs"]
mod km;
#[cfg(target_arch = "x86_64")]
#[path = "aes/x86_64_ni.rs"]
mod ni;
#[cfg(target_arch = "powerpc64")]
-#[allow(unsafe_code)]
#[path = "aes/powerpc64_ppc.rs"]
mod ppc;
#[cfg(target_arch = "riscv64")]
-#[allow(unsafe_code)]
#[path = "aes/riscv64_aes.rs"]
mod rv_aes;
-#[cfg(any(target_arch = "riscv64", test))]
+#[cfg(any(target_arch = "riscv64", all(test, not(target_arch = "s390x"))))]
#[path = "aes/riscv64_fixslice_aes.rs"]
mod rv_fixslice_aes;
#[cfg(target_arch = "riscv64")]
-#[allow(unsafe_code)]
#[path = "aes/riscv64_scalar_aes.rs"]
mod rv_scalar_aes;
-#[cfg(target_arch = "riscv64")]
-#[allow(unsafe_code)]
-#[path = "aes/riscv64_vperm_aes.rs"]
-mod rv_vperm_aes;
#[cfg(all(
target_arch = "x86_64",
target_os = "linux",
@@ -113,7 +103,7 @@ pub(crate) struct Aes256EncKey {
}
enum KeyInner {
- #[allow(dead_code)]
+ #[cfg(not(target_arch = "riscv64"))]
PortableRoundKeys([u32; EXPANDED_KEY_WORDS]),
#[cfg(target_arch = "x86_64")]
X86AesNi(ni::NiRoundKeys),
@@ -124,24 +114,21 @@ enum KeyInner {
#[cfg(target_arch = "powerpc64")]
Power8Crypto(ppc::PpcRoundKeys),
#[cfg(target_arch = "riscv64")]
- Riscv64ScalarCrypto(rv_scalar_aes::RvScalarRoundKeys),
- #[cfg(target_arch = "riscv64")]
- Riscv64VectorCrypto(rv_aes::RvRoundKeys),
- /// Hamburg vperm via `vrgather.vv` with the table-free portable key schedule.
+ ScalarCrypto(rv_scalar_aes::RvScalarRoundKeys),
#[cfg(target_arch = "riscv64")]
- #[allow(dead_code)] // V-only AES is kept for GCM-SIV and explicit diagnostic paths; GCM does not select it yet.
- Riscv64Vperm([u32; EXPANDED_KEY_WORDS]),
+ VectorCrypto(rv_aes::RvRoundKeys),
/// Four-block table-free fixslice fallback for scalar RV64 without AES extensions.
#[cfg(all(target_arch = "riscv64", feature = "alloc"))]
- Riscv64Fixslice(alloc::boxed::Box),
+ Fixslice(alloc::boxed::Box),
/// No-alloc RV64 builds keep the larger fixslice key schedule inline.
#[cfg(all(target_arch = "riscv64", not(feature = "alloc")))]
- Riscv64Fixslice(rv_fixslice_aes::RvFixsliceRoundKeys),
+ Fixslice(rv_fixslice_aes::RvFixsliceRoundKeys),
}
impl Drop for Aes256EncKey {
fn drop(&mut self) {
match &mut self.inner {
+ #[cfg(not(target_arch = "riscv64"))]
KeyInner::PortableRoundKeys(rk) => {
// SAFETY: [u32; 60] is layout-compatible with [u8; 240].
crate::traits::ct::zeroize(unsafe {
@@ -161,23 +148,15 @@ impl Drop for Aes256EncKey {
ppc_rk.zeroize();
}
#[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64ScalarCrypto(rv_rk) => {
+ KeyInner::ScalarCrypto(rv_rk) => {
rv_rk.zeroize();
}
#[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64VectorCrypto(rv_rk) => {
+ KeyInner::VectorCrypto(rv_rk) => {
rv_rk.zeroize();
}
#[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64Vperm(rk) => {
- // SAFETY: the expanded key is a contiguous `[u32; 60]`, so viewing it as
- // a mutable byte slice for zeroization is valid for its exact size.
- crate::traits::ct::zeroize(unsafe {
- core::slice::from_raw_parts_mut(rk.as_mut_ptr().cast::(), EXPANDED_KEY_WORDS.strict_mul(4))
- });
- }
- #[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64Fixslice(rk) => {
+ KeyInner::Fixslice(rk) => {
rk.zeroize();
}
}
@@ -195,7 +174,7 @@ pub(crate) struct Aes128EncKey {
}
enum Key128Inner {
- #[allow(dead_code)]
+ #[cfg(not(target_arch = "riscv64"))]
PortableRoundKeys([u32; EXPANDED_KEY_WORDS_128]),
#[cfg(target_arch = "x86_64")]
X86AesNi(ni::Ni128RoundKeys),
@@ -206,24 +185,21 @@ enum Key128Inner {
#[cfg(target_arch = "powerpc64")]
Power8Crypto(ppc::Ppc128RoundKeys),
#[cfg(target_arch = "riscv64")]
- Riscv64ScalarCrypto(rv_scalar_aes::RvScalar128RoundKeys),
+ ScalarCrypto(rv_scalar_aes::RvScalar128RoundKeys),
#[cfg(target_arch = "riscv64")]
- Riscv64VectorCrypto(rv_aes::Rv128RoundKeys),
- /// Hamburg vperm via `vrgather.vv` with the table-free portable key schedule.
- #[cfg(target_arch = "riscv64")]
- #[allow(dead_code)] // V-only AES is kept for GCM-SIV and explicit diagnostic paths; GCM does not select it yet.
- Riscv64Vperm([u32; EXPANDED_KEY_WORDS_128]),
+ VectorCrypto(rv_aes::Rv128RoundKeys),
/// Four-block table-free fixslice fallback for scalar RV64 without AES extensions.
#[cfg(all(target_arch = "riscv64", feature = "alloc"))]
- Riscv64Fixslice(alloc::boxed::Box),
+ Fixslice(alloc::boxed::Box),
/// No-alloc RV64 builds keep the larger fixslice key schedule inline.
#[cfg(all(target_arch = "riscv64", not(feature = "alloc")))]
- Riscv64Fixslice(rv_fixslice_aes::RvFixslice128RoundKeys),
+ Fixslice(rv_fixslice_aes::RvFixslice128RoundKeys),
}
impl Drop for Aes128EncKey {
fn drop(&mut self) {
match &mut self.inner {
+ #[cfg(not(target_arch = "riscv64"))]
Key128Inner::PortableRoundKeys(rk) => {
// SAFETY: [u32; 44] is layout-compatible with [u8; 176].
crate::traits::ct::zeroize(unsafe {
@@ -243,22 +219,15 @@ impl Drop for Aes128EncKey {
ppc_rk.zeroize();
}
#[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64ScalarCrypto(rv_rk) => {
+ Key128Inner::ScalarCrypto(rv_rk) => {
rv_rk.zeroize();
}
#[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64VectorCrypto(rv_rk) => {
+ Key128Inner::VectorCrypto(rv_rk) => {
rv_rk.zeroize();
}
#[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64Vperm(rk) => {
- // SAFETY: [u32; 44] is layout-compatible with [u8; 176].
- crate::traits::ct::zeroize(unsafe {
- core::slice::from_raw_parts_mut(rk.as_mut_ptr().cast::(), EXPANDED_KEY_WORDS_128.strict_mul(4))
- });
- }
- #[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64Fixslice(rk) => {
+ Key128Inner::Fixslice(rk) => {
rk.zeroize();
}
}
@@ -298,7 +267,7 @@ const fn gf256_mul(a: u8, b: u8) -> u8 {
prod ^= (prod >> 9).wrapping_mul(0x11b << 1);
prod ^= (prod >> 8).wrapping_mul(0x11b);
- prod as u8
+ prod.to_le_bytes()[0]
}
/// Square in GF(2^8). Equivalent to `gf256_mul(x, x)` but slightly cheaper.
@@ -350,11 +319,8 @@ const fn sbox(x: u8) -> u8 {
/// Apply SubBytes to a 32-bit word (four S-box applications).
#[inline(always)]
const fn sub_word(w: u32) -> u32 {
- let b0 = sbox((w >> 24) as u8) as u32;
- let b1 = sbox((w >> 16) as u8) as u32;
- let b2 = sbox((w >> 8) as u8) as u32;
- let b3 = sbox(w as u8) as u32;
- (b0 << 24) | (b1 << 16) | (b2 << 8) | b3
+ let [b0, b1, b2, b3] = w.to_be_bytes();
+ u32::from_be_bytes([sbox(b0), sbox(b1), sbox(b2), sbox(b3)])
}
/// Rotate a 32-bit word left by 8 bits.
@@ -471,13 +437,13 @@ pub(crate) fn aes128_expand_key_portable(key: &[u8; KEY_SIZE_128]) -> [u32; EXPA
#[cfg(all(target_arch = "riscv64", feature = "alloc"))]
#[inline]
fn riscv64_fixslice_key_inner(key: &[u8; KEY_SIZE]) -> KeyInner {
- KeyInner::Riscv64Fixslice(alloc::boxed::Box::new(rv_fixslice_aes::RvFixsliceRoundKeys::new(key)))
+ KeyInner::Fixslice(alloc::boxed::Box::new(rv_fixslice_aes::RvFixsliceRoundKeys::new(key)))
}
#[cfg(all(target_arch = "riscv64", not(feature = "alloc")))]
#[inline]
fn riscv64_fixslice_key_inner(key: &[u8; KEY_SIZE]) -> KeyInner {
- KeyInner::Riscv64Fixslice(rv_fixslice_aes::RvFixsliceRoundKeys::new(key))
+ KeyInner::Fixslice(rv_fixslice_aes::RvFixsliceRoundKeys::new(key))
}
/// Expand a 256-bit AES key into round keys.
@@ -529,7 +495,7 @@ pub(crate) fn aes256_expand_key(key: &[u8; KEY_SIZE]) -> Aes256EncKey {
let rv_keys = rv_aes::from_portable(&portable_rk);
zeroize_expanded_key_words(&mut portable_rk);
return Aes256EncKey {
- inner: KeyInner::Riscv64VectorCrypto(rv_keys),
+ inner: KeyInner::VectorCrypto(rv_keys),
};
}
if crate::platform::caps().has(crate::platform::caps::riscv::ZKNE) {
@@ -537,7 +503,7 @@ pub(crate) fn aes256_expand_key(key: &[u8; KEY_SIZE]) -> Aes256EncKey {
let rv_keys = rv_scalar_aes::from_portable(&portable_rk);
zeroize_expanded_key_words(&mut portable_rk);
return Aes256EncKey {
- inner: KeyInner::Riscv64ScalarCrypto(rv_keys),
+ inner: KeyInner::ScalarCrypto(rv_keys),
};
}
Aes256EncKey {
@@ -553,7 +519,7 @@ pub(crate) fn aes256_expand_key(key: &[u8; KEY_SIZE]) -> Aes256EncKey {
#[cfg(all(target_arch = "riscv64", feature = "alloc"))]
#[inline]
fn riscv64_fixslice_key_inner_128(key: &[u8; KEY_SIZE_128]) -> Key128Inner {
- Key128Inner::Riscv64Fixslice(alloc::boxed::Box::new(rv_fixslice_aes::RvFixslice128RoundKeys::new(
+ Key128Inner::Fixslice(alloc::boxed::Box::new(rv_fixslice_aes::RvFixslice128RoundKeys::new(
key,
)))
}
@@ -561,7 +527,7 @@ fn riscv64_fixslice_key_inner_128(key: &[u8; KEY_SIZE_128]) -> Key128Inner {
#[cfg(all(target_arch = "riscv64", not(feature = "alloc")))]
#[inline]
fn riscv64_fixslice_key_inner_128(key: &[u8; KEY_SIZE_128]) -> Key128Inner {
- Key128Inner::Riscv64Fixslice(rv_fixslice_aes::RvFixslice128RoundKeys::new(key))
+ Key128Inner::Fixslice(rv_fixslice_aes::RvFixslice128RoundKeys::new(key))
}
/// Expand a 128-bit AES key into round keys.
@@ -613,7 +579,7 @@ pub(crate) fn aes128_expand_key(key: &[u8; KEY_SIZE_128]) -> Aes128EncKey {
let rv_keys = rv_aes::from_portable_128(&portable_rk);
zeroize_expanded_key_words_128(&mut portable_rk);
return Aes128EncKey {
- inner: Key128Inner::Riscv64VectorCrypto(rv_keys),
+ inner: Key128Inner::VectorCrypto(rv_keys),
};
}
if crate::platform::caps().has(crate::platform::caps::riscv::ZKNE) {
@@ -621,7 +587,7 @@ pub(crate) fn aes128_expand_key(key: &[u8; KEY_SIZE_128]) -> Aes128EncKey {
let rv_keys = rv_scalar_aes::from_portable_128(&portable_rk);
zeroize_expanded_key_words_128(&mut portable_rk);
return Aes128EncKey {
- inner: Key128Inner::Riscv64ScalarCrypto(rv_keys),
+ inner: Key128Inner::ScalarCrypto(rv_keys),
};
}
Aes128EncKey {
@@ -641,7 +607,7 @@ pub(crate) fn aes256_expand_key_riscv_vector(key: &[u8; KEY_SIZE]) -> Aes256EncK
let rv_keys = rv_aes::from_portable(&portable_rk);
zeroize_expanded_key_words(&mut portable_rk);
Aes256EncKey {
- inner: KeyInner::Riscv64VectorCrypto(rv_keys),
+ inner: KeyInner::VectorCrypto(rv_keys),
}
}
@@ -652,15 +618,7 @@ pub(crate) fn aes256_expand_key_riscv_scalar(key: &[u8; KEY_SIZE]) -> Aes256EncK
let rv_keys = rv_scalar_aes::from_portable(&portable_rk);
zeroize_expanded_key_words(&mut portable_rk);
Aes256EncKey {
- inner: KeyInner::Riscv64ScalarCrypto(rv_keys),
- }
-}
-
-#[cfg(all(target_arch = "riscv64", feature = "aes-gcm-siv"))]
-#[inline]
-pub(crate) fn aes256_expand_key_riscv_vperm(key: &[u8; KEY_SIZE]) -> Aes256EncKey {
- Aes256EncKey {
- inner: KeyInner::Riscv64Vperm(aes256_expand_key_portable(key)),
+ inner: KeyInner::ScalarCrypto(rv_keys),
}
}
@@ -680,7 +638,7 @@ pub(crate) fn aes128_expand_key_riscv_vector(key: &[u8; KEY_SIZE_128]) -> Aes128
let rv_keys = rv_aes::from_portable_128(&portable_rk);
zeroize_expanded_key_words_128(&mut portable_rk);
Aes128EncKey {
- inner: Key128Inner::Riscv64VectorCrypto(rv_keys),
+ inner: Key128Inner::VectorCrypto(rv_keys),
}
}
@@ -691,15 +649,7 @@ pub(crate) fn aes128_expand_key_riscv_scalar(key: &[u8; KEY_SIZE_128]) -> Aes128
let rv_keys = rv_scalar_aes::from_portable_128(&portable_rk);
zeroize_expanded_key_words_128(&mut portable_rk);
Aes128EncKey {
- inner: Key128Inner::Riscv64ScalarCrypto(rv_keys),
- }
-}
-
-#[cfg(all(target_arch = "riscv64", feature = "aes-gcm-siv"))]
-#[inline]
-pub(crate) fn aes128_expand_key_riscv_vperm(key: &[u8; KEY_SIZE_128]) -> Aes128EncKey {
- Aes128EncKey {
- inner: Key128Inner::Riscv64Vperm(aes128_expand_key_portable(key)),
+ inner: Key128Inner::ScalarCrypto(rv_keys),
}
}
@@ -800,11 +750,32 @@ unsafe fn aarch64_encrypt_blocks_inline(keys: &ce::CeRoundKeys, blocks: &mut [[u
}
}
+#[cfg(all(target_arch = "aarch64", feature = "aes-gcm-siv"))]
+#[inline]
+fn gcmsiv_derive_keys_fallback(master_ek: &Aes256EncKey, nonce: &[u8; 12]) -> ([u8; 16], [u8; 32]) {
+ let mut blocks = [[0u8; BLOCK_SIZE]; 6];
+ for (counter, block) in (0u32..6).zip(&mut blocks) {
+ block[..4].copy_from_slice(&counter.to_le_bytes());
+ block[4..].copy_from_slice(nonce);
+ }
+ aes256_encrypt_blocks_ecb(master_ek, &mut blocks);
+
+ let mut auth_key = [0u8; 16];
+ let mut enc_key = [0u8; 32];
+ auth_key[..8].copy_from_slice(&blocks[0][..8]);
+ auth_key[8..].copy_from_slice(&blocks[1][..8]);
+ enc_key[..8].copy_from_slice(&blocks[2][..8]);
+ enc_key[8..16].copy_from_slice(&blocks[3][..8]);
+ enc_key[16..24].copy_from_slice(&blocks[4][..8]);
+ enc_key[24..].copy_from_slice(&blocks[5][..8]);
+ crate::traits::ct::zeroize(blocks.as_flattened_mut());
+ (auth_key, enc_key)
+}
+
/// Derive AES-256-GCM-SIV per-message keys directly with AES-CE.
///
/// # Safety
-/// Caller must ensure AES-CE is available and `master_ek` is the AArch64 AES
-/// backend variant.
+/// Caller must ensure AES-CE is available.
#[cfg(all(target_arch = "aarch64", feature = "aes-gcm-siv"))]
#[target_feature(enable = "aes,neon")]
#[inline]
@@ -813,7 +784,7 @@ pub(super) unsafe fn aarch64_gcmsiv_derive_keys_inline(
nonce: &[u8; 12],
) -> ([u8; 16], [u8; 32]) {
let KeyInner::Aarch64Aes(ce_rk) = &master_ek.inner else {
- unreachable!("AArch64 GCM-SIV KDF requires an AES-CE master key");
+ return gcmsiv_derive_keys_fallback(master_ek, nonce);
};
// SAFETY: direct AES-CE GCM-SIV KDF because:
// 1. This function's caller must guarantee AES-CE availability.
@@ -930,11 +901,30 @@ unsafe fn aarch64_encrypt_blocks_128_inline(keys: &ce::Ce128RoundKeys, blocks: &
}
}
+#[cfg(all(target_arch = "aarch64", feature = "aes-gcm-siv"))]
+#[inline]
+fn gcmsiv_derive_keys_128_fallback(master_ek: &Aes128EncKey, nonce: &[u8; 12]) -> ([u8; 16], [u8; 16]) {
+ let mut blocks = [[0u8; BLOCK_SIZE]; 4];
+ for (counter, block) in (0u32..4).zip(&mut blocks) {
+ block[..4].copy_from_slice(&counter.to_le_bytes());
+ block[4..].copy_from_slice(nonce);
+ }
+ aes128_encrypt_blocks_ecb(master_ek, &mut blocks);
+
+ let mut auth_key = [0u8; 16];
+ let mut enc_key = [0u8; 16];
+ auth_key[..8].copy_from_slice(&blocks[0][..8]);
+ auth_key[8..].copy_from_slice(&blocks[1][..8]);
+ enc_key[..8].copy_from_slice(&blocks[2][..8]);
+ enc_key[8..].copy_from_slice(&blocks[3][..8]);
+ crate::traits::ct::zeroize(blocks.as_flattened_mut());
+ (auth_key, enc_key)
+}
+
/// Derive AES-128-GCM-SIV per-message keys directly with AES-CE.
///
/// # Safety
-/// Caller must ensure AES-CE is available and `master_ek` is the AArch64 AES
-/// backend variant.
+/// Caller must ensure AES-CE is available.
#[cfg(all(target_arch = "aarch64", feature = "aes-gcm-siv"))]
#[target_feature(enable = "aes,neon")]
#[inline]
@@ -943,7 +933,7 @@ pub(super) unsafe fn aarch64_gcmsiv_derive_keys_128_inline(
nonce: &[u8; 12],
) -> ([u8; 16], [u8; 16]) {
let Key128Inner::Aarch64Aes(ce_rk) = &master_ek.inner else {
- unreachable!("AArch64 GCM-SIV KDF requires an AES-CE master key");
+ return gcmsiv_derive_keys_128_fallback(master_ek, nonce);
};
// SAFETY: direct AES-CE GCM-SIV KDF because:
// 1. This function's caller must guarantee AES-CE availability.
@@ -1679,6 +1669,7 @@ pub(super) unsafe fn s390x_encrypt_blocks_128_inline(key: &km::Km128Key, blocks:
#[inline]
pub(crate) fn aes256_encrypt_block(ek: &Aes256EncKey, block: &mut [u8; BLOCK_SIZE]) {
match &ek.inner {
+ #[cfg(not(target_arch = "riscv64"))]
KeyInner::PortableRoundKeys(rk) => aes256_encrypt_block_portable(rk, block),
#[cfg(target_arch = "x86_64")]
KeyInner::X86AesNi(ni_rk) => {
@@ -1701,27 +1692,21 @@ pub(crate) fn aes256_encrypt_block(ek: &Aes256EncKey, block: &mut [u8; BLOCK_SIZ
unsafe { ppc::encrypt_block(ppc_rk, block) }
}
#[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64ScalarCrypto(rv_rk) => {
+ KeyInner::ScalarCrypto(rv_rk) => {
// SAFETY: RvScalar variant is only constructed after runtime detection confirms Zkne.
unsafe { rv_scalar_aes::encrypt_block(rv_rk, block) }
}
#[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64VectorCrypto(rv_rk) => {
+ KeyInner::VectorCrypto(rv_rk) => {
// SAFETY: RvAes variant is only constructed after runtime detection confirms Zvkned.
unsafe { rv_aes::encrypt_block(rv_rk, block) }
}
#[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64Vperm(rk) => {
- // SAFETY: RvVperm variant is only constructed after runtime detection confirms V extension.
- unsafe { rv_vperm_aes::encrypt_block(rk, block) }
- }
- #[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64Fixslice(rk) => rv_fixslice_aes::encrypt_block(rk, block),
+ KeyInner::Fixslice(rk) => rv_fixslice_aes::encrypt_block(rk, block),
}
}
-#[cfg(any(target_arch = "riscv64", test))]
-#[allow(dead_code)]
+#[cfg(any(target_arch = "riscv64", all(test, not(target_arch = "s390x"))))]
#[inline]
pub(super) fn aes_enc_round_4_fixslice(blocks: &mut [[u8; BLOCK_SIZE]; 4], round_keys: &[[u8; BLOCK_SIZE]; 4]) {
rv_fixslice_aes::cipher_round_4(blocks, round_keys);
@@ -1736,6 +1721,7 @@ pub(super) fn aes_enc_round_4_fixslice(blocks: &mut [[u8; BLOCK_SIZE]; 4], round
#[inline]
pub(crate) fn aes128_encrypt_block(ek: &Aes128EncKey, block: &mut [u8; BLOCK_SIZE]) {
match &ek.inner {
+ #[cfg(not(target_arch = "riscv64"))]
Key128Inner::PortableRoundKeys(rk) => aes128_encrypt_block_portable(rk, block),
#[cfg(target_arch = "x86_64")]
Key128Inner::X86AesNi(ni_rk) => {
@@ -1758,22 +1744,17 @@ pub(crate) fn aes128_encrypt_block(ek: &Aes128EncKey, block: &mut [u8; BLOCK_SIZ
unsafe { ppc::encrypt_block_128(ppc_rk, block) }
}
#[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64ScalarCrypto(rv_rk) => {
- // SAFETY: Riscv64ScalarCrypto variant is only constructed after runtime detection confirms Zkne.
+ Key128Inner::ScalarCrypto(rv_rk) => {
+ // SAFETY: ScalarCrypto is only constructed after runtime detection confirms Zkne.
unsafe { rv_scalar_aes::encrypt_block_128(rv_rk, block) }
}
#[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64VectorCrypto(rv_rk) => {
- // SAFETY: Riscv64VectorCrypto variant is only constructed after runtime detection confirms Zvkned.
+ Key128Inner::VectorCrypto(rv_rk) => {
+ // SAFETY: VectorCrypto is only constructed after runtime detection confirms Zvkned.
unsafe { rv_aes::encrypt_block_128(rv_rk, block) }
}
#[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64Vperm(rk) => {
- // SAFETY: Riscv64Vperm variant is only constructed after runtime detection confirms V extension.
- unsafe { rv_vperm_aes::encrypt_block_128(rk, block) }
- }
- #[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64Fixslice(rk) => rv_fixslice_aes::encrypt_block_128(rk, block),
+ Key128Inner::Fixslice(rk) => rv_fixslice_aes::encrypt_block_128(rk, block),
}
}
@@ -1783,17 +1764,22 @@ pub(crate) fn aes128_encrypt_block(ek: &Aes128EncKey, block: &mut [u8; BLOCK_SIZ
/// instruction or the RV64 4-block kernels when available, otherwise calls
/// the per-block dispatcher. Used by `riscv64` from the AES-128 CTR paths
/// and by AES-128-GCM-SIV key derivation.
-#[cfg_attr(
- not(any(
- target_arch = "aarch64",
- target_arch = "powerpc64",
- target_arch = "riscv64",
- target_arch = "s390x",
- feature = "aes-gcm-siv",
- test
- )),
- allow(dead_code)
-)]
+// Live callers are GCM-SIV key derivation (any arch), the batch CTR path on the
+// arches that have a block-batch kernel, and the unit tests below -- `mod aes`
+// is also compiled for `aegis256` under `cfg(test)`.
+#[cfg(any(
+ test,
+ feature = "aes-gcm-siv",
+ all(
+ feature = "aes-gcm",
+ any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x"
+ )
+ )
+))]
#[inline]
pub(crate) fn aes128_encrypt_blocks_ecb(ek: &Aes128EncKey, blocks: &mut [[u8; BLOCK_SIZE]]) {
#[cfg(target_arch = "aarch64")]
@@ -1831,17 +1817,17 @@ pub(crate) fn aes128_encrypt_blocks_ecb(ek: &Aes128EncKey, blocks: &mut [[u8; BL
return;
}
#[cfg(target_arch = "riscv64")]
- if let Key128Inner::Riscv64VectorCrypto(rk) = &ek.inner {
+ if let Key128Inner::VectorCrypto(rk) = &ek.inner {
let mut offset = 0usize;
while offset.strict_add(4) <= blocks.len() {
let batch_slice = &mut blocks[offset..offset.strict_add(4)];
debug_assert_eq!(batch_slice.len(), 4);
// SAFETY: exact four-block RISC-V Zvkned AES-128 batch because:
// 1. `batch_slice` is sliced to exactly four contiguous `[u8; 16]` elements.
- // 2. `Riscv64VectorCrypto` is only constructed after runtime detection confirms Zvkned.
+ // 2. `VectorCrypto` is only constructed after runtime detection confirms Zvkned.
// 3. The mutable borrow is scoped to this loop iteration.
let batch: &mut [[u8; BLOCK_SIZE]; 4] = unsafe { &mut *batch_slice.as_mut_ptr().cast::<[[u8; BLOCK_SIZE]; 4]>() };
- // SAFETY: `Riscv64VectorCrypto` proves Zvkned availability for this key.
+ // SAFETY: `VectorCrypto` proves Zvkned availability for this key.
unsafe { rv_aes::encrypt_4blocks_128(rk, batch) };
offset = offset.strict_add(4);
}
@@ -1853,17 +1839,17 @@ pub(crate) fn aes128_encrypt_blocks_ecb(ek: &Aes128EncKey, blocks: &mut [[u8; BL
return;
}
#[cfg(target_arch = "riscv64")]
- if let Key128Inner::Riscv64ScalarCrypto(rk) = &ek.inner {
+ if let Key128Inner::ScalarCrypto(rk) = &ek.inner {
let mut offset = 0usize;
while offset.strict_add(4) <= blocks.len() {
let batch_slice = &mut blocks[offset..offset.strict_add(4)];
debug_assert_eq!(batch_slice.len(), 4);
// SAFETY: exact four-block RISC-V Zkne AES-128 batch because:
// 1. `batch_slice` is sliced to exactly four contiguous `[u8; 16]` elements.
- // 2. `Riscv64ScalarCrypto` is only constructed after runtime detection confirms Zkne.
+ // 2. `ScalarCrypto` is only constructed after runtime detection confirms Zkne.
// 3. The mutable borrow is scoped to this loop iteration.
let batch: &mut [[u8; BLOCK_SIZE]; 4] = unsafe { &mut *batch_slice.as_mut_ptr().cast::<[[u8; BLOCK_SIZE]; 4]>() };
- // SAFETY: `Riscv64ScalarCrypto` proves Zkne availability for this key.
+ // SAFETY: `ScalarCrypto` proves Zkne availability for this key.
unsafe { rv_scalar_aes::encrypt_4blocks_128(rk, batch) };
offset = offset.strict_add(4);
}
@@ -1875,26 +1861,7 @@ pub(crate) fn aes128_encrypt_blocks_ecb(ek: &Aes128EncKey, blocks: &mut [[u8; BL
return;
}
#[cfg(target_arch = "riscv64")]
- if let Key128Inner::Riscv64Vperm(rk) = &ek.inner {
- let mut offset = 0usize;
- while offset.strict_add(4) <= blocks.len() {
- let batch_slice = &mut blocks[offset..offset.strict_add(4)];
- debug_assert_eq!(batch_slice.len(), 4);
- // SAFETY: `batch_slice` is exactly 4 contiguous `[u8; 16]` elements.
- let batch: &mut [[u8; BLOCK_SIZE]; 4] = unsafe { &mut *batch_slice.as_mut_ptr().cast::<[[u8; BLOCK_SIZE]; 4]>() };
- // SAFETY: Riscv64Vperm variant is only constructed after runtime detection confirms V extension.
- unsafe { rv_vperm_aes::encrypt_4blocks_128(rk, batch) };
- offset = offset.strict_add(4);
- }
- while offset < blocks.len() {
- // SAFETY: same V-extension guarantee as the wide path above.
- unsafe { rv_vperm_aes::encrypt_block_128(rk, &mut blocks[offset]) };
- offset = offset.strict_add(1);
- }
- return;
- }
- #[cfg(target_arch = "riscv64")]
- if let Key128Inner::Riscv64Fixslice(rk) = &ek.inner {
+ if let Key128Inner::Fixslice(rk) = &ek.inner {
let mut offset = 0usize;
while offset.strict_add(4) <= blocks.len() {
let batch_slice = &mut blocks[offset..offset.strict_add(4)];
@@ -1928,6 +1895,7 @@ pub(crate) fn aes128_encrypt_blocks_ecb(ek: &Aes128EncKey, blocks: &mut [[u8; BL
/// Portable AES-128 block encryption (10 rounds).
#[inline]
+#[cfg(any(not(target_arch = "riscv64"), test))]
fn aes128_encrypt_block_portable(rk: &[u32; EXPANDED_KEY_WORDS_128], block: &mut [u8; BLOCK_SIZE]) {
// Load state as four big-endian u32 columns.
let mut s0 = u32::from_be_bytes([block[0], block[1], block[2], block[3]]);
@@ -1973,17 +1941,22 @@ fn aes128_encrypt_block_portable(rk: &[u32; EXPANDED_KEY_WORDS_128], block: &mut
/// On s390x this issues a single KM instruction for all `blocks`,
/// avoiding per-block parameter-block setup overhead. On other platforms
/// falls back to per-block dispatch.
-#[cfg_attr(
- not(any(
- target_arch = "aarch64",
- target_arch = "powerpc64",
- target_arch = "riscv64",
- target_arch = "s390x",
- feature = "aes-gcm-siv",
- test
- )),
- allow(dead_code)
-)]
+// Live callers are GCM-SIV key derivation (any arch), the batch CTR path on the
+// arches that have a block-batch kernel, and the unit tests below -- `mod aes`
+// is also compiled for `aegis256` under `cfg(test)`.
+#[cfg(any(
+ test,
+ feature = "aes-gcm-siv",
+ all(
+ feature = "aes-gcm",
+ any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x"
+ )
+ )
+))]
#[inline]
pub(crate) fn aes256_encrypt_blocks_ecb(ek: &Aes256EncKey, blocks: &mut [[u8; BLOCK_SIZE]]) {
#[cfg(target_arch = "aarch64")]
@@ -2021,17 +1994,17 @@ pub(crate) fn aes256_encrypt_blocks_ecb(ek: &Aes256EncKey, blocks: &mut [[u8; BL
return;
}
#[cfg(target_arch = "riscv64")]
- if let KeyInner::Riscv64VectorCrypto(rk) = &ek.inner {
+ if let KeyInner::VectorCrypto(rk) = &ek.inner {
let mut offset = 0usize;
while offset.strict_add(4) <= blocks.len() {
let batch_slice = &mut blocks[offset..offset.strict_add(4)];
debug_assert_eq!(batch_slice.len(), 4);
// SAFETY: exact four-block RISC-V Zvkned AES-256 batch because:
// 1. `batch_slice` is sliced to exactly four contiguous `[u8; 16]` elements.
- // 2. `Riscv64VectorCrypto` is only constructed after runtime detection confirms Zvkned.
+ // 2. `VectorCrypto` is only constructed after runtime detection confirms Zvkned.
// 3. The mutable borrow is scoped to this loop iteration.
let batch: &mut [[u8; BLOCK_SIZE]; 4] = unsafe { &mut *batch_slice.as_mut_ptr().cast::<[[u8; BLOCK_SIZE]; 4]>() };
- // SAFETY: `Riscv64VectorCrypto` proves Zvkned availability for this key.
+ // SAFETY: `VectorCrypto` proves Zvkned availability for this key.
unsafe { rv_aes::encrypt_4blocks(rk, batch) };
offset = offset.strict_add(4);
}
@@ -2043,17 +2016,17 @@ pub(crate) fn aes256_encrypt_blocks_ecb(ek: &Aes256EncKey, blocks: &mut [[u8; BL
return;
}
#[cfg(target_arch = "riscv64")]
- if let KeyInner::Riscv64ScalarCrypto(rk) = &ek.inner {
+ if let KeyInner::ScalarCrypto(rk) = &ek.inner {
let mut offset = 0usize;
while offset.strict_add(4) <= blocks.len() {
let batch_slice = &mut blocks[offset..offset.strict_add(4)];
debug_assert_eq!(batch_slice.len(), 4);
// SAFETY: exact four-block RISC-V Zkne AES-256 batch because:
// 1. `batch_slice` is sliced to exactly four contiguous `[u8; 16]` elements.
- // 2. `Riscv64ScalarCrypto` is only constructed after runtime detection confirms Zkne.
+ // 2. `ScalarCrypto` is only constructed after runtime detection confirms Zkne.
// 3. The mutable borrow is scoped to this loop iteration.
let batch: &mut [[u8; BLOCK_SIZE]; 4] = unsafe { &mut *batch_slice.as_mut_ptr().cast::<[[u8; BLOCK_SIZE]; 4]>() };
- // SAFETY: `Riscv64ScalarCrypto` proves Zkne availability for this key.
+ // SAFETY: `ScalarCrypto` proves Zkne availability for this key.
unsafe { rv_scalar_aes::encrypt_4blocks(rk, batch) };
offset = offset.strict_add(4);
}
@@ -2065,28 +2038,7 @@ pub(crate) fn aes256_encrypt_blocks_ecb(ek: &Aes256EncKey, blocks: &mut [[u8; BL
return;
}
#[cfg(target_arch = "riscv64")]
- if let KeyInner::Riscv64Vperm(rk) = &ek.inner {
- let mut offset = 0usize;
- while offset.strict_add(4) <= blocks.len() {
- let batch_slice = &mut blocks[offset..offset.strict_add(4)];
- debug_assert_eq!(batch_slice.len(), 4);
- // SAFETY: `batch_slice` is exactly 4 contiguous `[u8; 16]` elements, so
- // reborrowing it as `&mut [[u8; 16]; 4]` preserves layout and bounds.
- let batch: &mut [[u8; BLOCK_SIZE]; 4] = unsafe { &mut *batch_slice.as_mut_ptr().cast::<[[u8; BLOCK_SIZE]; 4]>() };
- // SAFETY: RvVperm variant is only constructed after runtime detection confirms V extension.
- unsafe { rv_vperm_aes::encrypt_4blocks(rk, batch) };
- offset = offset.strict_add(4);
- }
- while offset < blocks.len() {
- // SAFETY: Same runtime V-extension guarantee as above; tail stays on the
- // existing single-block kernel to avoid special-casing 1-3 blocks.
- unsafe { rv_vperm_aes::encrypt_block(rk, &mut blocks[offset]) };
- offset = offset.strict_add(1);
- }
- return;
- }
- #[cfg(target_arch = "riscv64")]
- if let KeyInner::Riscv64Fixslice(rk) = &ek.inner {
+ if let KeyInner::Fixslice(rk) = &ek.inner {
let mut offset = 0usize;
while offset.strict_add(4) <= blocks.len() {
let batch_slice = &mut blocks[offset..offset.strict_add(4)];
@@ -2120,6 +2072,7 @@ pub(crate) fn aes256_encrypt_blocks_ecb(ek: &Aes256EncKey, blocks: &mut [[u8; BL
/// Portable AES-256 block encryption.
#[inline]
+#[cfg(any(not(target_arch = "riscv64"), test))]
fn aes256_encrypt_block_portable(rk: &[u32; EXPANDED_KEY_WORDS], block: &mut [u8; BLOCK_SIZE]) {
// Load state as four big-endian u32 columns.
let mut s0 = u32::from_be_bytes([block[0], block[1], block[2], block[3]]);
@@ -2162,12 +2115,14 @@ fn aes256_encrypt_block_portable(rk: &[u32; EXPANDED_KEY_WORDS], block: &mut [u8
/// Extract byte `row` from a big-endian column word.
#[inline(always)]
+#[cfg(any(not(target_arch = "riscv64"), test))]
const fn col_byte(col: u32, row: usize) -> u8 {
- (col >> (24u32.strict_sub((row as u32).strict_mul(8)))) as u8
+ (col >> 24usize.strict_sub(row.strict_mul(8))).to_le_bytes()[0]
}
/// xtime: multiply by x in GF(2^8), i.e. x << 1 with conditional reduction.
#[inline(always)]
+#[cfg(any(not(target_arch = "riscv64"), test))]
const fn xtime(x: u8) -> u8 {
let hi = (x >> 7) & 1;
(x << 1) ^ (hi.wrapping_mul(0x1b))
@@ -2178,6 +2133,7 @@ const fn xtime(x: u8) -> u8 {
/// Input/output: four column words in big-endian byte order.
/// AddRoundKey is done by the caller.
#[inline(always)]
+#[cfg(any(not(target_arch = "riscv64"), test))]
const fn aes_round(s0: u32, s1: u32, s2: u32, s3: u32) -> (u32, u32, u32, u32) {
// After SubBytes + ShiftRows, column j contains:
// row 0 from column j, row 1 from (j+1)%4, row 2 from (j+2)%4, row 3 from (j+3)%4
@@ -2211,6 +2167,7 @@ const fn aes_round(s0: u32, s1: u32, s2: u32, s3: u32) -> (u32, u32, u32, u32) {
/// Final AES round: SubBytes → ShiftRows (no MixColumns).
#[inline(always)]
+#[cfg(any(not(target_arch = "riscv64"), test))]
const fn aes_final_round(s0: u32, s1: u32, s2: u32, s3: u32) -> (u32, u32, u32, u32) {
let t0 = (sbox(col_byte(s0, 0)) as u32) << 24
| (sbox(col_byte(s1, 1)) as u32) << 16
@@ -2234,6 +2191,7 @@ const fn aes_final_round(s0: u32, s1: u32, s2: u32, s3: u32) -> (u32, u32, u32,
/// MixColumns on a single column [b0, b1, b2, b3].
#[inline(always)]
+#[cfg(any(not(target_arch = "riscv64"), test))]
const fn mix_column(col: [u8; 4]) -> u32 {
let [b0, b1, b2, b3] = col;
@@ -2307,10 +2265,7 @@ pub(crate) fn aes256_ctr32_encrypt(ek: &Aes256EncKey, initial_counter: &[u8; BLO
#[cfg(target_arch = "riscv64")]
if matches!(
&ek.inner,
- KeyInner::Riscv64VectorCrypto(_)
- | KeyInner::Riscv64ScalarCrypto(_)
- | KeyInner::Riscv64Vperm(_)
- | KeyInner::Riscv64Fixslice(_)
+ KeyInner::VectorCrypto(_) | KeyInner::ScalarCrypto(_) | KeyInner::Fixslice(_)
) {
let iv_suffix: [u8; 12] = {
let mut buf = [0u8; 12];
@@ -2367,7 +2322,7 @@ pub(crate) fn aes256_ctr32_encrypt(ek: &Aes256EncKey, initial_counter: &[u8; BLO
aes256_encrypt_blocks_ecb(ek, &mut keystream[..block_count]);
let processed = xor_keystream_tail(data, offset, &keystream, block_count);
offset = offset.strict_add(processed);
- ctr = ctr.wrapping_add(block_count as u32);
+ ctr = ctr.wrapping_add(u32::from(block_count.to_le_bytes()[0]));
}
}
@@ -2416,10 +2371,7 @@ pub(crate) fn aes128_ctr32_encrypt(ek: &Aes128EncKey, initial_counter: &[u8; BLO
#[cfg(target_arch = "riscv64")]
if matches!(
&ek.inner,
- Key128Inner::Riscv64VectorCrypto(_)
- | Key128Inner::Riscv64ScalarCrypto(_)
- | Key128Inner::Riscv64Vperm(_)
- | Key128Inner::Riscv64Fixslice(_)
+ Key128Inner::VectorCrypto(_) | Key128Inner::ScalarCrypto(_) | Key128Inner::Fixslice(_)
) {
let iv_suffix: [u8; 12] = {
let mut buf = [0u8; 12];
@@ -2476,7 +2428,7 @@ pub(crate) fn aes128_ctr32_encrypt(ek: &Aes128EncKey, initial_counter: &[u8; BLO
aes128_encrypt_blocks_ecb(ek, &mut keystream[..block_count]);
let processed = xor_keystream_tail(data, offset, &keystream, block_count);
offset = offset.strict_add(processed);
- ctr = ctr.wrapping_add(block_count as u32);
+ ctr = ctr.wrapping_add(u32::from(block_count.to_le_bytes()[0]));
}
}
@@ -2528,10 +2480,8 @@ fn aes256_ctr32_be_uses_block_batch(ek: &Aes256EncKey) -> bool {
#[cfg(target_arch = "s390x")]
KeyInner::S390xMsa(_) => true,
#[cfg(target_arch = "riscv64")]
- KeyInner::Riscv64VectorCrypto(_)
- | KeyInner::Riscv64ScalarCrypto(_)
- | KeyInner::Riscv64Vperm(_)
- | KeyInner::Riscv64Fixslice(_) => true,
+ KeyInner::VectorCrypto(_) | KeyInner::ScalarCrypto(_) | KeyInner::Fixslice(_) => true,
+ #[cfg(not(target_arch = "riscv64"))]
_ => false,
}
}
@@ -2555,10 +2505,8 @@ fn aes128_ctr32_be_uses_block_batch(ek: &Aes128EncKey) -> bool {
#[cfg(target_arch = "s390x")]
Key128Inner::S390xMsa(_) => true,
#[cfg(target_arch = "riscv64")]
- Key128Inner::Riscv64VectorCrypto(_)
- | Key128Inner::Riscv64ScalarCrypto(_)
- | Key128Inner::Riscv64Vperm(_)
- | Key128Inner::Riscv64Fixslice(_) => true,
+ Key128Inner::VectorCrypto(_) | Key128Inner::ScalarCrypto(_) | Key128Inner::Fixslice(_) => true,
+ #[cfg(not(target_arch = "riscv64"))]
_ => false,
}
}
@@ -2674,7 +2622,7 @@ pub(crate) fn aes256_ctr32_encrypt_be(ek: &Aes256EncKey, initial_counter: &[u8;
aes256_encrypt_blocks_ecb(ek, &mut keystream[..block_count]);
let processed = xor_keystream_tail(data, offset, &keystream, block_count);
offset = offset.strict_add(processed);
- ctr = ctr.wrapping_add(block_count as u32);
+ ctr = ctr.wrapping_add(u32::from(block_count.to_le_bytes()[0]));
}
}
@@ -2724,13 +2672,13 @@ unsafe fn x86_gcm_ctr_blocks_be_4(iv_words: [u32; 3], ctr: u32) -> core::arch::x
let c1 = ctr.wrapping_add(1).swap_bytes();
let c2 = ctr.wrapping_add(2).swap_bytes();
let c3 = ctr.wrapping_add(3).swap_bytes();
- let iv0 = iv_words[0] as i32;
- let iv1 = iv_words[1] as i32;
- let iv2 = iv_words[2] as i32;
- let b0 = _mm_set_epi32(c0 as i32, iv2, iv1, iv0);
- let b1 = _mm_set_epi32(c1 as i32, iv2, iv1, iv0);
- let b2 = _mm_set_epi32(c2 as i32, iv2, iv1, iv0);
- let b3 = _mm_set_epi32(c3 as i32, iv2, iv1, iv0);
+ let iv0 = iv_words[0].cast_signed();
+ let iv1 = iv_words[1].cast_signed();
+ let iv2 = iv_words[2].cast_signed();
+ let b0 = _mm_set_epi32(c0.cast_signed(), iv2, iv1, iv0);
+ let b1 = _mm_set_epi32(c1.cast_signed(), iv2, iv1, iv0);
+ let b2 = _mm_set_epi32(c2.cast_signed(), iv2, iv1, iv0);
+ let b3 = _mm_set_epi32(c3.cast_signed(), iv2, iv1, iv0);
let z = _mm512_zextsi128_si512(b0);
let z = _mm512_inserti32x4(z, b1, 1);
@@ -2738,6 +2686,10 @@ unsafe fn x86_gcm_ctr_blocks_be_4(iv_words: [u32; 3], ctr: u32) -> core::arch::x
_mm512_inserti32x4(z, b3, 3)
}
+/// Build sixteen big-endian GCM counter blocks in four VAES registers.
+///
+/// # Safety
+/// Caller must ensure AVX-512F and AVX-512BW are available.
#[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
#[target_feature(enable = "avx512f,avx512bw")]
#[inline]
@@ -2752,9 +2704,14 @@ unsafe fn x86_gcm_ctr_blocks_be_16(
) {
use core::arch::x86_64::*;
- let iv = _mm_set_epi32(0, iv_words[2] as i32, iv_words[1] as i32, iv_words[0] as i32);
+ let iv = _mm_set_epi32(
+ 0,
+ iv_words[2].cast_signed(),
+ iv_words[1].cast_signed(),
+ iv_words[0].cast_signed(),
+ );
let template = _mm512_broadcast_i32x4(iv);
- let ctrs = _mm512_set1_epi32(ctr as i32);
+ let ctrs = _mm512_set1_epi32(ctr.cast_signed());
let bswap = _mm512_broadcast_i32x4(_mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3));
let offsets0 = _mm512_set_epi32(3, 0, 0, 0, 2, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0);
@@ -2775,54 +2732,21 @@ unsafe fn x86_gcm_ctr_blocks_be_16(
#[cfg(all(target_arch = "x86_64", feature = "aes-gcm", test))]
#[target_feature(enable = "avx2")]
#[inline]
+/// # Safety
+/// Caller must ensure AVX2 is available.
unsafe fn x86_gcm_ctr_blocks_be_2(iv_words: [u32; 3], ctr: u32) -> core::arch::x86_64::__m256i {
use core::arch::x86_64::*;
- let p0 = iv_words[0] as i32;
- let p1 = iv_words[1] as i32;
- let p2 = iv_words[2] as i32;
- let b0 = _mm_set_epi32(ctr.to_be() as i32, p2, p1, p0);
- let b1 = _mm_set_epi32(ctr.wrapping_add(1).to_be() as i32, p2, p1, p0);
+ let p0 = iv_words[0].cast_signed();
+ let p1 = iv_words[1].cast_signed();
+ let p2 = iv_words[2].cast_signed();
+ let b0 = _mm_set_epi32(ctr.to_be().cast_signed(), p2, p1, p0);
+ let b1 = _mm_set_epi32(ctr.wrapping_add(1).to_be().cast_signed(), p2, p1, p0);
let z = _mm256_castsi128_si256(b0);
_mm256_inserti128_si256(z, b1, 1)
}
-#[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
-#[target_feature(enable = "avx2")]
-#[inline]
-#[allow(dead_code)]
-unsafe fn x86_gcm_ctr_blocks_be_8_y256(
- iv_words: [u32; 3],
- ctr: u32,
-) -> (
- core::arch::x86_64::__m256i,
- core::arch::x86_64::__m256i,
- core::arch::x86_64::__m256i,
- core::arch::x86_64::__m256i,
-) {
- use core::arch::x86_64::*;
-
- let iv = _mm_set_epi32(0, iv_words[2] as i32, iv_words[1] as i32, iv_words[0] as i32);
- let template = _mm256_broadcastsi128_si256(iv);
- let ctrs = _mm256_set1_epi32(ctr as i32);
- let bswap = _mm256_broadcastsi128_si256(_mm_set_epi8(12, 13, 14, 15, 8, 9, 10, 11, 4, 5, 6, 7, 0, 1, 2, 3));
-
- let offsets0 = _mm256_set_epi32(1, 0, 0, 0, 0, 0, 0, 0);
- let offsets1 = _mm256_set_epi32(3, 0, 0, 0, 2, 0, 0, 0);
- let offsets2 = _mm256_set_epi32(5, 0, 0, 0, 4, 0, 0, 0);
- let offsets3 = _mm256_set_epi32(7, 0, 0, 0, 6, 0, 0, 0);
-
- macro_rules! make {
- ($offsets:expr) => {{
- let be = _mm256_shuffle_epi8(_mm256_add_epi32(ctrs, $offsets), bswap);
- _mm256_blend_epi32::<0x88>(template, be)
- }};
- }
-
- (make!(offsets0), make!(offsets1), make!(offsets2), make!(offsets3))
-}
-
/// Build one big-endian GCM counter block directly in an XMM register.
///
/// # Safety
@@ -2834,10 +2758,10 @@ unsafe fn x86_gcm_ctr_block_be(iv_words: [u32; 3], ctr: u32) -> core::arch::x86_
use core::arch::x86_64::*;
_mm_set_epi32(
- ctr.swap_bytes() as i32,
- iv_words[2] as i32,
- iv_words[1] as i32,
- iv_words[0] as i32,
+ ctr.swap_bytes().cast_signed(),
+ iv_words[2].cast_signed(),
+ iv_words[1].cast_signed(),
+ iv_words[0].cast_signed(),
)
}
@@ -2851,13 +2775,13 @@ unsafe fn x86_gcm_ctr_block_be(iv_words: [u32; 3], ctr: u32) -> core::arch::x86_
unsafe fn x86_gcmsiv_ctr_blocks_le_4(suffix_words: [u32; 3], ctr: u32) -> core::arch::x86_64::__m512i {
use core::arch::x86_64::*;
- let s0 = suffix_words[0] as i32;
- let s1 = suffix_words[1] as i32;
- let s2 = suffix_words[2] as i32;
- let b0 = _mm_set_epi32(s2, s1, s0, ctr as i32);
- let b1 = _mm_set_epi32(s2, s1, s0, ctr.wrapping_add(1) as i32);
- let b2 = _mm_set_epi32(s2, s1, s0, ctr.wrapping_add(2) as i32);
- let b3 = _mm_set_epi32(s2, s1, s0, ctr.wrapping_add(3) as i32);
+ let s0 = suffix_words[0].cast_signed();
+ let s1 = suffix_words[1].cast_signed();
+ let s2 = suffix_words[2].cast_signed();
+ let b0 = _mm_set_epi32(s2, s1, s0, ctr.cast_signed());
+ let b1 = _mm_set_epi32(s2, s1, s0, ctr.wrapping_add(1).cast_signed());
+ let b2 = _mm_set_epi32(s2, s1, s0, ctr.wrapping_add(2).cast_signed());
+ let b3 = _mm_set_epi32(s2, s1, s0, ctr.wrapping_add(3).cast_signed());
let z = _mm512_zextsi128_si512(b0);
let z = _mm512_inserti32x4(z, b1, 1);
@@ -3184,7 +3108,7 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_128.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
} else if data.len() >= 1024 {
x86_64_asm::rscrypto_aes256_gcm_seal_64x_vaes512_x86_64_linux(
@@ -3193,7 +3117,7 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_64.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
} else {
x86_64_asm::rscrypto_aes256_gcm_seal_16x_vaes512_x86_64_linux(
@@ -3202,7 +3126,7 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_32.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
}
acc = state.acc();
@@ -3360,7 +3284,7 @@ pub(crate) unsafe fn aes256_ctr32_decrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_128.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
} else if data.len() >= 1024 {
x86_64_asm::rscrypto_aes256_gcm_open_64x_vaes512_x86_64_linux(
@@ -3369,7 +3293,7 @@ pub(crate) unsafe fn aes256_ctr32_decrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_64.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
} else {
x86_64_asm::rscrypto_aes256_gcm_open_16x_vaes512_x86_64_linux(
@@ -3378,7 +3302,7 @@ pub(crate) unsafe fn aes256_ctr32_decrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_32.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
}
acc = state.acc();
@@ -3465,39 +3389,33 @@ pub(crate) unsafe fn aes256_ctr32_decrypt_be_wide_ghash(
}
}
-/// AES-256 CTR encryption fused with 256-bit VAES/VPCLMUL GHASH accumulation.
+/// AES-256 CTR encryption fused with PMULL GHASH accumulation for GCM sealing.
///
-/// This avoids ZMM data-path pressure on AMD while preserving the fused
-/// counter/AES/XOR/GHASH structure used by the 512-bit path.
+/// Encrypts `data` in place and returns the GHASH accumulator after the
+/// ciphertext has been folded in. The incoming `acc` is normally the GHASH
+/// state after AAD processing.
///
/// # Safety
-/// Caller must ensure AVX2 + AVX-512F + AVX-512VL + VAES + VPCLMULQDQ +
-/// PCLMULQDQ + AES + SSE2 + SSSE3.
-#[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
-#[target_feature(enable = "aes,sse2,ssse3,avx2,avx512f,avx512vl,vaes,vpclmulqdq,pclmulqdq")]
-#[allow(dead_code)]
-pub(crate) unsafe fn aes256_ctr32_encrypt_be_y256_ghash(
+/// Caller must ensure AES-CE and PMULL are available.
+#[cfg(all(target_arch = "aarch64", feature = "aes-gcm"))]
+#[target_feature(enable = "aes,neon")]
+pub(crate) unsafe fn aes256_ctr32_encrypt_be_aarch64_ghash(
ek: &Aes256EncKey,
initial_counter: &[u8; BLOCK_SIZE],
data: &mut [u8],
mut acc: u128,
- h_polyval: u128,
- h_powers_rev: &[u128; 4],
- h_powers_rev_8: &[u128; 8],
+ tables: &Aarch64GcmTables<'_>,
) -> u128 {
- use core::arch::x86_64::*;
-
- // SAFETY: fused x86 VAES-256 AES-GCM sealing because:
- // 1. This function's caller guarantees all required x86 target features.
- // 2. `data` is a valid mutable byte slice; all pointer arithmetic stays inside checked chunk
- // bounds.
- // 3. GHASH folds ciphertext registers after encryption, matching GCM authentication semantics.
+ // SAFETY: fused aarch64 AES-GCM sealing because:
+ // 1. This function's caller guarantees AES-CE and PMULL availability.
+ // 2. `data` is a valid mutable byte slice; all chunk processing uses checked slice ranges and the
+ // tail path handles partial blocks.
unsafe {
- let ni_rk = match &ek.inner {
- KeyInner::X86AesNi(rk) => rk,
+ let ce_rk = match &ek.inner {
+ KeyInner::Aarch64Aes(rk) => rk,
_ => {
aes256_ctr32_encrypt_be(ek, initial_counter, data);
- return ghash_ciphertext_fallback(acc, h_polyval, data);
+ return ghash_ciphertext_fallback(acc, tables.h_polyval, data);
}
};
@@ -3506,11 +3424,6 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_be_y256_ghash(
buf.copy_from_slice(&initial_counter[..12]);
buf
};
- let iv_words = [
- u32::from_le_bytes([iv_prefix[0], iv_prefix[1], iv_prefix[2], iv_prefix[3]]),
- u32::from_le_bytes([iv_prefix[4], iv_prefix[5], iv_prefix[6], iv_prefix[7]]),
- u32::from_le_bytes([iv_prefix[8], iv_prefix[9], iv_prefix[10], iv_prefix[11]]),
- ];
let mut ctr = u32::from_be_bytes([
initial_counter[12],
initial_counter[13],
@@ -3519,89 +3432,28 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_be_y256_ghash(
]);
let mut offset = 0usize;
- #[cfg(target_os = "linux")]
- if data.len() >= 128 {
- let mut state = x86_64_asm::AesGcmX86State::new(acc, ctr);
- // SAFETY: external x86-64 VAES-256 AES-256-GCM seal kernel because:
- // 1. This target-feature function is only entered after VAES, VPCLMULQDQ, AVX2, AVX-512VL, AES-NI,
- // PCLMULQDQ, SSE2, and SSSE3 were selected by runtime/backend dispatch.
- // 2. `ni_rk.as_ptr()` addresses 15 initialized 128-bit AES-256 round keys.
- // 3. `initial_counter` points to the full 16-byte GCM counter block, and `data` is valid for
- // `data.len()` mutable bytes.
- // 4. `h_powers_rev_8` contains exactly the [H^8..H] powers required by the 8-block fold.
- // 5. The kernel only processes complete 128-byte chunks and reports the processed byte count so the
- // Rust fallback below handles every remaining full/partial tail.
- x86_64_asm::rscrypto_aes256_gcm_seal_8x_vaes256_x86_64_linux(
- ni_rk.as_ptr(),
- initial_counter.as_ptr(),
- data.as_mut_ptr(),
- data.len(),
- h_powers_rev_8.as_ptr(),
- &mut state,
- );
- acc = state.acc();
- ctr = state.ctr;
- offset = state.processed;
- }
-
- while offset.strict_add(128) <= data.len() {
- let (ctr0, ctr1, ctr2, ctr3) = x86_gcm_ctr_blocks_be_8_y256(iv_words, ctr);
- let (ks0, ks1, ks2, ks3) = ni::encrypt_8blocks_y256(ni_rk, ctr0, ctr1, ctr2, ctr3);
-
- let p0 = _mm256_loadu_si256(data.as_ptr().add(offset).cast());
- let c0 = _mm256_xor_si256(p0, ks0);
- _mm256_storeu_si256(data.as_mut_ptr().add(offset).cast(), c0);
-
- let p1 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(32)).cast());
- let c1 = _mm256_xor_si256(p1, ks1);
- _mm256_storeu_si256(data.as_mut_ptr().add(offset.strict_add(32)).cast(), c1);
-
- let p2 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(64)).cast());
- let c2 = _mm256_xor_si256(p2, ks2);
- _mm256_storeu_si256(data.as_mut_ptr().add(offset.strict_add(64)).cast(), c2);
-
- let p3 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(96)).cast());
- let c3 = _mm256_xor_si256(p3, ks3);
- _mm256_storeu_si256(data.as_mut_ptr().add(offset.strict_add(96)).cast(), c3);
- acc = super::polyval::x86_aggregate_8blocks_be_lanes_256_inline(acc, h_powers_rev_8, c0, c1, c2, c3);
-
- ctr = ctr.wrapping_add(8);
- offset = offset.strict_add(128);
+ if offset.strict_add(128) <= data.len() {
+ let state = ce::encrypt_ctr32_be_xor_ghash_128b_chunks_core(ce_rk, &iv_prefix, ctr, data, acc, tables);
+ acc = state.0;
+ ctr = state.1;
+ offset = state.2;
}
while offset.strict_add(64) <= data.len() {
- let ctr0 = x86_gcm_ctr_block_be(iv_words, ctr);
- let ctr1 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(1));
- let ctr2 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(2));
- let ctr3 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(3));
- let (ks0, ks1, ks2, ks3) = ni::encrypt_4blocks_aesni(ni_rk, ctr0, ctr1, ctr2, ctr3);
-
- let ptr = data.as_mut_ptr().add(offset);
- let p0 = _mm_loadu_si128(ptr.cast());
- let p1 = _mm_loadu_si128(ptr.add(16).cast());
- let p2 = _mm_loadu_si128(ptr.add(32).cast());
- let p3 = _mm_loadu_si128(ptr.add(48).cast());
- let c0 = _mm_xor_si128(p0, ks0);
- let c1 = _mm_xor_si128(p1, ks1);
- let c2 = _mm_xor_si128(p2, ks2);
- let c3 = _mm_xor_si128(p3, ks3);
- _mm_storeu_si128(ptr.cast(), c0);
- _mm_storeu_si128(ptr.add(16).cast(), c1);
- _mm_storeu_si128(ptr.add(32).cast(), c2);
- _mm_storeu_si128(ptr.add(48).cast(), c3);
- acc = super::polyval::x86_pclmul_aggregate_4blocks_be_xmm_inline(acc, h_powers_rev, c0, c1, c2, c3);
-
+ let end = offset.strict_add(64);
+ let blocks = ce::encrypt_ctr32_be_xor_4blocks_core(ce_rk, &iv_prefix, ctr, &mut data[offset..end]);
+ acc = super::polyval::aarch64_aggregate_4blocks_inline(acc, tables.h_powers_rev, &blocks);
ctr = ctr.wrapping_add(4);
- offset = offset.strict_add(64);
+ offset = end;
}
while offset < data.len() {
- let mut counter_block = [0u8; 16];
+ let mut counter_block = [0u8; BLOCK_SIZE];
counter_block[..12].copy_from_slice(&iv_prefix);
counter_block[12..16].copy_from_slice(&ctr.to_be_bytes());
let mut keystream = counter_block;
- ni::encrypt_block(ni_rk, &mut keystream);
+ ce::encrypt_block_core(ce_rk, &mut keystream);
let remaining = data.len().strict_sub(offset);
if remaining >= BLOCK_SIZE {
@@ -3612,7 +3464,7 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_be_y256_ghash(
let ciphertext = xored.to_ne_bytes();
data[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&ciphertext);
acc ^= u128::from_be_bytes(ciphertext);
- acc = super::polyval::x86_clmul128_reduce_inline(acc, h_polyval);
+ acc = super::polyval::aarch64_clmul128_reduce_inline(acc, tables.h_polyval);
offset = offset.strict_add(BLOCK_SIZE);
} else {
let mut block = [0u8; BLOCK_SIZE];
@@ -3623,7 +3475,7 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_be_y256_ghash(
i = i.strict_add(1);
}
acc ^= u128::from_be_bytes(block);
- acc = super::polyval::x86_clmul128_reduce_inline(acc, h_polyval);
+ acc = super::polyval::aarch64_clmul128_reduce_inline(acc, tables.h_polyval);
offset = offset.strict_add(remaining);
}
ctr = ctr.wrapping_add(1);
@@ -3633,34 +3485,33 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_be_y256_ghash(
}
}
-/// AES-256 CTR decryption fused with 256-bit VAES/VPCLMUL GHASH accumulation.
+/// AES-256 CTR decryption fused with PMULL GHASH accumulation for GCM open.
+///
+/// Folds ciphertext into GHASH, decrypts the same chunk in place, and returns
+/// the accumulator. Authentication is still finalized and checked by the
+/// caller, which zeroizes the buffer on failure.
///
/// # Safety
-/// Caller must ensure AVX2 + AVX-512F + AVX-512VL + VAES + VPCLMULQDQ +
-/// PCLMULQDQ + AES + SSE2 + SSSE3.
-#[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
-#[target_feature(enable = "aes,sse2,ssse3,avx2,avx512f,avx512vl,vaes,vpclmulqdq,pclmulqdq")]
-#[allow(dead_code)]
-pub(crate) unsafe fn aes256_ctr32_decrypt_be_y256_ghash(
+/// Caller must ensure AES-CE and PMULL are available.
+#[cfg(all(target_arch = "aarch64", feature = "aes-gcm"))]
+#[target_feature(enable = "aes,neon")]
+pub(crate) unsafe fn aes256_ctr32_decrypt_be_aarch64_ghash(
ek: &Aes256EncKey,
initial_counter: &[u8; BLOCK_SIZE],
data: &mut [u8],
mut acc: u128,
- h_polyval: u128,
- h_powers_rev: &[u128; 4],
- h_powers_rev_8: &[u128; 8],
+ tables: &Aarch64GcmTables<'_>,
) -> u128 {
- use core::arch::x86_64::*;
-
- // SAFETY: fused x86 VAES-256 AES-GCM opening because:
- // 1. This function's caller guarantees all required x86 target features.
- // 2. Ciphertext registers are folded into GHASH before plaintext is stored back.
- // 3. All pointer arithmetic stays inside checked chunk bounds.
+ // SAFETY: fused aarch64 AES-GCM opening because:
+ // 1. This function's caller guarantees AES-CE and PMULL availability.
+ // 2. `data` is a valid mutable ciphertext slice. Ciphertext blocks are copied before in-place
+ // decryption, so GHASH always authenticates the original bytes.
+ // 3. All chunk processing uses checked slice bounds and the tail path handles partial blocks.
unsafe {
- let ni_rk = match &ek.inner {
- KeyInner::X86AesNi(rk) => rk,
+ let ce_rk = match &ek.inner {
+ KeyInner::Aarch64Aes(rk) => rk,
_ => {
- acc = ghash_ciphertext_fallback(acc, h_polyval, data);
+ acc = ghash_ciphertext_fallback(acc, tables.h_polyval, data);
aes256_ctr32_encrypt_be(ek, initial_counter, data);
return acc;
}
@@ -3671,275 +3522,16 @@ pub(crate) unsafe fn aes256_ctr32_decrypt_be_y256_ghash(
buf.copy_from_slice(&initial_counter[..12]);
buf
};
- let iv_words = [
- u32::from_le_bytes([iv_prefix[0], iv_prefix[1], iv_prefix[2], iv_prefix[3]]),
- u32::from_le_bytes([iv_prefix[4], iv_prefix[5], iv_prefix[6], iv_prefix[7]]),
- u32::from_le_bytes([iv_prefix[8], iv_prefix[9], iv_prefix[10], iv_prefix[11]]),
- ];
let mut ctr = u32::from_be_bytes([
initial_counter[12],
initial_counter[13],
initial_counter[14],
initial_counter[15],
]);
- let mut offset = 0usize;
-
- #[cfg(target_os = "linux")]
- if data.len() >= 128 {
- let mut state = x86_64_asm::AesGcmX86State::new(acc, ctr);
- // SAFETY: external x86-64 VAES-256 AES-256-GCM open kernel because:
- // 1. This target-feature function is only entered after VAES, VPCLMULQDQ, AVX2, AVX-512VL, AES-NI,
- // PCLMULQDQ, SSE2, and SSSE3 were selected by runtime/backend dispatch.
- // 2. `ni_rk.as_ptr()` addresses 15 initialized 128-bit AES-256 round keys.
- // 3. `initial_counter` points to the full 16-byte GCM counter block, and `data` is valid for
- // `data.len()` mutable ciphertext bytes.
- // 4. The kernel folds ciphertext into GHASH before storing plaintext.
- // 5. The kernel only processes complete 128-byte chunks and reports the processed byte count so the
- // Rust fallback below handles every remaining full/partial tail.
- x86_64_asm::rscrypto_aes256_gcm_open_8x_vaes256_x86_64_linux(
- ni_rk.as_ptr(),
- initial_counter.as_ptr(),
- data.as_mut_ptr(),
- data.len(),
- h_powers_rev_8.as_ptr(),
- &mut state,
- );
- acc = state.acc();
- ctr = state.ctr;
- offset = state.processed;
- }
-
- while offset.strict_add(128) <= data.len() {
- let (ctr0, ctr1, ctr2, ctr3) = x86_gcm_ctr_blocks_be_8_y256(iv_words, ctr);
- let (ks0, ks1, ks2, ks3) = ni::encrypt_8blocks_y256(ni_rk, ctr0, ctr1, ctr2, ctr3);
-
- let c0 = _mm256_loadu_si256(data.as_ptr().add(offset).cast());
- let c1 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(32)).cast());
- let c2 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(64)).cast());
- let c3 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(96)).cast());
- acc = super::polyval::x86_aggregate_8blocks_be_lanes_256_inline(acc, h_powers_rev_8, c0, c1, c2, c3);
-
- _mm256_storeu_si256(data.as_mut_ptr().add(offset).cast(), _mm256_xor_si256(c0, ks0));
- _mm256_storeu_si256(
- data.as_mut_ptr().add(offset.strict_add(32)).cast(),
- _mm256_xor_si256(c1, ks1),
- );
- _mm256_storeu_si256(
- data.as_mut_ptr().add(offset.strict_add(64)).cast(),
- _mm256_xor_si256(c2, ks2),
- );
- _mm256_storeu_si256(
- data.as_mut_ptr().add(offset.strict_add(96)).cast(),
- _mm256_xor_si256(c3, ks3),
- );
-
- ctr = ctr.wrapping_add(8);
- offset = offset.strict_add(128);
- }
-
- while offset.strict_add(64) <= data.len() {
- let ctr0 = x86_gcm_ctr_block_be(iv_words, ctr);
- let ctr1 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(1));
- let ctr2 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(2));
- let ctr3 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(3));
- let (ks0, ks1, ks2, ks3) = ni::encrypt_4blocks_aesni(ni_rk, ctr0, ctr1, ctr2, ctr3);
-
- let ptr = data.as_mut_ptr().add(offset);
- let c0 = _mm_loadu_si128(ptr.cast());
- let c1 = _mm_loadu_si128(ptr.add(16).cast());
- let c2 = _mm_loadu_si128(ptr.add(32).cast());
- let c3 = _mm_loadu_si128(ptr.add(48).cast());
- acc = super::polyval::x86_pclmul_aggregate_4blocks_be_xmm_inline(acc, h_powers_rev, c0, c1, c2, c3);
- _mm_storeu_si128(ptr.cast(), _mm_xor_si128(c0, ks0));
- _mm_storeu_si128(ptr.add(16).cast(), _mm_xor_si128(c1, ks1));
- _mm_storeu_si128(ptr.add(32).cast(), _mm_xor_si128(c2, ks2));
- _mm_storeu_si128(ptr.add(48).cast(), _mm_xor_si128(c3, ks3));
-
- ctr = ctr.wrapping_add(4);
- offset = offset.strict_add(64);
- }
-
- while offset < data.len() {
- let mut counter_block = [0u8; 16];
- counter_block[..12].copy_from_slice(&iv_prefix);
- counter_block[12..16].copy_from_slice(&ctr.to_be_bytes());
-
- let mut keystream = counter_block;
- ni::encrypt_block(ni_rk, &mut keystream);
-
- let remaining = data.len().strict_sub(offset);
- if remaining >= BLOCK_SIZE {
- let mut ciphertext = [0u8; BLOCK_SIZE];
- ciphertext.copy_from_slice(&data[offset..offset.strict_add(BLOCK_SIZE)]);
- acc ^= u128::from_be_bytes(ciphertext);
- acc = super::polyval::x86_clmul128_reduce_inline(acc, h_polyval);
-
- let plaintext = u128::from_ne_bytes(ciphertext) ^ u128::from_ne_bytes(keystream);
- data[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&plaintext.to_ne_bytes());
- offset = offset.strict_add(BLOCK_SIZE);
- } else {
- let mut block = [0u8; BLOCK_SIZE];
- block[..remaining].copy_from_slice(&data[offset..offset.strict_add(remaining)]);
- acc ^= u128::from_be_bytes(block);
- acc = super::polyval::x86_clmul128_reduce_inline(acc, h_polyval);
-
- let mut i = 0usize;
- while i < remaining {
- data[offset.strict_add(i)] ^= keystream[i];
- i = i.strict_add(1);
- }
- offset = offset.strict_add(remaining);
- }
- ctr = ctr.wrapping_add(1);
- }
-
- acc
- }
-}
-
-/// AES-256 CTR encryption fused with PMULL GHASH accumulation for GCM sealing.
-///
-/// Encrypts `data` in place and returns the GHASH accumulator after the
-/// ciphertext has been folded in. The incoming `acc` is normally the GHASH
-/// state after AAD processing.
-///
-/// # Safety
-/// Caller must ensure AES-CE and PMULL are available.
-#[cfg(all(target_arch = "aarch64", feature = "aes-gcm"))]
-#[target_feature(enable = "aes,neon")]
-pub(crate) unsafe fn aes256_ctr32_encrypt_be_aarch64_ghash(
- ek: &Aes256EncKey,
- initial_counter: &[u8; BLOCK_SIZE],
- data: &mut [u8],
- mut acc: u128,
- tables: &Aarch64GcmTables<'_>,
-) -> u128 {
- // SAFETY: fused aarch64 AES-GCM sealing because:
- // 1. This function's caller guarantees AES-CE and PMULL availability.
- // 2. `data` is a valid mutable byte slice; all chunk processing uses checked slice ranges and the
- // tail path handles partial blocks.
- unsafe {
- let ce_rk = match &ek.inner {
- KeyInner::Aarch64Aes(rk) => rk,
- _ => {
- aes256_ctr32_encrypt_be(ek, initial_counter, data);
- return ghash_ciphertext_fallback(acc, tables.h_polyval, data);
- }
- };
-
- let iv_prefix: [u8; 12] = {
- let mut buf = [0u8; 12];
- buf.copy_from_slice(&initial_counter[..12]);
- buf
- };
- let mut ctr = u32::from_be_bytes([
- initial_counter[12],
- initial_counter[13],
- initial_counter[14],
- initial_counter[15],
- ]);
- let mut offset = 0usize;
-
- if offset.strict_add(128) <= data.len() {
- let state = ce::encrypt_ctr32_be_xor_ghash_128b_chunks_core(ce_rk, &iv_prefix, ctr, data, acc, tables);
- acc = state.0;
- ctr = state.1;
- offset = state.2;
- }
-
- while offset.strict_add(64) <= data.len() {
- let end = offset.strict_add(64);
- let blocks = ce::encrypt_ctr32_be_xor_4blocks_core(ce_rk, &iv_prefix, ctr, &mut data[offset..end]);
- acc = super::polyval::aarch64_aggregate_4blocks_inline(acc, tables.h_powers_rev, &blocks);
- ctr = ctr.wrapping_add(4);
- offset = end;
- }
-
- while offset < data.len() {
- let mut counter_block = [0u8; BLOCK_SIZE];
- counter_block[..12].copy_from_slice(&iv_prefix);
- counter_block[12..16].copy_from_slice(&ctr.to_be_bytes());
-
- let mut keystream = counter_block;
- ce::encrypt_block_core(ce_rk, &mut keystream);
-
- let remaining = data.len().strict_sub(offset);
- if remaining >= BLOCK_SIZE {
- let ks = u128::from_ne_bytes(keystream);
- let mut d = [0u8; BLOCK_SIZE];
- d.copy_from_slice(&data[offset..offset.strict_add(BLOCK_SIZE)]);
- let xored = u128::from_ne_bytes(d) ^ ks;
- let ciphertext = xored.to_ne_bytes();
- data[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&ciphertext);
- acc ^= u128::from_be_bytes(ciphertext);
- acc = super::polyval::aarch64_clmul128_reduce_inline(acc, tables.h_polyval);
- offset = offset.strict_add(BLOCK_SIZE);
- } else {
- let mut block = [0u8; BLOCK_SIZE];
- let mut i = 0usize;
- while i < remaining {
- data[offset.strict_add(i)] ^= keystream[i];
- block[i] = data[offset.strict_add(i)];
- i = i.strict_add(1);
- }
- acc ^= u128::from_be_bytes(block);
- acc = super::polyval::aarch64_clmul128_reduce_inline(acc, tables.h_polyval);
- offset = offset.strict_add(remaining);
- }
- ctr = ctr.wrapping_add(1);
- }
-
- acc
- }
-}
-
-/// AES-256 CTR decryption fused with PMULL GHASH accumulation for GCM open.
-///
-/// Folds ciphertext into GHASH, decrypts the same chunk in place, and returns
-/// the accumulator. Authentication is still finalized and checked by the
-/// caller, which zeroizes the buffer on failure.
-///
-/// # Safety
-/// Caller must ensure AES-CE and PMULL are available.
-#[cfg(all(target_arch = "aarch64", feature = "aes-gcm"))]
-#[target_feature(enable = "aes,neon")]
-pub(crate) unsafe fn aes256_ctr32_decrypt_be_aarch64_ghash(
- ek: &Aes256EncKey,
- initial_counter: &[u8; BLOCK_SIZE],
- data: &mut [u8],
- mut acc: u128,
- tables: &Aarch64GcmTables<'_>,
-) -> u128 {
- // SAFETY: fused aarch64 AES-GCM opening because:
- // 1. This function's caller guarantees AES-CE and PMULL availability.
- // 2. `data` is a valid mutable ciphertext slice. Ciphertext blocks are copied before in-place
- // decryption, so GHASH always authenticates the original bytes.
- // 3. All chunk processing uses checked slice bounds and the tail path handles partial blocks.
- unsafe {
- let ce_rk = match &ek.inner {
- KeyInner::Aarch64Aes(rk) => rk,
- _ => {
- acc = ghash_ciphertext_fallback(acc, tables.h_polyval, data);
- aes256_ctr32_encrypt_be(ek, initial_counter, data);
- return acc;
- }
- };
-
- let iv_prefix: [u8; 12] = {
- let mut buf = [0u8; 12];
- buf.copy_from_slice(&initial_counter[..12]);
- buf
- };
- let mut ctr = u32::from_be_bytes([
- initial_counter[12],
- initial_counter[13],
- initial_counter[14],
- initial_counter[15],
- ]);
- let state = ce::decrypt_ctr32_be_xor_ghash_128b_chunks_core(ce_rk, &iv_prefix, ctr, data, acc, tables);
- acc = state.0;
- ctr = state.1;
- let mut offset = state.2;
+ let state = ce::decrypt_ctr32_be_xor_ghash_128b_chunks_core(ce_rk, &iv_prefix, ctr, data, acc, tables);
+ acc = state.0;
+ ctr = state.1;
+ let mut offset = state.2;
while offset.strict_add(64) <= data.len() {
let end = offset.strict_add(64);
@@ -4108,7 +3700,7 @@ pub(crate) fn aes128_ctr32_encrypt_be(ek: &Aes128EncKey, initial_counter: &[u8;
aes128_encrypt_blocks_ecb(ek, &mut keystream[..block_count]);
let processed = xor_keystream_tail(data, offset, &keystream, block_count);
offset = offset.strict_add(processed);
- ctr = ctr.wrapping_add(block_count as u32);
+ ctr = ctr.wrapping_add(u32::from(block_count.to_le_bytes()[0]));
}
}
@@ -4641,7 +4233,7 @@ pub(crate) unsafe fn aes128_ctr32_encrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_128.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
} else if data.len() >= 1024 {
x86_64_asm::rscrypto_aes128_gcm_seal_64x_vaes512_x86_64_linux(
@@ -4650,7 +4242,7 @@ pub(crate) unsafe fn aes128_ctr32_encrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_64.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
} else {
x86_64_asm::rscrypto_aes128_gcm_seal_16x_vaes512_x86_64_linux(
@@ -4659,7 +4251,7 @@ pub(crate) unsafe fn aes128_ctr32_encrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_32.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
}
acc = state.acc();
@@ -4817,7 +4409,7 @@ pub(crate) unsafe fn aes128_ctr32_decrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_128.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
} else if data.len() >= 1024 {
x86_64_asm::rscrypto_aes128_gcm_open_64x_vaes512_x86_64_linux(
@@ -4826,7 +4418,7 @@ pub(crate) unsafe fn aes128_ctr32_decrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_64.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
} else {
x86_64_asm::rscrypto_aes128_gcm_open_16x_vaes512_x86_64_linux(
@@ -4835,7 +4427,7 @@ pub(crate) unsafe fn aes128_ctr32_decrypt_be_wide_ghash(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_32.as_ptr(),
- &mut state,
+ core::ptr::from_mut(&mut state),
);
}
acc = state.acc();
@@ -4863,351 +4455,22 @@ pub(crate) unsafe fn aes128_ctr32_decrypt_be_wide_ghash(
);
_mm512_storeu_si512(
data.as_mut_ptr().add(offset.strict_add(192)).cast(),
- _mm512_xor_si512(c3, ks3),
- );
-
- ctr = ctr.wrapping_add(16);
- offset = offset.strict_add(256);
- }
-
- while offset.strict_add(64) <= data.len() {
- let ctr_vec = x86_gcm_ctr_blocks_be_4(iv_words, ctr);
- let keystream = ni::encrypt_4blocks_128(ni_rk, ctr_vec);
- let ciphertext = _mm512_loadu_si512(data.as_ptr().add(offset).cast());
- acc = super::polyval::x86_aggregate_4blocks_be_lanes_inline(acc, h_powers_rev, ciphertext);
- _mm512_storeu_si512(
- data.as_mut_ptr().add(offset).cast(),
- _mm512_xor_si512(ciphertext, keystream),
- );
-
- ctr = ctr.wrapping_add(4);
- offset = offset.strict_add(64);
- }
-
- while offset < data.len() {
- let mut counter_block = [0u8; 16];
- counter_block[..12].copy_from_slice(&iv_prefix);
- counter_block[12..16].copy_from_slice(&ctr.to_be_bytes());
-
- let mut keystream = counter_block;
- ni::encrypt_block_128(ni_rk, &mut keystream);
-
- let remaining = data.len().strict_sub(offset);
- if remaining >= BLOCK_SIZE {
- let mut ciphertext = [0u8; BLOCK_SIZE];
- ciphertext.copy_from_slice(&data[offset..offset.strict_add(BLOCK_SIZE)]);
- acc ^= u128::from_be_bytes(ciphertext);
- acc = super::polyval::x86_clmul128_reduce_inline(acc, h_polyval);
-
- let plaintext = u128::from_ne_bytes(ciphertext) ^ u128::from_ne_bytes(keystream);
- data[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&plaintext.to_ne_bytes());
- offset = offset.strict_add(BLOCK_SIZE);
- } else {
- let mut block = [0u8; BLOCK_SIZE];
- block[..remaining].copy_from_slice(&data[offset..offset.strict_add(remaining)]);
- acc ^= u128::from_be_bytes(block);
- acc = super::polyval::x86_clmul128_reduce_inline(acc, h_polyval);
-
- let mut i = 0usize;
- while i < remaining {
- data[offset.strict_add(i)] ^= keystream[i];
- i = i.strict_add(1);
- }
- offset = offset.strict_add(remaining);
- }
- ctr = ctr.wrapping_add(1);
- }
-
- acc
- }
-}
-
-/// AES-128 CTR encryption fused with 256-bit VAES/VPCLMUL GHASH accumulation.
-///
-/// # Safety
-/// Caller must ensure AVX2 + AVX-512F + AVX-512VL + VAES + VPCLMULQDQ +
-/// PCLMULQDQ + AES + SSE2 + SSSE3.
-#[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
-#[target_feature(enable = "aes,sse2,ssse3,avx2,avx512f,avx512vl,vaes,vpclmulqdq,pclmulqdq")]
-#[allow(dead_code)]
-pub(crate) unsafe fn aes128_ctr32_encrypt_be_y256_ghash(
- ek: &Aes128EncKey,
- initial_counter: &[u8; BLOCK_SIZE],
- data: &mut [u8],
- mut acc: u128,
- h_polyval: u128,
- h_powers_rev: &[u128; 4],
- h_powers_rev_8: &[u128; 8],
-) -> u128 {
- use core::arch::x86_64::*;
-
- // SAFETY: fused x86 VAES-128 AES-GCM sealing because:
- // 1. This function's caller guarantees all required x86 target features.
- // 2. `data` is a valid mutable byte slice; all pointer arithmetic stays inside checked chunk
- // bounds.
- // 3. GHASH folds ciphertext registers after encryption, matching GCM authentication semantics.
- unsafe {
- let ni_rk = match &ek.inner {
- Key128Inner::X86AesNi(rk) => rk,
- _ => {
- aes128_ctr32_encrypt_be(ek, initial_counter, data);
- return ghash_ciphertext_fallback(acc, h_polyval, data);
- }
- };
-
- let iv_prefix: [u8; 12] = {
- let mut buf = [0u8; 12];
- buf.copy_from_slice(&initial_counter[..12]);
- buf
- };
- let iv_words = [
- u32::from_le_bytes([iv_prefix[0], iv_prefix[1], iv_prefix[2], iv_prefix[3]]),
- u32::from_le_bytes([iv_prefix[4], iv_prefix[5], iv_prefix[6], iv_prefix[7]]),
- u32::from_le_bytes([iv_prefix[8], iv_prefix[9], iv_prefix[10], iv_prefix[11]]),
- ];
- let mut ctr = u32::from_be_bytes([
- initial_counter[12],
- initial_counter[13],
- initial_counter[14],
- initial_counter[15],
- ]);
- let mut offset = 0usize;
-
- #[cfg(target_os = "linux")]
- if data.len() >= 128 {
- let mut state = x86_64_asm::AesGcmX86State::new(acc, ctr);
- // SAFETY: external x86-64 VAES-256 AES-128-GCM seal kernel because:
- // 1. This target-feature function is only entered after VAES, VPCLMULQDQ, AVX2, AVX-512VL, AES-NI,
- // PCLMULQDQ, SSE2, and SSSE3 were selected by runtime/backend dispatch.
- // 2. `ni_rk.as_ptr()` addresses 11 initialized 128-bit AES-128 round keys.
- // 3. `initial_counter` points to the full 16-byte GCM counter block, and `data` is valid for
- // `data.len()` mutable bytes.
- // 4. `h_powers_rev_8` contains exactly the [H^8..H] powers required by the 8-block fold.
- // 5. The kernel only processes complete 128-byte chunks and reports the processed byte count so the
- // Rust fallback below handles every remaining full/partial tail.
- x86_64_asm::rscrypto_aes128_gcm_seal_8x_vaes256_x86_64_linux(
- ni_rk.as_ptr(),
- initial_counter.as_ptr(),
- data.as_mut_ptr(),
- data.len(),
- h_powers_rev_8.as_ptr(),
- &mut state,
- );
- acc = state.acc();
- ctr = state.ctr;
- offset = state.processed;
- }
-
- while offset.strict_add(128) <= data.len() {
- let (ctr0, ctr1, ctr2, ctr3) = x86_gcm_ctr_blocks_be_8_y256(iv_words, ctr);
- let (ks0, ks1, ks2, ks3) = ni::encrypt_8blocks_128_y256(ni_rk, ctr0, ctr1, ctr2, ctr3);
-
- let p0 = _mm256_loadu_si256(data.as_ptr().add(offset).cast());
- let c0 = _mm256_xor_si256(p0, ks0);
- _mm256_storeu_si256(data.as_mut_ptr().add(offset).cast(), c0);
-
- let p1 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(32)).cast());
- let c1 = _mm256_xor_si256(p1, ks1);
- _mm256_storeu_si256(data.as_mut_ptr().add(offset.strict_add(32)).cast(), c1);
-
- let p2 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(64)).cast());
- let c2 = _mm256_xor_si256(p2, ks2);
- _mm256_storeu_si256(data.as_mut_ptr().add(offset.strict_add(64)).cast(), c2);
-
- let p3 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(96)).cast());
- let c3 = _mm256_xor_si256(p3, ks3);
- _mm256_storeu_si256(data.as_mut_ptr().add(offset.strict_add(96)).cast(), c3);
- acc = super::polyval::x86_aggregate_8blocks_be_lanes_256_inline(acc, h_powers_rev_8, c0, c1, c2, c3);
-
- ctr = ctr.wrapping_add(8);
- offset = offset.strict_add(128);
- }
-
- while offset.strict_add(64) <= data.len() {
- let ctr0 = x86_gcm_ctr_block_be(iv_words, ctr);
- let ctr1 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(1));
- let ctr2 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(2));
- let ctr3 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(3));
- let (ks0, ks1, ks2, ks3) = ni::encrypt_4blocks_128_aesni(ni_rk, ctr0, ctr1, ctr2, ctr3);
-
- let ptr = data.as_mut_ptr().add(offset);
- let p0 = _mm_loadu_si128(ptr.cast());
- let p1 = _mm_loadu_si128(ptr.add(16).cast());
- let p2 = _mm_loadu_si128(ptr.add(32).cast());
- let p3 = _mm_loadu_si128(ptr.add(48).cast());
- let c0 = _mm_xor_si128(p0, ks0);
- let c1 = _mm_xor_si128(p1, ks1);
- let c2 = _mm_xor_si128(p2, ks2);
- let c3 = _mm_xor_si128(p3, ks3);
- _mm_storeu_si128(ptr.cast(), c0);
- _mm_storeu_si128(ptr.add(16).cast(), c1);
- _mm_storeu_si128(ptr.add(32).cast(), c2);
- _mm_storeu_si128(ptr.add(48).cast(), c3);
- acc = super::polyval::x86_pclmul_aggregate_4blocks_be_xmm_inline(acc, h_powers_rev, c0, c1, c2, c3);
-
- ctr = ctr.wrapping_add(4);
- offset = offset.strict_add(64);
- }
-
- while offset < data.len() {
- let mut counter_block = [0u8; 16];
- counter_block[..12].copy_from_slice(&iv_prefix);
- counter_block[12..16].copy_from_slice(&ctr.to_be_bytes());
-
- let mut keystream = counter_block;
- ni::encrypt_block_128(ni_rk, &mut keystream);
-
- let remaining = data.len().strict_sub(offset);
- if remaining >= BLOCK_SIZE {
- let ks = u128::from_ne_bytes(keystream);
- let mut d = [0u8; BLOCK_SIZE];
- d.copy_from_slice(&data[offset..offset.strict_add(BLOCK_SIZE)]);
- let xored = u128::from_ne_bytes(d) ^ ks;
- let ciphertext = xored.to_ne_bytes();
- data[offset..offset.strict_add(BLOCK_SIZE)].copy_from_slice(&ciphertext);
- acc ^= u128::from_be_bytes(ciphertext);
- acc = super::polyval::x86_clmul128_reduce_inline(acc, h_polyval);
- offset = offset.strict_add(BLOCK_SIZE);
- } else {
- let mut block = [0u8; BLOCK_SIZE];
- let mut i = 0usize;
- while i < remaining {
- data[offset.strict_add(i)] ^= keystream[i];
- block[i] = data[offset.strict_add(i)];
- i = i.strict_add(1);
- }
- acc ^= u128::from_be_bytes(block);
- acc = super::polyval::x86_clmul128_reduce_inline(acc, h_polyval);
- offset = offset.strict_add(remaining);
- }
- ctr = ctr.wrapping_add(1);
- }
-
- acc
- }
-}
-
-/// AES-128 CTR decryption fused with 256-bit VAES/VPCLMUL GHASH accumulation.
-///
-/// # Safety
-/// Caller must ensure AVX2 + AVX-512F + AVX-512VL + VAES + VPCLMULQDQ +
-/// PCLMULQDQ + AES + SSE2 + SSSE3.
-#[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
-#[target_feature(enable = "aes,sse2,ssse3,avx2,avx512f,avx512vl,vaes,vpclmulqdq,pclmulqdq")]
-#[allow(dead_code)]
-pub(crate) unsafe fn aes128_ctr32_decrypt_be_y256_ghash(
- ek: &Aes128EncKey,
- initial_counter: &[u8; BLOCK_SIZE],
- data: &mut [u8],
- mut acc: u128,
- h_polyval: u128,
- h_powers_rev: &[u128; 4],
- h_powers_rev_8: &[u128; 8],
-) -> u128 {
- use core::arch::x86_64::*;
-
- // SAFETY: fused x86 VAES-128 AES-GCM opening because:
- // 1. This function's caller guarantees all required x86 target features.
- // 2. Ciphertext registers are folded into GHASH before plaintext is stored back.
- // 3. All pointer arithmetic stays inside checked chunk bounds.
- unsafe {
- let ni_rk = match &ek.inner {
- Key128Inner::X86AesNi(rk) => rk,
- _ => {
- acc = ghash_ciphertext_fallback(acc, h_polyval, data);
- aes128_ctr32_encrypt_be(ek, initial_counter, data);
- return acc;
- }
- };
-
- let iv_prefix: [u8; 12] = {
- let mut buf = [0u8; 12];
- buf.copy_from_slice(&initial_counter[..12]);
- buf
- };
- let iv_words = [
- u32::from_le_bytes([iv_prefix[0], iv_prefix[1], iv_prefix[2], iv_prefix[3]]),
- u32::from_le_bytes([iv_prefix[4], iv_prefix[5], iv_prefix[6], iv_prefix[7]]),
- u32::from_le_bytes([iv_prefix[8], iv_prefix[9], iv_prefix[10], iv_prefix[11]]),
- ];
- let mut ctr = u32::from_be_bytes([
- initial_counter[12],
- initial_counter[13],
- initial_counter[14],
- initial_counter[15],
- ]);
- let mut offset = 0usize;
-
- #[cfg(target_os = "linux")]
- if data.len() >= 128 {
- let mut state = x86_64_asm::AesGcmX86State::new(acc, ctr);
- // SAFETY: external x86-64 VAES-256 AES-128-GCM open kernel because:
- // 1. This target-feature function is only entered after VAES, VPCLMULQDQ, AVX2, AVX-512VL, AES-NI,
- // PCLMULQDQ, SSE2, and SSSE3 were selected by runtime/backend dispatch.
- // 2. `ni_rk.as_ptr()` addresses 11 initialized 128-bit AES-128 round keys.
- // 3. `initial_counter` points to the full 16-byte GCM counter block, and `data` is valid for
- // `data.len()` mutable ciphertext bytes.
- // 4. The kernel folds ciphertext into GHASH before storing plaintext.
- // 5. The kernel only processes complete 128-byte chunks and reports the processed byte count so the
- // Rust fallback below handles every remaining full/partial tail.
- x86_64_asm::rscrypto_aes128_gcm_open_8x_vaes256_x86_64_linux(
- ni_rk.as_ptr(),
- initial_counter.as_ptr(),
- data.as_mut_ptr(),
- data.len(),
- h_powers_rev_8.as_ptr(),
- &mut state,
- );
- acc = state.acc();
- ctr = state.ctr;
- offset = state.processed;
- }
-
- while offset.strict_add(128) <= data.len() {
- let (ctr0, ctr1, ctr2, ctr3) = x86_gcm_ctr_blocks_be_8_y256(iv_words, ctr);
- let (ks0, ks1, ks2, ks3) = ni::encrypt_8blocks_128_y256(ni_rk, ctr0, ctr1, ctr2, ctr3);
-
- let c0 = _mm256_loadu_si256(data.as_ptr().add(offset).cast());
- let c1 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(32)).cast());
- let c2 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(64)).cast());
- let c3 = _mm256_loadu_si256(data.as_ptr().add(offset.strict_add(96)).cast());
- acc = super::polyval::x86_aggregate_8blocks_be_lanes_256_inline(acc, h_powers_rev_8, c0, c1, c2, c3);
-
- _mm256_storeu_si256(data.as_mut_ptr().add(offset).cast(), _mm256_xor_si256(c0, ks0));
- _mm256_storeu_si256(
- data.as_mut_ptr().add(offset.strict_add(32)).cast(),
- _mm256_xor_si256(c1, ks1),
- );
- _mm256_storeu_si256(
- data.as_mut_ptr().add(offset.strict_add(64)).cast(),
- _mm256_xor_si256(c2, ks2),
- );
- _mm256_storeu_si256(
- data.as_mut_ptr().add(offset.strict_add(96)).cast(),
- _mm256_xor_si256(c3, ks3),
- );
-
- ctr = ctr.wrapping_add(8);
- offset = offset.strict_add(128);
- }
-
- while offset.strict_add(64) <= data.len() {
- let ctr0 = x86_gcm_ctr_block_be(iv_words, ctr);
- let ctr1 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(1));
- let ctr2 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(2));
- let ctr3 = x86_gcm_ctr_block_be(iv_words, ctr.wrapping_add(3));
- let (ks0, ks1, ks2, ks3) = ni::encrypt_4blocks_128_aesni(ni_rk, ctr0, ctr1, ctr2, ctr3);
-
- let ptr = data.as_mut_ptr().add(offset);
- let c0 = _mm_loadu_si128(ptr.cast());
- let c1 = _mm_loadu_si128(ptr.add(16).cast());
- let c2 = _mm_loadu_si128(ptr.add(32).cast());
- let c3 = _mm_loadu_si128(ptr.add(48).cast());
- acc = super::polyval::x86_pclmul_aggregate_4blocks_be_xmm_inline(acc, h_powers_rev, c0, c1, c2, c3);
- _mm_storeu_si128(ptr.cast(), _mm_xor_si128(c0, ks0));
- _mm_storeu_si128(ptr.add(16).cast(), _mm_xor_si128(c1, ks1));
- _mm_storeu_si128(ptr.add(32).cast(), _mm_xor_si128(c2, ks2));
- _mm_storeu_si128(ptr.add(48).cast(), _mm_xor_si128(c3, ks3));
+ _mm512_xor_si512(c3, ks3),
+ );
+
+ ctr = ctr.wrapping_add(16);
+ offset = offset.strict_add(256);
+ }
+
+ while offset.strict_add(64) <= data.len() {
+ let ctr_vec = x86_gcm_ctr_blocks_be_4(iv_words, ctr);
+ let keystream = ni::encrypt_4blocks_128(ni_rk, ctr_vec);
+ let ciphertext = _mm512_loadu_si512(data.as_ptr().add(offset).cast());
+ acc = super::polyval::x86_aggregate_4blocks_be_lanes_inline(acc, h_powers_rev, ciphertext);
+ _mm512_storeu_si512(
+ data.as_mut_ptr().add(offset).cast(),
+ _mm512_xor_si512(ciphertext, keystream),
+ );
ctr = ctr.wrapping_add(4);
offset = offset.strict_add(64);
@@ -5251,9 +4514,19 @@ pub(crate) unsafe fn aes128_ctr32_decrypt_be_y256_ghash(
}
}
+#[cfg(all(target_arch = "x86_64", target_os = "linux", feature = "aes-gcm-siv"))]
+#[inline(always)]
+const fn usize_low_u32(value: usize) -> u32 {
+ let bytes = value.to_le_bytes();
+ u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]])
+}
+
/// AES-256 CTR encryption using VAES-512 for the bulk, AES-NI for the tail.
///
/// GCM-SIV variant: counter occupies bytes 0..3 (little-endian).
+///
+/// # Safety
+/// Caller must ensure AES-NI, SSE2, AVX-512F, AVX-512VL, and VAES are available.
#[cfg(all(target_arch = "x86_64", feature = "aes-gcm-siv"))]
#[target_feature(enable = "aes,sse2,avx512f,avx512vl,vaes")]
pub(crate) unsafe fn aes256_ctr32_encrypt_wide(ek: &Aes256EncKey, initial_counter: &[u8; BLOCK_SIZE], data: &mut [u8]) {
@@ -5304,7 +4577,7 @@ pub(crate) unsafe fn aes256_ctr32_encrypt_wide(ek: &Aes256EncKey, initial_counte
data.as_mut_ptr(),
data.len(),
);
- ctr = ctr.wrapping_add((processed / BLOCK_SIZE) as u32);
+ ctr = ctr.wrapping_add(usize_low_u32(processed.strict_div(BLOCK_SIZE)));
offset = processed;
}
@@ -5439,7 +4712,7 @@ pub(crate) unsafe fn aes128_ctr32_encrypt_wide(ek: &Aes128EncKey, initial_counte
data.as_mut_ptr(),
data.len(),
);
- ctr = ctr.wrapping_add((processed / BLOCK_SIZE) as u32);
+ ctr = ctr.wrapping_add(usize_low_u32(processed.strict_div(BLOCK_SIZE)));
offset = processed;
}
@@ -5544,12 +4817,12 @@ mod tests {
0x8c,0xa1,0x89,0x0d,0xbf,0xe6,0x42,0x68,0x41,0x99,0x2d,0x0f,0xb0,0x54,0xbb,0x16,
];
- for (i, &expected) in CANONICAL.iter().enumerate() {
+ for (input, &expected) in (0u8..=u8::MAX).zip(&CANONICAL) {
assert_eq!(
- sbox(i as u8),
+ sbox(input),
expected,
- "S-box mismatch at index {i:#04x}: got {:#04x}, expected {expected:#04x}",
- sbox(i as u8),
+ "S-box mismatch at input {input:#04x}: got {:#04x}, expected {expected:#04x}",
+ sbox(input),
);
}
}
@@ -5644,13 +4917,44 @@ mod tests {
#[test]
fn gf256_inv_exhaustive() {
assert_eq!(gf256_inv(0), 0, "inv(0) must be 0 by AES convention");
- for x in 1u16..=255 {
- let x = x as u8;
+ for x in 1u8..=u8::MAX {
let inv = gf256_inv(x);
assert_eq!(gf256_mul(x, inv), 1, "x={x:#04x}, inv={inv:#04x}: x * inv != 1");
}
}
+ #[cfg(all(target_arch = "aarch64", feature = "aes-gcm-siv"))]
+ #[test]
+ fn aarch64_gcmsiv_kdf_portable_fallback_matches_aes_ce() {
+ if !crate::platform::caps().has(crate::platform::caps::aarch64::AES) {
+ return;
+ }
+
+ let nonce = *b"kdf nonce 12";
+ let key_128 = [0x39; KEY_SIZE_128];
+ let key_256 = [0xa7; KEY_SIZE];
+ let accelerated_128 = aes128_expand_key(&key_128);
+ let accelerated_256 = aes256_expand_key(&key_256);
+ let portable_128 = Aes128EncKey {
+ inner: Key128Inner::PortableRoundKeys(aes128_expand_key_portable(&key_128)),
+ };
+ let portable_256 = Aes256EncKey {
+ inner: KeyInner::PortableRoundKeys(aes256_expand_key_portable(&key_256)),
+ };
+
+ // SAFETY: runtime capabilities above confirm AES-CE before both target-feature calls.
+ let expected_128 = unsafe { aarch64_gcmsiv_derive_keys_128_inline(&accelerated_128, &nonce) };
+ // SAFETY: runtime capabilities above confirm AES-CE; the portable key exercises the safe fallback.
+ let actual_128 = unsafe { aarch64_gcmsiv_derive_keys_128_inline(&portable_128, &nonce) };
+ assert_eq!(actual_128, expected_128);
+
+ // SAFETY: runtime capabilities above confirm AES-CE before both target-feature calls.
+ let expected_256 = unsafe { aarch64_gcmsiv_derive_keys_inline(&accelerated_256, &nonce) };
+ // SAFETY: runtime capabilities above confirm AES-CE; the portable key exercises the safe fallback.
+ let actual_256 = unsafe { aarch64_gcmsiv_derive_keys_inline(&portable_256, &nonce) };
+ assert_eq!(actual_256, expected_256);
+ }
+
/// AES-256 CTR mode: round-trip (encrypt then decrypt = identity).
#[cfg(feature = "aes-gcm-siv")]
#[test]
@@ -5713,18 +5017,12 @@ mod tests {
let mut buf = [0u8; 80];
aes128_ctr32_encrypt_be(&ek, &iv, &mut buf);
- for block_idx in 0..5usize {
+ for (block_idx, actual) in (0u32..5).zip(buf.as_chunks::().0) {
let mut expected = iv;
- let ctr = 3u32.wrapping_add(block_idx as u32);
+ let ctr = 3u32.wrapping_add(block_idx);
expected[12..16].copy_from_slice(&ctr.to_be_bytes());
aes128_encrypt_block(&ek, &mut expected);
- let start = block_idx.strict_mul(BLOCK_SIZE);
- let end = start.strict_add(BLOCK_SIZE);
- assert_eq!(
- &buf[start..end],
- &expected,
- "AES-128 CTR-BE block {block_idx} keystream mismatch"
- );
+ assert_eq!(actual, &expected, "AES-128 CTR-BE block {block_idx} keystream mismatch");
}
}
@@ -5741,16 +5039,13 @@ mod tests {
let mut buf = [0u8; 80];
aes128_ctr32_encrypt_be(&ek, &iv, &mut buf);
- for block_idx in 0..5usize {
+ for (block_idx, actual) in (0u32..5).zip(buf.as_chunks::().0) {
let mut expected = iv;
- let ctr = start_ctr.wrapping_add(block_idx as u32);
+ let ctr = start_ctr.wrapping_add(block_idx);
expected[12..16].copy_from_slice(&ctr.to_be_bytes());
aes128_encrypt_block(&ek, &mut expected);
- let start = block_idx.strict_mul(BLOCK_SIZE);
- let end = start.strict_add(BLOCK_SIZE);
assert_eq!(
- &buf[start..end],
- &expected,
+ actual, &expected,
"AES-128 CTR-BE wrap block {block_idx} keystream mismatch"
);
}
@@ -5768,18 +5063,12 @@ mod tests {
let mut buf = [0u8; 80];
aes256_ctr32_encrypt_be(&ek, &iv, &mut buf);
- for block_idx in 0..5usize {
+ for (block_idx, actual) in (0u32..5).zip(buf.as_chunks::().0) {
let mut expected = iv;
- let ctr = 7u32.wrapping_add(block_idx as u32);
+ let ctr = 7u32.wrapping_add(block_idx);
expected[12..16].copy_from_slice(&ctr.to_be_bytes());
aes256_encrypt_block(&ek, &mut expected);
- let start = block_idx.strict_mul(BLOCK_SIZE);
- let end = start.strict_add(BLOCK_SIZE);
- assert_eq!(
- &buf[start..end],
- &expected,
- "CTR-BE block {block_idx} keystream mismatch"
- );
+ assert_eq!(actual, &expected, "CTR-BE block {block_idx} keystream mismatch");
}
}
@@ -5796,16 +5085,13 @@ mod tests {
let mut buf = [0u8; 80];
aes256_ctr32_encrypt_be(&ek, &iv, &mut buf);
- for block_idx in 0..5usize {
+ for (block_idx, actual) in (0u32..5).zip(buf.as_chunks::().0) {
let mut expected = iv;
- let ctr = start_ctr.wrapping_add(block_idx as u32);
+ let ctr = start_ctr.wrapping_add(block_idx);
expected[12..16].copy_from_slice(&ctr.to_be_bytes());
aes256_encrypt_block(&ek, &mut expected);
- let start = block_idx.strict_mul(BLOCK_SIZE);
- let end = start.strict_add(BLOCK_SIZE);
assert_eq!(
- &buf[start..end],
- &expected,
+ actual, &expected,
"AES-256 CTR-BE wrap block {block_idx} keystream mismatch"
);
}
@@ -5891,7 +5177,9 @@ mod tests {
fn fill_aarch64_gcm_test_plaintext(out: &mut [u8; N]) {
let mut i = 0usize;
while i < N {
- out[i] = (i as u8).wrapping_mul(0x3d).wrapping_add(0x47) ^ ((i >> 3) as u8).wrapping_mul(0x91);
+ let index = i.to_le_bytes()[0];
+ let group = (i >> 3).to_le_bytes()[0];
+ out[i] = index.wrapping_mul(0x3d).wrapping_add(0x47) ^ group.wrapping_mul(0x91);
i = i.strict_add(1);
}
}
@@ -6060,14 +5348,11 @@ mod tests {
#[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
fn fill_expected_gcm_counter_blocks(iv_prefix: &[u8; 12], ctr: u32, expected: &mut [u8; N]) {
debug_assert_eq!(N.strict_rem(BLOCK_SIZE), 0);
- let blocks = N.strict_div(BLOCK_SIZE);
- let mut block_idx = 0usize;
- while block_idx < blocks {
- let start = block_idx.strict_mul(BLOCK_SIZE);
- expected[start..start.strict_add(12)].copy_from_slice(iv_prefix);
- expected[start.strict_add(12)..start.strict_add(16)]
- .copy_from_slice(&ctr.wrapping_add(block_idx as u32).to_be_bytes());
- block_idx = block_idx.strict_add(1);
+ let (blocks, tail) = expected.as_chunks_mut::();
+ debug_assert!(tail.is_empty());
+ for (block_idx, block) in (0u32..).zip(blocks) {
+ block[..12].copy_from_slice(iv_prefix);
+ block[12..].copy_from_slice(&ctr.wrapping_add(block_idx).to_be_bytes());
}
}
@@ -6146,29 +5431,6 @@ mod tests {
out
}
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- #[target_feature(enable = "avx2")]
- /// # Safety
- ///
- /// Caller must ensure AVX2 is available before calling this target-feature helper.
- unsafe fn x86_gcm_ctr_blocks_be_8_y256_test_bytes(iv_words: [u32; 3], ctr: u32) -> [u8; 128] {
- use core::arch::x86_64::*;
-
- let mut out = [0u8; 128];
- // SAFETY: test-only YMM counter block stores because:
- // 1. The caller verified AVX2 before invoking this target-feature helper.
- // 2. `out` is exactly 128 writable bytes, matching four contiguous `__m256i` stores.
- // 3. The vectors under test are produced directly from the eight-block counter constructor.
- unsafe {
- let (c0, c1, c2, c3) = x86_gcm_ctr_blocks_be_8_y256(iv_words, ctr);
- _mm256_storeu_si256(out.as_mut_ptr().cast(), c0);
- _mm256_storeu_si256(out.as_mut_ptr().add(32).cast(), c1);
- _mm256_storeu_si256(out.as_mut_ptr().add(64).cast(), c2);
- _mm256_storeu_si256(out.as_mut_ptr().add(96).cast(), c3);
- }
- out
- }
-
#[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
#[test]
fn x86_gcm_ctr_block_be_preserves_prefix_and_encodes_counter() {
@@ -6263,39 +5525,11 @@ mod tests {
}
}
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- #[test]
- fn x86_gcm_ctr_blocks_be_8_y256_preserves_prefix_and_wraps_counter() {
- if !crate::platform::caps().has(crate::platform::caps::x86::AVX2) {
- return;
- }
-
- let iv_prefix = *b"ctr wrap iv!";
- let iv_words = x86_gcm_iv_words(&iv_prefix);
-
- for ctr in [0x0102_0304, u32::MAX - 3] {
- let mut expected = [0u8; 128];
- fill_expected_gcm_counter_blocks(&iv_prefix, ctr, &mut expected);
- // SAFETY: runtime caps above confirmed AVX2 before calling the target-feature helper.
- let actual = unsafe { x86_gcm_ctr_blocks_be_8_y256_test_bytes(iv_words, ctr) };
- assert_eq!(
- actual.as_slice(),
- expected.as_slice(),
- "x86 vectorized YMM GCM counter block mismatch"
- );
- }
- }
-
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- fn x86_y256_gcm_caps_available() -> bool {
- let required = crate::platform::caps::x86::VAES_READY
- | crate::platform::caps::x86::VPCLMUL_READY
- | crate::platform::caps::x86::AVX2
- | crate::platform::caps::x86::AESNI;
- crate::platform::caps().has(required)
- }
-
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
+ #[cfg(all(
+ target_arch = "x86_64",
+ feature = "aes-gcm",
+ any(target_os = "linux", target_os = "macos")
+ ))]
fn x86_z512_gcm_caps_available() -> bool {
let required = crate::platform::caps::x86::VAES_READY
| crate::platform::caps::x86::VPCLMUL_READY
@@ -6303,20 +5537,11 @@ mod tests {
crate::platform::caps().has(required)
}
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- fn x86_gcm_test_powers() -> (u128, [u128; 4], [u128; 8]) {
- let h_polyval = 0x1287_3d5b_fedc_ba09_7654_3210_f0e1_d2c3u128;
- let powers = crate::aead::polyval::precompute_powers_8(h_polyval);
- (
- h_polyval,
- [powers[3], powers[2], powers[1], powers[0]],
- [
- powers[7], powers[6], powers[5], powers[4], powers[3], powers[2], powers[1], powers[0],
- ],
- )
- }
-
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
+ #[cfg(all(
+ target_arch = "x86_64",
+ feature = "aes-gcm",
+ any(target_os = "linux", target_os = "macos")
+ ))]
struct X86GcmTestPowers16 {
h_polyval: u128,
h_powers_rev: [u128; 4],
@@ -6329,7 +5554,11 @@ mod tests {
h_powers_rev_128: [u128; 128],
}
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
+ #[cfg(all(
+ target_arch = "x86_64",
+ feature = "aes-gcm",
+ any(target_os = "linux", target_os = "macos")
+ ))]
impl X86GcmTestPowers16 {
fn tables<'a>(&'a self) -> X86GcmTables<'a> {
X86GcmTables {
@@ -6346,7 +5575,11 @@ mod tests {
}
}
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
+ #[cfg(all(
+ target_arch = "x86_64",
+ feature = "aes-gcm",
+ any(target_os = "linux", target_os = "macos")
+ ))]
fn x86_gcm_test_powers_16() -> X86GcmTestPowers16 {
let h_polyval = 0x1287_3d5b_fedc_ba09_7654_3210_f0e1_d2c3u128;
let powers = crate::aead::polyval::precompute_powers_128(h_polyval);
@@ -6371,7 +5604,11 @@ mod tests {
}
}
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
+ #[cfg(all(
+ target_arch = "x86_64",
+ feature = "aes-gcm",
+ any(target_os = "linux", target_os = "macos")
+ ))]
fn x86_gcm_wrap_counter_block() -> [u8; 16] {
let mut counter = [0u8; 16];
counter[..12].copy_from_slice(b"ctr wrap iv!");
@@ -6379,329 +5616,19 @@ mod tests {
counter
}
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- fn fill_x86_gcm_test_plaintext(out: &mut [u8]) {
- let mut i = 0usize;
- while i < out.len() {
- out[i] = (i as u8).wrapping_mul(0x3d).wrapping_add(0x47) ^ ((i >> 3) as u8).wrapping_mul(0x91);
- i = i.strict_add(1);
- }
- }
-
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- #[test]
- fn x86_aes128_gcm_y256_encrypt_matches_scalar_across_counter_wrap() {
- if !x86_y256_gcm_caps_available() {
- return;
- }
-
- let ek = aes128_expand_key(&[0xA1u8; KEY_SIZE_128]);
- let counter = x86_gcm_wrap_counter_block();
- let (h_polyval, h_powers_rev, h_powers_rev_8) = x86_gcm_test_powers();
- let seed_acc = 0xfeed_face_cafe_babe_1020_3040_5060_7080u128;
-
- let mut plaintext = [0u8; 128];
- fill_x86_gcm_test_plaintext(&mut plaintext);
- let mut expected = plaintext;
- aes128_ctr32_encrypt_be(&ek, &counter, &mut expected);
- let expected_acc = ghash_ciphertext_fallback(seed_acc, h_polyval, &expected);
-
- let mut actual = plaintext;
- // SAFETY: runtime caps above confirmed VAES + VPCLMULQDQ + AVX2 + AES-NI before calling
- // the y256 target-feature helper. Inputs are fixed-size initialized test buffers.
- let actual_acc = unsafe {
- aes128_ctr32_encrypt_be_y256_ghash(
- &ek,
- &counter,
- &mut actual,
- seed_acc,
- h_polyval,
- &h_powers_rev,
- &h_powers_rev_8,
- )
- };
-
- assert_eq!(
- actual, expected,
- "AES-128 y256 seal ciphertext must match scalar CTR across wrap"
- );
- assert_eq!(
- actual_acc, expected_acc,
- "AES-128 y256 seal GHASH accumulator must match scalar fold across wrap"
- );
- }
-
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- #[test]
- fn x86_aes128_gcm_y256_decrypt_matches_scalar_across_counter_wrap() {
- if !x86_y256_gcm_caps_available() {
- return;
- }
-
- let ek = aes128_expand_key(&[0xB2u8; KEY_SIZE_128]);
- let counter = x86_gcm_wrap_counter_block();
- let (h_polyval, h_powers_rev, h_powers_rev_8) = x86_gcm_test_powers();
- let seed_acc = 0x9ace_0246_8bdf_1357_1122_3344_5566_7788u128;
-
- let mut plaintext = [0u8; 128];
- fill_x86_gcm_test_plaintext(&mut plaintext);
- let mut ciphertext = plaintext;
- aes128_ctr32_encrypt_be(&ek, &counter, &mut ciphertext);
- let expected_acc = ghash_ciphertext_fallback(seed_acc, h_polyval, &ciphertext);
-
- let mut actual = ciphertext;
- // SAFETY: runtime caps above confirmed VAES + VPCLMULQDQ + AVX2 + AES-NI before calling
- // the y256 target-feature helper. Inputs are fixed-size initialized test buffers.
- let actual_acc = unsafe {
- aes128_ctr32_decrypt_be_y256_ghash(
- &ek,
- &counter,
- &mut actual,
- seed_acc,
- h_polyval,
- &h_powers_rev,
- &h_powers_rev_8,
- )
- };
-
- assert_eq!(
- actual, plaintext,
- "AES-128 y256 open plaintext must match scalar CTR across wrap"
- );
- assert_eq!(
- actual_acc, expected_acc,
- "AES-128 y256 open GHASH accumulator must match scalar fold across wrap"
- );
- }
-
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- #[test]
- fn x86_aes256_gcm_y256_encrypt_matches_scalar_across_counter_wrap() {
- if !x86_y256_gcm_caps_available() {
- return;
- }
-
- let ek = aes256_expand_key(&[0xC3u8; KEY_SIZE]);
- let counter = x86_gcm_wrap_counter_block();
- let (h_polyval, h_powers_rev, h_powers_rev_8) = x86_gcm_test_powers();
- let seed_acc = 0x0123_4567_89ab_cdef_fedc_ba98_7654_3210u128;
-
- let mut plaintext = [0u8; 128];
- fill_x86_gcm_test_plaintext(&mut plaintext);
- let mut expected = plaintext;
- aes256_ctr32_encrypt_be(&ek, &counter, &mut expected);
- let expected_acc = ghash_ciphertext_fallback(seed_acc, h_polyval, &expected);
-
- let mut actual = plaintext;
- // SAFETY: runtime caps above confirmed VAES + VPCLMULQDQ + AVX2 + AES-NI before calling
- // the y256 target-feature helper. Inputs are fixed-size initialized test buffers.
- let actual_acc = unsafe {
- aes256_ctr32_encrypt_be_y256_ghash(
- &ek,
- &counter,
- &mut actual,
- seed_acc,
- h_polyval,
- &h_powers_rev,
- &h_powers_rev_8,
- )
- };
-
- assert_eq!(
- actual, expected,
- "AES-256 y256 seal ciphertext must match scalar CTR across wrap"
- );
- assert_eq!(
- actual_acc, expected_acc,
- "AES-256 y256 seal GHASH accumulator must match scalar fold across wrap"
- );
- }
-
- #[cfg(all(target_arch = "x86_64", feature = "aes-gcm"))]
- #[test]
- fn x86_aes256_gcm_y256_decrypt_matches_scalar_across_counter_wrap() {
- if !x86_y256_gcm_caps_available() {
- return;
- }
-
- let ek = aes256_expand_key(&[0xD4u8; KEY_SIZE]);
- let counter = x86_gcm_wrap_counter_block();
- let (h_polyval, h_powers_rev, h_powers_rev_8) = x86_gcm_test_powers();
- let seed_acc = 0xaa55_aa55_55aa_55aa_cc33_cc33_33cc_33ccu128;
-
- let mut plaintext = [0u8; 128];
- fill_x86_gcm_test_plaintext(&mut plaintext);
- let mut ciphertext = plaintext;
- aes256_ctr32_encrypt_be(&ek, &counter, &mut ciphertext);
- let expected_acc = ghash_ciphertext_fallback(seed_acc, h_polyval, &ciphertext);
-
- let mut actual = ciphertext;
- // SAFETY: runtime caps above confirmed VAES + VPCLMULQDQ + AVX2 + AES-NI before calling
- // the y256 target-feature helper. Inputs are fixed-size initialized test buffers.
- let actual_acc = unsafe {
- aes256_ctr32_decrypt_be_y256_ghash(
- &ek,
- &counter,
- &mut actual,
- seed_acc,
- h_polyval,
- &h_powers_rev,
- &h_powers_rev_8,
- )
- };
-
- assert_eq!(
- actual, plaintext,
- "AES-256 y256 open plaintext must match scalar CTR across wrap"
- );
- assert_eq!(
- actual_acc, expected_acc,
- "AES-256 y256 open GHASH accumulator must match scalar fold across wrap"
- );
- }
-
- #[cfg(all(
- target_arch = "x86_64",
- feature = "aes-gcm",
- any(target_os = "linux", target_os = "macos", target_os = "windows")
- ))]
- #[test]
- fn x86_aes128_gcm_y256_large_asm_tail_matches_scalar() {
- if !x86_y256_gcm_caps_available() {
- return;
- }
-
- const LEN: usize = 1057;
-
- let ek = aes128_expand_key(&[0xE5u8; KEY_SIZE_128]);
- let counter = x86_gcm_wrap_counter_block();
- let (h_polyval, h_powers_rev, h_powers_rev_8) = x86_gcm_test_powers();
- let seed_acc = 0x3141_5926_5358_9793_2384_6264_3383_2795u128;
-
- let mut plaintext = [0u8; LEN];
- fill_x86_gcm_test_plaintext(&mut plaintext);
-
- let mut expected = plaintext;
- aes128_ctr32_encrypt_be(&ek, &counter, &mut expected);
- let expected_acc = ghash_ciphertext_fallback(seed_acc, h_polyval, &expected);
-
- let mut actual = plaintext;
- // SAFETY: large x86 AES-128-GCM seal test because:
- // 1. Runtime caps above confirmed VAES + VPCLMULQDQ + AVX2 + AES-NI.
- // 2. The 1057-byte input forces the x86 ASM bulk path to process full blocks.
- // 3. The final byte is handled by the shared Rust tail after the ASM path returns.
- let actual_acc = unsafe {
- aes128_ctr32_encrypt_be_y256_ghash(
- &ek,
- &counter,
- &mut actual,
- seed_acc,
- h_polyval,
- &h_powers_rev,
- &h_powers_rev_8,
- )
- };
-
- assert_eq!(actual, expected, "AES-128 large y256/ASM seal ciphertext mismatch");
- assert_eq!(
- actual_acc, expected_acc,
- "AES-128 large y256/ASM seal GHASH accumulator mismatch"
- );
-
- let mut opened = actual;
- // SAFETY: large x86 AES-128-GCM open test because:
- // 1. Runtime caps above confirmed VAES + VPCLMULQDQ + AVX2 + AES-NI.
- // 2. The 1057-byte input forces the x86 ASM bulk path to process full blocks.
- // 3. The helper GHASHes ciphertext before decrypting and leaves the final byte to the Rust tail.
- let open_acc = unsafe {
- aes128_ctr32_decrypt_be_y256_ghash(
- &ek,
- &counter,
- &mut opened,
- seed_acc,
- h_polyval,
- &h_powers_rev,
- &h_powers_rev_8,
- )
- };
-
- assert_eq!(opened, plaintext, "AES-128 large y256/ASM open plaintext mismatch");
- assert_eq!(
- open_acc, expected_acc,
- "AES-128 large y256/ASM open GHASH accumulator mismatch"
- );
- }
-
#[cfg(all(
target_arch = "x86_64",
feature = "aes-gcm",
- any(target_os = "linux", target_os = "macos", target_os = "windows")
+ any(target_os = "linux", target_os = "macos")
))]
- #[test]
- fn x86_aes256_gcm_y256_large_asm_tail_matches_scalar() {
- if !x86_y256_gcm_caps_available() {
- return;
+ fn fill_x86_gcm_test_plaintext(out: &mut [u8]) {
+ let mut i = 0usize;
+ while i < out.len() {
+ let index = i.to_le_bytes()[0];
+ let group = (i >> 3).to_le_bytes()[0];
+ out[i] = index.wrapping_mul(0x3d).wrapping_add(0x47) ^ group.wrapping_mul(0x91);
+ i = i.strict_add(1);
}
-
- const LEN: usize = 1057;
-
- let ek = aes256_expand_key(&[0xF6u8; KEY_SIZE]);
- let counter = x86_gcm_wrap_counter_block();
- let (h_polyval, h_powers_rev, h_powers_rev_8) = x86_gcm_test_powers();
- let seed_acc = 0x2718_2818_2845_9045_2353_6028_7471_3526u128;
-
- let mut plaintext = [0u8; LEN];
- fill_x86_gcm_test_plaintext(&mut plaintext);
-
- let mut expected = plaintext;
- aes256_ctr32_encrypt_be(&ek, &counter, &mut expected);
- let expected_acc = ghash_ciphertext_fallback(seed_acc, h_polyval, &expected);
-
- let mut actual = plaintext;
- // SAFETY: large x86 AES-256-GCM seal test because:
- // 1. Runtime caps above confirmed VAES + VPCLMULQDQ + AVX2 + AES-NI.
- // 2. The 1057-byte input forces the x86 ASM bulk path to process full blocks.
- // 3. The final byte is handled by the shared Rust tail after the ASM path returns.
- let actual_acc = unsafe {
- aes256_ctr32_encrypt_be_y256_ghash(
- &ek,
- &counter,
- &mut actual,
- seed_acc,
- h_polyval,
- &h_powers_rev,
- &h_powers_rev_8,
- )
- };
-
- assert_eq!(actual, expected, "AES-256 large y256/ASM seal ciphertext mismatch");
- assert_eq!(
- actual_acc, expected_acc,
- "AES-256 large y256/ASM seal GHASH accumulator mismatch"
- );
-
- let mut opened = actual;
- // SAFETY: large x86 AES-256-GCM open test because:
- // 1. Runtime caps above confirmed VAES + VPCLMULQDQ + AVX2 + AES-NI.
- // 2. The 1057-byte input forces the x86 ASM bulk path to process full blocks.
- // 3. The helper GHASHes ciphertext before decrypting and leaves the final byte to the Rust tail.
- let open_acc = unsafe {
- aes256_ctr32_decrypt_be_y256_ghash(
- &ek,
- &counter,
- &mut opened,
- seed_acc,
- h_polyval,
- &h_powers_rev,
- &h_powers_rev_8,
- )
- };
-
- assert_eq!(opened, plaintext, "AES-256 large y256/ASM open plaintext mismatch");
- assert_eq!(
- open_acc, expected_acc,
- "AES-256 large y256/ASM open GHASH accumulator mismatch"
- );
}
#[cfg(all(
@@ -6834,9 +5761,9 @@ mod tests {
let ek = aes128_expand_key(&key);
let mut blocks = [[0u8; BLOCK_SIZE]; 6];
- for (i, block) in blocks.iter_mut().enumerate() {
- for (j, byte) in block.iter_mut().enumerate() {
- *byte = (i as u8).wrapping_mul(13) ^ (j as u8).wrapping_mul(31) ^ 0xA3;
+ for (i, block) in (0u8..).zip(&mut blocks) {
+ for (j, byte) in (0u8..).zip(block) {
+ *byte = i.wrapping_mul(13) ^ j.wrapping_mul(31) ^ 0xA3;
}
}
@@ -6855,9 +5782,9 @@ mod tests {
let ek = aes256_expand_key(&key);
let mut blocks = [[0u8; BLOCK_SIZE]; 6];
- for (i, block) in blocks.iter_mut().enumerate() {
- for (j, byte) in block.iter_mut().enumerate() {
- *byte = (i as u8).wrapping_mul(17) ^ (j as u8).wrapping_mul(29) ^ 0x5C;
+ for (i, block) in (0u8..).zip(&mut blocks) {
+ for (j, byte) in (0u8..).zip(block) {
+ *byte = i.wrapping_mul(17) ^ j.wrapping_mul(29) ^ 0x5C;
}
}
@@ -6871,6 +5798,7 @@ mod tests {
}
/// FIPS 197 Appendix C.1 against the table-free fixslice AES-128 path.
+ #[cfg(not(target_arch = "s390x"))]
#[test]
fn riscv64_fixslice_matches_nist_aes128_vector() {
let key: [u8; 16] = [
@@ -6889,6 +5817,7 @@ mod tests {
assert_eq!(block, expected);
}
+ #[cfg(not(target_arch = "s390x"))]
#[test]
fn riscv64_fixslice_128_4blocks_matches_portable() {
let key = [0xC4u8; KEY_SIZE_128];
@@ -6896,9 +5825,9 @@ mod tests {
let fixslice = rv_fixslice_aes::RvFixslice128RoundKeys::new(&key);
let mut blocks = [[0u8; BLOCK_SIZE]; 4];
- for (i, block) in blocks.iter_mut().enumerate() {
- for (j, byte) in block.iter_mut().enumerate() {
- *byte = (i as u8).wrapping_mul(0x47) ^ (j as u8).wrapping_mul(0x6d) ^ 0x9c;
+ for (i, block) in (0u8..).zip(&mut blocks) {
+ for (j, byte) in (0u8..).zip(block) {
+ *byte = i.wrapping_mul(0x47) ^ j.wrapping_mul(0x6d) ^ 0x9c;
}
}
@@ -6911,6 +5840,7 @@ mod tests {
assert_eq!(blocks, expected);
}
+ #[cfg(not(target_arch = "s390x"))]
#[test]
fn riscv64_fixslice_matches_nist_aes256_vector() {
let key: [u8; 32] = [
@@ -6930,6 +5860,7 @@ mod tests {
assert_eq!(block, expected);
}
+ #[cfg(not(target_arch = "s390x"))]
#[test]
fn riscv64_fixslice_4blocks_matches_portable() {
let key = [0x3cu8; KEY_SIZE];
@@ -6937,9 +5868,9 @@ mod tests {
let fixslice = rv_fixslice_aes::RvFixsliceRoundKeys::new(&key);
let mut blocks = [[0u8; BLOCK_SIZE]; 4];
- for (i, block) in blocks.iter_mut().enumerate() {
- for (j, byte) in block.iter_mut().enumerate() {
- *byte = (i as u8).wrapping_mul(0x31) ^ (j as u8).wrapping_mul(0x57) ^ 0xa6;
+ for (i, block) in (0u8..).zip(&mut blocks) {
+ for (j, byte) in (0u8..).zip(block) {
+ *byte = i.wrapping_mul(0x31) ^ j.wrapping_mul(0x57) ^ 0xa6;
}
}
diff --git a/src/aead/aes/aarch64/asm.rs b/src/aead/aes/aarch64/asm.rs
index f5115eb7..e40c9902 100644
--- a/src/aead/aes/aarch64/asm.rs
+++ b/src/aead/aes/aarch64/asm.rs
@@ -1,7 +1,5 @@
//! AArch64 rscrypto-owned AES-GCM assembly kernels.
-#![allow(unsafe_code)]
-
use core::arch::global_asm;
global_asm!(include_str!("asm/rscrypto_aes_gcm_aarch64_apple_darwin.s"));
@@ -19,8 +17,8 @@ impl AesGcmAarch64State {
#[inline]
pub(super) fn new(acc: u128, ctr: u32) -> Self {
Self {
- acc_lo: acc as u64,
- acc_hi: (acc >> 64) as u64,
+ acc_lo: u64::try_from(acc & u128::from(u64::MAX)).expect("masked accumulator half fits u64"),
+ acc_hi: u64::try_from(acc >> 64).expect("shifted accumulator half fits u64"),
ctr,
_pad: 0,
processed: 0,
diff --git a/src/aead/aes/aarch64_ce.rs b/src/aead/aes/aarch64_ce.rs
index b22c1cd5..ea1250aa 100644
--- a/src/aead/aes/aarch64_ce.rs
+++ b/src/aead/aes/aarch64_ce.rs
@@ -266,6 +266,11 @@ pub(super) unsafe fn encrypt_6blocks_core(keys: &CeRoundKeys, blocks: &mut [[u8;
#[cfg(feature = "aes-gcm")]
#[inline]
+/// Convert one initialized GHASH byte vector to its big-endian `u128` value.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available.
unsafe fn ghash_be_u128_from_vec(block: uint8x16_t) -> u128 {
// SAFETY: GHASH lane conversion because:
// 1. The caller is already inside an AArch64 NEON target scope.
@@ -282,16 +287,48 @@ unsafe fn ghash_be_u128_from_vec(block: uint8x16_t) -> u128 {
#[cfg(feature = "aes-gcm")]
#[inline(always)]
+/// Split one GHASH accumulator into its low and high 64-bit NEON lanes.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available.
unsafe fn ghash_u128_to_lanes(x: u128) -> uint64x2_t {
+ let [
+ lo_0,
+ lo_1,
+ lo_2,
+ lo_3,
+ lo_4,
+ lo_5,
+ lo_6,
+ lo_7,
+ hi_0,
+ hi_1,
+ hi_2,
+ hi_3,
+ hi_4,
+ hi_5,
+ hi_6,
+ hi_7,
+ ] = x.to_le_bytes();
+ let lo = u64::from_le_bytes([lo_0, lo_1, lo_2, lo_3, lo_4, lo_5, lo_6, lo_7]);
+ let hi = u64::from_le_bytes([hi_0, hi_1, hi_2, hi_3, hi_4, hi_5, hi_6, hi_7]);
+
// SAFETY: GHASH accumulator lane construction because:
// 1. The caller is already inside an AArch64 NEON target scope.
// 2. `vcreate_u64` initializes one 64-bit lane from an integer value.
// 3. `vcombine_u64` builds a fully initialized two-lane vector.
- unsafe { vcombine_u64(vcreate_u64(x as u64), vcreate_u64((x >> 64) as u64)) }
+ unsafe { vcombine_u64(vcreate_u64(lo), vcreate_u64(hi)) }
}
#[cfg(feature = "aes-gcm")]
#[inline(always)]
+/// Reduce one 256-bit GHASH carryless product to two Montgomery-form lanes.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available and pass initialized low and high
+/// product halves.
unsafe fn ghash_mont_reduce_neon(lo: uint64x2_t, hi: uint64x2_t) -> uint64x2_t {
// SAFETY: GHASH Montgomery reduction because:
// 1. The caller is already inside an AArch64 NEON target scope.
@@ -315,6 +352,12 @@ unsafe fn ghash_mont_reduce_neon(lo: uint64x2_t, hi: uint64x2_t) -> uint64x2_t {
#[cfg(feature = "aes-gcm")]
#[inline(always)]
+/// Finish the three Karatsuba accumulators and return the reduced GHASH value.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available and pass initialized low, high, and
+/// middle product accumulators for the same fold.
unsafe fn ghash_finish_products(ll: uint64x2_t, hh: uint64x2_t, mm: uint64x2_t) -> u128 {
// SAFETY: GHASH Karatsuba accumulator finalization because:
// 1. The caller is already inside an AArch64 NEON target scope.
@@ -332,6 +375,12 @@ unsafe fn ghash_finish_products(ll: uint64x2_t, hh: uint64x2_t, mm: uint64x2_t)
#[cfg(feature = "aes-gcm")]
#[inline(always)]
+/// Load one ciphertext block without imposing an alignment requirement.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available. `ptr` must remain valid and readable
+/// for 16 initialized bytes for the duration of the call; it need not be aligned.
unsafe fn gcm_load_ciphertext_block(ptr: *const u8) -> uint8x16_t {
// SAFETY: opaque 16-byte ciphertext load for GCM open because:
// 1. The caller passes a pointer into a 128-byte chunk already bounds-checked by the enclosing
@@ -353,6 +402,12 @@ unsafe fn gcm_load_ciphertext_block(ptr: *const u8) -> uint8x16_t {
#[cfg(feature = "aes-gcm")]
#[inline(always)]
+/// Load one GHASH key power as two unaligned 64-bit lanes.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available. `power` must remain valid and readable
+/// for one initialized `u128` for the duration of the call; it need not be aligned.
unsafe fn ghash_load_power(power: *const u128) -> uint64x2_t {
// SAFETY: GHASH H-power vector load because:
// 1. The caller passes a pointer into a live H-power table.
@@ -395,6 +450,11 @@ macro_rules! gcm_schedule_barrier {
#[cfg(feature = "aes-gcm")]
#[inline(always)]
+/// Build the fixed 96-bit prefix of an AES-GCM big-endian counter block.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available.
unsafe fn gcm_ctr32_base(iv_prefix: &[u8; 12]) -> uint8x16_t {
let mut block = [0u8; 16];
block[..12].copy_from_slice(iv_prefix);
@@ -407,6 +467,11 @@ unsafe fn gcm_ctr32_base(iv_prefix: &[u8; 12]) -> uint8x16_t {
#[cfg(feature = "aes-gcm")]
#[inline(always)]
+/// Insert one AES-GCM counter into the big-endian final lane.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available and pass an initialized counter base.
unsafe fn gcm_ctr32_block(base: uint8x16_t, ctr: u32) -> uint8x16_t {
// SAFETY: GCM counter lane update because:
// 1. The caller is already inside an AArch64 NEON target scope.
@@ -417,6 +482,11 @@ unsafe fn gcm_ctr32_block(base: uint8x16_t, ctr: u32) -> uint8x16_t {
#[cfg(feature = "aes-gcm-siv")]
#[inline(always)]
+/// Build the fixed 96-bit suffix of an AES-GCM-SIV little-endian counter block.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available.
unsafe fn gcmsiv_ctr32_base(iv_suffix: &[u8; 12]) -> uint8x16_t {
let mut block = [0u8; 16];
block[4..16].copy_from_slice(iv_suffix);
@@ -429,6 +499,11 @@ unsafe fn gcmsiv_ctr32_base(iv_suffix: &[u8; 12]) -> uint8x16_t {
#[cfg(feature = "aes-gcm-siv")]
#[inline(always)]
+/// Insert one AES-GCM-SIV counter into the little-endian first lane.
+///
+/// # Safety
+///
+/// The caller must execute this function only when AArch64 NEON is available and pass an initialized counter base.
unsafe fn gcmsiv_ctr32_block(base: uint8x16_t, ctr: u32) -> uint8x16_t {
// SAFETY: GCM-SIV counter lane update because:
// 1. The caller is inside a NEON target scope.
@@ -751,6 +826,12 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_8blocks_core(
#[cfg(feature = "aes-gcm")]
#[target_feature(enable = "aes,neon")]
#[inline]
+/// Encrypt eight AES-256-GCM blocks while folding the preceding ciphertext group.
+///
+/// # Safety
+///
+/// The caller must ensure AES, PMULL, and NEON are available; `data` contains at least 128 bytes; and the round keys,
+/// accumulator, GHASH powers, and preceding ciphertext lanes belong to the same GCM key and stream.
pub(super) unsafe fn encrypt_ctr32_be_xor_8blocks_ghash_prev_bytes_core(
keys: &CeRoundKeys,
iv_prefix: &[u8; 12],
@@ -883,6 +964,12 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_8blocks_ghash_prev_bytes_core(
#[cfg(feature = "aes-gcm")]
#[target_feature(enable = "aes,neon")]
#[inline]
+/// Fold and decrypt eight current AES-256-GCM ciphertext blocks.
+///
+/// # Safety
+///
+/// The caller must ensure AES, PMULL, and NEON are available; `data` contains at least 128 bytes; and the round keys,
+/// accumulator, and GHASH powers belong to the same GCM key and stream.
pub(super) unsafe fn decrypt_ctr32_be_xor_8blocks_ghash_current_core(
keys: &CeRoundKeys,
iv_prefix: &[u8; 12],
@@ -1071,6 +1158,12 @@ pub(super) unsafe fn decrypt_ctr32_be_xor_8blocks_ghash_current_core(
#[cfg(feature = "aes-gcm")]
#[target_feature(enable = "aes,neon")]
#[inline(never)]
+/// Encrypt complete 128-byte AES-256-GCM chunks and fold their ciphertext into GHASH.
+///
+/// # Safety
+///
+/// The caller must ensure AES, PMULL, and NEON are available; `data` contains at least 128 bytes; and the round keys,
+/// counter prefix, accumulator, and GHASH tables belong to the same GCM key and stream.
pub(super) unsafe fn encrypt_ctr32_be_xor_ghash_128b_chunks_core(
keys: &CeRoundKeys,
iv_prefix: &[u8; 12],
@@ -1102,7 +1195,7 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_ghash_128b_chunks_core(
tables.h_powers_rev_16.as_ptr(),
tables.h_powers_rev_16_mid.as_ptr(),
tables.h_powers_rev_16_pair.as_ptr(),
- &mut state,
+ &raw mut state,
);
if state.processed != 0 {
return (state.acc(), state.ctr, state.processed);
@@ -1116,7 +1209,7 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_ghash_128b_chunks_core(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_8.as_ptr(),
- &mut state,
+ &raw mut state,
);
if state.processed != 0 {
return (state.acc(), state.ctr, state.processed);
@@ -1157,6 +1250,12 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_ghash_128b_chunks_core(
#[cfg(feature = "aes-gcm")]
#[target_feature(enable = "aes,neon")]
#[inline(never)]
+/// Fold and decrypt complete 128-byte AES-256-GCM ciphertext chunks.
+///
+/// # Safety
+///
+/// The caller must ensure AES, PMULL, and NEON are available and the round keys, counter prefix, accumulator, and
+/// GHASH tables belong to the same GCM key and stream. `data` may be shorter than one complete chunk.
pub(super) unsafe fn decrypt_ctr32_be_xor_ghash_128b_chunks_core(
keys: &CeRoundKeys,
iv_prefix: &[u8; 12],
@@ -1184,7 +1283,7 @@ pub(super) unsafe fn decrypt_ctr32_be_xor_ghash_128b_chunks_core(
tables.h_powers_rev_16.as_ptr(),
tables.h_powers_rev_16_mid.as_ptr(),
tables.h_powers_rev_16_pair.as_ptr(),
- &mut state,
+ &raw mut state,
);
if state.processed != 0 {
return (state.acc(), state.ctr, state.processed);
@@ -1198,7 +1297,7 @@ pub(super) unsafe fn decrypt_ctr32_be_xor_ghash_128b_chunks_core(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_8.as_ptr(),
- &mut state,
+ &raw mut state,
);
if state.processed != 0 {
return (state.acc(), state.ctr, state.processed);
@@ -1733,6 +1832,12 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_8blocks_128_core(
#[cfg(feature = "aes-gcm")]
#[target_feature(enable = "aes,neon")]
#[inline]
+/// Encrypt eight AES-128-GCM blocks while folding the preceding ciphertext group.
+///
+/// # Safety
+///
+/// The caller must ensure AES, PMULL, and NEON are available; `data` contains at least 128 bytes; and the round keys,
+/// accumulator, GHASH powers, and preceding ciphertext lanes belong to the same GCM key and stream.
pub(super) unsafe fn encrypt_ctr32_be_xor_8blocks_ghash_prev_bytes_128_core(
keys: &Ce128RoundKeys,
iv_prefix: &[u8; 12],
@@ -1858,6 +1963,12 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_8blocks_ghash_prev_bytes_128_core(
#[cfg(feature = "aes-gcm")]
#[target_feature(enable = "aes,neon")]
#[inline]
+/// Fold and decrypt eight current AES-128-GCM ciphertext blocks.
+///
+/// # Safety
+///
+/// The caller must ensure AES, PMULL, and NEON are available; `data` contains at least 128 bytes; and the round keys,
+/// accumulator, and GHASH powers belong to the same GCM key and stream.
pub(super) unsafe fn decrypt_ctr32_be_xor_8blocks_ghash_current_128_core(
keys: &Ce128RoundKeys,
iv_prefix: &[u8; 12],
@@ -2039,6 +2150,12 @@ pub(super) unsafe fn decrypt_ctr32_be_xor_8blocks_ghash_current_128_core(
#[cfg(feature = "aes-gcm")]
#[target_feature(enable = "aes,neon")]
#[inline(never)]
+/// Encrypt complete 128-byte AES-128-GCM chunks and fold their ciphertext into GHASH.
+///
+/// # Safety
+///
+/// The caller must ensure AES, PMULL, and NEON are available; `data` contains at least 128 bytes; and the round keys,
+/// counter prefix, accumulator, and GHASH tables belong to the same GCM key and stream.
pub(super) unsafe fn encrypt_ctr32_be_xor_ghash_128b_chunks_128_core(
keys: &Ce128RoundKeys,
iv_prefix: &[u8; 12],
@@ -2070,7 +2187,7 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_ghash_128b_chunks_128_core(
tables.h_powers_rev_16.as_ptr(),
tables.h_powers_rev_16_mid.as_ptr(),
tables.h_powers_rev_16_pair.as_ptr(),
- &mut state,
+ &raw mut state,
);
if state.processed != 0 {
return (state.acc(), state.ctr, state.processed);
@@ -2084,7 +2201,7 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_ghash_128b_chunks_128_core(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_8.as_ptr(),
- &mut state,
+ &raw mut state,
);
if state.processed != 0 {
return (state.acc(), state.ctr, state.processed);
@@ -2125,6 +2242,12 @@ pub(super) unsafe fn encrypt_ctr32_be_xor_ghash_128b_chunks_128_core(
#[cfg(feature = "aes-gcm")]
#[target_feature(enable = "aes,neon")]
#[inline(never)]
+/// Fold and decrypt complete 128-byte AES-128-GCM ciphertext chunks.
+///
+/// # Safety
+///
+/// The caller must ensure AES, PMULL, and NEON are available and the round keys, counter prefix, accumulator, and
+/// GHASH tables belong to the same GCM key and stream. `data` may be shorter than one complete chunk.
pub(super) unsafe fn decrypt_ctr32_be_xor_ghash_128b_chunks_128_core(
keys: &Ce128RoundKeys,
iv_prefix: &[u8; 12],
@@ -2152,7 +2275,7 @@ pub(super) unsafe fn decrypt_ctr32_be_xor_ghash_128b_chunks_128_core(
tables.h_powers_rev_16.as_ptr(),
tables.h_powers_rev_16_mid.as_ptr(),
tables.h_powers_rev_16_pair.as_ptr(),
- &mut state,
+ &raw mut state,
);
if state.processed != 0 {
return (state.acc(), state.ctr, state.processed);
@@ -2166,7 +2289,7 @@ pub(super) unsafe fn decrypt_ctr32_be_xor_ghash_128b_chunks_128_core(
data.as_mut_ptr(),
data.len(),
tables.h_powers_rev_8.as_ptr(),
- &mut state,
+ &raw mut state,
);
if state.processed != 0 {
return (state.acc(), state.ctr, state.processed);
diff --git a/src/aead/aes/riscv64_fixslice_aes.rs b/src/aead/aes/riscv64_fixslice_aes.rs
index 19002124..5c896fe5 100644
--- a/src/aead/aes/riscv64_fixslice_aes.rs
+++ b/src/aead/aes/riscv64_fixslice_aes.rs
@@ -8,8 +8,6 @@
//! Reference: Adomnicai et al., "Fixslicing AES-like Ciphers",
//! .
-#![allow(clippy::unreadable_literal)]
-
use super::{BLOCK_SIZE, KEY_SIZE, KEY_SIZE_128};
type State = [u64; 8];
@@ -27,7 +25,7 @@ impl RvFixsliceRoundKeys {
}
#[inline]
- #[allow(dead_code)]
+ #[cfg(target_arch = "riscv64")]
pub(super) fn zeroize(&mut self) {
// SAFETY: `[u64; 120]` is contiguous and valid to view as bytes for its
// exact initialized size.
@@ -51,7 +49,7 @@ impl RvFixslice128RoundKeys {
}
#[inline]
- #[allow(dead_code)]
+ #[cfg(target_arch = "riscv64")]
pub(super) fn zeroize(&mut self) {
// SAFETY: `[u64; 88]` is contiguous and valid to view as bytes for its
// exact initialized size.
@@ -110,7 +108,6 @@ pub(super) fn encrypt_4blocks(rkeys: &RvFixsliceRoundKeys, blocks: &mut [[u8; BL
}
#[inline]
-#[allow(dead_code)]
pub(super) fn cipher_round_4(blocks: &mut [[u8; BLOCK_SIZE]; 4], round_keys: &[[u8; BLOCK_SIZE]; 4]) {
let mut state = State::default();
bitslice(&mut state, &blocks[0], &blocks[1], &blocks[2], &blocks[3]);
@@ -625,14 +622,15 @@ fn inv_bitslice(input: &[u64]) -> [[u8; BLOCK_SIZE]; 4] {
#[rustfmt::skip]
fn write_reordered(columns: u64, output: &mut [u8]) {
- output[0x0] = (columns ) as u8;
- output[0x1] = (columns >> 0x10) as u8;
- output[0x2] = (columns >> 0x20) as u8;
- output[0x3] = (columns >> 0x30) as u8;
- output[0x8] = (columns >> 0x08) as u8;
- output[0x9] = (columns >> 0x18) as u8;
- output[0xa] = (columns >> 0x28) as u8;
- output[0xb] = (columns >> 0x38) as u8;
+ let bytes = columns.to_le_bytes();
+ output[0x0] = bytes[0];
+ output[0x1] = bytes[2];
+ output[0x2] = bytes[4];
+ output[0x3] = bytes[6];
+ output[0x8] = bytes[1];
+ output[0x9] = bytes[3];
+ output[0xa] = bytes[5];
+ output[0xb] = bytes[7];
}
let mut output = [[0u8; BLOCK_SIZE]; 4];
@@ -691,7 +689,7 @@ fn ror(x: u64, y: u32) -> u64 {
#[inline(always)]
fn ror_distance(rows: u32, cols: u32) -> u32 {
- (rows << 4) + (cols << 2)
+ rows.strict_shl(4).strict_add(cols.strict_shl(2))
}
#[inline(always)]
diff --git a/src/aead/aes/riscv64_vperm_aes.rs b/src/aead/aes/riscv64_vperm_aes.rs
deleted file mode 100644
index f48333f8..00000000
--- a/src/aead/aes/riscv64_vperm_aes.rs
+++ /dev/null
@@ -1,617 +0,0 @@
-use core::arch::asm;
-
-use crate::aead::aes_round::{
- AES_AFFINE, MC_ROT1, MC_ROT2, VPERM_INV_HI, VPERM_INV_LO, VPERM_IPT_HI, VPERM_IPT_LO, VPERM_SBOT, VPERM_SBOU,
- VPERM_SR, XTIME_REDUCE,
-};
-
-/// Precomputed Hamburg vperm table block — contiguous for offset-based loads.
-#[repr(C, align(16))]
-struct VpermTables {
- ipt_lo: [u8; 16], // offset 0
- ipt_hi: [u8; 16], // offset 16
- inv_lo: [u8; 16], // offset 32
- inv_hi: [u8; 16], // offset 48
- sbou: [u8; 16], // offset 64
- sbot: [u8; 16], // offset 80
- sr_perm: [u8; 16], // offset 96
- mc_rot1: [u8; 16], // offset 112
- mc_rot2: [u8; 16], // offset 128
- affine: [u8; 16], // offset 144
- xtime: [u8; 16], // offset 160
-}
-
-impl VpermTables {
- #[inline(always)]
- fn load() -> Self {
- Self {
- ipt_lo: VPERM_IPT_LO,
- ipt_hi: VPERM_IPT_HI,
- inv_lo: VPERM_INV_LO,
- inv_hi: VPERM_INV_HI,
- sbou: VPERM_SBOU,
- sbot: VPERM_SBOT,
- sr_perm: VPERM_SR,
- mc_rot1: MC_ROT1,
- mc_rot2: MC_ROT2,
- affine: [AES_AFFINE; 16],
- xtime: [XTIME_REDUCE; 16],
- }
- }
-}
-
-/// Extract round key bytes from the portable key schedule.
-#[inline]
-fn round_key_bytes(rk: &[u32; super::EXPANDED_KEY_WORDS], round: usize) -> [u8; 16] {
- let off = round.strict_mul(4);
- let mut bytes = [0u8; 16];
- bytes[0..4].copy_from_slice(&rk[off].to_be_bytes());
- bytes[4..8].copy_from_slice(&rk[off.strict_add(1)].to_be_bytes());
- bytes[8..12].copy_from_slice(&rk[off.strict_add(2)].to_be_bytes());
- bytes[12..16].copy_from_slice(&rk[off.strict_add(3)].to_be_bytes());
- bytes
-}
-
-/// Extract round key bytes from the AES-128 portable key schedule.
-#[inline]
-fn round_key_bytes_128(rk: &[u32; super::EXPANDED_KEY_WORDS_128], round: usize) -> [u8; 16] {
- let off = round.strict_mul(4);
- let mut bytes = [0u8; 16];
- bytes[0..4].copy_from_slice(&rk[off].to_be_bytes());
- bytes[4..8].copy_from_slice(&rk[off.strict_add(1)].to_be_bytes());
- bytes[8..12].copy_from_slice(&rk[off.strict_add(2)].to_be_bytes());
- bytes[12..16].copy_from_slice(&rk[off.strict_add(3)].to_be_bytes());
- bytes
-}
-
-macro_rules! vperm_inner_round_m1 {
- ($state:literal, $rk:literal) => {
- concat!(
- "vand.vi v19, ",
- $state,
- ", 15\n",
- "vsrl.vi v20, ",
- $state,
- ", 4\n",
- "vrgather.vv v21, v8, v19\n",
- "vrgather.vv v22, v9, v20\n",
- "vxor.vv v19, v21, v22\n",
- "vand.vi v20, v19, 15\n",
- "vsrl.vi v21, v19, 4\n",
- "vrgather.vv v22, v11, v20\n",
- "vxor.vv v23, v21, v20\n",
- "vrgather.vv v24, v10, v21\n",
- "vxor.vv v25, v24, v22\n",
- "vrgather.vv v24, v10, v23\n",
- "vxor.vv v26, v24, v22\n",
- "vand.vi v27, v25, 15\n",
- "vrgather.vv v28, v10, v27\n",
- "vsra.vi v29, v25, 7\n",
- "vxor.vi v29, v29, -1\n",
- "vand.vv v28, v28, v29\n",
- "vxor.vv v19, v28, v23\n",
- "vand.vi v27, v26, 15\n",
- "vrgather.vv v28, v10, v27\n",
- "vsra.vi v29, v26, 7\n",
- "vxor.vi v29, v29, -1\n",
- "vand.vv v28, v28, v29\n",
- "vxor.vv v20, v28, v21\n",
- "vand.vi v27, v19, 15\n",
- "vrgather.vv v28, v12, v27\n",
- "vsra.vi v29, v19, 7\n",
- "vxor.vi v29, v29, -1\n",
- "vand.vv v22, v28, v29\n",
- "vand.vi v27, v20, 15\n",
- "vrgather.vv v30, v13, v27\n",
- "vsra.vi v29, v20, 7\n",
- "vxor.vi v29, v29, -1\n",
- "vand.vv v23, v30, v29\n",
- "vxor.vv v19, v22, v23\n",
- "vxor.vv v19, v19, v17\n",
- "vrgather.vv v20, v19, v14\n",
- "vrgather.vv v21, v20, v15\n",
- "vxor.vv v22, v20, v21\n",
- "vsll.vi v23, v22, 1\n",
- "vsra.vi v24, v22, 7\n",
- "vand.vv v24, v24, v18\n",
- "vxor.vv v23, v23, v24\n",
- "vrgather.vv v24, v22, v16\n",
- "vxor.vv v25, v22, v24\n",
- "vxor.vv v26, v20, v25\n",
- "vxor.vv v26, v26, v23\n",
- "vxor.vv ",
- $state,
- ", v26, ",
- $rk
- )
- };
-}
-
-macro_rules! vperm_final_round_m1 {
- ($state:literal, $rk:literal) => {
- concat!(
- "vand.vi v19, ",
- $state,
- ", 15\n",
- "vsrl.vi v20, ",
- $state,
- ", 4\n",
- "vrgather.vv v21, v8, v19\n",
- "vrgather.vv v22, v9, v20\n",
- "vxor.vv v19, v21, v22\n",
- "vand.vi v20, v19, 15\n",
- "vsrl.vi v21, v19, 4\n",
- "vrgather.vv v22, v11, v20\n",
- "vxor.vv v23, v21, v20\n",
- "vrgather.vv v24, v10, v21\n",
- "vxor.vv v25, v24, v22\n",
- "vrgather.vv v24, v10, v23\n",
- "vxor.vv v26, v24, v22\n",
- "vand.vi v27, v25, 15\n",
- "vrgather.vv v28, v10, v27\n",
- "vsra.vi v29, v25, 7\n",
- "vxor.vi v29, v29, -1\n",
- "vand.vv v28, v28, v29\n",
- "vxor.vv v19, v28, v23\n",
- "vand.vi v27, v26, 15\n",
- "vrgather.vv v28, v10, v27\n",
- "vsra.vi v29, v26, 7\n",
- "vxor.vi v29, v29, -1\n",
- "vand.vv v28, v28, v29\n",
- "vxor.vv v20, v28, v21\n",
- "vand.vi v27, v19, 15\n",
- "vrgather.vv v28, v12, v27\n",
- "vsra.vi v29, v19, 7\n",
- "vxor.vi v29, v29, -1\n",
- "vand.vv v22, v28, v29\n",
- "vand.vi v27, v20, 15\n",
- "vrgather.vv v30, v13, v27\n",
- "vsra.vi v29, v20, 7\n",
- "vxor.vi v29, v29, -1\n",
- "vand.vv v23, v30, v29\n",
- "vxor.vv v19, v22, v23\n",
- "vxor.vv v19, v19, v17\n",
- "vrgather.vv v20, v19, v14\n",
- "vxor.vv ",
- $state,
- ", v20, ",
- $rk
- )
- };
-}
-
-/// Single inner AES round (SubBytes + ShiftRows + MixColumns + AddRoundKey).
-/// Same asm as aegis256::rv_vperm::aes_round.
-#[target_feature(enable = "v")]
-#[inline]
-/// # Safety
-///
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-unsafe fn aes_inner_round(block: &[u8; 16], round_key: &[u8; 16], tables: &VpermTables) -> [u8; 16] {
- let mut out = [0u8; 16];
- // SAFETY: caller guarantees the RISC-V V extension is available and the
- // asm block only reads the provided state/table/key buffers and writes `out`.
- unsafe {
- asm!(
- "vsetivli zero, 16, e8, m1, ta, ma",
- // Load tables.
- "vle8.v v2, ({tbl})",
- "addi {tmp}, {tbl}, 16",
- "vle8.v v3, ({tmp})",
- "addi {tmp}, {tbl}, 32",
- "vle8.v v4, ({tmp})",
- "addi {tmp}, {tbl}, 48",
- "vle8.v v5, ({tmp})",
- "addi {tmp}, {tbl}, 64",
- "vle8.v v6, ({tmp})",
- "addi {tmp}, {tbl}, 80",
- "vle8.v v7, ({tmp})",
- "addi {tmp}, {tbl}, 96",
- "vle8.v v8, ({tmp})",
- "addi {tmp}, {tbl}, 112",
- "vle8.v v9, ({tmp})",
- "addi {tmp}, {tbl}, 128",
- "vle8.v v10, ({tmp})",
- "addi {tmp}, {tbl}, 144",
- "vle8.v v11, ({tmp})",
- "addi {tmp}, {tbl}, 160",
- "vle8.v v12, ({tmp})",
- "vle8.v v0, ({state})",
- "vle8.v v1, ({rk})",
- // Phase 1: Nibble extraction.
- "vand.vi v14, v0, 15",
- "vsrl.vi v15, v0, 4",
- // Phase 2: Input transform.
- "vrgather.vv v16, v2, v14",
- "vrgather.vv v17, v3, v15",
- "vxor.vv v14, v16, v17",
- // Phase 3: Re-extract nibbles.
- "vand.vi v15, v14, 15",
- "vsrl.vi v16, v14, 4",
- // Phase 4: GF(2^4) inverse.
- "vrgather.vv v17, v5, v15",
- "vxor.vv v18, v16, v15",
- "vrgather.vv v19, v4, v16",
- "vxor.vv v20, v19, v17",
- "vrgather.vv v21, v4, v18",
- "vxor.vv v22, v21, v17",
- "vand.vi v23, v20, 15",
- "vrgather.vv v24, v4, v23",
- "vsra.vi v25, v20, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v24, v24, v26",
- "vxor.vv v14, v24, v18",
- "vand.vi v23, v22, 15",
- "vrgather.vv v24, v4, v23",
- "vsra.vi v25, v22, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v24, v24, v26",
- "vxor.vv v15, v24, v16",
- // Phase 5: Output transform.
- "vand.vi v23, v14, 15",
- "vrgather.vv v24, v6, v23",
- "vsra.vi v25, v14, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v16, v24, v26",
- "vand.vi v23, v15, 15",
- "vrgather.vv v24, v7, v23",
- "vsra.vi v25, v15, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v17, v24, v26",
- "vxor.vv v14, v16, v17",
- "vxor.vv v14, v14, v11",
- // ShiftRows.
- "vrgather.vv v15, v14, v8",
- // MixColumns.
- "vrgather.vv v16, v15, v9",
- "vxor.vv v17, v15, v16",
- "vsll.vi v18, v17, 1",
- "vsra.vi v19, v17, 7",
- "vand.vv v19, v19, v12",
- "vxor.vv v18, v18, v19",
- "vrgather.vv v19, v17, v10",
- "vxor.vv v20, v17, v19",
- "vxor.vv v14, v15, v20",
- "vxor.vv v14, v14, v18",
- // AddRoundKey.
- "vxor.vv v0, v14, v1",
- "vse8.v v0, ({out})",
- state = in(reg) block.as_ptr(),
- rk = in(reg) round_key.as_ptr(),
- tbl = in(reg) tables as *const VpermTables as *const u8,
- out = in(reg) out.as_mut_ptr(),
- tmp = out(reg) _,
- options(nostack),
- );
- }
- out
-}
-
-/// Final AES round (SubBytes + ShiftRows + AddRoundKey, no MixColumns).
-#[target_feature(enable = "v")]
-#[inline]
-/// # Safety
-///
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-unsafe fn aes_final_round(block: &[u8; 16], round_key: &[u8; 16], tables: &VpermTables) -> [u8; 16] {
- let mut out = [0u8; 16];
- // SAFETY: caller guarantees the RISC-V V extension is available and the
- // asm block only reads the provided state/table/key buffers and writes `out`.
- unsafe {
- asm!(
- "vsetivli zero, 16, e8, m1, ta, ma",
- // Load tables (only need SubBytes + ShiftRows, no MixColumns).
- "vle8.v v2, ({tbl})",
- "addi {tmp}, {tbl}, 16",
- "vle8.v v3, ({tmp})",
- "addi {tmp}, {tbl}, 32",
- "vle8.v v4, ({tmp})",
- "addi {tmp}, {tbl}, 48",
- "vle8.v v5, ({tmp})",
- "addi {tmp}, {tbl}, 64",
- "vle8.v v6, ({tmp})",
- "addi {tmp}, {tbl}, 80",
- "vle8.v v7, ({tmp})",
- "addi {tmp}, {tbl}, 96",
- "vle8.v v8, ({tmp})",
- "addi {tmp}, {tbl}, 144",
- "vle8.v v11, ({tmp})",
- "vle8.v v0, ({state})",
- "vle8.v v1, ({rk})",
- // SubBytes (same as inner round).
- "vand.vi v14, v0, 15",
- "vsrl.vi v15, v0, 4",
- "vrgather.vv v16, v2, v14",
- "vrgather.vv v17, v3, v15",
- "vxor.vv v14, v16, v17",
- "vand.vi v15, v14, 15",
- "vsrl.vi v16, v14, 4",
- "vrgather.vv v17, v5, v15",
- "vxor.vv v18, v16, v15",
- "vrgather.vv v19, v4, v16",
- "vxor.vv v20, v19, v17",
- "vrgather.vv v21, v4, v18",
- "vxor.vv v22, v21, v17",
- "vand.vi v23, v20, 15",
- "vrgather.vv v24, v4, v23",
- "vsra.vi v25, v20, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v24, v24, v26",
- "vxor.vv v14, v24, v18",
- "vand.vi v23, v22, 15",
- "vrgather.vv v24, v4, v23",
- "vsra.vi v25, v22, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v24, v24, v26",
- "vxor.vv v15, v24, v16",
- "vand.vi v23, v14, 15",
- "vrgather.vv v24, v6, v23",
- "vsra.vi v25, v14, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v16, v24, v26",
- "vand.vi v23, v15, 15",
- "vrgather.vv v24, v7, v23",
- "vsra.vi v25, v15, 7",
- "vxor.vi v26, v25, -1",
- "vand.vv v17, v24, v26",
- "vxor.vv v14, v16, v17",
- "vxor.vv v14, v14, v11",
- // ShiftRows (no MixColumns).
- "vrgather.vv v15, v14, v8",
- // AddRoundKey.
- "vxor.vv v0, v15, v1",
- "vse8.v v0, ({out})",
- state = in(reg) block.as_ptr(),
- rk = in(reg) round_key.as_ptr(),
- tbl = in(reg) tables as *const VpermTables as *const u8,
- out = in(reg) out.as_mut_ptr(),
- tmp = out(reg) _,
- options(nostack),
- );
- }
- out
-}
-
-/// Four independent inner rounds with one shared table load.
-///
-/// This targets the AES-GCM-SIV fixed-cost path, where six ECB blocks are
-/// derived up front. Sharing the vperm tables across 4 blocks removes most
-/// of the per-call table-load overhead even before we attempt a wider LMUL
-/// kernel.
-#[target_feature(enable = "v")]
-#[inline]
-/// # Safety
-///
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-unsafe fn aes_inner_round_4(blocks: &mut [[u8; 16]; 4], round_key: &[u8; 16], tables: &VpermTables) {
- // SAFETY: caller guarantees the RISC-V V extension is available and all
- // block/key/table references are valid for 16-byte vector loads/stores.
- unsafe {
- asm!(
- "vsetivli zero, 16, e8, m1, ta, ma",
- "vle8.v v8, ({tbl})",
- "addi {tmp}, {tbl}, 16",
- "vle8.v v9, ({tmp})",
- "addi {tmp}, {tbl}, 32",
- "vle8.v v10, ({tmp})",
- "addi {tmp}, {tbl}, 48",
- "vle8.v v11, ({tmp})",
- "addi {tmp}, {tbl}, 64",
- "vle8.v v12, ({tmp})",
- "addi {tmp}, {tbl}, 80",
- "vle8.v v13, ({tmp})",
- "addi {tmp}, {tbl}, 96",
- "vle8.v v14, ({tmp})",
- "addi {tmp}, {tbl}, 112",
- "vle8.v v15, ({tmp})",
- "addi {tmp}, {tbl}, 128",
- "vle8.v v16, ({tmp})",
- "addi {tmp}, {tbl}, 144",
- "vle8.v v17, ({tmp})",
- "addi {tmp}, {tbl}, 160",
- "vle8.v v18, ({tmp})",
- "vle8.v v0, ({b0})",
- "vle8.v v1, ({b1})",
- "vle8.v v2, ({b2})",
- "vle8.v v3, ({b3})",
- "vle8.v v4, ({rk})",
- vperm_inner_round_m1!("v0", "v4"),
- vperm_inner_round_m1!("v1", "v4"),
- vperm_inner_round_m1!("v2", "v4"),
- vperm_inner_round_m1!("v3", "v4"),
- "vse8.v v0, ({b0})",
- "vse8.v v1, ({b1})",
- "vse8.v v2, ({b2})",
- "vse8.v v3, ({b3})",
- b0 = in(reg) blocks[0].as_mut_ptr(),
- b1 = in(reg) blocks[1].as_mut_ptr(),
- b2 = in(reg) blocks[2].as_mut_ptr(),
- b3 = in(reg) blocks[3].as_mut_ptr(),
- rk = in(reg) round_key.as_ptr(),
- tbl = in(reg) tables as *const VpermTables as *const u8,
- tmp = out(reg) _,
- options(nostack),
- );
- }
-}
-
-/// Four independent final rounds with one shared table load.
-#[target_feature(enable = "v")]
-#[inline]
-/// # Safety
-///
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-unsafe fn aes_final_round_4(blocks: &mut [[u8; 16]; 4], round_key: &[u8; 16], tables: &VpermTables) {
- // SAFETY: caller guarantees the RISC-V V extension is available and all
- // block/key/table references are valid for 16-byte vector loads/stores.
- unsafe {
- asm!(
- "vsetivli zero, 16, e8, m1, ta, ma",
- "vle8.v v8, ({tbl})",
- "addi {tmp}, {tbl}, 16",
- "vle8.v v9, ({tmp})",
- "addi {tmp}, {tbl}, 32",
- "vle8.v v10, ({tmp})",
- "addi {tmp}, {tbl}, 48",
- "vle8.v v11, ({tmp})",
- "addi {tmp}, {tbl}, 64",
- "vle8.v v12, ({tmp})",
- "addi {tmp}, {tbl}, 80",
- "vle8.v v13, ({tmp})",
- "addi {tmp}, {tbl}, 96",
- "vle8.v v14, ({tmp})",
- "addi {tmp}, {tbl}, 144",
- "vle8.v v17, ({tmp})",
- "vle8.v v0, ({b0})",
- "vle8.v v1, ({b1})",
- "vle8.v v2, ({b2})",
- "vle8.v v3, ({b3})",
- "vle8.v v4, ({rk})",
- vperm_final_round_m1!("v0", "v4"),
- vperm_final_round_m1!("v1", "v4"),
- vperm_final_round_m1!("v2", "v4"),
- vperm_final_round_m1!("v3", "v4"),
- "vse8.v v0, ({b0})",
- "vse8.v v1, ({b1})",
- "vse8.v v2, ({b2})",
- "vse8.v v3, ({b3})",
- b0 = in(reg) blocks[0].as_mut_ptr(),
- b1 = in(reg) blocks[1].as_mut_ptr(),
- b2 = in(reg) blocks[2].as_mut_ptr(),
- b3 = in(reg) blocks[3].as_mut_ptr(),
- rk = in(reg) round_key.as_ptr(),
- tbl = in(reg) tables as *const VpermTables as *const u8,
- tmp = out(reg) _,
- options(nostack),
- );
- }
-}
-
-/// AES-256 full-block encryption (14 rounds) using Hamburg vperm.
-///
-/// # Safety
-/// Requires the RISC-V V extension.
-#[target_feature(enable = "v")]
-pub(super) unsafe fn encrypt_block(rk: &[u32; super::EXPANDED_KEY_WORDS], block: &mut [u8; 16]) {
- // SAFETY: caller guarantees the RISC-V V extension is available for the
- // full AES-256 block operation and all references are valid Rust buffers.
- unsafe {
- let tables = VpermTables::load();
-
- // Initial AddRoundKey (round 0).
- let rk0 = round_key_bytes(rk, 0);
- for i in 0..16 {
- block[i] ^= rk0[i];
- }
-
- // Rounds 1-13: full AES round (SubBytes + ShiftRows + MixColumns + AddRoundKey).
- let mut round = 1usize;
- while round < super::ROUNDS {
- let rk_r = round_key_bytes(rk, round);
- *block = aes_inner_round(block, &rk_r, &tables);
- round = round.strict_add(1);
- }
-
- // Round 14 (final): SubBytes + ShiftRows + AddRoundKey (no MixColumns).
- let rk14 = round_key_bytes(rk, super::ROUNDS);
- *block = aes_final_round(block, &rk14, &tables);
- }
-}
-
-/// Encrypt 4 independent AES-256 blocks using the vperm backend.
-///
-/// The 4-block batch is aimed at GCM-SIV key derivation, which produces 6
-/// unrelated ECB inputs up front. Processing 4 of them together amortizes
-/// table loads across the hot fixed-cost path while keeping the existing
-/// single-block kernel for small tails.
-#[target_feature(enable = "v")]
-/// # Safety
-///
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-pub(super) unsafe fn encrypt_4blocks(rk: &[u32; super::EXPANDED_KEY_WORDS], blocks: &mut [[u8; 16]; 4]) {
- // SAFETY: caller guarantees the RISC-V V extension is available for the
- // duration of the batch and all block buffers are valid 16-byte arrays.
- unsafe {
- let tables = VpermTables::load();
-
- let rk0 = round_key_bytes(rk, 0);
- for block in blocks.iter_mut() {
- for i in 0..16 {
- block[i] ^= rk0[i];
- }
- }
-
- let mut round = 1usize;
- while round < super::ROUNDS {
- let rk_r = round_key_bytes(rk, round);
- aes_inner_round_4(blocks, &rk_r, &tables);
- round = round.strict_add(1);
- }
-
- let rk14 = round_key_bytes(rk, super::ROUNDS);
- aes_final_round_4(blocks, &rk14, &tables);
- }
-}
-
-// AES-128 (10 rounds)
-
-/// AES-128 full-block encryption (10 rounds) using Hamburg vperm.
-///
-/// # Safety
-/// Requires the RISC-V V extension.
-#[target_feature(enable = "v")]
-pub(super) unsafe fn encrypt_block_128(rk: &[u32; super::EXPANDED_KEY_WORDS_128], block: &mut [u8; 16]) {
- // SAFETY: caller guarantees the RISC-V V extension is available for the
- // full AES-128 block operation and all references are valid Rust buffers.
- unsafe {
- let tables = VpermTables::load();
-
- // Initial AddRoundKey (round 0).
- let rk0 = round_key_bytes_128(rk, 0);
- for i in 0..16 {
- block[i] ^= rk0[i];
- }
-
- // Rounds 1-9: full AES round (SubBytes + ShiftRows + MixColumns + AddRoundKey).
- let mut round = 1usize;
- while round < super::ROUNDS_128 {
- let rk_r = round_key_bytes_128(rk, round);
- *block = aes_inner_round(block, &rk_r, &tables);
- round = round.strict_add(1);
- }
-
- // Round 10 (final): SubBytes + ShiftRows + AddRoundKey (no MixColumns).
- let rk10 = round_key_bytes_128(rk, super::ROUNDS_128);
- *block = aes_final_round(block, &rk10, &tables);
- }
-}
-
-/// Encrypt 4 independent AES-128 blocks using the vperm backend.
-///
-/// # Safety
-/// Caller must ensure the CPU supports the RISC-V `v` extension.
-#[target_feature(enable = "v")]
-pub(super) unsafe fn encrypt_4blocks_128(rk: &[u32; super::EXPANDED_KEY_WORDS_128], blocks: &mut [[u8; 16]; 4]) {
- // SAFETY: caller guarantees the RISC-V V extension is available for the
- // duration of the batch and all block buffers are valid 16-byte arrays.
- unsafe {
- let tables = VpermTables::load();
-
- let rk0 = round_key_bytes_128(rk, 0);
- for block in blocks.iter_mut() {
- for i in 0..16 {
- block[i] ^= rk0[i];
- }
- }
-
- let mut round = 1usize;
- while round < super::ROUNDS_128 {
- let rk_r = round_key_bytes_128(rk, round);
- aes_inner_round_4(blocks, &rk_r, &tables);
- round = round.strict_add(1);
- }
-
- let rk10 = round_key_bytes_128(rk, super::ROUNDS_128);
- aes_final_round_4(blocks, &rk10, &tables);
- }
-}
diff --git a/src/aead/aes/x86_64/asm.rs b/src/aead/aes/x86_64/asm.rs
index 9a7caef4..24a09024 100644
--- a/src/aead/aes/x86_64/asm.rs
+++ b/src/aead/aes/x86_64/asm.rs
@@ -1,13 +1,10 @@
//! Linux x86-64 rscrypto-owned AES-GCM assembly kernels.
-#![allow(unsafe_code)]
-
use core::arch::global_asm;
global_asm!(include_str!("asm/rscrypto_aes_gcm_x86_64_linux.s"));
#[repr(C)]
-#[allow(dead_code)]
pub(super) struct AesGcmX86State {
acc_lo: u64,
acc_hi: u64,
@@ -16,13 +13,12 @@ pub(super) struct AesGcmX86State {
pub(super) processed: usize,
}
-#[allow(dead_code)]
impl AesGcmX86State {
#[inline]
pub(super) fn new(acc: u128, ctr: u32) -> Self {
Self {
- acc_lo: acc as u64,
- acc_hi: (acc >> 64) as u64,
+ acc_lo: u64::try_from(acc & u128::from(u64::MAX)).expect("masked accumulator half fits u64"),
+ acc_hi: u64::try_from(acc >> 64).expect("shifted accumulator half fits u64"),
ctr,
_pad: 0,
processed: 0,
@@ -36,7 +32,6 @@ impl AesGcmX86State {
}
unsafe extern "C" {
- #[allow(dead_code)]
pub(super) fn rscrypto_aes128_gcm_seal_16x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -46,7 +41,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes128_gcm_open_16x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -56,7 +50,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes128_gcm_seal_64x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -66,7 +59,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes128_gcm_open_64x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -76,7 +68,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes128_gcm_seal_128x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -86,7 +77,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes128_gcm_open_128x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -96,7 +86,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes256_gcm_seal_16x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -106,7 +95,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes256_gcm_open_16x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -116,7 +104,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes256_gcm_seal_64x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -126,7 +113,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes256_gcm_open_64x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -136,7 +122,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes256_gcm_seal_128x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -146,7 +131,6 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
pub(super) fn rscrypto_aes256_gcm_open_128x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -156,47 +140,7 @@ unsafe extern "C" {
state: *mut AesGcmX86State,
);
- #[allow(dead_code)]
- pub(super) fn rscrypto_aes128_gcm_seal_8x_vaes256_x86_64_linux(
- round_keys: *const u8,
- initial_counter: *const u8,
- data: *mut u8,
- len: usize,
- h_powers_rev_8: *const u128,
- state: *mut AesGcmX86State,
- );
-
- #[allow(dead_code)]
- pub(super) fn rscrypto_aes128_gcm_open_8x_vaes256_x86_64_linux(
- round_keys: *const u8,
- initial_counter: *const u8,
- data: *mut u8,
- len: usize,
- h_powers_rev_8: *const u128,
- state: *mut AesGcmX86State,
- );
-
- #[allow(dead_code)]
- pub(super) fn rscrypto_aes256_gcm_seal_8x_vaes256_x86_64_linux(
- round_keys: *const u8,
- initial_counter: *const u8,
- data: *mut u8,
- len: usize,
- h_powers_rev_8: *const u128,
- state: *mut AesGcmX86State,
- );
-
- #[allow(dead_code)]
- pub(super) fn rscrypto_aes256_gcm_open_8x_vaes256_x86_64_linux(
- round_keys: *const u8,
- initial_counter: *const u8,
- data: *mut u8,
- len: usize,
- h_powers_rev_8: *const u128,
- state: *mut AesGcmX86State,
- );
-
- #[allow(dead_code)]
+ #[cfg(feature = "aes-gcm-siv")]
pub(super) fn rscrypto_aes128_gcmsiv_ctr_16x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
@@ -204,7 +148,7 @@ unsafe extern "C" {
len: usize,
) -> usize;
- #[allow(dead_code)]
+ #[cfg(feature = "aes-gcm-siv")]
pub(super) fn rscrypto_aes256_gcmsiv_ctr_16x_vaes512_x86_64_linux(
round_keys: *const u8,
initial_counter: *const u8,
diff --git a/src/aead/aes/x86_64/asm/rscrypto_aes_gcm_x86_64_linux.s b/src/aead/aes/x86_64/asm/rscrypto_aes_gcm_x86_64_linux.s
index a5fe46de..f5a7d1a7 100644
--- a/src/aead/aes/x86_64/asm/rscrypto_aes_gcm_x86_64_linux.s
+++ b/src/aead/aes/x86_64/asm/rscrypto_aes_gcm_x86_64_linux.s
@@ -79,53 +79,6 @@
vmovdqu32 zmm3{{k1}}, zmm25
.endm
-.macro GCM_COUNTERS_8_SLOW
- vpbroadcastd ymm25, r10d
-
- vbroadcasti32x4 ymm0, XMMWORD PTR [rsi]
- vmovdqa64 ymm1, ymm0
- vmovdqa64 ymm2, ymm0
- vmovdqa64 ymm3, ymm0
-
- vmovdqa32 ymm24, ymm25
- vpaddd ymm24, ymm24, YMMWORD PTR [rip + .Lrscrypto_x86_ctr_inc_y0]
- vpshufb ymm24, ymm24, ymm26
- vmovdqu32 ymm0{{k1}}, ymm24
-
- vmovdqa32 ymm24, ymm25
- vpaddd ymm24, ymm24, YMMWORD PTR [rip + .Lrscrypto_x86_ctr_inc_y1]
- vpshufb ymm24, ymm24, ymm26
- vmovdqu32 ymm1{{k1}}, ymm24
-
- vmovdqa32 ymm24, ymm25
- vpaddd ymm24, ymm24, YMMWORD PTR [rip + .Lrscrypto_x86_ctr_inc_y2]
- vpshufb ymm24, ymm24, ymm26
- vmovdqu32 ymm2{{k1}}, ymm24
-
- vpaddd ymm25, ymm25, YMMWORD PTR [rip + .Lrscrypto_x86_ctr_inc_y3]
- vpshufb ymm25, ymm25, ymm26
- vmovdqu32 ymm3{{k1}}, ymm25
-.endm
-
-.macro GCM_COUNTERS_8
- mov eax, r10d
- cmp al, 249
- jae 1f
- bswap eax
- vpbroadcastd ymm24, eax
-
- vbroadcasti32x4 ymm25, XMMWORD PTR [rsi]
- vmovdqu32 ymm25{{k1}}, ymm24
-
- vpaddd ymm0, ymm25, YMMWORD PTR [rip + .Lrscrypto_x86_ctr_incbe_y0]
- vpaddd ymm1, ymm25, YMMWORD PTR [rip + .Lrscrypto_x86_ctr_incbe_y1]
- vpaddd ymm2, ymm25, YMMWORD PTR [rip + .Lrscrypto_x86_ctr_incbe_y2]
- vpaddd ymm3, ymm25, YMMWORD PTR [rip + .Lrscrypto_x86_ctr_incbe_y3]
- jmp 2f
-1:
- GCM_COUNTERS_8_SLOW
-2:
-.endm
.macro AES_ROUND off
vbroadcasti32x4 zmm27, XMMWORD PTR [rdi + \off]
@@ -173,51 +126,6 @@
.endif
.endm
-.macro AES_Y_ROUND off
- vbroadcasti32x4 ymm27, XMMWORD PTR [rdi + \off]
- vaesenc ymm0, ymm0, ymm27
- vaesenc ymm1, ymm1, ymm27
- vaesenc ymm2, ymm2, ymm27
- vaesenc ymm3, ymm3, ymm27
-.endm
-
-.macro AES_Y_START
- vbroadcasti32x4 ymm27, XMMWORD PTR [rdi]
- vpxord ymm0, ymm0, ymm27
- vpxord ymm1, ymm1, ymm27
- vpxord ymm2, ymm2, ymm27
- vpxord ymm3, ymm3, ymm27
-.endm
-
-.macro AES_Y_LAST off
- vbroadcasti32x4 ymm27, XMMWORD PTR [rdi + \off]
- vaesenclast ymm0, ymm0, ymm27
- vaesenclast ymm1, ymm1, ymm27
- vaesenclast ymm2, ymm2, ymm27
- vaesenclast ymm3, ymm3, ymm27
-.endm
-
-.macro AES_ENCRYPT_8Y aes256
- AES_Y_START
- AES_Y_ROUND 16
- AES_Y_ROUND 32
- AES_Y_ROUND 48
- AES_Y_ROUND 64
- AES_Y_ROUND 80
- AES_Y_ROUND 96
- AES_Y_ROUND 112
- AES_Y_ROUND 128
- AES_Y_ROUND 144
- .if \aes256
- AES_Y_ROUND 160
- AES_Y_ROUND 176
- AES_Y_ROUND 192
- AES_Y_ROUND 208
- AES_Y_LAST 224
- .else
- AES_Y_LAST 160
- .endif
-.endm
.macro GHASH_FOLD data, h
vpclmulqdq zmm21, \data, \h, 0x00
@@ -263,22 +171,6 @@
vpxorq zmm29, zmm29, zmm22
.endm
-.macro GHASH_FOLD_Y data, h
- vpclmulqdq ymm21, \data, \h, 0x00
- vpclmulqdq ymm22, \data, \h, 0x11
-
- vpshufd ymm23, \data, 0x4e
- vpxord ymm23, ymm23, \data
- vpshufd ymm24, \h, 0x4e
- vpxord ymm24, ymm24, \h
- vpclmulqdq ymm23, ymm23, ymm24, 0x00
- vpternlogq ymm23, ymm21, ymm22, 0x96
-
- vpslldq ymm24, ymm23, 8
- vpternlogq ymm28, ymm21, ymm24, 0x96
- vpsrldq ymm24, ymm23, 8
- vpternlogq ymm29, ymm22, ymm24, 0x96
-.endm
.macro GHASH_PREP d0, d1, d2, d3
vpshufb \d0, \d0, zmm31
@@ -293,19 +185,6 @@
vpxord zmm29, zmm29, zmm29
.endm
-.macro GHASH_PREP_Y d0, d1, d2, d3
- vpshufb \d0, \d0, ymm31
- vpshufb \d1, \d1, ymm31
- vpshufb \d2, \d2, ymm31
- vpshufb \d3, \d3, ymm31
-
- vpxord ymm30, ymm30, ymm30
- vmovdqu64 xmm30, XMMWORD PTR [r9]
- vpxord \d0, \d0, ymm30
-
- vpxord ymm28, ymm28, ymm28
- vpxord ymm29, ymm29, ymm29
-.endm
.macro GHASH_REDUCE_ASSEMBLE
vpxorq zmm20, zmm20, zmm28
@@ -369,37 +248,6 @@
GHASH_REDUCE_STORE
.endm
-.macro GHASH_REDUCE_Y
- vmovdqa64 xmm16, xmm28
- vextracti64x2 xmm17, ymm28, 1
- vpxorq xmm16, xmm16, xmm17
-
- vmovdqa64 xmm19, xmm29
- vextracti64x2 xmm17, ymm29, 1
- vpxorq xmm19, xmm19, xmm17
-
- vpsllq xmm17, xmm16, 63
- vpsllq xmm18, xmm16, 62
- vpsllq xmm20, xmm16, 57
- vpternlogq xmm17, xmm18, xmm20, 0x96
- vpslldq xmm17, xmm17, 8
- vpxorq xmm16, xmm16, xmm17
-
- vpsrlq xmm17, xmm16, 1
- vpsrlq xmm18, xmm16, 2
- vpsrlq xmm20, xmm16, 7
- vpternlogq xmm17, xmm18, xmm20, 0x96
- vpxorq xmm17, xmm17, xmm16
-
- vpsllq xmm18, xmm16, 63
- vpsllq xmm20, xmm16, 62
- vpsllq xmm21, xmm16, 57
- vpternlogq xmm18, xmm20, xmm21, 0x96
- vpsrldq xmm18, xmm18, 8
-
- vpternlogq xmm17, xmm18, xmm19, 0x96
- vmovdqu64 XMMWORD PTR [r9], xmm17
-.endm
.macro GHASH16_REG d0, d1, d2, d3
GHASH16_REG_H \d0, \d1, \d2, \d3, zmm12, zmm13, zmm14, zmm15
@@ -607,14 +455,6 @@
vmovdqu64 zmm19, ZMMWORD PTR [r8 + 1984]
.endm
-.macro GHASH8Y_REG d0, d1, d2, d3
- GHASH_PREP_Y \d0, \d1, \d2, \d3
- GHASH_FOLD_Y \d0, ymm12
- GHASH_FOLD_Y \d1, ymm13
- GHASH_FOLD_Y \d2, ymm14
- GHASH_FOLD_Y \d3, ymm15
- GHASH_REDUCE_Y
-.endm
.macro AES_ENCRYPT_16_WITH_GHASH aes256, d0, d1, d2, d3
AES_START
@@ -896,34 +736,6 @@
.endif
.endm
-.macro AES_ENCRYPT_8Y_WITH_GHASH aes256, d0, d1, d2, d3
- AES_Y_START
- GHASH_PREP_Y \d0, \d1, \d2, \d3
- AES_Y_ROUND 16
- GHASH_FOLD_Y \d0, ymm12
- AES_Y_ROUND 32
- GHASH_FOLD_Y \d1, ymm13
- AES_Y_ROUND 48
- GHASH_FOLD_Y \d2, ymm14
- AES_Y_ROUND 64
- GHASH_FOLD_Y \d3, ymm15
- AES_Y_ROUND 80
- AES_Y_ROUND 96
- AES_Y_ROUND 112
- AES_Y_ROUND 128
- AES_Y_ROUND 144
- .if \aes256
- AES_Y_ROUND 160
- AES_Y_ROUND 176
- AES_Y_ROUND 192
- AES_Y_ROUND 208
- GHASH_REDUCE_Y
- AES_Y_LAST 224
- .else
- GHASH_REDUCE_Y
- AES_Y_LAST 160
- .endif
-.endm
.macro AES_GCM_STORE_Z open, d0, d1, d2, d3
.if \open
@@ -1573,114 +1385,6 @@
.size \name, . - \name
.endm
-.macro AES_GCM_8Y_FUNC name, aes256, open
- .p2align 5
- .globl \name
- .type \name, @function
-\name:
- mov r10d, DWORD PTR [r9 + 16]
-
- cmp rcx, 128
- jb .L\name\()_done
-
- vmovdqu64 ymm31, YMMWORD PTR [rip + .Lrscrypto_x86_gcm_bswap]
- vmovdqu64 ymm26, YMMWORD PTR [rip + .Lrscrypto_x86_dword_bswap]
- vmovdqu64 ymm12, YMMWORD PTR [r8]
- vmovdqu64 ymm13, YMMWORD PTR [r8 + 32]
- vmovdqu64 ymm14, YMMWORD PTR [r8 + 64]
- vmovdqu64 ymm15, YMMWORD PTR [r8 + 96]
- mov eax, 0x88
- kmovw k1, eax
-
- GCM_COUNTERS_8
-
- AES_ENCRYPT_8Y \aes256
-
- .if \open
- vmovdqu64 ymm4, YMMWORD PTR [rdx]
- vmovdqu64 ymm5, YMMWORD PTR [rdx + 32]
- vmovdqu64 ymm6, YMMWORD PTR [rdx + 64]
- vmovdqu64 ymm7, YMMWORD PTR [rdx + 96]
- vpxord ymm8, ymm0, ymm4
- vpxord ymm9, ymm1, ymm5
- vpxord ymm10, ymm2, ymm6
- vpxord ymm11, ymm3, ymm7
- vmovdqu64 YMMWORD PTR [rdx], ymm8
- vmovdqu64 YMMWORD PTR [rdx + 32], ymm9
- vmovdqu64 YMMWORD PTR [rdx + 64], ymm10
- vmovdqu64 YMMWORD PTR [rdx + 96], ymm11
- .else
- vmovdqu64 ymm4, YMMWORD PTR [rdx]
- vmovdqu64 ymm5, YMMWORD PTR [rdx + 32]
- vmovdqu64 ymm6, YMMWORD PTR [rdx + 64]
- vmovdqu64 ymm7, YMMWORD PTR [rdx + 96]
- vpxord ymm4, ymm4, ymm0
- vpxord ymm5, ymm5, ymm1
- vpxord ymm6, ymm6, ymm2
- vpxord ymm7, ymm7, ymm3
- vmovdqu64 YMMWORD PTR [rdx], ymm4
- vmovdqu64 YMMWORD PTR [rdx + 32], ymm5
- vmovdqu64 YMMWORD PTR [rdx + 64], ymm6
- vmovdqu64 YMMWORD PTR [rdx + 96], ymm7
- .endif
-
- add rdx, 128
- sub rcx, 128
- add r10d, 8
- add QWORD PTR [r9 + 24], 128
- cmp rcx, 128
- jb .L\name\()_final_ghash
-
- .p2align 5
-.L\name\()_loop:
- GCM_COUNTERS_8
-
- AES_ENCRYPT_8Y_WITH_GHASH \aes256, ymm4, ymm5, ymm6, ymm7
-
- .if \open
- vmovdqu64 ymm4, YMMWORD PTR [rdx]
- vmovdqu64 ymm5, YMMWORD PTR [rdx + 32]
- vmovdqu64 ymm6, YMMWORD PTR [rdx + 64]
- vmovdqu64 ymm7, YMMWORD PTR [rdx + 96]
- vpxord ymm8, ymm0, ymm4
- vpxord ymm9, ymm1, ymm5
- vpxord ymm10, ymm2, ymm6
- vpxord ymm11, ymm3, ymm7
- vmovdqu64 YMMWORD PTR [rdx], ymm8
- vmovdqu64 YMMWORD PTR [rdx + 32], ymm9
- vmovdqu64 YMMWORD PTR [rdx + 64], ymm10
- vmovdqu64 YMMWORD PTR [rdx + 96], ymm11
- .else
- vmovdqu64 ymm4, YMMWORD PTR [rdx]
- vmovdqu64 ymm5, YMMWORD PTR [rdx + 32]
- vmovdqu64 ymm6, YMMWORD PTR [rdx + 64]
- vmovdqu64 ymm7, YMMWORD PTR [rdx + 96]
- vpxord ymm4, ymm4, ymm0
- vpxord ymm5, ymm5, ymm1
- vpxord ymm6, ymm6, ymm2
- vpxord ymm7, ymm7, ymm3
- vmovdqu64 YMMWORD PTR [rdx], ymm4
- vmovdqu64 YMMWORD PTR [rdx + 32], ymm5
- vmovdqu64 YMMWORD PTR [rdx + 64], ymm6
- vmovdqu64 YMMWORD PTR [rdx + 96], ymm7
- .endif
-
- add rdx, 128
- sub rcx, 128
- add r10d, 8
- add QWORD PTR [r9 + 24], 128
- cmp rcx, 128
- jae .L\name\()_loop
-
-.L\name\()_final_ghash:
- GHASH8Y_REG ymm4, ymm5, ymm6, ymm7
-
-.L\name\()_done:
- mov DWORD PTR [r9 + 16], r10d
- vzeroupper
- ret
- .size \name, . - \name
-.endm
AES_GCM_16X_FUNC rscrypto_aes128_gcm_seal_16x_vaes512_x86_64_linux, 0, 0
AES_GCM_16X_FUNC rscrypto_aes128_gcm_open_16x_vaes512_x86_64_linux, 0, 1
@@ -1696,10 +1400,6 @@ AES_GCM_128X_FUNC rscrypto_aes256_gcm_seal_128x_vaes512_x86_64_linux, 1, 0
AES_GCM_128X_FUNC rscrypto_aes256_gcm_open_128x_vaes512_x86_64_linux, 1, 1
AES_GCMSIV_CTR_16X_FUNC rscrypto_aes128_gcmsiv_ctr_16x_vaes512_x86_64_linux, 0
AES_GCMSIV_CTR_16X_FUNC rscrypto_aes256_gcmsiv_ctr_16x_vaes512_x86_64_linux, 1
-AES_GCM_8Y_FUNC rscrypto_aes128_gcm_seal_8x_vaes256_x86_64_linux, 0, 0
-AES_GCM_8Y_FUNC rscrypto_aes128_gcm_open_8x_vaes256_x86_64_linux, 0, 1
-AES_GCM_8Y_FUNC rscrypto_aes256_gcm_seal_8x_vaes256_x86_64_linux, 1, 0
-AES_GCM_8Y_FUNC rscrypto_aes256_gcm_open_8x_vaes256_x86_64_linux, 1, 1
.section .rodata.cst64,"aM",@progbits,64
.p2align 6
@@ -1745,23 +1445,3 @@ AES_GCM_8Y_FUNC rscrypto_aes256_gcm_open_8x_vaes256_x86_64_linux, 1, 1
.long 8, 0, 0, 0, 9, 0, 0, 0, 10, 0, 0, 0, 11, 0, 0, 0
.Lrscrypto_x86_gcmsiv_ctr_inc_z3:
.long 12, 0, 0, 0, 13, 0, 0, 0, 14, 0, 0, 0, 15, 0, 0, 0
-
-.p2align 5
-.Lrscrypto_x86_ctr_inc_y0:
- .long 0, 0, 0, 0, 0, 0, 0, 1
-.Lrscrypto_x86_ctr_inc_y1:
- .long 0, 0, 0, 2, 0, 0, 0, 3
-.Lrscrypto_x86_ctr_inc_y2:
- .long 0, 0, 0, 4, 0, 0, 0, 5
-.Lrscrypto_x86_ctr_inc_y3:
- .long 0, 0, 0, 6, 0, 0, 0, 7
-
-.p2align 5
-.Lrscrypto_x86_ctr_incbe_y0:
- .long 0, 0, 0, 0x00000000, 0, 0, 0, 0x01000000
-.Lrscrypto_x86_ctr_incbe_y1:
- .long 0, 0, 0, 0x02000000, 0, 0, 0, 0x03000000
-.Lrscrypto_x86_ctr_incbe_y2:
- .long 0, 0, 0, 0x04000000, 0, 0, 0, 0x05000000
-.Lrscrypto_x86_ctr_incbe_y3:
- .long 0, 0, 0, 0x06000000, 0, 0, 0, 0x07000000
diff --git a/src/aead/aes/x86_64_ni.rs b/src/aead/aes/x86_64_ni.rs
index f441cc19..c9fbb60c 100644
--- a/src/aead/aes/x86_64_ni.rs
+++ b/src/aead/aes/x86_64_ni.rs
@@ -90,6 +90,7 @@ pub(super) unsafe fn expand_key(key: &[u8; 32]) -> NiRoundKeys {
///
/// # Safety
/// Caller must ensure AVX-512F + AVX-512VL + VAES + AES + SSE2.
+#[cfg(any(feature = "aes-gcm", feature = "aes-gcm-siv"))]
#[target_feature(enable = "aes,sse2,avx512f,avx512vl,vaes")]
#[inline]
pub(super) unsafe fn encrypt_4blocks(keys: &NiRoundKeys, blocks: __m512i) -> __m512i {
@@ -219,61 +220,6 @@ pub(super) unsafe fn encrypt_16blocks(
)
}
-/// Encrypt 8 AES-256 blocks as four independent VAES-256 dependency chains.
-///
-/// # Safety
-/// Caller must ensure AVX2 + AVX-512F + AVX-512VL + VAES + AES + SSE2.
-#[cfg(feature = "aes-gcm")]
-#[target_feature(enable = "aes,sse2,avx2,avx512f,avx512vl,vaes")]
-#[inline]
-#[allow(dead_code)]
-pub(super) unsafe fn encrypt_8blocks_y256(
- keys: &NiRoundKeys,
- b0: __m256i,
- b1: __m256i,
- b2: __m256i,
- b3: __m256i,
-) -> (__m256i, __m256i, __m256i, __m256i) {
- let k = &keys.rk;
- let rk0 = _mm256_broadcastsi128_si256(k[0]);
- let mut s0 = _mm256_xor_si256(b0, rk0);
- let mut s1 = _mm256_xor_si256(b1, rk0);
- let mut s2 = _mm256_xor_si256(b2, rk0);
- let mut s3 = _mm256_xor_si256(b3, rk0);
-
- macro_rules! round {
- ($idx:expr) => {{
- let rk = _mm256_broadcastsi128_si256(k[$idx]);
- s0 = _mm256_aesenc_epi128(s0, rk);
- s1 = _mm256_aesenc_epi128(s1, rk);
- s2 = _mm256_aesenc_epi128(s2, rk);
- s3 = _mm256_aesenc_epi128(s3, rk);
- }};
- }
-
- round!(1);
- round!(2);
- round!(3);
- round!(4);
- round!(5);
- round!(6);
- round!(7);
- round!(8);
- round!(9);
- round!(10);
- round!(11);
- round!(12);
- round!(13);
-
- let rk14 = _mm256_broadcastsi128_si256(k[14]);
- (
- _mm256_aesenclast_epi128(s0, rk14),
- _mm256_aesenclast_epi128(s1, rk14),
- _mm256_aesenclast_epi128(s2, rk14),
- _mm256_aesenclast_epi128(s3, rk14),
- )
-}
-
/// Encrypt a single 16-byte block using AES-256 with AES-NI.
///
/// # Safety
@@ -379,6 +325,7 @@ pub(super) unsafe fn expand_key_128(key: &[u8; 16]) -> Ni128RoundKeys {
///
/// # Safety
/// Caller must ensure AVX-512F + AVX-512VL + VAES + AES + SSE2.
+#[cfg(any(feature = "aes-gcm", feature = "aes-gcm-siv"))]
#[target_feature(enable = "aes,sse2,avx512f,avx512vl,vaes")]
#[inline]
pub(super) unsafe fn encrypt_4blocks_128(keys: &Ni128RoundKeys, blocks: __m512i) -> __m512i {
@@ -496,57 +443,6 @@ pub(super) unsafe fn encrypt_16blocks_128(
)
}
-/// Encrypt 8 AES-128 blocks as four independent VAES-256 dependency chains.
-///
-/// # Safety
-/// Caller must ensure AVX2 + AVX-512F + AVX-512VL + VAES + AES + SSE2.
-#[cfg(feature = "aes-gcm")]
-#[target_feature(enable = "aes,sse2,avx2,avx512f,avx512vl,vaes")]
-#[inline]
-#[allow(dead_code)]
-pub(super) unsafe fn encrypt_8blocks_128_y256(
- keys: &Ni128RoundKeys,
- b0: __m256i,
- b1: __m256i,
- b2: __m256i,
- b3: __m256i,
-) -> (__m256i, __m256i, __m256i, __m256i) {
- let k = &keys.rk;
- let rk0 = _mm256_broadcastsi128_si256(k[0]);
- let mut s0 = _mm256_xor_si256(b0, rk0);
- let mut s1 = _mm256_xor_si256(b1, rk0);
- let mut s2 = _mm256_xor_si256(b2, rk0);
- let mut s3 = _mm256_xor_si256(b3, rk0);
-
- macro_rules! round {
- ($idx:expr) => {{
- let rk = _mm256_broadcastsi128_si256(k[$idx]);
- s0 = _mm256_aesenc_epi128(s0, rk);
- s1 = _mm256_aesenc_epi128(s1, rk);
- s2 = _mm256_aesenc_epi128(s2, rk);
- s3 = _mm256_aesenc_epi128(s3, rk);
- }};
- }
-
- round!(1);
- round!(2);
- round!(3);
- round!(4);
- round!(5);
- round!(6);
- round!(7);
- round!(8);
- round!(9);
-
- let rk10 = _mm256_broadcastsi128_si256(k[10]);
- (
- _mm256_aesenclast_epi128(s0, rk10),
- _mm256_aesenclast_epi128(s1, rk10),
- _mm256_aesenclast_epi128(s2, rk10),
- _mm256_aesenclast_epi128(s3, rk10),
- )
-}
-
/// Encrypt a single 16-byte block using AES-128 with AES-NI.
///
/// # Safety
diff --git a/src/aead/aes128gcm.rs b/src/aead/aes128gcm.rs
index f3e860fb..a73cc202 100644
--- a/src/aead/aes128gcm.rs
+++ b/src/aead/aes128gcm.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::indexing_slicing)]
-
//! AES-128-GCM public AEAD surface (NIST SP 800-38D).
use core::fmt;
@@ -277,9 +275,9 @@ fn ghash_update_padded(mut acc: u128, h_polyval: u128, data: &[u8]) -> u128 {
#[inline]
fn ghash_update_padded_wide(mut acc: u128, h_polyval: u128, h_powers_rev: &[u128; 4], data: &[u8]) -> u128 {
let (full_blocks, remainder) = data.as_chunks::<16>();
- let mut chunks = full_blocks.chunks_exact(4);
+ let (chunks, trailing_blocks) = full_blocks.as_chunks::<4>();
- for chunk in &mut chunks {
+ for chunk in chunks {
let blocks = [
u128::from_be_bytes(chunk[0]),
u128::from_be_bytes(chunk[1]),
@@ -289,7 +287,7 @@ fn ghash_update_padded_wide(mut acc: u128, h_polyval: u128, h_powers_rev: &[u128
acc = polyval::accumulate_4blocks(acc, h_polyval, h_powers_rev, &blocks);
}
- for block in chunks.remainder() {
+ for block in trailing_blocks {
acc ^= u128::from_be_bytes(*block);
acc = polyval::clmul128_reduce(acc, h_polyval);
}
@@ -380,6 +378,7 @@ fn encrypt_j0_tag(ek: &aes::Aes128EncKey, j0: &[u8; 16], acc: u128) -> [u8; TAG_
}
#[cfg(feature = "diag")]
+/// Exercise AES-128 counter-mode encryption and fold the fixed diagnostic output to one block.
#[must_use]
pub fn diag_aes128gcm_ctr32_be(cipher: &Aes128Gcm, nonce: &Nonce96, plaintext: &[u8; 44]) -> [u8; 16] {
let (_, ctr_block) = make_j0_and_ctr(nonce);
@@ -389,6 +388,7 @@ pub fn diag_aes128gcm_ctr32_be(cipher: &Aes128Gcm, nonce: &Nonce96, plaintext: &
}
#[cfg(feature = "diag")]
+/// Return the AES-128-GCM GHASH accumulator for diagnostic backend comparison.
#[unsafe(no_mangle)]
#[inline(never)]
#[must_use]
@@ -412,6 +412,7 @@ pub fn diag_aes128gcm_ghash(cipher: &Aes128Gcm, aad: &[u8], ciphertext: &[u8]) -
}
#[cfg(feature = "diag")]
+/// Encrypt a diagnostic GHASH accumulator into the final AES-128-GCM tag.
#[must_use]
pub fn diag_aes128gcm_tag_aes(cipher: &Aes128Gcm, nonce: &Nonce96, acc: &[u8; 16]) -> [u8; 16] {
let (j0, _) = make_j0_and_ctr(nonce);
@@ -505,9 +506,9 @@ unsafe fn ghash_update_padded_wide_aarch64(
data: &[u8],
) -> u128 {
let (full_blocks, remainder) = data.as_chunks::<16>();
- let mut chunks = full_blocks.chunks_exact(4);
+ let (chunks, trailing_blocks) = full_blocks.as_chunks::<4>();
- for chunk in &mut chunks {
+ for chunk in chunks {
let blocks = [
u128::from_be_bytes(chunk[0]),
u128::from_be_bytes(chunk[1]),
@@ -520,7 +521,7 @@ unsafe fn ghash_update_padded_wide_aarch64(
acc = unsafe { polyval::aarch64_aggregate_4blocks_inline(acc, h_powers_rev, &blocks) };
}
- for block in chunks.remainder() {
+ for block in trailing_blocks {
acc ^= u128::from_be_bytes(*block);
// SAFETY: PMULL carryless multiply because:
// 1. This function's caller must guarantee AES-CE/PMULL availability.
@@ -549,9 +550,9 @@ unsafe fn ghash_update_padded_wide_aarch64(
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
unsafe fn ghash_update_padded_wide_ppc(mut acc: u128, h_polyval: u128, h_powers_rev: &[u128; 4], data: &[u8]) -> u128 {
let (full_blocks, remainder) = data.as_chunks::<16>();
- let mut chunks = full_blocks.chunks_exact(4);
+ let (chunks, trailing_blocks) = full_blocks.as_chunks::<4>();
- for chunk in &mut chunks {
+ for chunk in chunks {
let blocks = [
u128::from_be_bytes(chunk[0]),
u128::from_be_bytes(chunk[1]),
@@ -564,7 +565,7 @@ unsafe fn ghash_update_padded_wide_ppc(mut acc: u128, h_polyval: u128, h_powers_
acc = unsafe { polyval::ppc_aggregate_4blocks_inline(acc, h_powers_rev, &blocks) };
}
- for block in chunks.remainder() {
+ for block in trailing_blocks {
acc ^= u128::from_be_bytes(*block);
// SAFETY: POWER8 carryless multiply because:
// 1. This function's caller must guarantee POWER8 crypto availability.
@@ -775,10 +776,6 @@ impl Aead for Aes128Gcm {
// 2. `aad` is a valid byte slice; padding is handled inside the helper.
let mut acc =
GhashAccumulator(unsafe { ghash_update_padded_wide_aarch64(0, h_polyval, &self.h_powers_rev, aad) });
- // SAFETY: fused intrinsic AArch64 AES-GCM sealing because:
- // 1. Backend resolution selected an AES/PMULL backend only after runtime detection confirmed AES-CE
- // and PMULL.
- // 2. The helper encrypts `buffer` in place and folds the resulting ciphertext into GHASH.
let tables = aes::Aarch64GcmTables {
h_polyval,
h_powers_rev: &self.h_powers_rev,
@@ -929,10 +926,6 @@ impl Aead for Aes128Gcm {
// 2. `aad` is a valid byte slice; padding is handled inside the helper.
let mut acc =
GhashAccumulator(unsafe { ghash_update_padded_wide_aarch64(0, h_polyval, &self.h_powers_rev, aad) });
- // SAFETY: fused intrinsic AArch64 AES-GCM open because:
- // 1. Backend resolution selected an AES/PMULL backend only after runtime detection confirmed AES-CE
- // and PMULL.
- // 2. The helper GHASHes ciphertext bytes before decrypting each chunk in place.
let tables = aes::Aarch64GcmTables {
h_polyval,
h_powers_rev: &self.h_powers_rev,
@@ -1076,10 +1069,13 @@ impl Drop for Aes128Gcm {
#[cfg(test)]
mod tests {
- use alloc::{vec, vec::Vec};
+ use alloc::vec;
use super::*;
- use crate::aead::expert::AeadWithNonce;
+ use crate::aead::{
+ expert::AeadWithNonce,
+ test_vectors::{hex_vec, hex12, hex16},
+ };
// NIST SP 800-38D Test Case 1: AES-128-GCM, empty plaintext, empty AAD.
// Key: 00000000000000000000000000000000
@@ -1097,10 +1093,14 @@ mod tests {
let expected_tag = hex16("58e2fccefa7e3061367f1d57a4e7455a");
let mut buf = vec![];
- let tag = cipher.encrypt_in_place(&nonce, &[], &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &[], &mut buf)
+ .expect("NIST empty AES-128-GCM encryption must succeed");
assert_eq!(tag.0, expected_tag, "Tag mismatch on encrypt");
- cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &[], &mut buf, &tag)
+ .expect("NIST empty AES-128-GCM decryption must succeed");
}
// NIST SP 800-38D Test Case 2: AES-128-GCM, 16-byte zero plaintext, empty AAD.
@@ -1120,11 +1120,15 @@ mod tests {
let expected_tag = hex16("ab6e47d42cec13bdf53a67b21257bddf");
let mut buf = vec![0u8; 16];
- let tag = cipher.encrypt_in_place(&nonce, &[], &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &[], &mut buf)
+ .expect("NIST one-block AES-128-GCM encryption must succeed");
assert_eq!(buf, expected_ct, "Ciphertext mismatch");
assert_eq!(tag.0, expected_tag, "Tag mismatch");
- cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &[], &mut buf, &tag)
+ .expect("NIST one-block AES-128-GCM decryption must succeed");
assert_eq!(buf, vec![0u8; 16], "Plaintext mismatch after decrypt");
}
@@ -1150,11 +1154,15 @@ mod tests {
let cipher = Aes128Gcm::new(&key);
let mut buf = plaintext.clone();
- let tag = cipher.encrypt_in_place(&nonce, &[], &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &[], &mut buf)
+ .expect("NIST multi-block AES-128-GCM encryption must succeed");
assert_eq!(buf, expected_ct, "Ciphertext mismatch");
assert_eq!(tag.0, expected_tag, "Tag mismatch");
- cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &[], &mut buf, &tag)
+ .expect("NIST multi-block AES-128-GCM decryption must succeed");
assert_eq!(buf, plaintext, "Plaintext mismatch after decrypt");
}
@@ -1181,11 +1189,15 @@ mod tests {
let cipher = Aes128Gcm::new(&key);
let mut buf = plaintext.clone();
- let tag = cipher.encrypt_in_place(&nonce, &aad, &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &aad, &mut buf)
+ .expect("NIST AES-128-GCM encryption with AAD must succeed");
assert_eq!(buf, expected_ct, "Ciphertext mismatch");
assert_eq!(tag.0, expected_tag, "Tag mismatch");
- cipher.decrypt_in_place(&nonce, &aad, &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &aad, &mut buf, &tag)
+ .expect("NIST AES-128-GCM decryption with AAD must succeed");
assert_eq!(buf, plaintext, "Plaintext mismatch after decrypt");
}
@@ -1197,11 +1209,15 @@ mod tests {
let cipher = Aes128Gcm::new(&key);
let mut buf = vec![0u8; 16];
- let mut tag = cipher.encrypt_in_place(&nonce, &[], &mut buf).unwrap();
+ let mut tag = cipher
+ .encrypt_in_place(&nonce, &[], &mut buf)
+ .expect("AES-128-GCM test setup encryption must succeed");
tag.0[0] ^= 1;
- let result = cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
/// Decryption with wrong AAD should fail.
@@ -1210,14 +1226,17 @@ mod tests {
let key = Aes128GcmKey::from_bytes(hex16("feffe9928665731c6d6a8f9467308308"));
let nonce = Nonce96::from_bytes(hex12("cafebabefacedbaddecaf888"));
let aad = hex_vec("feedfacedeadbeeffeedfacedeadbeefabaddad2");
- let plaintext = hex_vec("d9313225f88406e5a55909c5aff5269a");
let cipher = Aes128Gcm::new(&key);
- let mut buf = plaintext.clone();
- let tag = cipher.encrypt_in_place(&nonce, &aad, &mut buf).unwrap();
+ let mut buf = hex_vec("d9313225f88406e5a55909c5aff5269a");
+ let tag = cipher
+ .encrypt_in_place(&nonce, &aad, &mut buf)
+ .expect("AES-128-GCM test setup encryption must succeed");
- let result = cipher.decrypt_in_place(&nonce, b"wrong aad", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&nonce, b"wrong aad", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
/// Ciphertext tampering should fail verification.
@@ -1228,11 +1247,15 @@ mod tests {
let cipher = Aes128Gcm::new(&key);
let mut buf = vec![0u8; 32];
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AES-128-GCM test setup encryption must succeed");
buf[0] ^= 1;
- let result = cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
/// Detached encrypt/decrypt round-trip.
@@ -1245,10 +1268,14 @@ mod tests {
let cipher = Aes128Gcm::new(&key);
let mut buf = plaintext.to_vec();
- let tag = cipher.encrypt_in_place(&nonce, aad, &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, aad, &mut buf)
+ .expect("AES-128-GCM detached encryption must succeed");
assert_ne!(&buf[..], &plaintext[..]);
- cipher.decrypt_in_place(&nonce, aad, &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, aad, &mut buf, &tag)
+ .expect("AES-128-GCM detached decryption must succeed");
assert_eq!(&buf[..], &plaintext[..]);
}
@@ -1262,20 +1289,34 @@ mod tests {
let cipher = Aes128Gcm::new(&key);
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, aad, plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, aad, plaintext, &mut out)
+ .expect("AES-128-GCM combined encryption must succeed");
let mut pt_out = vec![0u8; plaintext.len()];
- cipher.decrypt(&nonce, aad, &out, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, aad, &out, &mut pt_out)
+ .expect("AES-128-GCM combined decryption must succeed");
assert_eq!(&pt_out[..], &plaintext[..]);
}
/// `tag_from_slice` rejects wrong-length input.
#[test]
fn tag_from_slice_rejects_bad_length() {
- assert!(Aes128Gcm::tag_from_slice(&[0u8; 15]).is_err());
- assert!(Aes128Gcm::tag_from_slice(&[0u8; 17]).is_err());
- assert!(Aes128Gcm::tag_from_slice(&[0u8; 0]).is_err());
- assert!(Aes128Gcm::tag_from_slice(&[0u8; 16]).is_ok());
+ assert_eq!(
+ Aes128Gcm::tag_from_slice(&[0u8; 15]).expect_err("short AES-128-GCM tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aes128Gcm::tag_from_slice(&[0u8; 17]).expect_err("long AES-128-GCM tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aes128Gcm::tag_from_slice(&[]).expect_err("empty AES-128-GCM tag must be rejected"),
+ AeadBufferError::new()
+ );
+ let tag = Aes128Gcm::tag_from_slice(&[0u8; 16]).expect("16-byte AES-128-GCM tag must be accepted");
+ assert_eq!(tag.as_bytes(), &[0u8; 16]);
}
/// Decryption with wrong nonce must fail.
@@ -1286,11 +1327,15 @@ mod tests {
let cipher = Aes128Gcm::new(&key);
let mut buf = *b"hello gcm";
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AES-128-GCM test setup encryption must succeed");
let wrong_nonce = Nonce96::from_bytes([0x08u8; 12]);
- let result = cipher.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
/// On authentication failure, the output buffer must be zeroed.
@@ -1302,42 +1347,18 @@ mod tests {
let plaintext = *b"zero me on failure";
let mut buf = plaintext;
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AES-128-GCM test setup encryption must succeed");
let mut bad_tag = tag.to_bytes();
bad_tag[0] ^= 0xFF;
let bad_tag = Aes128GcmTag::from_bytes(bad_tag);
- let result = cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag),
+ Err(OpenError::verification())
+ );
assert!(buf.iter().all(|&b| b == 0), "buffer not zeroed on auth failure");
}
-
- // --- Hex helpers ---
-
- fn hex16(hex: &str) -> [u8; 16] {
- let mut out = [0u8; 16];
- for i in 0..16 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex12(hex: &str) -> [u8; 12] {
- let mut out = [0u8; 12];
- for i in 0..12 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex_vec(hex: &str) -> Vec {
- let mut out = Vec::with_capacity(hex.len() / 2);
- let mut i = 0;
- while i < hex.len() {
- out.push(u8::from_str_radix(&hex[i..i + 2], 16).unwrap());
- i += 2;
- }
- out
- }
}
diff --git a/src/aead/aes128gcmsiv.rs b/src/aead/aes128gcmsiv.rs
index 2d9cceb4..9796f20f 100644
--- a/src/aead/aes128gcmsiv.rs
+++ b/src/aead/aes128gcmsiv.rs
@@ -1,15 +1,18 @@
-#![allow(clippy::indexing_slicing)]
-
//! AES-128-GCM-SIV public AEAD surface (RFC 8452).
use core::fmt;
#[cfg(target_arch = "x86_64")]
use super::polyval::{accumulate_padded_x86, precompute_powers, precompute_powers_16};
-use super::{
- AeadBufferError, Nonce96, OpenError, SealError, aes, polyval,
- targets::{AeadBackend, AeadPrimitive, select_backend},
-};
+#[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+))]
+use super::targets::{AeadBackend, AeadPrimitive, select_backend};
+use super::{AeadBufferError, Nonce96, OpenError, SealError, aes, polyval};
use crate::traits::{Aead, ct};
const KEY_SIZE: usize = 16;
@@ -85,7 +88,13 @@ define_aead_tag_type!(
/// timing claims are configuration- and release-evidence-bound; see `ct.toml`.
pub struct Aes128GcmSiv {
master_ek: aes::Aes128EncKey,
- #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
+ #[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ ))]
backend: AeadBackend,
}
@@ -219,12 +228,14 @@ fn compute_tag(
}
#[cfg(feature = "diag")]
+/// Derive the per-nonce authentication and encryption keys for diagnostic comparison.
#[must_use]
pub fn diag_aes128gcmsiv_derive_keys(cipher: &Aes128GcmSiv, nonce: &Nonce96) -> ([u8; 16], [u8; 16]) {
derive_keys(&cipher.master_ek, nonce)
}
#[cfg(feature = "diag")]
+/// Return the AES-128-GCM-SIV POLYVAL digest before nonce and AES tag finalization.
#[must_use]
pub fn diag_aes128gcmsiv_polyval_digest(auth_key: &[u8; 16], aad: &[u8], plaintext: &[u8]) -> [u8; 16] {
let mut pv = polyval::Polyval::new(auth_key);
@@ -236,6 +247,7 @@ pub fn diag_aes128gcmsiv_polyval_digest(auth_key: &[u8; 16], aad: &[u8], plainte
}
#[cfg(feature = "diag")]
+/// Encrypt one diagnostic tag block with a raw AES-128 key.
#[must_use]
pub fn diag_aes128gcmsiv_raw_tag_aes(enc_key: &[u8; 16], block: &[u8; 16]) -> [u8; 16] {
let mut out = *block;
@@ -253,6 +265,7 @@ pub fn diag_aes128gcmsiv_raw_tag_aes(enc_key: &[u8; 16], block: &[u8; 16]) -> [u
}
#[cfg(feature = "diag")]
+/// Exercise AES-128 counter-mode encryption and fold the fixed diagnostic output to one block.
#[must_use]
pub fn diag_aes128gcmsiv_ctr32(enc_key: &[u8; 16], tag: &[u8; 16], plaintext: &[u8; 44]) -> [u8; 16] {
let mut counter_block = *tag;
@@ -404,7 +417,6 @@ fn expand_key_riscv_for_backend(key: &[u8; 16], backend: AeadBackend) -> aes::Ae
match backend {
AeadBackend::Riscv64VectorCrypto => aes::aes128_expand_key_riscv_vector(key),
AeadBackend::Riscv64ScalarCrypto => aes::aes128_expand_key_riscv_scalar(key),
- AeadBackend::Riscv64Vperm => aes::aes128_expand_key_riscv_vperm(key),
AeadBackend::Portable => aes::aes128_expand_key_riscv_ttable(key),
_ => aes::aes128_expand_key_riscv_ttable(key),
}
@@ -416,6 +428,13 @@ fn expand_message_key_riscv(enc_key: &[u8; 16], backend: AeadBackend) -> aes::Ae
expand_key_riscv_for_backend(enc_key, backend)
}
+#[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+))]
#[inline]
fn resolve_backend() -> AeadBackend {
select_backend(
@@ -431,7 +450,7 @@ fn riscv_polyval_backend(backend: AeadBackend) -> RiscvPolyvalBackend {
match backend {
AeadBackend::Riscv64VectorCrypto => RiscvPolyvalBackend::Vector,
AeadBackend::Riscv64ScalarCrypto => RiscvPolyvalBackend::Scalar,
- AeadBackend::Portable | AeadBackend::Riscv64Vperm => {
+ AeadBackend::Portable => {
let caps = crate::platform::caps();
if caps.has(crate::platform::caps::riscv::ZBC) || caps.has(crate::platform::caps::riscv::ZBKC) {
RiscvPolyvalBackend::Scalar
@@ -559,6 +578,12 @@ fn compute_tag_wide(
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "aes,neon")]
+/// Seal one message through the fused AArch64 AES-128-GCM-SIV path.
+///
+/// # Safety
+///
+/// The current CPU must support AArch64 AES, NEON, and PMULL. Callers must establish those
+/// capabilities through validated backend selection before entering this function.
unsafe fn encrypt_fused_aarch64(
auth_key: &mut [u8; 16],
enc_key_bytes: &mut [u8; 16],
@@ -705,6 +730,12 @@ unsafe fn encrypt_fused_aarch64(
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "aes,neon")]
+/// Open one message through the fused AArch64 AES-128-GCM-SIV path.
+///
+/// # Safety
+///
+/// The current CPU must support AArch64 AES, NEON, and PMULL. Callers must establish those
+/// capabilities through validated backend selection before entering this function.
unsafe fn decrypt_fused_aarch64(
auth_key: &mut [u8; 16],
enc_key_bytes: &mut [u8; 16],
@@ -857,6 +888,11 @@ unsafe fn decrypt_fused_aarch64(
// powerpc64 fused encrypt/decrypt (single #[target_feature] scope)
+/// Encrypt with the fused POWER8 AES-128-GCM-SIV backend.
+///
+/// # Safety
+///
+/// The executing CPU must support AltiVec, VSX, POWER8 vector, and POWER8 crypto.
#[cfg(target_arch = "powerpc64")]
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
unsafe fn encrypt_fused_ppc(
@@ -986,6 +1022,11 @@ unsafe fn encrypt_fused_ppc(
}
}
+/// Decrypt and authenticate with the fused POWER8 AES-128-GCM-SIV backend.
+///
+/// # Safety
+///
+/// The executing CPU must support AltiVec, VSX, POWER8 vector, and POWER8 crypto.
#[cfg(target_arch = "powerpc64")]
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
unsafe fn decrypt_fused_ppc(
@@ -1122,6 +1163,11 @@ unsafe fn decrypt_fused_ppc(
// s390x fused encrypt/decrypt
+/// XOR an AES-128 counter stream into `buffer` using s390x CPACF.
+///
+/// # Safety
+///
+/// The executing CPU must support MSA AES instructions.
#[cfg(target_arch = "s390x")]
unsafe fn s390x_ctr32_le_xor_raw(enc_key_bytes: &[u8; 16], counter_block: &mut [u8; 16], buffer: &mut [u8]) {
let mut ctr = u32::from_le_bytes([counter_block[0], counter_block[1], counter_block[2], counter_block[3]]);
@@ -1147,12 +1193,17 @@ unsafe fn s390x_ctr32_le_xor_raw(enc_key_bytes: &[u8; 16], counter_block: &mut [
let processed = aes::xor_keystream_tail(buffer, offset, &keystream, block_count);
offset = offset.strict_add(processed);
- ctr = ctr.wrapping_add(block_count as u32);
+ ctr = ctr.wrapping_add(u32::from(block_count.to_le_bytes()[0]));
}
counter_block[0..4].copy_from_slice(&ctr.to_le_bytes());
}
+/// Encrypt with the fused s390x AES-128-GCM-SIV backend.
+///
+/// # Safety
+///
+/// The executing CPU must support the vector facility and MSA AES instructions.
#[cfg(target_arch = "s390x")]
#[target_feature(enable = "vector")]
unsafe fn encrypt_fused_s390x(
@@ -1257,6 +1308,11 @@ unsafe fn encrypt_fused_s390x(
}
}
+/// Decrypt and authenticate with the fused s390x AES-128-GCM-SIV backend.
+///
+/// # Safety
+///
+/// The executing CPU must support the vector facility and MSA AES instructions.
#[cfg(target_arch = "s390x")]
#[target_feature(enable = "vector")]
unsafe fn decrypt_fused_s390x(
@@ -1376,6 +1432,13 @@ impl Aead for Aes128GcmSiv {
type Tag = Aes128GcmSivTag;
fn new(key: &Self::Key) -> Self {
+ #[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ ))]
let backend = resolve_backend();
Self {
@@ -1383,6 +1446,13 @@ impl Aead for Aes128GcmSiv {
master_ek: expand_key_riscv_for_backend(key.as_bytes(), backend),
#[cfg(not(target_arch = "riscv64"))]
master_ek: aes::aes128_expand_key(key.as_bytes()),
+ #[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ ))]
backend,
}
}
@@ -1460,10 +1530,7 @@ impl Aead for Aes128GcmSiv {
#[cfg(target_arch = "riscv64")]
{
match self.backend {
- AeadBackend::Portable
- | AeadBackend::Riscv64VectorCrypto
- | AeadBackend::Riscv64ScalarCrypto
- | AeadBackend::Riscv64Vperm => {
+ AeadBackend::Portable | AeadBackend::Riscv64VectorCrypto | AeadBackend::Riscv64ScalarCrypto => {
let tag_bytes = encrypt_riscv(&self.master_ek, self.backend, nonce, aad, buffer);
return Ok(Aes128GcmSivTag::from_bytes(tag_bytes));
}
@@ -1556,10 +1623,7 @@ impl Aead for Aes128GcmSiv {
#[cfg(target_arch = "riscv64")]
{
match self.backend {
- AeadBackend::Portable
- | AeadBackend::Riscv64VectorCrypto
- | AeadBackend::Riscv64ScalarCrypto
- | AeadBackend::Riscv64Vperm => {
+ AeadBackend::Portable | AeadBackend::Riscv64VectorCrypto | AeadBackend::Riscv64ScalarCrypto => {
return decrypt_riscv(&self.master_ek, self.backend, nonce, aad, buffer, tag).map_err(OpenError::from);
}
_ => {}
@@ -1590,10 +1654,13 @@ impl Aead for Aes128GcmSiv {
#[cfg(test)]
mod tests {
- use alloc::{vec, vec::Vec};
+ use alloc::vec;
use super::*;
- use crate::aead::expert::AeadWithNonce;
+ use crate::aead::{
+ expert::AeadWithNonce,
+ test_vectors::{hex_vec, hex12, hex16},
+ };
/// RFC 8452 Appendix C.1, test 1: empty plaintext, empty AAD.
#[test]
@@ -1604,11 +1671,15 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut out = vec![0u8; expected_ct_tag.len()];
- cipher.encrypt(&nonce, &[], &[], &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &[], &[], &mut out)
+ .expect("RFC 8452 empty AES-128-GCM-SIV encryption must succeed");
assert_eq!(out, expected_ct_tag);
let mut pt_out = vec![0u8; 0];
- cipher.decrypt(&nonce, &[], &expected_ct_tag, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, &[], &expected_ct_tag, &mut pt_out)
+ .expect("RFC 8452 empty AES-128-GCM-SIV decryption must succeed");
assert!(pt_out.is_empty());
}
@@ -1622,11 +1693,15 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, &[], &plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &[], &plaintext, &mut out)
+ .expect("RFC 8452 AES-128-GCM-SIV encryption must succeed");
assert_eq!(out, expected_ct_tag);
let mut pt_out = vec![0u8; plaintext.len()];
- cipher.decrypt(&nonce, &[], &expected_ct_tag, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, &[], &expected_ct_tag, &mut pt_out)
+ .expect("RFC 8452 AES-128-GCM-SIV decryption must succeed");
assert_eq!(pt_out, plaintext);
}
@@ -1642,11 +1717,15 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, &aad, &plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &aad, &plaintext, &mut out)
+ .expect("RFC 8452 AES-128-GCM-SIV encryption with AAD must succeed");
assert_eq!(out, expected_ct_tag);
let mut pt_out = vec![0u8; plaintext.len()];
- cipher.decrypt(&nonce, &aad, &expected_ct_tag, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, &aad, &expected_ct_tag, &mut pt_out)
+ .expect("RFC 8452 AES-128-GCM-SIV decryption with AAD must succeed");
assert_eq!(pt_out, plaintext);
}
@@ -1659,22 +1738,16 @@ mod tests {
let plaintext = hex_vec(
"0200000000000000000000000000000003000000000000000000000000000000040000000000000000000000000000000500000000000000000000000000000000",
);
- let expected_ct_tag = hex_vec(
- "2f5c64059db55ee0fb847ed51300374651a8c75e07ecbef82c1ed1bf48bb1ff5c1cdf0a4a3aa6a17ce7022eafd1129b25aafdca1fa9706ce0d1e23df1ce04c4a8e10cb44ed62b9a3a39d61b8b3eba23586eb6df746a45dd8d6ce80e7f5d33d39",
- );
-
- // The expected vector above is illustrative; oracle tests at
- // tests/aes128gcmsiv_oracle.rs are the authoritative byte-equivalence
- // check. This unit test fixes the 4-block + AAD shape against the
- // round-trip rather than a hand-transcribed vector.
- let _ = expected_ct_tag;
-
let cipher = Aes128GcmSiv::new(&key);
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, &aad, &plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &aad, &plaintext, &mut out)
+ .expect("multi-block AES-128-GCM-SIV encryption must succeed");
let mut pt_out = vec![0u8; plaintext.len()];
- cipher.decrypt(&nonce, &aad, &out, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, &aad, &out, &mut pt_out)
+ .expect("multi-block AES-128-GCM-SIV decryption must succeed");
assert_eq!(pt_out, plaintext);
}
@@ -1688,8 +1761,10 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut pt_out = vec![0u8; 0];
- let result = cipher.decrypt(&nonce, &[], &bad_ct_tag, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&nonce, &[], &bad_ct_tag, &mut pt_out),
+ Err(OpenError::verification())
+ );
}
/// Decryption with wrong AAD should fail.
@@ -1701,8 +1776,10 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut pt_out = vec![0u8; 8];
- let result = cipher.decrypt(&nonce, &[0x02], &ct_tag, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&nonce, &[0x02], &ct_tag, &mut pt_out),
+ Err(OpenError::verification())
+ );
}
/// Decryption with wrong nonce should fail.
@@ -1715,8 +1792,10 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut pt_out = vec![0u8; 8];
let wrong_nonce = Nonce96::from_bytes(hex12("040000000000000000000000"));
- let result = cipher.decrypt(&wrong_nonce, &aad, &ct_tag, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&wrong_nonce, &aad, &ct_tag, &mut pt_out),
+ Err(OpenError::verification())
+ );
}
/// Ciphertext tampering should fail verification.
@@ -1732,8 +1811,10 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut pt_out = vec![0u8; plaintext.len()];
- let result = cipher.decrypt(&nonce, &aad, &ct_tag, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&nonce, &aad, &ct_tag, &mut pt_out),
+ Err(OpenError::verification())
+ );
}
/// On authentication failure, the output buffer must be zeroed.
@@ -1746,14 +1827,18 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, &aad, &plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &aad, &plaintext, &mut out)
+ .expect("AES-128-GCM-SIV test setup encryption must succeed");
let last = out.len().strict_sub(1);
out[last] ^= 0xff;
let mut pt_out = vec![0xffu8; plaintext.len()];
- let result = cipher.decrypt(&nonce, &aad, &out, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&nonce, &aad, &out, &mut pt_out),
+ Err(OpenError::verification())
+ );
assert!(pt_out.iter().all(|&b| b == 0), "buffer not zeroed on auth failure");
}
@@ -1768,57 +1853,51 @@ mod tests {
let cipher = Aes128GcmSiv::new(&key);
let mut buf = plaintext.clone();
- let tag = cipher.encrypt_in_place(&nonce, &aad, &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &aad, &mut buf)
+ .expect("AES-128-GCM-SIV detached encryption must succeed");
assert_ne!(buf, plaintext);
- cipher.decrypt_in_place(&nonce, &aad, &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &aad, &mut buf, &tag)
+ .expect("AES-128-GCM-SIV detached decryption must succeed");
assert_eq!(buf, plaintext);
}
/// `tag_from_slice` rejects wrong-length input.
#[test]
fn aes128gcmsiv_tag_from_slice_rejects_bad_length() {
- assert!(Aes128GcmSiv::tag_from_slice(&[0u8; 15]).is_err());
- assert!(Aes128GcmSiv::tag_from_slice(&[0u8; 17]).is_err());
- assert!(Aes128GcmSiv::tag_from_slice(&[0u8; 0]).is_err());
- assert!(Aes128GcmSiv::tag_from_slice(&[0u8; 16]).is_ok());
+ assert_eq!(
+ Aes128GcmSiv::tag_from_slice(&[0u8; 15]).expect_err("short AES-128-GCM-SIV tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aes128GcmSiv::tag_from_slice(&[0u8; 17]).expect_err("long AES-128-GCM-SIV tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aes128GcmSiv::tag_from_slice(&[]).expect_err("empty AES-128-GCM-SIV tag must be rejected"),
+ AeadBufferError::new()
+ );
+ let tag = Aes128GcmSiv::tag_from_slice(&[0u8; 16]).expect("16-byte AES-128-GCM-SIV tag must be accepted");
+ assert_eq!(tag.as_bytes(), &[0u8; 16]);
}
#[test]
#[cfg(target_pointer_width = "64")]
fn aes128gcmsiv_input_limit_matches_rfc8452() {
for len in [MAX_INPUT_LEN.strict_sub(1), MAX_INPUT_LEN] {
- assert!(super::super::try_bounded_length_as_u64(len as usize, MAX_INPUT_LEN).is_ok());
- }
- assert!(super::super::try_bounded_length_as_u64(MAX_INPUT_LEN.strict_add(1) as usize, MAX_INPUT_LEN).is_err());
- }
-
- // --- Hex helpers ---
-
- fn hex16(hex: &str) -> [u8; 16] {
- let mut out = [0u8; 16];
- for i in 0..16 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex12(hex: &str) -> [u8; 12] {
- let mut out = [0u8; 12];
- for i in 0..12 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex_vec(hex: &str) -> Vec {
- let mut out = Vec::with_capacity(hex.len() / 2);
- let mut i = 0;
- while i < hex.len() {
- out.push(u8::from_str_radix(&hex[i..i + 2], 16).unwrap());
- i += 2;
- }
- out
+ let platform_len = usize::try_from(len).expect("RFC 8452 input limit fits 64-bit usize");
+ assert_eq!(
+ super::super::try_bounded_length_as_u64(platform_len, MAX_INPUT_LEN),
+ Ok(len)
+ );
+ }
+ let too_large = usize::try_from(MAX_INPUT_LEN.strict_add(1)).expect("RFC 8452 input limit fits 64-bit usize");
+ assert_eq!(
+ super::super::try_bounded_length_as_u64(too_large, MAX_INPUT_LEN),
+ Err(super::super::LengthOverflow)
+ );
}
}
diff --git a/src/aead/aes256gcm.rs b/src/aead/aes256gcm.rs
index 2c806bc6..ba27f516 100644
--- a/src/aead/aes256gcm.rs
+++ b/src/aead/aes256gcm.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::indexing_slicing)]
-
//! AES-256-GCM public AEAD surface (NIST SP 800-38D).
use core::fmt;
@@ -275,9 +273,9 @@ fn ghash_update_padded(mut acc: u128, h_polyval: u128, data: &[u8]) -> u128 {
#[inline]
fn ghash_update_padded_wide(mut acc: u128, h_polyval: u128, h_powers_rev: &[u128; 4], data: &[u8]) -> u128 {
let (full_blocks, remainder) = data.as_chunks::<16>();
- let mut chunks = full_blocks.chunks_exact(4);
+ let (chunks, trailing_blocks) = full_blocks.as_chunks::<4>();
- for chunk in &mut chunks {
+ for chunk in chunks {
let blocks = [
u128::from_be_bytes(chunk[0]),
u128::from_be_bytes(chunk[1]),
@@ -287,7 +285,7 @@ fn ghash_update_padded_wide(mut acc: u128, h_polyval: u128, h_powers_rev: &[u128
acc = polyval::accumulate_4blocks(acc, h_polyval, h_powers_rev, &blocks);
}
- for block in chunks.remainder() {
+ for block in trailing_blocks {
acc ^= u128::from_be_bytes(*block);
acc = polyval::clmul128_reduce(acc, h_polyval);
}
@@ -378,6 +376,7 @@ fn encrypt_j0_tag(ek: &aes::Aes256EncKey, j0: &[u8; 16], acc: u128) -> [u8; TAG_
}
#[cfg(feature = "diag")]
+/// Exercise AES-256 counter-mode encryption and fold the fixed diagnostic output to one block.
#[must_use]
pub fn diag_aes256gcm_ctr32_be(cipher: &Aes256Gcm, nonce: &Nonce96, plaintext: &[u8; 44]) -> [u8; 16] {
let (_, ctr_block) = make_j0_and_ctr(nonce);
@@ -387,6 +386,7 @@ pub fn diag_aes256gcm_ctr32_be(cipher: &Aes256Gcm, nonce: &Nonce96, plaintext: &
}
#[cfg(feature = "diag")]
+/// Return the AES-256-GCM GHASH accumulator for diagnostic backend comparison.
#[unsafe(no_mangle)]
#[inline(never)]
#[must_use]
@@ -410,6 +410,7 @@ pub fn diag_aes256gcm_ghash(cipher: &Aes256Gcm, aad: &[u8], ciphertext: &[u8]) -
}
#[cfg(feature = "diag")]
+/// Encrypt a diagnostic GHASH accumulator into the final AES-256-GCM tag.
#[must_use]
pub fn diag_aes256gcm_tag_aes(cipher: &Aes256Gcm, nonce: &Nonce96, acc: &[u8; 16]) -> [u8; 16] {
let (j0, _) = make_j0_and_ctr(nonce);
@@ -506,9 +507,9 @@ unsafe fn ghash_update_padded_wide_aarch64(
data: &[u8],
) -> u128 {
let (full_blocks, remainder) = data.as_chunks::<16>();
- let mut chunks = full_blocks.chunks_exact(4);
+ let (chunks, trailing_blocks) = full_blocks.as_chunks::<4>();
- for chunk in &mut chunks {
+ for chunk in chunks {
let blocks = [
u128::from_be_bytes(chunk[0]),
u128::from_be_bytes(chunk[1]),
@@ -521,7 +522,7 @@ unsafe fn ghash_update_padded_wide_aarch64(
acc = unsafe { polyval::aarch64_aggregate_4blocks_inline(acc, h_powers_rev, &blocks) };
}
- for block in chunks.remainder() {
+ for block in trailing_blocks {
acc ^= u128::from_be_bytes(*block);
// SAFETY: PMULL carryless multiply because:
// 1. This function's caller must guarantee AES-CE/PMULL availability.
@@ -550,9 +551,9 @@ unsafe fn ghash_update_padded_wide_aarch64(
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
unsafe fn ghash_update_padded_wide_ppc(mut acc: u128, h_polyval: u128, h_powers_rev: &[u128; 4], data: &[u8]) -> u128 {
let (full_blocks, remainder) = data.as_chunks::<16>();
- let mut chunks = full_blocks.chunks_exact(4);
+ let (chunks, trailing_blocks) = full_blocks.as_chunks::<4>();
- for chunk in &mut chunks {
+ for chunk in chunks {
let blocks = [
u128::from_be_bytes(chunk[0]),
u128::from_be_bytes(chunk[1]),
@@ -565,7 +566,7 @@ unsafe fn ghash_update_padded_wide_ppc(mut acc: u128, h_polyval: u128, h_powers_
acc = unsafe { polyval::ppc_aggregate_4blocks_inline(acc, h_powers_rev, &blocks) };
}
- for block in chunks.remainder() {
+ for block in trailing_blocks {
acc ^= u128::from_be_bytes(*block);
// SAFETY: POWER8 carryless multiply because:
// 1. This function's caller must guarantee POWER8 crypto availability.
@@ -778,10 +779,6 @@ impl Aead for Aes256Gcm {
// 2. `aad` is a valid byte slice; padding is handled inside the helper.
let mut acc =
GhashAccumulator(unsafe { ghash_update_padded_wide_aarch64(0, h_polyval, &self.h_powers_rev, aad) });
- // SAFETY: fused intrinsic AArch64 AES-GCM sealing because:
- // 1. Backend resolution selected an AES/PMULL backend only after runtime detection confirmed AES-CE
- // and PMULL.
- // 2. The helper encrypts `buffer` in place and folds the resulting ciphertext into GHASH.
let tables = aes::Aarch64GcmTables {
h_polyval,
h_powers_rev: &self.h_powers_rev,
@@ -932,10 +929,6 @@ impl Aead for Aes256Gcm {
// 2. `aad` is a valid byte slice; padding is handled inside the helper.
let mut acc =
GhashAccumulator(unsafe { ghash_update_padded_wide_aarch64(0, h_polyval, &self.h_powers_rev, aad) });
- // SAFETY: fused intrinsic AArch64 AES-GCM open because:
- // 1. Backend resolution selected an AES/PMULL backend only after runtime detection confirmed AES-CE
- // and PMULL.
- // 2. The helper GHASHes ciphertext bytes before decrypting each chunk in place.
let tables = aes::Aarch64GcmTables {
h_polyval,
h_powers_rev: &self.h_powers_rev,
@@ -1079,10 +1072,13 @@ impl Drop for Aes256Gcm {
#[cfg(test)]
mod tests {
- use alloc::{vec, vec::Vec};
+ use alloc::vec;
use super::*;
- use crate::aead::expert::AeadWithNonce;
+ use crate::aead::{
+ expert::AeadWithNonce,
+ test_vectors::{hex_vec, hex12, hex16, hex32},
+ };
// NIST SP 800-38D Test Case 13: AES-256-GCM, empty plaintext, empty AAD.
// Key: 0000...00 (32 bytes)
@@ -1101,11 +1097,15 @@ mod tests {
// Encrypt.
let mut buf = vec![];
- let tag = cipher.encrypt_in_place(&nonce, &[], &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &[], &mut buf)
+ .expect("NIST empty AES-256-GCM encryption must succeed");
assert_eq!(tag.0, expected_tag, "Tag mismatch on encrypt");
// Decrypt.
- cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &[], &mut buf, &tag)
+ .expect("NIST empty AES-256-GCM decryption must succeed");
}
// NIST SP 800-38D Test Case 14: AES-256-GCM, 16-byte plaintext, empty AAD.
@@ -1126,12 +1126,16 @@ mod tests {
// Encrypt.
let mut buf = vec![0u8; 16];
- let tag = cipher.encrypt_in_place(&nonce, &[], &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &[], &mut buf)
+ .expect("NIST one-block AES-256-GCM encryption must succeed");
assert_eq!(buf, expected_ct, "Ciphertext mismatch");
assert_eq!(tag.0, expected_tag, "Tag mismatch");
// Decrypt.
- cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &[], &mut buf, &tag)
+ .expect("NIST one-block AES-256-GCM decryption must succeed");
assert_eq!(buf, vec![0u8; 16], "Plaintext mismatch after decrypt");
}
@@ -1160,12 +1164,16 @@ mod tests {
// Encrypt.
let mut buf = plaintext.clone();
- let tag = cipher.encrypt_in_place(&nonce, &[], &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &[], &mut buf)
+ .expect("NIST multi-block AES-256-GCM encryption must succeed");
assert_eq!(buf, expected_ct, "Ciphertext mismatch");
assert_eq!(tag.0, expected_tag, "Tag mismatch");
// Decrypt.
- cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &[], &mut buf, &tag)
+ .expect("NIST multi-block AES-256-GCM decryption must succeed");
assert_eq!(buf, plaintext, "Plaintext mismatch after decrypt");
}
@@ -1195,12 +1203,16 @@ mod tests {
// Encrypt.
let mut buf = plaintext.clone();
- let tag = cipher.encrypt_in_place(&nonce, &aad, &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &aad, &mut buf)
+ .expect("NIST AES-256-GCM encryption with AAD must succeed");
assert_eq!(buf, expected_ct, "Ciphertext mismatch");
assert_eq!(tag.0, expected_tag, "Tag mismatch");
// Decrypt.
- cipher.decrypt_in_place(&nonce, &aad, &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &aad, &mut buf, &tag)
+ .expect("NIST AES-256-GCM decryption with AAD must succeed");
assert_eq!(buf, plaintext, "Plaintext mismatch after decrypt");
}
@@ -1212,11 +1224,15 @@ mod tests {
let cipher = Aes256Gcm::new(&key);
let mut buf = vec![0u8; 16];
- let mut tag = cipher.encrypt_in_place(&nonce, &[], &mut buf).unwrap();
+ let mut tag = cipher
+ .encrypt_in_place(&nonce, &[], &mut buf)
+ .expect("AES-256-GCM test setup encryption must succeed");
tag.0[0] ^= 1;
- let result = cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&nonce, &[], &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
/// Decryption with wrong AAD should fail.
@@ -1227,15 +1243,18 @@ mod tests {
));
let nonce = Nonce96::from_bytes(hex12("cafebabefacedbaddecaf888"));
let aad = hex_vec("feedfacedeadbeeffeedfacedeadbeefabaddad2");
- let plaintext = hex_vec("d9313225f88406e5a55909c5aff5269a");
let cipher = Aes256Gcm::new(&key);
- let mut buf = plaintext.clone();
- let tag = cipher.encrypt_in_place(&nonce, &aad, &mut buf).unwrap();
+ let mut buf = hex_vec("d9313225f88406e5a55909c5aff5269a");
+ let tag = cipher
+ .encrypt_in_place(&nonce, &aad, &mut buf)
+ .expect("AES-256-GCM test setup encryption must succeed");
// Wrong AAD.
- let result = cipher.decrypt_in_place(&nonce, b"wrong aad", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&nonce, b"wrong aad", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
/// Ciphertext tampering should fail verification.
@@ -1246,11 +1265,15 @@ mod tests {
let cipher = Aes256Gcm::new(&key);
let mut buf = vec![0u8; 32];
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AES-256-GCM test setup encryption must succeed");
buf[0] ^= 1;
- let result = cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
/// Detached encrypt/decrypt round-trip.
@@ -1263,10 +1286,14 @@ mod tests {
let cipher = Aes256Gcm::new(&key);
let mut buf = plaintext.to_vec();
- let tag = cipher.encrypt_in_place(&nonce, aad, &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, aad, &mut buf)
+ .expect("AES-256-GCM detached encryption must succeed");
assert_ne!(&buf[..], &plaintext[..]);
- cipher.decrypt_in_place(&nonce, aad, &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, aad, &mut buf, &tag)
+ .expect("AES-256-GCM detached decryption must succeed");
assert_eq!(&buf[..], &plaintext[..]);
}
@@ -1280,56 +1307,34 @@ mod tests {
let cipher = Aes256Gcm::new(&key);
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, aad, plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, aad, plaintext, &mut out)
+ .expect("AES-256-GCM combined encryption must succeed");
let mut pt_out = vec![0u8; plaintext.len()];
- cipher.decrypt(&nonce, aad, &out, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, aad, &out, &mut pt_out)
+ .expect("AES-256-GCM combined decryption must succeed");
assert_eq!(&pt_out[..], &plaintext[..]);
}
/// `tag_from_slice` rejects wrong-length input.
#[test]
fn tag_from_slice_rejects_bad_length() {
- assert!(Aes256Gcm::tag_from_slice(&[0u8; 15]).is_err());
- assert!(Aes256Gcm::tag_from_slice(&[0u8; 17]).is_err());
- assert!(Aes256Gcm::tag_from_slice(&[0u8; 0]).is_err());
- assert!(Aes256Gcm::tag_from_slice(&[0u8; 16]).is_ok());
- }
-
- // --- Hex helpers ---
-
- fn hex16(hex: &str) -> [u8; 16] {
- let mut out = [0u8; 16];
- for i in 0..16 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex32(hex: &str) -> [u8; 32] {
- let mut out = [0u8; 32];
- for i in 0..32 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex12(hex: &str) -> [u8; 12] {
- let mut out = [0u8; 12];
- for i in 0..12 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex_vec(hex: &str) -> Vec {
- let mut out = Vec::with_capacity(hex.len() / 2);
- let mut i = 0;
- while i < hex.len() {
- out.push(u8::from_str_radix(&hex[i..i + 2], 16).unwrap());
- i += 2;
- }
- out
+ assert_eq!(
+ Aes256Gcm::tag_from_slice(&[0u8; 15]).expect_err("short AES-256-GCM tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aes256Gcm::tag_from_slice(&[0u8; 17]).expect_err("long AES-256-GCM tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aes256Gcm::tag_from_slice(&[]).expect_err("empty AES-256-GCM tag must be rejected"),
+ AeadBufferError::new()
+ );
+ let tag = Aes256Gcm::tag_from_slice(&[0u8; 16]).expect("16-byte AES-256-GCM tag must be accepted");
+ assert_eq!(tag.as_bytes(), &[0u8; 16]);
}
/// Decryption with wrong nonce must fail.
@@ -1340,11 +1345,15 @@ mod tests {
let cipher = Aes256Gcm::new(&key);
let mut buf = *b"hello gcm";
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AES-256-GCM test setup encryption must succeed");
let wrong_nonce = Nonce96::from_bytes([0x08u8; 12]);
- let result = cipher.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
/// On authentication failure, the output buffer must be zeroed.
@@ -1356,15 +1365,19 @@ mod tests {
let plaintext = *b"zero me on failure";
let mut buf = plaintext;
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("AES-256-GCM test setup encryption must succeed");
// Corrupt the tag.
let mut bad_tag = tag.to_bytes();
bad_tag[0] ^= 0xFF;
let bad_tag = Aes256GcmTag::from_bytes(bad_tag);
- let result = cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag),
+ Err(OpenError::verification())
+ );
assert!(buf.iter().all(|&b| b == 0), "buffer not zeroed on auth failure");
}
}
diff --git a/src/aead/aes256gcmsiv.rs b/src/aead/aes256gcmsiv.rs
index 57824a7f..14376a2e 100644
--- a/src/aead/aes256gcmsiv.rs
+++ b/src/aead/aes256gcmsiv.rs
@@ -1,15 +1,18 @@
-#![allow(clippy::indexing_slicing)]
-
//! AES-256-GCM-SIV public AEAD surface (RFC 8452).
use core::fmt;
#[cfg(target_arch = "x86_64")]
use super::polyval::{accumulate_padded_x86, precompute_powers, precompute_powers_16};
-use super::{
- AeadBufferError, Nonce96, OpenError, SealError, aes, polyval,
- targets::{AeadBackend, AeadPrimitive, select_backend},
-};
+#[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+))]
+use super::targets::{AeadBackend, AeadPrimitive, select_backend};
+use super::{AeadBufferError, Nonce96, OpenError, SealError, aes, polyval};
use crate::traits::{Aead, ct};
const KEY_SIZE: usize = 32;
@@ -84,7 +87,13 @@ define_aead_tag_type!(
/// are configuration- and release-evidence-bound; see `ct.toml`.
pub struct Aes256GcmSiv {
master_ek: aes::Aes256EncKey,
- #[cfg_attr(target_arch = "wasm32", allow(dead_code))]
+ #[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ ))]
backend: AeadBackend,
}
@@ -227,12 +236,14 @@ fn compute_tag(
}
#[cfg(feature = "diag")]
+/// Derive the per-nonce authentication and encryption keys for diagnostic comparison.
#[must_use]
pub fn diag_aes256gcmsiv_derive_keys(cipher: &Aes256GcmSiv, nonce: &Nonce96) -> ([u8; 16], [u8; 32]) {
derive_keys(&cipher.master_ek, nonce)
}
#[cfg(feature = "diag")]
+/// Encrypt one diagnostic tag block with a raw AES-256 key.
#[must_use]
pub fn diag_aes256gcmsiv_raw_tag_aes(enc_key: &[u8; 32], block: &[u8; 16]) -> [u8; 16] {
let mut out = *block;
@@ -250,6 +261,7 @@ pub fn diag_aes256gcmsiv_raw_tag_aes(enc_key: &[u8; 32], block: &[u8; 16]) -> [u
}
#[cfg(feature = "diag")]
+/// Exercise AES-256 counter-mode encryption and fold the fixed diagnostic output to one block.
#[must_use]
pub fn diag_aes256gcmsiv_ctr32(enc_key: &[u8; 32], tag: &[u8; 16], plaintext: &[u8; 44]) -> [u8; 16] {
let mut counter_block = *tag;
@@ -401,7 +413,6 @@ fn expand_key_riscv_for_backend(key: &[u8; 32], backend: AeadBackend) -> aes::Ae
match backend {
AeadBackend::Riscv64VectorCrypto => aes::aes256_expand_key_riscv_vector(key),
AeadBackend::Riscv64ScalarCrypto => aes::aes256_expand_key_riscv_scalar(key),
- AeadBackend::Riscv64Vperm => aes::aes256_expand_key_riscv_vperm(key),
AeadBackend::Portable => aes::aes256_expand_key_riscv_ttable(key),
_ => aes::aes256_expand_key_riscv_ttable(key),
}
@@ -413,6 +424,13 @@ fn expand_message_key_riscv(enc_key: &[u8; 32], backend: AeadBackend) -> aes::Ae
expand_key_riscv_for_backend(enc_key, backend)
}
+#[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+))]
#[inline]
fn resolve_backend() -> AeadBackend {
select_backend(
@@ -428,7 +446,7 @@ fn riscv_polyval_backend(backend: AeadBackend) -> RiscvPolyvalBackend {
match backend {
AeadBackend::Riscv64VectorCrypto => RiscvPolyvalBackend::Vector,
AeadBackend::Riscv64ScalarCrypto => RiscvPolyvalBackend::Scalar,
- AeadBackend::Portable | AeadBackend::Riscv64Vperm => {
+ AeadBackend::Portable => {
let caps = crate::platform::caps();
if caps.has(crate::platform::caps::riscv::ZBC) || caps.has(crate::platform::caps::riscv::ZBKC) {
RiscvPolyvalBackend::Scalar
@@ -569,6 +587,12 @@ fn compute_tag_wide(
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "aes,neon")]
+/// Seal one message through the fused AArch64 AES-256-GCM-SIV path.
+///
+/// # Safety
+///
+/// The current CPU must support AArch64 AES, NEON, and PMULL. Callers must establish those
+/// capabilities through validated backend selection before entering this function.
unsafe fn encrypt_fused_aarch64(
auth_key: &mut [u8; 16],
enc_key_bytes: &mut [u8; 32],
@@ -725,6 +749,12 @@ unsafe fn encrypt_fused_aarch64(
#[cfg(target_arch = "aarch64")]
#[target_feature(enable = "aes,neon")]
+/// Open one message through the fused AArch64 AES-256-GCM-SIV path.
+///
+/// # Safety
+///
+/// The current CPU must support AArch64 AES, NEON, and PMULL. Callers must establish those
+/// capabilities through validated backend selection before entering this function.
unsafe fn decrypt_fused_aarch64(
auth_key: &mut [u8; 16],
enc_key_bytes: &mut [u8; 32],
@@ -886,6 +916,11 @@ unsafe fn decrypt_fused_aarch64(
// powerpc64 fused encrypt/decrypt (single #[target_feature] scope)
+/// Encrypt with the fused POWER8 AES-256-GCM-SIV backend.
+///
+/// # Safety
+///
+/// The executing CPU must support AltiVec, VSX, POWER8 vector, and POWER8 crypto.
#[cfg(target_arch = "powerpc64")]
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
unsafe fn encrypt_fused_ppc(
@@ -1025,6 +1060,11 @@ unsafe fn encrypt_fused_ppc(
}
}
+/// Decrypt and authenticate with the fused POWER8 AES-256-GCM-SIV backend.
+///
+/// # Safety
+///
+/// The executing CPU must support AltiVec, VSX, POWER8 vector, and POWER8 crypto.
#[cfg(target_arch = "powerpc64")]
#[target_feature(enable = "altivec,vsx,power8-vector,power8-crypto")]
unsafe fn decrypt_fused_ppc(
@@ -1171,6 +1211,11 @@ unsafe fn decrypt_fused_ppc(
// s390x fused encrypt/decrypt (#[target_feature(enable = "vector")] for POLYVAL)
+/// XOR an AES-256 counter stream into `buffer` using s390x CPACF.
+///
+/// # Safety
+///
+/// The executing CPU must support MSA AES instructions.
#[cfg(target_arch = "s390x")]
unsafe fn s390x_ctr32_le_xor_raw(enc_key_bytes: &[u8; 32], counter_block: &mut [u8; 16], buffer: &mut [u8]) {
let mut ctr = u32::from_le_bytes([counter_block[0], counter_block[1], counter_block[2], counter_block[3]]);
@@ -1196,12 +1241,17 @@ unsafe fn s390x_ctr32_le_xor_raw(enc_key_bytes: &[u8; 32], counter_block: &mut [
let processed = aes::xor_keystream_tail(buffer, offset, &keystream, block_count);
offset = offset.strict_add(processed);
- ctr = ctr.wrapping_add(block_count as u32);
+ ctr = ctr.wrapping_add(u32::from(block_count.to_le_bytes()[0]));
}
counter_block[0..4].copy_from_slice(&ctr.to_le_bytes());
}
+/// Encrypt with the fused s390x AES-256-GCM-SIV backend.
+///
+/// # Safety
+///
+/// The executing CPU must support the vector facility and MSA AES instructions.
#[cfg(target_arch = "s390x")]
#[target_feature(enable = "vector")]
unsafe fn encrypt_fused_s390x(
@@ -1315,6 +1365,11 @@ unsafe fn encrypt_fused_s390x(
}
}
+/// Decrypt and authenticate with the fused s390x AES-256-GCM-SIV backend.
+///
+/// # Safety
+///
+/// The executing CPU must support the vector facility and MSA AES instructions.
#[cfg(target_arch = "s390x")]
#[target_feature(enable = "vector")]
unsafe fn decrypt_fused_s390x(
@@ -1443,6 +1498,13 @@ impl Aead for Aes256GcmSiv {
type Tag = Aes256GcmSivTag;
fn new(key: &Self::Key) -> Self {
+ #[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ ))]
let backend = resolve_backend();
Self {
@@ -1450,6 +1512,13 @@ impl Aead for Aes256GcmSiv {
master_ek: expand_key_riscv_for_backend(key.as_bytes(), backend),
#[cfg(not(target_arch = "riscv64"))]
master_ek: aes::aes256_expand_key(key.as_bytes()),
+ #[cfg(any(
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ ))]
backend,
}
}
@@ -1527,10 +1596,7 @@ impl Aead for Aes256GcmSiv {
#[cfg(target_arch = "riscv64")]
{
match self.backend {
- AeadBackend::Portable
- | AeadBackend::Riscv64VectorCrypto
- | AeadBackend::Riscv64ScalarCrypto
- | AeadBackend::Riscv64Vperm => {
+ AeadBackend::Portable | AeadBackend::Riscv64VectorCrypto | AeadBackend::Riscv64ScalarCrypto => {
let tag_bytes = encrypt_riscv(&self.master_ek, self.backend, nonce, aad, buffer);
return Ok(Aes256GcmSivTag::from_bytes(tag_bytes));
}
@@ -1623,10 +1689,7 @@ impl Aead for Aes256GcmSiv {
#[cfg(target_arch = "riscv64")]
{
match self.backend {
- AeadBackend::Portable
- | AeadBackend::Riscv64VectorCrypto
- | AeadBackend::Riscv64ScalarCrypto
- | AeadBackend::Riscv64Vperm => {
+ AeadBackend::Portable | AeadBackend::Riscv64VectorCrypto | AeadBackend::Riscv64ScalarCrypto => {
return decrypt_riscv(&self.master_ek, self.backend, nonce, aad, buffer, tag).map_err(OpenError::from);
}
_ => {}
@@ -1657,10 +1720,13 @@ impl Aead for Aes256GcmSiv {
#[cfg(test)]
mod tests {
- use alloc::{vec, vec::Vec};
+ use alloc::vec;
use super::*;
- use crate::aead::expert::AeadWithNonce;
+ use crate::aead::{
+ expert::AeadWithNonce,
+ test_vectors::{hex_vec, hex12, hex32},
+ };
/// RFC 8452 Appendix C.2, test case 1: empty plaintext, empty AAD.
#[test]
@@ -1673,12 +1739,16 @@ mod tests {
let cipher = Aes256GcmSiv::new(&key);
let mut out = vec![0u8; expected_ct_tag.len()];
- cipher.encrypt(&nonce, &[], &[], &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &[], &[], &mut out)
+ .expect("RFC 8452 empty AES-256-GCM-SIV encryption must succeed");
assert_eq!(out, expected_ct_tag);
// Decrypt.
let mut pt_out = vec![0u8; 0];
- cipher.decrypt(&nonce, &[], &expected_ct_tag, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, &[], &expected_ct_tag, &mut pt_out)
+ .expect("RFC 8452 empty AES-256-GCM-SIV decryption must succeed");
assert!(pt_out.is_empty());
}
@@ -1697,12 +1767,16 @@ mod tests {
// Encrypt.
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, &aad, &plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &aad, &plaintext, &mut out)
+ .expect("RFC 8452 AES-256-GCM-SIV encryption with AAD must succeed");
assert_eq!(out, expected_ct_tag);
// Decrypt.
let mut pt_out = vec![0u8; plaintext.len()];
- cipher.decrypt(&nonce, &aad, &expected_ct_tag, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, &aad, &expected_ct_tag, &mut pt_out)
+ .expect("RFC 8452 AES-256-GCM-SIV decryption with AAD must succeed");
assert_eq!(pt_out, plaintext);
}
@@ -1720,11 +1794,15 @@ mod tests {
let cipher = Aes256GcmSiv::new(&key);
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, &aad, &plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &aad, &plaintext, &mut out)
+ .expect("RFC 8452 long AES-256-GCM-SIV encryption must succeed");
assert_eq!(out, expected_ct_tag);
let mut pt_out = vec![0u8; plaintext.len()];
- cipher.decrypt(&nonce, &aad, &expected_ct_tag, &mut pt_out).unwrap();
+ cipher
+ .decrypt(&nonce, &aad, &expected_ct_tag, &mut pt_out)
+ .expect("RFC 8452 long AES-256-GCM-SIV decryption must succeed");
assert_eq!(pt_out, plaintext);
}
@@ -1741,8 +1819,10 @@ mod tests {
let cipher = Aes256GcmSiv::new(&key);
let mut pt_out = vec![0u8; 0];
- let result = cipher.decrypt(&nonce, &[], &bad_ct_tag, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&nonce, &[], &bad_ct_tag, &mut pt_out),
+ Err(OpenError::verification())
+ );
}
/// Decryption with wrong AAD should fail.
@@ -1757,8 +1837,10 @@ mod tests {
let cipher = Aes256GcmSiv::new(&key);
let mut pt_out = vec![0u8; 8]; // plaintext was 8 bytes
// Wrong AAD: 0x02 instead of 0x01.
- let result = cipher.decrypt(&nonce, &[0x02], &ct_tag, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&nonce, &[0x02], &ct_tag, &mut pt_out),
+ Err(OpenError::verification())
+ );
}
/// Decryption with wrong nonce should fail.
@@ -1774,8 +1856,10 @@ mod tests {
let mut pt_out = vec![0u8; 8]; // plaintext was 8 bytes
// Wrong nonce: 0x04 instead of 0x03.
let wrong_nonce = Nonce96::from_bytes(hex12("040000000000000000000000"));
- let result = cipher.decrypt(&wrong_nonce, &aad, &ct_tag, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&wrong_nonce, &aad, &ct_tag, &mut pt_out),
+ Err(OpenError::verification())
+ );
}
/// Ciphertext body tampering should fail verification.
@@ -1794,8 +1878,10 @@ mod tests {
let cipher = Aes256GcmSiv::new(&key);
let mut pt_out = vec![0u8; plaintext.len()];
- let result = cipher.decrypt(&nonce, &aad, &ct_tag, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&nonce, &aad, &ct_tag, &mut pt_out),
+ Err(OpenError::verification())
+ );
}
/// On authentication failure, the output buffer must be zeroed.
@@ -1810,15 +1896,19 @@ mod tests {
let cipher = Aes256GcmSiv::new(&key);
let mut out = vec![0u8; plaintext.len().strict_add(TAG_SIZE)];
- cipher.encrypt(&nonce, &aad, &plaintext, &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &aad, &plaintext, &mut out)
+ .expect("AES-256-GCM-SIV test setup encryption must succeed");
// Corrupt the tag.
let last = out.len().strict_sub(1);
out[last] ^= 0xff;
let mut pt_out = vec![0xffu8; plaintext.len()]; // fill with non-zero
- let result = cipher.decrypt(&nonce, &aad, &out, &mut pt_out);
- assert!(result.is_err());
+ assert_eq!(
+ cipher.decrypt(&nonce, &aad, &out, &mut pt_out),
+ Err(OpenError::verification())
+ );
// Buffer must be zeroed even though decryption was attempted.
assert!(pt_out.iter().all(|&b| b == 0), "buffer not zeroed on auth failure");
}
@@ -1836,31 +1926,53 @@ mod tests {
let cipher = Aes256GcmSiv::new(&key);
let mut buf = plaintext.clone();
- let tag = cipher.encrypt_in_place(&nonce, &aad, &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, &aad, &mut buf)
+ .expect("AES-256-GCM-SIV detached encryption must succeed");
// buf is now ciphertext, tag is separate.
assert_ne!(buf, plaintext);
- cipher.decrypt_in_place(&nonce, &aad, &mut buf, &tag).unwrap();
+ cipher
+ .decrypt_in_place(&nonce, &aad, &mut buf, &tag)
+ .expect("AES-256-GCM-SIV detached decryption must succeed");
assert_eq!(buf, plaintext);
}
/// `tag_from_slice` rejects wrong-length input.
#[test]
fn aes256gcmsiv_tag_from_slice_rejects_bad_length() {
- assert!(Aes256GcmSiv::tag_from_slice(&[0u8; 15]).is_err());
- assert!(Aes256GcmSiv::tag_from_slice(&[0u8; 17]).is_err());
- assert!(Aes256GcmSiv::tag_from_slice(&[0u8; 0]).is_err());
- assert!(Aes256GcmSiv::tag_from_slice(&[0u8; 16]).is_ok());
+ assert_eq!(
+ Aes256GcmSiv::tag_from_slice(&[0u8; 15]).expect_err("short AES-256-GCM-SIV tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aes256GcmSiv::tag_from_slice(&[0u8; 17]).expect_err("long AES-256-GCM-SIV tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ Aes256GcmSiv::tag_from_slice(&[]).expect_err("empty AES-256-GCM-SIV tag must be rejected"),
+ AeadBufferError::new()
+ );
+ let tag = Aes256GcmSiv::tag_from_slice(&[0u8; 16]).expect("16-byte AES-256-GCM-SIV tag must be accepted");
+ assert_eq!(tag.as_bytes(), &[0u8; 16]);
}
#[test]
#[cfg(target_pointer_width = "64")]
fn aes256gcmsiv_input_limit_matches_rfc8452() {
for len in [MAX_INPUT_LEN.strict_sub(1), MAX_INPUT_LEN] {
- assert!(super::super::try_bounded_length_as_u64(len as usize, MAX_INPUT_LEN).is_ok());
- }
- assert!(super::super::try_bounded_length_as_u64(MAX_INPUT_LEN.strict_add(1) as usize, MAX_INPUT_LEN).is_err());
+ let platform_len = usize::try_from(len).expect("RFC 8452 input limit fits 64-bit usize");
+ assert_eq!(
+ super::super::try_bounded_length_as_u64(platform_len, MAX_INPUT_LEN),
+ Ok(len)
+ );
+ }
+ let too_large = usize::try_from(MAX_INPUT_LEN.strict_add(1)).expect("RFC 8452 input limit fits 64-bit usize");
+ assert_eq!(
+ super::super::try_bounded_length_as_u64(too_large, MAX_INPUT_LEN),
+ Err(super::super::LengthOverflow)
+ );
}
/// RFC 8452 Appendix C.2 vector with a different key (empty PT+AAD).
@@ -1875,38 +1987,14 @@ mod tests {
let cipher = Aes256GcmSiv::new(&key);
let mut out = vec![0u8; expected_ct_tag.len()];
- cipher.encrypt(&nonce, &[], &[], &mut out).unwrap();
+ cipher
+ .encrypt(&nonce, &[], &[], &mut out)
+ .expect("RFC 8452 alternate-key AES-256-GCM-SIV encryption must succeed");
assert_eq!(out, expected_ct_tag);
let mut pt_out = vec![0u8; 0];
- cipher.decrypt(&nonce, &[], &expected_ct_tag, &mut pt_out).unwrap();
- }
-
- // --- Hex helpers ---
-
- fn hex32(hex: &str) -> [u8; 32] {
- let mut out = [0u8; 32];
- for i in 0..32 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex12(hex: &str) -> [u8; 12] {
- let mut out = [0u8; 12];
- for i in 0..12 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- }
- out
- }
-
- fn hex_vec(hex: &str) -> Vec {
- let mut out = Vec::with_capacity(hex.len() / 2);
- let mut i = 0;
- while i < hex.len() {
- out.push(u8::from_str_radix(&hex[i..i + 2], 16).unwrap());
- i += 2;
- }
- out
+ cipher
+ .decrypt(&nonce, &[], &expected_ct_tag, &mut pt_out)
+ .expect("RFC 8452 alternate-key AES-256-GCM-SIV decryption must succeed");
}
}
diff --git a/src/aead/aes_round.rs b/src/aead/aes_round.rs
index 09f48810..73538fd8 100644
--- a/src/aead/aes_round.rs
+++ b/src/aead/aes_round.rs
@@ -23,7 +23,7 @@ const BLOCK_SIZE: usize = 16;
/// Input transform, low nibble: maps AES GF(2^8) basis to tower-field basis.
#[rustfmt::skip]
-#[cfg(any(test, target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(any(test, target_arch = "s390x"))]
pub(crate) const VPERM_IPT_LO: [u8; 16] = [
0x00, 0x70, 0x2A, 0x5A, 0x98, 0xE8, 0xB2, 0xC2,
0x08, 0x78, 0x22, 0x52, 0x90, 0xE0, 0xBA, 0xCA,
@@ -31,7 +31,7 @@ pub(crate) const VPERM_IPT_LO: [u8; 16] = [
/// Input transform, high nibble: maps AES GF(2^8) basis to tower-field basis.
#[rustfmt::skip]
-#[cfg(any(test, target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(any(test, target_arch = "s390x"))]
pub(crate) const VPERM_IPT_HI: [u8; 16] = [
0x00, 0x4D, 0x7C, 0x31, 0x7D, 0x30, 0x01, 0x4C,
0x81, 0xCC, 0xFD, 0xB0, 0xFC, 0xB1, 0x80, 0xCD,
@@ -39,7 +39,7 @@ pub(crate) const VPERM_IPT_HI: [u8; 16] = [
/// GF(2^4) inverse table, low nibble. Entry 0 is `0x80` (infinity sentinel).
#[rustfmt::skip]
-#[cfg(any(test, target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(any(test, target_arch = "s390x"))]
pub(crate) const VPERM_INV_LO: [u8; 16] = [
0x80, 0x01, 0x08, 0x0D, 0x0F, 0x06, 0x05, 0x0E,
0x02, 0x0C, 0x0B, 0x0A, 0x09, 0x03, 0x07, 0x04,
@@ -47,7 +47,7 @@ pub(crate) const VPERM_INV_LO: [u8; 16] = [
/// GF(2^4) inverse table, high nibble. Entry 0 is `0x80` (infinity sentinel).
#[rustfmt::skip]
-#[cfg(any(test, target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(any(test, target_arch = "s390x"))]
pub(crate) const VPERM_INV_HI: [u8; 16] = [
0x80, 0x07, 0x0B, 0x0F, 0x06, 0x0A, 0x04, 0x01,
0x09, 0x08, 0x05, 0x02, 0x0C, 0x0E, 0x0D, 0x03,
@@ -56,7 +56,7 @@ pub(crate) const VPERM_INV_HI: [u8; 16] = [
/// SubBytes output table, upper component (inverse-isomorphism + AES affine,
/// no MixColumns). `sbou[io] ^ sbot[jo] = AES_SBOX[x] ^ 0x63`.
#[rustfmt::skip]
-#[cfg(any(test, target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(any(test, target_arch = "s390x"))]
pub(crate) const VPERM_SBOU: [u8; 16] = [
0x00, 0xC7, 0xBD, 0x6F, 0x17, 0x6D, 0xD2, 0xD0,
0x78, 0xA8, 0x02, 0xC5, 0x7A, 0xBF, 0xAA, 0x15,
@@ -65,7 +65,7 @@ pub(crate) const VPERM_SBOU: [u8; 16] = [
/// SubBytes output table, lower component (inverse-isomorphism + AES affine,
/// no MixColumns). `sbou[io] ^ sbot[jo] = AES_SBOX[x] ^ 0x63`.
#[rustfmt::skip]
-#[cfg(any(test, target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(any(test, target_arch = "s390x"))]
pub(crate) const VPERM_SBOT: [u8; 16] = [
0x00, 0x6A, 0xBB, 0x5F, 0xA5, 0x74, 0xE4, 0xCF,
0xFA, 0x35, 0x2B, 0x41, 0xD1, 0x90, 0x1E, 0x8E,
@@ -73,18 +73,18 @@ pub(crate) const VPERM_SBOT: [u8; 16] = [
/// AES ShiftRows permutation: output byte `i` reads from input byte `SR[i]`.
#[rustfmt::skip]
-#[cfg(any(test, target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(any(test, target_arch = "s390x"))]
pub(crate) const VPERM_SR: [u8; 16] = [
0x00, 0x05, 0x0A, 0x0F, 0x04, 0x09, 0x0E, 0x03,
0x08, 0x0D, 0x02, 0x07, 0x0C, 0x01, 0x06, 0x0B,
];
/// AES affine constant: Hamburg vperm S-box omits this; XOR after SubBytes.
-#[cfg(any(test, target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(any(test, target_arch = "s390x"))]
pub(crate) const AES_AFFINE: u8 = 0x63;
/// AES MixColumns reduction constant: `xtime(b) = (b<<1) ^ ((b>>7) * 0x1B)`.
-#[cfg(any(target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(target_arch = "s390x")]
pub(crate) const XTIME_REDUCE: u8 = 0x1B;
/// Nibble mask: extract low 4 bits of each byte.
@@ -94,7 +94,7 @@ pub(crate) const NIBBLE_MASK: u8 = 0x0F;
/// MixColumns column-rotate-by-1 permutation: rotates each 4-byte column by 1.
/// `[b1,b2,b3,b0, b5,b6,b7,b4, b9,b10,b11,b8, b13,b14,b15,b12]`
#[rustfmt::skip]
-#[cfg(any(target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(target_arch = "s390x")]
pub(crate) const MC_ROT1: [u8; 16] = [
0x01, 0x02, 0x03, 0x00, 0x05, 0x06, 0x07, 0x04,
0x09, 0x0A, 0x0B, 0x08, 0x0D, 0x0E, 0x0F, 0x0C,
@@ -103,7 +103,7 @@ pub(crate) const MC_ROT1: [u8; 16] = [
/// MixColumns column-rotate-by-2 permutation: rotates each 4-byte column by 2.
/// `[b2,b3,b0,b1, b6,b7,b4,b5, b10,b11,b8,b9, b14,b15,b12,b13]`
#[rustfmt::skip]
-#[cfg(any(target_arch = "s390x", target_arch = "riscv64"))]
+#[cfg(target_arch = "s390x")]
pub(crate) const MC_ROT2: [u8; 16] = [
0x02, 0x03, 0x00, 0x01, 0x06, 0x07, 0x04, 0x05,
0x0A, 0x0B, 0x08, 0x09, 0x0E, 0x0F, 0x0C, 0x0D,
@@ -140,7 +140,9 @@ const fn gf256_mul(a: u8, b: u8) -> u8 {
prod ^= (prod >> 9).wrapping_mul(0x11b << 1);
prod ^= (prod >> 8).wrapping_mul(0x11b);
- prod as u8
+ let [low, high] = prod.to_le_bytes();
+ debug_assert!(high == 0);
+ low
}
#[cfg(any(
@@ -194,7 +196,7 @@ const fn sbox(x: u8) -> u8 {
))]
#[inline(always)]
const fn col_byte(col: u32, row: usize) -> u8 {
- (col >> (24u32.strict_sub((row as u32).strict_mul(8)))) as u8
+ col.to_be_bytes()[row]
}
#[cfg(any(
@@ -289,6 +291,7 @@ pub(crate) fn aes_enc_round_portable(block: &[u8; BLOCK_SIZE], round_key: &[u8;
#[cfg(all(feature = "diag", feature = "aegis256"))]
#[must_use]
+/// Apply one portable AES encryption round for backend diagnostics.
pub fn diag_aes_enc_round_portable(block: &[u8; BLOCK_SIZE], round_key: &[u8; BLOCK_SIZE]) -> [u8; BLOCK_SIZE] {
aes_enc_round_portable(block, round_key)
}
diff --git a/src/aead/ascon128.rs b/src/aead/ascon128.rs
index 51cad04a..a6a4cdab 100644
--- a/src/aead/ascon128.rs
+++ b/src/aead/ascon128.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::indexing_slicing)]
-
//! Ascon-AEAD128 authenticated encryption (NIST SP 800-232).
//!
//! Pure Rust, `no_std` implementation with fixed-work, table-free source
@@ -26,17 +24,19 @@ const DOMAIN_SEPARATOR: u64 = 0x8000_0000_0000_0000;
/// Little-endian padding: set the first free byte at position `n`.
#[inline(always)]
-const fn pad(n: usize) -> u64 {
- 0x01_u64 << (8 * n)
+fn pad(n: usize) -> u64 {
+ let shift = u32::try_from(n.strict_mul(8)).expect("Ascon tail position must fit the word width");
+ 0x01_u64.strict_shl(shift)
}
/// Clear the lowest `n` bytes of `word`.
#[inline(always)]
-const fn clear(word: u64, n: usize) -> u64 {
+fn clear(word: u64, n: usize) -> u64 {
if n == 0 {
return word;
}
- word & (u64::MAX << (8 * n))
+ let shift = u32::try_from(n.strict_mul(8)).expect("Ascon tail length must fit the word width");
+ word & u64::MAX.strict_shl(shift)
}
/// Load up to 8 bytes little-endian into a u64, zero-padding on the right.
@@ -213,14 +213,13 @@ impl AsconAead128 {
/// Absorb associated data into the state.
fn process_aad(s: &mut [u64; 5], aad: &[u8]) {
if !aad.is_empty() {
- let mut chunks = aad.chunks_exact(RATE);
- for chunk in chunks.by_ref() {
+ let (chunks, mut rest) = aad.as_chunks::();
+ for chunk in chunks {
s[0] ^= load_bytes(&chunk[..8]);
s[1] ^= load_bytes(&chunk[8..]);
permute_8_portable(s);
}
- let mut rest = chunks.remainder();
let sidx = if rest.len() >= 8 {
s[0] ^= load_bytes(&rest[..8]);
rest = &rest[8..];
@@ -289,8 +288,8 @@ impl Aead for AsconAead128 {
let mut s = self.initialize(nonce);
Self::process_aad(&mut s, aad);
- let mut blocks = buffer.chunks_exact_mut(RATE);
- for block in blocks.by_ref() {
+ let (blocks, mut tail) = buffer.as_chunks_mut::();
+ for block in blocks {
s[0] ^= load_bytes(&block[..8]);
block[..8].copy_from_slice(&s[0].to_le_bytes());
s[1] ^= load_bytes(&block[8..]);
@@ -298,7 +297,6 @@ impl Aead for AsconAead128 {
permute_8_portable(&mut s);
}
- let mut tail = blocks.into_remainder();
let sidx = if tail.len() >= 8 {
s[0] ^= load_bytes(&tail[..8]);
tail[..8].copy_from_slice(&s[0].to_le_bytes());
@@ -328,8 +326,8 @@ impl Aead for AsconAead128 {
let mut s = self.initialize(nonce);
Self::process_aad(&mut s, aad);
- let mut blocks = buffer.chunks_exact_mut(RATE);
- for block in blocks.by_ref() {
+ let (blocks, mut tail) = buffer.as_chunks_mut::();
+ for block in blocks {
let c0 = load_bytes(&block[..8]);
block[..8].copy_from_slice(&(s[0] ^ c0).to_le_bytes());
s[0] = c0;
@@ -339,7 +337,6 @@ impl Aead for AsconAead128 {
permute_8_portable(&mut s);
}
- let mut tail = blocks.into_remainder();
let sidx = if tail.len() >= 8 {
let c0 = load_bytes(&tail[..8]);
tail[..8].copy_from_slice(&(s[0] ^ c0).to_le_bytes());
@@ -369,6 +366,7 @@ impl Aead for AsconAead128 {
}
#[cfg(feature = "diag")]
+/// Compare a portable Ascon-AEAD128 tag computation with an expected diagnostic tag.
#[unsafe(no_mangle)]
#[inline(never)]
pub fn diag_ascon_aead128_tag_portable(
@@ -402,18 +400,24 @@ mod tests {
fn assert_matches_oracle(key: [u8; 16], nonce: [u8; 16], aad: &[u8], plaintext: &[u8]) {
let aead = AsconAead128::new(&AsconAead128Key::from_bytes(key));
let nonce_typed = Nonce128::from_bytes(nonce);
- let oracle = ascon_aead::AsconAead128::new_from_slice(&key).unwrap();
+ let oracle = ascon_aead::AsconAead128::new_from_slice(&key).expect("16-byte Ascon oracle key must be accepted");
let oracle_nonce = Array(nonce);
let mut ours = plaintext.to_vec();
- let tag = aead.encrypt_in_place(&nonce_typed, aad, &mut ours).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce_typed, aad, &mut ours)
+ .expect("rscrypto Ascon encryption must succeed");
let mut ours_combined = ours.clone();
ours_combined.extend_from_slice(tag.as_bytes());
- let expected = oracle.encrypt(&oracle_nonce, Payload { msg: plaintext, aad }).unwrap();
+ let expected = oracle
+ .encrypt(&oracle_nonce, Payload { msg: plaintext, aad })
+ .expect("oracle Ascon encryption must succeed");
assert_eq!(ours_combined, expected, "encryption mismatch");
let mut ours_buf = ours.clone();
- aead.decrypt_in_place(&nonce_typed, aad, &mut ours_buf, &tag).unwrap();
+ aead
+ .decrypt_in_place(&nonce_typed, aad, &mut ours_buf, &tag)
+ .expect("rscrypto Ascon self-decryption must succeed");
assert_eq!(ours_buf, plaintext, "self decrypt mismatch");
let (oracle_ct, oracle_tag) = expected.split_at(expected.len().strict_sub(TAG_SIZE));
@@ -423,9 +427,13 @@ mod tests {
&nonce_typed,
aad,
&mut oracle_buf,
- &AsconAead128Tag::from_bytes(oracle_tag.try_into().unwrap()),
+ &AsconAead128Tag::from_bytes(
+ oracle_tag
+ .try_into()
+ .expect("oracle Ascon output must end in a 16-byte tag"),
+ ),
)
- .unwrap();
+ .expect("rscrypto must decrypt the oracle Ascon ciphertext");
assert_eq!(oracle_buf, plaintext, "oracle decrypt mismatch");
}
@@ -437,8 +445,12 @@ mod tests {
let aead = AsconAead128::new(&key);
let mut buf = [];
- let tag = aead.encrypt_in_place(&nonce, b"", &mut buf).unwrap();
- aead.decrypt_in_place(&nonce, b"", &mut buf, &tag).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"", &mut buf)
+ .expect("empty Ascon encryption must succeed");
+ aead
+ .decrypt_in_place(&nonce, b"", &mut buf, &tag)
+ .expect("empty Ascon decryption must succeed");
}
#[test]
@@ -449,10 +461,14 @@ mod tests {
let plaintext = b"the quick brown fox jumps over the lazy dog";
let mut buf = *plaintext;
- let tag = aead.encrypt_in_place(&nonce, b"header", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"header", &mut buf)
+ .expect("Ascon encryption with AAD must succeed");
assert_ne!(&buf[..], &plaintext[..]);
- aead.decrypt_in_place(&nonce, b"header", &mut buf, &tag).unwrap();
+ aead
+ .decrypt_in_place(&nonce, b"header", &mut buf, &tag)
+ .expect("Ascon decryption with AAD must succeed");
assert_eq!(&buf[..], &plaintext[..]);
}
@@ -465,10 +481,10 @@ mod tests {
let mut buf = [];
let tag = aead
.encrypt_in_place(&nonce, b"associated data only", &mut buf)
- .unwrap();
+ .expect("AAD-only Ascon encryption must succeed");
aead
.decrypt_in_place(&nonce, b"associated data only", &mut buf, &tag)
- .unwrap();
+ .expect("AAD-only Ascon decryption must succeed");
}
#[test]
@@ -478,14 +494,18 @@ mod tests {
let aead = AsconAead128::new(&key);
let mut buf = *b"zero me on failure";
- let tag = aead.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("Ascon test setup encryption must succeed");
let mut bad_tag = tag.to_bytes();
bad_tag[0] ^= 0xFF;
let bad_tag = AsconAead128Tag::from_bytes(bad_tag);
- let result = aead.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag),
+ Err(OpenError::verification())
+ );
assert!(buf.iter().all(|&b| b == 0), "buffer not zeroed on auth failure");
}
@@ -496,11 +516,15 @@ mod tests {
let aead = AsconAead128::new(&key);
let mut buf = *b"secret";
- let tag = aead.encrypt_in_place(&nonce, b"", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"", &mut buf)
+ .expect("Ascon test setup encryption must succeed");
buf[0] ^= 1;
- let result = aead.decrypt_in_place(&nonce, b"", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&nonce, b"", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
// Buffer must be zeroized on failure.
assert_eq!(&buf, &[0u8; 6]);
}
@@ -512,14 +536,18 @@ mod tests {
let aead = AsconAead128::new(&key);
let mut buf = *b"data";
- let tag = aead.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("Ascon test setup encryption must succeed");
let mut bad_tag_bytes = tag.to_bytes();
bad_tag_bytes[15] ^= 1;
let bad_tag = AsconAead128Tag::from_bytes(bad_tag_bytes);
- let result = aead.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&nonce, b"aad", &mut buf, &bad_tag),
+ Err(OpenError::verification())
+ );
assert_eq!(&buf, &[0u8; 4]);
}
@@ -530,10 +558,14 @@ mod tests {
let aead = AsconAead128::new(&key);
let mut buf = *b"msg";
- let tag = aead.encrypt_in_place(&nonce, b"correct", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"correct", &mut buf)
+ .expect("Ascon test setup encryption must succeed");
- let result = aead.decrypt_in_place(&nonce, b"wrong", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&nonce, b"wrong", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
#[test]
@@ -543,11 +575,15 @@ mod tests {
let aead = AsconAead128::new(&key);
let mut buf = *b"nonce test";
- let tag = aead.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("Ascon test setup encryption must succeed");
let wrong_nonce = Nonce128::from_bytes([11; 16]);
- let result = aead.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag);
- assert!(result.is_err());
+ assert_eq!(
+ aead.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag),
+ Err(OpenError::verification())
+ );
}
#[test]
@@ -558,18 +594,29 @@ mod tests {
let pt = b"combined mode";
let mut sealed = vec![0u8; pt.len().strict_add(TAG_SIZE)];
- aead.encrypt(&nonce, b"h", pt.as_slice(), &mut sealed).unwrap();
+ aead
+ .encrypt(&nonce, b"h", pt.as_slice(), &mut sealed)
+ .expect("combined Ascon encryption must succeed");
let mut opened = vec![0u8; pt.len()];
- aead.decrypt(&nonce, b"h", &sealed, &mut opened).unwrap();
+ aead
+ .decrypt(&nonce, b"h", &sealed, &mut opened)
+ .expect("combined Ascon decryption must succeed");
assert_eq!(&opened, &pt[..]);
}
#[test]
fn tag_from_slice_rejects_wrong_length() {
- assert!(AsconAead128::tag_from_slice(&[0u8; 15]).is_err());
- assert!(AsconAead128::tag_from_slice(&[0u8; 17]).is_err());
- assert!(AsconAead128::tag_from_slice(&[0u8; 16]).is_ok());
+ assert_eq!(
+ AsconAead128::tag_from_slice(&[0u8; 15]).expect_err("short Ascon tag must be rejected"),
+ AeadBufferError::new()
+ );
+ assert_eq!(
+ AsconAead128::tag_from_slice(&[0u8; 17]).expect_err("long Ascon tag must be rejected"),
+ AeadBufferError::new()
+ );
+ let tag = AsconAead128::tag_from_slice(&[0u8; 16]).expect("16-byte Ascon tag must be accepted");
+ assert_eq!(tag.as_bytes(), &[0u8; 16]);
}
#[test]
@@ -583,7 +630,7 @@ mod tests {
let mut buf = plaintext;
let tag = aead
.encrypt_in_place(&nonce, b"multi-block aad that is longer than one rate block", &mut buf)
- .unwrap();
+ .expect("multi-block Ascon encryption must succeed");
aead
.decrypt_in_place(
&nonce,
@@ -591,7 +638,7 @@ mod tests {
&mut buf,
&tag,
)
- .unwrap();
+ .expect("multi-block Ascon decryption must succeed");
assert_eq!(buf, plaintext);
}
@@ -604,15 +651,23 @@ mod tests {
// Exactly 8 bytes = 1 full block, 0-byte tail.
let plaintext = [0x55u8; 8];
let mut buf = plaintext;
- let tag = aead.encrypt_in_place(&nonce, b"", &mut buf).unwrap();
- aead.decrypt_in_place(&nonce, b"", &mut buf, &tag).unwrap();
+ let tag = aead
+ .encrypt_in_place(&nonce, b"", &mut buf)
+ .expect("one-word Ascon encryption must succeed");
+ aead
+ .decrypt_in_place(&nonce, b"", &mut buf, &tag)
+ .expect("one-word Ascon decryption must succeed");
assert_eq!(buf, plaintext);
// Exactly 16 bytes = 2 full blocks, 0-byte tail.
let plaintext16 = [0x66u8; 16];
let mut buf16 = plaintext16;
- let tag16 = aead.encrypt_in_place(&nonce, b"", &mut buf16).unwrap();
- aead.decrypt_in_place(&nonce, b"", &mut buf16, &tag16).unwrap();
+ let tag16 = aead
+ .encrypt_in_place(&nonce, b"", &mut buf16)
+ .expect("one-rate Ascon encryption must succeed");
+ aead
+ .decrypt_in_place(&nonce, b"", &mut buf16, &tag16)
+ .expect("one-rate Ascon decryption must succeed");
assert_eq!(buf16, plaintext16);
}
@@ -653,7 +708,7 @@ mod tests {
fn differential_multiblock_matches_oracle() {
let key = [0x42; 16];
let nonce = [0x24; 16];
- let aad: Vec = (0..48).map(|i| i as u8).collect();
+ let aad: Vec = (0u8..48).collect();
let pt: Vec = (0u8..97).map(|i| i.wrapping_mul(17)).collect();
assert_matches_oracle(key, nonce, &aad, &pt);
}
diff --git a/src/aead/chacha20.rs b/src/aead/chacha20.rs
index 091d6374..0a1db21a 100644
--- a/src/aead/chacha20.rs
+++ b/src/aead/chacha20.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::indexing_slicing)]
-
//! Portable ChaCha20 and HChaCha20 core.
use core::mem;
@@ -25,9 +23,11 @@ pub(crate) const POLY1305_KEY_SIZE: usize = 32;
const CONSTANTS: [u32; 4] = [0x6170_7865, 0x3320_646e, 0x7962_2d32, 0x6b20_6574];
-pub(crate) type XorKeystreamFn = fn(&[u8; KEY_SIZE], u32, &[u8; NONCE_SIZE], &mut [u8]);
+type XorKeystreamFn = unsafe fn(&[u8; KEY_SIZE], u32, &[u8; NONCE_SIZE], &mut [u8]);
+#[cfg(feature = "xchacha20poly1305")]
static XCHACHA20POLY1305_XOR_KEYSTREAM_DISPATCH: OnceCache = OnceCache::new();
+#[cfg(feature = "chacha20poly1305")]
static CHACHA20POLY1305_XOR_KEYSTREAM_DISPATCH: OnceCache = OnceCache::new();
#[inline(always)]
@@ -133,7 +133,9 @@ pub(crate) fn block(key: &[u8; KEY_SIZE], counter: u32, nonce: &[u8; NONCE_SIZE]
}
let mut out = [0u8; BLOCK_SIZE];
- for (chunk, word) in out.chunks_exact_mut(4).zip(state) {
+ let (chunks, remainder) = out.as_chunks_mut::<4>();
+ debug_assert!(remainder.is_empty());
+ for (chunk, word) in chunks.iter_mut().zip(state) {
chunk.copy_from_slice(&word.to_le_bytes());
}
out
@@ -185,19 +187,25 @@ pub(crate) fn xor_keystream(
}
}
- xor_keystream_resolved(primitive)(key, initial_counter, nonce, buffer);
+ let kernel = xor_keystream_resolved(primitive);
+ // SAFETY: the counter-range check above covers every 64-byte block, and the resolver returns an accelerated kernel
+ // only when its complete capability set is available. The portable function also coerces to this unsafe fn type.
+ unsafe { kernel(key, initial_counter, nonce, buffer) };
Ok(())
}
#[inline]
-pub(crate) fn xor_keystream_resolved(primitive: AeadPrimitive) -> XorKeystreamFn {
+fn xor_keystream_resolved(primitive: AeadPrimitive) -> XorKeystreamFn {
match primitive {
+ #[cfg(feature = "xchacha20poly1305")]
AeadPrimitive::XChaCha20Poly1305 => {
XCHACHA20POLY1305_XOR_KEYSTREAM_DISPATCH.get_or_init(|| resolve_xor_keystream(primitive))
}
+ #[cfg(feature = "chacha20poly1305")]
AeadPrimitive::ChaCha20Poly1305 => {
CHACHA20POLY1305_XOR_KEYSTREAM_DISPATCH.get_or_init(|| resolve_xor_keystream(primitive))
}
+ #[cfg(any(test, feature = "aegis256", feature = "aes-gcm", feature = "aes-gcm-siv"))]
_ => resolve_xor_keystream(primitive),
}
}
@@ -247,7 +255,7 @@ fn xor_keystream_portable(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u
}
}
-#[cfg_attr(not(all(target_arch = "powerpc64", target_endian = "little")), allow(dead_code))]
+#[cfg(all(target_arch = "powerpc64", target_endian = "little"))]
pub(crate) fn xor_keystream_first_block_portable(
key: &[u8; KEY_SIZE],
counter: u32,
@@ -265,31 +273,46 @@ pub(crate) fn xor_keystream_first_block_portable(
#[cfg(target_arch = "riscv64")]
#[inline(always)]
-fn simd_u32x4_rotl(value: u32x4, bits: u32) -> u32x4 {
- (value << u32x4::splat(bits)) | (value >> u32x4::splat(32u32.wrapping_sub(bits)))
+fn simd_u32x4_rotl(value: u32x4) -> u32x4 {
+ const { assert!(BITS > 0 && BITS < 32) }
+ let left = core::ops::Shl::shl(value, u32x4::splat(BITS));
+ let right = core::ops::Shr::shr(value, u32x4::splat(32u32.strict_sub(BITS)));
+ core::ops::BitOr::bitor(left, right)
+}
+
+#[cfg(target_arch = "riscv64")]
+#[inline(always)]
+fn simd_u32x4_wrapping_add(left: u32x4, right: u32x4) -> u32x4 {
+ core::ops::Add::add(left, right)
}
#[cfg(target_arch = "riscv64")]
#[inline(always)]
fn simd_u32x4_quarter_round(a: &mut u32x4, b: &mut u32x4, c: &mut u32x4, d: &mut u32x4) {
- *a += *b;
+ *a = simd_u32x4_wrapping_add(*a, *b);
*d ^= *a;
- *d = simd_u32x4_rotl(*d, 16);
+ *d = simd_u32x4_rotl::<16>(*d);
- *c += *d;
+ *c = simd_u32x4_wrapping_add(*c, *d);
*b ^= *c;
- *b = simd_u32x4_rotl(*b, 12);
+ *b = simd_u32x4_rotl::<12>(*b);
- *a += *b;
+ *a = simd_u32x4_wrapping_add(*a, *b);
*d ^= *a;
- *d = simd_u32x4_rotl(*d, 8);
+ *d = simd_u32x4_rotl::<8>(*d);
- *c += *d;
+ *c = simd_u32x4_wrapping_add(*c, *d);
*b ^= *c;
- *b = simd_u32x4_rotl(*b, 7);
+ *b = simd_u32x4_rotl::<7>(*b);
}
#[cfg(target_arch = "riscv64")]
+/// Generate and XOR a ChaCha20 stream in four-block RISC-V vector batches.
+///
+/// # Safety
+///
+/// The caller must ensure that the RISC-V vector extension is available and that `buffer`'s 64-byte block count fits
+/// the counter range starting at `initial_counter`.
#[target_feature(enable = "v")]
unsafe fn xor_keystream_u32x4_impl(
key: &[u8; KEY_SIZE],
@@ -298,12 +321,14 @@ unsafe fn xor_keystream_u32x4_impl(
buffer: &mut [u8],
) {
const BLOCKS_PER_BATCH: usize = 4;
+ const BLOCKS_PER_BATCH_U32: u32 = 4;
+ const BATCH_SIZE: usize = BLOCK_SIZE.strict_mul(BLOCKS_PER_BATCH);
let mut counter = initial_counter;
- let mut batches = buffer.chunks_exact_mut(BLOCK_SIZE * BLOCKS_PER_BATCH);
- for chunk in &mut batches {
+ let (batches, remainder) = buffer.as_chunks_mut::();
+ for chunk in batches {
debug_assert!(
- counter.checked_add((BLOCKS_PER_BATCH - 1) as u32).is_some(),
+ counter <= u32::MAX.strict_sub(BLOCKS_PER_BATCH_U32.strict_sub(1)),
"ChaCha20 block counter overflow"
);
@@ -321,9 +346,9 @@ unsafe fn xor_keystream_u32x4_impl(
let mut x11 = u32x4::splat(load_u32_le(&key[28..32]));
let mut x12 = u32x4::from_array([
counter,
- counter.wrapping_add(1),
- counter.wrapping_add(2),
- counter.wrapping_add(3),
+ counter.strict_add(1),
+ counter.strict_add(2),
+ counter.strict_add(3),
]);
let mut x13 = u32x4::splat(load_u32_le(&nonce[0..4]));
let mut x14 = u32x4::splat(load_u32_le(&nonce[4..8]));
@@ -361,22 +386,22 @@ unsafe fn xor_keystream_u32x4_impl(
round = round.strict_add(1);
}
- x0 += o0;
- x1 += o1;
- x2 += o2;
- x3 += o3;
- x4 += o4;
- x5 += o5;
- x6 += o6;
- x7 += o7;
- x8 += o8;
- x9 += o9;
- x10 += o10;
- x11 += o11;
- x12 += o12;
- x13 += o13;
- x14 += o14;
- x15 += o15;
+ x0 = simd_u32x4_wrapping_add(x0, o0);
+ x1 = simd_u32x4_wrapping_add(x1, o1);
+ x2 = simd_u32x4_wrapping_add(x2, o2);
+ x3 = simd_u32x4_wrapping_add(x3, o3);
+ x4 = simd_u32x4_wrapping_add(x4, o4);
+ x5 = simd_u32x4_wrapping_add(x5, o5);
+ x6 = simd_u32x4_wrapping_add(x6, o6);
+ x7 = simd_u32x4_wrapping_add(x7, o7);
+ x8 = simd_u32x4_wrapping_add(x8, o8);
+ x9 = simd_u32x4_wrapping_add(x9, o9);
+ x10 = simd_u32x4_wrapping_add(x10, o10);
+ x11 = simd_u32x4_wrapping_add(x11, o11);
+ x12 = simd_u32x4_wrapping_add(x12, o12);
+ x13 = simd_u32x4_wrapping_add(x13, o13);
+ x14 = simd_u32x4_wrapping_add(x14, o14);
+ x15 = simd_u32x4_wrapping_add(x15, o15);
let words = [
x0.to_array(),
@@ -412,10 +437,9 @@ unsafe fn xor_keystream_u32x4_impl(
block_index = block_index.strict_add(1);
}
- counter = counter.wrapping_add(BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(BLOCKS_PER_BATCH_U32);
}
- let remainder = batches.into_remainder();
if !remainder.is_empty() {
xor_keystream_portable(key, counter, nonce, remainder);
}
@@ -429,7 +453,9 @@ pub(crate) fn hchacha20(key: &[u8; KEY_SIZE], nonce: &[u8; HCHACHA_NONCE_SIZE])
rounds(&mut state);
let mut out = [0u8; KEY_SIZE];
- for (chunk, word) in out.chunks_exact_mut(4).zip([
+ let (chunks, remainder) = out.as_chunks_mut::<4>();
+ debug_assert!(remainder.is_empty());
+ for (chunk, word) in chunks.iter_mut().zip([
state[0], state[1], state[2], state[3], state[12], state[13], state[14], state[15],
]) {
chunk.copy_from_slice(&word.to_le_bytes());
@@ -462,15 +488,22 @@ mod x86_avx512;
#[path = "chacha20/x86_64_ssse3_x4.rs"]
mod x86_ssse3_x4;
-#[cfg(target_arch = "aarch64")]
+#[cfg(all(target_arch = "aarch64", feature = "chacha20poly1305"))]
#[inline]
-pub(crate) fn xor_keystream_aarch64_neon(
+/// Apply the AArch64 NEON ChaCha20 kernel for the interleaved AEAD path.
+///
+/// # Safety
+///
+/// The caller must verify [`aarch64::NEON`](crate::platform::caps::aarch64::NEON) and ensure that the number of
+/// 64-byte blocks in `buffer` does not exhaust the `u32` block counter starting at `initial_counter`.
+pub(super) unsafe fn xor_keystream_aarch64_neon(
key: &[u8; KEY_SIZE],
initial_counter: u32,
nonce: &[u8; NONCE_SIZE],
buffer: &mut [u8],
) {
- aarch64_neon::xor_keystream(key, initial_counter, nonce, buffer);
+ // SAFETY: the caller establishes the NEON capability and counter-range preconditions required by this backend.
+ unsafe { aarch64_neon::xor_keystream(key, initial_counter, nonce, buffer) }
}
// Forced entry points let backend-equivalence tests bypass runtime dispatch.
@@ -490,105 +523,120 @@ pub fn diag_chacha20_xor_keystream_portable(
///
/// # Safety
///
-/// Caller must verify the host has `aarch64::NEON`. Compile-time gated to
-/// `target_arch = "aarch64"`.
+/// Caller must verify the host has `aarch64::NEON` and that `buffer`'s 64-byte block count fits the counter range
+/// starting at `initial_counter`. Compile-time gated to `target_arch = "aarch64"`.
#[cfg(all(feature = "diag", target_arch = "aarch64"))]
-pub fn diag_chacha20_xor_keystream_aarch64_neon(
+pub unsafe fn diag_chacha20_xor_keystream_aarch64_neon(
key: &[u8; KEY_SIZE],
initial_counter: u32,
nonce: &[u8; NONCE_SIZE],
buffer: &mut [u8],
) {
- aarch64_neon::xor_keystream(key, initial_counter, nonce, buffer);
+ // SAFETY: the caller contract is exactly the private backend entry's contract.
+ unsafe { aarch64_neon::xor_keystream(key, initial_counter, nonce, buffer) };
}
/// Run the x86_64 AVX2 ChaCha20 XOR-keystream.
///
/// # Safety
///
-/// Caller must verify the host has `x86::AVX2`.
+/// Caller must verify the host has `x86::AVX2` and that `buffer`'s 64-byte block count fits the counter range starting
+/// at `initial_counter`.
#[cfg(all(feature = "diag", target_arch = "x86_64"))]
-pub fn diag_chacha20_xor_keystream_x86_avx2(
+pub unsafe fn diag_chacha20_xor_keystream_x86_avx2(
key: &[u8; KEY_SIZE],
initial_counter: u32,
nonce: &[u8; NONCE_SIZE],
buffer: &mut [u8],
) {
- x86_avx2::xor_keystream(key, initial_counter, nonce, buffer);
+ // SAFETY: the caller contract is exactly the private backend entry's contract.
+ unsafe { x86_avx2::xor_keystream(key, initial_counter, nonce, buffer) };
}
/// Run the x86_64 AVX-512 ChaCha20 XOR-keystream.
///
/// # Safety
///
-/// Caller must verify the host has `x86::AVX512F + AVX512VL + AVX512BW`.
+/// Caller must verify the host has `x86::AVX512F + AVX512VL + AVX512BW + AVX512DQ` and that `buffer`'s 64-byte block
+/// count fits the counter range starting at `initial_counter`.
#[cfg(all(feature = "diag", target_arch = "x86_64"))]
-pub fn diag_chacha20_xor_keystream_x86_avx512(
+pub unsafe fn diag_chacha20_xor_keystream_x86_avx512(
key: &[u8; KEY_SIZE],
initial_counter: u32,
nonce: &[u8; NONCE_SIZE],
buffer: &mut [u8],
) {
- x86_avx512::xor_keystream(key, initial_counter, nonce, buffer);
+ // SAFETY: the caller contract is exactly the private backend entry's contract.
+ unsafe { x86_avx512::xor_keystream(key, initial_counter, nonce, buffer) };
}
/// Run the POWER VSX ChaCha20 XOR-keystream.
///
/// # Safety
///
-/// Caller must verify the host is `powerpc64le` with VSX. The portable
-/// kernel — which has been the correctness oracle since commit `2631aefa`
-/// fixed the rotation-amount bug here — must produce identical bytes.
+/// Caller must verify the host has `power::POWER8_VECTOR` and that `buffer`'s 64-byte block count fits the counter
+/// range starting at `initial_counter`. The portable kernel — which has been the correctness oracle since commit
+/// `2631aefa` fixed the rotation-amount bug here — must produce identical bytes.
#[cfg(all(feature = "diag", target_arch = "powerpc64", target_endian = "little"))]
-pub fn diag_chacha20_xor_keystream_power_vsx(
+pub unsafe fn diag_chacha20_xor_keystream_power_vsx(
key: &[u8; KEY_SIZE],
initial_counter: u32,
nonce: &[u8; NONCE_SIZE],
buffer: &mut [u8],
) {
- power_vsx::xor_keystream(key, initial_counter, nonce, buffer);
+ // SAFETY: the caller contract is exactly the private backend entry's contract.
+ unsafe { power_vsx::xor_keystream(key, initial_counter, nonce, buffer) };
}
/// Run the s390x z/Vector ChaCha20 XOR-keystream.
///
/// # Safety
///
-/// Caller must verify the host has `s390x::VECTOR`. Same correctness-oracle
-/// invariant as POWER VSX above.
+/// Caller must verify the host has `s390x::VECTOR` and that `buffer`'s 64-byte block count fits the counter range
+/// starting at `initial_counter`. Same correctness-oracle invariant as POWER VSX above.
#[cfg(all(feature = "diag", target_arch = "s390x"))]
-pub fn diag_chacha20_xor_keystream_s390x_vector(
+pub unsafe fn diag_chacha20_xor_keystream_s390x_vector(
key: &[u8; KEY_SIZE],
initial_counter: u32,
nonce: &[u8; NONCE_SIZE],
buffer: &mut [u8],
) {
- s390x_vector::xor_keystream(key, initial_counter, nonce, buffer);
+ // SAFETY: the caller contract is exactly the private backend entry's contract.
+ unsafe { s390x_vector::xor_keystream(key, initial_counter, nonce, buffer) };
}
/// Run the riscv64 RVV ChaCha20 XOR-keystream.
///
/// # Safety
///
-/// Caller must verify the host has `riscv::V`.
+/// Caller must verify the host has `riscv::V` and that `buffer`'s 64-byte block count fits the counter range starting
+/// at `initial_counter`.
#[cfg(all(feature = "diag", target_arch = "riscv64"))]
-pub fn diag_chacha20_xor_keystream_riscv64_vector(
+pub unsafe fn diag_chacha20_xor_keystream_riscv64_vector(
key: &[u8; KEY_SIZE],
initial_counter: u32,
nonce: &[u8; NONCE_SIZE],
buffer: &mut [u8],
) {
- riscv64_vector::xor_keystream(key, initial_counter, nonce, buffer);
+ // SAFETY: the caller contract is exactly the private backend entry's contract.
+ unsafe { riscv64_vector::xor_keystream(key, initial_counter, nonce, buffer) };
}
/// Run the wasm32 simd128 ChaCha20 XOR-keystream.
+///
+/// # Safety
+///
+/// Caller must verify the host has `wasm::SIMD128` and that `buffer`'s 64-byte block count fits the counter range
+/// starting at `initial_counter`.
#[cfg(all(feature = "diag", target_arch = "wasm32"))]
-pub fn diag_chacha20_xor_keystream_wasm_simd128(
+pub unsafe fn diag_chacha20_xor_keystream_wasm_simd128(
key: &[u8; KEY_SIZE],
initial_counter: u32,
nonce: &[u8; NONCE_SIZE],
buffer: &mut [u8],
) {
- wasm_simd128::xor_keystream(key, initial_counter, nonce, buffer);
+ // SAFETY: the caller contract is exactly the private backend entry's contract.
+ unsafe { wasm_simd128::xor_keystream(key, initial_counter, nonce, buffer) };
}
#[cfg(test)]
mod tests {
@@ -611,6 +659,17 @@ mod tests {
use super::xor_keystream_portable;
use super::{KEY_SIZE, NONCE_SIZE, block, xor_keystream};
use crate::aead::targets::AeadPrimitive;
+
+ fn primitive() -> AeadPrimitive {
+ #[cfg(feature = "chacha20poly1305")]
+ {
+ AeadPrimitive::ChaCha20Poly1305
+ }
+ #[cfg(all(not(feature = "chacha20poly1305"), feature = "xchacha20poly1305"))]
+ {
+ AeadPrimitive::XChaCha20Poly1305
+ }
+ }
#[cfg(target_arch = "aarch64")]
use crate::platform::caps::aarch64;
#[cfg(all(target_arch = "powerpc64", target_endian = "little"))]
@@ -662,10 +721,12 @@ mod tests {
let plaintext = *b"chacha20 portable core";
let mut ciphertext = plaintext;
- xor_keystream(AeadPrimitive::ChaCha20Poly1305, &key, 1, &nonce, &mut ciphertext).unwrap();
+ xor_keystream(primitive(), &key, 1, &nonce, &mut ciphertext)
+ .expect("test message must fit the ChaCha20 counter range");
assert_ne!(ciphertext, plaintext);
- xor_keystream(AeadPrimitive::XChaCha20Poly1305, &key, 1, &nonce, &mut ciphertext).unwrap();
+ xor_keystream(primitive(), &key, 1, &nonce, &mut ciphertext)
+ .expect("test message must fit the XChaCha20 counter range");
assert_eq!(ciphertext, plaintext);
}
@@ -675,10 +736,58 @@ mod tests {
let nonce = [0u8; NONCE_SIZE];
let mut one_block = [0u8; 64];
- assert!(xor_keystream(AeadPrimitive::ChaCha20Poly1305, &key, u32::MAX, &nonce, &mut one_block).is_ok());
+ xor_keystream(primitive(), &key, u32::MAX, &nonce, &mut one_block)
+ .expect("one block at the final counter must fit");
let mut two_blocks = [0u8; 65];
- assert!(xor_keystream(AeadPrimitive::ChaCha20Poly1305, &key, u32::MAX, &nonce, &mut two_blocks).is_err());
+ xor_keystream(primitive(), &key, u32::MAX, &nonce, &mut two_blocks)
+ .expect_err("a second block after the final counter must fail");
+ }
+
+ #[cfg(target_arch = "x86_64")]
+ /// Compare one capability-gated x86 backend with the portable authority and preserve both guard regions.
+ ///
+ /// # Safety
+ ///
+ /// The caller must establish `kernel`'s CPU features and ensure that the block count implied by the `len` bytes fits
+ /// the counter range starting at `initial_counter`.
+ unsafe fn assert_x86_backend_matches_portable(
+ backend_name: &str,
+ kernel: unsafe fn(&[u8; KEY_SIZE], u32, &[u8; NONCE_SIZE], &mut [u8]),
+ key: &[u8; KEY_SIZE],
+ nonce: &[u8; NONCE_SIZE],
+ initial_counter: u32,
+ offset: usize,
+ len: usize,
+ ) {
+ const GUARD_LEN: usize = 64;
+
+ let allocation_len = GUARD_LEN.strict_add(offset).strict_add(len).strict_add(GUARD_LEN);
+ let start = GUARD_LEN.strict_add(offset);
+ let mut portable_storage = vec![0xa5; allocation_len];
+ let mut accelerated_storage = portable_storage.clone();
+ {
+ let (_, portable_suffix) = portable_storage.split_at_mut(start);
+ let (portable, _) = portable_suffix.split_at_mut(len);
+ let (_, accelerated_suffix) = accelerated_storage.split_at_mut(start);
+ let (accelerated, _) = accelerated_suffix.split_at_mut(len);
+
+ for (index, (portable_byte, accelerated_byte)) in portable.iter_mut().zip(accelerated.iter_mut()).enumerate() {
+ let [value, ..] = index.strict_mul(29).strict_add(3).to_le_bytes();
+ *portable_byte = value;
+ *accelerated_byte = value;
+ }
+
+ xor_keystream_portable(key, initial_counter, nonce, portable);
+ // SAFETY: the caller established the kernel capability and counter range; `accelerated` is the exact initialized
+ // in-place segment and the surrounding allocation stays live for the call.
+ unsafe { kernel(key, initial_counter, nonce, accelerated) };
+ }
+
+ assert_eq!(
+ accelerated_storage, portable_storage,
+ "{backend_name} mismatch at counter={initial_counter}, offset={offset}, len={len}"
+ );
}
#[test]
@@ -690,22 +799,48 @@ mod tests {
let key = [0x71; KEY_SIZE];
let nonce = [0x19; NONCE_SIZE];
- for len in [
- 0usize, 1, 63, 64, 65, 255, 256, 257, 511, 512, 513, 768, 769, 1023, 1024, 1280, 1281, 1536, 2048, 4096, 8192,
+ for initial_counter in [0, 0x7fff_ffff, 0x8000_0000] {
+ for offset in [0usize, 1, 15, 31, 63] {
+ for len in [
+ 0usize, 1, 63, 64, 65, 255, 256, 257, 511, 512, 513, 768, 769, 1023, 1024, 1279, 1280, 1281, 1536, 2048,
+ 4096, 8192,
+ ] {
+ // SAFETY: the test returned unless AVX512_READY is available; these ordinary/sign-boundary counters leave
+ // room for every selected test length.
+ unsafe {
+ assert_x86_backend_matches_portable(
+ "AVX-512",
+ super::x86_avx512::xor_keystream,
+ &key,
+ &nonce,
+ initial_counter,
+ offset,
+ len,
+ );
+ }
+ }
+ }
+ }
+ for (initial_counter, len) in [
+ (u32::MAX.strict_sub(15), 1024usize),
+ (u32::MAX.strict_sub(3), 256),
+ (u32::MAX, 64),
] {
- let mut portable = vec![0u8; len];
- let mut accelerated = vec![0u8; len];
- let mut index = 0usize;
- while index < len {
- let value = index.strict_mul(13).strict_add(5) as u8;
- portable[index] = value;
- accelerated[index] = value;
- index = index.strict_add(1);
+ for offset in [0usize, 1, 15, 31, 63] {
+ // SAFETY: the test returned unless AVX512_READY is available, and each pair is the exact maximum-valid counter
+ // for the 16-way, four-way, or portable width.
+ unsafe {
+ assert_x86_backend_matches_portable(
+ "AVX-512",
+ super::x86_avx512::xor_keystream,
+ &key,
+ &nonce,
+ initial_counter,
+ offset,
+ len,
+ );
+ }
}
-
- xor_keystream_portable(&key, 3, &nonce, &mut portable);
- super::x86_avx512::xor_keystream(&key, 3, &nonce, &mut accelerated);
- assert_eq!(accelerated, portable, "AVX-512 mismatch at len={len}");
}
}
@@ -718,22 +853,48 @@ mod tests {
let key = [0x55; KEY_SIZE];
let nonce = [0x33; NONCE_SIZE];
- for len in [
- 0usize, 1, 63, 64, 65, 255, 256, 257, 511, 512, 513, 768, 769, 1023, 1024, 1280, 1281, 2048, 4096, 8192,
+ for initial_counter in [0, 0x7fff_ffff, 0x8000_0000] {
+ for offset in [0usize, 1, 15, 31, 63] {
+ for len in [
+ 0usize, 1, 63, 64, 65, 255, 256, 257, 511, 512, 513, 767, 768, 769, 1023, 1024, 1279, 1280, 1281, 2048, 4096,
+ 8192,
+ ] {
+ // SAFETY: the test returned unless AVX2 is available; these ordinary/sign-boundary counters leave room for
+ // every selected test length.
+ unsafe {
+ assert_x86_backend_matches_portable(
+ "AVX2",
+ super::x86_avx2::xor_keystream,
+ &key,
+ &nonce,
+ initial_counter,
+ offset,
+ len,
+ );
+ }
+ }
+ }
+ }
+ for (initial_counter, len) in [
+ (u32::MAX.strict_sub(7), 512usize),
+ (u32::MAX.strict_sub(3), 256),
+ (u32::MAX, 64),
] {
- let mut portable = vec![0u8; len];
- let mut accelerated = vec![0u8; len];
- let mut index = 0usize;
- while index < len {
- let value = index.strict_mul(17).strict_add(9) as u8;
- portable[index] = value;
- accelerated[index] = value;
- index = index.strict_add(1);
+ for offset in [0usize, 1, 15, 31, 63] {
+ // SAFETY: the test returned unless AVX2 is available, and each pair is the exact maximum-valid counter for
+ // the eight-way, four-way, or portable width.
+ unsafe {
+ assert_x86_backend_matches_portable(
+ "AVX2",
+ super::x86_avx2::xor_keystream,
+ &key,
+ &nonce,
+ initial_counter,
+ offset,
+ len,
+ );
+ }
}
-
- xor_keystream_portable(&key, 7, &nonce, &mut portable);
- super::x86_avx2::xor_keystream(&key, 7, &nonce, &mut accelerated);
- assert_eq!(accelerated, portable, "AVX2 mismatch at len={len}");
}
}
@@ -746,20 +907,32 @@ mod tests {
let key = [0x66; KEY_SIZE];
let nonce = [0x11; NONCE_SIZE];
- for len in [0usize, 1, 63, 64, 65, 127, 128, 129, 255, 256, 257, 768] {
- let mut portable = vec![0u8; len];
- let mut accelerated = vec![0u8; len];
- let mut index = 0usize;
- while index < len {
- let value = index.strict_mul(29).strict_add(3) as u8;
- portable[index] = value;
- accelerated[index] = value;
- index = index.strict_add(1);
+ for initial_counter in [0, 11, u32::MAX.strict_sub(11)] {
+ for offset in 0usize..16 {
+ for len in [0usize, 1, 63, 64, 65, 127, 128, 129, 255, 256, 257, 511, 512, 513, 768] {
+ let allocation_len = offset.strict_add(len);
+ let mut portable_storage = vec![0u8; allocation_len];
+ let mut accelerated_storage = vec![0u8; allocation_len];
+ let (_, portable) = portable_storage.split_at_mut(offset);
+ let (_, accelerated) = accelerated_storage.split_at_mut(offset);
+ let mut index = 0usize;
+ while index < len {
+ let [value, ..] = index.strict_mul(29).strict_add(3).to_le_bytes();
+ portable[index] = value;
+ accelerated[index] = value;
+ index = index.strict_add(1);
+ }
+
+ xor_keystream_portable(&key, initial_counter, &nonce, portable);
+ // SAFETY: the test capability guard proves NEON, and the selected maximum counter leaves room for every
+ // selected length.
+ unsafe { super::aarch64_neon::xor_keystream(&key, initial_counter, &nonce, accelerated) };
+ assert_eq!(
+ accelerated, portable,
+ "NEON mismatch at counter={initial_counter}, offset={offset}, len={len}"
+ );
+ }
}
-
- xor_keystream_portable(&key, 11, &nonce, &mut portable);
- super::aarch64_neon::xor_keystream(&key, 11, &nonce, &mut accelerated);
- assert_eq!(accelerated, portable);
}
}
@@ -784,7 +957,8 @@ mod tests {
}
xor_keystream_portable(&key, 5, &nonce, &mut portable);
- super::power_vsx::xor_keystream(&key, 5, &nonce, &mut accelerated);
+ // SAFETY: the test capability guard proves POWER8 vector support, and counter 5 leaves room for every length.
+ unsafe { super::power_vsx::xor_keystream(&key, 5, &nonce, &mut accelerated) };
assert_eq!(accelerated, portable, "POWER VSX mismatch at len={len}");
}
}
@@ -810,7 +984,8 @@ mod tests {
}
xor_keystream_portable(&key, 9, &nonce, &mut portable);
- super::s390x_vector::xor_keystream(&key, 9, &nonce, &mut accelerated);
+ // SAFETY: the test capability guard proves z/Vector support, and counter 9 leaves room for every length.
+ unsafe { super::s390x_vector::xor_keystream(&key, 9, &nonce, &mut accelerated) };
assert_eq!(accelerated, portable, "s390x vector mismatch at len={len}");
}
}
diff --git a/src/aead/chacha20/aarch64_neon.rs b/src/aead/chacha20/aarch64_neon.rs
index de857391..1c23e37e 100644
--- a/src/aead/chacha20/aarch64_neon.rs
+++ b/src/aead/chacha20/aarch64_neon.rs
@@ -1,21 +1,37 @@
use core::arch::aarch64::{
- uint32x4_t, vaddq_u32, vcombine_u32, vdupq_n_u32, veorq_u32, vget_high_u32, vget_low_u32, vld1q_u32,
- vreinterpretq_u16_u32, vreinterpretq_u32_u16, vrev32q_u16, vshrq_n_u32, vsliq_n_u32, vst1q_u32, vzip1q_u32,
- vzip2q_u32,
+ uint32x4_t, vaddq_u32, vcombine_u32, vdupq_n_u32, veorq_u32, vget_high_u32, vget_low_u32, vld1q_u8, vld1q_u32,
+ vreinterpretq_u8_u32, vreinterpretq_u16_u32, vreinterpretq_u32_u8, vreinterpretq_u32_u16, vrev32q_u16, vshrq_n_u32,
+ vsliq_n_u32, vst1q_u8, vzip1q_u32, vzip2q_u32,
};
use super::{BLOCK_SIZE, KEY_SIZE, NONCE_SIZE, load_u32_le, xor_keystream_portable};
const BLOCKS_PER_BATCH: usize = 4;
-
+const COUNTERS_PER_BATCH: u32 = 4;
+const COUNTERS_PER_DOUBLE_BATCH: u32 = 8;
+
+/// Generate and XOR a ChaCha20 stream with the NEON kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that NEON is available and that `buffer`'s 64-byte block count fits the counter range
+/// starting at `initial_counter`.
#[inline]
-pub(super) fn xor_keystream(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
- // SAFETY: Backend selection guarantees NEON is available before this wrapper is chosen because:
- // 1. Runtime dispatch selects this module only after `aarch64::NEON` is present.
- // 2. `xor_keystream_impl` is annotated with `#[target_feature(enable = "neon")]`.
+pub(super) unsafe fn xor_keystream(
+ key: &[u8; KEY_SIZE],
+ initial_counter: u32,
+ nonce: &[u8; NONCE_SIZE],
+ buffer: &mut [u8],
+) {
+ // SAFETY: Production validates the counter range and detects `aarch64::NEON`; direct test and diagnostic callers
+ // establish the same conditions. `xor_keystream_impl` enables NEON.
unsafe { xor_keystream_impl(key, initial_counter, nonce, buffer) }
}
+/// # Safety
+///
+/// The caller must ensure that NEON is available and that `buffer`'s 64-byte block count fits the counter range
+/// starting at `initial_counter`.
#[target_feature(enable = "neon")]
unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
let c0 = vdupq_n_u32(0x6170_7865);
@@ -35,9 +51,9 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
let n15 = vdupq_n_u32(load_u32_le(&nonce[8..12]));
let mut counter = initial_counter;
- let mut double_batches = buffer.chunks_exact_mut(BLOCK_SIZE * BLOCKS_PER_BATCH * 2);
- for chunk in &mut double_batches {
- debug_assert!(counter.checked_add((BLOCKS_PER_BATCH * 2 - 1) as u32).is_some());
+ let (double_batches, double_remainder) = buffer.as_chunks_mut::<{ BLOCK_SIZE * BLOCKS_PER_BATCH * 2 }>();
+ for chunk in double_batches {
+ debug_assert!(counter.checked_add(COUNTERS_PER_DOUBLE_BATCH.strict_sub(1)).is_some());
let mut x0 = c0;
let mut x1 = c1;
@@ -159,7 +175,7 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
let ptr = chunk.as_mut_ptr();
// SAFETY: vector transpose and XOR stores because:
- // 1. `chunk` is exactly eight ChaCha20 blocks from `chunks_exact_mut`.
+ // 1. `chunk` is an array of exactly eight ChaCha20 blocks.
// 2. The first four-block group starts at `ptr`; the second starts at `ptr + 256`.
// 3. NEON is guaranteed by the enclosing `#[target_feature(enable = "neon")]`.
unsafe {
@@ -175,14 +191,12 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
xor_store_word_group(hi, 12, y12, y13, y14, y15);
}
- counter = counter.wrapping_add((BLOCKS_PER_BATCH * 2) as u32);
+ counter = counter.wrapping_add(COUNTERS_PER_DOUBLE_BATCH);
}
- let mut batches = double_batches
- .into_remainder()
- .chunks_exact_mut(BLOCK_SIZE * BLOCKS_PER_BATCH);
- for chunk in &mut batches {
- debug_assert!(counter.checked_add((BLOCKS_PER_BATCH - 1) as u32).is_some());
+ let (batches, remainder) = double_remainder.as_chunks_mut::<{ BLOCK_SIZE * BLOCKS_PER_BATCH }>();
+ for chunk in batches {
+ debug_assert!(counter.checked_add(COUNTERS_PER_BATCH.strict_sub(1)).is_some());
let mut x0 = c0;
let mut x1 = c1;
@@ -269,7 +283,7 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
let ptr = chunk.as_mut_ptr();
// SAFETY: vector transpose and XOR stores because:
- // 1. `chunk` is exactly `BLOCKS_PER_BATCH * BLOCK_SIZE` bytes from `chunks_exact_mut`.
+ // 1. `chunk` is an array of exactly `BLOCKS_PER_BATCH * BLOCK_SIZE` bytes.
// 2. Each call stores four 16-byte word groups at offsets inside the 256-byte chunk.
// 3. NEON is guaranteed by the enclosing `#[target_feature(enable = "neon")]`.
unsafe {
@@ -279,16 +293,20 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
xor_store_word_group(ptr, 12, x12, x13, x14, x15);
}
- counter = counter.wrapping_add(BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(COUNTERS_PER_BATCH);
}
- let remainder = batches.into_remainder();
if !remainder.is_empty() {
xor_keystream_portable(key, counter, nonce, remainder);
}
}
/// Transpose four word-major ChaCha vectors into four block-major 16-byte word groups.
+///
+/// # Safety
+///
+/// The caller must ensure NEON is available, `chunk` is valid for exclusive writes across four complete ChaCha20
+/// blocks, and `word_start` is one of 0, 4, 8, or 12.
#[inline(always)]
unsafe fn xor_store_word_group(
chunk: *mut u8,
@@ -320,18 +338,25 @@ unsafe fn xor_store_word_group(
}
}
+/// XOR one 16-byte word group into a block of a four-block ChaCha20 batch.
+///
+/// # Safety
+///
+/// The caller must ensure NEON is available, `chunk` is valid for exclusive writes across four complete ChaCha20
+/// blocks, `block_index < 4`, and `word_start <= 12`.
#[inline(always)]
unsafe fn xor_store_block_words(chunk: *mut u8, block_index: usize, word_start: usize, keystream: uint32x4_t) {
// SAFETY: in-place 16-byte XOR/store because:
// 1. Caller guarantees `chunk` points to a full four-block chunk.
// 2. `block_index < 4` and `word_start <= 12`, so `block_index * 64 + word_start * 4 + 16` stays
// within the 256-byte chunk.
- // 3. `vld1q_u32`/`vst1q_u32` support unaligned addresses and the pointer does not escape.
+ // 3. The byte-typed NEON load/store accepts every address alignment and the pointer does not escape.
unsafe {
let offset = block_index.strict_mul(BLOCK_SIZE).strict_add(word_start.strict_mul(4));
- let ptr = chunk.add(offset).cast::();
- let plaintext = vld1q_u32(ptr);
- vst1q_u32(ptr, veorq_u32(plaintext, keystream));
+ let ptr = chunk.add(offset);
+ let plaintext = vreinterpretq_u32_u8(vld1q_u8(ptr));
+ let ciphertext = veorq_u32(plaintext, keystream);
+ vst1q_u8(ptr, vreinterpretq_u8_u32(ciphertext));
}
}
diff --git a/src/aead/chacha20/powerpc64_vsx.rs b/src/aead/chacha20/powerpc64_vsx.rs
index 151c2b95..52544af7 100644
--- a/src/aead/chacha20/powerpc64_vsx.rs
+++ b/src/aead/chacha20/powerpc64_vsx.rs
@@ -3,10 +3,23 @@ use core::simd::i64x2;
use super::{BLOCK_SIZE, KEY_SIZE, NONCE_SIZE, load_u32_le, xor_keystream_portable};
const BLOCKS_PER_BATCH: usize = 4;
+const COUNTERS_PER_BATCH: u32 = 4;
+/// Generate and XOR a ChaCha20 stream with the POWER8 vector kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that POWER8 vector support is available and that `buffer`'s 64-byte block count fits the
+/// counter range starting at `initial_counter`.
#[inline]
-pub(super) fn xor_keystream(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
- // SAFETY: Backend selection guarantees POWER vector support before this wrapper is chosen.
+pub(super) unsafe fn xor_keystream(
+ key: &[u8; KEY_SIZE],
+ initial_counter: u32,
+ nonce: &[u8; NONCE_SIZE],
+ buffer: &mut [u8],
+) {
+ // SAFETY: Production validates the counter range and detects `power::POWER8_VECTOR`; direct test and diagnostic
+ // callers establish the same conditions.
unsafe { xor_keystream_impl(key, initial_counter, nonce, buffer) }
}
@@ -42,14 +55,20 @@ impl RotShifts {
}
}
+/// Generate and XOR a ChaCha20 stream in four-block POWER8 vector batches.
+///
+/// # Safety
+///
+/// The caller must ensure that POWER8 vector support is available and that `buffer`'s 64-byte block count fits the
+/// counter range starting at `initial_counter`.
#[target_feature(enable = "altivec", enable = "vsx", enable = "power8-vector")]
unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
let rot = RotShifts::new();
let mut counter = initial_counter;
- let mut batches = buffer.chunks_exact_mut(BLOCK_SIZE * BLOCKS_PER_BATCH);
- for chunk in &mut batches {
- debug_assert!(counter.checked_add((BLOCKS_PER_BATCH - 1) as u32).is_some());
+ let (batches, remainder) = buffer.as_chunks_mut::<{ BLOCK_SIZE * BLOCKS_PER_BATCH }>();
+ for chunk in batches {
+ debug_assert!(counter.checked_add(COUNTERS_PER_BATCH.strict_sub(1)).is_some());
let mut x0 = splat(0x6170_7865);
let mut x1 = splat(0x3320_646e);
@@ -162,10 +181,9 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
block_index = block_index.strict_add(1);
}
- counter = counter.wrapping_add(BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(COUNTERS_PER_BATCH);
}
- let remainder = batches.into_remainder();
if !remainder.is_empty() {
xor_keystream_portable(key, counter, nonce, remainder);
}
@@ -189,6 +207,10 @@ fn splat(val: u32) -> i64x2 {
}
/// Vector add unsigned word modulo: `vadduwm`.
+///
+/// # Safety
+///
+/// The executing CPU must support POWER8 vector instructions.
#[inline(always)]
unsafe fn vadduwm(a: i64x2, b: i64x2) -> i64x2 {
let out: i64x2;
@@ -206,6 +228,10 @@ unsafe fn vadduwm(a: i64x2, b: i64x2) -> i64x2 {
}
/// Vector XOR: `vxor`.
+///
+/// # Safety
+///
+/// The executing CPU must support AltiVec.
#[inline(always)]
unsafe fn vxor(a: i64x2, b: i64x2) -> i64x2 {
let out: i64x2;
@@ -223,6 +249,10 @@ unsafe fn vxor(a: i64x2, b: i64x2) -> i64x2 {
}
/// Vector rotate left word: `vrlw`.
+///
+/// # Safety
+///
+/// The executing CPU must support AltiVec.
#[inline(always)]
unsafe fn vrlw(value: i64x2, shift: i64x2) -> i64x2 {
let out: i64x2;
@@ -239,6 +269,11 @@ unsafe fn vrlw(value: i64x2, shift: i64x2) -> i64x2 {
out
}
+/// Apply one vectorized ChaCha20 quarter round.
+///
+/// # Safety
+///
+/// The executing CPU must support POWER8 vector instructions.
#[inline(always)]
unsafe fn quarter_round(a: &mut i64x2, b: &mut i64x2, c: &mut i64x2, d: &mut i64x2, rot: &RotShifts) {
// SAFETY: POWER8+ VSX available via enclosing target_feature.
diff --git a/src/aead/chacha20/riscv64_vector.rs b/src/aead/chacha20/riscv64_vector.rs
index 77516044..2a7376d6 100644
--- a/src/aead/chacha20/riscv64_vector.rs
+++ b/src/aead/chacha20/riscv64_vector.rs
@@ -1,15 +1,18 @@
use super::{KEY_SIZE, NONCE_SIZE, xor_keystream_u32x4_impl};
-define_target_feature_forwarder! {
- pub(super) fn xor_keystream(
- key: &[u8; KEY_SIZE],
- initial_counter: u32,
- nonce: &[u8; NONCE_SIZE],
- buffer: &mut [u8]
- ) {
- feature = "v";
- outer_safety = "backend selection guarantees the vector extension before this wrapper is chosen.";
- inner_safety = "the wrapper only reaches this function when the RISC-V vector extension is available.";
- call = xor_keystream_u32x4_impl(key, initial_counter, nonce, buffer);
- }
+/// Generate and XOR a ChaCha20 stream with the RISC-V vector kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that the RISC-V vector extension is available and that `buffer`'s 64-byte block count fits
+/// the counter range starting at `initial_counter`.
+#[inline]
+pub(super) unsafe fn xor_keystream(
+ key: &[u8; KEY_SIZE],
+ initial_counter: u32,
+ nonce: &[u8; NONCE_SIZE],
+ buffer: &mut [u8],
+) {
+ // SAFETY: the caller established the vector capability and counter range required by the shared implementation.
+ unsafe { xor_keystream_u32x4_impl(key, initial_counter, nonce, buffer) }
}
diff --git a/src/aead/chacha20/s390x_vector.rs b/src/aead/chacha20/s390x_vector.rs
index 1f82eab7..5cb78603 100644
--- a/src/aead/chacha20/s390x_vector.rs
+++ b/src/aead/chacha20/s390x_vector.rs
@@ -3,19 +3,38 @@ use core::simd::i64x2;
use super::{BLOCK_SIZE, KEY_SIZE, NONCE_SIZE, load_u32_le, xor_keystream_portable};
const BLOCKS_PER_BATCH: usize = 4;
+const COUNTERS_PER_BATCH: u32 = 4;
+/// Generate and XOR a ChaCha20 stream with the z/Vector kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that the z/Vector facility is available and that `buffer`'s 64-byte block count fits the
+/// counter range starting at `initial_counter`.
#[inline]
-pub(super) fn xor_keystream(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
- // SAFETY: Backend selection guarantees the z/Vector facility before this wrapper is chosen.
+pub(super) unsafe fn xor_keystream(
+ key: &[u8; KEY_SIZE],
+ initial_counter: u32,
+ nonce: &[u8; NONCE_SIZE],
+ buffer: &mut [u8],
+) {
+ // SAFETY: Production validates the counter range and detects `s390x::VECTOR`; direct test and diagnostic callers
+ // establish the same conditions.
unsafe { xor_keystream_impl(key, initial_counter, nonce, buffer) }
}
+/// Generate and XOR a ChaCha20 stream in four-block z/Vector batches.
+///
+/// # Safety
+///
+/// The caller must ensure that the z/Vector facility is available and that `buffer`'s 64-byte block count fits the
+/// counter range starting at `initial_counter`.
#[target_feature(enable = "vector")]
unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
let mut counter = initial_counter;
- let mut batches = buffer.chunks_exact_mut(BLOCK_SIZE * BLOCKS_PER_BATCH);
- for chunk in &mut batches {
- debug_assert!(counter.checked_add((BLOCKS_PER_BATCH - 1) as u32).is_some());
+ let (batches, remainder) = buffer.as_chunks_mut::<{ BLOCK_SIZE * BLOCKS_PER_BATCH }>();
+ for chunk in batches {
+ debug_assert!(counter.checked_add(COUNTERS_PER_BATCH.strict_sub(1)).is_some());
let mut x0 = splat(0x6170_7865);
let mut x1 = splat(0x3320_646e);
@@ -128,10 +147,9 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
block_index = block_index.strict_add(1);
}
- counter = counter.wrapping_add(BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(COUNTERS_PER_BATCH);
}
- let remainder = batches.into_remainder();
if !remainder.is_empty() {
xor_keystream_portable(key, counter, nonce, remainder);
}
@@ -155,6 +173,10 @@ fn splat(val: u32) -> i64x2 {
}
/// Vector add fullword: `vaf`.
+///
+/// # Safety
+///
+/// The executing CPU must support the vector facility.
#[inline]
#[target_feature(enable = "vector")]
unsafe fn vaf(a: i64x2, b: i64x2) -> i64x2 {
@@ -173,6 +195,10 @@ unsafe fn vaf(a: i64x2, b: i64x2) -> i64x2 {
}
/// Vector exclusive OR: `vx`.
+///
+/// # Safety
+///
+/// The executing CPU must support the vector facility.
#[inline]
#[target_feature(enable = "vector")]
unsafe fn vx(a: i64x2, b: i64x2) -> i64x2 {
@@ -194,6 +220,10 @@ unsafe fn vx(a: i64x2, b: i64x2) -> i64x2 {
///
/// ChaCha20 quarter rounds rotate LEFT by 16, 12, 8, and 7 bits, and `verll`
/// already rotates LEFT. The immediate maps directly to the ChaCha constant.
+///
+/// # Safety
+///
+/// The executing CPU must support the vector facility.
#[inline]
#[target_feature(enable = "vector")]
unsafe fn rotl32_via_verll(a: i64x2) -> i64x2 {
@@ -215,6 +245,11 @@ unsafe fn rotl32_via_verll(a: i64x2) -> i64x2 {
out
}
+/// Apply one vectorized ChaCha20 quarter round.
+///
+/// # Safety
+///
+/// The executing CPU must support the vector facility.
#[inline]
#[target_feature(enable = "vector")]
unsafe fn quarter_round(a: &mut i64x2, b: &mut i64x2, c: &mut i64x2, d: &mut i64x2) {
diff --git a/src/aead/chacha20/wasm32_simd128.rs b/src/aead/chacha20/wasm32_simd128.rs
index 128e6290..aa853f1b 100644
--- a/src/aead/chacha20/wasm32_simd128.rs
+++ b/src/aead/chacha20/wasm32_simd128.rs
@@ -4,18 +4,36 @@ use super::{BLOCK_SIZE, KEY_SIZE, NONCE_SIZE, load_u32_le, xor_keystream_portabl
const BLOCKS_PER_BATCH: usize = 4;
+/// Generate and XOR a ChaCha20 stream with the wasm SIMD128 kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that SIMD128 is available and that `buffer`'s 64-byte block count fits the counter range
+/// starting at `initial_counter`.
#[inline]
-pub(super) fn xor_keystream(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
- // SAFETY: Backend selection guarantees simd128 is available before this wrapper is chosen.
+pub(super) unsafe fn xor_keystream(
+ key: &[u8; KEY_SIZE],
+ initial_counter: u32,
+ nonce: &[u8; NONCE_SIZE],
+ buffer: &mut [u8],
+) {
+ // SAFETY: Production validates the counter range and selects this wrapper only when simd128 is available; direct
+ // test and diagnostic callers establish the same conditions.
unsafe { xor_keystream_impl(key, initial_counter, nonce, buffer) }
}
+/// Generate and XOR a ChaCha20 stream in four-block SIMD128 batches.
+///
+/// # Safety
+///
+/// The caller must ensure that SIMD128 is available and that `buffer`'s 64-byte block count fits the counter range
+/// starting at `initial_counter`.
#[target_feature(enable = "simd128")]
unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
let mut counter = initial_counter;
let mut batches = buffer.chunks_exact_mut(BLOCK_SIZE * BLOCKS_PER_BATCH);
for chunk in &mut batches {
- debug_assert!(counter.checked_add((BLOCKS_PER_BATCH - 1) as u32).is_some());
+ debug_assert!(counter.checked_add(3).is_some());
let mut x0 = u32x4_splat(0x6170_7865);
let mut x1 = u32x4_splat(0x3320_646e);
@@ -88,26 +106,24 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
x14 = u32x4_add(x14, o14);
x15 = u32x4_add(x15, o15);
- let mut words = [[0u32; BLOCKS_PER_BATCH]; 16];
- // SAFETY: each destination is a valid four-lane `u32` array for one unaligned `v128` store.
- unsafe {
- v128_store(words[0].as_mut_ptr() as *mut v128, x0);
- v128_store(words[1].as_mut_ptr() as *mut v128, x1);
- v128_store(words[2].as_mut_ptr() as *mut v128, x2);
- v128_store(words[3].as_mut_ptr() as *mut v128, x3);
- v128_store(words[4].as_mut_ptr() as *mut v128, x4);
- v128_store(words[5].as_mut_ptr() as *mut v128, x5);
- v128_store(words[6].as_mut_ptr() as *mut v128, x6);
- v128_store(words[7].as_mut_ptr() as *mut v128, x7);
- v128_store(words[8].as_mut_ptr() as *mut v128, x8);
- v128_store(words[9].as_mut_ptr() as *mut v128, x9);
- v128_store(words[10].as_mut_ptr() as *mut v128, x10);
- v128_store(words[11].as_mut_ptr() as *mut v128, x11);
- v128_store(words[12].as_mut_ptr() as *mut v128, x12);
- v128_store(words[13].as_mut_ptr() as *mut v128, x13);
- v128_store(words[14].as_mut_ptr() as *mut v128, x14);
- v128_store(words[15].as_mut_ptr() as *mut v128, x15);
- }
+ let words = [
+ lanes(x0),
+ lanes(x1),
+ lanes(x2),
+ lanes(x3),
+ lanes(x4),
+ lanes(x5),
+ lanes(x6),
+ lanes(x7),
+ lanes(x8),
+ lanes(x9),
+ lanes(x10),
+ lanes(x11),
+ lanes(x12),
+ lanes(x13),
+ lanes(x14),
+ lanes(x15),
+ ];
let mut block_index = 0usize;
while block_index < BLOCKS_PER_BATCH {
@@ -124,7 +140,7 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
block_index = block_index.strict_add(1);
}
- counter = counter.wrapping_add(BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(4);
}
let remainder = batches.into_remainder();
@@ -133,6 +149,16 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
}
}
+#[inline(always)]
+fn lanes(value: v128) -> [u32; BLOCKS_PER_BATCH] {
+ [
+ u32x4_extract_lane::<0>(value),
+ u32x4_extract_lane::<1>(value),
+ u32x4_extract_lane::<2>(value),
+ u32x4_extract_lane::<3>(value),
+ ]
+}
+
#[inline(always)]
fn quarter_round(a: &mut v128, b: &mut v128, c: &mut v128, d: &mut v128) {
*a = u32x4_add(*a, *b);
diff --git a/src/aead/chacha20/x86_64_avx2.rs b/src/aead/chacha20/x86_64_avx2.rs
index 5165bce0..a6752bfe 100644
--- a/src/aead/chacha20/x86_64_avx2.rs
+++ b/src/aead/chacha20/x86_64_avx2.rs
@@ -7,13 +7,32 @@ use core::arch::x86_64::{
use super::{BLOCK_SIZE, KEY_SIZE, NONCE_SIZE, load_u32_le, x86_ssse3_x4, xor_keystream_portable};
const BLOCKS_PER_BATCH: usize = 8;
+const COUNTERS_PER_BATCH: u32 = 8;
+/// Generate and XOR a ChaCha20 stream with the AVX2 kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that AVX2 is available and that `buffer`'s 64-byte block count fits the counter range
+/// starting at `initial_counter`.
#[inline]
-pub(super) fn xor_keystream(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
- // SAFETY: Backend selection guarantees AVX2 is available before this wrapper is chosen.
+pub(super) unsafe fn xor_keystream(
+ key: &[u8; KEY_SIZE],
+ initial_counter: u32,
+ nonce: &[u8; NONCE_SIZE],
+ buffer: &mut [u8],
+) {
+ // SAFETY: Production validates the counter range and detects AVX2; direct test and diagnostic callers establish the
+ // same conditions.
unsafe { xor_keystream_impl(key, initial_counter, nonce, buffer) }
}
+/// Generate and XOR a ChaCha20 stream with the AVX2 eight-way kernel and four-way/portable tails.
+///
+/// # Safety
+///
+/// The caller must ensure that AVX2 is available and that the number of 64-byte blocks in `buffer` does not exhaust
+/// the 32-bit block counter starting at `initial_counter`.
#[target_feature(enable = "avx2")]
unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
// vpshufb masks for byte-aligned rotations (16-bit and 8-bit).
@@ -25,35 +44,35 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
);
let mut counter = initial_counter;
- let mut batches = buffer.chunks_exact_mut(BLOCK_SIZE * BLOCKS_PER_BATCH);
- for chunk in &mut batches {
- debug_assert!(counter.checked_add((BLOCKS_PER_BATCH - 1) as u32).is_some());
-
- let mut x0 = _mm256_set1_epi32(0x6170_7865u32 as i32);
- let mut x1 = _mm256_set1_epi32(0x3320_646eu32 as i32);
- let mut x2 = _mm256_set1_epi32(0x7962_2d32u32 as i32);
- let mut x3 = _mm256_set1_epi32(0x6b20_6574u32 as i32);
- let mut x4 = _mm256_set1_epi32(load_u32_le(&key[0..4]) as i32);
- let mut x5 = _mm256_set1_epi32(load_u32_le(&key[4..8]) as i32);
- let mut x6 = _mm256_set1_epi32(load_u32_le(&key[8..12]) as i32);
- let mut x7 = _mm256_set1_epi32(load_u32_le(&key[12..16]) as i32);
- let mut x8 = _mm256_set1_epi32(load_u32_le(&key[16..20]) as i32);
- let mut x9 = _mm256_set1_epi32(load_u32_le(&key[20..24]) as i32);
- let mut x10 = _mm256_set1_epi32(load_u32_le(&key[24..28]) as i32);
- let mut x11 = _mm256_set1_epi32(load_u32_le(&key[28..32]) as i32);
+ let (batches, remainder) = buffer.as_chunks_mut::<{ BLOCK_SIZE * BLOCKS_PER_BATCH }>();
+ for chunk in batches {
+ debug_assert!(counter.checked_add(COUNTERS_PER_BATCH.strict_sub(1)).is_some());
+
+ let mut x0 = _mm256_set1_epi32(0x6170_7865u32.cast_signed());
+ let mut x1 = _mm256_set1_epi32(0x3320_646eu32.cast_signed());
+ let mut x2 = _mm256_set1_epi32(0x7962_2d32u32.cast_signed());
+ let mut x3 = _mm256_set1_epi32(0x6b20_6574u32.cast_signed());
+ let mut x4 = _mm256_set1_epi32(load_u32_le(&key[0..4]).cast_signed());
+ let mut x5 = _mm256_set1_epi32(load_u32_le(&key[4..8]).cast_signed());
+ let mut x6 = _mm256_set1_epi32(load_u32_le(&key[8..12]).cast_signed());
+ let mut x7 = _mm256_set1_epi32(load_u32_le(&key[12..16]).cast_signed());
+ let mut x8 = _mm256_set1_epi32(load_u32_le(&key[16..20]).cast_signed());
+ let mut x9 = _mm256_set1_epi32(load_u32_le(&key[20..24]).cast_signed());
+ let mut x10 = _mm256_set1_epi32(load_u32_le(&key[24..28]).cast_signed());
+ let mut x11 = _mm256_set1_epi32(load_u32_le(&key[28..32]).cast_signed());
let mut x12 = _mm256_setr_epi32(
- counter as i32,
- counter.wrapping_add(1) as i32,
- counter.wrapping_add(2) as i32,
- counter.wrapping_add(3) as i32,
- counter.wrapping_add(4) as i32,
- counter.wrapping_add(5) as i32,
- counter.wrapping_add(6) as i32,
- counter.wrapping_add(7) as i32,
+ counter.cast_signed(),
+ counter.wrapping_add(1).cast_signed(),
+ counter.wrapping_add(2).cast_signed(),
+ counter.wrapping_add(3).cast_signed(),
+ counter.wrapping_add(4).cast_signed(),
+ counter.wrapping_add(5).cast_signed(),
+ counter.wrapping_add(6).cast_signed(),
+ counter.wrapping_add(7).cast_signed(),
);
- let mut x13 = _mm256_set1_epi32(load_u32_le(&nonce[0..4]) as i32);
- let mut x14 = _mm256_set1_epi32(load_u32_le(&nonce[4..8]) as i32);
- let mut x15 = _mm256_set1_epi32(load_u32_le(&nonce[8..12]) as i32);
+ let mut x13 = _mm256_set1_epi32(load_u32_le(&nonce[0..4]).cast_signed());
+ let mut x14 = _mm256_set1_epi32(load_u32_le(&nonce[4..8]).cast_signed());
+ let mut x15 = _mm256_set1_epi32(load_u32_le(&nonce[8..12]).cast_signed());
let o0 = x0;
let o1 = x1;
@@ -108,7 +127,6 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
// to block-major (each pair of YMM registers = one 64-byte block).
//
// Stage 1: 32-bit interleave.
- // SAFETY: AVX2 intrinsics are valid under the enclosing target_feature.
let s1_0 = _mm256_unpacklo_epi32(x0, x1);
let s1_1 = _mm256_unpackhi_epi32(x0, x1);
let s1_2 = _mm256_unpacklo_epi32(x2, x3);
@@ -162,19 +180,17 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
xor_block_pair(ptr, 3, 7, s2_3, s2_7, s2_11, s2_15);
}
- counter = counter.wrapping_add(BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(COUNTERS_PER_BATCH);
}
- let remainder = batches.into_remainder();
- let mut x4_batches = remainder.chunks_exact_mut(BLOCK_SIZE * x86_ssse3_x4::BLOCKS_PER_BATCH);
- for chunk in &mut x4_batches {
+ let (x4_batches, remainder) = remainder.as_chunks_mut::<{ BLOCK_SIZE * x86_ssse3_x4::BLOCKS_PER_BATCH }>();
+ for chunk in x4_batches {
// SAFETY: AVX2-capable CPUs provide the SSSE3 instructions used by the
// 4-block tail kernel, and `chunk` is exactly 4 ChaCha20 blocks.
unsafe { x86_ssse3_x4::xor_blocks(key, counter, nonce, chunk) };
- counter = counter.wrapping_add(x86_ssse3_x4::BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(x86_ssse3_x4::COUNTERS_PER_BATCH);
}
- let remainder = x4_batches.into_remainder();
if !remainder.is_empty() {
xor_keystream_portable(key, counter, nonce, remainder);
}
@@ -182,6 +198,11 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
/// Permute stage-2 results into two complete blocks (lo_idx and hi_idx) and
/// XOR+store them in-place. Each block is 64 bytes = 2 × YMM.
+///
+/// # Safety
+///
+/// The caller must ensure that AVX2 is available, `buf` is valid for exclusive access to 512 initialized bytes,
+/// and `lo_idx` and `hi_idx` are distinct values below eight.
#[inline(always)]
unsafe fn xor_block_pair(
buf: *mut u8,
diff --git a/src/aead/chacha20/x86_64_avx512.rs b/src/aead/chacha20/x86_64_avx512.rs
index 8f8c49a7..5fc5dbd2 100644
--- a/src/aead/chacha20/x86_64_avx512.rs
+++ b/src/aead/chacha20/x86_64_avx512.rs
@@ -7,53 +7,72 @@ use core::arch::x86_64::{
use super::{BLOCK_SIZE, KEY_SIZE, NONCE_SIZE, load_u32_le, x86_ssse3_x4, xor_keystream_portable};
const BLOCKS_PER_BATCH: usize = 16;
+const COUNTERS_PER_BATCH: u32 = 16;
+/// Generate and XOR a ChaCha20 stream with the AVX-512 kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that AVX512F, AVX512VL, AVX512BW, and AVX512DQ are available and that `buffer`'s 64-byte
+/// block count fits the counter range starting at `initial_counter`.
#[inline]
-pub(super) fn xor_keystream(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
- // SAFETY: Backend selection guarantees the AVX-512 feature set required by this kernel.
+pub(super) unsafe fn xor_keystream(
+ key: &[u8; KEY_SIZE],
+ initial_counter: u32,
+ nonce: &[u8; NONCE_SIZE],
+ buffer: &mut [u8],
+) {
+ // SAFETY: Production validates the counter range and detects AVX512F+VL+BW+DQ; direct test and diagnostic callers
+ // establish the same conditions.
unsafe { xor_keystream_impl(key, initial_counter, nonce, buffer) }
}
+/// Generate and XOR a ChaCha20 stream with the AVX-512 16-way kernel and four-way/portable tails.
+///
+/// # Safety
+///
+/// The caller must ensure that AVX512F, AVX512VL, AVX512BW, and AVX512DQ are available and that the number of
+/// 64-byte blocks in `buffer` does not exhaust the 32-bit block counter starting at `initial_counter`.
#[target_feature(enable = "avx512f,avx512vl,avx512bw,avx512dq")]
unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], buffer: &mut [u8]) {
let mut counter = initial_counter;
- let mut batches = buffer.chunks_exact_mut(BLOCK_SIZE * BLOCKS_PER_BATCH);
- for chunk in &mut batches {
- debug_assert!(counter.checked_add((BLOCKS_PER_BATCH - 1) as u32).is_some());
+ let (batches, remainder) = buffer.as_chunks_mut::<{ BLOCK_SIZE * BLOCKS_PER_BATCH }>();
+ for chunk in batches {
+ debug_assert!(counter.checked_add(COUNTERS_PER_BATCH.strict_sub(1)).is_some());
- let mut x0 = _mm512_set1_epi32(0x6170_7865u32 as i32);
- let mut x1 = _mm512_set1_epi32(0x3320_646eu32 as i32);
- let mut x2 = _mm512_set1_epi32(0x7962_2d32u32 as i32);
- let mut x3 = _mm512_set1_epi32(0x6b20_6574u32 as i32);
- let mut x4 = _mm512_set1_epi32(load_u32_le(&key[0..4]) as i32);
- let mut x5 = _mm512_set1_epi32(load_u32_le(&key[4..8]) as i32);
- let mut x6 = _mm512_set1_epi32(load_u32_le(&key[8..12]) as i32);
- let mut x7 = _mm512_set1_epi32(load_u32_le(&key[12..16]) as i32);
- let mut x8 = _mm512_set1_epi32(load_u32_le(&key[16..20]) as i32);
- let mut x9 = _mm512_set1_epi32(load_u32_le(&key[20..24]) as i32);
- let mut x10 = _mm512_set1_epi32(load_u32_le(&key[24..28]) as i32);
- let mut x11 = _mm512_set1_epi32(load_u32_le(&key[28..32]) as i32);
+ let mut x0 = _mm512_set1_epi32(0x6170_7865u32.cast_signed());
+ let mut x1 = _mm512_set1_epi32(0x3320_646eu32.cast_signed());
+ let mut x2 = _mm512_set1_epi32(0x7962_2d32u32.cast_signed());
+ let mut x3 = _mm512_set1_epi32(0x6b20_6574u32.cast_signed());
+ let mut x4 = _mm512_set1_epi32(load_u32_le(&key[0..4]).cast_signed());
+ let mut x5 = _mm512_set1_epi32(load_u32_le(&key[4..8]).cast_signed());
+ let mut x6 = _mm512_set1_epi32(load_u32_le(&key[8..12]).cast_signed());
+ let mut x7 = _mm512_set1_epi32(load_u32_le(&key[12..16]).cast_signed());
+ let mut x8 = _mm512_set1_epi32(load_u32_le(&key[16..20]).cast_signed());
+ let mut x9 = _mm512_set1_epi32(load_u32_le(&key[20..24]).cast_signed());
+ let mut x10 = _mm512_set1_epi32(load_u32_le(&key[24..28]).cast_signed());
+ let mut x11 = _mm512_set1_epi32(load_u32_le(&key[28..32]).cast_signed());
let mut x12 = _mm512_setr_epi32(
- counter as i32,
- counter.wrapping_add(1) as i32,
- counter.wrapping_add(2) as i32,
- counter.wrapping_add(3) as i32,
- counter.wrapping_add(4) as i32,
- counter.wrapping_add(5) as i32,
- counter.wrapping_add(6) as i32,
- counter.wrapping_add(7) as i32,
- counter.wrapping_add(8) as i32,
- counter.wrapping_add(9) as i32,
- counter.wrapping_add(10) as i32,
- counter.wrapping_add(11) as i32,
- counter.wrapping_add(12) as i32,
- counter.wrapping_add(13) as i32,
- counter.wrapping_add(14) as i32,
- counter.wrapping_add(15) as i32,
+ counter.cast_signed(),
+ counter.wrapping_add(1).cast_signed(),
+ counter.wrapping_add(2).cast_signed(),
+ counter.wrapping_add(3).cast_signed(),
+ counter.wrapping_add(4).cast_signed(),
+ counter.wrapping_add(5).cast_signed(),
+ counter.wrapping_add(6).cast_signed(),
+ counter.wrapping_add(7).cast_signed(),
+ counter.wrapping_add(8).cast_signed(),
+ counter.wrapping_add(9).cast_signed(),
+ counter.wrapping_add(10).cast_signed(),
+ counter.wrapping_add(11).cast_signed(),
+ counter.wrapping_add(12).cast_signed(),
+ counter.wrapping_add(13).cast_signed(),
+ counter.wrapping_add(14).cast_signed(),
+ counter.wrapping_add(15).cast_signed(),
);
- let mut x13 = _mm512_set1_epi32(load_u32_le(&nonce[0..4]) as i32);
- let mut x14 = _mm512_set1_epi32(load_u32_le(&nonce[4..8]) as i32);
- let mut x15 = _mm512_set1_epi32(load_u32_le(&nonce[8..12]) as i32);
+ let mut x13 = _mm512_set1_epi32(load_u32_le(&nonce[0..4]).cast_signed());
+ let mut x14 = _mm512_set1_epi32(load_u32_le(&nonce[4..8]).cast_signed());
+ let mut x15 = _mm512_set1_epi32(load_u32_le(&nonce[8..12]).cast_signed());
let o0 = x0;
let o1 = x1;
@@ -108,7 +127,6 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
// 16 blocks) to block-major (each register = one complete 64-byte block).
//
// Stage 1: 32-bit interleave — pairwise unpack adjacent state-word registers.
- // SAFETY: AVX-512 intrinsics are valid under the enclosing target_feature.
let s1_0 = _mm512_unpacklo_epi32(x0, x1);
let s1_1 = _mm512_unpackhi_epi32(x0, x1);
let s1_2 = _mm512_unpacklo_epi32(x2, x3);
@@ -215,30 +233,35 @@ unsafe fn xor_keystream_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce:
xor_block(ptr, 15, blk15);
}
- counter = counter.wrapping_add(BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(COUNTERS_PER_BATCH);
}
- let remainder = batches.into_remainder();
- let mut x4_batches = remainder.chunks_exact_mut(BLOCK_SIZE * x86_ssse3_x4::BLOCKS_PER_BATCH);
- for chunk in &mut x4_batches {
+ let (x4_batches, remainder) = remainder.as_chunks_mut::<{ BLOCK_SIZE * x86_ssse3_x4::BLOCKS_PER_BATCH }>();
+ for chunk in x4_batches {
// SAFETY: AVX-512-ready CPUs provide the SSSE3 instructions used by the
// 4-block tail kernel, and `chunk` is exactly 4 ChaCha20 blocks.
unsafe { x86_ssse3_x4::xor_blocks(key, counter, nonce, chunk) };
- counter = counter.wrapping_add(x86_ssse3_x4::BLOCKS_PER_BATCH as u32);
+ counter = counter.wrapping_add(x86_ssse3_x4::COUNTERS_PER_BATCH);
}
- let remainder = x4_batches.into_remainder();
if !remainder.is_empty() {
xor_keystream_portable(key, counter, nonce, remainder);
}
}
/// Load 64 bytes of plaintext at block offset `idx`, XOR with keystream block, store.
+///
+/// # Safety
+///
+/// The caller must ensure that AVX512F is available, `buf` is valid for exclusive access to 1,024 initialized bytes,
+/// and `idx` is less than 16. The buffer need not be 64-byte aligned.
#[inline(always)]
unsafe fn xor_block(buf: *mut u8, idx: usize, keystream: __m512i) {
// SAFETY: caller guarantees `buf` points to a 1024-byte chunk and `idx < 16`.
unsafe {
- let p = buf.add(idx.strict_mul(BLOCK_SIZE)).cast::<__m512i>();
+ let p = core::ptr::NonNull::new_unchecked(buf.add(idx.strict_mul(BLOCK_SIZE)))
+ .cast::<__m512i>()
+ .as_ptr();
let plaintext = _mm512_loadu_si512(p);
_mm512_storeu_si512(p, _mm512_xor_si512(plaintext, keystream));
}
diff --git a/src/aead/chacha20/x86_64_ssse3_x4.rs b/src/aead/chacha20/x86_64_ssse3_x4.rs
index 7ac1cce2..acfb8b9e 100644
--- a/src/aead/chacha20/x86_64_ssse3_x4.rs
+++ b/src/aead/chacha20/x86_64_ssse3_x4.rs
@@ -7,7 +7,14 @@ use core::arch::x86_64::{
use super::{BLOCK_SIZE, KEY_SIZE, NONCE_SIZE, load_u32_le};
pub(super) const BLOCKS_PER_BATCH: usize = 4;
-
+pub(super) const COUNTERS_PER_BATCH: u32 = 4;
+
+/// XOR four consecutive ChaCha20 blocks with the SSSE3 four-way kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that SSSE3 and AVX are available, `chunk` is exactly four blocks, and
+/// `initial_counter + 3` fits in `u32`.
#[inline]
pub(super) unsafe fn xor_blocks(
key: &[u8; KEY_SIZE],
@@ -16,37 +23,45 @@ pub(super) unsafe fn xor_blocks(
chunk: &mut [u8],
) {
debug_assert_eq!(chunk.len(), BLOCK_SIZE * BLOCKS_PER_BATCH);
+ debug_assert!(initial_counter.checked_add(COUNTERS_PER_BATCH.strict_sub(1)).is_some());
// SAFETY: callers only reach this helper from x86 SIMD backends whose
- // dispatch gates guarantee AVX/SSSE3-capable hardware.
+ // dispatch or forced-backend contracts guarantee AVX/SSSE3-capable hardware; the assertions above restate the
+ // exact block and counter bounds.
unsafe { xor_blocks_impl(key, initial_counter, nonce, chunk) }
}
+/// Generate and XOR four consecutive ChaCha20 blocks.
+///
+/// # Safety
+///
+/// The caller must ensure that SSSE3 and AVX are available, `chunk` is exactly four blocks, and
+/// `initial_counter + 3` fits in `u32`.
#[target_feature(enable = "ssse3,avx")]
unsafe fn xor_blocks_impl(key: &[u8; KEY_SIZE], initial_counter: u32, nonce: &[u8; NONCE_SIZE], chunk: &mut [u8]) {
let rot16 = _mm_set_epi8(13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2);
let rot8 = _mm_set_epi8(14, 13, 12, 15, 10, 9, 8, 11, 6, 5, 4, 7, 2, 1, 0, 3);
- let mut x0 = _mm_set1_epi32(0x6170_7865u32 as i32);
- let mut x1 = _mm_set1_epi32(0x3320_646eu32 as i32);
- let mut x2 = _mm_set1_epi32(0x7962_2d32u32 as i32);
- let mut x3 = _mm_set1_epi32(0x6b20_6574u32 as i32);
- let mut x4 = _mm_set1_epi32(load_u32_le(&key[0..4]) as i32);
- let mut x5 = _mm_set1_epi32(load_u32_le(&key[4..8]) as i32);
- let mut x6 = _mm_set1_epi32(load_u32_le(&key[8..12]) as i32);
- let mut x7 = _mm_set1_epi32(load_u32_le(&key[12..16]) as i32);
- let mut x8 = _mm_set1_epi32(load_u32_le(&key[16..20]) as i32);
- let mut x9 = _mm_set1_epi32(load_u32_le(&key[20..24]) as i32);
- let mut x10 = _mm_set1_epi32(load_u32_le(&key[24..28]) as i32);
- let mut x11 = _mm_set1_epi32(load_u32_le(&key[28..32]) as i32);
+ let mut x0 = _mm_set1_epi32(0x6170_7865u32.cast_signed());
+ let mut x1 = _mm_set1_epi32(0x3320_646eu32.cast_signed());
+ let mut x2 = _mm_set1_epi32(0x7962_2d32u32.cast_signed());
+ let mut x3 = _mm_set1_epi32(0x6b20_6574u32.cast_signed());
+ let mut x4 = _mm_set1_epi32(load_u32_le(&key[0..4]).cast_signed());
+ let mut x5 = _mm_set1_epi32(load_u32_le(&key[4..8]).cast_signed());
+ let mut x6 = _mm_set1_epi32(load_u32_le(&key[8..12]).cast_signed());
+ let mut x7 = _mm_set1_epi32(load_u32_le(&key[12..16]).cast_signed());
+ let mut x8 = _mm_set1_epi32(load_u32_le(&key[16..20]).cast_signed());
+ let mut x9 = _mm_set1_epi32(load_u32_le(&key[20..24]).cast_signed());
+ let mut x10 = _mm_set1_epi32(load_u32_le(&key[24..28]).cast_signed());
+ let mut x11 = _mm_set1_epi32(load_u32_le(&key[28..32]).cast_signed());
let mut x12 = _mm_setr_epi32(
- initial_counter as i32,
- initial_counter.wrapping_add(1) as i32,
- initial_counter.wrapping_add(2) as i32,
- initial_counter.wrapping_add(3) as i32,
+ initial_counter.cast_signed(),
+ initial_counter.wrapping_add(1).cast_signed(),
+ initial_counter.wrapping_add(2).cast_signed(),
+ initial_counter.wrapping_add(3).cast_signed(),
);
- let mut x13 = _mm_set1_epi32(load_u32_le(&nonce[0..4]) as i32);
- let mut x14 = _mm_set1_epi32(load_u32_le(&nonce[4..8]) as i32);
- let mut x15 = _mm_set1_epi32(load_u32_le(&nonce[8..12]) as i32);
+ let mut x13 = _mm_set1_epi32(load_u32_le(&nonce[0..4]).cast_signed());
+ let mut x14 = _mm_set1_epi32(load_u32_le(&nonce[4..8]).cast_signed());
+ let mut x15 = _mm_set1_epi32(load_u32_le(&nonce[8..12]).cast_signed());
let o0 = x0;
let o1 = x1;
@@ -129,6 +144,11 @@ fn transpose_words(w0: __m128i, w1: __m128i, w2: __m128i, w3: __m128i) -> [__m12
}
}
+/// XOR one generated ChaCha20 block into a selected block of a four-block buffer.
+///
+/// # Safety
+///
+/// `buf` must be valid for exclusive access to 256 initialized bytes, and `idx` must be less than four.
#[inline(always)]
unsafe fn xor_block(buf: *mut u8, idx: usize, w03: __m128i, w47: __m128i, w811: __m128i, w1215: __m128i) {
// SAFETY: caller guarantees `buf` points to four full ChaCha20 blocks and
@@ -142,6 +162,11 @@ unsafe fn xor_block(buf: *mut u8, idx: usize, w03: __m128i, w47: __m128i, w811:
}
}
+/// XOR 16 keystream bytes into an in-place buffer segment.
+///
+/// # Safety
+///
+/// `ptr` must be valid for reading and exclusively writing 16 initialized bytes. It need not be aligned.
#[inline(always)]
unsafe fn xor_store(ptr: *mut u8, keystream: __m128i) {
// SAFETY: caller guarantees `ptr..ptr+16` is in bounds for unaligned access.
diff --git a/src/aead/chacha20poly1305.rs b/src/aead/chacha20poly1305.rs
index 0309998f..7faecdd4 100644
--- a/src/aead/chacha20poly1305.rs
+++ b/src/aead/chacha20poly1305.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::indexing_slicing)]
-
//! ChaCha20-Poly1305 public AEAD surface.
use core::fmt;
@@ -20,7 +18,9 @@ const POWER_SHORT_FAST_MAX: usize = chacha20::BLOCK_SIZE;
#[cfg(target_arch = "aarch64")]
const AARCH64_INTERLEAVED_MIN: usize = 1024;
#[cfg(target_arch = "aarch64")]
-const AARCH64_INTERLEAVED_CHUNK: usize = 1024 * 1024;
+const AARCH64_INTERLEAVED_BLOCKS: u32 = 16 * 1024;
+#[cfg(target_arch = "aarch64")]
+const AARCH64_INTERLEAVED_CHUNK: usize = (AARCH64_INTERLEAVED_BLOCKS as usize) * chacha20::BLOCK_SIZE;
#[cfg(all(
target_arch = "x86_64",
target_os = "linux",
@@ -323,7 +323,9 @@ impl ChaCha20Poly1305 {
return None;
}
- let tag = x86_64_asm::seal_in_place(self.key.as_bytes(), nonce.as_bytes(), aad, buffer);
+ // SAFETY: the diagnostic entry validated the ChaCha20 length bound; the guards above prove a nonempty buffer and
+ // AVX2+BMI2 availability.
+ let tag = unsafe { x86_64_asm::seal_in_place(self.key.as_bytes(), nonce.as_bytes(), aad, buffer) };
Some(Ok(ChaCha20Poly1305Tag::from_bytes(tag)))
}
@@ -348,7 +350,9 @@ impl ChaCha20Poly1305 {
return None;
}
- let expected = x86_64_asm::open_in_place(self.key.as_bytes(), nonce.as_bytes(), aad, buffer);
+ // SAFETY: the diagnostic entry validated the ChaCha20 length bound; the guards above prove a nonempty buffer and
+ // AVX2+BMI2 availability.
+ let expected = unsafe { x86_64_asm::open_in_place(self.key.as_bytes(), nonce.as_bytes(), aad, buffer) };
if !ct::fixed_eq(&expected, tag.as_bytes()).declassify() {
ct::zeroize(buffer);
return Some(Err(OpenError::verification()));
@@ -377,7 +381,9 @@ impl ChaCha20Poly1305 {
return None;
}
- let tag = x86_64_asm::seal_in_place(self.key.as_bytes(), nonce.as_bytes(), aad, buffer);
+ // SAFETY: the public AEAD entry validated the ChaCha20 length bound, and the recommendation gate proves a
+ // nonempty buffer plus AVX2+BMI2 availability.
+ let tag = unsafe { x86_64_asm::seal_in_place(self.key.as_bytes(), nonce.as_bytes(), aad, buffer) };
Some(Ok(ChaCha20Poly1305Tag::from_bytes(tag)))
}
@@ -403,7 +409,9 @@ impl ChaCha20Poly1305 {
return None;
}
- let expected = x86_64_asm::open_in_place(self.key.as_bytes(), nonce.as_bytes(), aad, buffer);
+ // SAFETY: the public AEAD entry validated the ChaCha20 length bound, and the recommendation gate proves a
+ // nonempty buffer plus AVX2+BMI2 availability.
+ let expected = unsafe { x86_64_asm::open_in_place(self.key.as_bytes(), nonce.as_bytes(), aad, buffer) };
if !ct::fixed_eq(&expected, tag.as_bytes()).declassify() {
ct::zeroize(buffer);
return Some(Err(OpenError::verification()));
@@ -488,16 +496,21 @@ impl ChaCha20Poly1305 {
authenticator.update_padded_segment(aad);
let mut counter = 1u32;
- let mut chunks = buffer.chunks_exact_mut(AARCH64_INTERLEAVED_CHUNK);
- for chunk in &mut chunks {
- chacha20::xor_keystream_aarch64_neon(self.key.as_bytes(), counter, nonce.as_bytes(), chunk);
+ let (chunks, remainder) = buffer.as_chunks_mut::();
+ for chunk in chunks {
+ // SAFETY: the capability gate proves NEON. Every caller reaches this private helper after the public or
+ // diagnostic length bound, or from a same-module test with a bounded buffer; the fixed-size chunks and exact
+ // counter advance preserve that whole-buffer bound for this segment.
+ unsafe { chacha20::xor_keystream_aarch64_neon(self.key.as_bytes(), counter, nonce.as_bytes(), chunk) };
authenticator.update_padded_segment(chunk);
- counter = counter.wrapping_add((AARCH64_INTERLEAVED_CHUNK / chacha20::BLOCK_SIZE) as u32);
+ counter = counter.wrapping_add(AARCH64_INTERLEAVED_BLOCKS);
}
- let remainder = chunks.into_remainder();
if !remainder.is_empty() {
- chacha20::xor_keystream_aarch64_neon(self.key.as_bytes(), counter, nonce.as_bytes(), remainder);
+ // SAFETY: the capability gate proves NEON. Every caller reaches this private helper after the public or
+ // diagnostic length bound, or from a same-module test with a bounded buffer; `counter` tracks the preceding
+ // full chunks exactly, so the final remainder cannot exhaust the counter range.
+ unsafe { chacha20::xor_keystream_aarch64_neon(self.key.as_bytes(), counter, nonce.as_bytes(), remainder) };
authenticator.update_padded_segment(remainder);
}
@@ -539,17 +552,22 @@ impl ChaCha20Poly1305 {
authenticator.update_padded_segment(aad);
let mut counter = 1u32;
- let mut chunks = buffer.chunks_exact_mut(AARCH64_INTERLEAVED_CHUNK);
- for chunk in &mut chunks {
+ let (chunks, remainder) = buffer.as_chunks_mut::();
+ for chunk in chunks {
authenticator.update_padded_segment(chunk);
- chacha20::xor_keystream_aarch64_neon(self.key.as_bytes(), counter, nonce.as_bytes(), chunk);
- counter = counter.wrapping_add((AARCH64_INTERLEAVED_CHUNK / chacha20::BLOCK_SIZE) as u32);
+ // SAFETY: the capability gate proves NEON. Every caller reaches this private helper after the public or
+ // diagnostic length bound, or from a same-module test with a bounded buffer; the fixed-size chunks and exact
+ // counter advance preserve that whole-buffer bound for this segment.
+ unsafe { chacha20::xor_keystream_aarch64_neon(self.key.as_bytes(), counter, nonce.as_bytes(), chunk) };
+ counter = counter.wrapping_add(AARCH64_INTERLEAVED_BLOCKS);
}
- let remainder = chunks.into_remainder();
if !remainder.is_empty() {
authenticator.update_padded_segment(remainder);
- chacha20::xor_keystream_aarch64_neon(self.key.as_bytes(), counter, nonce.as_bytes(), remainder);
+ // SAFETY: the capability gate proves NEON. Every caller reaches this private helper after the public or
+ // diagnostic length bound, or from a same-module test with a bounded buffer; `counter` tracks the preceding
+ // full chunks exactly, so the final remainder cannot exhaust the counter range.
+ unsafe { chacha20::xor_keystream_aarch64_neon(self.key.as_bytes(), counter, nonce.as_bytes(), remainder) };
}
let expected = authenticator.finalize(lengths);
@@ -622,6 +640,10 @@ impl ChaCha20Poly1305 {
}
}
+/// Encrypts in place without using a platform-specific integrated ChaCha20-Poly1305 assembly entrypoint.
+///
+/// Lower-level ChaCha20 and Poly1305 dispatch remains enabled. Returns an error when the input lengths exceed the
+/// supported limits.
#[cfg(feature = "diag")]
pub fn diag_chacha20poly1305_encrypt_in_place_owned(
cipher: &ChaCha20Poly1305,
@@ -634,6 +656,9 @@ pub fn diag_chacha20poly1305_encrypt_in_place_owned(
}
#[cfg(all(feature = "diag", target_arch = "x86_64", target_os = "linux"))]
+/// Encrypts in place through the Linux x86-64 assembly entrypoint when that entrypoint is available.
+///
+/// Returns `None` when the current CPU cannot execute the assembly backend.
pub fn diag_chacha20poly1305_encrypt_in_place_x86_64_asm(
cipher: &ChaCha20Poly1305,
nonce: &Nonce96,
@@ -648,6 +673,9 @@ pub fn diag_chacha20poly1305_encrypt_in_place_x86_64_asm(
}
#[cfg(all(feature = "diag", target_arch = "x86_64", target_os = "linux"))]
+/// Authenticates and decrypts in place through the Linux x86-64 assembly entrypoint when it is available.
+///
+/// Returns `None` when the current CPU cannot execute the assembly backend.
pub fn diag_chacha20poly1305_decrypt_in_place_x86_64_asm(
cipher: &ChaCha20Poly1305,
nonce: &Nonce96,
@@ -662,6 +690,11 @@ pub fn diag_chacha20poly1305_decrypt_in_place_x86_64_asm(
cipher.decrypt_in_place_asm_x86_64_forced(nonce, aad, buffer, tag)
}
+/// Authenticates and decrypts in place without using a platform-specific integrated ChaCha20-Poly1305 assembly
+/// entrypoint.
+///
+/// Lower-level ChaCha20 and Poly1305 dispatch remains enabled. Authentication failure zeroes `buffer` and returns an
+/// opaque verification error; unsupported input lengths also return an error.
#[cfg(feature = "diag")]
pub fn diag_chacha20poly1305_decrypt_in_place_owned(
cipher: &ChaCha20Poly1305,
@@ -803,8 +836,12 @@ mod tests {
let cipher = ChaCha20Poly1305::new(&key);
let mut buf = *b"hello chacha";
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
- cipher.decrypt_in_place(&nonce, b"aad", &mut buf, &tag).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("valid input must encrypt");
+ cipher
+ .decrypt_in_place(&nonce, b"aad", &mut buf, &tag)
+ .expect("matching nonce, AAD, and tag must decrypt");
assert_eq!(&buf, b"hello chacha");
}
@@ -815,7 +852,9 @@ mod tests {
let cipher = ChaCha20Poly1305::new(&key);
let mut buf = *b"nonce test";
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("valid input must encrypt");
let wrong_nonce = Nonce96::from_bytes([0x08u8; 12]);
let result = cipher.decrypt_in_place(&wrong_nonce, b"aad", &mut buf, &tag);
@@ -829,7 +868,9 @@ mod tests {
let cipher = ChaCha20Poly1305::new(&key);
let mut buf = *b"zero me on failure";
- let tag = cipher.encrypt_in_place(&nonce, b"aad", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"aad", &mut buf)
+ .expect("valid input must encrypt");
let mut bad_tag = tag.to_bytes();
bad_tag[0] ^= 0xFF;
@@ -900,14 +941,15 @@ mod tests {
let cipher = ChaCha20Poly1305::new(&key);
for aad_len in [0usize, 1, 14, 15, 16, 17, 31, 32, 33, 63, 64] {
- let aad = (0..aad_len)
- .map(|index| 0xa7u8.wrapping_add((index as u8).wrapping_mul(7)))
+ let aad = (0u8..)
+ .take(aad_len)
+ .map(|index| 0xa7u8.wrapping_add(index.wrapping_mul(7)))
.collect::>();
let mut ciphertext = Vec::new();
let expected_tag = cipher
.encrypt_in_place_owned_unchecked(&nonce, &aad, &mut ciphertext)
- .unwrap();
+ .expect("owned path must produce an empty-text tag");
let actual = cipher.decrypt_empty_text_fast(&nonce, &aad, &expected_tag);
if aad_len > SMALL_AAD_FAST_MAX {
@@ -918,9 +960,10 @@ mod tests {
continue;
}
- actual
- .expect("empty decrypt fast path must apply inside its configured gate")
- .unwrap();
+ assert_eq!(
+ actual.expect("empty decrypt fast path must apply inside its configured gate"),
+ Ok(())
+ );
let mut bad_tag = expected_tag.to_bytes();
bad_tag[0] ^= 0x80;
@@ -947,21 +990,26 @@ mod tests {
for plaintext_len in [
1usize, 15, 16, 17, 31, 32, 33, 63, 64, 65, 127, 128, 129, 255, 256, 257, 1024,
] {
- let plaintext = (0..plaintext_len)
- .map(|index| 0x51u8.wrapping_add((index as u8).wrapping_mul(13)))
+ let plaintext = (0u8..=u8::MAX)
+ .cycle()
+ .take(plaintext_len)
+ .map(|index| 0x51u8.wrapping_add(index.wrapping_mul(13)))
.collect::>();
for aad_len in [0usize, 1, 13, 14, 15, 16, 17, 31, 32, 63, 64] {
- let aad = (0..aad_len)
- .map(|index| 0xa7u8.wrapping_add((index as u8).wrapping_mul(7)))
+ let aad = (0u8..)
+ .take(aad_len)
+ .map(|index| 0xa7u8.wrapping_add(index.wrapping_mul(7)))
.collect::>();
let mut ciphertext = plaintext.clone();
let tag = cipher
.encrypt_in_place_owned_unchecked(&nonce, &aad, &mut ciphertext)
- .unwrap();
+ .expect("bounded test input must encrypt through the owned path");
let mut actual = ciphertext.clone();
- let actual_tag = x86_64_asm::open_in_place(key.as_bytes(), nonce.as_bytes(), &aad, &mut actual);
+ // SAFETY: the test returned unless AVX2+BMI2 are available; every selected ciphertext is nonempty and well
+ // below ChaCha20's 2^32-block limit.
+ let actual_tag = unsafe { x86_64_asm::open_in_place(key.as_bytes(), nonce.as_bytes(), &aad, &mut actual) };
assert_eq!(
actual, plaintext,
@@ -984,13 +1032,15 @@ mod tests {
let cipher = ChaCha20Poly1305::new(&key);
for plaintext_len in [0usize, 1, 15, 16, 17, 31, 32, 33, 63, 64, 65] {
- let plaintext = (0..plaintext_len)
- .map(|index| 0x51u8.wrapping_add((index as u8).wrapping_mul(13)))
+ let plaintext = (0u8..)
+ .take(plaintext_len)
+ .map(|index| 0x51u8.wrapping_add(index.wrapping_mul(13)))
.collect::>();
for aad_len in [0usize, 1, 14, 15, 16, 17, 31, 32, 33, 63, 64] {
- let aad = (0..aad_len)
- .map(|index| 0xa7u8.wrapping_add((index as u8).wrapping_mul(7)))
+ let aad = (0u8..)
+ .take(aad_len)
+ .map(|index| 0xa7u8.wrapping_add(index.wrapping_mul(7)))
.collect::>();
let mut actual = plaintext.clone();
@@ -1008,10 +1058,10 @@ mod tests {
let mut expected = plaintext.clone();
let expected_tag = cipher
.encrypt_in_place_owned_unchecked(&nonce, &aad, &mut expected)
- .unwrap();
+ .expect("bounded test input must encrypt through the owned path");
let actual_tag = actual_tag
.expect("Power short fast path must apply inside its configured gate")
- .unwrap();
+ .expect("bounded test input must encrypt through the Power fast path");
assert_eq!(
actual, expected,
@@ -1033,19 +1083,21 @@ mod tests {
let cipher = ChaCha20Poly1305::new(&key);
for plaintext_len in [0usize, 1, 15, 16, 17, 31, 32, 33, 63, 64, 65] {
- let plaintext = (0..plaintext_len)
- .map(|index| 0x51u8.wrapping_add((index as u8).wrapping_mul(13)))
+ let plaintext = (0u8..)
+ .take(plaintext_len)
+ .map(|index| 0x51u8.wrapping_add(index.wrapping_mul(13)))
.collect::>();
for aad_len in [0usize, 1, 14, 15, 16, 17, 31, 32, 33, 63, 64] {
- let aad = (0..aad_len)
- .map(|index| 0xa7u8.wrapping_add((index as u8).wrapping_mul(7)))
+ let aad = (0u8..)
+ .take(aad_len)
+ .map(|index| 0xa7u8.wrapping_add(index.wrapping_mul(7)))
.collect::>();
let mut ciphertext = plaintext.clone();
let tag = cipher
.encrypt_in_place_owned_unchecked(&nonce, &aad, &mut ciphertext)
- .unwrap();
+ .expect("bounded test input must encrypt through the owned path");
let mut actual = ciphertext.clone();
let actual_result = cipher.decrypt_short_text_power_fast(&nonce, &aad, &mut actual, &tag);
@@ -1059,9 +1111,10 @@ mod tests {
continue;
}
- actual_result
- .expect("Power short decrypt fast path must apply inside its configured gate")
- .unwrap();
+ assert_eq!(
+ actual_result.expect("Power short decrypt fast path must apply inside its configured gate"),
+ Ok(())
+ );
assert_eq!(
actual, plaintext,
"Power short plaintext mismatch plaintext_len={plaintext_len} aad_len={aad_len}"
@@ -1091,7 +1144,9 @@ mod tests {
let cipher = ChaCha20Poly1305::new(&key);
let mut buf = *b"aad test";
- let tag = cipher.encrypt_in_place(&nonce, b"correct", &mut buf).unwrap();
+ let tag = cipher
+ .encrypt_in_place(&nonce, b"correct", &mut buf)
+ .expect("valid input must encrypt");
let result = cipher.decrypt_in_place(&nonce, b"wrong", &mut buf, &tag);
assert!(result.is_err());
diff --git a/src/aead/chacha20poly1305/aarch64_asm.rs b/src/aead/chacha20poly1305/aarch64_asm.rs
index c2d1efc5..85b7471e 100644
--- a/src/aead/chacha20poly1305/aarch64_asm.rs
+++ b/src/aead/chacha20poly1305/aarch64_asm.rs
@@ -4,8 +4,6 @@
//! ChaCha20-Poly1305 assembly. This Rust module owns the ABI boundary and
//! keeps runtime feature dispatch in the parent AEAD implementation.
-#![allow(unsafe_code)]
-
use core::{arch::global_asm, mem};
use super::KEY_SIZE;
@@ -120,7 +118,10 @@ pub(super) fn seal_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
// empty slices because the length is zero.
// 4. `extra_ciphertext` points at 16 initialized bytes and `extra_ciphertext_len` is zero, matching
// the AWS-LC seal ABI for callers without extra trailing ciphertext.
- // 5. `data` is 16-byte aligned and matches the assembly input/output union layout.
+ // 5. `data` is 16-byte aligned and matches the assembly input/output union layout; the assembly initializes all
+ // 16 bytes of `data.out.tag` before the union field is read.
+ // 6. After that write, the tag replaces the first 16 copied key bytes; the second half remains initialized key
+ // material and is volatile-zeroed before the stack allocation expires.
unsafe {
#[cfg(target_os = "macos")]
rscrypto_chacha20_poly1305_seal_aarch64_apple_darwin(
@@ -129,7 +130,7 @@ pub(super) fn seal_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
buffer.len(),
aad.as_ptr(),
aad.len(),
- &mut data,
+ core::ptr::from_mut(&mut data),
);
#[cfg(target_os = "linux")]
rscrypto_chacha20_poly1305_seal_aarch64(
@@ -138,8 +139,9 @@ pub(super) fn seal_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
buffer.len(),
aad.as_ptr(),
aad.len(),
- &mut data,
+ core::ptr::from_mut(&mut data),
);
+ crate::traits::ct::zeroize(&mut data.input.key[16..]);
data.out.tag
}
}
@@ -161,7 +163,10 @@ pub(super) fn open_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
// the assembly routine supports in-place open, matching the AWS-LC ABI.
// 3. `aad.as_ptr()` is valid for `aad.len()` bytes, including the conventional dangling pointer for
// empty slices because the length is zero.
- // 4. `data` is 16-byte aligned and matches the assembly input/output union layout.
+ // 4. `data` is 16-byte aligned and matches the assembly input/output union layout; the assembly initializes all
+ // 16 bytes of `data.out.tag` before the union field is read.
+ // 5. After that write, the tag replaces the first 16 copied key bytes; the second half remains initialized key
+ // material and is volatile-zeroed before the stack allocation expires.
unsafe {
#[cfg(target_os = "macos")]
rscrypto_chacha20_poly1305_open_aarch64_apple_darwin(
@@ -170,7 +175,7 @@ pub(super) fn open_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
buffer.len(),
aad.as_ptr(),
aad.len(),
- &mut data,
+ core::ptr::from_mut(&mut data),
);
#[cfg(target_os = "linux")]
rscrypto_chacha20_poly1305_open_aarch64(
@@ -179,8 +184,9 @@ pub(super) fn open_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
buffer.len(),
aad.as_ptr(),
aad.len(),
- &mut data,
+ core::ptr::from_mut(&mut data),
);
+ crate::traits::ct::zeroize(&mut data.input.key[16..]);
data.out.tag
}
}
diff --git a/src/aead/chacha20poly1305/x86_64_asm.rs b/src/aead/chacha20poly1305/x86_64_asm.rs
index bbc2351b..55ac6124 100644
--- a/src/aead/chacha20poly1305/x86_64_asm.rs
+++ b/src/aead/chacha20poly1305/x86_64_asm.rs
@@ -4,8 +4,6 @@
//! ChaCha20-Poly1305 assembly. This Rust module owns the ABI boundary and
//! keeps runtime feature dispatch in the parent AEAD implementation.
-#![allow(unsafe_code)]
-
use core::{arch::global_asm, mem};
use super::KEY_SIZE;
@@ -76,8 +74,14 @@ unsafe extern "C" {
);
}
+/// Seal one nonempty buffer with the integrated x86-64 assembly kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that AVX2 and BMI2 are available and that `buffer` is nonempty and within ChaCha20's
+/// 2³²-block limit.
#[inline]
-pub(super) fn seal_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8], buffer: &mut [u8]) -> [u8; 16] {
+pub(super) unsafe fn seal_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8], buffer: &mut [u8]) -> [u8; 16] {
debug_assert!(!buffer.is_empty());
let extra_ciphertext = [0u8; 16];
@@ -99,7 +103,11 @@ pub(super) fn seal_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
// empty slices because the length is zero.
// 4. `extra_ciphertext` points at 16 initialized bytes and `extra_ciphertext_len` is zero, matching
// the AWS-LC seal ABI for callers without extra trailing ciphertext.
- // 5. `data` is 16-byte aligned and matches the assembly input/output union layout.
+ // 5. `data` is 16-byte aligned and matches the assembly input/output union layout; `from_mut` preserves that
+ // allocation's provenance and exclusive writability for the call, which initializes all 16 tag bytes before
+ // the union output is read.
+ // 6. The tag replaces the first 16 copied key bytes; the second half remains initialized key material and is
+ // volatile-zeroed before the stack allocation expires.
unsafe {
rscrypto_chacha20_poly1305_seal_x86_64(
buffer.as_mut_ptr(),
@@ -107,14 +115,21 @@ pub(super) fn seal_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
buffer.len(),
aad.as_ptr(),
aad.len(),
- &mut data,
+ core::ptr::from_mut(&mut data),
);
+ crate::traits::ct::zeroize(&mut data.input.key[16..]);
data.out.tag
}
}
+/// Open one nonempty buffer with the integrated x86-64 assembly kernel.
+///
+/// # Safety
+///
+/// The caller must ensure that AVX2 and BMI2 are available and that `buffer` is nonempty and within ChaCha20's
+/// 2³²-block limit.
#[inline]
-pub(super) fn open_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8], buffer: &mut [u8]) -> [u8; 16] {
+pub(super) unsafe fn open_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8], buffer: &mut [u8]) -> [u8; 16] {
debug_assert!(!buffer.is_empty());
let mut data = OpenData {
@@ -131,7 +146,11 @@ pub(super) fn open_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
// the assembly routine supports in-place open, matching the AWS-LC ABI.
// 3. `aad.as_ptr()` is valid for `aad.len()` bytes, including the conventional dangling pointer for
// empty slices because the length is zero.
- // 4. `data` is 16-byte aligned and matches the assembly input/output union layout.
+ // 4. `data` is 16-byte aligned and matches the assembly input/output union layout; `from_mut` preserves that
+ // allocation's provenance and exclusive writability for the call, which initializes all 16 tag bytes before
+ // the union output is read.
+ // 5. The tag replaces the first 16 copied key bytes; the second half remains initialized key material and is
+ // volatile-zeroed before the stack allocation expires.
unsafe {
rscrypto_chacha20_poly1305_open_x86_64(
buffer.as_mut_ptr(),
@@ -139,8 +158,9 @@ pub(super) fn open_in_place(key: &[u8; KEY_SIZE], nonce: &[u8; 12], aad: &[u8],
buffer.len(),
aad.as_ptr(),
aad.len(),
- &mut data,
+ core::ptr::from_mut(&mut data),
);
+ crate::traits::ct::zeroize(&mut data.input.key[16..]);
data.out.tag
}
}
diff --git a/src/aead/ghash.rs b/src/aead/ghash.rs
index 77ef35e6..55470c44 100644
--- a/src/aead/ghash.rs
+++ b/src/aead/ghash.rs
@@ -1,5 +1,3 @@
-#![allow(clippy::indexing_slicing)]
-
//! Fixed-schedule, table-free GHASH universal hash (NIST SP 800-38D).
//!
//! Generated-code timing claims remain configuration- and
@@ -52,6 +50,7 @@ pub(crate) fn h_to_polyval(h_bytes: &[u8; KEY_SIZE]) -> u128 {
mul_x_polyval(h)
}
+/// Computes one GHASH block with the portable POLYVAL-domain reduction.
#[cfg(feature = "diag")]
#[must_use]
pub fn diag_ghash_block_portable(h_bytes: &[u8; KEY_SIZE], block: &[u8; KEY_SIZE]) -> [u8; KEY_SIZE] {
@@ -129,10 +128,11 @@ impl Ghash {
#[cfg(test)]
impl Drop for Ghash {
fn drop(&mut self) {
- // SAFETY: self.acc/self.h are valid, aligned, dereferenceable pointers to initialized memory.
+ // SAFETY: the raw pointers address initialized, aligned `u128` fields owned
+ // exclusively by `self` for the duration of `drop`.
unsafe {
- core::ptr::write_volatile(&mut self.acc, 0);
- core::ptr::write_volatile(&mut self.h, 0);
+ core::ptr::write_volatile(&raw mut self.acc, 0);
+ core::ptr::write_volatile(&raw mut self.h, 0);
}
core::sync::atomic::compiler_fence(core::sync::atomic::Ordering::SeqCst);
}
@@ -144,6 +144,8 @@ impl Drop for Ghash {
mod tests {
use super::*;
+ const TEST_H: [u8; 16] = 0x66e9_4bd4_ef8a_2c3b_884c_fa59_ca34_2b2eu128.to_be_bytes();
+
/// GHASH with empty input should return zero.
#[test]
fn ghash_empty() {
@@ -165,26 +167,26 @@ mod tests {
/// Verify update_padded matches manual block-by-block.
#[test]
fn ghash_padded_matches_manual() {
- let h = hex_to_16("66e94bd4ef8a2c3b884cfa59ca342b2e");
let data = b"Hello, World! This is test data for GHASH padding.";
// Manual: split into 16-byte blocks, pad last one.
- let mut manual = Ghash::new(&h);
- let mut offset = 0;
- while offset + 16 <= data.len() {
- let block: [u8; 16] = data[offset..offset + 16].try_into().unwrap();
+ let mut manual = Ghash::new(&TEST_H);
+ let mut chunks = data.chunks_exact(BLOCK_SIZE);
+ for chunk in chunks.by_ref() {
+ let mut block = [0u8; BLOCK_SIZE];
+ block.copy_from_slice(chunk);
manual.update_block(&block);
- offset += 16;
}
- if offset < data.len() {
- let mut block = [0u8; 16];
- block[..data.len() - offset].copy_from_slice(&data[offset..]);
+ let remainder = chunks.remainder();
+ if !remainder.is_empty() {
+ let mut block = [0u8; BLOCK_SIZE];
+ block[..remainder.len()].copy_from_slice(remainder);
manual.update_block(&block);
}
let manual_result = manual.finalize();
// Padded API.
- let mut padded = Ghash::new(&h);
+ let mut padded = Ghash::new(&TEST_H);
padded.update_padded(data);
let padded_result = padded.finalize();
@@ -213,14 +215,4 @@ mod tests {
"mulX(x^127) should reduce to feedback polynomial"
);
}
-
- fn hex_to_16(hex: &str) -> [u8; 16] {
- let mut out = [0u8; 16];
- let mut i = 0;
- while i < 16 {
- out[i] = u8::from_str_radix(&hex[2 * i..2 * i + 2], 16).unwrap();
- i = i.strict_add(1);
- }
- out
- }
}
diff --git a/src/aead/introspect.rs b/src/aead/introspect.rs
index fe12e5c5..b350c8f8 100644
--- a/src/aead/introspect.rs
+++ b/src/aead/introspect.rs
@@ -18,8 +18,7 @@
feature = "aes-gcm-siv",
feature = "chacha20poly1305",
feature = "xchacha20poly1305",
- feature = "aegis256",
- feature = "ascon-aead"
+ feature = "aegis256"
))]
use crate::aead::targets::{AeadPrimitive, select_backend};
pub use crate::platform::DispatchInfo;
@@ -29,8 +28,7 @@ pub use crate::platform::DispatchInfo;
feature = "aes-gcm-siv",
feature = "chacha20poly1305",
feature = "xchacha20poly1305",
- feature = "aegis256",
- feature = "ascon-aead"
+ feature = "aegis256"
))]
#[inline]
fn backend_for(primitive: AeadPrimitive) -> &'static str {
@@ -98,7 +96,10 @@ pub fn aegis256_backend() -> &'static str {
#[inline]
#[must_use]
pub fn ascon_aead128_backend() -> &'static str {
- backend_for(AeadPrimitive::AsconAead128)
+ match crate::platform::arch() {
+ crate::platform::Arch::Wasm32 | crate::platform::Arch::Wasm64 => "wasm32/portable",
+ _ => "portable",
+ }
}
#[cfg(test)]
diff --git a/src/aead/mod.rs b/src/aead/mod.rs
index 0ed370d8..366faeee 100644
--- a/src/aead/mod.rs
+++ b/src/aead/mod.rs
@@ -34,10 +34,10 @@
//! ```toml
//! [dependencies]
//! # ChaCha20-Poly1305 only
-//! rscrypto = { version = "0.5.0", default-features = false, features = ["chacha20poly1305"] }
+//! rscrypto = { version = "0.8.1", default-features = false, features = ["chacha20poly1305"] }
//!
//! # All AEADs
-//! rscrypto = { version = "0.5.0", default-features = false, features = ["aead"] }
+//! rscrypto = { version = "0.8.1", default-features = false, features = ["aead"] }
//! ```
//!
//! # API Conventions
@@ -73,14 +73,6 @@ mod aegis256;
all(feature = "aegis256", target_arch = "riscv64"),
all(feature = "aegis256", test),
))]
-#[cfg_attr(
- all(
- feature = "aegis256",
- target_arch = "riscv64",
- not(any(feature = "aes-gcm", feature = "aes-gcm-siv"))
- ),
- allow(dead_code)
-)]
mod aes;
#[cfg(feature = "aes-gcm")]
mod aes128gcm;
@@ -95,6 +87,42 @@ mod aes256gcmsiv;
all(target_arch = "riscv64", any(feature = "aes-gcm", feature = "aes-gcm-siv"))
))]
mod aes_round;
+#[cfg(all(test, any(feature = "aes-gcm", feature = "aes-gcm-siv", feature = "aegis256")))]
+mod test_vectors {
+ use alloc::{vec, vec::Vec};
+
+ #[track_caller]
+ pub(super) fn hex_array(hex: &str) -> [u8; N] {
+ let mut out = [0u8; N];
+ crate::hex::from_hex(hex, &mut out).expect("AEAD test vector must contain valid hexadecimal");
+ out
+ }
+
+ #[track_caller]
+ #[cfg(any(feature = "aes-gcm", feature = "aes-gcm-siv"))]
+ pub(super) fn hex12(hex: &str) -> [u8; 12] {
+ hex_array(hex)
+ }
+
+ #[track_caller]
+ #[cfg(any(feature = "aes-gcm", feature = "aes-gcm-siv"))]
+ pub(super) fn hex16(hex: &str) -> [u8; 16] {
+ hex_array(hex)
+ }
+
+ #[track_caller]
+ #[cfg(any(feature = "aes-gcm", feature = "aes-gcm-siv"))]
+ pub(super) fn hex32(hex: &str) -> [u8; 32] {
+ hex_array(hex)
+ }
+
+ #[track_caller]
+ pub(super) fn hex_vec(hex: &str) -> Vec {
+ let mut out = vec![0u8; hex.as_bytes().chunks_exact(2).len()];
+ crate::hex::from_hex(hex, &mut out).expect("AEAD test vector must contain valid hexadecimal");
+ out
+ }
+}
#[cfg(feature = "ascon-aead")]
mod ascon128;
#[cfg(any(feature = "chacha20poly1305", feature = "xchacha20poly1305"))]
@@ -111,6 +139,33 @@ mod nonce_counter;
mod poly1305;
#[cfg(any(feature = "aes-gcm", feature = "aes-gcm-siv"))]
mod polyval;
+#[cfg(any(
+ feature = "aes-gcm",
+ feature = "chacha20poly1305",
+ feature = "xchacha20poly1305",
+ all(
+ feature = "aes-gcm-siv",
+ any(
+ feature = "diag",
+ target_arch = "aarch64",
+ target_arch = "powerpc64",
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ )
+ ),
+ all(
+ feature = "aegis256",
+ any(
+ feature = "diag",
+ target_arch = "aarch64",
+ all(target_arch = "powerpc64", target_endian = "little"),
+ target_arch = "riscv64",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ )
+ )
+))]
mod targets;
#[cfg(feature = "xchacha20poly1305")]
mod xchacha20poly1305;
@@ -595,36 +650,40 @@ impl From for OpenError {
}
}
-#[cfg_attr(
- not(any(
- feature = "aes-gcm",
- feature = "aes-gcm-siv",
- feature = "chacha20poly1305",
- feature = "xchacha20poly1305"
- )),
- allow(dead_code)
-)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg(any(
+ feature = "aegis256",
+ feature = "aes-gcm",
+ feature = "aes-gcm-siv",
+ feature = "chacha20poly1305",
+ feature = "xchacha20poly1305"
+))]
pub(crate) struct LengthOverflow;
const _: () = assert!(usize::BITS <= u64::BITS);
-#[cfg_attr(
- not(any(
- feature = "aes-gcm",
- feature = "aes-gcm-siv",
- feature = "chacha20poly1305",
- feature = "xchacha20poly1305"
- )),
- allow(dead_code)
-)]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+#[cfg(any(
+ feature = "aegis256",
+ feature = "aes-gcm",
+ feature = "aes-gcm-siv",
+ feature = "chacha20poly1305",
+ feature = "xchacha20poly1305"
+))]
pub(crate) struct AeadByteLengths {
aad: u64,
text: u64,
}
+#[cfg(any(
+ feature = "aegis256",
+ feature = "aes-gcm",
+ feature = "aes-gcm-siv",
+ feature = "chacha20poly1305",
+ feature = "xchacha20poly1305"
+))]
impl AeadByteLengths {
+ #[cfg(any(feature = "aes-gcm-siv", feature = "chacha20poly1305", feature = "xchacha20poly1305"))]
#[inline]
pub(crate) const fn from_usize(aad_len: usize, text_len: usize) -> Self {
Self {
@@ -633,19 +692,13 @@ impl AeadByteLengths {
}
}
- #[cfg_attr(
- not(any(feature = "chacha20poly1305", feature = "xchacha20poly1305")),
- allow(dead_code)
- )]
+ #[cfg(any(feature = "chacha20poly1305", feature = "xchacha20poly1305"))]
#[inline]
pub(crate) fn try_new(aad_len: usize, text_len: usize) -> Result {
Ok(Self::from_usize(aad_len, text_len))
}
- #[cfg_attr(
- not(any(feature = "aes-gcm", feature = "aes-gcm-siv", feature = "aegis256")),
- allow(dead_code)
- )]
+ #[cfg(any(feature = "aegis256", feature = "aes-gcm", feature = "aes-gcm-siv"))]
#[inline]
pub(crate) fn try_new_bit_lengths(aad_len: usize, text_len: usize) -> Result {
const MAX_BIT_ENCODED_BYTES: u64 = u64::MAX / 8;
@@ -655,10 +708,7 @@ impl AeadByteLengths {
})
}
- #[cfg_attr(
- not(any(feature = "chacha20poly1305", feature = "xchacha20poly1305")),
- allow(dead_code)
- )]
+ #[cfg(any(feature = "chacha20poly1305", feature = "xchacha20poly1305"))]
#[inline]
pub(crate) fn to_le_bytes_block(self) -> [u8; 16] {
let mut block = [0u8; 16];
@@ -667,7 +717,7 @@ impl AeadByteLengths {
block
}
- #[cfg_attr(not(feature = "aes-gcm-siv"), allow(dead_code))]
+ #[cfg(feature = "aes-gcm-siv")]
#[inline]
pub(crate) fn to_le_bits_block(self) -> [u8; 16] {
let aad_bits = self.aad.strict_mul(8);
@@ -678,7 +728,7 @@ impl AeadByteLengths {
block
}
- #[cfg_attr(not(feature = "aes-gcm"), allow(dead_code))]
+ #[cfg(feature = "aes-gcm")]
#[inline]
pub(crate) fn to_be_bits_block(self) -> [u8; 16] {
let aad_bits = self.aad.strict_mul(8);
@@ -689,45 +739,38 @@ impl AeadByteLengths {
block
}
- #[cfg_attr(
- not(all(
- target_arch = "x86_64",
- any(feature = "chacha20poly1305", feature = "xchacha20poly1305")
- )),
- allow(dead_code)
- )]
#[inline]
+ #[cfg(all(
+ any(feature = "chacha20poly1305", feature = "xchacha20poly1305"),
+ any(test, target_arch = "x86_64", target_arch = "aarch64", target_arch = "riscv64")
+ ))]
pub(crate) const fn total_at_least(self, minimum: u64) -> bool {
if self.aad >= minimum {
return true;
}
- self.text >= minimum - self.aad
+ self.text >= minimum.strict_sub(self.aad)
}
}
-#[cfg_attr(
- not(any(
- feature = "aes-gcm",
- feature = "aes-gcm-siv",
- feature = "chacha20poly1305",
- feature = "xchacha20poly1305"
- )),
- allow(dead_code)
-)]
+#[cfg(any(
+ feature = "aegis256",
+ feature = "aes-gcm",
+ feature = "aes-gcm-siv",
+ feature = "chacha20poly1305",
+ feature = "xchacha20poly1305"
+))]
#[inline]
pub(crate) fn try_length_as_u64(len: usize) -> Result {
u64::try_from(len).map_err(|_| LengthOverflow)
}
-#[cfg_attr(
- not(any(
- feature = "aes-gcm",
- feature = "aes-gcm-siv",
- feature = "chacha20poly1305",
- feature = "xchacha20poly1305"
- )),
- allow(dead_code)
-)]
+#[cfg(any(
+ feature = "aegis256",
+ feature = "aes-gcm",
+ feature = "aes-gcm-siv",
+ feature = "chacha20poly1305",
+ feature = "xchacha20poly1305"
+))]
#[inline]
pub(crate) fn try_bounded_length_as_u64(len: usize, max: u64) -> Result {
let len = try_length_as_u64(len)?;
@@ -737,47 +780,35 @@ pub(crate) fn try_bounded_length_as_u64(len: usize, max: u64) -> Result Result {
try_bounded_length_as_u64(len, max).map_err(|_| SealError::too_large())
}
-#[cfg_attr(
- not(any(feature = "aes-gcm", feature = "aes-gcm-siv", feature = "aegis256")),
- allow(dead_code)
-)]
+#[cfg(any(feature = "aegis256", feature = "aes-gcm", feature = "aes-gcm-siv"))]
#[inline]
pub(crate) fn seal_bit_lengths(aad_len: usize, text_len: usize) -> Result {
AeadByteLengths::try_new_bit_lengths(aad_len, text_len).map_err(|_| SealError::too_large())
}
-#[cfg_attr(
- not(any(
- feature = "aes-gcm",
- feature = "aes-gcm-siv",
- feature = "chacha20poly1305",
- feature = "xchacha20poly1305"
- )),
- allow(dead_code)
-)]
+#[cfg(any(
+ feature = "aes-gcm",
+ feature = "aes-gcm-siv",
+ feature = "chacha20poly1305",
+ feature = "xchacha20poly1305"
+))]
#[inline]
pub(crate) fn open_bounded_length_as_u64(len: usize, max: u64) -> Result {
try_bounded_length_as_u64(len, max).map_err(|_| OpenError::too_large())
}
-#[cfg_attr(
- not(any(feature = "aes-gcm", feature = "aes-gcm-siv", feature = "aegis256")),
- allow(dead_code)
-)]
+#[cfg(any(feature = "aegis256", feature = "aes-gcm", feature = "aes-gcm-siv"))]
#[inline]
pub(crate) fn open_bit_lengths(aad_len: usize, text_len: usize) -> Result {
AeadByteLengths::try_new_bit_lengths(aad_len, text_len).map_err(|_| OpenError::too_large())
@@ -787,7 +818,17 @@ pub(crate) fn open_bit_lengths(aad_len: usize, text_len: usize) -> Result` builds 96-bit nonces as:
-//!
-//! - 32-bit fixed prefix chosen by the caller
-//! - 64-bit big-endian invocation counter
-//!
-//! This follows the deterministic IV shape from SP 800-38D and removes the
-//! easiest nonce-reuse footgun from high-volume AES-GCM usage.
-//!
-//! ```rust
-//! use rscrypto::{Aead, Aes256Gcm, Aes256GcmKey, aead::NonceCounter};
-//!
-//! let cipher = Aes256Gcm::new(&Aes256GcmKey::from_bytes([0x42; 32]));
-//! let mut counter = NonceCounter::::new(*b"sess");
-//!
-//! let mut sealed = [0u8; 4 + Aes256Gcm::TAG_SIZE];
-//! let nonce = counter.encrypt(&cipher, b"hdr", b"data", &mut sealed)?;
-//!
-//! let mut opened = [0u8; 4];
-//! cipher.decrypt(&nonce, b"hdr", &sealed, &mut opened)?;
-//! assert_eq!(&opened, b"data");
-//! # Ok::<(), Box>(())
-//! ```
-
use core::{fmt, marker::PhantomData};
use super::{Aes128Gcm, Aes128GcmTag, Aes256Gcm, Aes256GcmTag, Nonce96, SealError};
@@ -98,6 +73,10 @@ impl From for NonceCounterSealError {
/// Monotonic deterministic nonce generator for AES-GCM.
///
+/// Each 96-bit nonce consists of a caller-selected 32-bit fixed prefix and a
+/// 64-bit big-endian invocation counter, following the deterministic IV shape
+/// from SP 800-38D.
+///
/// The counter is intentionally not `Clone` or `Copy`. One instance owns one
/// nonce stream. If you need restart-safe continuation, persist
/// [`next_counter`](Self::next_counter) and restore with
@@ -108,6 +87,15 @@ pub struct NonceCounter {
_cipher: PhantomData Cipher>,
}
+impl fmt::Debug for NonceCounter {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_struct("NonceCounter")
+ .field("fixed_prefix", &self.fixed_prefix)
+ .field("next_counter", &self.next)
+ .finish()
+ }
+}
+
impl NonceCounter {
/// Fixed per-stream prefix length in bytes.
pub const FIXED_PREFIX_LEN: usize = FIXED_PREFIX_LEN;
@@ -355,22 +343,41 @@ impl NonceCounter {
#[cfg(test)]
mod tests {
+ use alloc::format;
+ use core::marker::PhantomData;
+
use super::{Aes128Gcm, Aes256Gcm, NonceCounter, NonceCounterSealError};
use crate::{
Aes128GcmKey, Aes256GcmKey,
aead::{Nonce96, SealError},
};
+ #[test]
+ fn nonce_counter_debug_does_not_require_cipher_debug() {
+ struct CipherWithoutDebug;
+
+ let counter = NonceCounter:: {
+ fixed_prefix: *b"test",
+ next: 7,
+ _cipher: PhantomData,
+ };
+
+ assert_eq!(
+ format!("{counter:?}"),
+ "NonceCounter { fixed_prefix: [116, 101, 115, 116], next_counter: 7 }"
+ );
+ }
+
#[test]
fn aes_gcm_nonce_counter_formats_prefix_and_counter() {
let mut counter = NonceCounter::::new(*b"conn");
assert_eq!(
- counter.next_nonce().unwrap(),
+ counter.next_nonce().expect("first nonce should be available"),
Nonce96::from_bytes([b'c', b'o', b'n', b'n', 0, 0, 0, 0, 0, 0, 0, 0])
);
assert_eq!(
- counter.next_nonce().unwrap(),
+ counter.next_nonce().expect("second nonce should be available"),
Nonce96::from_bytes([b'c', b'o', b'n', b'n', 0, 0, 0, 0, 0, 0, 0, 1])
);
assert_eq!(counter.issued(), 2);
@@ -382,10 +389,14 @@ mod tests {
let mut counter = NonceCounter::::new(*b"sess");
let mut sealed = [0u8; 4 + Aes256Gcm::TAG_SIZE];
- let nonce = counter.encrypt(&cipher, b"hdr", b"data", &mut sealed).unwrap();
+ let nonce = counter
+ .encrypt(&cipher, b"hdr", b"data", &mut sealed)
+ .expect("valid inputs should seal");
let mut opened = [0u8; 4];
- cipher.decrypt(&nonce, b"hdr", &sealed, &mut opened).unwrap();
+ cipher
+ .decrypt(&nonce, b"hdr", &sealed, &mut opened)
+ .expect("freshly sealed ciphertext should open");
assert_eq!(&opened, b"data");
assert_eq!(counter.next_counter(), 1);
}
@@ -396,7 +407,9 @@ mod tests {
let mut counter = NonceCounter::::new(*b"bufr");
let mut out = [0u8; 3];
- let err = counter.encrypt(&cipher, b"", b"data", &mut out).unwrap_err();
+ let err = counter
+ .encrypt(&cipher, b"", b"data", &mut out)
+ .expect_err("undersized output should be rejected");
assert_eq!(err, NonceCounterSealError::from(SealError::buffer()));
assert_eq!(counter.next_counter(), 1);
}
@@ -404,11 +417,14 @@ mod tests {
#[test]
fn aes_gcm_nonce_counter_exhausts_cleanly() {
let mut counter =
- NonceCounter::::with_counter(*b"last", NonceCounter::::MAX_MESSAGES.strict_sub(1)).unwrap();
+ NonceCounter::::with_counter(*b"last", NonceCounter::::MAX_MESSAGES.strict_sub(1))
+ .expect("last permitted counter should be accepted");
- assert!(counter.next_nonce().is_ok());
+ counter.next_nonce().expect("last nonce should be available");
assert_eq!(counter.remaining(), 0);
- assert!(counter.next_nonce().is_err());
+ counter
+ .next_nonce()
+ .expect_err("exhausted counter should reject another nonce");
}
#[test]
@@ -417,13 +433,15 @@ mod tests {
// greater than or equal to MAX_MESSAGES. Pin both the equality and the
// strictly-greater branch so a future relaxation cannot silently widen
// the deterministic-IV budget past the SP 800-38D limit.
- assert!(NonceCounter::::with_counter(*b"oflw", NonceCounter::::MAX_MESSAGES).is_err());
- assert!(NonceCounter::::with_counter(*b"oflw", u64::MAX).is_err());
+ NonceCounter::::with_counter(*b"oflw", NonceCounter::::MAX_MESSAGES)
+ .expect_err("maximum counter should be rejected");
+ NonceCounter::::with_counter(*b"oflw", u64::MAX).expect_err("out-of-range counter should be rejected");
}
#[test]
fn aes_gcm_nonce_counters_report_resumed_state() {
- let counter256 = NonceCounter::::with_counter(*b"r256", 37).unwrap();
+ let counter256 =
+ NonceCounter::::with_counter(*b"r256", 37).expect("in-range counter should be accepted");
assert_eq!(counter256.fixed_prefix(), *b"r256");
assert_eq!(counter256.next_counter(), 37);
assert_eq!(counter256.issued(), 37);
@@ -432,7 +450,8 @@ mod tests {
NonceCounter::::MAX_MESSAGES.strict_sub(37)
);
- let counter128 = NonceCounter::::with_counter(*b"r128", 73).unwrap();
+ let counter128 =
+ NonceCounter::::with_counter(*b"r128", 73).expect("in-range counter should be accepted");
assert_eq!(counter128.fixed_prefix(), *b"r128");
assert_eq!(counter128.next_counter(), 73);
assert_eq!(counter128.issued(), 73);
@@ -448,10 +467,14 @@ mod tests {
let mut counter = NonceCounter::::new(*b"sess");
let mut sealed = [0u8; 4 + Aes128Gcm::TAG_SIZE];
- let nonce = counter.encrypt(&cipher, b"hdr", b"data", &mut sealed).unwrap();
+ let nonce = counter
+ .encrypt(&cipher, b"hdr", b"data", &mut sealed)
+ .expect("valid inputs should seal");
let mut opened = [0u8; 4];
- cipher.decrypt(&nonce, b"hdr", &sealed, &mut opened).unwrap();
+ cipher
+ .decrypt(&nonce, b"hdr", &sealed, &mut opened)
+ .expect("freshly sealed ciphertext should open");
assert_eq!(&opened, b"data");
assert_eq!(counter.next_counter(), 1);
}
@@ -461,11 +484,11 @@ mod tests {
let mut counter = NonceCounter::::new(*b"conn");
assert_eq!(
- counter.next_nonce().unwrap(),
+ counter.next_nonce().expect("first nonce should be available"),
Nonce96::from_bytes([b'c', b'o', b'n', b'n', 0, 0, 0, 0, 0, 0, 0, 0])
);
assert_eq!(
- counter.next_nonce().unwrap(),
+ counter.next_nonce().expect("second nonce should be available"),
Nonce96::from_bytes([b'c', b'o', b'n', b'n', 0, 0, 0, 0, 0, 0, 0, 1])
);
assert_eq!(counter.issued(), 2);
@@ -477,7 +500,9 @@ mod tests {
let mut counter = NonceCounter::::new(*b"bufr");
let mut out = [0u8; 3];
- let err = counter.encrypt(&cipher, b"", b"data", &mut out).unwrap_err();
+ let err = counter
+ .encrypt(&cipher, b"", b"data", &mut out)
+ .expect_err("undersized output should be rejected");
assert_eq!(err, NonceCounterSealError::from(SealError::buffer()));
assert_eq!(counter.next_counter(), 1);
}
@@ -485,11 +510,14 @@ mod tests {
#[test]
fn aes128_gcm_nonce_counter_exhausts_cleanly() {
let mut counter =
- NonceCounter::::with_counter(*b"last", NonceCounter::::MAX_MESSAGES.strict_sub(1)).unwrap();
+ NonceCounter::