diff --git a/.changes/restore-release-assurance-ct-riscv.md b/.changes/restore-release-assurance-ct-riscv.md new file mode 100644 index 00000000..aab3bc8f --- /dev/null +++ b/.changes/restore-release-assurance-ct-riscv.md @@ -0,0 +1,5 @@ +--- +"rscrypto" = "patch" +--- + +Restore release assurance with an attributable RSA fixed-width exponent timing gate, FEAT_DIT hardening for AArch64 private exponentiation, and RISC-V-compatible XXH3 backend tests. diff --git a/.config/benchmark-matrix.json b/.config/benchmark-matrix.json new file mode 100644 index 00000000..a29b8b52 --- /dev/null +++ b/.config/benchmark-matrix.json @@ -0,0 +1,195 @@ +{ + "schema": 1, + "default_features": ["parallel", "full"], + "crates": { + "checksum": ["crc"], + "hashes": ["sha2", "sha3", "kmac_cshake", "ascon", "xxh3", "rapidhash", "blake2", "blake3"], + "auth": ["auth", "kmac_cshake", "password_hashing", "rsa"], + "rsa": ["rsa"], + "aead": ["aead"] + }, + "benches": { + "crc": { + "binary": "crc", + "kind": "criterion", + "required": true, + "features": ["parallel", "checksums"] + }, + "sha2": { + "binary": "sha2", + "kind": "criterion", + "required": true, + "features": ["parallel", "sha2", "diag"] + }, + "sha3": { + "binary": "sha3", + "kind": "criterion", + "required": true, + "features": ["parallel", "sha3"] + }, + "kmac_cshake": { + "binary": "kmac_cshake", + "kind": "criterion", + "required": true, + "features": ["parallel", "sha3", "kmac"] + }, + "ascon": { + "binary": "ascon", + "kind": "criterion", + "required": true, + "features": ["parallel", "ascon-hash"] + }, + "xxh3": { + "binary": "xxh3", + "kind": "criterion", + "required": true, + "features": ["parallel", "xxh3"] + }, + "rapidhash": { + "binary": "rapidhash", + "kind": "criterion", + "required": true, + "features": ["parallel", "rapidhash"] + }, + "aead_kernels": { + "binary": "aead_kernels", + "kind": "criterion", + "required": false, + "features": ["parallel", "sha2", "chacha20poly1305", "diag"] + }, + "blake2": { + "binary": "blake2", + "kind": "criterion", + "required": true, + "features": ["parallel", "blake2b", "blake2s"] + }, + "blake3": { + "binary": "blake3", + "kind": "criterion", + "required": true, + "features": ["parallel", "blake3"] + }, + "auth": { + "binary": "auth", + "kind": "criterion", + "required": true, + "features": ["parallel", "hmac", "hkdf", "pbkdf2", "ecdsa", "ed25519", "x25519", "ml-kem", "diag"] + }, + "password_hashing": { + "binary": "password_hashing", + "kind": "criterion", + "required": true, + "features": ["parallel", "argon2", "scrypt", "phc-strings", "getrandom"] + }, + "rsa": { + "binary": "rsa", + "kind": "criterion", + "required": true, + "features": ["parallel", "rsa", "diag"] + }, + "aead_diag": { + "binary": "aead", + "kind": "criterion", + "required": false, + "features": ["parallel", "sha2", "aes-gcm", "aes-gcm-siv", "chacha20poly1305", "xchacha20poly1305", "aegis256", "ascon-aead", "diag"] + }, + "aead": { + "binary": "aead", + "kind": "criterion", + "required": true, + "features": ["parallel", "aes-gcm", "aes-gcm-siv", "chacha20poly1305", "xchacha20poly1305", "aegis256", "ascon-aead"] + }, + "structural": { + "binary": "structural", + "kind": "gungraun", + "required": false, + "features": ["checksums", "sha2", "blake3"] + } + }, + "algorithms": { + "crc16-ccitt": {"crate": "checksum", "bench": "crc", "filter": "crc16-ccitt"}, + "crc16-ibm": {"crate": "checksum", "bench": "crc", "filter": "crc16-ibm"}, + "crc24-openpgp": {"crate": "checksum", "bench": "crc", "filter": "crc24-openpgp"}, + "crc32-ieee": {"crate": "checksum", "bench": "crc", "filter": "^crc32/"}, + "crc32c": {"crate": "checksum", "bench": "crc", "filter": "crc32c"}, + "crc64-xz": {"crate": "checksum", "bench": "crc", "filter": "crc64-xz"}, + "crc64-nvme": {"crate": "checksum", "bench": "crc", "filter": "crc64-nvme"}, + "sha224": {"crate": "hashes", "bench": "sha2", "filter": "sha224"}, + "sha256": {"crate": "hashes", "bench": "sha2", "filter": "sha256"}, + "sha384": {"crate": "hashes", "bench": "sha2", "filter": "sha384"}, + "sha512": {"crate": "hashes", "bench": "sha2", "filter": "^sha512/"}, + "sha512-256": {"crate": "hashes", "bench": "sha2", "filter": "sha512-256"}, + "sha3-224": {"crate": "hashes", "bench": "sha3", "filter": "sha3-224"}, + "sha3-256": {"crate": "hashes", "bench": "sha3", "filter": "sha3-256"}, + "sha3-384": {"crate": "hashes", "bench": "sha3", "filter": "sha3-384"}, + "sha3-512": {"crate": "hashes", "bench": "sha3", "filter": "sha3-512"}, + "shake128": {"crate": "hashes", "bench": "sha3", "filter": "shake128"}, + "shake256": {"crate": "hashes", "bench": "sha3", "filter": "shake256"}, + "cshake256": {"crate": "auth", "bench": "kmac_cshake", "filter": "cshake256"}, + "blake2": {"crate": "hashes", "bench": "blake2", "filter": "^blake2/(rscrypto|rustcrypto|keyed|streaming)/"}, + "blake3": {"crate": "hashes", "bench": "blake3", "filter": "blake3"}, + "xxh3": {"crate": "hashes", "bench": "xxh3", "filter": "xxh3"}, + "rapidhash": {"crate": "hashes", "bench": "rapidhash", "filter": "rapidhash"}, + "ascon-hash256": {"crate": "hashes", "bench": "ascon", "filter": "ascon-hash256"}, + "ascon-xof128": {"crate": "hashes", "bench": "ascon", "filter": "ascon-xof128"}, + "ascon-cxof128": {"crate": "hashes", "bench": "ascon", "filter": "ascon-cxof128"}, + "hmac-sha256": {"crate": "auth", "bench": "auth", "filter": "^hmac-sha256"}, + "hmac-sha384": {"crate": "auth", "bench": "auth", "filter": "hmac-sha384"}, + "hmac-sha512": {"crate": "auth", "bench": "auth", "filter": "hmac-sha512"}, + "hkdf-sha256": {"crate": "auth", "bench": "auth", "filter": "^hkdf-sha256"}, + "hkdf-sha384": {"crate": "auth", "bench": "auth", "filter": "hkdf-sha384"}, + "pbkdf2-sha256": {"crate": "auth", "bench": "auth", "filter": "^pbkdf2-sha256/"}, + "pbkdf2-sha512": {"crate": "auth", "bench": "auth", "filter": "^pbkdf2-sha512/"}, + "ecdsa-p256": {"crate": "auth", "bench": "auth", "filter": "^ecdsa-p256/"}, + "ecdsa-p384": {"crate": "auth", "bench": "auth", "filter": "^ecdsa-p384/"}, + "ed25519": {"crate": "auth", "bench": "auth", "filter": "ed25519"}, + "x25519": {"crate": "auth", "bench": "auth", "filter": "x25519"}, + "mlkem512": {"crate": "auth", "bench": "auth", "filter": "^mlkem512/"}, + "mlkem768": {"crate": "auth", "bench": "auth", "filter": "^mlkem768/"}, + "mlkem1024": {"crate": "auth", "bench": "auth", "filter": "^mlkem1024/"}, + "kmac256": {"crate": "auth", "bench": "kmac_cshake", "filter": "kmac256"}, + "argon2d": {"crate": "auth", "bench": "password_hashing", "filter": "^argon2d-"}, + "argon2i": {"crate": "auth", "bench": "password_hashing", "filter": "^argon2i-"}, + "argon2id": {"crate": "auth", "bench": "password_hashing", "filter": "^argon2id-"}, + "scrypt": {"crate": "auth", "bench": "password_hashing", "filter": "^scrypt-"}, + "rsa": {"crate": "auth", "bench": "rsa", "filter": "rsa"}, + "xchacha20-poly1305": {"crate": "aead", "bench": "aead", "filter": "xchacha20-poly1305"}, + "chacha20-poly1305": {"crate": "aead", "bench": "aead", "filter": "^chacha20-poly1305/"}, + "aes-256-gcm-siv": {"crate": "aead", "bench": "aead", "filter": "aes-256-gcm-siv"}, + "aes-128-gcm-siv": {"crate": "aead", "bench": "aead", "filter": "aes-128-gcm-siv"}, + "aes-256-gcm": {"crate": "aead", "bench": "aead", "filter": "^aes-256-gcm/"}, + "aes-128-gcm": {"crate": "aead", "bench": "aead", "filter": "^aes-128-gcm/"}, + "aegis-256": {"crate": "aead", "bench": "aead", "filter": "aegis-256"}, + "ascon-aead128": {"crate": "aead", "bench": "aead", "filter": "ascon-aead128"}, + "aead-diag": {"crate": "aead", "bench": "aead_diag", "filter": "chacha20-poly1305/encrypt"} + }, + "selectors": { + "all": ["crc16-ccitt", "crc16-ibm", "crc24-openpgp", "crc32-ieee", "crc32c", "crc64-xz", "crc64-nvme", "sha224", "sha256", "sha384", "sha512", "sha512-256", "sha3-224", "sha3-256", "sha3-384", "sha3-512", "shake128", "shake256", "cshake256", "blake2", "blake3", "xxh3", "rapidhash", "ascon-hash256", "ascon-xof128", "ascon-cxof128", "hmac-sha256", "hmac-sha384", "hmac-sha512", "hkdf-sha256", "hkdf-sha384", "pbkdf2-sha256", "pbkdf2-sha512", "ecdsa-p256", "ecdsa-p384", "ed25519", "x25519", "mlkem512", "mlkem768", "mlkem1024", "kmac256", "argon2d", "argon2i", "argon2id", "scrypt", "rsa", "xchacha20-poly1305", "chacha20-poly1305", "aes-256-gcm-siv", "aes-128-gcm-siv", "aes-256-gcm", "aes-128-gcm", "aegis-256", "ascon-aead128"], + "auth": ["hmac-sha256", "hmac-sha384", "hmac-sha512", "hkdf-sha256", "hkdf-sha384", "pbkdf2-sha256", "pbkdf2-sha512", "ecdsa-p256", "ecdsa-p384", "ed25519", "x25519", "mlkem512", "mlkem768", "mlkem1024", "kmac256", "argon2d", "argon2i", "argon2id", "scrypt", "rsa"], + "rsa": ["rsa"], + "aead": ["xchacha20-poly1305", "chacha20-poly1305", "aes-256-gcm-siv", "aes-128-gcm-siv", "aes-256-gcm", "aes-128-gcm", "aegis-256", "ascon-aead128"], + "aeaddiag": ["aead-diag"], + "chacha20poly1305diag": ["aead-diag"], + "chacha20poly1305par4": ["aead-diag"], + "checksum": ["crc16-ccitt", "crc16-ibm", "crc24-openpgp", "crc32-ieee", "crc32c", "crc64-xz", "crc64-nvme"], + "checksums": ["crc16-ccitt", "crc16-ibm", "crc24-openpgp", "crc32-ieee", "crc32c", "crc64-xz", "crc64-nvme"], + "hash": ["sha224", "sha256", "sha384", "sha512", "sha512-256", "sha3-224", "sha3-256", "sha3-384", "sha3-512", "shake128", "shake256", "cshake256", "blake2", "blake3", "xxh3", "rapidhash", "ascon-hash256", "ascon-xof128", "ascon-cxof128"], + "hashes": ["sha224", "sha256", "sha384", "sha512", "sha512-256", "sha3-224", "sha3-256", "sha3-384", "sha3-512", "shake128", "shake256", "cshake256", "blake2", "blake3", "xxh3", "rapidhash", "ascon-hash256", "ascon-xof128", "ascon-cxof128"], + "mlkem": ["mlkem512", "mlkem768", "mlkem1024"], + "crc64": ["crc64-xz", "crc64-nvme"], + "crc64nvme": ["crc64-xz", "crc64-nvme"], + "crc64xz": ["crc64-xz", "crc64-nvme"], + "crc32": ["crc32-ieee", "crc32c"], + "crc16": ["crc16-ccitt", "crc16-ibm"] + }, + "bench_aliases": { + "comp": ["crc", "sha2", "sha3", "kmac_cshake", "ascon", "auth", "aead", "xxh3", "rapidhash", "blake3"], + "kernels": ["blake3"], + "checksum_comp": ["crc"], + "checksum_kernels": ["crc"], + "hashes_comp": ["sha2", "sha3", "kmac_cshake", "ascon", "xxh3", "rapidhash", "blake3"], + "auth_comp": ["auth"], + "aead_comp": ["aead"], + "hashes_kernels": ["blake3"] + } +} diff --git a/.config/ci-tool-archives.tsv b/.config/ci-tool-archives.tsv index fcda12a9..ff718e53 100644 --- a/.config/ci-tool-archives.tsv +++ b/.config/ci-tool-archives.tsv @@ -1,6 +1,10 @@ # tool version os architecture filename url sha256 -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 +wasmtime v48.0.0 linux x86_64 wasmtime-v48.0.0-x86_64-linux.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v48.0.0/wasmtime-v48.0.0-x86_64-linux.tar.xz 1d23a692da51a4f825698f3f999da71f28bad19a96df5395fadc8d07f162dac3 +wasmtime v48.0.0 linux aarch64 wasmtime-v48.0.0-aarch64-linux.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v48.0.0/wasmtime-v48.0.0-aarch64-linux.tar.xz ea20f14785f908afcd07063ade8d89b82b15228d9e6be426ea12ebecbc9c0c8b +wasmtime v48.0.0 macos x86_64 wasmtime-v48.0.0-x86_64-macos.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v48.0.0/wasmtime-v48.0.0-x86_64-macos.tar.xz 3cf3c8d1f67184183acd61567e758046d40dddd169295dc9e7570b2b20505ac9 +wasmtime v48.0.0 macos aarch64 wasmtime-v48.0.0-aarch64-macos.tar.xz https://github.com/bytecodealliance/wasmtime/releases/download/v48.0.0/wasmtime-v48.0.0-aarch64-macos.tar.xz b62f687c7b19dd7c04b44ba5d6170a92a2c99bfc190887348aee9323e3703e60 +wasm-tools 1.257.1 linux x86_64 wasm-tools-1.257.1-x86_64-linux.tar.gz https://github.com/bytecodealliance/wasm-tools/releases/download/v1.257.1/wasm-tools-1.257.1-x86_64-linux.tar.gz acea0a6f71b15f3b2d394f97453398f29099cec52df93bb25741a22561639b24 +wasm-tools 1.257.1 linux aarch64 wasm-tools-1.257.1-aarch64-linux.tar.gz https://github.com/bytecodealliance/wasm-tools/releases/download/v1.257.1/wasm-tools-1.257.1-aarch64-linux.tar.gz ff23fa2ed1bf46c590ebaff9ec585c941b820dd56384080466dabc61ed829178 +wasm-tools 1.257.1 macos x86_64 wasm-tools-1.257.1-x86_64-macos.tar.gz https://github.com/bytecodealliance/wasm-tools/releases/download/v1.257.1/wasm-tools-1.257.1-x86_64-macos.tar.gz e35977a05d9e04b8e20208db5d2cd67031d3f782be2da763296b6a8ba13527ce +wasm-tools 1.257.1 macos aarch64 wasm-tools-1.257.1-aarch64-macos.tar.gz https://github.com/bytecodealliance/wasm-tools/releases/download/v1.257.1/wasm-tools-1.257.1-aarch64-macos.tar.gz 1d1ced29aa8320733a7e27452434e66f9bcb61ce39a00ee63229594d71f8544d 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 f139574f..28f5e4cf 100644 --- a/.config/nextest.toml +++ b/.config/nextest.toml @@ -21,6 +21,10 @@ slow-timeout = { period = "60s", terminate-after = 8 } 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.default.overrides]] +filter = 'binary(=rsa_wycheproof) & test(=wycheproof_oaep_sha2_decrypt_vectors_match_expected_results)' +slow-timeout = { period = "60s", terminate-after = 4 } + [profile.commit] status-level = "fail" success-output = "never" diff --git a/.config/rail.toml b/.config/rail.toml index ef09c902..fe4535a2 100644 --- a/.config/rail.toml +++ b/.config/rail.toml @@ -49,7 +49,7 @@ require_changelog_entries = false require_release_notes = true release_notes_dir = "release-notes" pre_1_breaking_bump = "minor" -unconventional_commits = "allow" # Required .changes files own bumps and user-facing release notes. +unconventional_commits = "allow" # Required .changes files own bumps and user-facing release notes. semver_check = "warn" change_dir = ".changes" require_change_files = ["rscrypto"] @@ -58,16 +58,16 @@ remote_effects = "push" # Release automation pushes commits/tags; the repository [release.changelog] path = "CHANGELOG.md" relative_to = "workspace" -entry_format = "- {scope}{breaking}{description}{prs} ({sha_link})" # Entry placeholders: scope, breaking, description, prs, sha, sha_link, type -emoji = true # Render emoji in changelog section headers -group_order = [] # Render reviewed .changes entries only; commit subjects are not release notes. -fallback = "skip" # Drop commit-derived entries. +entry_format = "- {scope}{breaking}{description}{prs} ({sha_link})" # Entry placeholders: scope, breaking, description, prs, sha, sha_link, type +emoji = true # Render emoji in changelog section headers +group_order = [] # Render reviewed .changes entries only; commit subjects are not release notes. +fallback = "skip" # Drop commit-derived entries. [release.changelog.filters] skip_types = ["chore", "ci"] skip_scopes = [] -include_paths = [] # Optional changelog attribution include globs -exclude_paths = [] # Optional changelog attribution exclude globs +include_paths = [] # Optional changelog attribution include globs +exclude_paths = [] # Optional changelog attribution exclude globs [change-detection] diff --git a/.config/target-matrix.json b/.config/target-matrix.json index 9294bc28..a9844bf8 100644 --- a/.config/target-matrix.json +++ b/.config/target-matrix.json @@ -1,13 +1,7 @@ { "groups": { - "win": [ - "aarch64-pc-windows-msvc", - "x86_64-pc-windows-msvc" - ], - "macos": [ - "aarch64-apple-darwin", - "x86_64-apple-darwin" - ], + "win": ["aarch64-pc-windows-msvc", "x86_64-pc-windows-msvc"], + "macos": ["aarch64-apple-darwin", "x86_64-apple-darwin"], "linux": [ "aarch64-unknown-linux-gnu", "aarch64-unknown-linux-musl", @@ -15,25 +9,19 @@ "x86_64-unknown-linux-gnu", "x86_64-unknown-linux-musl" ], - "ibm": [ - "powerpc64le-unknown-linux-gnu", - "s390x-unknown-linux-gnu" - ], + "ibm": ["powerpc64le-unknown-linux-gnu", "s390x-unknown-linux-gnu"], "no_std": [ "aarch64-unknown-none", "riscv32imac-unknown-none-elf", "thumbv6m-none-eabi", "x86_64-unknown-none" ], - "wasm": [ - "wasm32-unknown-unknown", - "wasm32-wasip1" - ] + "wasm": ["wasm32-unknown-unknown", "wasm32-wasip1"] }, "ci": [ - { "name": "aarch64-pc-windows-msvc", "type": "gha", "runner": "windows-11-arm" }, + { "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": "x86_64-unknown-linux-gnu", "type": "runson", "pool": "linux-x64-ci" } + { "name": "x86_64-pc-windows-msvc", "type": "gha", "runner": "windows-latest" }, + { "name": "x86_64-unknown-linux-gnu", "type": "runson", "pool": "linux-x64-ci" } ] } diff --git a/.github/actions/setup/action.yaml b/.github/actions/setup/action.yaml index 99a2b35d..d45c299f 100644 --- a/.github/actions/setup/action.yaml +++ b/.github/actions/setup/action.yaml @@ -35,7 +35,7 @@ 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@46910bf61b41721b0579f237e186afb35477007a # v2.3.0 + uses: runs-on/action@46910bf61b41721b0579f237e186afb35477007a # v2.3.0 - name: Setup Toolchain uses: ./.github/actions/setup-toolchain @@ -52,7 +52,7 @@ runs: - name: Setup Rust Cache if: inputs.enable-rust-cache == 'true' - uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2 + 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/rulesets/protect-main.json b/.github/rulesets/protect-main.json index b20c034b..2a597dae 100644 --- a/.github/rulesets/protect-main.json +++ b/.github/rulesets/protect-main.json @@ -5,9 +5,7 @@ "conditions": { "ref_name": { "exclude": [], - "include": [ - "~DEFAULT_BRANCH" - ] + "include": ["~DEFAULT_BRANCH"] } }, "bypass_actors": [], @@ -27,11 +25,7 @@ "require_code_owner_review": false, "require_last_push_approval": false, "required_review_thread_resolution": true, - "allowed_merge_methods": [ - "merge", - "squash", - "rebase" - ] + "allowed_merge_methods": ["merge", "squash", "rebase"] } }, { diff --git a/.github/rulesets/protect-release-tags.json b/.github/rulesets/protect-release-tags.json index ea714bd0..1dfe0072 100644 --- a/.github/rulesets/protect-release-tags.json +++ b/.github/rulesets/protect-release-tags.json @@ -5,9 +5,7 @@ "conditions": { "ref_name": { "exclude": [], - "include": [ - "refs/tags/v*" - ] + "include": ["refs/tags/v*"] } }, "bypass_actors": [], diff --git a/.github/workflows/_ci-suite.yaml b/.github/workflows/_ci-suite.yaml index f4c7357c..d1cdbf08 100644 --- a/.github/workflows/_ci-suite.yaml +++ b/.github/workflows/_ci-suite.yaml @@ -42,7 +42,7 @@ jobs: ci: ${{ steps.matrix.outputs.ci }} steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Load matrix diff --git a/.github/workflows/_rust-job.yaml b/.github/workflows/_rust-job.yaml index e0ef686c..5f02f3f9 100644 --- a/.github/workflows/_rust-job.yaml +++ b/.github/workflows/_rust-job.yaml @@ -123,7 +123,7 @@ jobs: timeout-minutes: ${{ inputs.timeout_minutes }} steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false @@ -163,7 +163,7 @@ jobs: - name: Upload Artifact after completion if: ${{ !cancelled() && inputs.artifact_name != '' && inputs.artifact_path != '' && inputs.artifact_always }} - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ inputs.artifact_name }} path: ${{ inputs.artifact_path }} @@ -172,7 +172,7 @@ jobs: - name: Upload Artifact (success) if: inputs.artifact_name != '' && inputs.artifact_path != '' && !inputs.artifact_always - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: ${{ inputs.artifact_name }} path: ${{ inputs.artifact_path }} diff --git a/.github/workflows/bench.yaml b/.github/workflows/bench.yaml index a3ebd8fe..33ccc0bc 100644 --- a/.github/workflows/bench.yaml +++ b/.github/workflows/bench.yaml @@ -40,7 +40,7 @@ jobs: matrix: ${{ steps.plan.outputs.matrix }} steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 persist-credentials: false diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6e890908..742ffe18 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -29,7 +29,7 @@ jobs: surfaces_json: ${{ steps.rail.outputs.surfaces-json }} steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false @@ -37,7 +37,7 @@ jobs: - name: Build Plan (PR) if: github.event_name == 'pull_request' id: rail - uses: loadingalias/cargo-rail-action@ebc8ef2740efefe2cd5c42826344a8f1213113ae # v6.1.2 + uses: loadingalias/cargo-rail-action@ebc8ef2740efefe2cd5c42826344a8f1213113ae # v6.1.2 with: version: "0.21.0" checksum: required diff --git a/.github/workflows/ct.yaml b/.github/workflows/ct.yaml index 0c7fabbf..57c4dff5 100644 --- a/.github/workflows/ct.yaml +++ b/.github/workflows/ct.yaml @@ -109,7 +109,7 @@ jobs: matrix: ${{ steps.plan.outputs.matrix }} steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index c38fd0a4..ed5762d4 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -54,7 +54,7 @@ jobs: s390x_ct_run_url: ${{ steps.s390x_ct.outputs.s390x_run_url }} steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false @@ -109,7 +109,7 @@ jobs: - name: Checkout reviewed recovery tooling if: github.event_name == 'workflow_dispatch' - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 1 path: target/release-automation @@ -152,7 +152,7 @@ jobs: run: scripts/ci/release-preflight.sh --crate rscrypto --tag "$RELEASE_TAG" - name: Preserve validated release inputs - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: release-inputs-${{ steps.identity.outputs.release_commit }} path: | @@ -176,7 +176,7 @@ jobs: artifact-metadata: write steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false @@ -192,7 +192,7 @@ jobs: enable-rust-cache: true - name: Restore validated release inputs - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: release-inputs-${{ needs.preflight.outputs.release_commit }} path: target/package @@ -234,12 +234,12 @@ jobs: } >> "$GITHUB_OUTPUT" - name: Attest crate provenance - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-path: ${{ steps.package.outputs.crate_path }} - name: Attest source archive provenance - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-path: ${{ steps.package.outputs.source_path }} @@ -256,12 +256,12 @@ jobs: --allow-redacted-bypass - name: Attest repository controls - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-path: ${{ steps.repository_controls.outputs.evidence_path }} - name: Download non-RISC-V CT evidence - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: ct-* path: ct-release-artifacts @@ -274,7 +274,7 @@ jobs: - name: Download recovered s390x CT evidence if: needs.preflight.outputs.s390x_ct_run_id != '' - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ct-raw-ibm-s390x path: ct-release-artifacts/ct-raw-ibm-s390x @@ -282,7 +282,7 @@ jobs: run-id: ${{ needs.preflight.outputs.s390x_ct_run_id }} - name: Download RISC-V CT evidence - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: name: ct-raw-rise-riscv path: ct-release-artifacts/ct-raw-rise-riscv @@ -304,7 +304,7 @@ jobs: --evidence-commit "$EVIDENCE_COMMIT" - name: Attest CT evidence provenance - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-path: ${{ steps.ct_evidence.outputs.bundle_path }} @@ -334,7 +334,7 @@ jobs: --output "release-artifacts/rscrypto-${CRATE_VERSION}-release-manifest.json" - name: Attest release identity manifest - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-path: ${{ steps.release_manifest.outputs.manifest_path }} @@ -382,7 +382,7 @@ jobs: } > SHA256SUMS - name: Attest release checksums - uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 + uses: actions/attest-build-provenance@4d101475d8b20a2381f78447822ac1eab6504dd8 # v4.2.2 with: subject-path: SHA256SUMS @@ -473,7 +473,7 @@ jobs: - name: Authenticate with crates.io if: steps.existing.outputs.published != 'true' id: auth - uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5 + uses: rust-lang/crates-io-auth-action@c6f97d42243bad5fab37ca0427f495c86d5b1a18 # v1.0.5 - name: Publish to crates.io if: steps.existing.outputs.published != 'true' diff --git a/.github/workflows/scorecard.yaml b/.github/workflows/scorecard.yaml index bb496e1f..4d409d2b 100644 --- a/.github/workflows/scorecard.yaml +++ b/.github/workflows/scorecard.yaml @@ -23,18 +23,18 @@ jobs: security-events: write steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false - name: Run Scorecard - uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4 + uses: ossf/scorecard-action@2d1146689b8cda280b9bc96326124645441f03bc # v2.4.4 with: results_file: scorecard.sarif results_format: sarif publish_results: true - name: Upload SARIF - uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 + uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7 with: sarif_file: scorecard.sarif diff --git a/.github/workflows/weekly.yaml b/.github/workflows/weekly.yaml index cb9de23e..c06fd325 100644 --- a/.github/workflows/weekly.yaml +++ b/.github/workflows/weekly.yaml @@ -219,7 +219,7 @@ jobs: timeout-minutes: 120 steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: fetch-depth: 0 persist-credentials: false @@ -234,7 +234,7 @@ jobs: enable-rust-cache: "true" - name: Download fuzz corpus artifacts - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 with: pattern: fuzz-output-weekly-* path: fuzz-output-artifacts @@ -261,7 +261,7 @@ jobs: run: scripts/ci/install-codecov.sh - name: Upload to Codecov - uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 + uses: codecov/codecov-action@fb8b3582c8e4def4969c97caa2f19720cb33a72f # v7.0.0 with: binary: ${{ steps.codecov.outputs.binary }} token: ${{ secrets.CODECOV_TOKEN }} @@ -271,7 +271,7 @@ jobs: fail_ci_if_error: true - name: Upload Coverage Artifacts - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: coverage-reports path: | @@ -283,7 +283,19 @@ jobs: # The mode-specific terminal name is part of the release qualification contract. complete: name: Complete (${{ needs.mode.outputs.mode }}) - needs: [mode, suite, miri, miri-tree-borrows, fuzzing, asan-fuzzing, mlkem-graviton, ct, rsa, coverage] + needs: + [ + mode, + suite, + miri, + miri-tree-borrows, + fuzzing, + asan-fuzzing, + mlkem-graviton, + ct, + rsa, + coverage, + ] if: ${{ !cancelled() }} runs-on: ubuntu-latest steps: diff --git a/.gitignore b/.gitignore index cac10004..997b9803 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ Thumbs.db # LLM/AI Harness/Agents .claude .agents -.codex AGENTS.md # Zed/IDE diff --git a/CHANGELOG.md b/CHANGELOG.md index e7a8b17b..9751e9bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,14 +1,11 @@ # Changelog - - ## [0.8.1](https://github.com/loadingalias/rscrypto/compare/v0.8.0...v0.8.1) - 2026-08-11 - Align canonical install examples and the security support matrix with rscrypto 0.8.0. - Allow Argon2idPassword and ScryptPassword to generate canonical PHC records from a caller-owned entropy source without enabling getrandom. Add exact-width HMAC-SHA256 verification for protocols that specify a 64-bit truncated tag. - ## [0.8.0](https://github.com/loadingalias/rscrypto/compare/v0.7.8...v0.8.0) - 2026-08-02 - Authenticate CI tool downloads and package-manager installs. @@ -118,7 +115,7 @@ also no longer do, because they can retain a MAC key. The `Mac` trait no longer requires `Clone`; reuse or share one owner, or construct another keyed state explicitly. - + Secret-bearing SHA/HMAC, Ed25519, Keccak, BLAKE2, and BLAKE3 temporaries now clear at their actual finalization, reset, transfer, or drop boundary, including keyed XOF and parallel heap scratch. The optimized zeroization gate covers @@ -137,7 +134,6 @@ - Verify imported assembly and cryptographic test vectors against immutable upstream sources and deterministic transforms. - ## [0.7.8](https://github.com/loadingalias/rscrypto/compare/v0.6.4...v0.7.8) - 2026-07-15 ### Security and compatibility @@ -181,7 +177,6 @@ test tooling. - Updated the ECDSA differential-test oracles for the current `p256` behavior. - ## [0.6.2](https://github.com/loadingalias/rscrypto/compare/v0.6.1...v0.6.2) - 2026-07-04 ### 🏗️ Build @@ -232,16 +227,12 @@ - hashes: delete Darwin SHA-2 assembly for owned aarch64 kernels aead: expose owned ChaCha20-Poly1305 diagnostic path auth: widen public RSA CIOS threshold and add portable diagnostics bench: split AEAD and RSA assembly replacement benchmarks benchmarks: refresh BLAKE3 overview and README perf chart docs: track assembly ownership notebook decisions ([76f4fa3](https://github.com/loadingalias/rscrypto/commit/76f4fa343412c39c8c6a3bd1b7f3e866a42513aa)) - hashes: retarget BLAKE3 x86 compression and expose diag kernels aead: mark AES-GCM assembly as rscrypto-owned auth: mark RSA assembly as rscrypto-owned bench: add BLAKE3 diagnostic benches and gap report build: gate assembly provenance with ledger check ([e643fe5](https://github.com/loadingalias/rscrypto/commit/e643fe55af7311aa2ccf17f18613e8a63871d08a)) - - ## [0.6.1](https://github.com/loadingalias/rscrypto/compare/v0.6.0...v0.6.1) - 2026-06-24 ### 📦 Other Changes - crypto: validate platform overrides and gate s390x AEGIS ([83bcde8](https://github.com/loadingalias/rscrypto/commit/83bcde865334691387c2d7abb5242f1cfc3479e1)) - - ## [0.6.0](https://github.com/loadingalias/rscrypto/compare/v0.5.0...v0.6.0) - 2026-06-23 ### 👷 CI @@ -290,8 +281,6 @@ - auth: fix s390x ML-KEM barrier build mode ([75e7d0a](https://github.com/loadingalias/rscrypto/commit/75e7d0a8821a4cdaa89082e1fef3e00f9334fcd5)) - auth: harden s390x ML-KEM constant-time arithmetic ([446e3d4](https://github.com/loadingalias/rscrypto/commit/446e3d46fc2b3298b79d502904bab4bb0131e1c8)) - - ## [0.5.0](https://github.com/loadingalias/rscrypto/compare/v0.4.1...v0.5.0) - 2026-06-14 ### 📝 Documentation @@ -303,8 +292,6 @@ - auth: fix RSA-2048 leakage fixture policy ([c8f6886](https://github.com/loadingalias/rscrypto/commit/c8f6886f343c3771d7bdab14261b703b7277b8a4)) - crypto: harden secret handling and CT validation paths ci: scope CT evidence to required primitives and repair macOS RSA fixtures docs: align migration guidance with hardened verification defaults bench: refresh crypto benches for typed APIs checksum: clarify CRC64 reference constants workspace: align feature metadata and lockfiles for CT tooling ([30ddfb6](https://github.com/loadingalias/rscrypto/commit/30ddfb6632d2d364574e9c4379775cb669219f53)) - - ## [0.4.1](https://github.com/loadingalias/rscrypto/compare/v0.4.0...v0.4.1) - 2026-06-13 ### 📝 Documentation @@ -319,8 +306,6 @@ - auth: harden ECDSA P-256/P-384 CT backends ci: add ECDSA DudeCT diagnostics and target-scoped CT policy ([82db892](https://github.com/loadingalias/rscrypto/commit/82db8924e88136ac3070e892602cb38f4a25d620)) - auth: add ECDSA P-256/P-384 signing and CT coverage ([f24375d](https://github.com/loadingalias/rscrypto/commit/f24375d31784527ee0964b9dd871d64d5c0a6991)) - - ## [0.4.0](https://github.com/loadingalias/rscrypto/compare/v0.3.1...v0.4.0) - 2026-06-09 ### 🏗️ Build @@ -348,8 +333,6 @@ - auth: clear CT helper slice lints hashes: clear Blake2b diagnostic slice lints ([32f0e12](https://github.com/loadingalias/rscrypto/commit/32f0e12c5cfb5b0a15e72651d8aff57af8c66a9a)) - auth: align RSA keygen with FIPS 186-5 A.1.3 ([5ceb703](https://github.com/loadingalias/rscrypto/commit/5ceb703cea4b5355eff022fbb1013f2bdcf30e19)) - - ## [0.3.1](https://github.com/loadingalias/rscrypto/compare/v0.3.0...v0.3.1) - 2026-06-01 ### 📦 Other Changes @@ -357,8 +340,6 @@ - workspace: enable cargo-rail release publishing ([bb7ec88](https://github.com/loadingalias/rscrypto/commit/bb7ec88d59a2cce916d82af9b182f4905be6600b)) - bench: add Ascon coverage and refresh HMAC measurement shape ([b06b946](https://github.com/loadingalias/rscrypto/commit/b06b946d217248d634c43688211c9ebe5c2692e8)) - - ## [0.3.0](https://github.com/loadingalias/rscrypto/compare/v0.2.0...v0.3.0) - 2026-05-28 ### 🏗️ Build @@ -384,8 +365,6 @@ - workspace: refresh README release snippets and local asset ignores ([1d838fe](https://github.com/loadingalias/rscrypto/commit/1d838febe1e8d2d751b8c38d32d838b9a5c2db04)) - workspace: fix fuzz support path dependency and refresh locks ci: refresh weekly action pins ([a906007](https://github.com/loadingalias/rscrypto/commit/a906007a4b91c691c99ada30883243c714061cd2)) - - ## [0.2.0](https://github.com/loadingalias/rscrypto/compare/v0.1.1...v0.2.0) - 2026-05-17 ### 📦 Other Changes @@ -408,16 +387,12 @@ - aead: ship AES-128-GCM and AES-128-GCM-SIV with full SIMD kernel coverage hashes: add Blake3KeyedHash type with constant-time equality bench: add aws-lc-rs, ring, dryoc, gxhash, ahash, and foldhash competitor rows workspace: wire competitor dev-deps, ungate internal hex module, refresh docs ([0a39948](https://github.com/loadingalias/rscrypto/commit/0a39948a73df05c816163dc7c49b043efc976326)) - workspace: sharpen adoption docs and publish migration guides ([30f9846](https://github.com/loadingalias/rscrypto/commit/30f9846d52a8e6ce0a3d39adf9e0dacfda5cd5f4)) - - ## [0.1.1](https://github.com/loadingalias/rscrypto/compare/v0.1.0...v0.1.1) - 2026-05-02 ### 🏗️ Build - trim tests/testdata/benches from published crate include list docs: fix README quick start imports and add Xxh3 FastHash trait workspace: wire README into doctest harness via ReadmeDoctests hook ([f16a44f](https://github.com/loadingalias/rscrypto/commit/f16a44f5db5527f77ad294e717c64f1b12930bdb)) - - ## [0.1.0](https://github.com/loadingalias/rscrypto/releases/tag/v0.1.0) - 2026-05-02 ### 🐛 Bug Fixes @@ -746,7 +721,7 @@ - Revert "hashes/blake3: debranch streaming chunk compression and simplify xof" ([b0c088b](https://github.com/loadingalias/rscrypto/commit/b0c088b48e262a4abc234a5cf62f2bdaadca1372)) - hashes/blake3: document AJ bench regression and reject via 70e7519 ([ea7f2c5](https://github.com/loadingalias/rscrypto/commit/ea7f2c5c9b6413e561bdbe01174e3c2602d74918)) - Revert "hashes/blake3: use kernel-aware xof root-hash path and retune x86" ([70e7519](https://github.com/loadingalias/rscrypto/commit/70e7519ec2670b53f46f32b0f347bb572bca1477)) -- hashes/blake3: record AI regression from run 22559324946 and revert decision ([ddf5c5a](https://github.com/loadingalias/rscrypto/commit/ddf5c5ac25e440f5a26a46bb2e3b892d42f2a353)) +- hashes/blake3: record AI regression from run 22559324946 and revert decision ([ddf5c5a](https://github.com/loadingalias/rscrypto/commit/ddf5c5ac25e440f5a26a46bb2e3b892d42f2a353)) - Revert "hashes: inline blake3 OutputState compress dispatch" ([a787d48](https://github.com/loadingalias/rscrypto/commit/a787d4811af15ed848dcf633a8b0cbde8f069e1e)) - hashes/blake3: revert AH lazy tiny xof tail-hint path; record 22556963869 regression ([800445d](https://github.com/loadingalias/rscrypto/commit/800445deda3f0634bfce48bbd1389a2b4fe9034a)) - hashes/blake3: lazily optimize tiny xof init+read with single-chunk tail hint ([03d9943](https://github.com/loadingalias/rscrypto/commit/03d9943e50fad6b5c7bbe5f9e09ace4d0fa6e7d8)) @@ -762,7 +737,7 @@ - hashes: select size-class kernel for single-chunk finalize_xof ([aea12be](https://github.com/loadingalias/rscrypto/commit/aea12be21542ba146af042819f0dd4546c4ec62a)) - ashes: revert Blake3Xof lazy scratch-buffer initialization ([19bc72b](https://github.com/loadingalias/rscrypto/commit/19bc72b286c43140a48497b9f06d1fa92753e156)) - hashes: lazily initialize Blake3Xof scratch buffers ([bd61c53](https://github.com/loadingalias/rscrypto/commit/bd61c5392b35289685ab277815aefa1414f93f83)) -- hashes/blake3: optimize short streaming/xof paths and retune 64KiB+ parallel admission ([b493be1](https://github.com/loadingalias/rscrypto/commit/b493be1a0bb220ee01703aa9ba8f9c6d0e84acdc)) +- hashes/blake3: optimize short streaming/xof paths and retune 64KiB+ parallel admission ([b493be1](https://github.com/loadingalias/rscrypto/commit/b493be1a0bb220ee01703aa9ba8f9c6d0e84acdc)) - checksum: add aarch64 crc16 pmull+eor3 kernels and wire graviton dispatch ([b34a9b9](https://github.com/loadingalias/rscrypto/commit/b34a9b9e0ede0b02661aef95847fe8a8006b9b5a)) - hashes: promote streaming kernel after first full chunk ([b160f5a](https://github.com/loadingalias/rscrypto/commit/b160f5acda892e6f4f4aa879bff7ed2736f2f2d5)) - checksum/hashes: remove hot-path overhead in arm crc16 2way and blake3 dispatch accessors ([171d60d](https://github.com/loadingalias/rscrypto/commit/171d60de8df636e0040257965ec5784fb07aed19)) @@ -971,7 +946,7 @@ - rscrypto: fixing the tests ([88ec68c](https://github.com/loadingalias/rscrypto/commit/88ec68c5a8aaa900d7ce9cfcd5ee919b23c39f63)) - rscrypto: tuning ([0938d5e](https://github.com/loadingalias/rscrypto/commit/0938d5e1b49873484193ef6fe62e310679a32fab)) - rscrypto: update for CI ([eef34c9](https://github.com/loadingalias/rscrypto/commit/eef34c9702c82b66dde8148b2c1b2c68c0a5e36b)) -- rscrypto: refactor: pre-release cleanup for v0.1.0 - De-macro CRC-64 types for better auditability Replace define_crc64_type! macro with explicit implementations matching the CRC-32 pattern - Fix repository URLs in CONTRIBUTING.md - Document tuning coverage in README.md - Add platform coverage matrix showing measured vs inferred presets and list hardware we need contributions for - Remove speculative flag from Tune struct. The field added complexity without practical benefit; tuning docs now indicate extrapolated values in comments instead ([f89284d](https://github.com/loadingalias/rscrypto/commit/f89284db0f44f71fe3eae5df4b1c50193bf7f28b)) +- rscrypto: refactor: pre-release cleanup for v0.1.0 - De-macro CRC-64 types for better auditability Replace define_crc64_type! macro with explicit implementations matching the CRC-32 pattern - Fix repository URLs in CONTRIBUTING.md - Document tuning coverage in README.md - Add platform coverage matrix showing measured vs inferred presets and list hardware we need contributions for - Remove speculative flag from Tune struct. The field added complexity without practical benefit; tuning docs now indicate extrapolated values in comments instead ([f89284d](https://github.com/loadingalias/rscrypto/commit/f89284db0f44f71fe3eae5df4b1c50193bf7f28b)) - rscrypto: fixing the tuning engine to use the KernelSet and update the existing tables in dispatch.rs ([ce9b4a5](https://github.com/loadingalias/rscrypto/commit/ce9b4a51e844f40d2f8187d929e1576ed6674195)) - rscrypto: improved performance everywhere via an architectural update. dropped the policy/runtime code for the determinism of compile-time - based on benches. ([89a5dcf](https://github.com/loadingalias/rscrypto/commit/89a5dcfbf7a4969623987f115446a052f0b5e1c7)) - rscrypto: tuning ([179c144](https://github.com/loadingalias/rscrypto/commit/179c1443ceece7b81ce4dae8f006803a292f5935)) @@ -1088,7 +1063,7 @@ - rscrypto: added the weekly.yaml for fuzzing/etc. ([9081378](https://github.com/loadingalias/rscrypto/commit/9081378dcf5170dab22c5fbae90d45b2e6cdda96)) - rscrypto: fixing the Barrett reduction order; it was backwards. ([1f4b4ab](https://github.com/loadingalias/rscrypto/commit/1f4b4ab5fbc43a7bd46085567874a5defca127e9)) - rscrypto: added new no_std targets; removed the 'std' gate because it's just not needed and then added hand-rolled errors ([51283a0](https://github.com/loadingalias/rscrypto/commit/51283a05f6f19dfb0aaff944eea48e3dfec89fa5)) -- rscrypto: ci: fix CI failures and expand no_std target coverage; setup new Namespace 'profiles' for rscrypto, too. ([625044e](https://github.com/loadingalias/rscrypto/commit/625044e71d469de09f80b5d7d7a7f11a6f4fd4ca)) +- rscrypto: ci: fix CI failures and expand no_std target coverage; setup new Namespace 'profiles' for rscrypto, too. ([625044e](https://github.com/loadingalias/rscrypto/commit/625044e71d469de09f80b5d7d7a7f11a6f4fd4ca)) - rscrypto: fix the cicd issues; simple ([632a18a](https://github.com/loadingalias/rscrypto/commit/632a18ac4779e3939d95c0cb9b1ec59d1a4e3925)) - rscrypto: wiring the 'pre-push' hook/script. ([e76634f](https://github.com/loadingalias/rscrypto/commit/e76634f87b5a88a2b97216f9ac4935e0d05789e2)) - rscrypto: initial commit ([3d06ff9](https://github.com/loadingalias/rscrypto/commit/3d06ff975c0baaed6b2067df041b11266bca696b)) @@ -1106,7 +1081,7 @@ - CRC32 aarch64 dispatch bypass, SHA-3 sponge output extraction; XXH3 small-input codegen overhaul — cold dispatch + typed mix16_b ([09f0dc2](https://github.com/loadingalias/rscrypto/commit/09f0dc2f44b055c1fb88ca5a97713415cd996536)) - SHA-512 single-block rotation schedule + vector K addition ([a291d28](https://github.com/loadingalias/rscrypto/commit/a291d28a7fcdb6a8538d09ceb7c23cc5c61fbed0)) - SHA-512 Zen5 dispatch fix, Keccak θ rewrite, RapidHash codegen tune ([8a23f40](https://github.com/loadingalias/rscrypto/commit/8a23f40b5ad8313de53a80dc169ed0a2d575a3c8)) -- RapidHashFast inner core, HKDF midstate cache, Ed25519 field/point optimizations RapidHash: - RapidHashFast64/128 now uses a dedicated inner-algorithm core instead of V3-no-avalanche. Size-tuned dispatch: 3-stream (49-400B), 7-stream (>400B), cold-path separation for codegen quality. Oracle: rapidhash::fast::RapidHasher. - RapidHash64/128 (standard V3) unchanged. Auth: - HKDF-SHA256: cache HmacSha256 keyed with PRK at extract time. expand() now resets (1 memcpy) instead of re-creating (2 SHA-256 compressions) per chunk. - Ed25519 field: dedicated squaring — 15 wide muls vs 25 (40% fewer). - Ed25519 point: dedicated dbl-2008-hwcd doubling — 4 sq + 4 mul, no D2 multiply. - Ed25519 point: precomputed 16-entry basepoint table with 4-bit windowed scalar mul — adds drop from ~128 to ~60. - Ed25519 verify: Straus/Shamir interleaved [s]B + [-h]A in one 256-bit scan, halving doublings from 512 to 256. ([89dbfc2](https://github.com/loadingalias/rscrypto/commit/89dbfc2c29dd27feb37736e155f2d57996560953)) +- RapidHashFast inner core, HKDF midstate cache, Ed25519 field/point optimizations RapidHash: - RapidHashFast64/128 now uses a dedicated inner-algorithm core instead of V3-no-avalanche. Size-tuned dispatch: 3-stream (49-400B), 7-stream (>400B), cold-path separation for codegen quality. Oracle: rapidhash::fast::RapidHasher. - RapidHash64/128 (standard V3) unchanged. Auth: - HKDF-SHA256: cache HmacSha256 keyed with PRK at extract time. expand() now resets (1 memcpy) instead of re-creating (2 SHA-256 compressions) per chunk. - Ed25519 field: dedicated squaring — 15 wide muls vs 25 (40% fewer). - Ed25519 point: dedicated dbl-2008-hwcd doubling — 4 sq + 4 mul, no D2 multiply. - Ed25519 point: precomputed 16-entry basepoint table with 4-bit windowed scalar mul — adds drop from ~128 to ~60. - Ed25519 verify: Straus/Shamir interleaved [s]B + [-h]A in one 256-bit scan, halving doublings from 512 to 256. ([89dbfc2](https://github.com/loadingalias/rscrypto/commit/89dbfc2c29dd27feb37736e155f2d57996560953)) - bypass dispatch overhead for small fast-hash inputs ([e12085c](https://github.com/loadingalias/rscrypto/commit/e12085c77afc42cceb70d63102a3c1a2ee7ea95a)) - cascade AVX-512 sub-degree tails to AVX2 for BLAKE3 hash_many and parent compression ([17f47d6](https://github.com/loadingalias/rscrypto/commit/17f47d6a52df857a03bce7a5473965f70b962d66)) - fix BLAKE3 XOF ~250ns Drop overhead, upgrade zeroize to word-sized writes feat: add SHA-NI/SHA2 CE hardware acceleration for SHA-256/SHA-224 ([9f76def](https://github.com/loadingalias/rscrypto/commit/9f76defed3d793386ae9d058df3bdd7e9a08945d)) @@ -1122,6 +1097,4 @@ - rustfmt ([1c28ea2](https://github.com/loadingalias/rscrypto/commit/1c28ea2c1142013360ea446ad3bc5a6c04c29543)) - - All notable changes to rscrypto will be documented in this file. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 4f7959ed..bc30be55 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -5,12 +5,12 @@ releasable history; it is not a working branch. ## Gate model -| Action | Purpose | -|---|---| -| Commit | Creates a local, reviewable checkpoint. | -| Push | Shares the branch after fast, change-aware local checks. | +| Action | Purpose | +| ------------ | --------------------------------------------------------- | +| Commit | Creates a local, reviewable checkpoint. | +| Push | Shares the branch after fast, change-aware local checks. | | Pull request | Declares merge intent and runs CI on the proposed change. | -| Merge | Adds the reviewed change to protected `main`. | +| Merge | Adds the reviewed change to protected `main`. | The protected branch requires an up-to-date `Complete` result and has no bypass actors, so merged commits do not repeat the pull-request suite. Release @@ -63,15 +63,15 @@ just test --all Use deeper checks where the risk requires them: -| Change | Required validation | -|---|---| -| Parser, import, DER, PHC, hex, or untrusted input | `just test-fuzz ` or `just test-fuzz --all` | -| `unsafe`, SIMD, ASM, or dispatch | Backend equivalence tests and `just test-fuzz-asan --all` where the target runs natively | -| Portable unsafe path | `just test-miri` | -| Constant-time claim boundary | `just ct-full --target `; update `ct.toml` only with matching evidence | -| Apple Silicon RSA assembly | `just test-rsa-macos-asm` on a physical local Arm64 Mac; GitHub Actions intentionally has no macOS runner | -| Public API change | `cargo semver-checks --package rscrypto --all-features` | -| Dependency or release change | `cargo deny check all` and `cargo audit --ignore RUSTSEC-2023-0071` | +| Change | Required validation | +| ------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | +| Parser, import, DER, PHC, hex, or untrusted input | `just test-fuzz ` or `just test-fuzz --all` | +| `unsafe`, SIMD, ASM, or dispatch | Backend equivalence tests and `just test-fuzz-asan --all` where the target runs natively | +| Portable unsafe path | `just test-miri` | +| Constant-time claim boundary | `just ct-full --target `; update `ct.toml` only with matching evidence | +| Apple Silicon RSA assembly | `just test-rsa-macos-asm` on a physical local Arm64 Mac; GitHub Actions intentionally has no macOS runner | +| Public API change | `cargo semver-checks --package rscrypto --all-features` | +| Dependency or release change | `cargo deny check all` and `cargo audit --ignore RUSTSEC-2023-0071` | ## Review and commit @@ -110,6 +110,22 @@ the required `Complete` check. Resolve every open review thread and review the final diff before merging in the GitHub UI. GitHub enforces the current approval policy. +For a broad or release-sensitive pull request, run the slow physical assurance +lanes on the pushed branch before merging: + +```bash +branch=$(git branch --show-current) +test -n "$branch" && test "$branch" != main +test "$(git rev-parse HEAD)" = "$(git rev-parse '@{upstream}')" +gh workflow run weekly.yaml --ref "$branch" -f mode=assurance +gh workflow run riscv.yaml --ref "$branch" -f mode=evidence +``` + +These branch runs expose platform, constant-time, and RISC-V failures before +they reach `main`. They do not replace the release runbook's post-merge, +exact-commit evidence: a squash or merge commit has a different SHA, and +ancestor evidence is never promoted into a release claim. + ## Clean up after merge After GitHub reports the pull request merged: diff --git a/Cargo.lock b/Cargo.lock index 46569232..1f7bfc0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -185,6 +185,18 @@ version = "1.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2af50177e190e07a26ab74f8b1efbfe2ef87da2116221318cb1c2e82baf7de06" +[[package]] +name = "bincode-next" +version = "3.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d6626829353ae29293be5c86f42de5f0468bc758af074b0c7d08f07e538ccbc" +dependencies = [ + "pastey", + "rapidhash", + "serde", + "unty-next", +] + [[package]] name = "bindgen" version = "0.72.1" @@ -736,6 +748,27 @@ dependencies = [ "zeroize", ] +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.119", +] + [[package]] name = "digest" version = "0.10.7" @@ -830,6 +863,16 @@ version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e5e8f6c15a24b9a3ee5efec809ccd006d3b30e8b3bb63c39af737c7f87daa1d" +[[package]] +name = "either-or-both" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6717164c227120ba9ddf3e2b32305fc0122741d3ee41a54968eae7573ee01933" +dependencies = [ + "indexmap", + "serde", +] + [[package]] name = "elliptic-curve" version = "0.14.1" @@ -850,6 +893,12 @@ dependencies = [ "zeroize", ] +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + [[package]] name = "errno" version = "0.3.14" @@ -1015,6 +1064,44 @@ dependencies = [ "subtle", ] +[[package]] +name = "gungraun" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9300d38d30facf6870c15bc9e243edcc47e5ddc270c9d2c1ae824a00b1faea79" +dependencies = [ + "bincode-next", + "derive_more", + "gungraun-macros", + "gungraun-runner", +] + +[[package]] +name = "gungraun-macros" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7f3214bae6cfd6739f4f29edb262bae439c6393a7b7b12c4eeb96417c2e50df" +dependencies = [ + "derive_more", + "proc-macro-error3", + "proc-macro2", + "quote", + "rustc_version", + "serde", + "serde_json", + "syn 2.0.119", +] + +[[package]] +name = "gungraun-runner" +version = "0.19.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47c68c92812da579a2510a2cc702d5aa3251992b9662d4a98502ca46e0cbe16e" +dependencies = [ + "either-or-both", + "serde", +] + [[package]] name = "half" version = "2.7.1" @@ -1026,6 +1113,12 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + [[package]] name = "hax-lib" version = "0.3.7" @@ -1093,6 +1186,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown", +] + [[package]] name = "inout" version = "0.2.2" @@ -1600,6 +1703,16 @@ dependencies = [ "quote", ] +[[package]] +name = "proc-macro-error-attr3" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0084e6206a967a2dad822180626b2f6b07a3b379325e8f1ec0438e33a469ba7" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "proc-macro-error2" version = "2.0.1" @@ -1612,6 +1725,18 @@ dependencies = [ "syn 2.0.119", ] +[[package]] +name = "proc-macro-error3" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cf066225f2373bc711684792b69bdeac0356019b007e721090c24d92d5d5a50" +dependencies = [ + "proc-macro-error-attr3", + "proc-macro2", + "quote", + "syn 3.0.3", +] + [[package]] name = "proc-macro2" version = "1.0.107" @@ -1881,6 +2006,7 @@ dependencies = [ "ed25519-dalek", "fips203", "getrandom 0.4.3", + "gungraun", "hkdf", "hmac", "keccak 0.2.1", @@ -2299,6 +2425,12 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" +[[package]] +name = "unty-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16062d030850f35054e37746427b9febb74a2f24c9c6dd6fa2d0c13c5f53221e" + [[package]] name = "uuid" version = "1.24.0" diff --git a/Cargo.toml b/Cargo.toml index 108993d4..bc381376 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -219,6 +219,7 @@ criterion = { version = "^0.8.2", default-features = false, features = [ "html_reports", "plotters", ] } +gungraun = "=0.19.4" proptest = "^1.11.0" # Oracles @@ -246,8 +247,14 @@ p256 = { version = "0.14.0", default-features = false, features = ["ecdsa", "pkc 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"] } +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" @@ -399,6 +406,11 @@ name = "kmac_cshake" harness = false required-features = ["sha3", "kmac"] +[[bench]] +name = "structural" +harness = false +required-features = ["checksums", "sha2", "blake3"] + [profile.dev] opt-level = 0 debug = true diff --git a/README.md b/README.md index 26b1cb72..4de92890 100644 --- a/README.md +++ b/README.md @@ -72,28 +72,28 @@ The common API shape is one-shot when convenient and streaming when needed. ## Common workflows -| Task | Features | Start here | -|---|---|---| -| AEAD seal/open | `chacha20poly1305`, `getrandom` | [`examples/aead_seal_open.rs`](examples/aead_seal_open.rs) | -| Ed25519 and ECDSA signatures | `ed25519`, `ecdsa-p256`, `getrandom` | [`examples/signatures.rs`](examples/signatures.rs) | -| RSA-PSS verification | `rsa` | [`examples/rsa_pss_verify.rs`](examples/rsa_pss_verify.rs) | -| ML-KEM shared secret | `ml-kem`, `getrandom` | [`examples/mlkem_encapsulation.rs`](examples/mlkem_encapsulation.rs) | -| Argon2id and scrypt password hashing | `password-hashing`, `getrandom` | [`examples/password_hashing.rs`](examples/password_hashing.rs) | +| Task | Features | Start here | +| ------------------------------------ | ------------------------------------ | -------------------------------------------------------------------- | +| AEAD seal/open | `chacha20poly1305`, `getrandom` | [`examples/aead_seal_open.rs`](examples/aead_seal_open.rs) | +| Ed25519 and ECDSA signatures | `ed25519`, `ecdsa-p256`, `getrandom` | [`examples/signatures.rs`](examples/signatures.rs) | +| RSA-PSS verification | `rsa` | [`examples/rsa_pss_verify.rs`](examples/rsa_pss_verify.rs) | +| ML-KEM shared secret | `ml-kem`, `getrandom` | [`examples/mlkem_encapsulation.rs`](examples/mlkem_encapsulation.rs) | +| Argon2id and scrypt password hashing | `password-hashing`, `getrandom` | [`examples/password_hashing.rs`](examples/password_hashing.rs) | Use [`docs/types.md`](docs/types.md) when you need the full type map, and [`docs/features.md`](docs/features.md) when you need the smallest feature set. ## Capabilities -| Need | Included | Feature path | -|---|---|---| -| Cryptographic hashes | SHA-2, SHA-3, SHAKE, cSHAKE128/256, BLAKE2, BLAKE3, Ascon-Hash/XOF/CXOF | `hashes` or leaf features | -| MACs and KDFs | HMAC-SHA-2/SHA-3, KMAC128/256, standalone Poly1305, HKDF-SHA-2, PBKDF2-HMAC-SHA-2 | `macs`, `kdfs`, or leaf features | -| Password hashing | Raw Argon2d/i/id and scrypt KDFs; generated, bounded PHC password records | `password-hashing` or leaf features | +| Need | Included | Feature path | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------- | +| Cryptographic hashes | SHA-2, SHA-3, SHAKE, cSHAKE128/256, BLAKE2, BLAKE3, Ascon-Hash/XOF/CXOF | `hashes` or leaf features | +| MACs and KDFs | HMAC-SHA-2/SHA-3, KMAC128/256, standalone Poly1305, HKDF-SHA-2, PBKDF2-HMAC-SHA-2 | `macs`, `kdfs`, or leaf features | +| Password hashing | Raw Argon2d/i/id and scrypt KDFs; generated, bounded PHC password records | `password-hashing` or leaf features | | Public-key primitives | ECDSA P-256/P-384 signing/verification, Ed25519 signatures, RSA signing/verification/OAEP/RSAES-PKCS1-v1_5/key generation, X25519 key exchange, ML-KEM-512/768/1024 KEMs | `signatures`, `key-exchange`, or leaf features | -| AEAD encryption | AES-128/256-GCM, AES-128/256-GCM-SIV, ChaCha20-Poly1305, XChaCha20-Poly1305, AEGIS-256, Ascon-AEAD128 | `aead` or leaf features | -| Checksums | CRC-16, CRC-24, CRC-32, CRC-32C, CRC-64/XZ, CRC-64/NVMe | `checksums` or leaf features | -| Fast hashes | XXH3-64/128, RapidHash V3-64 | `xxh3`, `rapidhash` | +| AEAD encryption | AES-128/256-GCM, AES-128/256-GCM-SIV, ChaCha20-Poly1305, XChaCha20-Poly1305, AEGIS-256, Ascon-AEAD128 | `aead` or leaf features | +| Checksums | CRC-16, CRC-24, CRC-32, CRC-32C, CRC-64/XZ, CRC-64/NVMe | `checksums` or leaf features | +| Fast hashes | XXH3-64/128, RapidHash V3-64 | `xxh3`, `rapidhash` | Feature layers: diff --git a/SECURITY.md b/SECURITY.md index d4cf5d7e..b5901623 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -51,7 +51,7 @@ Out of scope: | Version | Supported | | ------- | --------- | -| `0.8.x` | Yes | +| `0.8.x` | Yes | Only the latest patch release in the current minor line receives security fixes. Upgrade to the latest published `0.8.x` release before reporting an issue diff --git a/THREAT_MODEL.md b/THREAT_MODEL.md index 6736bf2f..7faaaa77 100644 --- a/THREAT_MODEL.md +++ b/THREAT_MODEL.md @@ -46,13 +46,13 @@ lifecycle, transport, and access control. Inputs crossing the boundary: -| Input | Source | Assumption | -|---|---|---| -| Keys, passwords, seeds | Caller | The caller protects confidentiality and supplies the required entropy. Imports enforce documented shape and algorithm constraints, not key quality. | -| Messages, AAD, ciphertexts, tags, signatures, encoded keys | Caller, usually relayed from a network peer | Untrusted. | -| Randomness | `getrandom` or caller-supplied fill closures | The operating system or caller provides the required entropy quality. Output lengths are fixed by the API. | -| CPU capability reports | CPUID, auxv, sysctl, OS APIs | The host reports capabilities correctly. Forced-backend overrides are validated before use. | -| Build configuration | Cargo features, target features | The builder selects and records the intended configuration. | +| Input | Source | Assumption | +| ---------------------------------------------------------- | -------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| Keys, passwords, seeds | Caller | The caller protects confidentiality and supplies the required entropy. Imports enforce documented shape and algorithm constraints, not key quality. | +| Messages, AAD, ciphertexts, tags, signatures, encoded keys | Caller, usually relayed from a network peer | Untrusted. | +| Randomness | `getrandom` or caller-supplied fill closures | The operating system or caller provides the required entropy quality. Output lengths are fixed by the API. | +| CPU capability reports | CPUID, auxv, sysctl, OS APIs | The host reports capabilities correctly. Forced-backend overrides are validated before use. | +| Build configuration | Cargo features, target features | The builder selects and records the intended configuration. | Outputs are digests, tags, ciphertexts, signatures, derived keys, and opaque errors. Direct comparison of fixed-size secret-bearing owners returns an opaque @@ -102,25 +102,25 @@ The following threats are out of scope: Ordered by exposure to untrusted input: -| Surface | Entry points | Primary risks | -|---|---|---| -| Parsers | RSA DER/SPKI/PKCS#8 import, ECDSA DER signatures and SEC1 points, ML-KEM key and ciphertext parsing, PHC strings, hex | Memory safety, panics, accepting what should be rejected | -| Verification oracles | MAC `verify_tag`, AEAD open, signature `verify`, ML-KEM implicit rejection | Timing or error detail beyond the single failure bit | -| Secret-bearing compute | Sign, decrypt, decapsulate, derive; the release-evidenced subset of `ct.toml` | Timing leakage, incorrect arithmetic | -| `unsafe` low-level code | SIMD/assembly kernels, raw buffer helpers, zeroization, and dispatch | Undefined behavior, divergence from the portable authority | -| Dispatch | `src/platform`, `src/backend` | Selecting a kernel the CPU cannot run, or one that produces wrong output | +| Surface | Entry points | Primary risks | +| ----------------------- | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| Parsers | RSA DER/SPKI/PKCS#8 import, ECDSA DER signatures and SEC1 points, ML-KEM key and ciphertext parsing, PHC strings, hex | Memory safety, panics, accepting what should be rejected | +| Verification oracles | MAC `verify_tag`, AEAD open, signature `verify`, ML-KEM implicit rejection | Timing or error detail beyond the single failure bit | +| Secret-bearing compute | Sign, decrypt, decapsulate, derive; the release-evidenced subset of `ct.toml` | Timing leakage, incorrect arithmetic | +| `unsafe` low-level code | SIMD/assembly kernels, raw buffer helpers, zeroization, and dispatch | Undefined behavior, divergence from the portable authority | +| Dispatch | `src/platform`, `src/backend` | Selecting a kernel the CPU cannot run, or one that produces wrong output | ## Mitigations and evidence -| Risk | Mitigation | Evidence | -|---|---|---| -| Memory safety | Unsafe operations are lint-gated and require local `SAFETY` proofs; the portable Rust path remains authoritative | Miri on portable paths in CI | -| Parser abuse | Strict imports, `strict_*` arithmetic, release overflow checks | Fuzz targets, Wycheproof where mapped, official vectors | -| Wrong output from accelerated kernels | Portable path is the byte-for-byte authority | Portable-vs-accelerated differential tests and native CI | -| Timing leakage | Constant-time coding rules on claimed paths | `ct.toml` evidence gate: timing tests, generated-code review, binary checks where supported | -| Oracle behavior | Opaque errors, failed-open output clearing, single-bit failure shape | AEAD and verification tests, fuzz targets | -| Secret exposure at rest | Zeroize at the last owned use and on drop, masked `Debug` and errors, and sealed fixed-size comparison only on semantic secret owners | [`docs/secret-ownership.md`](docs/secret-ownership.md), [`docs/secret-lifecycle.md`](docs/secret-lifecycle.md), `scripts/check/zeroize-evidence.sh`, and `tests/secret_redaction.rs` | -| Supply chain | Minimal optional runtime dependencies, `cargo deny`, `cargo audit`, signed tags, Trusted Publishing, release attestations | [`deny.toml`](deny.toml), [`.github/workflows/release.yaml`](.github/workflows/release.yaml), [`docs/release.md`](docs/release.md) | +| Risk | Mitigation | Evidence | +| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| Memory safety | Unsafe operations are lint-gated and require local `SAFETY` proofs; the portable Rust path remains authoritative | Miri on portable paths in CI | +| Parser abuse | Strict imports, `strict_*` arithmetic, release overflow checks | Fuzz targets, Wycheproof where mapped, official vectors | +| Wrong output from accelerated kernels | Portable path is the byte-for-byte authority | Portable-vs-accelerated differential tests and native CI | +| Timing leakage | Constant-time coding rules on claimed paths | `ct.toml` evidence gate: timing tests, generated-code review, binary checks where supported | +| Oracle behavior | Opaque errors, failed-open output clearing, single-bit failure shape | AEAD and verification tests, fuzz targets | +| Secret exposure at rest | Zeroize at the last owned use and on drop, masked `Debug` and errors, and sealed fixed-size comparison only on semantic secret owners | [`docs/secret-ownership.md`](docs/secret-ownership.md), [`docs/secret-lifecycle.md`](docs/secret-lifecycle.md), `scripts/check/zeroize-evidence.sh`, and `tests/secret_redaction.rs` | +| Supply chain | Minimal optional runtime dependencies, `cargo deny`, `cargo audit`, signed tags, Trusted Publishing, release attestations | [`deny.toml`](deny.toml), [`.github/workflows/release.yaml`](.github/workflows/release.yaml), [`docs/release.md`](docs/release.md) | ## Known gaps diff --git a/benches/structural.rs b/benches/structural.rs new file mode 100644 index 00000000..a779b107 --- /dev/null +++ b/benches/structural.rs @@ -0,0 +1,34 @@ +//! Stable structural-cost benchmarks for representative public hash and checksum paths. + +use core::hint::black_box; + +use gungraun::{library_benchmark, library_benchmark_group}; +use rscrypto::{Blake3, Checksum, Crc32, Sha256}; + +static INPUT_64: [u8; 64] = [0x3d; 64]; +static INPUT_4K: [u8; 4096] = [0xa7; 4096]; +static INPUT_16K: [u8; 16_384] = [0x5c; 16_384]; + +#[library_benchmark] +#[bench::bytes_64(&INPUT_64)] +#[bench::bytes_4096(&INPUT_4K)] +fn sha256(input: &[u8]) { + black_box(Sha256::digest(black_box(input))); +} + +#[library_benchmark] +#[bench::bytes_4096(&INPUT_4K)] +#[bench::bytes_16384(&INPUT_16K)] +fn blake3(input: &[u8]) { + black_box(Blake3::digest(black_box(input))); +} + +#[library_benchmark] +#[bench::bytes_64(&INPUT_64)] +#[bench::bytes_4096(&INPUT_4K)] +fn crc32(input: &[u8]) { + black_box(Crc32::checksum(black_box(input))); +} + +library_benchmark_group!(name = structural; benchmarks = sha256, blake3, crc32); +gungraun::main!(library_benchmark_groups = structural); diff --git a/benchmark_results/OVERVIEW.md b/benchmark_results/OVERVIEW.md index f55021e3..0a36c304 100644 --- a/benchmark_results/OVERVIEW.md +++ b/benchmark_results/OVERVIEW.md @@ -47,10 +47,10 @@ Both runs used the pinned `rustc 1.97.0-nightly (ca9a134e0 2026-04-26)` toolchain on the same Apple Silicon macOS host. Criterion used 50 samples, a 1-second warm-up, and a 3-second measurement window. -| Source | Median | 95% confidence interval | Mean | -|---|---:|---:|---:| -| Baseline | 21.892 µs | 21.874–21.919 µs | 21.885 µs | -| Current | 21.754 µs | 21.704–21.799 µs | 21.757 µs | +| Source | Median | 95% confidence interval | Mean | +| -------- | --------: | ----------------------: | --------: | +| Baseline | 21.892 µs | 21.874–21.919 µs | 21.885 µs | +| Current | 21.754 µs | 21.704–21.799 µs | 21.757 µs | The observed current/baseline median ratio is 0.9937. This check found no regression. It was not an interleaved release benchmark, so it does not support @@ -60,18 +60,18 @@ The repository policy retains only this curated overview. The local Criterion metadata, estimates, and raw 50-sample files were distinct and hashed before curation: -| Artifact | Baseline SHA-256 | Current SHA-256 | -|---|---|---| +| Artifact | Baseline SHA-256 | Current SHA-256 | +| ---------------- | ------------------------------------------------------------------ | ------------------------------------------------------------------ | | `benchmark.json` | `6d27e19fd2a9563ecea5328345420c12b79f9924d3ecde179bc0166f5a62e6dd` | `6d27e19fd2a9563ecea5328345420c12b79f9924d3ecde179bc0166f5a62e6dd` | | `estimates.json` | `728945652c3ec804ec064e9888fc431a5fa3528e885edf76e350392ae95ea2fc` | `3b987405f949847972740cb549826d46f2529caa1187bc13786f7d662ca63e03` | -| `sample.json` | `f36052bcf65362d6203a6be768e251822dc3182ce8fab75dd9bba20097db30f9` | `a92a7e9fcc1af048c2bb5dcfd8782d07b8727e46477a27bc7948cd02c7a8a6bc` | +| `sample.json` | `f36052bcf65362d6203a6be768e251822dc3182ce8fab75dd9bba20097db30f9` | `a92a7e9fcc1af048c2bb5dcfd8782d07b8727e46477a27bc7948cd02c7a8a6bc` | ## Headline -| Scope | Pairs | W/T/L | Win % | Geomean | Median | -| --- | --- | --- | --- | --- | --- | -| 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 | +| Scope | Pairs | W/T/L | Win % | Geomean | Median | +| --------------------------------------- | ----- | --------------- | ----- | ------- | ------ | +| 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: @@ -89,84 +89,84 @@ Snapshot summary: ## Coverage Matrix -| Platform | Raw Criterion rows | All pairs | Fastest rows | W/T/L | Win % | Geomean | Median | -| --- | --- | --- | --- | --- | --- | --- | --- | -| 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 | +| Platform | Raw Criterion rows | All pairs | Fastest rows | W/T/L | Win % | Geomean | Median | +| --------------------- | ------------------ | --------- | ------------ | ----------- | ----- | ------- | ------ | +| 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 | 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 | +| Category | Rows | W/T/L | Win % | Geomean | Median | +| ---------------- | ----- | --------------- | ----- | ------- | ------ | +| 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 [#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 | 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 | 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` | 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 | +| Scope | Rows | W/T/L | Geomean | Median | +| --------------------- | ---- | ---------- | ------- | ------ | +| 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 | 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` | 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 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.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` | 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 | +| Platform | Raw ML-KEM rows | Fastest rows | W/T/L | Geomean | Median | Fastest external split | +| --------------------- | --------------- | ------------ | ----- | ------- | ------ | -------------------------- | +| 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` | 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 @@ -179,121 +179,121 @@ 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` | 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 | +| Operation | Rows | W/T/L | Geomean | Median | +| ----------------------- | ---- | ------- | ------- | ------ | +| `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 Linux CI primitives with matched exact `rscrypto` comparisons. Fastest columns are strongest-external comparisons; all-pair columns include every matched external implementation. -| Primitive | Fastest rows | Fastest W/T/L | Fastest geomean | All pairs | All W/T/L | All geomean | -| --- | --- | --- | --- | --- | --- | --- | -| `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 | +| Primitive | Fastest rows | Fastest W/T/L | Fastest geomean | All pairs | All W/T/L | All geomean | +| ----------------------- | ------------ | ------------- | --------------- | --------- | ---------- | ----------- | +| `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 | -| --- | --- | --- | --- | -| 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 | +| Platform | Case | Fastest external | Ratio | +| ------------- | ---------------------------- | ----------------- | ----- | +| 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-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-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 | +| Platform | Case | Fastest external | Ratio | +| --------------------- | ----------------------- | ---------------- | ------- | +| 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 @@ -305,42 +305,42 @@ Linux CI primitives with matched exact `rscrypto` comparisons. Fastest columns a ## External Pressure -| External | Pairs | W/T/L | Win % | Geomean | Median | -| --- | --- | --- | --- | --- | --- | -| `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 | +| External | Pairs | W/T/L | Win % | Geomean | Median | +| ----------------- | ----- | ------------- | ----- | ------- | ------ | +| `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`, 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 | -| --- | --- | --- | --- | --- | --- | -| macOS local: all matched performance pairs | 1,297 | 815/404/78 | 63% | 1.66x | 1.16x | -| macOS local: fastest external per case | 774 | 382/326/66 | 49% | 1.37x | 1.05x | -| macOS local: ML-KEM fastest external | 9 | 6/1/2 | 67% | 1.35x | 1.39x | +| Scope | Pairs | W/T/L | Win % | Geomean | Median | +| ------------------------------------------ | ----- | ---------- | ----- | ------- | ------ | +| macOS local: all matched performance pairs | 1,297 | 815/404/78 | 63% | 1.66x | 1.16x | +| macOS local: fastest external per case | 774 | 382/326/66 | 49% | 1.37x | 1.05x | +| macOS local: ML-KEM fastest external | 9 | 6/1/2 | 67% | 1.35x | 1.39x | ## README Numbers @@ -358,14 +358,14 @@ The macOS Apple Silicon run is local evidence from the 2026-07-04 full benchmark ## Raw Results -| Platform | Mode | Date/time | Parsed rows | Result | -| --- | --- | --- | --- | --- | -| 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` | +| Platform | Mode | Date/time | Parsed rows | Result | +| --------------------- | ------- | --------------------- | ----------- | ------------------------------------------------------------ | +| 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/ct.toml b/ct.toml index 854f2c81..2e6b1509 100644 --- a/ct.toml +++ b/ct.toml @@ -192,7 +192,11 @@ limited_call_count = 0 id = "rsa.public_modulus_and_profile_shapes" source = "src/auth/rsa.rs" call_count = 12 -operations = ["rsa.private_key_import_export_and_generation", "rsa.private_sign_and_decrypt", "rsa.public_operations"] +operations = [ + "rsa.private_key_import_export_and_generation", + "rsa.private_sign_and_decrypt", + "rsa.public_operations", +] public_length = "Widths derive from the public modulus, digest algorithm, DER/padding profile, or caller-visible encoded length." secret_contents = "Private representatives, decoded plaintext/hash fields, or validation intermediates are compared without content-dependent exits after public shape checks." tests = "RSA official/Wycheproof/differential/private-operation tests and RSA CT harness/DudeCT cases." @@ -231,7 +235,11 @@ operations = ["password.argon2i", "password.argon2d_and_argon2id"] public_length = "Primitive verification derives exactly the caller-supplied expected output length after public bounds checks." secret_contents = "The freshly derived password verifier and expected bytes are traversed fully before the temporary is cleared." tests = "Argon2 vectors/differentials/parallel tests and variant CT harness/DudeCT cases." -evidence_symbols = ["ct_entry_argon2i_verify", "ct_entry_argon2d_verify", "ct_entry_argon2id_verify"] +evidence_symbols = [ + "ct_entry_argon2i_verify", + "ct_entry_argon2d_verify", + "ct_entry_argon2id_verify", +] evidenced_call_count = 1 limited_call_count = 0 @@ -1125,7 +1133,19 @@ left_class = "valid RSA-2048 key with 128-byte canonical CRT exponents" right_class = "valid same-factor-width RSA-2048 key with a 127-byte canonical dQ" samples = 512 smoke_samples = 128 -notes = "Both imported keys retain 128-byte CRT exponent buffers. The whole-operation comparison also varies secret key values, as required by the private-operation CT boundary." +gate = "diagnostic" +reason = "The unrelated valid keys have different public moduli, which may leak by policy; this comparison cannot attribute timing separation to canonical CRT exponent width." +notes = "Broad whole-operation diagnostic retained for trend analysis. Required fixed-width exponent evidence uses one public modulus and representative." + +[[dudect_case]] +name = "rsa_private_exponent_fixed_width_high_byte" +primitive = "rsa.private_ops" +filter = "rsa_private_exponent_fixed_width_high_byte" +left_class = "fixed-width exponent with a nonzero high byte" +right_class = "same fixed-width exponent with a zero high byte" +samples = 512 +smoke_samples = 128 +notes = "The precomputed CRT component modulus, representative, reusable scratch addresses, buffer lengths, and all lower exponent bytes are identical across classes. AArch64 execution enters FEAT_DIT when the target advertises it." [[dudect_case]] name = "rsa_oaep_decrypt_fixed_vs_random_plaintext" @@ -1809,7 +1829,7 @@ binsec = ["aead.symmetric_transform.ascon_aead128_tag_portable.all"] id = "rsa.private_ops.pkcs1v15_sign" primitive = "rsa.private_ops" variant = "Pkcs1v15Sign" -dudect = ["rsa_pkcs1v15_fixed_vs_random_message", "rsa_pkcs1v15_full_width_vs_short_canonical_crt_exponent"] +dudect = ["rsa_pkcs1v15_fixed_vs_random_message", "rsa_private_exponent_fixed_width_high_byte"] binsec = ["rsa.private_ops.window_select.portable.all"] [[evidence_unit]] @@ -2955,7 +2975,9 @@ features = ["algorithm owner feature"] targets = ["all-supported"] secret_inputs = ["left owner bytes", "right owner bytes"] public_inputs = ["concrete owner type and fixed size"] -variable_time_components = ["none at the Rust source boundary; optimized lowering is target-specific evidence"] +variable_time_components = [ + "none at the Rust source boundary; optimized lowering is target-specific evidence", +] permitted_leakage = ["owner type", "fixed owner size", "explicitly declassified equality result"] claim = "ct-intended" evidence = [ @@ -2978,10 +3000,16 @@ features = ["always", "alloc for SecretVec"] targets = ["all-supported"] secret_inputs = ["wrapped bytes", "bytes being cleared"] public_inputs = ["wrapper size", "allocation length", "explicit exposure request"] -variable_time_components = ["public-length copy and clear loops", "allocator behavior for SecretVec extraction/drop"] +variable_time_components = [ + "public-length copy and clear loops", + "allocator behavior for SecretVec extraction/drop", +] permitted_leakage = ["wrapper type", "public length", "explicitly exposed bytes"] claim = "best-effort" -evidence = ["primitive:secret_wrappers.exposure_and_display", "harness:ct_entry_secret_bytes32_debug_masked"] +evidence = [ + "primitive:secret_wrappers.exposure_and_display", + "harness:ct_entry_secret_bytes32_debug_masked", +] limitation = "SecretBytes and SecretVec intentionally provide no equality. Explicit extraction transfers secret-lifecycle responsibility to the caller; zeroization evidence is maintained separately from timing claims." [[operation]] @@ -2996,8 +3024,17 @@ features = ["algorithm owner feature", "getrandom", "serde-secrets"] targets = ["all-supported"] secret_inputs = ["raw secret bytes", "generated entropy", "serialized secret material"] public_inputs = ["concrete owner type", "fixed owner size", "parse success", "exposure request"] -variable_time_components = ["entropy-source latency", "public parse/feature paths", "explicit formatting and serialization"] -permitted_leakage = ["owner type", "fixed size", "construction success", "caller-authorized secret exposure"] +variable_time_components = [ + "entropy-source latency", + "public parse/feature paths", + "explicit formatting and serialization", +] +permitted_leakage = [ + "owner type", + "fixed size", + "construction success", + "caller-authorized secret exposure", +] claim = "best-effort" evidence = ["primitive:secret_wrappers.exposure_and_display"] limitation = "display_secret, expose_secret, AsRef/as_bytes, and serde-secrets are explicit escape hatches. Debug remains masked; the manifest does not claim that authorized formatting or serialization hides the secret." @@ -3009,14 +3046,27 @@ api = [ "rscrypto::{Aes128Gcm,Aes256Gcm,Aes128GcmSiv,Aes256GcmSiv,ChaCha20Poly1305,XChaCha20Poly1305,Aegis256,AsconAead128}::{new,encrypt_in_place,encrypt}", "rscrypto::NonceCounter::*::encrypt*", ] -features = ["aead algorithm feature", "alloc/std for allocating helpers", "getrandom for random nonces"] +features = [ + "aead algorithm feature", + "alloc/std for allocating helpers", + "getrandom for random nonces", +] targets = ["all-supported", "backend selected by public target capabilities"] secret_inputs = ["key", "plaintext", "derived authentication/encryption subkeys", "cipher state"] public_inputs = ["nonce", "AAD", "plaintext length", "output capacity", "backend capabilities"] -variable_time_components = ["public-length loops", "public buffer errors", "public backend dispatch", "entropy-source latency for random nonces"] +variable_time_components = [ + "public-length loops", + "public buffer errors", + "public backend dispatch", + "entropy-source latency for random nonces", +] permitted_leakage = ["algorithm", "lengths", "nonce", "AAD", "backend", "opaque success/failure"] claim = "ct-intended" -evidence = ["primitive:aead.symmetric_transform", "unit:aead.symmetric_transform.aes128gcm", "unit:aead.symmetric_transform.chacha20poly1305"] +evidence = [ + "primitive:aead.symmetric_transform", + "unit:aead.symmetric_transform.aes128gcm", + "unit:aead.symmetric_transform.chacha20poly1305", +] limitation = "Release claims require exact target artifacts. Random-nonce helper timing includes the platform entropy source." [[operation]] @@ -3029,10 +3079,20 @@ features = ["aead algorithm feature", "alloc/std for allocating helpers"] targets = ["all-supported", "backend selected by public target capabilities"] secret_inputs = ["key", "derived authentication/encryption subkeys", "authenticated plaintext"] public_inputs = ["nonce", "AAD", "ciphertext", "tag owner", "lengths", "backend capabilities"] -variable_time_components = ["public-length loops", "public buffer-shape rejection", "public backend dispatch", "opaque authentication result"] +variable_time_components = [ + "public-length loops", + "public buffer-shape rejection", + "public backend dispatch", + "opaque authentication result", +] permitted_leakage = ["algorithm", "lengths", "nonce", "AAD", "backend", "opaque success/failure"] claim = "ct-intended" -evidence = ["primitive:aead.open_authentication", "primitive:aead.symmetric_transform", "unit:aead.open_authentication.aes128gcm", "unit:aead.open_authentication.ascon_aead128"] +evidence = [ + "primitive:aead.open_authentication", + "primitive:aead.symmetric_transform", + "unit:aead.open_authentication.aes128gcm", + "unit:aead.open_authentication.ascon_aead128", +] limitation = "The bool-like authentication decision is surfaced only as an opaque Result, but exact release-machine-code evidence remains T3.3 work." [[operation]] @@ -3065,7 +3125,12 @@ public_inputs = ["message length", "algorithm", "backend capabilities"] variable_time_components = ["public message-length loops", "public backend dispatch"] permitted_leakage = ["algorithm", "message length", "backend", "returned tag"] claim = "ct-intended" -evidence = ["primitive:mac.hmac_verify", "primitive:mac.kmac256_verify", "harness:ct_entry_hmac_sha256_verify", "harness:ct_entry_kmac256_verify"] +evidence = [ + "primitive:mac.hmac_verify", + "primitive:mac.kmac256_verify", + "harness:ct_entry_hmac_sha256_verify", + "harness:ct_entry_kmac256_verify", +] limitation = "The HMAC verification evidence computes and finalizes HMAC-SHA-2; the KMAC evidence covers KMAC256. Other MAC variants and downstream Mac implementations are inventoried separately without generated-code evidence." [[operation]] @@ -3078,24 +3143,35 @@ features = ["hmac-sha3", "kmac", "poly1305", "alloc/std for Vec/IoSlice helpers" targets = ["all-supported", "backend selected by public target capabilities"] secret_inputs = ["MAC key", "MAC state", "message when caller classifies it secret", "computed tag"] public_inputs = ["message length", "algorithm", "backend capabilities"] -variable_time_components = ["public message-length loops", "public backend dispatch", "allocation for Vec helpers"] +variable_time_components = [ + "public message-length loops", + "public backend dispatch", + "allocation for Vec helpers", +] permitted_leakage = ["algorithm", "message length", "backend", "returned tag"] claim = "best-effort" limitation = "No generated-code unit currently covers HMAC-SHA-3, KMAC128, standalone Poly1305, allocating trait helpers, or downstream Mac implementations. This row records source intent only." [[operation]] id = "mac.verify_typed_tag" -api = [ - "rscrypto::{HmacSha256,HmacSha384,HmacSha512}::{verify,verify_tag}", -] +api = ["rscrypto::{HmacSha256,HmacSha384,HmacSha512}::{verify,verify_tag}"] features = ["hmac"] targets = ["all-supported"] secret_inputs = ["MAC key/state", "computed tag"] public_inputs = ["message", "typed expected tag", "public lengths"] -variable_time_components = ["MAC computation over public length", "sealed fixed-owner comparison", "opaque result conversion"] +variable_time_components = [ + "MAC computation over public length", + "sealed fixed-owner comparison", + "opaque result conversion", +] permitted_leakage = ["message length", "tag size", "opaque verification result"] claim = "ct-intended" -evidence = ["primitive:mac.hmac_verify", "unit:mac.hmac_verify.sha256", "unit:mac.hmac_verify.sha384", "unit:mac.hmac_verify.sha512"] +evidence = [ + "primitive:mac.hmac_verify", + "unit:mac.hmac_verify.sha256", + "unit:mac.hmac_verify.sha384", + "unit:mac.hmac_verify.sha512", +] limitation = "Evidence is variant-specific to the three built-in HMAC-SHA-2 implementations." [[operation]] @@ -3109,33 +3185,75 @@ features = ["hmac-sha3", "poly1305"] targets = ["all-supported"] secret_inputs = ["MAC key/state", "computed tag"] public_inputs = ["message", "typed expected tag", "public lengths"] -variable_time_components = ["MAC computation over public length", "sealed fixed-owner comparison", "opaque result conversion"] +variable_time_components = [ + "MAC computation over public length", + "sealed fixed-owner comparison", + "opaque result conversion", +] permitted_leakage = ["message length", "tag size", "opaque verification result"] claim = "best-effort" limitation = "No generated-code unit currently covers the HMAC-SHA-3 or standalone Poly1305 verification paths. Downstream Mac implementations remain an explicit evidence boundary." [[operation]] id = "kmac.variable_output_and_verify" -api = ["rscrypto::Kmac256::{new,update,finalize_into,mac_into,mac_array,verify,verify_tag,verify_primitive,verify_tag_primitive}"] +api = [ + "rscrypto::Kmac256::{new,update,finalize_into,mac_into,mac_array,verify,verify_tag,verify_primitive,verify_tag_primitive}", +] features = ["kmac", "alloc for allocating outputs"] targets = ["all-supported"] secret_inputs = ["KMAC key", "state", "computed output"] -public_inputs = ["customization", "message", "requested output length", "expected output length", "authentication minimum policy"] -variable_time_components = ["public-length sponge absorption/squeeze", "public authentication-length policy rejection", "public-length full-content comparison"] -permitted_leakage = ["customization", "message/output lengths", "authentication-length policy decision", "opaque verification result"] +public_inputs = [ + "customization", + "message", + "requested output length", + "expected output length", + "authentication minimum policy", +] +variable_time_components = [ + "public-length sponge absorption/squeeze", + "public authentication-length policy rejection", + "public-length full-content comparison", +] +permitted_leakage = [ + "customization", + "message/output lengths", + "authentication-length policy decision", + "opaque verification result", +] claim = "ct-intended" -evidence = ["primitive:mac.kmac256_verify", "harness:ct_entry_kmac256_verify", "dudect:kmac256_valid_vs_invalid_tag"] +evidence = [ + "primitive:mac.kmac256_verify", + "harness:ct_entry_kmac256_verify", + "dudect:kmac256_valid_vs_invalid_tag", +] limitation = "KMAC has protocol-defined variable output. The default authentication verifier rejects tags below 32 bytes; explicit primitive verification permits shorter nonempty protocol outputs. Length and the policy decision are public." [[operation]] id = "kmac128.variable_output_and_verify" -api = ["rscrypto::Kmac128::{new,update,finalize_into,mac_into,mac_array,verify,verify_tag,verify_primitive,verify_tag_primitive}"] +api = [ + "rscrypto::Kmac128::{new,update,finalize_into,mac_into,mac_array,verify,verify_tag,verify_primitive,verify_tag_primitive}", +] features = ["kmac", "alloc for allocating outputs"] targets = ["all-supported"] secret_inputs = ["KMAC key", "state", "computed output"] -public_inputs = ["customization", "message", "requested output length", "expected output length", "authentication minimum policy"] -variable_time_components = ["public-length sponge absorption/squeeze", "public authentication-length policy rejection", "public-length full-content comparison"] -permitted_leakage = ["customization", "message/output lengths", "authentication-length policy decision", "opaque verification result"] +public_inputs = [ + "customization", + "message", + "requested output length", + "expected output length", + "authentication minimum policy", +] +variable_time_components = [ + "public-length sponge absorption/squeeze", + "public authentication-length policy rejection", + "public-length full-content comparison", +] +permitted_leakage = [ + "customization", + "message/output lengths", + "authentication-length policy decision", + "opaque verification result", +] claim = "best-effort" limitation = "The source follows the same structure as KMAC256 and the default verifier rejects tags below 16 bytes, but no KMAC128 generated-code evidence unit currently exists." @@ -3148,12 +3266,27 @@ api = [ ] features = ["blake2b", "blake2s", "blake3", "parallel"] targets = ["all-supported", "backend selected by public target capabilities"] -secret_inputs = ["key", "derive-key material", "keyed state", "keyed output when caller keeps it secret"] +secret_inputs = [ + "key", + "derive-key material", + "keyed state", + "keyed output when caller keeps it secret", +] public_inputs = ["message", "context", "message/output lengths", "backend capabilities"] -variable_time_components = ["public-length chunking", "public parallel threshold", "public backend dispatch", "fixed owner comparison for Blake3 verification"] +variable_time_components = [ + "public-length chunking", + "public parallel threshold", + "public backend dispatch", + "fixed owner comparison for Blake3 verification", +] permitted_leakage = ["algorithm", "context", "lengths", "backend", "verification result"] claim = "ct-intended" -evidence = ["primitive:keyed_hash.blake2_blake3", "primitive:keyed_hash.blake3_verify", "harness:ct_entry_blake3_keyed_digest", "harness:ct_entry_blake3_verify_keyed"] +evidence = [ + "primitive:keyed_hash.blake2_blake3", + "primitive:keyed_hash.blake3_verify", + "harness:ct_entry_blake3_keyed_digest", + "harness:ct_entry_blake3_verify_keyed", +] limitation = "Keyed outputs are explicitly extractable/serializable. Only Blake3KeyedHash has a typed built-in verification path." [[operation]] @@ -3195,9 +3328,27 @@ api = [ features = ["pbkdf2", "alloc", "phc-strings"] targets = ["all-supported"] secret_inputs = ["password", "derived key", "computed verifier"] -public_inputs = ["salt", "iteration count", "caller-selected maximum iteration count", "output length", "PHC string", "verification policy"] -variable_time_components = ["public iteration count", "public output-length loops", "PHC parsing and public work-policy rejection", "public-length full-content comparison"] -permitted_leakage = ["parameters", "salt", "lengths", "public work-policy decision", "opaque verification result"] +public_inputs = [ + "salt", + "iteration count", + "caller-selected maximum iteration count", + "output length", + "PHC string", + "verification policy", +] +variable_time_components = [ + "public iteration count", + "public output-length loops", + "PHC parsing and public work-policy rejection", + "public-length full-content comparison", +] +permitted_leakage = [ + "parameters", + "salt", + "lengths", + "public work-policy decision", + "opaque verification result", +] claim = "ct-intended" evidence = ["primitive:kdf.pbkdf2", "unit:kdf.pbkdf2.sha256", "unit:kdf.pbkdf2.sha512"] limitation = "Iteration count, policy minima, and max_iterations are public. Bounded verification rejects excessive public work before constructing password-derived HMAC state; primitive and compatibility methods deliberately do not cap it." @@ -3208,21 +3359,56 @@ api = ["rscrypto::Argon2i::{derive,derive_with_context,verify}"] features = ["argon2", "alloc", "parallel"] targets = ["all-supported", "backend selected by public target capabilities"] secret_inputs = ["password", "secret/pepper", "computed output", "working memory"] -public_inputs = ["salt", "associated data", "Argon2 parameters", "output length", "backend capabilities"] -variable_time_components = ["public resource parameters", "public output length", "allocation/thread scheduling", "public backend dispatch"] -permitted_leakage = ["parameters", "lengths", "backend", "allocation failure", "opaque verification result"] +public_inputs = [ + "salt", + "associated data", + "Argon2 parameters", + "output length", + "backend capabilities", +] +variable_time_components = [ + "public resource parameters", + "public output length", + "allocation/thread scheduling", + "public backend dispatch", +] +permitted_leakage = [ + "parameters", + "lengths", + "backend", + "allocation failure", + "opaque verification result", +] claim = "ct-intended" -evidence = ["primitive:password.argon2i", "harness:ct_entry_argon2i_hash", "harness:ct_entry_argon2i_verify"] +evidence = [ + "primitive:password.argon2i", + "harness:ct_entry_argon2i_hash", + "harness:ct_entry_argon2i_verify", +] limitation = "OS allocation and parallel scheduling are outside instruction-level CT claims." [[operation]] id = "password.argon2d_and_argon2id" -api = ["rscrypto::{Argon2d,Argon2id}::{derive,derive_with_context,verify}", "rscrypto::Argon2idPassword::{hash_password,verify_password,verify_password_with_context}"] +api = [ + "rscrypto::{Argon2d,Argon2id}::{derive,derive_with_context,verify}", + "rscrypto::Argon2idPassword::{hash_password,verify_password,verify_password_with_context}", +] features = ["argon2", "alloc", "parallel", "phc-strings", "getrandom"] targets = ["all-supported", "backend selected by public target capabilities"] secret_inputs = ["password", "secret/pepper", "computed output", "working memory"] -public_inputs = ["salt", "associated data", "resource parameters", "PHC string/policy", "backend capabilities"] -variable_time_components = ["Argon2d/Argon2id data-dependent memory addressing", "public resource parameters", "allocation/thread scheduling", "PHC parsing"] +public_inputs = [ + "salt", + "associated data", + "resource parameters", + "PHC string/policy", + "backend capabilities", +] +variable_time_components = [ + "Argon2d/Argon2id data-dependent memory addressing", + "public resource parameters", + "allocation/thread scheduling", + "PHC parsing", +] permitted_leakage = ["parameters", "salt", "lengths", "backend", "opaque verification result"] claim = "best-effort" evidence = ["primitive:password.argon2d_argon2id", "harness:ct_entry_argon2id_verify"] @@ -3230,12 +3416,21 @@ limitation = "Argon2d and the data-dependent Argon2id phase intentionally use pa [[operation]] id = "password.scrypt" -api = ["rscrypto::Scrypt::{derive,verify}", "rscrypto::ScryptPassword::{hash_password,verify_password}", "rscrypto::ScryptParams::*"] +api = [ + "rscrypto::Scrypt::{derive,verify}", + "rscrypto::ScryptPassword::{hash_password,verify_password}", + "rscrypto::ScryptParams::*", +] features = ["scrypt", "alloc", "parallel", "phc-strings", "getrandom"] targets = ["all-supported"] secret_inputs = ["password", "computed output", "ROMix working memory"] public_inputs = ["salt", "resource parameters", "output length", "PHC string/policy"] -variable_time_components = ["password-dependent ROMix memory addressing", "public resource parameters", "allocation/thread scheduling", "PHC parsing"] +variable_time_components = [ + "password-dependent ROMix memory addressing", + "public resource parameters", + "allocation/thread scheduling", + "PHC parsing", +] permitted_leakage = ["parameters", "salt", "lengths", "opaque verification result"] claim = "best-effort" evidence = ["primitive:password.scrypt", "harness:ct_entry_scrypt_verify"] @@ -3243,62 +3438,162 @@ limitation = "scrypt intentionally uses password-dependent memory addressing and [[operation]] id = "kx.x25519_key_agreement" -api = ["rscrypto::X25519SecretKey::{public_key,diffie_hellman}", "rscrypto::X25519SharedSecret::diffie_hellman", "rscrypto::X25519PublicKey::{from_bytes,basepoint,to_bytes,as_bytes}"] +api = [ + "rscrypto::X25519SecretKey::{public_key,diffie_hellman}", + "rscrypto::X25519SharedSecret::diffie_hellman", + "rscrypto::X25519PublicKey::{from_bytes,basepoint,to_bytes,as_bytes}", +] features = ["x25519", "getrandom", "serde-secrets"] targets = ["all-supported", "aarch64/x86_64 assembly where selected", "portable fallback"] secret_inputs = ["secret scalar", "shared secret", "clamped scalar scratch"] public_inputs = ["peer public key", "backend capabilities"] variable_time_components = ["public backend dispatch", "opaque all-zero shared-secret rejection"] -permitted_leakage = ["public key", "backend", "opaque success/failure", "caller-authorized shared-secret exposure"] +permitted_leakage = [ + "public key", + "backend", + "opaque success/failure", + "caller-authorized shared-secret exposure", +] claim = "ct-intended" -evidence = ["primitive:kx.x25519", "harness:ct_entry_x25519", "dudect:x25519_fixed_vs_random_scalar"] +evidence = [ + "primitive:kx.x25519", + "harness:ct_entry_x25519", + "dudect:x25519_fixed_vs_random_scalar", +] limitation = "Exact assembly and portable lowering are target-specific evidence. Shared-secret comparison returns CtDecision; downstream declassification and codegen remain outside this operation's release evidence." [[operation]] id = "kem.mlkem512" -api = ["rscrypto::MlKem512::{generate_keypair,generate_keypair_with,encapsulate,encapsulate_with,decapsulate}", "rscrypto::MlKem512*::{from_bytes,try_from_slice,to_bytes,as_bytes,expose_secret,duplicate_secret}", "rscrypto::MlKem512{DecapsulationKey,PreparedDecapsulationKey,SharedSecret}::ct_eq", "rscrypto::MlKem512{EncapsulationKey,PreparedEncapsulationKey,Ciphertext}::eq"] +api = [ + "rscrypto::MlKem512::{generate_keypair,generate_keypair_with,encapsulate,encapsulate_with,decapsulate}", + "rscrypto::MlKem512*::{from_bytes,try_from_slice,to_bytes,as_bytes,expose_secret,duplicate_secret}", + "rscrypto::MlKem512{DecapsulationKey,PreparedDecapsulationKey,SharedSecret}::ct_eq", + "rscrypto::MlKem512{EncapsulationKey,PreparedEncapsulationKey,Ciphertext}::eq", +] features = ["ml-kem", "getrandom", "serde", "serde-secrets"] targets = ["all-supported", "portable implementation"] -secret_inputs = ["key-generation secret seed", "decapsulation key", "encapsulation randomness", "shared secret", "implicit-rejection fallback"] +secret_inputs = [ + "key-generation secret seed", + "decapsulation key", + "encapsulation randomness", + "shared secret", + "implicit-rejection fallback", +] public_inputs = ["encapsulation key", "ciphertext", "parameter set", "encoded lengths"] -variable_time_components = ["public matrix rejection sampling", "public fixed-shape parsing", "full fixed-shape implicit-rejection comparison"] -permitted_leakage = ["parameter set", "public key", "ciphertext", "fixed sizes", "caller-authorized secret exposure"] +variable_time_components = [ + "public matrix rejection sampling", + "public fixed-shape parsing", + "full fixed-shape implicit-rejection comparison", +] +permitted_leakage = [ + "parameter set", + "public key", + "ciphertext", + "fixed sizes", + "caller-authorized secret exposure", +] claim = "ct-intended" -evidence = ["primitive:kem.mlkem512", "unit:kem.mlkem512.keygen", "unit:kem.mlkem512.encapsulate", "unit:kem.mlkem512.decapsulate"] +evidence = [ + "primitive:kem.mlkem512", + "unit:kem.mlkem512.keygen", + "unit:kem.mlkem512.encapsulate", + "unit:kem.mlkem512.decapsulate", +] limitation = "Public encapsulation-key/ciphertext equality is ordinary equality. Decapsulation-key/shared-secret owners return CtDecision and require explicit declassification." [[operation]] id = "kem.mlkem768" -api = ["rscrypto::MlKem768::{generate_keypair,generate_keypair_with,encapsulate,encapsulate_with,decapsulate}", "rscrypto::MlKem768*::{from_bytes,try_from_slice,to_bytes,as_bytes,expose_secret,duplicate_secret}", "rscrypto::MlKem768{DecapsulationKey,PreparedDecapsulationKey,SharedSecret}::ct_eq", "rscrypto::MlKem768{EncapsulationKey,PreparedEncapsulationKey,Ciphertext}::eq"] +api = [ + "rscrypto::MlKem768::{generate_keypair,generate_keypair_with,encapsulate,encapsulate_with,decapsulate}", + "rscrypto::MlKem768*::{from_bytes,try_from_slice,to_bytes,as_bytes,expose_secret,duplicate_secret}", + "rscrypto::MlKem768{DecapsulationKey,PreparedDecapsulationKey,SharedSecret}::ct_eq", + "rscrypto::MlKem768{EncapsulationKey,PreparedEncapsulationKey,Ciphertext}::eq", +] features = ["ml-kem", "getrandom", "serde", "serde-secrets"] targets = ["all-supported", "portable implementation"] -secret_inputs = ["key-generation secret seed", "decapsulation key", "encapsulation randomness", "shared secret", "implicit-rejection fallback"] +secret_inputs = [ + "key-generation secret seed", + "decapsulation key", + "encapsulation randomness", + "shared secret", + "implicit-rejection fallback", +] public_inputs = ["encapsulation key", "ciphertext", "parameter set", "encoded lengths"] -variable_time_components = ["public matrix rejection sampling", "public fixed-shape parsing", "full fixed-shape implicit-rejection comparison"] -permitted_leakage = ["parameter set", "public key", "ciphertext", "fixed sizes", "caller-authorized secret exposure"] +variable_time_components = [ + "public matrix rejection sampling", + "public fixed-shape parsing", + "full fixed-shape implicit-rejection comparison", +] +permitted_leakage = [ + "parameter set", + "public key", + "ciphertext", + "fixed sizes", + "caller-authorized secret exposure", +] claim = "ct-intended" -evidence = ["primitive:kem.mlkem768", "unit:kem.mlkem768.keygen", "unit:kem.mlkem768.encapsulate", "unit:kem.mlkem768.decapsulate"] +evidence = [ + "primitive:kem.mlkem768", + "unit:kem.mlkem768.keygen", + "unit:kem.mlkem768.encapsulate", + "unit:kem.mlkem768.decapsulate", +] limitation = "Public encapsulation-key/ciphertext equality is ordinary equality. Decapsulation-key/shared-secret owners return CtDecision and require explicit declassification." [[operation]] id = "kem.mlkem1024" -api = ["rscrypto::MlKem1024::{generate_keypair,generate_keypair_with,encapsulate,encapsulate_with,decapsulate}", "rscrypto::MlKem1024*::{from_bytes,try_from_slice,to_bytes,as_bytes,expose_secret,duplicate_secret}", "rscrypto::MlKem1024{DecapsulationKey,PreparedDecapsulationKey,SharedSecret}::ct_eq", "rscrypto::MlKem1024{EncapsulationKey,PreparedEncapsulationKey,Ciphertext}::eq"] +api = [ + "rscrypto::MlKem1024::{generate_keypair,generate_keypair_with,encapsulate,encapsulate_with,decapsulate}", + "rscrypto::MlKem1024*::{from_bytes,try_from_slice,to_bytes,as_bytes,expose_secret,duplicate_secret}", + "rscrypto::MlKem1024{DecapsulationKey,PreparedDecapsulationKey,SharedSecret}::ct_eq", + "rscrypto::MlKem1024{EncapsulationKey,PreparedEncapsulationKey,Ciphertext}::eq", +] features = ["ml-kem", "getrandom", "serde", "serde-secrets"] targets = ["all-supported", "portable implementation"] -secret_inputs = ["key-generation secret seed", "decapsulation key", "encapsulation randomness", "shared secret", "implicit-rejection fallback"] +secret_inputs = [ + "key-generation secret seed", + "decapsulation key", + "encapsulation randomness", + "shared secret", + "implicit-rejection fallback", +] public_inputs = ["encapsulation key", "ciphertext", "parameter set", "encoded lengths"] -variable_time_components = ["public matrix rejection sampling", "public fixed-shape parsing", "full fixed-shape implicit-rejection comparison"] -permitted_leakage = ["parameter set", "public key", "ciphertext", "fixed sizes", "caller-authorized secret exposure"] +variable_time_components = [ + "public matrix rejection sampling", + "public fixed-shape parsing", + "full fixed-shape implicit-rejection comparison", +] +permitted_leakage = [ + "parameter set", + "public key", + "ciphertext", + "fixed sizes", + "caller-authorized secret exposure", +] claim = "ct-intended" -evidence = ["primitive:kem.mlkem1024", "unit:kem.mlkem1024.keygen", "unit:kem.mlkem1024.encapsulate", "unit:kem.mlkem1024.decapsulate"] +evidence = [ + "primitive:kem.mlkem1024", + "unit:kem.mlkem1024.keygen", + "unit:kem.mlkem1024.encapsulate", + "unit:kem.mlkem1024.decapsulate", +] limitation = "Public encapsulation-key/ciphertext equality is ordinary equality. Decapsulation-key/shared-secret owners return CtDecision and require explicit declassification." [[operation]] id = "signature.ed25519_signing" -api = ["rscrypto::Ed25519SecretKey::{public_key,sign}", "rscrypto::Ed25519Keypair::{from_secret_key,sign,try_sign}"] +api = [ + "rscrypto::Ed25519SecretKey::{public_key,sign}", + "rscrypto::Ed25519Keypair::{from_secret_key,sign,try_sign}", +] features = ["ed25519", "getrandom", "serde-secrets"] targets = ["all-supported", "target-selected fixed-base backend"] -secret_inputs = ["secret seed", "expanded scalar", "nonce prefix", "deterministic nonce", "signing intermediates"] +secret_inputs = [ + "secret seed", + "expanded scalar", + "nonce prefix", + "deterministic nonce", + "signing intermediates", +] public_inputs = ["message", "public key", "backend capabilities"] variable_time_components = ["public message-length hashing", "public backend dispatch"] permitted_leakage = ["message length", "public key", "signature", "backend"] @@ -3308,25 +3603,45 @@ limitation = "Target-specific fixed-base kernels require their own release artif [[operation]] id = "signature.ecdsa_signing" -api = ["rscrypto::EcdsaP{256,384}SecretKey::{try_sign,try_sign_blinded}", "rscrypto::EcdsaP{256,384}Keypair::{try_sign,try_sign_blinded}"] +api = [ + "rscrypto::EcdsaP{256,384}SecretKey::{try_sign,try_sign_blinded}", + "rscrypto::EcdsaP{256,384}Keypair::{try_sign,try_sign_blinded}", +] features = ["ecdsa-p256", "ecdsa-p384", "getrandom", "serde-secrets"] targets = ["all-supported", "target-selected arithmetic backend"] secret_inputs = ["private scalar", "deterministic nonce", "blinding input", "signing intermediates"] public_inputs = ["message", "public key", "backend capabilities"] -variable_time_components = ["public message-length hashing", "public backend dispatch", "opaque invalid-scalar/signing failure"] +variable_time_components = [ + "public message-length hashing", + "public backend dispatch", + "opaque invalid-scalar/signing failure", +] permitted_leakage = ["message length", "public key", "signature", "backend", "opaque failure"] claim = "ct-intended" -evidence = ["primitive:signature.ecdsa_p256_sign", "primitive:signature.ecdsa_p384_sign", "harness:ct_entry_ecdsa_p256_sign", "harness:ct_entry_ecdsa_p384_sign"] +evidence = [ + "primitive:signature.ecdsa_p256_sign", + "primitive:signature.ecdsa_p384_sign", + "harness:ct_entry_ecdsa_p256_sign", + "harness:ct_entry_ecdsa_p384_sign", +] limitation = "Blinding closures and entropy-source behavior are external. Exact arithmetic kernels require target-specific release evidence." [[operation]] id = "signature.public_parse_serialize_and_verify" -api = ["rscrypto::{Ed25519,EcdsaP256,EcdsaP384}PublicKey::{from_bytes,to_bytes,as_bytes,verify}", "rscrypto::{Ed25519,EcdsaP256,EcdsaP384}Signature::{from_bytes,from_der,to_bytes,as_bytes}"] +api = [ + "rscrypto::{Ed25519,EcdsaP256,EcdsaP384}PublicKey::{from_bytes,to_bytes,as_bytes,verify}", + "rscrypto::{Ed25519,EcdsaP256,EcdsaP384}Signature::{from_bytes,from_der,to_bytes,as_bytes}", +] features = ["ed25519", "ecdsa-p256", "ecdsa-p384", "serde"] targets = ["all-supported"] secret_inputs = [] public_inputs = ["public key", "message", "signature", "encoding"] -variable_time_components = ["public parsing and validation", "public message-length hashing", "public scalar/point arithmetic", "ordinary public-value equality"] +variable_time_components = [ + "public parsing and validation", + "public message-length hashing", + "public scalar/point arithmetic", + "ordinary public-value equality", +] permitted_leakage = ["all inputs", "parse failure", "opaque verification result"] claim = "best-effort" evidence = ["primitive:signature.ed25519_verify_failure_shape", "harness:ct_entry_ed25519_verify"] @@ -3334,64 +3649,151 @@ limitation = "Signature verification inputs are public and are not blanket const [[operation]] id = "rsa.private_key_import_export_and_generation" -api = ["rscrypto::RsaPrivateKey::{from_components,from_pkcs1_der*,from_pkcs8_der*,to_pkcs1_der,to_pkcs8_der,generate*}", "rscrypto::RsaPrivateKey::{modulus_bits,public_key}"] +api = [ + "rscrypto::RsaPrivateKey::{from_components,from_pkcs1_der*,from_pkcs8_der*,to_pkcs1_der,to_pkcs8_der,generate*}", + "rscrypto::RsaPrivateKey::{modulus_bits,public_key}", +] features = ["rsa", "alloc", "getrandom for generation"] targets = ["all-supported"] -secret_inputs = ["private components", "private DER", "generated prime candidates", "exported secret DER"] +secret_inputs = [ + "private components", + "private DER", + "generated prime candidates", + "exported secret DER", +] public_inputs = ["public exponent", "policy", "requested modulus size", "DER shape"] -variable_time_components = ["DER parsing", "big-integer allocation", "secret prime search and rejection", "entropy-source latency"] -permitted_leakage = ["public modulus size/exponent", "parse/policy result", "generation progress/timing", "caller-authorized DER exposure"] +variable_time_components = [ + "DER parsing", + "big-integer allocation", + "secret prime search and rejection", + "entropy-source latency", +] +permitted_leakage = [ + "public modulus size/exponent", + "parse/policy result", + "generation progress/timing", + "caller-authorized DER exposure", +] claim = "ct-intended" -evidence = ["primitive:rsa.private_key_material", "unit:rsa.private_key_material.component_validation_32", "harness:ct_entry_rsa_private_key_pkcs8_roundtrip"] +evidence = [ + "primitive:rsa.private_key_material", + "unit:rsa.private_key_material.component_validation_32", + "harness:ct_entry_rsa_private_key_pkcs8_roundtrip", +] limitation = "Prime generation is inherently variable-work and not a CT claim. Secret DER export is an explicit SecretVec escape hatch. The ct-intended claim covers fixed-shape private-component validation/import leaves tracked by evidence." [[operation]] id = "rsa.private_sign_and_decrypt" -api = ["rscrypto::RsaPrivateKey::{sign_signature*,sign_pkcs1v15*,sign_pss*,decrypt_oaep*,decrypt_pkcs1v15*}"] +api = [ + "rscrypto::RsaPrivateKey::{sign_signature*,sign_pkcs1v15*,sign_pss*,decrypt_oaep*,decrypt_pkcs1v15*}", +] features = ["rsa", "alloc", "getrandom for blinding/randomized padding"] targets = ["all-supported"] -secret_inputs = ["private exponent/components", "plaintext", "blinding factor", "PSS salt", "private arithmetic scratch"] -public_inputs = ["message/digest", "ciphertext", "padding profile", "modulus size", "output capacity"] -variable_time_components = ["public modulus-width loops", "public padding/hash profiles", "entropy-source latency", "opaque input-shape rejection"] +secret_inputs = [ + "private exponent/components", + "plaintext", + "blinding factor", + "PSS salt", + "private arithmetic scratch", +] +public_inputs = [ + "message/digest", + "ciphertext", + "padding profile", + "modulus size", + "output capacity", +] +variable_time_components = [ + "public modulus-width loops", + "public padding/hash profiles", + "entropy-source latency", + "opaque input-shape rejection", +] permitted_leakage = ["modulus/profile/lengths", "opaque success/failure"] claim = "ct-intended" -evidence = ["primitive:rsa.private_ops", "unit:rsa.private_ops.pkcs1v15_sign", "unit:rsa.private_ops.pss_sign", "unit:rsa.private_ops.oaep_decrypt", "unit:rsa.private_ops.pkcs1v15_decrypt"] -limitation = "Retained CRT exponents are normalized to the corresponding factor width before arithmetic, so canonical exponent encoding length is not a steady-state control or address input. Full private operations remain target-artifact/DudeCT evidence, not source-only claims." +evidence = [ + "primitive:rsa.private_ops", + "unit:rsa.private_ops.pkcs1v15_sign", + "unit:rsa.private_ops.pss_sign", + "unit:rsa.private_ops.oaep_decrypt", + "unit:rsa.private_ops.pkcs1v15_decrypt", +] +limitation = "Retained CRT exponents are normalized to the corresponding factor width before arithmetic, so canonical exponent encoding length is not a steady-state control or address input. AArch64 private exponentiation scopes FEAT_DIT when runtime or compile-time detection advertises it. Full private operations remain target-artifact/DudeCT evidence, not source-only claims." [[operation]] id = "rsa.public_operations" -api = ["rscrypto::RsaPublicKey::{from_pkcs1_der,from_spki_der,to_pkcs1_der,to_spki_der,encrypt*,verify*}", "rscrypto::Rsa*Profile::*", "rscrypto::RsaPublicKeyPolicy::*"] +api = [ + "rscrypto::RsaPublicKey::{from_pkcs1_der,from_spki_der,to_pkcs1_der,to_spki_der,encrypt*,verify*}", + "rscrypto::Rsa*Profile::*", + "rscrypto::RsaPublicKeyPolicy::*", +] features = ["rsa", "alloc", "getrandom for randomized encryption"] targets = ["all-supported"] secret_inputs = ["plaintext passed to encryption"] -public_inputs = ["public key", "message/digest", "signature", "ciphertext length", "padding/profile/policy"] -variable_time_components = ["public DER parsing", "public-key arithmetic", "public padding validation", "entropy-source latency"] +public_inputs = [ + "public key", + "message/digest", + "signature", + "ciphertext length", + "padding/profile/policy", +] +variable_time_components = [ + "public DER parsing", + "public-key arithmetic", + "public padding validation", + "entropy-source latency", +] permitted_leakage = ["public key/profile/lengths", "parse/policy result", "verification result"] claim = "best-effort" limitation = "Public verification/parsing inputs are not CT claims. Encryption consumes plaintext but necessarily reveals ciphertext length and includes external randomness; no secret-dependent private-key arithmetic occurs." [[operation]] id = "public.hash_checksum_and_xof" -api = ["rscrypto::{Digest,Xof,Checksum,ChecksumCombine,FastHash}::*", "rscrypto::{Sha*,Blake*,AsconHash*,Shake*,Cshake*,Crc*,Xxh3*,RapidHash*}::*"] +api = [ + "rscrypto::{Digest,Xof,Checksum,ChecksumCombine,FastHash}::*", + "rscrypto::{Sha*,Blake*,AsconHash*,Shake*,Cshake*,Crc*,Xxh3*,RapidHash*}::*", +] features = ["hash/checksum algorithm feature", "std/alloc for I/O wrappers"] targets = ["all-supported", "backend selected by public target capabilities"] secret_inputs = [] -public_inputs = ["data", "seed", "initial checksum", "requested output length", "backend capabilities"] +public_inputs = [ + "data", + "seed", + "initial checksum", + "requested output length", + "backend capabilities", +] variable_time_components = ["public-length loops", "public backend dispatch", "public I/O behavior"] permitted_leakage = ["all inputs/results", "lengths", "backend"] claim = "best-effort" -evidence = ["primitive:public_hashes_and_checksums", "harness:ct_entry_public_sha256_digest", "harness:ct_entry_public_crc32_checksum"] +evidence = [ + "primitive:public_hashes_and_checksums", + "harness:ct_entry_public_sha256_digest", + "harness:ct_entry_public_crc32_checksum", +] limitation = "Unkeyed hashes/checksums are public-data APIs and make no whole-API CT claim. Callers hashing secrets must use a separately inventoried keyed construction where appropriate." [[operation]] id = "traits.downstream_security_defaults" -api = ["rscrypto::TrySigner::try_sign", "rscrypto::TrySignerInto::{try_sign_into,try_sign_to_vec}", "rscrypto::Verifier::verify", "rscrypto::Kem::{generate_keypair,encapsulate,decapsulate}"] +api = [ + "rscrypto::TrySigner::try_sign", + "rscrypto::TrySignerInto::{try_sign_into,try_sign_to_vec}", + "rscrypto::Verifier::verify", + "rscrypto::Kem::{generate_keypair,encapsulate,decapsulate}", +] features = ["always", "alloc for try_sign_to_vec"] targets = ["all-supported"] -secret_inputs = ["implementation-defined signing key, decapsulation key, randomness, and derived secret"] +secret_inputs = [ + "implementation-defined signing key, decapsulation key, randomness, and derived secret", +] public_inputs = ["implementation-defined message, signature, public key, and ciphertext"] -variable_time_components = ["entirely implementation-defined except for each built-in implementation's separately inventoried path"] -permitted_leakage = ["implementation-defined", "opaque verification failure where the contract requires it"] +variable_time_components = [ + "entirely implementation-defined except for each built-in implementation's separately inventoried path", +] +permitted_leakage = [ + "implementation-defined", + "opaque verification failure where the contract requires it", +] claim = "unsupported" limitation = "These public traits are downstream implementation boundaries. Built-in rscrypto implementations map to their algorithm operation rows; the traits cannot enforce timing, zeroization, or error-opacity properties on external implementations." @@ -3403,7 +3805,10 @@ targets = ["host or explicitly cfg-gated diagnostic target"] secret_inputs = ["operation-specific diagnostic key, password, scalar, state, or intermediate"] public_inputs = ["operation-specific fixed diagnostic shape and backend selector"] variable_time_components = ["operation-specific; each linked primitive/harness owns classification"] -permitted_leakage = ["diagnostic digest/result intentionally returned to the caller", "feature and target"] +permitted_leakage = [ + "diagnostic digest/result intentionally returned to the caller", + "feature and target", +] claim = "unsupported" limitation = "The diag feature is an evidence/test surface, not a production secrecy boundary. Diagnostic return values deliberately declassify intermediates for harnesses and must not be enabled as an application security API." @@ -3591,7 +3996,13 @@ secrets = [ "encapsulation_random", "implicit_rejection_seed", ] -public = ["encapsulation_key", "ciphertext", "ciphertext_length", "public_shape_error", "rng_failure"] +public = [ + "encapsulation_key", + "ciphertext", + "ciphertext_length", + "public_shape_error", + "rng_failure", +] may_leak = ["input_length", "public_shape_error", "public_rng_failure", "opaque_success_or_failure"] must_not_leak_ref = "common_secret_data" required = ["tier_a"] @@ -3627,7 +4038,13 @@ secrets = [ "encapsulation_random", "implicit_rejection_seed", ] -public = ["encapsulation_key", "ciphertext", "ciphertext_length", "public_shape_error", "rng_failure"] +public = [ + "encapsulation_key", + "ciphertext", + "ciphertext_length", + "public_shape_error", + "rng_failure", +] may_leak = ["input_length", "public_shape_error", "public_rng_failure", "opaque_success_or_failure"] must_not_leak_ref = "common_secret_data" required = ["tier_a"] @@ -3663,7 +4080,13 @@ secrets = [ "encapsulation_random", "implicit_rejection_seed", ] -public = ["encapsulation_key", "ciphertext", "ciphertext_length", "public_shape_error", "rng_failure"] +public = [ + "encapsulation_key", + "ciphertext", + "ciphertext_length", + "public_shape_error", + "rng_failure", +] may_leak = ["input_length", "public_shape_error", "public_rng_failure", "opaque_success_or_failure"] must_not_leak_ref = "common_secret_data" required = ["tier_a"] @@ -3710,10 +4133,7 @@ id = "signature.ecdsa_p256_sign" tier = "A" claim = "ct-intended" features = ["ecdsa-p256"] -entrypoints = [ - "EcdsaP256SecretKey::try_sign_blinded", - "EcdsaP256Keypair::try_sign_blinded", -] +entrypoints = ["EcdsaP256SecretKey::try_sign_blinded", "EcdsaP256Keypair::try_sign_blinded"] secrets = ["secret_key", "nonce_material", "nonce_scalar", "private_scalar", "coordinate_blinding"] public = ["message", "message_length", "signature"] may_leak = ["message_length", "signature"] @@ -3729,10 +4149,7 @@ id = "signature.ecdsa_p384_sign" tier = "A" claim = "ct-intended" features = ["ecdsa-p384"] -entrypoints = [ - "EcdsaP384SecretKey::try_sign_blinded", - "EcdsaP384Keypair::try_sign_blinded", -] +entrypoints = ["EcdsaP384SecretKey::try_sign_blinded", "EcdsaP384Keypair::try_sign_blinded"] secrets = ["secret_key", "nonce_material", "nonce_scalar", "private_scalar", "coordinate_blinding"] public = ["message", "message_length", "signature"] may_leak = ["message_length", "signature"] @@ -3887,10 +4304,7 @@ id = "password.argon2i" tier = "A" claim = "ct-intended" features = ["argon2"] -entrypoints = [ - "Argon2i::derive", - "Argon2i::verify", -] +entrypoints = ["Argon2i::derive", "Argon2i::verify"] secrets = ["password", "derived_key", "expected_key"] public = ["salt", "params", "phc_string"] may_leak = ["input_length", "public_parameter_error", "opaque_success_or_failure"] @@ -3934,11 +4348,7 @@ id = "password.scrypt" tier = "A" claim = "best-effort" features = ["scrypt"] -entrypoints = [ - "Scrypt::derive", - "Scrypt::verify", - "ScryptPassword::verify_password", -] +entrypoints = ["Scrypt::derive", "Scrypt::verify", "ScryptPassword::verify_password"] secrets = ["password", "derived_key", "expected_key"] public = ["salt", "params", "phc_string"] may_leak = ["input_length", "public_parameter_error", "opaque_success_or_failure"] diff --git a/deny.toml b/deny.toml index bfe406b2..cbef5257 100644 --- a/deny.toml +++ b/deny.toml @@ -30,10 +30,7 @@ ignore = ["RUSTSEC-2023-0071"] [licenses] # Common Open Source Licenses -allow = [ - "MIT", - "Apache-2.0", -] +allow = ["MIT", "Apache-2.0"] confidence-threshold = 0.93 diff --git a/docs/benchmarking.md b/docs/benchmarking.md index 234e2bb3..7aabfaa4 100644 --- a/docs/benchmarking.md +++ b/docs/benchmarking.md @@ -49,23 +49,27 @@ Platform-specific claims need platform-specific raw results. A strong x86_64 result does not imply the same result on aarch64, Power, s390x, RISC-V, WASM, or `no_std`. +`.config/benchmark-matrix.json` is the source of truth for benchmark binaries, +features, selectors, aliases, and algorithm filters. Keep it synchronized with +Cargo benchmark targets; `just check` validates that contract. + ## Competitor set The comparison set in the published snapshot is Rust-focused and shape-compatible: -| Area | Compared against | -|---|---| -| AEAD | RustCrypto AEADs, `aws-lc-rs`, `ring`, `aegis` | -| SHA-2 / HMAC / HKDF / PBKDF2 | RustCrypto, `aws-lc-rs`, `ring` | -| BLAKE2 / BLAKE3 | RustCrypto, `dryoc`, upstream `blake3` | -| ECDSA P-256/P-384 | RustCrypto `p256`/`p384`, `aws-lc-rs`, `ring` | -| Ed25519 / X25519 | dalek, `aws-lc-rs`, `ring` where API-compatible, `dryoc` | -| ML-KEM-512/768/1024 | `libcrux`, `fips203`, RustCrypto `ml-kem`, and target-available `aws-lc-rs` | -| RSA import / verification | RustCrypto `rsa`, `ring`, target-available `aws-lc-rs` | -| Password hashing | RustCrypto, `dryoc` where API-compatible | -| XXH3 / RapidHash | upstream crates | -| CRC | `crc`, `crc-fast`, `crc32fast`, `crc32c`, `crc64fast` | +| Area | Compared against | +| ---------------------------- | --------------------------------------------------------------------------- | +| AEAD | RustCrypto AEADs, `aws-lc-rs`, `ring`, `aegis` | +| SHA-2 / HMAC / HKDF / PBKDF2 | RustCrypto, `aws-lc-rs`, `ring` | +| BLAKE2 / BLAKE3 | RustCrypto, `dryoc`, upstream `blake3` | +| ECDSA P-256/P-384 | RustCrypto `p256`/`p384`, `aws-lc-rs`, `ring` | +| Ed25519 / X25519 | dalek, `aws-lc-rs`, `ring` where API-compatible, `dryoc` | +| ML-KEM-512/768/1024 | `libcrux`, `fips203`, RustCrypto `ml-kem`, and target-available `aws-lc-rs` | +| RSA import / verification | RustCrypto `rsa`, `ring`, target-available `aws-lc-rs` | +| Password hashing | RustCrypto, `dryoc` where API-compatible | +| XXH3 / RapidHash | upstream crates | +| CRC | `crc`, `crc-fast`, `crc32fast`, `crc32c`, `crc64fast` | Some common libraries are not primary benchmark baselines: @@ -104,6 +108,24 @@ just bench crate=rscrypto bench=auth filter='^ecdsa-p384/' just bench mlkem ``` +Criterion is the wall-clock authority. The small `just bench-structural` suite +uses Gungraun and Valgrind to inspect instruction and cache-cost structure on a +supported Linux host. Structural counts are compiler- and model-sensitive; do +not convert them into elapsed-time claims. + +Use the diagnostic recipes only after a benchmark establishes a concrete +question: + +```sh +just profile sha2 'sha256/64' 10 +just perf-codegen --asm +just perf-llvm-lines --filter +``` + +`just profile` saves a Samply capture under `target/profiles/`. +`perf-codegen` and `perf-llvm-lines` explain generated code and IR volume; they +do not prove that a change is faster. + Local runs are useful for capacity planning on your hardware. They should not be mixed with published claims unless the run metadata and raw results are kept. On macOS, the local benchmark entry point selects the host CPU unless the caller diff --git a/docs/compliance.md b/docs/compliance.md index 45859257..1f08f7f5 100644 --- a/docs/compliance.md +++ b/docs/compliance.md @@ -17,13 +17,13 @@ text, and their assessor's requirements before release. ## Quick answer -| User question | Answer | -|---|---| -| Do I get a FIPS 140-3 validated module by depending on `rscrypto`? | No. This crate claims no CMVP certificate. | -| Can I use `rscrypto` inside a FIPS-oriented project? | Possibly, if your project owns the module boundary, operational environment, allowed algorithms, self-tests, documentation, and validation path. | -| Which APIs are standards-aligned? | See the inventory below. It is an algorithm and API map, not a validation claim. | -| Which evidence can I cite during review? | Start with [`constant-time.md`](constant-time.md), [`test-vector-coverage.md`](test-vector-coverage.md), [`features.md`](features.md), and [`platforms.md`](platforms.md). | -| What if procurement requires a validated cryptographic module? | Use a module listed by CMVP for the required environment, or pursue validation of a defined module that includes `rscrypto`. | +| User question | Answer | +| ------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Do I get a FIPS 140-3 validated module by depending on `rscrypto`? | No. This crate claims no CMVP certificate. | +| Can I use `rscrypto` inside a FIPS-oriented project? | Possibly, if your project owns the module boundary, operational environment, allowed algorithms, self-tests, documentation, and validation path. | +| Which APIs are standards-aligned? | See the inventory below. It is an algorithm and API map, not a validation claim. | +| Which evidence can I cite during review? | Start with [`constant-time.md`](constant-time.md), [`test-vector-coverage.md`](test-vector-coverage.md), [`features.md`](features.md), and [`platforms.md`](platforms.md). | +| What if procurement requires a validated cryptographic module? | Use a module listed by CMVP for the required environment, or pursue validation of a defined module that includes `rscrypto`. | ## Standards-aligned primitives @@ -32,16 +32,16 @@ API maps to named standards or profiles. They are not FIPS 140-3 validation claims or a list of CMVP-approved security functions. Check current CMVP requirements before placing any surface inside a validation boundary. -| Area | `rscrypto` surface | Boundary users must still own | -|---|---|---| -| AES-GCM AEAD | `Aes128Gcm`, `Aes256Gcm` (`aes-gcm`) follow the SP 800-38D GCM shape. | Key lifecycle, nonce lifecycle, invocation limits, protocol binding, and allowed-use policy. | -| Ascon | `AsconAead128`, `AsconHash256`, `AsconXof128`, and `AsconCxof128` map to the SP 800-232 parameter sets. | Current CMVP eligibility, protocol profile, key and nonce lifecycle, customization strings, and validation boundary. | -| SHA-2 / SHA-3 / SHAKE | `Sha224`, `Sha256`, `Sha384`, `Sha512`, `Sha512_256`, `Sha3_*`, `Shake128`, `Shake256`. | Algorithm selection, message/domain separation, and any protocol-specific hash profile. | -| KMAC / cSHAKE | `Kmac128`, `Kmac256`, `Cshake128`, `Cshake256`. | Customization strings, key management, and protocol profile. | -| HMAC / HKDF / PBKDF2 | `HmacSha256`, `HmacSha384`, `HmacSha512`, `HmacSha3_*`, `HkdfSha256`, `HkdfSha384`, `HkdfSha512`, `Pbkdf2Sha256`, `Pbkdf2Sha512`. | Key separation, salt/IKM policy, iteration counts, output lengths, and password policy. | -| ECDSA | P-256/SHA-256 and P-384/SHA-384 signing and verification (`ecdsa-p256`, `ecdsa-p384`, `ecdsa`). | Key generation policy, signature format, protocol profile, and acceptance criteria. | -| RSA | RSA-PSS, RSASSA-PKCS1-v1_5, OAEP, RSAES-PKCS1-v1_5, DER import/export, and `RsaPrivateKey::generate` under the crate's FIPS 186-5 Appendix A.1.3 probable-prime contract. | Entropy source, key policy, protocol profile, padding choice, blinding policy, and module-level validation. | -| ML-KEM | `MlKem512`, `MlKem768`, `MlKem1024` expose the FIPS 203 parameter sets with typed keys, ciphertexts, shared secrets, prepared-key paths, ACVP vectors, and `fips203` differential tests. | Entropy source, key establishment protocol, hybrid/PQ migration policy, and validation boundary. | +| Area | `rscrypto` surface | Boundary users must still own | +| --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | +| AES-GCM AEAD | `Aes128Gcm`, `Aes256Gcm` (`aes-gcm`) follow the SP 800-38D GCM shape. | Key lifecycle, nonce lifecycle, invocation limits, protocol binding, and allowed-use policy. | +| Ascon | `AsconAead128`, `AsconHash256`, `AsconXof128`, and `AsconCxof128` map to the SP 800-232 parameter sets. | Current CMVP eligibility, protocol profile, key and nonce lifecycle, customization strings, and validation boundary. | +| SHA-2 / SHA-3 / SHAKE | `Sha224`, `Sha256`, `Sha384`, `Sha512`, `Sha512_256`, `Sha3_*`, `Shake128`, `Shake256`. | Algorithm selection, message/domain separation, and any protocol-specific hash profile. | +| KMAC / cSHAKE | `Kmac128`, `Kmac256`, `Cshake128`, `Cshake256`. | Customization strings, key management, and protocol profile. | +| HMAC / HKDF / PBKDF2 | `HmacSha256`, `HmacSha384`, `HmacSha512`, `HmacSha3_*`, `HkdfSha256`, `HkdfSha384`, `HkdfSha512`, `Pbkdf2Sha256`, `Pbkdf2Sha512`. | Key separation, salt/IKM policy, iteration counts, output lengths, and password policy. | +| ECDSA | P-256/SHA-256 and P-384/SHA-384 signing and verification (`ecdsa-p256`, `ecdsa-p384`, `ecdsa`). | Key generation policy, signature format, protocol profile, and acceptance criteria. | +| RSA | RSA-PSS, RSASSA-PKCS1-v1_5, OAEP, RSAES-PKCS1-v1_5, DER import/export, and `RsaPrivateKey::generate` under the crate's FIPS 186-5 Appendix A.1.3 probable-prime contract. | Entropy source, key policy, protocol profile, padding choice, blinding policy, and module-level validation. | +| ML-KEM | `MlKem512`, `MlKem768`, `MlKem1024` expose the FIPS 203 parameter sets with typed keys, ciphertexts, shared secrets, prepared-key paths, ACVP vectors, and `fips203` differential tests. | Entropy source, key establishment protocol, hybrid/PQ migration policy, and validation boundary. | For exact public types and features, use [`types.md`](types.md) and [`features.md`](features.md). @@ -52,14 +52,14 @@ The following APIs may be correct and useful, but they should not be presented as FIPS 140-3 validated, CMVP certified, or part of the FIPS-oriented inventory above unless your own compliance target explicitly allows them: -| Area | Examples | -|---|---| -| Misuse-resistant or non-NIST AEADs | `Aes128GcmSiv`, `Aes256GcmSiv`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `Aegis256` | -| Other hashes / XOFs | `Blake*`, `Blake3`, `Xxh3`, `RapidHash64` | -| Other MACs | Standalone `Poly1305` | -| Other public-key primitives | `Ed25519*`, `X25519*` | -| Password hashing outside SP 800-132 | `Argon2*`, `Scrypt` | -| Checksums and fast hashes | `Crc*`, `Xxh3`, `RapidHash64` | +| Area | Examples | +| ----------------------------------- | ----------------------------------------------------------------------------------- | +| Misuse-resistant or non-NIST AEADs | `Aes128GcmSiv`, `Aes256GcmSiv`, `ChaCha20Poly1305`, `XChaCha20Poly1305`, `Aegis256` | +| Other hashes / XOFs | `Blake*`, `Blake3`, `Xxh3`, `RapidHash64` | +| Other MACs | Standalone `Poly1305` | +| Other public-key primitives | `Ed25519*`, `X25519*` | +| Password hashing outside SP 800-132 | `Argon2*`, `Scrypt` | +| Checksums and fast hashes | `Crc*`, `Xxh3`, `RapidHash64` | This table is about compliance positioning, not engineering quality. For example, Argon2 and scrypt are appropriate password-hashing choices in many @@ -70,14 +70,14 @@ systems, but they are not a FIPS 140-3 validation claim. `rscrypto` publishes evidence that can help a security review or vendor-risk review, but none of it replaces an audit or validation certificate. -| Evidence | Where | -|---|---| -| Security posture and non-claims | [`../README.md#security`](../README.md#security) and this page | -| Constant-time threat model, target scope, and evidence rules | [`constant-time.md`](constant-time.md) and [`../ct.toml`](../ct.toml) | -| Official vectors, Wycheproof coverage, ACVP ML-KEM vectors, differential tests, fuzz corpus replay, and Miri coverage | [`test-vector-coverage.md`](test-vector-coverage.md) | -| Feature and dependency control | [`features.md`](features.md) | -| Platform and dispatch model | [`platforms.md`](platforms.md) | -| Vulnerability reporting process | [`../SECURITY.md`](../SECURITY.md) | +| Evidence | Where | +| --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------- | +| Security posture and non-claims | [`../README.md#security`](../README.md#security) and this page | +| Constant-time threat model, target scope, and evidence rules | [`constant-time.md`](constant-time.md) and [`../ct.toml`](../ct.toml) | +| Official vectors, Wycheproof coverage, ACVP ML-KEM vectors, differential tests, fuzz corpus replay, and Miri coverage | [`test-vector-coverage.md`](test-vector-coverage.md) | +| Feature and dependency control | [`features.md`](features.md) | +| Platform and dispatch model | [`platforms.md`](platforms.md) | +| Vulnerability reporting process | [`../SECURITY.md`](../SECURITY.md) | ## What users still own diff --git a/docs/constant-time.md b/docs/constant-time.md index ec70e11d..d4c336d4 100644 --- a/docs/constant-time.md +++ b/docs/constant-time.md @@ -113,18 +113,24 @@ The release workflow requires native evidence for these LLVM-generated target classes. A row is covered only when it appears as a passing lane in the matching release bundle: -| Target class | Required release evidence | -|---|---| -| Linux `x86_64-unknown-linux-gnu` | Artifact review, generated-code heuristics, empirical timing tests, and binary checks where supported. | -| Linux `aarch64-unknown-linux-gnu` | Artifact review, generated-code heuristics, empirical timing tests, and binary checks where supported. | -| Linux `riscv64gc-unknown-linux-gnu` | Artifact review, generated-code heuristics, and empirical timing tests. | -| Linux `s390x-unknown-linux-gnu` | Artifact review, generated-code heuristics, and empirical timing tests. | -| Linux `powerpc64le-unknown-linux-gnu` | Artifact review, generated-code heuristics, and empirical timing tests. | +| Target class | Required release evidence | +| ------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| Linux `x86_64-unknown-linux-gnu` | Artifact review, generated-code heuristics, empirical timing tests, and binary checks where supported. | +| Linux `aarch64-unknown-linux-gnu` | Artifact review, generated-code heuristics, empirical timing tests, and binary checks where supported. | +| Linux `riscv64gc-unknown-linux-gnu` | Artifact review, generated-code heuristics, and empirical timing tests. | +| Linux `s390x-unknown-linux-gnu` | Artifact review, generated-code heuristics, and empirical timing tests. | +| Linux `powerpc64le-unknown-linux-gnu` | Artifact review, generated-code heuristics, and empirical timing tests. | Release CI exercises multiple x86_64 and AArch64 microarchitectures. The exact CPU, target features, compiler, linker, tools, and artifact hashes are recorded per lane rather than generalized to every CPU implementing the same triple. +On AArch64 targets that advertise FEAT_DIT, RSA private exponentiation enters +data-independent-timing state for the complete fixed-window arithmetic loop and +restores the caller's prior PSTATE afterward. Generic `no_std` AArch64 builds +use this hardening only when compiled with `+dit`; physical evidence remains +bound to the exact release lanes above. + ECDSA P-256/P-384 signing uses multiplication-free, fixed-work limb arithmetic on s390x and RISC-V to avoid the variable-latency scalar multiply observed in earlier native runs. That source and disassembly property is necessary, not diff --git a/docs/features.md b/docs/features.md index e7b70b36..1f1ea0f5 100644 --- a/docs/features.md +++ b/docs/features.md @@ -42,78 +42,78 @@ rscrypto = { version = "0.8.1", features = ["full", "portable-only"] } ### Core features -| Feature | Pulls in | Use | -|---|---|---| -| `default` | `std` | Normal server, CLI, and app builds. | -| `std` | `alloc` | Runtime CPU detection and `std::io` adapters. | -| `alloc` | -- | Allocating APIs such as PHC string encoding and `Vec`-returning digest, MAC, AEAD, and signature helpers. | +| Feature | Pulls in | Use | +| --------- | -------- | --------------------------------------------------------------------------------------------------------- | +| `default` | `std` | Normal server, CLI, and app builds. | +| `std` | `alloc` | Runtime CPU detection and `std::io` adapters. | +| `alloc` | -- | Allocating APIs such as PHC string encoding and `Vec`-returning digest, MAC, AEAD, and signature helpers. | ### Umbrella features -| Feature | Pulls in | -|---|---| -| `full` | `checksums`, `hashes`, `auth`, `aead` | -| `checksums` | `crc16`, `crc24`, `crc32`, `crc64` | -| `hashes` | `crypto-hashes`, `fast-hashes` | -| `crypto-hashes` | `sha2`, `sha3`, `blake2b`, `blake2s`, `blake3`, `ascon-hash` | -| `fast-hashes` | `xxh3`, `rapidhash` | -| `auth` | `macs`, `kdfs`, `password-hashing`, `signatures`, `key-exchange` | -| `macs` | `hmac`, `hmac-sha3`, `kmac`, `poly1305` | -| `kdfs` | `hkdf`, `pbkdf2` | -| `password-hashing` | `argon2`, `scrypt`, `phc-strings` | -| `signatures` | `ecdsa`, `ed25519`, `rsa` | -| `key-exchange` | `x25519`, `ml-kem` | -| `aead` | `aes-gcm`, `aes-gcm-siv`, `chacha20poly1305`, `xchacha20poly1305`, `aegis256`, `ascon-aead` | +| Feature | Pulls in | +| ------------------ | ------------------------------------------------------------------------------------------- | +| `full` | `checksums`, `hashes`, `auth`, `aead` | +| `checksums` | `crc16`, `crc24`, `crc32`, `crc64` | +| `hashes` | `crypto-hashes`, `fast-hashes` | +| `crypto-hashes` | `sha2`, `sha3`, `blake2b`, `blake2s`, `blake3`, `ascon-hash` | +| `fast-hashes` | `xxh3`, `rapidhash` | +| `auth` | `macs`, `kdfs`, `password-hashing`, `signatures`, `key-exchange` | +| `macs` | `hmac`, `hmac-sha3`, `kmac`, `poly1305` | +| `kdfs` | `hkdf`, `pbkdf2` | +| `password-hashing` | `argon2`, `scrypt`, `phc-strings` | +| `signatures` | `ecdsa`, `ed25519`, `rsa` | +| `key-exchange` | `x25519`, `ml-kem` | +| `aead` | `aes-gcm`, `aes-gcm-siv`, `chacha20poly1305`, `xchacha20poly1305`, `aegis256`, `ascon-aead` | ### Algorithm leaf features -| Feature | Pulls in | Enables | -|---|---|---| -| `crc16` | -- | CRC-16/IBM and CRC-16/CCITT | -| `crc24` | -- | CRC-24/OpenPGP | -| `crc32` | -- | CRC-32/IEEE and CRC-32C | -| `crc64` | -- | CRC-64/XZ and CRC-64/NVMe | -| `sha2` | -- | SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/256 | -| `sha3` | -- | SHA3-224/256/384/512, SHAKE128/256, cSHAKE128/256 | -| `blake2b` | -- | BLAKE2b variable output, BLAKE2b-256, BLAKE2b-512 | -| `blake2s` | -- | BLAKE2s-128, BLAKE2s-256 | -| `blake3` | -- | BLAKE3 hash, keyed hash, and XOF | -| `ascon-hash` | -- | Ascon-Hash256, Ascon-XOF128, Ascon-CXOF128 | -| `xxh3` | -- | XXH3-64 and XXH3-128 | -| `rapidhash` | -- | Portable RapidHash V3-64, streaming, and collection state | -| `hmac` | `sha2` | HMAC-SHA256/384/512 | -| `hmac-sha3` | `sha3` | HMAC-SHA3-224/256/384/512 | -| `kmac` | `sha3` | KMAC128 and KMAC256 | -| `hkdf` | `hmac` | HKDF-SHA256, HKDF-SHA384, and HKDF-SHA512 | -| `poly1305` | -- | Standalone Poly1305 one-time MAC | -| `pbkdf2` | `hmac` | PBKDF2-HMAC-SHA256 and PBKDF2-HMAC-SHA512 | -| `phc-strings` | `alloc` | Canonical password-record generation and bounded PHC verification | -| `argon2` | `blake2b`, `alloc` | Argon2i, Argon2d, Argon2id | -| `scrypt` | `pbkdf2`, `alloc` | scrypt | -| `ecdsa-p256` | `hmac` | ECDSA P-256/SHA-256 signing and verification | -| `ecdsa-p384` | `hmac` | ECDSA P-384/SHA-384 signing and verification | -| `ecdsa` | `ecdsa-p256`, `ecdsa-p384` | ECDSA P-256/P-384 signing and verification | -| `ed25519` | `sha2` | Ed25519 signatures | -| `rsa` | `alloc`, `sha2` | RSA public/private keys, RSA signatures, OAEP, PKCS#1 v1.5, key generation | -| `x25519` | -- | X25519 key exchange | -| `ml-kem` | `sha3` | ML-KEM-512, ML-KEM-768, and ML-KEM-1024 key encapsulation | -| `aes-gcm` | -- | AES-128-GCM and AES-256-GCM | -| `aes-gcm-siv` | -- | AES-128-GCM-SIV and AES-256-GCM-SIV | -| `chacha20poly1305` | -- | ChaCha20-Poly1305 | -| `xchacha20poly1305` | -- | XChaCha20-Poly1305 | -| `aegis256` | -- | AEGIS-256 | -| `ascon-aead` | -- | Ascon-AEAD128 | +| Feature | Pulls in | Enables | +| ------------------- | -------------------------- | -------------------------------------------------------------------------- | +| `crc16` | -- | CRC-16/IBM and CRC-16/CCITT | +| `crc24` | -- | CRC-24/OpenPGP | +| `crc32` | -- | CRC-32/IEEE and CRC-32C | +| `crc64` | -- | CRC-64/XZ and CRC-64/NVMe | +| `sha2` | -- | SHA-224, SHA-256, SHA-384, SHA-512, SHA-512/256 | +| `sha3` | -- | SHA3-224/256/384/512, SHAKE128/256, cSHAKE128/256 | +| `blake2b` | -- | BLAKE2b variable output, BLAKE2b-256, BLAKE2b-512 | +| `blake2s` | -- | BLAKE2s-128, BLAKE2s-256 | +| `blake3` | -- | BLAKE3 hash, keyed hash, and XOF | +| `ascon-hash` | -- | Ascon-Hash256, Ascon-XOF128, Ascon-CXOF128 | +| `xxh3` | -- | XXH3-64 and XXH3-128 | +| `rapidhash` | -- | Portable RapidHash V3-64, streaming, and collection state | +| `hmac` | `sha2` | HMAC-SHA256/384/512 | +| `hmac-sha3` | `sha3` | HMAC-SHA3-224/256/384/512 | +| `kmac` | `sha3` | KMAC128 and KMAC256 | +| `hkdf` | `hmac` | HKDF-SHA256, HKDF-SHA384, and HKDF-SHA512 | +| `poly1305` | -- | Standalone Poly1305 one-time MAC | +| `pbkdf2` | `hmac` | PBKDF2-HMAC-SHA256 and PBKDF2-HMAC-SHA512 | +| `phc-strings` | `alloc` | Canonical password-record generation and bounded PHC verification | +| `argon2` | `blake2b`, `alloc` | Argon2i, Argon2d, Argon2id | +| `scrypt` | `pbkdf2`, `alloc` | scrypt | +| `ecdsa-p256` | `hmac` | ECDSA P-256/SHA-256 signing and verification | +| `ecdsa-p384` | `hmac` | ECDSA P-384/SHA-384 signing and verification | +| `ecdsa` | `ecdsa-p256`, `ecdsa-p384` | ECDSA P-256/P-384 signing and verification | +| `ed25519` | `sha2` | Ed25519 signatures | +| `rsa` | `alloc`, `sha2` | RSA public/private keys, RSA signatures, OAEP, PKCS#1 v1.5, key generation | +| `x25519` | -- | X25519 key exchange | +| `ml-kem` | `sha3` | ML-KEM-512, ML-KEM-768, and ML-KEM-1024 key encapsulation | +| `aes-gcm` | -- | AES-128-GCM and AES-256-GCM | +| `aes-gcm-siv` | -- | AES-128-GCM-SIV and AES-256-GCM-SIV | +| `chacha20poly1305` | -- | ChaCha20-Poly1305 | +| `xchacha20poly1305` | -- | XChaCha20-Poly1305 | +| `aegis256` | -- | AEGIS-256 | +| `ascon-aead` | -- | Ascon-AEAD128 | ### Auxiliary features -| Feature | Effect | -|---|---| -| `getrandom` | Adds OS-backed random generation; see below. | -| `serde` | Serde for non-secret byte wrappers (nonces, tags, public keys, signatures). | -| `serde-secrets` | Serde for secret-key and shared-secret bytes. Implies `serde`. Use only for controlled key-material storage, not logs or DTOs. | -| `parallel` | Rayon-backed BLAKE3 and Argon2 lane parallelism. Requires `std`, `blake3`, `argon2`. | -| `diag` | Diagnostic introspection of dispatch decisions and selected benchmark-only component hooks. Requires `std`; hidden diagnostic symbols are not stable application API. | -| `portable-only` | Makes runtime capability detection report no SIMD/ASM capabilities. See below. | +| Feature | Effect | +| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `getrandom` | Adds OS-backed random generation; see below. | +| `serde` | Serde for non-secret byte wrappers (nonces, tags, public keys, signatures). | +| `serde-secrets` | Serde for secret-key and shared-secret bytes. Implies `serde`. Use only for controlled key-material storage, not logs or DTOs. | +| `parallel` | Rayon-backed BLAKE3 and Argon2 lane parallelism. Requires `std`, `blake3`, `argon2`. | +| `diag` | Diagnostic introspection of dispatch decisions and selected benchmark-only component hooks. Requires `std`; hidden diagnostic symbols are not stable application API. | +| `portable-only` | Makes runtime capability detection report no SIMD/ASM capabilities. See below. | ## `getrandom` diff --git a/docs/migration/README.md b/docs/migration/README.md index e5ce58e8..a8556666 100644 --- a/docs/migration/README.md +++ b/docs/migration/README.md @@ -17,69 +17,69 @@ For projects upgrading rscrypto itself, start with ## Checksums -| From | To | Status | -|---|---|---| -| [`crc`](crc.md) | `Crc16Ccitt`, `Crc16Ibm`, `Crc24OpenPgp`, `Crc32`, `Crc32C`, `Crc64`, `Crc64Nvme` | Verified against `crc 3.4.0` | -| [`crc-fast`](crc-fast.md) | `Crc32`, `Crc32C`, `Crc64`, `Crc64Nvme` (catalogue subset) | Verified against `crc-fast 1.10.0` | -| [`crc32fast`](crc32fast.md) | `Crc32` | Output covered by CRC-32 oracle/property tests | -| [`crc32c`](crc32c.md) | `Crc32C` | Output covered by CRC-32C oracle/property tests | -| [`crc64fast`](crc64fast.md) | `Crc64`, `Crc64Nvme` (covers `crc64fast-nvme` aside) | Verified against `crc64fast 1.1.0`; NVMe oracle coverage uses `crc-fast 1.10.0` | +| From | To | Status | +| --------------------------- | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- | +| [`crc`](crc.md) | `Crc16Ccitt`, `Crc16Ibm`, `Crc24OpenPgp`, `Crc32`, `Crc32C`, `Crc64`, `Crc64Nvme` | Verified against `crc 3.4.0` | +| [`crc-fast`](crc-fast.md) | `Crc32`, `Crc32C`, `Crc64`, `Crc64Nvme` (catalogue subset) | Verified against `crc-fast 1.10.0` | +| [`crc32fast`](crc32fast.md) | `Crc32` | Output covered by CRC-32 oracle/property tests | +| [`crc32c`](crc32c.md) | `Crc32C` | Output covered by CRC-32C oracle/property tests | +| [`crc64fast`](crc64fast.md) | `Crc64`, `Crc64Nvme` (covers `crc64fast-nvme` aside) | Verified against `crc64fast 1.1.0`; NVMe oracle coverage uses `crc-fast 1.10.0` | ## Hashes -| From | To | Status | -|---|---|---| -| [`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.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.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` | +| From | To | Status | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------- | +| [`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.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.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` | ## Authentication, MACs, and KDFs -| From | To | Status | -|---|---|---| -| [`hmac`](RustCrypto/hmac.md) (RustCrypto) | `HmacSha256`, `HmacSha384`, `HmacSha512`, `HmacSha3_224`, `HmacSha3_256`, `HmacSha3_384`, `HmacSha3_512` | Verified against `hmac 0.13.0` and RustCrypto SHA-3 digests | -| [`hkdf`](RustCrypto/hkdf.md) (RustCrypto) | `HkdfSha256`, `HkdfSha384`, `HkdfSha512` | Verified against `hkdf 0.13.0` | -| [`pbkdf2`](RustCrypto/pbkdf2.md) (RustCrypto) | `Pbkdf2Sha256`, `Pbkdf2Sha512` | Verified against `pbkdf2 0.13.0` | -| [`sha3-kmac`](sha3-kmac.md) | `Kmac128`, `Kmac256` | KMAC128/256 covered by NIST and `tiny-keccak`; KMAC256 also has Wycheproof coverage | -| [`tiny-keccak`](tiny-keccak.md) | `Kmac128`, `Kmac256`, `Cshake128`, `Cshake256` | Verified against `tiny-keccak 2.0.2` | +| From | To | Status | +| --------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | +| [`hmac`](RustCrypto/hmac.md) (RustCrypto) | `HmacSha256`, `HmacSha384`, `HmacSha512`, `HmacSha3_224`, `HmacSha3_256`, `HmacSha3_384`, `HmacSha3_512` | Verified against `hmac 0.13.0` and RustCrypto SHA-3 digests | +| [`hkdf`](RustCrypto/hkdf.md) (RustCrypto) | `HkdfSha256`, `HkdfSha384`, `HkdfSha512` | Verified against `hkdf 0.13.0` | +| [`pbkdf2`](RustCrypto/pbkdf2.md) (RustCrypto) | `Pbkdf2Sha256`, `Pbkdf2Sha512` | Verified against `pbkdf2 0.13.0` | +| [`sha3-kmac`](sha3-kmac.md) | `Kmac128`, `Kmac256` | KMAC128/256 covered by NIST and `tiny-keccak`; KMAC256 also has Wycheproof coverage | +| [`tiny-keccak`](tiny-keccak.md) | `Kmac128`, `Kmac256`, `Cshake128`, `Cshake256` | Verified against `tiny-keccak 2.0.2` | ## AEAD -| 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.12.0` | +| 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.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.15` | +| [`ascon-aead`](RustCrypto/ascon-aead.md) (RustCrypto) | `AsconAead128` | Verified against `ascon-aead 0.6.0` | +| [`aegis`](aegis.md) | `Aegis256` | Verified against `aegis 0.9.15` | ## Signatures and key exchange -| From | To | Status | -|---|---|---| +| 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.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 3.0.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 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.5.3` | -| [`scrypt`](RustCrypto/scrypt.md) (RustCrypto) | Raw `Scrypt` KDF; bounded `ScryptPassword` records | Verified against `scrypt 0.12.0` | +| From | To | Status | +| --------------------------------------------- | ------------------------------------------------------------- | -------------------------------- | +| [`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 -| From | To | Status | -|---|---|---| -| [`aws-lc-rs`](aws-lc-rs.md) | AEAD, SHA-2, HMAC, HKDF, PBKDF2, ECDSA, Ed25519, X25519, RSA verify | Partial; shape-compatible surfaces only | -| [`aws-lc-sys`](aws-lc-sys.md) | none directly | Not a direct migration; replace the safe wrapper API instead | -| [`dryoc`](dryoc.md) | Ed25519, X25519, BLAKE2, Argon2id/Argon2i-adjacent surfaces | Partial; libsodium-style APIs are not one-to-one | -| [`ring`](ring.md) | AEAD, SHA-2, HMAC, HKDF, PBKDF2, ECDSA, Ed25519, RSA verify | Partial; `ring` is protocol-shaped in several areas | -| [`openssl`](openssl.md) | selected hash, MAC, AEAD, RSA operations | Partial; rscrypto does not replace TLS, PKI, engines, or providers | +| From | To | Status | +| ----------------------------- | ------------------------------------------------------------------- | ------------------------------------------------------------------ | +| [`aws-lc-rs`](aws-lc-rs.md) | AEAD, SHA-2, HMAC, HKDF, PBKDF2, ECDSA, Ed25519, X25519, RSA verify | Partial; shape-compatible surfaces only | +| [`aws-lc-sys`](aws-lc-sys.md) | none directly | Not a direct migration; replace the safe wrapper API instead | +| [`dryoc`](dryoc.md) | Ed25519, X25519, BLAKE2, Argon2id/Argon2i-adjacent surfaces | Partial; libsodium-style APIs are not one-to-one | +| [`ring`](ring.md) | AEAD, SHA-2, HMAC, HKDF, PBKDF2, ECDSA, Ed25519, RSA verify | Partial; `ring` is protocol-shaped in several areas | +| [`openssl`](openssl.md) | selected hash, MAC, AEAD, RSA operations | Partial; rscrypto does not replace TLS, PKI, engines, or providers | diff --git a/docs/migration/RustCrypto/aes-gcm-siv.md b/docs/migration/RustCrypto/aes-gcm-siv.md index bff269c5..8bf4205a 100644 --- a/docs/migration/RustCrypto/aes-gcm-siv.md +++ b/docs/migration/RustCrypto/aes-gcm-siv.md @@ -9,11 +9,11 @@ Evidence: `tests/aes128gcmsiv_oracle.rs`, `tests/aes256gcmsiv_oracle.rs`, and `t ## TL;DR -| | Before (`aes-gcm-siv` 0.12.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)?` | +| | Before (`aes-gcm-siv` 0.12.x) | After (`rscrypto` 0.8.1) | +| --------- | ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | +| 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)?` | ## Cargo.toml @@ -31,10 +31,10 @@ rscrypto = { version = "0.8.1", features = ["aes-gcm-siv"] } ## Algorithm map -| `aes-gcm-siv` type | rscrypto type | Key bytes | -|---|---|---| -| `Aes128GcmSiv` | `Aes128GcmSiv` | 16 | -| `Aes256GcmSiv` | `Aes256GcmSiv` | 32 | +| `aes-gcm-siv` type | rscrypto type | Key bytes | +| ------------------ | -------------- | --------- | +| `Aes128GcmSiv` | `Aes128GcmSiv` | 16 | +| `Aes256GcmSiv` | `Aes256GcmSiv` | 32 | Both variants share the same typed surface (`Aes128GcmSivKey`/`Aes256GcmSivKey`, `Nonce96`, `Aes128GcmSivTag`/`Aes256GcmSivTag`) and the same `Aead` trait. diff --git a/docs/migration/RustCrypto/aes-gcm.md b/docs/migration/RustCrypto/aes-gcm.md index f21f7304..384a36c9 100644 --- a/docs/migration/RustCrypto/aes-gcm.md +++ b/docs/migration/RustCrypto/aes-gcm.md @@ -9,11 +9,11 @@ Evidence: `tests/aes128gcm_oracle.rs`, `tests/aes256gcm_oracle.rs`, and `tests/a ## TL;DR -| | Before (`aes-gcm` 0.11.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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) | +| | Before (`aes-gcm` 0.11.x) | After (`rscrypto` 0.8.1) | +| --------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | +| 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) | ## Cargo.toml @@ -32,9 +32,9 @@ rscrypto = { version = "0.8.1", features = ["aes-gcm"] } ## Algorithm map | `aes-gcm` type | rscrypto type | Key bytes | -|---|---|---| -| `Aes128Gcm` | `Aes128Gcm` | 16 | -| `Aes256Gcm` | `Aes256Gcm` | 32 | +| -------------- | ------------- | --------- | +| `Aes128Gcm` | `Aes128Gcm` | 16 | +| `Aes256Gcm` | `Aes256Gcm` | 32 | Both variants share the same typed surface (`Aes128GcmKey`/`Aes256GcmKey`, `Nonce96`, `Aes128GcmTag`/`Aes256GcmTag`) and the same `Aead` trait. diff --git a/docs/migration/RustCrypto/ascon-aead.md b/docs/migration/RustCrypto/ascon-aead.md index 2c8436e4..3a0b1476 100644 --- a/docs/migration/RustCrypto/ascon-aead.md +++ b/docs/migration/RustCrypto/ascon-aead.md @@ -9,11 +9,11 @@ Evidence: `tests/ascon_aead_oracle.rs`. ## TL;DR -| | Before (`ascon-aead` 0.6.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)?` | +| | Before (`ascon-aead` 0.6.x) | After (`rscrypto` 0.8.1) | +| --------- | ----------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------- | +| 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)?` | ## Cargo.toml @@ -31,11 +31,11 @@ rscrypto = { version = "0.8.1", features = ["ascon-aead"] } ## Algorithm map -| `ascon-aead` type | rscrypto type | Key | Nonce | Tag | -|---|---|---|---|---| -| `AsconAead128` | `AsconAead128` | 16 bytes | 16 bytes | 16 bytes | -| `AsconAead128a` (legacy) | not mapped: superseded by NIST SP 800-232 | | | | -| `AsconAead80pq` (post-quantum-flavored) | not mapped | | | | +| `ascon-aead` type | rscrypto type | Key | Nonce | Tag | +| --------------------------------------- | ----------------------------------------- | -------- | -------- | -------- | +| `AsconAead128` | `AsconAead128` | 16 bytes | 16 bytes | 16 bytes | +| `AsconAead128a` (legacy) | not mapped: superseded by NIST SP 800-232 | | | | +| `AsconAead80pq` (post-quantum-flavored) | not mapped | | | | ## API patterns @@ -98,7 +98,7 @@ cipher.decrypt_in_place(&nonce, aad, &mut buffer, &tag)?; [release evidence](../../constant-time.md). - **128-bit key is the only key length.** Ascon-AEAD does not have a 256-bit variant; SP 800-232 specifies the 128-bit parameter set. -- **Nonce reuse semantics.** Ascon-AEAD-128 is *not* nonce-misuse-resistant. Reusing `(key, nonce)` reveals plaintext XORs. Prefer deterministic uniqueness. A uniformly random 128-bit nonce has lower collision probability than a uniformly random 96-bit nonce at the same message count, but the deployment must still define a message limit. +- **Nonce reuse semantics.** Ascon-AEAD-128 is _not_ nonce-misuse-resistant. Reusing `(key, nonce)` reveals plaintext XORs. Prefer deterministic uniqueness. A uniformly random 128-bit nonce has lower collision probability than a uniformly random 96-bit nonce at the same message count, but the deployment must still define a message limit. - **No `Payload`, no `KeyInit` import.** Same simplification as the rest of the AEAD lane. - **Failed-open buffer semantics change.** RustCrypto keeps the in-place buffer unchanged on error. rscrypto clears it on authentication failure. Combined diff --git a/docs/migration/RustCrypto/ascon-hash.md b/docs/migration/RustCrypto/ascon-hash.md index 538925b3..964a73e1 100644 --- a/docs/migration/RustCrypto/ascon-hash.md +++ b/docs/migration/RustCrypto/ascon-hash.md @@ -9,11 +9,11 @@ Evidence: `tests/ascon_official_vectors.rs`, `tests/ascon_hash_oracle.rs`, `test ## TL;DR -| | Before (`ascon-hash` 0.4.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`ascon-hash` 0.4.x) | After (`rscrypto` 0.8.1) | +| --------- | ------------------------------------------------- | ------------------------------------------------------------- | +| 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)` | ## Cargo.toml @@ -31,11 +31,11 @@ rscrypto = { version = "0.8.1", features = ["ascon-hash"] } ## Algorithm map -| `ascon-hash` type | rscrypto type | Output | -|---|---|---| -| `AsconHash256` | `AsconHash256` | `[u8; 32]` | -| `AsconXof128` | `AsconXof` (XOF) | variable | -| `AsconCxof128` | `AsconCxof128` (customizable XOF) | variable | +| `ascon-hash` type | rscrypto type | Output | +| ----------------- | --------------------------------- | ---------- | +| `AsconHash256` | `AsconHash256` | `[u8; 32]` | +| `AsconXof128` | `AsconXof` (XOF) | variable | +| `AsconCxof128` | `AsconCxof128` (customizable XOF) | variable | ## API patterns diff --git a/docs/migration/RustCrypto/blake2.md b/docs/migration/RustCrypto/blake2.md index 907b5a5f..2728f632 100644 --- a/docs/migration/RustCrypto/blake2.md +++ b/docs/migration/RustCrypto/blake2.md @@ -9,11 +9,11 @@ shape for existing projects. ## TL;DR -| | Before (`blake2` 0.10.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`blake2` 0.10.x) | After (`rscrypto` 0.8.1) | +| --------- | ----------------------------------- | --------------------------------------------------------------------- | +| 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)` | Drop one or both of `blake2b` / `blake2s` from the feature list if you don't use that family. @@ -33,16 +33,16 @@ rscrypto = { version = "0.8.1", features = ["blake2b", "blake2s"] } ## Algorithm map -| `blake2` type | rscrypto type | Output | -|---|---|---| -| `Blake2b` (generic) | `Blake2b256` | `[u8; 32]` | -| `Blake2b` or `Blake2b512` | `Blake2b512` | `[u8; 64]` | -| `Blake2b` (variable, runtime) | `Blake2b` | 1-64 bytes via `digest_into` | -| `Blake2s` | `Blake2s128` | `[u8; 16]` | -| `Blake2s` or `Blake2s256` | `Blake2s256` | `[u8; 32]` | -| `Blake2bMac` (keyed) | `Blake2b256::keyed_digest(...)` | `[u8; 32]` | -| `Blake2bMac512` (keyed, 512-bit) | `Blake2b512::keyed_digest(...)` | `[u8; 64]` | -| `Blake2sMac` (keyed) | `Blake2s256::keyed_digest(...)` | `[u8; 32]` | +| `blake2` type | rscrypto type | Output | +| --------------------------------- | ------------------------------- | ---------------------------- | +| `Blake2b` (generic) | `Blake2b256` | `[u8; 32]` | +| `Blake2b` or `Blake2b512` | `Blake2b512` | `[u8; 64]` | +| `Blake2b` (variable, runtime) | `Blake2b` | 1-64 bytes via `digest_into` | +| `Blake2s` | `Blake2s128` | `[u8; 16]` | +| `Blake2s` or `Blake2s256` | `Blake2s256` | `[u8; 32]` | +| `Blake2bMac` (keyed) | `Blake2b256::keyed_digest(...)` | `[u8; 32]` | +| `Blake2bMac512` (keyed, 512-bit) | `Blake2b512::keyed_digest(...)` | `[u8; 64]` | +| `Blake2sMac` (keyed) | `Blake2s256::keyed_digest(...)` | `[u8; 32]` | `Blake2bp` / `Blake2sp` (parallel variants) are not currently mapped. diff --git a/docs/migration/RustCrypto/chacha20poly1305.md b/docs/migration/RustCrypto/chacha20poly1305.md index e90e6462..d0abfe59 100644 --- a/docs/migration/RustCrypto/chacha20poly1305.md +++ b/docs/migration/RustCrypto/chacha20poly1305.md @@ -10,11 +10,11 @@ Evidence: `tests/chacha20poly1305.rs`, `tests/xchacha20poly1305.rs`, and `tests/ ## TL;DR -| | Before (`chacha20poly1305` 0.11.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)?` | +| | Before (`chacha20poly1305` 0.11.x) | After (`rscrypto` 0.8.1) | +| --------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| 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)?` | Drop `xchacha20poly1305` from the feature list if you don't use the 192-bit-nonce variant. @@ -34,11 +34,11 @@ rscrypto = { version = "0.8.1", features = ["chacha20poly1305", "xchacha20poly13 ## Algorithm map -| `chacha20poly1305` type | rscrypto type | Nonce | -|---|---|---| -| `ChaCha20Poly1305` (RFC 8439, 96-bit nonce) | `ChaCha20Poly1305` | `Nonce96` | -| `XChaCha20Poly1305` (extended, 192-bit nonce) | `XChaCha20Poly1305` | `Nonce192` | -| `ChaCha8Poly1305`, `ChaCha12Poly1305` (reduced rounds) | not mapped: keep `chacha20poly1305` | | +| `chacha20poly1305` type | rscrypto type | Nonce | +| ------------------------------------------------------ | ----------------------------------- | ---------- | +| `ChaCha20Poly1305` (RFC 8439, 96-bit nonce) | `ChaCha20Poly1305` | `Nonce96` | +| `XChaCha20Poly1305` (extended, 192-bit nonce) | `XChaCha20Poly1305` | `Nonce192` | +| `ChaCha8Poly1305`, `ChaCha12Poly1305` (reduced rounds) | not mapped: keep `chacha20poly1305` | | ## API patterns diff --git a/docs/migration/RustCrypto/ed25519-dalek.md b/docs/migration/RustCrypto/ed25519-dalek.md index 11e904e4..acdc9446 100644 --- a/docs/migration/RustCrypto/ed25519-dalek.md +++ b/docs/migration/RustCrypto/ed25519-dalek.md @@ -9,12 +9,12 @@ Evidence: `tests/ed25519_rfc8032_vectors.rs`, `tests/ed25519_oracle.rs`, and `te ## TL;DR -| | Before (`ed25519-dalek` 3.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)?` | +| | Before (`ed25519-dalek` 3.x) | After (`rscrypto` 0.8.1) | +| --------- | ---------------------------------------------------- | ----------------------------------------------------------------------- | +| 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)?` | ## Cargo.toml @@ -34,12 +34,12 @@ The `ed25519` feature implies `sha2` (Ed25519 uses SHA-512 internally per RFC 80 ## Type map -| `ed25519-dalek` type | rscrypto type | Bytes | -|---|---|---| -| `SigningKey` | `Ed25519SecretKey` | `[u8; 32]` (seed) | -| `VerifyingKey` | `Ed25519PublicKey` | `[u8; 32]` | -| `Signature` | `Ed25519Signature` | `[u8; 64]` | -| (implicit pair) | `Ed25519Keypair` | secret + public + expanded form | +| `ed25519-dalek` type | rscrypto type | Bytes | +| -------------------- | ------------------ | ------------------------------- | +| `SigningKey` | `Ed25519SecretKey` | `[u8; 32]` (seed) | +| `VerifyingKey` | `Ed25519PublicKey` | `[u8; 32]` | +| `Signature` | `Ed25519Signature` | `[u8; 64]` | +| (implicit pair) | `Ed25519Keypair` | secret + public + expanded form | ## API patterns @@ -114,7 +114,7 @@ verifying_key.verify(b"message", &sig)?; // legacy lax mode public_key.verify(b"message", &sig)?; // strict by default: there is no lax mode ``` -rscrypto's `verify` is *always* strict (rejects small-order public keys, non-canonical S values per RFC 8032 §5.1.7). There is no separate `verify_strict` method; the only verifier is the strict one. +rscrypto's `verify` is _always_ strict (rejects small-order public keys, non-canonical S values per RFC 8032 §5.1.7). There is no separate `verify_strict` method; the only verifier is the strict one. ### Keypair (combined secret + public) diff --git a/docs/migration/RustCrypto/hkdf.md b/docs/migration/RustCrypto/hkdf.md index a54b6b52..fa5b5112 100644 --- a/docs/migration/RustCrypto/hkdf.md +++ b/docs/migration/RustCrypto/hkdf.md @@ -7,11 +7,11 @@ Evidence: `tests/hkdf_sha256_vectors.rs`, `tests/hkdf_sha384_vectors.rs`, `tests ## TL;DR -| | Before (`hkdf` 0.13.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)?` | +| | Before (`hkdf` 0.13.x) | After (`rscrypto` 0.8.1) | +| --------- | -------------------------------------------------------------- | ------------------------------------------------------- | +| 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)?` | ## Cargo.toml @@ -33,10 +33,10 @@ The `hkdf` feature implies `hmac` which implies `sha2`. ## Algorithm map | `hkdf` instantiation | rscrypto type | HashLen / PRK size | -|---|---|---| -| `Hkdf` | `HkdfSha256` | 32 bytes | -| `Hkdf` | `HkdfSha384` | 48 bytes | -| `Hkdf` | `HkdfSha512` | 64 bytes | +| -------------------- | ------------- | ------------------ | +| `Hkdf` | `HkdfSha256` | 32 bytes | +| `Hkdf` | `HkdfSha384` | 48 bytes | +| `Hkdf` | `HkdfSha512` | 64 bytes | ## API patterns diff --git a/docs/migration/RustCrypto/hmac.md b/docs/migration/RustCrypto/hmac.md index edc7745c..8608b8ea 100644 --- a/docs/migration/RustCrypto/hmac.md +++ b/docs/migration/RustCrypto/hmac.md @@ -7,11 +7,11 @@ Evidence: `tests/hmac_sha256_vectors.rs`, `tests/hmac_sha2_family_vectors.rs`, ` ## TL;DR -| | Before (`hmac` 0.13.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`hmac` 0.13.x) | After (`rscrypto` 0.8.1) | +| --------- | ----------------------------------------------------------------------------------------- | ------------------------------------------------------- | +| 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)` | ## Cargo.toml @@ -32,16 +32,16 @@ The `hmac` feature implies `sha2`: no second dep to manage for SHA-2 HMAC. Use ` ## Algorithm map -| `hmac` instantiation | rscrypto type | Tag size | -|---|---|---| -| `Hmac` | `HmacSha256` | `HmacSha256Tag` | -| `Hmac` | `HmacSha384` | `HmacSha384Tag` | -| `Hmac` | `HmacSha512` | `HmacSha512Tag` | -| `Hmac` | `HmacSha3_224` | `HmacSha3_224Tag` | -| `Hmac` | `HmacSha3_256` | `HmacSha3_256Tag` | -| `Hmac` | `HmacSha3_384` | `HmacSha3_384Tag` | -| `Hmac` | `HmacSha3_512` | `HmacSha3_512Tag` | -| `Hmac` / `Hmac` / `Hmac` | not mapped: keep RustCrypto HMAC | | +| `hmac` instantiation | rscrypto type | Tag size | +| -------------------------------------------------------- | -------------------------------- | ----------------- | +| `Hmac` | `HmacSha256` | `HmacSha256Tag` | +| `Hmac` | `HmacSha384` | `HmacSha384Tag` | +| `Hmac` | `HmacSha512` | `HmacSha512Tag` | +| `Hmac` | `HmacSha3_224` | `HmacSha3_224Tag` | +| `Hmac` | `HmacSha3_256` | `HmacSha3_256Tag` | +| `Hmac` | `HmacSha3_384` | `HmacSha3_384Tag` | +| `Hmac` | `HmacSha3_512` | `HmacSha3_512Tag` | +| `Hmac` / `Hmac` / `Hmac` | not mapped: keep RustCrypto HMAC | | ## API patterns diff --git a/docs/migration/RustCrypto/p256.md b/docs/migration/RustCrypto/p256.md index 74ffe26c..a6dd7894 100644 --- a/docs/migration/RustCrypto/p256.md +++ b/docs/migration/RustCrypto/p256.md @@ -13,12 +13,12 @@ Evidence: `tests/ecdsa_oracle.rs`, `src/auth/ecdsa.rs` unit tests, and ## TL;DR -| | Before (`p256` 0.14) | After (`rscrypto` 0.8.1) | -|---|---|---| +| | Before (`p256` 0.14) | After (`rscrypto` 0.8.1) | +| --------- | --------------------------------------------------- | --------------------------------------------------------------------------------------- | | 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)?` | +| 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)?` | ## Cargo.toml @@ -35,11 +35,11 @@ derivation. ## Type Map -| RustCrypto `p256` | rscrypto | Bytes | -|---|---|---| -| `ecdsa::SigningKey` | `EcdsaP256SecretKey` | 32-byte scalar | -| `ecdsa::VerifyingKey` | `EcdsaP256PublicKey` | uncompressed SEC1 65B | -| `ecdsa::Signature` | `EcdsaP256Signature` | raw `r || s` 64B | +| RustCrypto `p256` | rscrypto | Bytes | +| ------------------------- | ------------------------------------- | ---------------------- | +| `ecdsa::SigningKey` | `EcdsaP256SecretKey` | 32-byte scalar | +| `ecdsa::VerifyingKey` | `EcdsaP256PublicKey` | uncompressed SEC1 65B | +| `ecdsa::Signature` | `EcdsaP256Signature` | raw `r | | s` 64B | | `EncodedPoint` public key | `EcdsaP256PublicKey::from_sec1_bytes` | uncompressed SEC1 only | ## Sign diff --git a/docs/migration/RustCrypto/p384.md b/docs/migration/RustCrypto/p384.md index 4f1328b8..4488e339 100644 --- a/docs/migration/RustCrypto/p384.md +++ b/docs/migration/RustCrypto/p384.md @@ -13,12 +13,12 @@ Evidence: `tests/ecdsa_oracle.rs`, `src/auth/ecdsa.rs` unit tests, and ## TL;DR -| | Before (`p384` 0.14) | After (`rscrypto` 0.8.1) | -|---|---|---| +| | Before (`p384` 0.14) | After (`rscrypto` 0.8.1) | +| --------- | --------------------------------------------------- | --------------------------------------------------------------------------------------- | | 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)?` | +| 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)?` | ## Cargo.toml @@ -35,11 +35,11 @@ derivation. ## Type Map -| RustCrypto `p384` | rscrypto | Bytes | -|---|---|---| -| `ecdsa::SigningKey` | `EcdsaP384SecretKey` | 48-byte scalar | -| `ecdsa::VerifyingKey` | `EcdsaP384PublicKey` | uncompressed SEC1 97B | -| `ecdsa::Signature` | `EcdsaP384Signature` | raw `r || s` 96B | +| RustCrypto `p384` | rscrypto | Bytes | +| ------------------------- | ------------------------------------- | ---------------------- | +| `ecdsa::SigningKey` | `EcdsaP384SecretKey` | 48-byte scalar | +| `ecdsa::VerifyingKey` | `EcdsaP384PublicKey` | uncompressed SEC1 97B | +| `ecdsa::Signature` | `EcdsaP384Signature` | raw `r | | s` 96B | | `EncodedPoint` public key | `EcdsaP384PublicKey::from_sec1_bytes` | uncompressed SEC1 only | ## Sign diff --git a/docs/migration/RustCrypto/pbkdf2.md b/docs/migration/RustCrypto/pbkdf2.md index 4ac1a8ed..c8fa576a 100644 --- a/docs/migration/RustCrypto/pbkdf2.md +++ b/docs/migration/RustCrypto/pbkdf2.md @@ -7,11 +7,11 @@ Evidence: `tests/pbkdf2_kat_vectors.rs`, `tests/pbkdf2_differential.rs`, and `te ## TL;DR -| | Before (`pbkdf2` 0.13.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)?` | +| | Before (`pbkdf2` 0.13.x) | After (`rscrypto` 0.8.1) | +| --------- | -------------------------------------------------- | --------------------------------------------------------- | +| 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)?` | ## Cargo.toml @@ -32,11 +32,11 @@ The `pbkdf2` feature implies `hmac` which implies `sha2`. ## Algorithm map -| `pbkdf2` instantiation | rscrypto type | OWASP Password Storage Cheat Sheet minimum, checked 2026-07-29 | -|---|---|---| -| `pbkdf2_hmac::` | `Pbkdf2Sha256` | `Pbkdf2Sha256::MIN_RECOMMENDED_ITERATIONS` (600,000) | -| `pbkdf2_hmac::` | `Pbkdf2Sha512` | `Pbkdf2Sha512::MIN_RECOMMENDED_ITERATIONS` (220,000) | -| `pbkdf2_hmac::` | not mapped: SHA-1 deprecated for KDF since 2010 | | +| `pbkdf2` instantiation | rscrypto type | OWASP Password Storage Cheat Sheet minimum, checked 2026-07-29 | +| ----------------------- | ----------------------------------------------- | -------------------------------------------------------------- | +| `pbkdf2_hmac::` | `Pbkdf2Sha256` | `Pbkdf2Sha256::MIN_RECOMMENDED_ITERATIONS` (600,000) | +| `pbkdf2_hmac::` | `Pbkdf2Sha512` | `Pbkdf2Sha512::MIN_RECOMMENDED_ITERATIONS` (220,000) | +| `pbkdf2_hmac::` | not mapped: SHA-1 deprecated for KDF since 2010 | | ## API patterns diff --git a/docs/migration/RustCrypto/rsa.md b/docs/migration/RustCrypto/rsa.md index 7bcf1867..2c23daea 100644 --- a/docs/migration/RustCrypto/rsa.md +++ b/docs/migration/RustCrypto/rsa.md @@ -25,13 +25,13 @@ rscrypto = { version = "0.8.1", default-features = false, features = ["rsa", "ge ## Map -| RustCrypto `rsa` | rscrypto | -|---|---| -| `RsaPublicKey` | `RsaPublicKey` | -| `RsaPrivateKey` | `RsaPrivateKey` | -| PKCS#1 / PKCS#8 / SPKI import-export traits | inherent DER import-export methods | -| `Pss`, `Pkcs1v15Sign`, `Oaep` | `RsaPssProfile`, `RsaPkcs1v15Profile`, `RsaOaepProfile` | -| caller-managed RNG | `getrandom` wrappers, or encryption `*_with_random_fill` methods for no-std callers | +| RustCrypto `rsa` | rscrypto | +| ------------------------------------------- | ----------------------------------------------------------------------------------- | +| `RsaPublicKey` | `RsaPublicKey` | +| `RsaPrivateKey` | `RsaPrivateKey` | +| PKCS#1 / PKCS#8 / SPKI import-export traits | inherent DER import-export methods | +| `Pss`, `Pkcs1v15Sign`, `Oaep` | `RsaPssProfile`, `RsaPkcs1v15Profile`, `RsaOaepProfile` | +| caller-managed RNG | `getrandom` wrappers, or encryption `*_with_random_fill` methods for no-std callers | ## Import Keys diff --git a/docs/migration/RustCrypto/sha2.md b/docs/migration/RustCrypto/sha2.md index 116d77ff..77e64a06 100644 --- a/docs/migration/RustCrypto/sha2.md +++ b/docs/migration/RustCrypto/sha2.md @@ -7,11 +7,11 @@ Evidence: `tests/sha2_official_vectors.rs`, `tests/sha256_differential.rs`, and ## TL;DR -| | Before (`sha2` 0.11.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`sha2` 0.11.x) | After (`rscrypto` 0.8.1) | +| --------- | ----------------------------- | ------------------------------------------------------- | +| 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)` | ## Cargo.toml @@ -29,12 +29,12 @@ rscrypto = { version = "0.8.1", features = ["sha2"] } ## Algorithm map -| `sha2` type | rscrypto type | Output | -|---|---|---| -| `sha2::Sha224` | `rscrypto::Sha224` | `[u8; 28]` | -| `sha2::Sha256` | `rscrypto::Sha256` | `[u8; 32]` | -| `sha2::Sha384` | `rscrypto::Sha384` | `[u8; 48]` | -| `sha2::Sha512` | `rscrypto::Sha512` | `[u8; 64]` | +| `sha2` type | rscrypto type | Output | +| ------------------ | ---------------------- | ---------- | +| `sha2::Sha224` | `rscrypto::Sha224` | `[u8; 28]` | +| `sha2::Sha256` | `rscrypto::Sha256` | `[u8; 32]` | +| `sha2::Sha384` | `rscrypto::Sha384` | `[u8; 48]` | +| `sha2::Sha512` | `rscrypto::Sha512` | `[u8; 64]` | | `sha2::Sha512_256` | `rscrypto::Sha512_256` | `[u8; 32]` | `sha2::Sha512_224` is not mapped; keep RustCrypto SHA-2 for that variant. diff --git a/docs/migration/RustCrypto/sha3.md b/docs/migration/RustCrypto/sha3.md index e27ce7f1..82a3a227 100644 --- a/docs/migration/RustCrypto/sha3.md +++ b/docs/migration/RustCrypto/sha3.md @@ -7,11 +7,11 @@ Evidence: `tests/sha3_official_vectors.rs`, `tests/sha3_differential.rs`, `tests ## TL;DR -| | Before (`sha3` 0.12.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`sha3` 0.12.x) | After (`rscrypto` 0.8.1) | +| --------- | ------------------------------- | ------------------------------------------------------- | +| 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)` | ## Cargo.toml @@ -29,14 +29,14 @@ rscrypto = { version = "0.8.1", features = ["sha3"] } ## Algorithm map -| `sha3` type | rscrypto type | Output | -|---|---|---| -| `sha3::Sha3_224` | `rscrypto::Sha3_224` | `[u8; 28]` | -| `sha3::Sha3_256` | `rscrypto::Sha3_256` | `[u8; 32]` | -| `sha3::Sha3_384` | `rscrypto::Sha3_384` | `[u8; 48]` | -| `sha3::Sha3_512` | `rscrypto::Sha3_512` | `[u8; 64]` | -| `sha3::Shake128` | `rscrypto::Shake128` | XOF (variable) | -| `sha3::Shake256` | `rscrypto::Shake256` | XOF (variable) | +| `sha3` type | rscrypto type | Output | +| ----------------- | --------------------- | ---------------------------- | +| `sha3::Sha3_224` | `rscrypto::Sha3_224` | `[u8; 28]` | +| `sha3::Sha3_256` | `rscrypto::Sha3_256` | `[u8; 32]` | +| `sha3::Sha3_384` | `rscrypto::Sha3_384` | `[u8; 48]` | +| `sha3::Sha3_512` | `rscrypto::Sha3_512` | `[u8; 64]` | +| `sha3::Shake128` | `rscrypto::Shake128` | XOF (variable) | +| `sha3::Shake256` | `rscrypto::Shake256` | XOF (variable) | | `sha3::CShake128` | `rscrypto::Cshake128` | XOF (variable, customizable) | | `sha3::CShake256` | `rscrypto::Cshake256` | XOF (variable, customizable) | @@ -103,11 +103,11 @@ reader.squeeze(&mut out); Three changes: -| RustCrypto | rscrypto | -|---|---| -| `Shake128::default()` | `Shake128::new()` | -| `XofReader::read(&mut out)` | `Xof::squeeze(&mut out)` | -| imports `ExtendableOutput`, `Update`, `XofReader` separately | one trait `Xof` | +| RustCrypto | rscrypto | +| ------------------------------------------------------------ | ------------------------ | +| `Shake128::default()` | `Shake128::new()` | +| `XofReader::read(&mut out)` | `Xof::squeeze(&mut out)` | +| imports `ExtendableOutput`, `Update`, `XofReader` separately | one trait `Xof` | The one-shot form is `Shake128::xof(data)`: returns the reader directly, no `new`/`update`/`finalize_xof` chain. diff --git a/docs/migration/RustCrypto/x25519-dalek.md b/docs/migration/RustCrypto/x25519-dalek.md index 5f8590b0..1f726f47 100644 --- a/docs/migration/RustCrypto/x25519-dalek.md +++ b/docs/migration/RustCrypto/x25519-dalek.md @@ -10,11 +10,11 @@ Evidence: `tests/x25519_vectors.rs`, `tests/x25519_oracle.rs`, and `tests/x25519 ## TL;DR -| | Before (`x25519-dalek` 3.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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`) | +| | Before (`x25519-dalek` 3.x) | After (`rscrypto` 0.8.1) | +| --------- | ------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| 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`) | ## Cargo.toml @@ -34,12 +34,12 @@ The `x25519` feature has no transitive dependencies: X25519 needs nothing beyond ## Type map -| `x25519-dalek` type | rscrypto type | Notes | -|---|---|---| -| `StaticSecret` | `X25519SecretKey` | reusable scalar; zeroizes on drop | -| `EphemeralSecret` | `X25519SecretKey` | rscrypto unifies the two: see "Ephemeral vs static" below | -| `PublicKey` | `X25519PublicKey` | 32-byte little-endian Montgomery u-coordinate | -| `SharedSecret` | `X25519SharedSecret` | zeroizes on drop | +| `x25519-dalek` type | rscrypto type | Notes | +| ------------------- | -------------------- | --------------------------------------------------------- | +| `StaticSecret` | `X25519SecretKey` | reusable scalar; zeroizes on drop | +| `EphemeralSecret` | `X25519SecretKey` | rscrypto unifies the two: see "Ephemeral vs static" below | +| `PublicKey` | `X25519PublicKey` | 32-byte little-endian Montgomery u-coordinate | +| `SharedSecret` | `X25519SharedSecret` | zeroizes on drop | ## API patterns diff --git a/docs/migration/aegis.md b/docs/migration/aegis.md index fd8f771c..067d8283 100644 --- a/docs/migration/aegis.md +++ b/docs/migration/aegis.md @@ -9,11 +9,11 @@ Evidence: `tests/aegis256_oracle.rs` and `tests/aead_wycheproof.rs`. ## TL;DR -| | Before (`aegis` 0.9.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)?` | +| | Before (`aegis` 0.9.x) | After (`rscrypto` 0.8.1) | +| --------- | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- | +| 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)?` | ## Cargo.toml @@ -31,12 +31,12 @@ rscrypto = { version = "0.8.1", features = ["aegis256"] } ## Algorithm map -| `aegis` type | rscrypto type | Key | Nonce | Tag | -|---|---|---|---|---| -| `aegis::aegis256::Aegis256` | `Aegis256` | 32 bytes | 32 bytes | 16 bytes (or 32 in upstream) | -| `aegis::aegis128l::Aegis128L` | not currently mapped: open an issue | 16 bytes | 16 bytes | 16/32 bytes | -| `aegis::aegis128x2::Aegis128X2` | not currently mapped | 16 bytes | 16 bytes | 16/32 bytes | -| `aegis::aegis128x4::Aegis128X4` | not currently mapped | 16 bytes | 16 bytes | 16/32 bytes | +| `aegis` type | rscrypto type | Key | Nonce | Tag | +| ------------------------------- | ----------------------------------- | -------- | -------- | ---------------------------- | +| `aegis::aegis256::Aegis256` | `Aegis256` | 32 bytes | 32 bytes | 16 bytes (or 32 in upstream) | +| `aegis::aegis128l::Aegis128L` | not currently mapped: open an issue | 16 bytes | 16 bytes | 16/32 bytes | +| `aegis::aegis128x2::Aegis128X2` | not currently mapped | 16 bytes | 16 bytes | 16/32 bytes | +| `aegis::aegis128x4::Aegis128X4` | not currently mapped | 16 bytes | 16 bytes | 16/32 bytes | rscrypto ships only AEGIS-256 with a 16-byte tag. The `aegis` crate is parameterised over tag size (`Aegis256<16>` or `Aegis256<32>`): fix the upstream tag at 16 to match. @@ -71,11 +71,11 @@ caller-supplied nonce. Prefer `seal_random` for new protocols. Three structural differences: -| `aegis` | rscrypto | -|---|---| +| `aegis` | rscrypto | +| --------------------------------------------------------------- | ----------------------------------------------------------------------- | | `(key, nonce)` together at construction; one cipher per message | `key` at construction; `nonce` per call (cipher reused across messages) | -| Returns `(ciphertext, tag)` separately | Returns single buffer `[ciphertext || tag]` | -| Construction-then-call shape | `Aead`-trait-style call shape | +| Returns `(ciphertext, tag)` separately | Returns single buffer `[ciphertext | | tag]` | +| Construction-then-call shape | `Aead`-trait-style call shape | To rebuild the exact `(ct, tag)` pair, take the last 16 bytes of `ct` as the tag: diff --git a/docs/migration/aws-lc-rs.md b/docs/migration/aws-lc-rs.md index 261b7f99..384ec607 100644 --- a/docs/migration/aws-lc-rs.md +++ b/docs/migration/aws-lc-rs.md @@ -17,18 +17,18 @@ rscrypto = { version = "0.8.1", default-features = false, features = ["sha2", "h ## Map -| `aws-lc-rs` | rscrypto | Status | -|---|---|---| -| `digest::{SHA256, SHA384, SHA512}` | `Sha256`, `Sha384`, `Sha512` | Supported | -| `hmac::{HMAC_SHA256, HMAC_SHA384, HMAC_SHA512}` | `HmacSha256`, `HmacSha384`, `HmacSha512` | Supported | -| `hkdf::{HKDF_SHA256, HKDF_SHA384}` | `HkdfSha256`, `HkdfSha384` | Supported | -| `pbkdf2::{PBKDF2_HMAC_SHA256, PBKDF2_HMAC_SHA512}` | `Pbkdf2Sha256`, `Pbkdf2Sha512` | Supported | -| `aead::{AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305}` | `Aes128Gcm`, `Aes256Gcm`, `ChaCha20Poly1305` | Supported | -| Ed25519 signing / verification | `Ed25519SecretKey`, `Ed25519PublicKey` | Supported | -| X25519 agreement | `X25519SecretKey`, `X25519PublicKey` | Supported | -| RSA-PSS / RSASSA-PKCS1-v1_5 verification | `RsaPublicKey`, `RsaPssProfile`, `RsaPkcs1v15Profile` | Supported | -| ECDSA P-256/P-384 SHA-2 signing and verification | `EcdsaP256SecretKey`, `EcdsaP384SecretKey`, `EcdsaP256PublicKey`, `EcdsaP384PublicKey` | Supported for raw and DER signatures | -| ML-DSA, TLS helpers, provider configuration, FIPS mode | none | Keep `aws-lc-rs` | +| `aws-lc-rs` | rscrypto | Status | +| ------------------------------------------------------ | -------------------------------------------------------------------------------------- | ------------------------------------ | +| `digest::{SHA256, SHA384, SHA512}` | `Sha256`, `Sha384`, `Sha512` | Supported | +| `hmac::{HMAC_SHA256, HMAC_SHA384, HMAC_SHA512}` | `HmacSha256`, `HmacSha384`, `HmacSha512` | Supported | +| `hkdf::{HKDF_SHA256, HKDF_SHA384}` | `HkdfSha256`, `HkdfSha384` | Supported | +| `pbkdf2::{PBKDF2_HMAC_SHA256, PBKDF2_HMAC_SHA512}` | `Pbkdf2Sha256`, `Pbkdf2Sha512` | Supported | +| `aead::{AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305}` | `Aes128Gcm`, `Aes256Gcm`, `ChaCha20Poly1305` | Supported | +| Ed25519 signing / verification | `Ed25519SecretKey`, `Ed25519PublicKey` | Supported | +| X25519 agreement | `X25519SecretKey`, `X25519PublicKey` | Supported | +| RSA-PSS / RSASSA-PKCS1-v1_5 verification | `RsaPublicKey`, `RsaPssProfile`, `RsaPkcs1v15Profile` | Supported | +| ECDSA P-256/P-384 SHA-2 signing and verification | `EcdsaP256SecretKey`, `EcdsaP384SecretKey`, `EcdsaP256PublicKey`, `EcdsaP384PublicKey` | Supported for raw and DER signatures | +| ML-DSA, TLS helpers, provider configuration, FIPS mode | none | Keep `aws-lc-rs` | ## Digest diff --git a/docs/migration/aws-lc-sys.md b/docs/migration/aws-lc-sys.md index 2f49d48c..e3bd2b97 100644 --- a/docs/migration/aws-lc-sys.md +++ b/docs/migration/aws-lc-sys.md @@ -8,11 +8,11 @@ FFI surface for AWS-LC; rscrypto exposes safe Rust primitive APIs. If your code uses `aws-lc-sys` directly, first identify the safe operation you need: -| `aws-lc-sys` use | Better migration target | -|---|---| -| Hashing, MAC, KDF, AEAD, signatures, X25519, RSA verify | rscrypto primitive APIs | -| TLS provider integration | keep AWS-LC through `aws-lc-rs` / your TLS stack | -| Raw BIGNUM, ASN.1, EVP, provider, engine, or FIPS-module plumbing | keep AWS-LC/OpenSSL-family bindings | +| `aws-lc-sys` use | Better migration target | +| ----------------------------------------------------------------- | ------------------------------------------------ | +| Hashing, MAC, KDF, AEAD, signatures, X25519, RSA verify | rscrypto primitive APIs | +| TLS provider integration | keep AWS-LC through `aws-lc-rs` / your TLS stack | +| Raw BIGNUM, ASN.1, EVP, provider, engine, or FIPS-module plumbing | keep AWS-LC/OpenSSL-family bindings | ## Notes diff --git a/docs/migration/blake3.md b/docs/migration/blake3.md index 8e70bd1b..c8c082df 100644 --- a/docs/migration/blake3.md +++ b/docs/migration/blake3.md @@ -9,11 +9,11 @@ Evidence: `tests/blake3_official_vectors.rs` and `tests/blake3_differential.rs`. ## TL;DR -| | Before (`blake3` 1.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`blake3` 1.x) | After (`rscrypto` 0.8.1) | +| --------- | ------------------------------- | --------------------------------------------------------- | +| 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)` | ## Cargo.toml diff --git a/docs/migration/crc-fast.md b/docs/migration/crc-fast.md index d2b791f8..8711279e 100644 --- a/docs/migration/crc-fast.md +++ b/docs/migration/crc-fast.md @@ -10,11 +10,11 @@ Evidence: `tests/crc16_properties.rs`, `tests/crc32_properties.rs`, and ## TL;DR -| | Before (`crc-fast` 1.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`crc-fast` 1.x) | After (`rscrypto` 0.8.1) | +| --------- | --------------------------------------------------- | ----------------------------------------------------------------- | +| 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)` | ## Cargo.toml @@ -37,14 +37,14 @@ families. Use `features = ["checksums"]` to enable every CRC family. `crc-fast` exposes the full RevEng catalogue via `CrcAlgorithm`. The intersection with rscrypto: -| `CrcAlgorithm` variant | rscrypto type | Feature flag | -|---|---|---| -| `Crc32IsoHdlc` (IEEE 802.3) | `Crc32` | `crc32` | -| `Crc32Iscsi` (Castagnoli) | `Crc32C` | `crc32` | -| `Crc64Xz` (ECMA-182) | `Crc64` | `crc64` | -| `Crc64Nvme` | `Crc64Nvme` | `crc64` | -| (CRC-16 variants) | `Crc16Ccitt`, `Crc16Ibm` | `crc16` | -| (CRC-24 variants) | `Crc24OpenPgp` | `crc24` | +| `CrcAlgorithm` variant | rscrypto type | Feature flag | +| --------------------------- | ------------------------ | ------------ | +| `Crc32IsoHdlc` (IEEE 802.3) | `Crc32` | `crc32` | +| `Crc32Iscsi` (Castagnoli) | `Crc32C` | `crc32` | +| `Crc64Xz` (ECMA-182) | `Crc64` | `crc64` | +| `Crc64Nvme` | `Crc64Nvme` | `crc64` | +| (CRC-16 variants) | `Crc16Ccitt`, `Crc16Ibm` | `crc16` | +| (CRC-24 variants) | `Crc24OpenPgp` | `crc24` | For variants outside this list, keep `crc-fast` as a sibling dependency or open a feature request. diff --git a/docs/migration/crc.md b/docs/migration/crc.md index 89102218..4c7091b7 100644 --- a/docs/migration/crc.md +++ b/docs/migration/crc.md @@ -9,11 +9,11 @@ Evidence: `tests/crc16_properties.rs`, `tests/crc24_properties.rs`, `tests/crc32 ## TL;DR -| | Before (`crc` 3.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`crc` 3.x) | After (`rscrypto` 0.8.1) | +| --------- | -------------------------------------------------- | ------------------------------------------------------------ | +| 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)` | ## Cargo.toml @@ -33,15 +33,15 @@ rscrypto = { version = "0.8.1", features = ["checksums"] } ## Algorithm map -| `crc` constant | rscrypto type | Feature flag | -|---|---|---| -| `CRC_16_IBM_SDLC` (CRC-16/X25) | `Crc16Ccitt` | `crc16` | -| `CRC_16_ARC` (CRC-16/IBM) | `Crc16Ibm` | `crc16` | -| `CRC_24_OPENPGP` | `Crc24OpenPgp` | `crc24` | -| `CRC_32_ISO_HDLC` (IEEE 802.3) | `Crc32` (alias `Crc32Ieee`) | `crc32` | -| `CRC_32_ISCSI` (Castagnoli) | `Crc32C` (alias `Crc32Castagnoli`) | `crc32` | -| `CRC_64_XZ` (ECMA-182) | `Crc64` (alias `Crc64Xz`) | `crc64` | -| `CRC_64_NVME` | `Crc64Nvme` | `crc64` | +| `crc` constant | rscrypto type | Feature flag | +| ------------------------------ | ---------------------------------- | ------------ | +| `CRC_16_IBM_SDLC` (CRC-16/X25) | `Crc16Ccitt` | `crc16` | +| `CRC_16_ARC` (CRC-16/IBM) | `Crc16Ibm` | `crc16` | +| `CRC_24_OPENPGP` | `Crc24OpenPgp` | `crc24` | +| `CRC_32_ISO_HDLC` (IEEE 802.3) | `Crc32` (alias `Crc32Ieee`) | `crc32` | +| `CRC_32_ISCSI` (Castagnoli) | `Crc32C` (alias `Crc32Castagnoli`) | `crc32` | +| `CRC_64_XZ` (ECMA-182) | `Crc64` (alias `Crc64Xz`) | `crc64` | +| `CRC_64_NVME` | `Crc64Nvme` | `crc64` | ## API patterns diff --git a/docs/migration/crc32c.md b/docs/migration/crc32c.md index 8f5d3fc3..0e0d689c 100644 --- a/docs/migration/crc32c.md +++ b/docs/migration/crc32c.md @@ -9,11 +9,11 @@ Output is covered by the CRC-32C oracle/property tests in `tests/crc32_propertie ## TL;DR -| | Before (`crc32c` 0.6.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`crc32c` 0.6.x) | After (`rscrypto` 0.8.1) | +| --------- | ------------------------------------------------------ | -------------------------------------------------------------- | +| 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)` | ## Cargo.toml diff --git a/docs/migration/crc32fast.md b/docs/migration/crc32fast.md index e5768a69..f87501ba 100644 --- a/docs/migration/crc32fast.md +++ b/docs/migration/crc32fast.md @@ -6,11 +6,11 @@ Output is covered by the CRC-32 oracle/property tests in `tests/crc32_properties ## TL;DR -| | Before (`crc32fast` 1.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`crc32fast` 1.x) | After (`rscrypto` 0.8.1) | +| --------- | ------------------------ | -------------------------------------------------------- | +| 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)` | ## Cargo.toml diff --git a/docs/migration/crc64fast.md b/docs/migration/crc64fast.md index 1b3e3622..9497a2c0 100644 --- a/docs/migration/crc64fast.md +++ b/docs/migration/crc64fast.md @@ -7,11 +7,11 @@ Evidence: `tests/crc64_properties.rs` compares one-shot, streaming, and combine ## TL;DR -| | Before (`crc64fast` 1.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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()` | +| | Before (`crc64fast` 1.x) | After (`rscrypto` 0.8.1) | +| --------- | --------------------------------------- | -------------------------------------------------------- | +| 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()` | ## Cargo.toml @@ -31,10 +31,10 @@ rscrypto = { version = "0.8.1", features = ["crc64"] } ## Algorithm map -| Upstream crate | Polynomial label | rscrypto type | -|---|---|---| -| `crc64fast::Digest` | "ECMA" (== ECMA-182, == XZ) | `Crc64` (alias `Crc64Xz`) | -| `crc64fast_nvme::Digest` | NVMe (Rocksoft) | `Crc64Nvme` | +| Upstream crate | Polynomial label | rscrypto type | +| ------------------------ | --------------------------- | ------------------------- | +| `crc64fast::Digest` | "ECMA" (== ECMA-182, == XZ) | `Crc64` (alias `Crc64Xz`) | +| `crc64fast_nvme::Digest` | NVMe (Rocksoft) | `Crc64Nvme` | `crc64fast` calls its polynomial "ECMA". This is the ECMA-182 polynomial used by XZ Utils, 7-Zip, and `CRC_64_XZ` in `crc`. Map it to `Crc64` / `Crc64Xz`. @@ -62,10 +62,10 @@ let value = hasher.finalize(); Three method renames: -| `crc64fast` | rscrypto | -|---|---| -| `Digest::new()` | `Crc64::new()` | -| `.write(&[u8])` | `.update(&[u8])` | +| `crc64fast` | rscrypto | +| ----------------- | -------------------- | +| `Digest::new()` | `Crc64::new()` | +| `.write(&[u8])` | `.update(&[u8])` | | `.sum64() -> u64` | `.finalize() -> u64` | ### One-shot (rscrypto-only) diff --git a/docs/migration/dryoc.md b/docs/migration/dryoc.md index 031710dd..14422abe 100644 --- a/docs/migration/dryoc.md +++ b/docs/migration/dryoc.md @@ -19,13 +19,13 @@ rscrypto = { version = "0.8.1", default-features = false, features = ["blake2b", ## Map -| `dryoc` use | rscrypto | Status | -|---|---|---| -| `crypto_generichash` BLAKE2b | `Blake2b256`, `Blake2b512`, `Blake2bParams` | Supported for matching output/keyed modes | -| `crypto_sign_*` Ed25519 | `Ed25519SecretKey`, `Ed25519PublicKey` | Supported | -| `crypto_scalarmult*` X25519 | `X25519SecretKey`, `X25519PublicKey` | Supported | -| `crypto_pwhash` Argon2id / Argon2i | `Argon2id`, `Argon2i` | Partial; map parameters explicitly | -| `secretbox`, `box`, sealed boxes, secretstream | none | Keep `dryoc` or a protocol crate | +| `dryoc` use | rscrypto | Status | +| ---------------------------------------------- | ------------------------------------------- | ----------------------------------------- | +| `crypto_generichash` BLAKE2b | `Blake2b256`, `Blake2b512`, `Blake2bParams` | Supported for matching output/keyed modes | +| `crypto_sign_*` Ed25519 | `Ed25519SecretKey`, `Ed25519PublicKey` | Supported | +| `crypto_scalarmult*` X25519 | `X25519SecretKey`, `X25519PublicKey` | Supported | +| `crypto_pwhash` Argon2id / Argon2i | `Argon2id`, `Argon2i` | Partial; map parameters explicitly | +| `secretbox`, `box`, sealed boxes, secretstream | none | Keep `dryoc` or a protocol crate | ## BLAKE2b Generic Hash diff --git a/docs/migration/openssl.md b/docs/migration/openssl.md index 3ca47e4e..d0dd4ba0 100644 --- a/docs/migration/openssl.md +++ b/docs/migration/openssl.md @@ -20,14 +20,14 @@ rscrypto = { version = "0.8.1", default-features = false, features = ["sha2", "h ## Map -| OpenSSL use | rscrypto | Status | -|---|---|---| -| SHA-2 / SHA-3 / BLAKE2 hashing | hash types | Supported primitive replacement | -| HMAC / HKDF / PBKDF2 | auth types | Supported primitive replacement | -| AES-GCM, ChaCha20-Poly1305 | AEAD types | Supported primitive replacement | -| Ed25519, X25519 | auth key types | Supported primitive replacement | -| RSA-PSS, RSASSA-PKCS1-v1_5, OAEP, RSAES-PKCS1-v1_5 | RSA key/profile APIs | Supported for implemented profiles | -| TLS, X.509 path validation, engines, providers, CMS, PKCS#12, OCSP | none | Keep OpenSSL or a protocol crate | +| OpenSSL use | rscrypto | Status | +| ------------------------------------------------------------------ | -------------------- | ---------------------------------- | +| SHA-2 / SHA-3 / BLAKE2 hashing | hash types | Supported primitive replacement | +| HMAC / HKDF / PBKDF2 | auth types | Supported primitive replacement | +| AES-GCM, ChaCha20-Poly1305 | AEAD types | Supported primitive replacement | +| Ed25519, X25519 | auth key types | Supported primitive replacement | +| RSA-PSS, RSASSA-PKCS1-v1_5, OAEP, RSAES-PKCS1-v1_5 | RSA key/profile APIs | Supported for implemented profiles | +| TLS, X.509 path validation, engines, providers, CMS, PKCS#12, OCSP | none | Keep OpenSSL or a protocol crate | ## Practical Path diff --git a/docs/migration/ring.md b/docs/migration/ring.md index 2600a697..a5489c62 100644 --- a/docs/migration/ring.md +++ b/docs/migration/ring.md @@ -18,18 +18,18 @@ rscrypto = { version = "0.8.1", default-features = false, features = ["sha2", "h ## Map -| `ring` | rscrypto | Status | -|---|---|---| -| `digest::{SHA256, SHA384, SHA512}` | `Sha256`, `Sha384`, `Sha512` | Supported | -| `hmac::{HMAC_SHA256, HMAC_SHA384, HMAC_SHA512}` | `HmacSha256`, `HmacSha384`, `HmacSha512` | Supported | -| `hkdf::{HKDF_SHA256, HKDF_SHA384}` | `HkdfSha256`, `HkdfSha384` | Supported | -| `pbkdf2::{PBKDF2_HMAC_SHA256, PBKDF2_HMAC_SHA512}` | `Pbkdf2Sha256`, `Pbkdf2Sha512` | Supported | -| `aead::{AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305}` | `Aes128Gcm`, `Aes256Gcm`, `ChaCha20Poly1305` | Supported | -| `signature::Ed25519KeyPair` | `Ed25519SecretKey`, `Ed25519PublicKey` | Supported | -| ECDSA P-256/P-384 SHA-2 signing and verification | `EcdsaP256SecretKey`, `EcdsaP384SecretKey`, `EcdsaP256PublicKey`, `EcdsaP384PublicKey` | Supported for raw and DER signatures | -| RSA-PSS / RSASSA-PKCS1-v1_5 public verification | `RsaPublicKey` profiles | Supported | -| X25519 agreement | `X25519SecretKey` | Not a direct call-site migration; `ring` is ephemeral-agreement shaped | -| ECDH P-256/P-384, random, TLS wrappers | none | Keep `ring` or another protocol crate | +| `ring` | rscrypto | Status | +| ----------------------------------------------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | +| `digest::{SHA256, SHA384, SHA512}` | `Sha256`, `Sha384`, `Sha512` | Supported | +| `hmac::{HMAC_SHA256, HMAC_SHA384, HMAC_SHA512}` | `HmacSha256`, `HmacSha384`, `HmacSha512` | Supported | +| `hkdf::{HKDF_SHA256, HKDF_SHA384}` | `HkdfSha256`, `HkdfSha384` | Supported | +| `pbkdf2::{PBKDF2_HMAC_SHA256, PBKDF2_HMAC_SHA512}` | `Pbkdf2Sha256`, `Pbkdf2Sha512` | Supported | +| `aead::{AES_128_GCM, AES_256_GCM, CHACHA20_POLY1305}` | `Aes128Gcm`, `Aes256Gcm`, `ChaCha20Poly1305` | Supported | +| `signature::Ed25519KeyPair` | `Ed25519SecretKey`, `Ed25519PublicKey` | Supported | +| ECDSA P-256/P-384 SHA-2 signing and verification | `EcdsaP256SecretKey`, `EcdsaP384SecretKey`, `EcdsaP256PublicKey`, `EcdsaP384PublicKey` | Supported for raw and DER signatures | +| RSA-PSS / RSASSA-PKCS1-v1_5 public verification | `RsaPublicKey` profiles | Supported | +| X25519 agreement | `X25519SecretKey` | Not a direct call-site migration; `ring` is ephemeral-agreement shaped | +| ECDH P-256/P-384, random, TLS wrappers | none | Keep `ring` or another protocol crate | ## Digest diff --git a/docs/migration/sha3-kmac.md b/docs/migration/sha3-kmac.md index 97a13aa2..23b1e530 100644 --- a/docs/migration/sha3-kmac.md +++ b/docs/migration/sha3-kmac.md @@ -13,11 +13,11 @@ Evidence: `tests/kmac128_nist_vectors.rs`, `tests/kmac128_differential.rs`, ## TL;DR -| | Before (`sha3-kmac` 0.3.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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);` | +| | Before (`sha3-kmac` 0.3.x) | After (`rscrypto` 0.8.1) | +| --------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------- | +| 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);` | ## Cargo.toml @@ -37,11 +37,11 @@ The `kmac` feature implies `sha3`. ## Algorithm map -| `sha3-kmac` type | rscrypto type | Security | -|---|---|---| -| `Kmac128` | `Kmac128` | 128-bit | -| `Kmac256` | `Kmac256` | 256-bit | -| `KmacXof128` / `KmacXof256` | not mapped | Keep `sha3-kmac` for KMACXOF | +| `sha3-kmac` type | rscrypto type | Security | +| --------------------------- | ------------- | ---------------------------- | +| `Kmac128` | `Kmac128` | 128-bit | +| `Kmac256` | `Kmac256` | 256-bit | +| `KmacXof128` / `KmacXof256` | not mapped | Keep `sha3-kmac` for KMACXOF | ## API patterns @@ -64,10 +64,10 @@ let tag: [u8; 32] = Kmac256::mac_array(key, custom, data); Two changes: -| `sha3-kmac` | rscrypto | -|---|---| +| `sha3-kmac` | rscrypto | +| ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | | `Kmac256::new(key, custom)` returns `Result` (key < 32 bytes errors) | `Kmac256::new(key, custom)` is infallible (no minimum key size enforced; SP 800-185 leaves it to caller) | -| `k.finalize_into(&mut tag)` consumes `k` | `Kmac256::mac_into(...)` and the streaming `.finalize_into(&mut [u8])` borrow | +| `k.finalize_into(&mut tag)` consumes `k` | `Kmac256::mac_into(...)` and the streaming `.finalize_into(&mut [u8])` borrow | If your code relied on `sha3-kmac` rejecting keys shorter than 32 bytes, port that application policy explicitly: diff --git a/docs/migration/tiny-keccak.md b/docs/migration/tiny-keccak.md index 4f96756a..458c00a4 100644 --- a/docs/migration/tiny-keccak.md +++ b/docs/migration/tiny-keccak.md @@ -10,13 +10,13 @@ Evidence: `tests/kmac128_differential.rs`, `tests/kmac256_differential.rs`, `tes ## TL;DR -| | Before (`tiny-keccak` 2.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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};` | -| cSHAKE call | `let mut x = CShake::v256(name, custom); x.update(data); x.finalize(&mut out);` | `Cshake256::xof(name, custom, data).squeeze(&mut out);` | +| | Before (`tiny-keccak` 2.x) | After (`rscrypto` 0.8.1) | +| ------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------- | +| 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};` | +| cSHAKE call | `let mut x = CShake::v256(name, custom); x.update(data); x.finalize(&mut out);` | `Cshake256::xof(name, custom, data).squeeze(&mut out);` | ## Cargo.toml @@ -38,14 +38,14 @@ If you only use cSHAKE and not KMAC, swap the feature for `sha3` alone. That exp ## Algorithm map -| `tiny-keccak` type | rscrypto type | Spec | -|---|---|---| -| `Kmac::v256(key, custom)` | `Kmac256` | NIST SP 800-185 §4.3 | -| `Kmac::v128(key, custom)` | `Kmac128` | NIST SP 800-185 §4.3 | -| `KmacXof::v128` / `KmacXof::v256` | not mapped | Keep `tiny-keccak` for KMACXOF | -| `CShake::v256(name, custom)` | `Cshake256` | NIST SP 800-185 §3 | -| `CShake::v128(name, custom)` | `Cshake128` | NIST SP 800-185 §3 | -| `Sha3*`, `Keccak*`, `Shake*`, `ParallelHash*`, `TupleHash*` | covered by `RustCrypto/sha3.md` (SHA-3/SHAKE) or unsupported (Keccak, ParallelHash, TupleHash) | FIPS 202 / SP 800-185 | +| `tiny-keccak` type | rscrypto type | Spec | +| ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------ | +| `Kmac::v256(key, custom)` | `Kmac256` | NIST SP 800-185 §4.3 | +| `Kmac::v128(key, custom)` | `Kmac128` | NIST SP 800-185 §4.3 | +| `KmacXof::v128` / `KmacXof::v256` | not mapped | Keep `tiny-keccak` for KMACXOF | +| `CShake::v256(name, custom)` | `Cshake256` | NIST SP 800-185 §3 | +| `CShake::v128(name, custom)` | `Cshake128` | NIST SP 800-185 §3 | +| `Sha3*`, `Keccak*`, `Shake*`, `ParallelHash*`, `TupleHash*` | covered by `RustCrypto/sha3.md` (SHA-3/SHAKE) or unsupported (Keccak, ParallelHash, TupleHash) | FIPS 202 / SP 800-185 | If you migrate from `tiny-keccak` for SHA-3 / SHAKE specifically (not KMAC / cSHAKE), follow `RustCrypto/sha3.md` instead: same destination types, slightly different upstream API. @@ -70,9 +70,9 @@ let tag: [u8; 32] = Kmac256::mac_array(key, custom, data); Two changes: -| `tiny-keccak` | rscrypto | -|---|---| -| `Hasher` trait import required | inherent methods on `Kmac256` | +| `tiny-keccak` | rscrypto | +| ------------------------------------ | ---------------------------------------------------------------------------------- | +| `Hasher` trait import required | inherent methods on `Kmac256` | | `finalize(&mut tag)` consumes `self` | `mac_into` / `mac_array` are static; streaming `finalize_into` borrows `&mut self` | ### KMAC256: streaming @@ -101,7 +101,7 @@ k.finalize_into(&mut tag); // borrows &mut self ### KMAC256: variable-length output -The output length is part of the KMAC tag derivation per SP 800-185: a 32-byte tag is *not* the prefix of a 64-byte tag. Both crates encode the length identically (verified at 32 and 64 bytes in the harness): +The output length is part of the KMAC tag derivation per SP 800-185: a 32-byte tag is _not_ the prefix of a 64-byte tag. Both crates encode the length identically (verified at 32 and 64 bytes in the harness): ```rust // After @@ -177,11 +177,11 @@ reader.squeeze(&mut out); Three changes from `tiny-keccak`: -| `tiny-keccak` | rscrypto | -|---|---| +| `tiny-keccak` | rscrypto | +| ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | `Hasher::finalize` consumes the sponge and writes to a fixed buffer | `Cshake256` has a fused one-shot `xof(name, custom, data)` that returns a reader; streaming `finalize_xof()` returns the same reader | -| Cannot squeeze more bytes after `finalize` | Reader is a dedicated XOF type: call `squeeze` repeatedly for additional bytes | -| `Hasher::update` adds chunks | `Digest::update` plays the same role | +| Cannot squeeze more bytes after `finalize` | Reader is a dedicated XOF type: call `squeeze` repeatedly for additional bytes | +| `Hasher::update` adds chunks | `Digest::update` plays the same role | ## Notes diff --git a/docs/migration/twox-hash.md b/docs/migration/twox-hash.md index 32fb95f4..7a7a4602 100644 --- a/docs/migration/twox-hash.md +++ b/docs/migration/twox-hash.md @@ -7,11 +7,11 @@ The `twox-hash` mapping is API migration guidance for its 2.x XXH3 surface. ## TL;DR -| | Before (`twox-hash` 2.x) | After (`rscrypto` 0.8.1) | -|---|---|---| -| 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)` | +| | Before (`twox-hash` 2.x) | After (`rscrypto` 0.8.1) | +| --------- | ---------------------------- | ------------------------------------------------------- | +| 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)` | ## Cargo.toml @@ -29,12 +29,12 @@ rscrypto = { version = "0.8.1", features = ["xxh3"] } ## Algorithm map -| `twox-hash` type | rscrypto type | Output | -|---|---|---| -| `XxHash3_64` | `Xxh3` (alias for `Xxh3_64`) | `u64` | -| `XxHash3_128` | `Xxh3_128` | `u128` | -| `XxHash64` (legacy XXH64) | not mapped: keep `twox-hash` | `u64` | -| `XxHash32` (legacy XXH32) | not mapped: keep `twox-hash` | `u32` | +| `twox-hash` type | rscrypto type | Output | +| ------------------------- | ---------------------------- | ------ | +| `XxHash3_64` | `Xxh3` (alias for `Xxh3_64`) | `u64` | +| `XxHash3_128` | `Xxh3_128` | `u128` | +| `XxHash64` (legacy XXH64) | not mapped: keep `twox-hash` | `u64` | +| `XxHash32` (legacy XXH32) | not mapped: keep `twox-hash` | `u32` | ## API patterns diff --git a/docs/migration/xxhash-rust.md b/docs/migration/xxhash-rust.md index 20e056ac..bb127be3 100644 --- a/docs/migration/xxhash-rust.md +++ b/docs/migration/xxhash-rust.md @@ -7,11 +7,11 @@ Evidence: `tests/xxh3_differential.rs`. ## TL;DR -| | Before (`xxhash-rust` 0.8.x) | After (`rscrypto` 0.8.1) | -|---|---|---| +| | Before (`xxhash-rust` 0.8.x) | After (`rscrypto` 0.8.1) | +| --------- | -------------------------------------------------------- | ------------------------------------------------------- | | 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)` | +| Import | `use xxhash_rust::xxh3::xxh3_64;` | `use rscrypto::{FastHash, Xxh3};` | +| Call | `xxh3_64(data)` | `Xxh3::hash(data)` | ## Cargo.toml @@ -29,12 +29,12 @@ rscrypto = { version = "0.8.1", features = ["xxh3"] } ## Algorithm map -| `xxhash-rust` function | rscrypto type | Output | -|---|---|---| -| `xxh3::xxh3_64` | `Xxh3` (alias for `Xxh3_64`) | `u64` | -| `xxh3::xxh3_64_with_seed` | `Xxh3::hash_with_seed` | `u64` | -| `xxh3::xxh3_128` | `Xxh3_128` | `u128` | -| `xxh3::xxh3_128_with_seed` | `Xxh3_128::hash_with_seed` | `u128` | +| `xxhash-rust` function | rscrypto type | Output | +| -------------------------- | ---------------------------- | ------ | +| `xxh3::xxh3_64` | `Xxh3` (alias for `Xxh3_64`) | `u64` | +| `xxh3::xxh3_64_with_seed` | `Xxh3::hash_with_seed` | `u64` | +| `xxh3::xxh3_128` | `Xxh3_128` | `u128` | +| `xxh3::xxh3_128_with_seed` | `Xxh3_128::hash_with_seed` | `u128` | ## API patterns @@ -107,12 +107,12 @@ let h: u64 = hasher.finish(); Renames at the streaming layer: -| `xxhash-rust` | rscrypto | Notes | -|---|---|---| -| `Xxh3::new()` | `Xxh3Hasher::default()` | both unseeded | -| `Xxh3::with_seed(seed)` | `Xxh3Hasher::with_seed(seed)` | seeded ctor | -| `.update(&[u8])` | `.write(&[u8])` | matches `core::hash::Hasher` | -| `.digest() -> u64` | `.finish() -> u64` | matches `core::hash::Hasher` | +| `xxhash-rust` | rscrypto | Notes | +| ----------------------- | ----------------------------- | ---------------------------- | +| `Xxh3::new()` | `Xxh3Hasher::default()` | both unseeded | +| `Xxh3::with_seed(seed)` | `Xxh3Hasher::with_seed(seed)` | seeded ctor | +| `.update(&[u8])` | `.write(&[u8])` | matches `core::hash::Hasher` | +| `.digest() -> u64` | `.finish() -> u64` | matches `core::hash::Hasher` | `Xxh3Hasher` implements `core::hash::Hasher` directly. Drop the `xxhash-rust` crate from your `Hasher` / `BuildHasher` bounds and use `Xxh3Hasher` / `Xxh3BuildHasher` (e.g., `HashMap`). diff --git a/docs/platforms.md b/docs/platforms.md index fa140ec0..22dfc052 100644 --- a/docs/platforms.md +++ b/docs/platforms.md @@ -29,14 +29,14 @@ every listed backend. A target-specific candidate is eligible only when it wins representative target-native measurements. Otherwise dispatch uses a proven lower tier, including portable Rust. -| Target family | Backends used (when CPU advertises them) | -|---|---| -| x86_64 | SSE4.2 CRC32; SSSE3 / PCLMULQDQ; AVX2; AES-NI; SHA-NI; AVX-512F / VL / BW / DQ; AVX-512IFMA; VPCLMULQDQ; VAES | -| aarch64 / Apple Silicon | NEON; AES; PMULL; CRC; SHA2; SHA3 / EOR3; SHA512; SVE2-PMULL where available | -| s390x (IBM Z) | z/Vector; vector enhancements; CPACF / MSA; VGFM; fixed-work ML-KEM arithmetic | -| ppc64le (POWER) | AltiVec; VSX; POWER8 vector / crypto and atomics; POWER9 / POWER10 vector; VPMSUMD | -| riscv64 | V / RVV; Zbc; Zvbc; Zbkc; Zkne / Zknd; Zvkned; Zkt / Zvkt | -| wasm32 | SIMD128 where enabled | +| Target family | Backends used (when CPU advertises them) | +| ----------------------- | ------------------------------------------------------------------------------------------------------------- | +| x86_64 | SSE4.2 CRC32; SSSE3 / PCLMULQDQ; AVX2; AES-NI; SHA-NI; AVX-512F / VL / BW / DQ; AVX-512IFMA; VPCLMULQDQ; VAES | +| aarch64 / Apple Silicon | NEON; AES; PMULL; CRC; SHA2; SHA3 / EOR3; SHA512; SVE2-PMULL where available | +| s390x (IBM Z) | z/Vector; vector enhancements; CPACF / MSA; VGFM; fixed-work ML-KEM arithmetic | +| ppc64le (POWER) | AltiVec; VSX; POWER8 vector / crypto and atomics; POWER9 / POWER10 vector; VPMSUMD | +| riscv64 | V / RVV; Zbc; Zvbc; Zbkc; Zkne / Zknd; Zvkned; Zkt / Zvkt | +| wasm32 | SIMD128 where enabled | ECDSA P-256/P-384 always has a portable Rust path. x86_64 and aarch64 targets also use assembly helpers for selected scalar, field, and basepoint operations diff --git a/docs/release.md b/docs/release.md index 486e9bd1..ef5f619a 100644 --- a/docs/release.md +++ b/docs/release.md @@ -30,7 +30,7 @@ never run `cargo publish` locally. lockfiles, and pushes that follow-up commit. It does not tag or publish. The adapter is required because Cargo Rail does not yet include auxiliary workspace lockfiles in its release mutation. Running `cargo rail release - run rscrypto --bump auto --yes --pr` directly would leave the CT workspaces +run rscrypto --bump auto --yes --pr` directly would leave the CT workspaces stale under `--locked`. 3. Wait for the release pull request's required `Complete` check. Review the @@ -80,15 +80,15 @@ never run `cargo publish` locally. ## Why each gate exists -| Gate | What it prevents | -|---|---| -| Release pull request | An unreviewed version or changelog mutation reaching protected `main`. | -| Exact-commit Weekly release mode | Tagging a candidate without an explicitly requested full suite, compiler-backed Cargo graph assurance, raw CT artifacts, and complete CT/RSA gates. | -| Weekly and RISC-V evidence | Publishing cryptographic claims without the required platform and timing evidence. | -| Signed immutable tag | Moving a released version to different source later. | -| Immutable, attested GitHub Release | Publishing artifacts that cannot be tied back to the tag and build. | -| Environment approval | A tag or compromised workflow publishing to crates.io without a final human decision. | -| Trusted Publishing | Long-lived crates.io credentials becoming a repository secret. | +| Gate | What it prevents | +| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | +| Release pull request | An unreviewed version or changelog mutation reaching protected `main`. | +| Exact-commit Weekly release mode | Tagging a candidate without an explicitly requested full suite, compiler-backed Cargo graph assurance, raw CT artifacts, and complete CT/RSA gates. | +| Weekly and RISC-V evidence | Publishing cryptographic claims without the required platform and timing evidence. | +| Signed immutable tag | Moving a released version to different source later. | +| Immutable, attested GitHub Release | Publishing artifacts that cannot be tied back to the tag and build. | +| Environment approval | A tag or compromised workflow publishing to crates.io without a final human decision. | +| Trusted Publishing | Long-lived crates.io credentials becoming a repository secret. | Pull-request CI answers "may this change merge?" once. Scheduled Weekly assurance keeps routine safety coverage current with compact, short-lived reports. Only a @@ -99,12 +99,12 @@ manually dispatched exact-commit Weekly release run and RISC-V evidence answer Configure the crate on crates.io: -| Field | Value | -|---|---| -| Repository owner | `loadingalias` | -| Repository name | `rscrypto` | +| Field | Value | +| ----------------- | -------------- | +| Repository owner | `loadingalias` | +| Repository name | `rscrypto` | | Workflow filename | `release.yaml` | -| Environment | `crates-io` | +| Environment | `crates-io` | After the first successful Trusted Publishing release, enable crates.io Trusted Publishing Only Mode for `rscrypto`. That disables traditional API diff --git a/docs/secret-lifecycle.md b/docs/secret-lifecycle.md index fba1e16d..fd8a5838 100644 --- a/docs/secret-lifecycle.md +++ b/docs/secret-lifecycle.md @@ -22,18 +22,18 @@ owners merely because they are produced by secret-bearing operations. ## Source ownership and cleanup -| Owner or flow | Retained secret state | Cleanup boundary | -|---|---|---| -| `SecretBytes`, `SecretVec`, typed keys, private keys, and shared secrets | Fixed or variable-length key material | Concrete `Drop`; consuming export either clears the source allocation or explicitly transfers responsibility to the returned ordinary bytes | -| AEAD contexts and AES backend schedules | Expanded encryption keys and authentication subkeys | Context and nested schedule `Drop`; operation-local subkeys and authentication state are cleared after use; failed open and private-output paths clear rejected output | -| HMAC-SHA-2 | Live SHA state, keyed inner/outer prefixes, oversized-key digests, and inner-digest finalization snapshots | Secret-specific SHA finalization clears copied state and padding blocks; reset clears the replaced live state; `Drop` clears the live state and both saved prefixes | -| HKDF and PBKDF2 | PRK or password-derived HMAC prefix words and derivation scratch | Prefix-owner `Drop`; oversized-key/password digests and per-block working values are cleared on every return path | -| Ed25519 signing | Expanded scalar, nonce prefix, nonce hash state, digest, and scalar intermediates | Expanded-secret `Drop`; secret-specific SHA-512 digest/finalization clears hash state and padding snapshots; signing clears scalar and digest temporaries before return | -| HMAC-SHA-3 and KMAC | Keyed Keccak state, initial snapshots, and finalized inner state | Secret-mode Keccak owners clear on replacement and `Drop`; fixed-output finalized sponge copies and inner digests are cleared after absorption | -| Keyed BLAKE2 | Stored key, chaining state, block buffer, and finalization copies | Core and parameter-owner `Drop`; finalized chaining words and partial-block copies are cleared before return | -| Keyed and derive-key BLAKE3 | Key words, chunk/output/root state, CV stack, XOF root, and per-state, reduction, or thread-local parallel CV vectors | Conditional nested `Drop` follows the mode flags; emitted output blocks are cleared after copying; reusable and reduction vectors are cleared after their last keyed use, before logical `Vec::clear`, and on owner drop | -| ECDSA, X25519, ML-KEM, and RSA private operations | Secret scalars, decapsulation arithmetic, private components, blinding values, limbs, encoded-message buffers, and reusable private scratch | Typed and nested owner `Drop`; operation wrappers clear local arrays and initialized heap regions; rejected private outputs are cleared | -| Argon2 and scrypt | Password/pepper-derived matrix or ROMix working set | Owning matrix/state `Drop` clears every initialized block; allocation and parameter failures retain the same RAII boundary | +| Owner or flow | Retained secret state | Cleanup boundary | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `SecretBytes`, `SecretVec`, typed keys, private keys, and shared secrets | Fixed or variable-length key material | Concrete `Drop`; consuming export either clears the source allocation or explicitly transfers responsibility to the returned ordinary bytes | +| AEAD contexts and AES backend schedules | Expanded encryption keys and authentication subkeys | Context and nested schedule `Drop`; operation-local subkeys and authentication state are cleared after use; failed open and private-output paths clear rejected output | +| HMAC-SHA-2 | Live SHA state, keyed inner/outer prefixes, oversized-key digests, and inner-digest finalization snapshots | Secret-specific SHA finalization clears copied state and padding blocks; reset clears the replaced live state; `Drop` clears the live state and both saved prefixes | +| HKDF and PBKDF2 | PRK or password-derived HMAC prefix words and derivation scratch | Prefix-owner `Drop`; oversized-key/password digests and per-block working values are cleared on every return path | +| Ed25519 signing | Expanded scalar, nonce prefix, nonce hash state, digest, and scalar intermediates | Expanded-secret `Drop`; secret-specific SHA-512 digest/finalization clears hash state and padding snapshots; signing clears scalar and digest temporaries before return | +| HMAC-SHA-3 and KMAC | Keyed Keccak state, initial snapshots, and finalized inner state | Secret-mode Keccak owners clear on replacement and `Drop`; fixed-output finalized sponge copies and inner digests are cleared after absorption | +| Keyed BLAKE2 | Stored key, chaining state, block buffer, and finalization copies | Core and parameter-owner `Drop`; finalized chaining words and partial-block copies are cleared before return | +| Keyed and derive-key BLAKE3 | Key words, chunk/output/root state, CV stack, XOF root, and per-state, reduction, or thread-local parallel CV vectors | Conditional nested `Drop` follows the mode flags; emitted output blocks are cleared after copying; reusable and reduction vectors are cleared after their last keyed use, before logical `Vec::clear`, and on owner drop | +| ECDSA, X25519, ML-KEM, and RSA private operations | Secret scalars, decapsulation arithmetic, private components, blinding values, limbs, encoded-message buffers, and reusable private scratch | Typed and nested owner `Drop`; operation wrappers clear local arrays and initialized heap regions; rejected private outputs are cleared | +| Argon2 and scrypt | Password/pepper-derived matrix or ROMix working set | Owning matrix/state `Drop` clears every initialized block; allocation and parameter failures retain the same RAII boundary | Backend call records and fixed-size array arguments may be copied into registers or ABI spill slots by the compiler. Their durable source owners and explicit @@ -42,14 +42,14 @@ explicit limitation rather than an unprovable erasure claim. ## Path audit -| Path | Source audit result | Optimized evidence | -|---|---|---| -| Success | Finalized HMAC/Keccak/BLAKE copies, oversized-key digests, emitted BLAKE3 blocks, AEAD authentication state, parser staging, and private-operation scratch are cleared after the last read | Fixed stack, secret hex success, HMAC-SHA-2/SHA-3 finalization, keyed BLAKE3, and portable AEAD authentication wrappers retain volatile zero stores | -| Error | `ZeroizingBytes` and RAII owners cover parser/generator failure; AEAD and RSA clear rejected plaintext/private output | The secret hex error wrapper reaches the same audited parser as success; RSA private-component validation routes every initialized secret buffer through the audited owner drop before deallocation | -| Early return | Scope-owned fixed and heap secrets retain `Drop` cleanup across `return` and `?`; explicit cleanup precedes returns from manual scratch paths | `diag_zeroize_early_return` retains zero stores; RSA validation stages retain the nested owner-drop chain in release MIR, LLVM IR, and assembly | -| Move or transfer | `SecretBytes::expose` clears its source before returning ordinary bytes; `SecretVec::into_unprotected_vec` transfers the allocation and responsibility; keyed XOF moves transfer one root owner whose destination clears on drop | Fixed-owner move and keyed BLAKE3 XOF move/consume wrappers retain source and destination cleanup | -| Reuse | HMAC clears replaced live SHA state; secret-mode Keccak assignment drops the replaced state; BLAKE3 replacement drops the old owner; BLAKE3 parallel vectors are wiped before reuse | The BLAKE3 reset wrapper contains separate production `Drop` calls for the replaced and final owners | -| Drop | Every confidential public owner in the ownership inventory reaches a concrete or nested cleanup implementation; heap owners traverse initialized storage before deallocation | The gate follows the keyed BLAKE3 wrapper into its production `drop_in_place` and requires retained owner and heap-scratch zero stores | +| Path | Source audit result | Optimized evidence | +| ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Success | Finalized HMAC/Keccak/BLAKE copies, oversized-key digests, emitted BLAKE3 blocks, AEAD authentication state, parser staging, and private-operation scratch are cleared after the last read | Fixed stack, secret hex success, HMAC-SHA-2/SHA-3 finalization, keyed BLAKE3, and portable AEAD authentication wrappers retain volatile zero stores | +| Error | `ZeroizingBytes` and RAII owners cover parser/generator failure; AEAD and RSA clear rejected plaintext/private output | The secret hex error wrapper reaches the same audited parser as success; RSA private-component validation routes every initialized secret buffer through the audited owner drop before deallocation | +| Early return | Scope-owned fixed and heap secrets retain `Drop` cleanup across `return` and `?`; explicit cleanup precedes returns from manual scratch paths | `diag_zeroize_early_return` retains zero stores; RSA validation stages retain the nested owner-drop chain in release MIR, LLVM IR, and assembly | +| Move or transfer | `SecretBytes::expose` clears its source before returning ordinary bytes; `SecretVec::into_unprotected_vec` transfers the allocation and responsibility; keyed XOF moves transfer one root owner whose destination clears on drop | Fixed-owner move and keyed BLAKE3 XOF move/consume wrappers retain source and destination cleanup | +| Reuse | HMAC clears replaced live SHA state; secret-mode Keccak assignment drops the replaced state; BLAKE3 replacement drops the old owner; BLAKE3 parallel vectors are wiped before reuse | The BLAKE3 reset wrapper contains separate production `Drop` calls for the replaced and final owners | +| Drop | Every confidential public owner in the ownership inventory reaches a concrete or nested cleanup implementation; heap owners traverse initialized storage before deallocation | The gate follows the keyed BLAKE3 wrapper into its production `drop_in_place` and requires retained owner and heap-scratch zero stores | ## Inspect an optimized binary @@ -67,21 +67,21 @@ stores and host-architecture zero-store instructions. The gate maps evidence to production behavior as follows: -| Evidence entry point | Boundary made observable | -|---|---| -| `diag_zeroize_fixed_stack`, `diag_zeroize_variable_heap` | Inline and heap owner drop | -| `diag_zeroize_fixed_move`, `diag_zeroize_early_return` | Ownership transfer and early return | -| `diag_zeroize_hex_success`, `diag_zeroize_hex_error` | Shared secret-parser success and error cleanup | -| `diag_zeroize_hmac_sha256_finalize`, `diag_zeroize_hmac_sha3_finalize` | SHA-2 and Keccak keyed finalization, temporary cleanup, and owner drop | -| `diag_hkdf_sha256_derive_portable`, `diag_hkdf_sha384_derive_portable`, `diag_hkdf_sha512_derive_portable` | HKDF SHA-256, SHA-384, and SHA-512 prefix-owner and expansion-scratch cleanup | -| `diag_zeroize_ecdsa_p256_platform_scratch`, `diag_zeroize_ecdsa_p384_platform_scratch` | Accelerated ECDSA wide-input reduction and modular-inversion workspace cleanup | -| `diag_zeroize_blake3_drop`, `diag_zeroize_blake3_reuse` | Production keyed owner drop and replaced-state cleanup | -| `diag_zeroize_blake3_xof_move`, `diag_zeroize_blake3_xof_consume` | Keyed XOF ownership transfer and destination drop | -| `diag_zeroize_blake3_thread_scratch`, `diag_zeroize_blake3_parallel_scratch` | Thread-local and per-state heap CV wipe before reuse or deallocation | -| `diag_poly1305_block_portable_digest`, `diag_ascon_aead128_tag_portable`, `diag_aegis256_update_portable` | Portable Poly1305, Ascon-AEAD, and AEGIS-256 authentication-state cleanup | -| `diag_aes128gcm_ghash`, `diag_aes256gcm_ghash` | AES-GCM authentication-accumulator cleanup | -| `diag_zeroize_mlkem_sha3_512`, `diag_zeroize_mlkem_shake256_{scalar,pair,quad}` | ML-KEM secret SHA3-512 and scalar, paired, or quad SHAKE256 owner and seeded-state cleanup | -| `diag_rsa_validate_pkcs8_private_key_der_stage` | RSA private-component validation success, staged exits, and errors through heap-owner drop before deallocation | +| Evidence entry point | Boundary made observable | +| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | +| `diag_zeroize_fixed_stack`, `diag_zeroize_variable_heap` | Inline and heap owner drop | +| `diag_zeroize_fixed_move`, `diag_zeroize_early_return` | Ownership transfer and early return | +| `diag_zeroize_hex_success`, `diag_zeroize_hex_error` | Shared secret-parser success and error cleanup | +| `diag_zeroize_hmac_sha256_finalize`, `diag_zeroize_hmac_sha3_finalize` | SHA-2 and Keccak keyed finalization, temporary cleanup, and owner drop | +| `diag_hkdf_sha256_derive_portable`, `diag_hkdf_sha384_derive_portable`, `diag_hkdf_sha512_derive_portable` | HKDF SHA-256, SHA-384, and SHA-512 prefix-owner and expansion-scratch cleanup | +| `diag_zeroize_ecdsa_p256_platform_scratch`, `diag_zeroize_ecdsa_p384_platform_scratch` | Accelerated ECDSA wide-input reduction and modular-inversion workspace cleanup | +| `diag_zeroize_blake3_drop`, `diag_zeroize_blake3_reuse` | Production keyed owner drop and replaced-state cleanup | +| `diag_zeroize_blake3_xof_move`, `diag_zeroize_blake3_xof_consume` | Keyed XOF ownership transfer and destination drop | +| `diag_zeroize_blake3_thread_scratch`, `diag_zeroize_blake3_parallel_scratch` | Thread-local and per-state heap CV wipe before reuse or deallocation | +| `diag_poly1305_block_portable_digest`, `diag_ascon_aead128_tag_portable`, `diag_aegis256_update_portable` | Portable Poly1305, Ascon-AEAD, and AEGIS-256 authentication-state cleanup | +| `diag_aes128gcm_ghash`, `diag_aes256gcm_ghash` | AES-GCM authentication-accumulator cleanup | +| `diag_zeroize_mlkem_sha3_512`, `diag_zeroize_mlkem_shake256_{scalar,pair,quad}` | ML-KEM secret SHA3-512 and scalar, paired, or quad SHAKE256 owner and seeded-state cleanup | +| `diag_rsa_validate_pkcs8_private_key_der_stage` | RSA private-component validation success, staged exits, and errors through heap-owner drop before deallocation | This is host-binary evidence, not a universal machine-code proof. The gate must run on each target whose generated cleanup is being claimed; unsupported diff --git a/docs/secret-ownership.md b/docs/secret-ownership.md index a9b69c34..247f4ff8 100644 --- a/docs/secret-ownership.md +++ b/docs/secret-ownership.md @@ -29,33 +29,33 @@ capability for permanent retention. ## Confidential public owners and views -| Type or family | Clone / Copy | Debug | Serialization or export | Storage | Capability use | -|---|---|---|---|---|---| -| `SecretBytes` | Neither | Masked | Consuming `expose()` returns a plain array | Inline | Fixed-size integration boundary whose owned source is cleared on extraction | -| `SecretVec` | Neither | Masked | Consuming `into_unprotected_vec()` returns an ordinary allocation | Heap `Vec` | Variable-size RSA private-key export and explicit transfer to APIs that cannot borrow | -| `expert::DisplaySecret<'a>` | Neither | Intentionally prints bytes through both `Display` and `Debug` | Hex formatting only | Borrowed | Explicit opt-in escape hatch for integrations that must render a key; never use it in logs | -| AEAD `*Key` types | Explicit duplicate; no `Clone` or `Copy` | Masked | `SecretBytes` export, hex opt-in, and `serde-secrets` | Inline | Lets an owned cipher context coexist with a caller-retained typed key while making the extra lifetime visible | -| AEAD cipher contexts | Neither | Masked | None | Inline, except boxed RISC-V fixslice AES schedules | Reusable expanded key and authentication subkey state without exposing a generic duplication path | -| ECDSA P-256/P-384 secret keys and keypairs | Explicit duplicate; no `Clone` or `Copy` | Secret keys are masked; keypairs show only the public half | `SecretBytes` export and hex opt-in; no Serde | Inline | Caller-controlled key/keypair duplication for independent signing owners | -| Ed25519 secret key and keypair | Explicit duplicate; no `Clone` or `Copy` | Secret key is masked; keypair shows only the public half | Secret-key `SecretBytes` export, hex opt-in, and `serde-secrets`; no keypair Serde | Inline | Independent signing owners; keypair duplication also copies its expanded secret state deliberately | -| X25519 secret key and shared secret | Explicit duplicate; no `Clone` or `Copy` | Masked | `SecretBytes` export, hex opt-in, and `serde-secrets` | Inline | Explicit transfer of key-agreement material into a separately owned protocol or KDF context | -| ML-KEM decapsulation keys and shared secrets | Explicit duplicate; no `Clone` or `Copy` | Masked | `SecretBytes` export, hex opt-in, and `serde-secrets` | Inline | Explicit transfer of decapsulation or established key material into another owner | -| 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` | 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 | -| KMAC128/256 states | Neither | Masked | None | Inline | Reuse through `reset`; private cSHAKE snapshots implement non-consuming finalization and reset without exposing a public keyed-state copy | -| PBKDF2-SHA-2 states | Neither | Masked | None | Inline | Repeated derivation borrows one password-prefix owner, which can also be shared by reference | -| `Poly1305OneTimeKey` and `Poly1305` | Neither | Masked | `SecretBytes` key export; no Serde | Inline | Enforces one-time key consumption and consuming finalization | -| `Argon2Context<'a>` | `Clone + Copy` | Secret bytes redacted; secret and associated-data lengths shown | None | Borrowed | Pass-by-value optional pepper/associated-data configuration without duplicating either byte string | -| BLAKE2 keyed parameter builders and variable-output state | Neither | Key bytes masked; builders show key length, salt, and personalization | None | Inline | Reuse by borrowing; independent keyed owners require explicit construction | -| Fixed-output BLAKE2 states | `Clone` | Masked | None | Inline | The same type serves unkeyed `Digest` and keyed modes, so the public `Digest: Clone` contract also permits keyed prefix forks | -| `Blake3` | `Clone` | Masked | None | Inline, plus `Vec` scratch with `parallel` | Fork a streamed common prefix in keyed or derive-key mode; cloning also duplicates initialized parallel scratch | -| `Blake3XofReader` | `Clone` | Masked | None | Inline | Checkpoint or fork an output cursor; the reader is secret-bearing when created from keyed or derive-key state | +| Type or family | Clone / Copy | Debug | Serialization or export | Storage | Capability use | +| --------------------------------------------------------- | ---------------------------------------- | --------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | -------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------- | +| `SecretBytes` | Neither | Masked | Consuming `expose()` returns a plain array | Inline | Fixed-size integration boundary whose owned source is cleared on extraction | +| `SecretVec` | Neither | Masked | Consuming `into_unprotected_vec()` returns an ordinary allocation | Heap `Vec` | Variable-size RSA private-key export and explicit transfer to APIs that cannot borrow | +| `expert::DisplaySecret<'a>` | Neither | Intentionally prints bytes through both `Display` and `Debug` | Hex formatting only | Borrowed | Explicit opt-in escape hatch for integrations that must render a key; never use it in logs | +| AEAD `*Key` types | Explicit duplicate; no `Clone` or `Copy` | Masked | `SecretBytes` export, hex opt-in, and `serde-secrets` | Inline | Lets an owned cipher context coexist with a caller-retained typed key while making the extra lifetime visible | +| AEAD cipher contexts | Neither | Masked | None | Inline, except boxed RISC-V fixslice AES schedules | Reusable expanded key and authentication subkey state without exposing a generic duplication path | +| ECDSA P-256/P-384 secret keys and keypairs | Explicit duplicate; no `Clone` or `Copy` | Secret keys are masked; keypairs show only the public half | `SecretBytes` export and hex opt-in; no Serde | Inline | Caller-controlled key/keypair duplication for independent signing owners | +| Ed25519 secret key and keypair | Explicit duplicate; no `Clone` or `Copy` | Secret key is masked; keypair shows only the public half | Secret-key `SecretBytes` export, hex opt-in, and `serde-secrets`; no keypair Serde | Inline | Independent signing owners; keypair duplication also copies its expanded secret state deliberately | +| X25519 secret key and shared secret | Explicit duplicate; no `Clone` or `Copy` | Masked | `SecretBytes` export, hex opt-in, and `serde-secrets` | Inline | Explicit transfer of key-agreement material into a separately owned protocol or KDF context | +| ML-KEM decapsulation keys and shared secrets | Explicit duplicate; no `Clone` or `Copy` | Masked | `SecretBytes` export, hex opt-in, and `serde-secrets` | Inline | Explicit transfer of decapsulation or established key material into another owner | +| 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` | 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 | +| KMAC128/256 states | Neither | Masked | None | Inline | Reuse through `reset`; private cSHAKE snapshots implement non-consuming finalization and reset without exposing a public keyed-state copy | +| PBKDF2-SHA-2 states | Neither | Masked | None | Inline | Repeated derivation borrows one password-prefix owner, which can also be shared by reference | +| `Poly1305OneTimeKey` and `Poly1305` | Neither | Masked | `SecretBytes` key export; no Serde | Inline | Enforces one-time key consumption and consuming finalization | +| `Argon2Context<'a>` | `Clone + Copy` | Secret bytes redacted; secret and associated-data lengths shown | None | Borrowed | Pass-by-value optional pepper/associated-data configuration without duplicating either byte string | +| BLAKE2 keyed parameter builders and variable-output state | Neither | Key bytes masked; builders show key length, salt, and personalization | None | Inline | Reuse by borrowing; independent keyed owners require explicit construction | +| Fixed-output BLAKE2 states | `Clone` | Masked | None | Inline | The same type serves unkeyed `Digest` and keyed modes, so the public `Digest: Clone` contract also permits keyed prefix forks | +| `Blake3` | `Clone` | Masked | None | Inline, plus `Vec` scratch with `parallel` | Fork a streamed common prefix in keyed or derive-key mode; cloning also duplicates initialized parallel scratch | +| `Blake3XofReader` | `Clone` | Masked | None | Inline | Checkpoint or fork an output cursor; the reader is secret-bearing when created from keyed or derive-key state | ## Protocol-visible authentication owners @@ -65,29 +65,29 @@ copying, rendering, and ordinary serialization do not duplicate a confidential key. Generated-code timing claims remain limited by [`constant-time.md`](constant-time.md). -| Type or family | Clone / Copy | Debug | Serialization | Storage | Capability use | -|---|---|---|---|---|---| -| AEAD tags | `Clone + Copy` | Raw hex | `serde` | Inline | Detached-tag wire formats and cheap by-value API use | -| HMAC-SHA-2 and HMAC-SHA-3 tags | `Clone + Copy` | Raw hex | `serde` | Inline | Protocol transport plus sealed `ct_eq` verification | -| `Poly1305Tag` | `Clone + Copy` | Raw hex | None | Inline | Detached one-time authenticator transport and verification | -| `Blake3KeyedHash` | `Clone + Copy` | Raw hex | None | Inline | Protocol-visible keyed output with sealed `ct_eq` verification | +| Type or family | Clone / Copy | Debug | Serialization | Storage | Capability use | +| ------------------------------ | -------------- | ------- | ------------- | ------- | -------------------------------------------------------------- | +| AEAD tags | `Clone + Copy` | Raw hex | `serde` | Inline | Detached-tag wire formats and cheap by-value API use | +| HMAC-SHA-2 and HMAC-SHA-3 tags | `Clone + Copy` | Raw hex | `serde` | Inline | Protocol transport plus sealed `ct_eq` verification | +| `Poly1305Tag` | `Clone + Copy` | Raw hex | None | Inline | Detached one-time authenticator transport and verification | +| `Blake3KeyedHash` | `Clone + Copy` | Raw hex | None | Inline | Protocol-visible keyed output with sealed `ct_eq` verification | ## Internal and operation-scoped owners -| Owner | Clone / Copy | Debug / serialization | Storage | Capability use | -|---|---|---|---|---| -| `ZeroizingBytes` | Neither | Neither | Inline | Generation and parsing scratch that cannot escape as a generic clone | -| AES expanded schedules | Neither | Neither | Inline; boxed only for the large RISC-V fixslice schedule | Retained by an AEAD context and borrowed by block operations; unused private `Clone` derives were removed during this inventory | -| AEAD authentication working state | Private copies only where a backend finalizer consumes a value | Neither | Inline | Bound, intra-operation snapshot needed by consuming backend finalization | -| HMAC-SHA-3 and KMAC Keccak/cSHAKE snapshots | Private use of `Clone` | Neither | Inline | Implement non-consuming finalization and reset inside one public keyed owner | -| Ed25519 `ExpandedSecret` | Private `Clone` | Masked; no serialization | Inline | Implements the public keypair's explicit `duplicate_secret()` operation | -| X25519 clamped scalar and ECDSA secret scalar/word wrappers | No generic duplication on the owning wrappers | Neither | Inline | Bound one-operation arithmetic ownership | -| ML-KEM prepared decapsulation arithmetic | Private `Clone` | Neither | Inline | Implements the prepared key's explicit `duplicate_secret()` operation | -| RSA private components, buffers, limbs, and key-generation DRBG | Neither | Private integers are masked; no serialization | Heap for variable-width integers/scratch; DRBG inline | Variable-width private arithmetic, reusable scratch, and generated-key construction | -| Argon2 matrix | `MemoryBlock` and borrowed `MatrixView` are `Clone + Copy`; `Matrix` is not | Neither | Heap `Vec` | Algorithm-defined whole-block mixing and disjoint parallel lane views; copying the view duplicates only a pointer/length pair | -| scrypt working state | `SalsaBlock` is `Clone + Copy`; owning states are not | Neither | Heap vectors | Algorithm-defined whole-block ROMix copies while one owner retains the complete work area | -| BLAKE3 parallel scratch | Cloned with `Blake3` | Neither | Per-state vectors plus thread-local vectors | Avoid repeated allocation during parallel subtree reduction; keyed modes make stored chaining values secret-derived | -| Private-key and AEAD backend transfer records | Private `Clone`/`Copy` only where passed by value or snapshotted by a consuming kernel | Neither | Inline | Fixed-layout, call-scoped handoff to portable, SIMD, or assembly code; no public capability | +| Owner | Clone / Copy | Debug / serialization | Storage | Capability use | +| --------------------------------------------------------------- | -------------------------------------------------------------------------------------- | --------------------------------------------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| `ZeroizingBytes` | Neither | Neither | Inline | Generation and parsing scratch that cannot escape as a generic clone | +| AES expanded schedules | Neither | Neither | Inline; boxed only for the large RISC-V fixslice schedule | Retained by an AEAD context and borrowed by block operations; unused private `Clone` derives were removed during this inventory | +| AEAD authentication working state | Private copies only where a backend finalizer consumes a value | Neither | Inline | Bound, intra-operation snapshot needed by consuming backend finalization | +| HMAC-SHA-3 and KMAC Keccak/cSHAKE snapshots | Private use of `Clone` | Neither | Inline | Implement non-consuming finalization and reset inside one public keyed owner | +| Ed25519 `ExpandedSecret` | Private `Clone` | Masked; no serialization | Inline | Implements the public keypair's explicit `duplicate_secret()` operation | +| X25519 clamped scalar and ECDSA secret scalar/word wrappers | No generic duplication on the owning wrappers | Neither | Inline | Bound one-operation arithmetic ownership | +| ML-KEM prepared decapsulation arithmetic | Private `Clone` | Neither | Inline | Implements the prepared key's explicit `duplicate_secret()` operation | +| RSA private components, buffers, limbs, and key-generation DRBG | Neither | Private integers are masked; no serialization | Heap for variable-width integers/scratch; DRBG inline | Variable-width private arithmetic, reusable scratch, and generated-key construction | +| Argon2 matrix | `MemoryBlock` and borrowed `MatrixView` are `Clone + Copy`; `Matrix` is not | Neither | Heap `Vec` | Algorithm-defined whole-block mixing and disjoint parallel lane views; copying the view duplicates only a pointer/length pair | +| scrypt working state | `SalsaBlock` is `Clone + Copy`; owning states are not | Neither | Heap vectors | Algorithm-defined whole-block ROMix copies while one owner retains the complete work area | +| BLAKE3 parallel scratch | Cloned with `Blake3` | Neither | Per-state vectors plus thread-local vectors | Avoid repeated allocation during parallel subtree reduction; keyed modes make stored chaining values secret-derived | +| Private-key and AEAD backend transfer records | Private `Clone`/`Copy` only where passed by value or snapshotted by a consuming kernel | Neither | Inline | Fixed-layout, call-scoped handoff to portable, SIMD, or assembly code; no public capability | ## Review consequences diff --git a/docs/test-vector-coverage.md b/docs/test-vector-coverage.md index 5882e3cf..37f62fd2 100644 --- a/docs/test-vector-coverage.md +++ b/docs/test-vector-coverage.md @@ -23,54 +23,54 @@ the concrete inputs and outputs of each cryptography API. ## Coverage table -| Primitive | Positive vector / oracle coverage | Negative behavior coverage | Gaps / notes | -| --- | --- | --- | --- | -| CRC-16 family | `tests/crc16_properties.rs`; oracle crates in dev-deps | Property tests cover combine/reset/streaming boundaries | No Wycheproof suite applies | -| CRC-24 family | `tests/crc24_properties.rs` | Property tests cover combine/reset/streaming boundaries | No Wycheproof suite applies | -| CRC-32 family | `tests/crc32_properties.rs`; `crc`, `crc32fast`, `crc32c` oracles | Property tests cover combine/reset/streaming boundaries | No Wycheproof suite applies | -| CRC-64 family | `tests/crc64_properties.rs`; `crc64fast` and `crc-fast` oracles | Property tests cover combine/reset/streaming boundaries | No Wycheproof suite applies | -| SHA-224 | `tests/sha2_official_vectors.rs`, `tests/sha256_official_vectors.rs`, `testdata/sha2/sha224.blb` | Streaming/oneshot and dispatch equivalence | No invalid input class | -| SHA-256 | `tests/sha2_official_vectors.rs`, `tests/sha256_official_vectors.rs`, `tests/sha256_differential.rs`, `testdata/sha2/sha256.blb` | Streaming/oneshot and dispatch equivalence | Wycheproof has HMAC/HKDF/PBKDF2 SHA-256 suites, not raw SHA-256 | -| SHA-384 | `tests/sha2_official_vectors.rs`, `testdata/sha2/sha384.blb` | Streaming/oneshot and dispatch equivalence; shared SHA-512-family boundary test panics before overlength absorption | Inputs beyond the FIPS 180-4 length field panic | -| SHA-512 | `tests/sha2_official_vectors.rs`, `tests/sha512_differential.rs`, `testdata/sha2/sha512.blb` | Streaming/oneshot and dispatch equivalence; `message_length_above_boundary_panics` covers the encoded-length boundary | Inputs beyond the FIPS 180-4 length field panic | -| SHA-512/256 | `tests/sha2_official_vectors.rs`, `testdata/sha2/sha512_256.blb` | Streaming/oneshot and dispatch equivalence; shared SHA-512-family boundary test panics before overlength absorption | Inputs beyond the FIPS 180-4 length field panic | -| SHA3-224/256/384/512 | `tests/sha3_official_vectors.rs`, `tests/sha3_differential.rs`, `testdata/sha3/sha3_*.blb` | Streaming/oneshot and dispatch equivalence | No invalid input class | -| SHAKE128 | `tests/sha3_official_vectors.rs`, `tests/shake128_differential.rs`, `testdata/sha3/shake128.blb` | XOF output-length boundaries | No invalid input class | -| SHAKE256 | `tests/sha3_official_vectors.rs`, `tests/shake256_differential.rs`, `testdata/sha3/shake256.blb` | XOF output-length boundaries | No invalid input class | -| cSHAKE128 / cSHAKE256 | `tests/cshake256_nist_vectors.rs`, `tests/cshake256_differential.rs`, `src/hashes/crypto/cshake.rs` unit tests | Customization/function-name boundary coverage through oracle tests; cSHAKE128 also covers SHAKE128 equivalence for empty function/custom strings | No Wycheproof suite currently mapped | -| KMAC128 | `tests/kmac128_nist_vectors.rs`, `tests/kmac128_differential.rs` | NIST samples, streaming/one-shot equivalence, empty/corrupted tag rejection, and `tiny-keccak` differential coverage | No Wycheproof KMAC128 suite currently mapped | -| KMAC256 | `tests/kmac256_nist_vectors.rs`, `tests/kmac256_differential.rs` | `tests/kmac_wycheproof.rs` covers Wycheproof KMAC256 no-customization valid/invalid tags across 128/256/512-bit outputs | Customization coverage stays in NIST/differential tests | -| BLAKE2b | `tests/blake2_official_vectors.rs`, `tests/blake2_differential.rs`, `testdata/blake2/blake2b.blb` | Keyed/unkeyed differential coverage | No invalid input class | -| BLAKE2s | `tests/blake2_official_vectors.rs`, `tests/blake2_differential.rs`, `testdata/blake2/blake2s.blb` | Keyed/unkeyed differential coverage | No invalid input class | -| BLAKE3 | `tests/blake3_official_vectors.rs`, `tests/blake3_differential.rs`, `testdata/blake3/test_vectors.*` | XOF/keyed/derive-key differential coverage | No invalid input class | -| Ascon hash/XOF/CXOF | Final SP 800-232 reference corpora in `tests/ascon_official_vectors.rs`, `tests/ascon_final_kats.rs`, and `testdata/ascon/`; independent coverage in `tests/ascon_hash_oracle.rs` and `tests/ascon_differential.rs` | XOF output and CXOF customization boundaries, streaming, and reset coverage | No invalid input class | -| XXH3 | `tests/xxh3_differential.rs`, `src/hashes/fast/xxh3/stream.rs` unit tests, `fuzz/fuzz_targets/fast_xxh3.rs` | Seeded one-shot properties, streaming oracle/state coverage, and fuzzed partitions | Non-cryptographic; no Wycheproof suite applies | -| RapidHash | `tests/rapidhash_differential.rs`; `src/hashes/fast/rapidhash/stream.rs` unit tests | Seeded one-shot property and collection-state schedule differentials; streaming chunk and reset equivalence | Non-cryptographic; no Wycheproof suite applies | -| HMAC-SHA-256 | `tests/hmac_sha256_vectors.rs`, `tests/hmac_sha256_proptest.rs`, `tests/hmac_sha2_family_vectors.rs` | `tests/hmac_wycheproof.rs` covers full-tag Wycheproof valid/invalid tags; the RFC 4231 prefix case covers exact 64-bit truncated verification and every corrupted byte | Other truncated-tag widths remain out of API scope | -| HMAC-SHA-384 | `tests/hmac_sha384_proptest.rs`, `tests/hmac_sha2_family_vectors.rs` | `tests/hmac_wycheproof.rs` covers full-tag Wycheproof valid/invalid tags; proptests/family vectors cover mismatch behavior | Truncated-tag Wycheproof groups are out of API scope: `verify_tag` accepts only `[u8; 48]` full tags | -| HMAC-SHA-512 | `tests/hmac_sha512_proptest.rs`, `tests/hmac_sha2_family_vectors.rs` | `tests/hmac_wycheproof.rs` covers full-tag Wycheproof valid/invalid tags; proptests/family vectors cover mismatch behavior | Truncated-tag Wycheproof groups are out of API scope: `verify_tag` accepts only `[u8; 64]` full tags | -| HMAC-SHA3-224/256/384/512 | `tests/hmac_sha3_vectors.rs` | Streaming/one-shot/reset coverage against direct RFC 2104 oracles built over RustCrypto SHA-3 digests | No Wycheproof HMAC-SHA3 suite currently mapped | -| HKDF-SHA-256 | `tests/hkdf_sha256_vectors.rs`, `tests/hkdf_sha256_proptest.rs` | `tests/hkdf_wycheproof.rs` covers Wycheproof valid OKM vectors and oversized-output rejection | Current suite maps directly | -| HKDF-SHA-384 | `tests/hkdf_sha384_vectors.rs`, `tests/hkdf_sha384_proptest.rs` | `tests/hkdf_wycheproof.rs` covers Wycheproof valid OKM vectors and oversized-output rejection | Current suite maps directly | -| HKDF-SHA-512 | `tests/hkdf_sha512_vectors.rs` | RFC 5869 case 1, RustCrypto differential coverage, derive-vs-expand equivalence, and oversized-output rejection | Current suite maps directly | -| Poly1305 | `tests/poly1305_vectors.rs` | RFC 8439 §2.5.2 vector, streaming/one-shot equivalence, corrupted tag rejection, and fallible key-generation hook coverage | No Wycheproof standalone Poly1305 suite currently mapped | -| PBKDF2-SHA-256/SHA-512 | `tests/pbkdf2_kat_vectors.rs`, `tests/pbkdf2_differential.rs` | `tests/pbkdf2_wycheproof.rs` covers Wycheproof valid derived-key vectors plus explicit wrong-password/wrong-output rejection | Wycheproof PBKDF2 suites contain valid KATs only for the mapped SHA-2 profiles | -| Argon2d/i/id | `tests/argon2_vectors.rs`, `tests/argon2_differential.rs`, `tests/argon2_kernels.rs`, `tests/argon2_parallel.rs`, `tests/argon2_miri.rs` | `tests/phc_roundtrip.rs` covers generated records, wrong passwords, canonical-only parsing, rehash status, and zero-allocation resource rejection; `tests/phc_external_entropy.rs` covers caller-owned entropy without `getrandom`; fuzz corpus replay covers the bounded public verifier | No Wycheproof PHC string suite exists | -| scrypt | `tests/scrypt_vectors.rs`, `tests/scrypt_differential.rs` | `tests/phc_roundtrip.rs` covers generated records, wrong passwords, canonical-only parsing, rehash status, and zero-allocation resource rejection; `tests/phc_external_entropy.rs` covers caller-owned entropy without `getrandom`; fuzz corpus replay covers the bounded public verifier | Wycheproof PBKDF2 exists, but not scrypt PHC strings | -| AES-128-GCM | `tests/aes128gcm_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AES-GCM 128-bit key, 96-bit nonce open failure; oracle tamper tests cover modified tag/ciphertext/AAD | AES-192 vectors are unsupported by API and skipped | -| AES-256-GCM | `tests/aes256gcm_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AES-GCM 256-bit key, 96-bit nonce open failure; oracle tamper tests cover modified tag/ciphertext/AAD | Non-96-bit Wycheproof nonce cases are unsupported by API and skipped | -| AES-128-GCM-SIV | `tests/aes128gcmsiv_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AES-GCM-SIV 128-bit key open failure; oracle tamper tests cover modified tag/ciphertext/AAD | Current suite maps directly | -| AES-256-GCM-SIV | `tests/aes256gcmsiv_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AES-GCM-SIV 256-bit key open failure; oracle tamper tests cover modified tag/ciphertext/AAD | Current suite maps directly | -| ChaCha20-Poly1305 | `tests/chacha20poly1305.rs` | `tests/aead_wycheproof.rs` covers Wycheproof 96-bit nonce open failure; unit/integration tests cover wrong nonce/tag/AAD | Non-96-bit Wycheproof nonce cases are unsupported by API and skipped | -| 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.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 | -| RSA signatures | `tests/rsa_wycheproof.rs`, `tests/rsa_nist_cavp.rs`, `tests/rsa_public_key.rs` | Wycheproof invalid PKCS#1 v1.5/PSS signatures; `tests/rsa_profile_confusion.rs` rejects PKCS#1/PSS and protocol-scheme confusion | RSA-PSS parameter Wycheproof suites are partly not mapped because the public profile supports SHA-2 fixed profiles | -| RSA OAEP / RSAES-PKCS1-v1_5 | `tests/rsa_wycheproof.rs`, `tests/rsa_public_key.rs` | Wycheproof invalid ciphertexts; scratch decrypt failure clears plaintext; unsupported MGF1-SHA1 vectors reject | Current SHA-2 OAEP suites map directly | -| RSA key parsing / X.509 / TLS / COSE adapters | `tests/rsa_public_key.rs`, `tests/rsa_allocations.rs`, `tests/rsa_leakage.rs` | DER non-canonical forms, unsupported algorithms, policy boundaries, profile confusion, and leakage gate | Keep these tests explicit because the attack surface is protocol/profile confusion, not only raw RSA math | -| Hex/serde public formats | `src/hex.rs`, `tests/serde_roundtrip.rs` | Invalid hex length/character tests; serde byte roundtrips | Negative coverage is format-boundary only | -| Dispatch/fallback surface | `tests/aead_kernel_equivalence.rs`, `tests/aead_foundations.rs`, `tests/portable_fallback.rs`, `tests/vectored_dispatch.rs` | Backend equivalence and fallback dispatch checks | Not a primitive vector suite, but required for SIMD correctness | +| Primitive | Positive vector / oracle coverage | Negative behavior coverage | Gaps / notes | +| --------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| CRC-16 family | `tests/crc16_properties.rs`; oracle crates in dev-deps | Property tests cover combine/reset/streaming boundaries | No Wycheproof suite applies | +| CRC-24 family | `tests/crc24_properties.rs` | Property tests cover combine/reset/streaming boundaries | No Wycheproof suite applies | +| CRC-32 family | `tests/crc32_properties.rs`; `crc`, `crc32fast`, `crc32c` oracles | Property tests cover combine/reset/streaming boundaries | No Wycheproof suite applies | +| CRC-64 family | `tests/crc64_properties.rs`; `crc64fast` and `crc-fast` oracles | Property tests cover combine/reset/streaming boundaries | No Wycheproof suite applies | +| SHA-224 | `tests/sha2_official_vectors.rs`, `tests/sha256_official_vectors.rs`, `testdata/sha2/sha224.blb` | Streaming/oneshot and dispatch equivalence | No invalid input class | +| SHA-256 | `tests/sha2_official_vectors.rs`, `tests/sha256_official_vectors.rs`, `tests/sha256_differential.rs`, `testdata/sha2/sha256.blb` | Streaming/oneshot and dispatch equivalence | Wycheproof has HMAC/HKDF/PBKDF2 SHA-256 suites, not raw SHA-256 | +| SHA-384 | `tests/sha2_official_vectors.rs`, `testdata/sha2/sha384.blb` | Streaming/oneshot and dispatch equivalence; shared SHA-512-family boundary test panics before overlength absorption | Inputs beyond the FIPS 180-4 length field panic | +| SHA-512 | `tests/sha2_official_vectors.rs`, `tests/sha512_differential.rs`, `testdata/sha2/sha512.blb` | Streaming/oneshot and dispatch equivalence; `message_length_above_boundary_panics` covers the encoded-length boundary | Inputs beyond the FIPS 180-4 length field panic | +| SHA-512/256 | `tests/sha2_official_vectors.rs`, `testdata/sha2/sha512_256.blb` | Streaming/oneshot and dispatch equivalence; shared SHA-512-family boundary test panics before overlength absorption | Inputs beyond the FIPS 180-4 length field panic | +| SHA3-224/256/384/512 | `tests/sha3_official_vectors.rs`, `tests/sha3_differential.rs`, `testdata/sha3/sha3_*.blb` | Streaming/oneshot and dispatch equivalence | No invalid input class | +| SHAKE128 | `tests/sha3_official_vectors.rs`, `tests/shake128_differential.rs`, `testdata/sha3/shake128.blb` | XOF output-length boundaries | No invalid input class | +| SHAKE256 | `tests/sha3_official_vectors.rs`, `tests/shake256_differential.rs`, `testdata/sha3/shake256.blb` | XOF output-length boundaries | No invalid input class | +| cSHAKE128 / cSHAKE256 | `tests/cshake256_nist_vectors.rs`, `tests/cshake256_differential.rs`, `src/hashes/crypto/cshake.rs` unit tests | Customization/function-name boundary coverage through oracle tests; cSHAKE128 also covers SHAKE128 equivalence for empty function/custom strings | No Wycheproof suite currently mapped | +| KMAC128 | `tests/kmac128_nist_vectors.rs`, `tests/kmac128_differential.rs` | NIST samples, streaming/one-shot equivalence, empty/corrupted tag rejection, and `tiny-keccak` differential coverage | No Wycheproof KMAC128 suite currently mapped | +| KMAC256 | `tests/kmac256_nist_vectors.rs`, `tests/kmac256_differential.rs` | `tests/kmac_wycheproof.rs` covers Wycheproof KMAC256 no-customization valid/invalid tags across 128/256/512-bit outputs | Customization coverage stays in NIST/differential tests | +| BLAKE2b | `tests/blake2_official_vectors.rs`, `tests/blake2_differential.rs`, `testdata/blake2/blake2b.blb` | Keyed/unkeyed differential coverage | No invalid input class | +| BLAKE2s | `tests/blake2_official_vectors.rs`, `tests/blake2_differential.rs`, `testdata/blake2/blake2s.blb` | Keyed/unkeyed differential coverage | No invalid input class | +| BLAKE3 | `tests/blake3_official_vectors.rs`, `tests/blake3_differential.rs`, `testdata/blake3/test_vectors.*` | XOF/keyed/derive-key differential coverage | No invalid input class | +| Ascon hash/XOF/CXOF | Final SP 800-232 reference corpora in `tests/ascon_official_vectors.rs`, `tests/ascon_final_kats.rs`, and `testdata/ascon/`; independent coverage in `tests/ascon_hash_oracle.rs` and `tests/ascon_differential.rs` | XOF output and CXOF customization boundaries, streaming, and reset coverage | No invalid input class | +| XXH3 | `tests/xxh3_differential.rs`, `src/hashes/fast/xxh3/stream.rs` unit tests, `fuzz/fuzz_targets/fast_xxh3.rs` | Seeded one-shot properties, streaming oracle/state coverage, and fuzzed partitions | Non-cryptographic; no Wycheproof suite applies | +| RapidHash | `tests/rapidhash_differential.rs`; `src/hashes/fast/rapidhash/stream.rs` unit tests | Seeded one-shot property and collection-state schedule differentials; streaming chunk and reset equivalence | Non-cryptographic; no Wycheproof suite applies | +| HMAC-SHA-256 | `tests/hmac_sha256_vectors.rs`, `tests/hmac_sha256_proptest.rs`, `tests/hmac_sha2_family_vectors.rs` | `tests/hmac_wycheproof.rs` covers full-tag Wycheproof valid/invalid tags; the RFC 4231 prefix case covers exact 64-bit truncated verification and every corrupted byte | Other truncated-tag widths remain out of API scope | +| HMAC-SHA-384 | `tests/hmac_sha384_proptest.rs`, `tests/hmac_sha2_family_vectors.rs` | `tests/hmac_wycheproof.rs` covers full-tag Wycheproof valid/invalid tags; proptests/family vectors cover mismatch behavior | Truncated-tag Wycheproof groups are out of API scope: `verify_tag` accepts only `[u8; 48]` full tags | +| HMAC-SHA-512 | `tests/hmac_sha512_proptest.rs`, `tests/hmac_sha2_family_vectors.rs` | `tests/hmac_wycheproof.rs` covers full-tag Wycheproof valid/invalid tags; proptests/family vectors cover mismatch behavior | Truncated-tag Wycheproof groups are out of API scope: `verify_tag` accepts only `[u8; 64]` full tags | +| HMAC-SHA3-224/256/384/512 | `tests/hmac_sha3_vectors.rs` | Streaming/one-shot/reset coverage against direct RFC 2104 oracles built over RustCrypto SHA-3 digests | No Wycheproof HMAC-SHA3 suite currently mapped | +| HKDF-SHA-256 | `tests/hkdf_sha256_vectors.rs`, `tests/hkdf_sha256_proptest.rs` | `tests/hkdf_wycheproof.rs` covers Wycheproof valid OKM vectors and oversized-output rejection | Current suite maps directly | +| HKDF-SHA-384 | `tests/hkdf_sha384_vectors.rs`, `tests/hkdf_sha384_proptest.rs` | `tests/hkdf_wycheproof.rs` covers Wycheproof valid OKM vectors and oversized-output rejection | Current suite maps directly | +| HKDF-SHA-512 | `tests/hkdf_sha512_vectors.rs` | RFC 5869 case 1, RustCrypto differential coverage, derive-vs-expand equivalence, and oversized-output rejection | Current suite maps directly | +| Poly1305 | `tests/poly1305_vectors.rs` | RFC 8439 §2.5.2 vector, streaming/one-shot equivalence, corrupted tag rejection, and fallible key-generation hook coverage | No Wycheproof standalone Poly1305 suite currently mapped | +| PBKDF2-SHA-256/SHA-512 | `tests/pbkdf2_kat_vectors.rs`, `tests/pbkdf2_differential.rs` | `tests/pbkdf2_wycheproof.rs` covers Wycheproof valid derived-key vectors plus explicit wrong-password/wrong-output rejection | Wycheproof PBKDF2 suites contain valid KATs only for the mapped SHA-2 profiles | +| Argon2d/i/id | `tests/argon2_vectors.rs`, `tests/argon2_differential.rs`, `tests/argon2_kernels.rs`, `tests/argon2_parallel.rs`, `tests/argon2_miri.rs` | `tests/phc_roundtrip.rs` covers generated records, wrong passwords, canonical-only parsing, rehash status, and zero-allocation resource rejection; `tests/phc_external_entropy.rs` covers caller-owned entropy without `getrandom`; fuzz corpus replay covers the bounded public verifier | No Wycheproof PHC string suite exists | +| scrypt | `tests/scrypt_vectors.rs`, `tests/scrypt_differential.rs` | `tests/phc_roundtrip.rs` covers generated records, wrong passwords, canonical-only parsing, rehash status, and zero-allocation resource rejection; `tests/phc_external_entropy.rs` covers caller-owned entropy without `getrandom`; fuzz corpus replay covers the bounded public verifier | Wycheproof PBKDF2 exists, but not scrypt PHC strings | +| AES-128-GCM | `tests/aes128gcm_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AES-GCM 128-bit key, 96-bit nonce open failure; oracle tamper tests cover modified tag/ciphertext/AAD | AES-192 vectors are unsupported by API and skipped | +| AES-256-GCM | `tests/aes256gcm_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AES-GCM 256-bit key, 96-bit nonce open failure; oracle tamper tests cover modified tag/ciphertext/AAD | Non-96-bit Wycheproof nonce cases are unsupported by API and skipped | +| AES-128-GCM-SIV | `tests/aes128gcmsiv_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AES-GCM-SIV 128-bit key open failure; oracle tamper tests cover modified tag/ciphertext/AAD | Current suite maps directly | +| AES-256-GCM-SIV | `tests/aes256gcmsiv_oracle.rs` | `tests/aead_wycheproof.rs` covers Wycheproof AES-GCM-SIV 256-bit key open failure; oracle tamper tests cover modified tag/ciphertext/AAD | Current suite maps directly | +| ChaCha20-Poly1305 | `tests/chacha20poly1305.rs` | `tests/aead_wycheproof.rs` covers Wycheproof 96-bit nonce open failure; unit/integration tests cover wrong nonce/tag/AAD | Non-96-bit Wycheproof nonce cases are unsupported by API and skipped | +| 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.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 | +| RSA signatures | `tests/rsa_wycheproof.rs`, `tests/rsa_nist_cavp.rs`, `tests/rsa_public_key.rs` | Wycheproof invalid PKCS#1 v1.5/PSS signatures; `tests/rsa_profile_confusion.rs` rejects PKCS#1/PSS and protocol-scheme confusion | RSA-PSS parameter Wycheproof suites are partly not mapped because the public profile supports SHA-2 fixed profiles | +| RSA OAEP / RSAES-PKCS1-v1_5 | `tests/rsa_wycheproof.rs`, `tests/rsa_public_key.rs` | Wycheproof invalid ciphertexts; scratch decrypt failure clears plaintext; unsupported MGF1-SHA1 vectors reject | Current SHA-2 OAEP suites map directly | +| RSA key parsing / X.509 / TLS / COSE adapters | `tests/rsa_public_key.rs`, `tests/rsa_allocations.rs`, `tests/rsa_leakage.rs` | DER non-canonical forms, unsupported algorithms, policy boundaries, profile confusion, and leakage gate | Keep these tests explicit because the attack surface is protocol/profile confusion, not only raw RSA math | +| Hex/serde public formats | `src/hex.rs`, `tests/serde_roundtrip.rs` | Invalid hex length/character tests; serde byte roundtrips | Negative coverage is format-boundary only | +| Dispatch/fallback surface | `tests/aead_kernel_equivalence.rs`, `tests/aead_foundations.rs`, `tests/portable_fallback.rs`, `tests/vectored_dispatch.rs` | Backend equivalence and fallback dispatch checks | Not a primitive vector suite, but required for SIMD correctness | diff --git a/docs/types.md b/docs/types.md index 5480ba5e..3ec01fc4 100644 --- a/docs/types.md +++ b/docs/types.md @@ -10,16 +10,16 @@ impl types. ## Traits -| Trait | Purpose | -|-------|---------| -| `Checksum` | Stateful + one-shot checksums | -| `ChecksumCombine` | O(log n) parallel CRC combine | -| `Digest` | Fixed-output cryptographic hash | -| `Xof` | Variable-output extendable function | -| `Mac` | Keyed streaming MAC | -| `Kem` | Key encapsulation mechanism profile | -| `FastHash` | One-shot seeded non-crypto hash | -| `Aead` | Authenticated encryption | +| Trait | Purpose | +| ----------------- | ----------------------------------- | +| `Checksum` | Stateful + one-shot checksums | +| `ChecksumCombine` | O(log n) parallel CRC combine | +| `Digest` | Fixed-output cryptographic hash | +| `Xof` | Variable-output extendable function | +| `Mac` | Keyed streaming MAC | +| `Kem` | Key encapsulation mechanism profile | +| `FastHash` | One-shot seeded non-crypto hash | +| `Aead` | Authenticated encryption | Prelude: `rscrypto::prelude` re-exports `Aead`, `Checksum`, `ChecksumCombine`, `Digest`, `FastHash`, `Kem`, `Mac`, @@ -40,12 +40,12 @@ Prelude: `rscrypto::prelude` re-exports `Aead`, `Checksum`, Features: `checksums` or `crc16` / `crc24` / `crc32` / `crc64`. -| Type | Output | Standard | -|------|--------|----------| -| `Crc16Ccitt` / `Crc16Ibm` | `u16` | X.25/HDLC, ARC/IBM | -| `Crc24OpenPgp` | `u32` | RFC 4880 | -| `Crc32` / `Crc32C` | `u32` | Ethernet/gzip, iSCSI/ext4 | -| `Crc64` / `Crc64Nvme` | `u64` | XZ Utils, NVMe | +| Type | Output | Standard | +| ------------------------- | ------ | ------------------------- | +| `Crc16Ccitt` / `Crc16Ibm` | `u16` | X.25/HDLC, ARC/IBM | +| `Crc24OpenPgp` | `u32` | RFC 4880 | +| `Crc32` / `Crc32C` | `u32` | Ethernet/gzip, iSCSI/ext4 | +| `Crc64` / `Crc64Nvme` | `u64` | XZ Utils, NVMe | Aliases: `checksum::Crc32Ieee`, `checksum::Crc32Castagnoli`, and `checksum::Crc64Xz`. @@ -60,16 +60,16 @@ BufferedCrc64Nvme}`, and `checksum::io::{ChecksumReader, ChecksumWriter}`. Features: `crypto-hashes` or `sha2` / `sha3` / `blake2b` / `blake2s` / `blake3` / `ascon-hash`. -| Type | Output | Standard | -|------|--------|----------| -| `Sha224` / `Sha256` / `Sha384` / `Sha512` / `Sha512_256` | 28-64B | FIPS 180-4 | -| `Sha3_224` / `Sha3_256` / `Sha3_384` / `Sha3_512` | 28-64B | FIPS 202 | -| `Shake128` / `Shake256` | XOF | FIPS 202 | -| `Cshake128` / `Cshake256` | XOF | SP 800-185 | -| `Blake2b`, `Blake2b256`, `Blake2b512`, `Blake2bParams` | 1-64B / 32B / 64B | RFC 7693 | -| `Blake2s128`, `Blake2s256`, `Blake2sParams` | 16B / 32B | RFC 7693 | -| `Blake3`, `Blake3KeyedHash` | 32B / XOF | BLAKE3 spec | -| `AsconHash256` / `AsconXof` / `AsconCxof128` | 32B / XOF | NIST SP 800-232 | +| Type | Output | Standard | +| -------------------------------------------------------- | ----------------- | --------------- | +| `Sha224` / `Sha256` / `Sha384` / `Sha512` / `Sha512_256` | 28-64B | FIPS 180-4 | +| `Sha3_224` / `Sha3_256` / `Sha3_384` / `Sha3_512` | 28-64B | FIPS 202 | +| `Shake128` / `Shake256` | XOF | FIPS 202 | +| `Cshake128` / `Cshake256` | XOF | SP 800-185 | +| `Blake2b`, `Blake2b256`, `Blake2b512`, `Blake2bParams` | 1-64B / 32B / 64B | RFC 7693 | +| `Blake2s128`, `Blake2s256`, `Blake2sParams` | 16B / 32B | RFC 7693 | +| `Blake3`, `Blake3KeyedHash` | 32B / XOF | BLAKE3 spec | +| `AsconHash256` / `AsconXof` / `AsconCxof128` | 32B / XOF | NIST SP 800-232 | XOF readers: `Shake128XofReader`, `Shake256XofReader`, `Cshake128XofReader`, `Cshake256XofReader`, `Blake3XofReader`, `AsconXofReader`, and @@ -84,13 +84,13 @@ borrowing key bytes without allocation or copying. Features: `fast-hashes` or `xxh3` / `rapidhash`. -| Type | Output | -|------|--------| -| `Xxh3` / `Xxh3_128` | `u64` / `u128` | -| `RapidHash64` | `u64` | -| `Xxh3Hasher` / `Xxh3_128Hasher` | streaming `u64` / `u128` | -| `RapidStreamHasher` | streaming `u64` | -| `RapidHasher` | collection-key `u64` | +| Type | Output | +| --------------------------------------- | ------------------------------------------- | +| `Xxh3` / `Xxh3_128` | `u64` / `u128` | +| `RapidHash64` | `u64` | +| `Xxh3Hasher` / `Xxh3_128Hasher` | streaming `u64` / `u128` | +| `RapidStreamHasher` | streaming `u64` | +| `RapidHasher` | collection-key `u64` | | `RapidSeededState` / `RapidRandomState` | deterministic / randomized collection state | Alias: `hashes::fast::Xxh3_64`. @@ -104,28 +104,28 @@ pure `no_std` and adds `try_new()` when `getrandom` is enabled. Features: `macs` / `kdfs` or `hmac` / `hmac-sha3` / `hkdf` / `pbkdf2` / `kmac` / `poly1305`. -| Type | Tag/Output | Standard | -|------|------------|----------| -| `HmacSha256` / `HmacSha384` / `HmacSha512`; `HmacSha256Tag` / `HmacSha384Tag` / `HmacSha512Tag` | 32-64B | RFC 2104 | -| `HmacSha3_224` / `HmacSha3_256` / `HmacSha3_384` / `HmacSha3_512`; matching `HmacSha3_*Tag` types | 28-64B | RFC 2104 over FIPS 202 SHA-3 | -| `Kmac128` / `Kmac256` | variable; authentication verification requires 16 / 32 bytes | SP 800-185 | -| `Poly1305`, `Poly1305OneTimeKey`, `Poly1305Tag` | 16B tag | RFC 8439 | -| `HkdfSha256` / `HkdfSha384` / `HkdfSha512` | 32-64B PRK | RFC 5869 | -| `Pbkdf2Sha256` / `Pbkdf2Sha512` | variable | RFC 2898 / SP 800-132 | +| Type | Tag/Output | Standard | +| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------- | +| `HmacSha256` / `HmacSha384` / `HmacSha512`; `HmacSha256Tag` / `HmacSha384Tag` / `HmacSha512Tag` | 32-64B | RFC 2104 | +| `HmacSha3_224` / `HmacSha3_256` / `HmacSha3_384` / `HmacSha3_512`; matching `HmacSha3_*Tag` types | 28-64B | RFC 2104 over FIPS 202 SHA-3 | +| `Kmac128` / `Kmac256` | variable; authentication verification requires 16 / 32 bytes | SP 800-185 | +| `Poly1305`, `Poly1305OneTimeKey`, `Poly1305Tag` | 16B tag | RFC 8439 | +| `HkdfSha256` / `HkdfSha384` / `HkdfSha512` | 32-64B PRK | RFC 5869 | +| `Pbkdf2Sha256` / `Pbkdf2Sha512` | variable | RFC 2898 / SP 800-132 | ## Password hashing Features: `password-hashing` or `argon2` / `scrypt` / `phc-strings`. -| Type | Output | Standard | -|------|--------|----------| -| `Argon2d` / `Argon2i` / `Argon2id` | variable | RFC 9106 | -| `Argon2Params`, `Argon2Context` | -- | RFC 9106 raw-KDF configuration | -| `Argon2idPassword`, `Argon2VerificationLimits` | 32B verifier | Bounded canonical Argon2id PHC records | -| `Scrypt`, `ScryptParams` | variable | RFC 7914 raw KDF | -| `ScryptPassword`, `ScryptVerificationLimits` | 32B verifier | Bounded canonical scrypt PHC records | -| `PasswordHashError` | -- | Caller entropy / password-hash failure boundary | -| `PasswordStatus` | -- | Current-profile / rehash decision | +| Type | Output | Standard | +| ---------------------------------------------- | ------------ | ----------------------------------------------- | +| `Argon2d` / `Argon2i` / `Argon2id` | variable | RFC 9106 | +| `Argon2Params`, `Argon2Context` | -- | RFC 9106 raw-KDF configuration | +| `Argon2idPassword`, `Argon2VerificationLimits` | 32B verifier | Bounded canonical Argon2id PHC records | +| `Scrypt`, `ScryptParams` | variable | RFC 7914 raw KDF | +| `ScryptPassword`, `ScryptVerificationLimits` | 32B verifier | Bounded canonical scrypt PHC records | +| `PasswordHashError` | -- | Caller entropy / password-hash failure boundary | +| `PasswordStatus` | -- | Current-profile / rehash decision | Password-record operations require `phc-strings`. `hash_password_with` accepts a caller-owned entropy source without another feature; OS-salted @@ -137,26 +137,26 @@ algorithm-specific verification limits. Features: `signatures` / `key-exchange` or `ecdsa` / `ed25519` / `rsa` / `x25519` / `ml-kem`. -| Type | Size | Standard | -|------|------|----------| -| `EcdsaP256SecretKey` / `EcdsaP256PublicKey` / `EcdsaP256Signature` | secret 32B / SEC1 65B / raw 64B | FIPS 186-5 / SEC 1 | -| `EcdsaP384SecretKey` / `EcdsaP384PublicKey` / `EcdsaP384Signature` | secret 48B / SEC1 97B / raw 96B | FIPS 186-5 / SEC 1 | -| `EcdsaP256Keypair` / `EcdsaP384Keypair` | secret + public | FIPS 186-5 / SEC 1 | -| `Ed25519SecretKey` / `Ed25519PublicKey` / `Ed25519Signature` | 32/32/64B | RFC 8032 | -| `Ed25519Keypair` | -- | RFC 8032 | -| `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 | -| `RsaPublicExponent`, `RsaPublicExponentPolicy`, `RsaTlsSignatureSchemes`, `RsaX509PublicKeyAlgorithm` | -- | RSA policy / protocol mapping | -| `X25519SecretKey` / `X25519PublicKey` / `X25519SharedSecret` | 32B each | RFC 7748 | -| `MlKem512` / `MlKem768` / `MlKem1024` | profile types | FIPS 203 | -| `MlKem512EncapsulationKey` / `MlKem512DecapsulationKey` / `MlKem512Ciphertext` / `MlKem512SharedSecret` | 800B / 1632B / 768B / 32B | FIPS 203 ML-KEM-512 | -| `MlKem768EncapsulationKey` / `MlKem768DecapsulationKey` / `MlKem768Ciphertext` / `MlKem768SharedSecret` | 1184B / 2400B / 1088B / 32B | FIPS 203 ML-KEM-768 | -| `MlKem1024EncapsulationKey` / `MlKem1024DecapsulationKey` / `MlKem1024Ciphertext` / `MlKem1024SharedSecret` | 1568B / 3168B / 1568B / 32B | FIPS 203 ML-KEM-1024 | -| `MlKem512PreparedEncapsulationKey` / `MlKem512PreparedDecapsulationKey` | 800B / 1632B | Validated reusable ML-KEM-512 state | -| `MlKem768PreparedEncapsulationKey` / `MlKem768PreparedDecapsulationKey` | 1184B / 2400B | Validated reusable ML-KEM-768 state | -| `MlKem1024PreparedEncapsulationKey` / `MlKem1024PreparedDecapsulationKey` | 1568B / 3168B | Validated reusable ML-KEM-1024 state | +| Type | Size | Standard | +| ------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- | ------------------------------------------------------------- | +| `EcdsaP256SecretKey` / `EcdsaP256PublicKey` / `EcdsaP256Signature` | secret 32B / SEC1 65B / raw 64B | FIPS 186-5 / SEC 1 | +| `EcdsaP384SecretKey` / `EcdsaP384PublicKey` / `EcdsaP384Signature` | secret 48B / SEC1 97B / raw 96B | FIPS 186-5 / SEC 1 | +| `EcdsaP256Keypair` / `EcdsaP384Keypair` | secret + public | FIPS 186-5 / SEC 1 | +| `Ed25519SecretKey` / `Ed25519PublicKey` / `Ed25519Signature` | 32/32/64B | RFC 8032 | +| `Ed25519Keypair` | -- | RFC 8032 | +| `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 | +| `RsaPublicExponent`, `RsaPublicExponentPolicy`, `RsaTlsSignatureSchemes`, `RsaX509PublicKeyAlgorithm` | -- | RSA policy / protocol mapping | +| `X25519SecretKey` / `X25519PublicKey` / `X25519SharedSecret` | 32B each | RFC 7748 | +| `MlKem512` / `MlKem768` / `MlKem1024` | profile types | FIPS 203 | +| `MlKem512EncapsulationKey` / `MlKem512DecapsulationKey` / `MlKem512Ciphertext` / `MlKem512SharedSecret` | 800B / 1632B / 768B / 32B | FIPS 203 ML-KEM-512 | +| `MlKem768EncapsulationKey` / `MlKem768DecapsulationKey` / `MlKem768Ciphertext` / `MlKem768SharedSecret` | 1184B / 2400B / 1088B / 32B | FIPS 203 ML-KEM-768 | +| `MlKem1024EncapsulationKey` / `MlKem1024DecapsulationKey` / `MlKem1024Ciphertext` / `MlKem1024SharedSecret` | 1568B / 3168B / 1568B / 32B | FIPS 203 ML-KEM-1024 | +| `MlKem512PreparedEncapsulationKey` / `MlKem512PreparedDecapsulationKey` | 800B / 1632B | Validated reusable ML-KEM-512 state | +| `MlKem768PreparedEncapsulationKey` / `MlKem768PreparedDecapsulationKey` | 1184B / 2400B | Validated reusable ML-KEM-768 state | +| `MlKem1024PreparedEncapsulationKey` / `MlKem1024PreparedDecapsulationKey` | 1568B / 3168B | Validated reusable ML-KEM-1024 state | ECDSA supports P-256/SHA-256 and P-384/SHA-384 signing and verification, raw `r || s` and DER signature import, SEC1/SPKI public keys, deterministic signing, @@ -184,16 +184,16 @@ constants. Feature: `aead` or individual leaves. -| Cipher | Key | Nonce | Tag | Standard | -|--------|-----|-------|-----|----------| -| `Aes128Gcm` | `Aes128GcmKey` 16B | `Nonce96` 12B | `Aes128GcmTag` 16B | SP 800-38D | -| `Aes256Gcm` | `Aes256GcmKey` 32B | `Nonce96` 12B | `Aes256GcmTag` 16B | SP 800-38D | -| `Aes128GcmSiv` | `Aes128GcmSivKey` 16B | `Nonce96` 12B | `Aes128GcmSivTag` 16B | RFC 8452 | -| `Aes256GcmSiv` | `Aes256GcmSivKey` 32B | `Nonce96` 12B | `Aes256GcmSivTag` 16B | RFC 8452 | -| `ChaCha20Poly1305` | `ChaCha20Poly1305Key` 32B | `Nonce96` 12B | `ChaCha20Poly1305Tag` 16B | RFC 8439 | -| `XChaCha20Poly1305` | `XChaCha20Poly1305Key` 32B | `Nonce192` 24B | `XChaCha20Poly1305Tag` 16B | draft-irtf-cfrg-xchacha | -| `AsconAead128` | `AsconAead128Key` 16B | `Nonce128` 16B | `AsconAead128Tag` 16B | NIST SP 800-232 | -| `Aegis256` | `Aegis256Key` 32B | `Nonce256` 32B | `Aegis256Tag` 16B | draft-irtf-cfrg-aegis-aead | +| Cipher | Key | Nonce | Tag | Standard | +| ------------------- | -------------------------- | -------------- | -------------------------- | -------------------------- | +| `Aes128Gcm` | `Aes128GcmKey` 16B | `Nonce96` 12B | `Aes128GcmTag` 16B | SP 800-38D | +| `Aes256Gcm` | `Aes256GcmKey` 32B | `Nonce96` 12B | `Aes256GcmTag` 16B | SP 800-38D | +| `Aes128GcmSiv` | `Aes128GcmSivKey` 16B | `Nonce96` 12B | `Aes128GcmSivTag` 16B | RFC 8452 | +| `Aes256GcmSiv` | `Aes256GcmSivKey` 32B | `Nonce96` 12B | `Aes256GcmSivTag` 16B | RFC 8452 | +| `ChaCha20Poly1305` | `ChaCha20Poly1305Key` 32B | `Nonce96` 12B | `ChaCha20Poly1305Tag` 16B | RFC 8439 | +| `XChaCha20Poly1305` | `XChaCha20Poly1305Key` 32B | `Nonce192` 24B | `XChaCha20Poly1305Tag` 16B | draft-irtf-cfrg-xchacha | +| `AsconAead128` | `AsconAead128Key` 16B | `Nonce128` 16B | `AsconAead128Tag` 16B | NIST SP 800-232 | +| `Aegis256` | `Aegis256Key` 32B | `Nonce256` 32B | `Aegis256Tag` 16B | draft-irtf-cfrg-aegis-aead | Nonce types: `Nonce96` (12B), `Nonce128` (16B), `Nonce192` (24B), `Nonce256` (32B). @@ -208,52 +208,52 @@ detached forms remain allocation-free. With `alloc`, decryption has ## Error types -| Error | When | Recovery | -|-------|------|----------| -| `VerificationError` | MAC/AEAD/signature/password verification fails | Reject input without revealing failure detail | -| `PasswordHashError` | Caller entropy or password-record hashing fails | Match the variant; repair the entropy source or handle the algorithm error | -| `EcdsaKeyGenerationError` | ECDSA random source failure or bounded scalar rejection exhaustion | Fix entropy source; investigate deterministic fillers | -| `AeadBufferError` | Output buffer wrong size | Fix buffer length | -| `SealError` | Combined AEAD buffer length is wrong or input exceeds the algorithm limit | Correct the public buffer/input length | -| `OpenError` | Combined AEAD buffer length is wrong, input is too large, or authentication fails | Correct public lengths; reject opaque verification failures | -| `NonceCounterSealError` | AES-GCM nonce counter is exhausted or sealing fails | Rotate the key before counter reuse, or correct the sealing input | -| `HkdfOutputLengthError` | HKDF expand exceeds max | Request less output | -| `Pbkdf2Error` | PBKDF2 parameter validation fails | Adjust the iteration count, salt, policy, or output length | -| `Blake2Error` | Invalid BLAKE2 key or variable output length, or a mismatched streaming output buffer | Correct the public key/output length | -| `Argon2Error` | Argon2 configuration, input, entropy, or resource failure | Fix the profile/input or restore resources | -| `ScryptError` | scrypt configuration, entropy, or resource failure | Fix N/r/p or restore resources | -| `X25519Error` | Low-order DH point | Reject peer key | -| `MlKemError` | ML-KEM random source, key, or ciphertext validation failure | Reject input or fix entropy source | -| `RsaKeyError` | RSA DER or component validation fails | Reject the key or tighten the import policy | -| `RsaPublicOpError` | RSA public operation input shape/range failure | Fix representative length or reject input | -| `RsaPrivateOpError` | RSA private operation, padding, entropy, or fault-check failure | Reject input; do not expose reason to peer | -| `RsaEncryptionError` | RSA public encryption shape, padding, or entropy fails | Fix the input or entropy source | -| `RsaKeyGenerationError` | RSA key-generation policy or entropy fails | Adjust the key size or policy, or fix the entropy source | -| `RsaProtocolAlgorithmError` | Unsupported/confused COSE/TLS/X.509 RSA selector | Reject algorithm mapping | -| `AsconCxofCustomizationError` | Customization > 256 bytes | Shorten string | -| `InvalidHexError` | Hex decode failure | Fix input | -| `platform::expert::OverrideError` | Invalid, unsupported, or late detection override | Configure through `platform::expert::try_set_override` before first detection | +| Error | When | Recovery | +| --------------------------------- | ------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `VerificationError` | MAC/AEAD/signature/password verification fails | Reject input without revealing failure detail | +| `PasswordHashError` | Caller entropy or password-record hashing fails | Match the variant; repair the entropy source or handle the algorithm error | +| `EcdsaKeyGenerationError` | ECDSA random source failure or bounded scalar rejection exhaustion | Fix entropy source; investigate deterministic fillers | +| `AeadBufferError` | Output buffer wrong size | Fix buffer length | +| `SealError` | Combined AEAD buffer length is wrong or input exceeds the algorithm limit | Correct the public buffer/input length | +| `OpenError` | Combined AEAD buffer length is wrong, input is too large, or authentication fails | Correct public lengths; reject opaque verification failures | +| `NonceCounterSealError` | AES-GCM nonce counter is exhausted or sealing fails | Rotate the key before counter reuse, or correct the sealing input | +| `HkdfOutputLengthError` | HKDF expand exceeds max | Request less output | +| `Pbkdf2Error` | PBKDF2 parameter validation fails | Adjust the iteration count, salt, policy, or output length | +| `Blake2Error` | Invalid BLAKE2 key or variable output length, or a mismatched streaming output buffer | Correct the public key/output length | +| `Argon2Error` | Argon2 configuration, input, entropy, or resource failure | Fix the profile/input or restore resources | +| `ScryptError` | scrypt configuration, entropy, or resource failure | Fix N/r/p or restore resources | +| `X25519Error` | Low-order DH point | Reject peer key | +| `MlKemError` | ML-KEM random source, key, or ciphertext validation failure | Reject input or fix entropy source | +| `RsaKeyError` | RSA DER or component validation fails | Reject the key or tighten the import policy | +| `RsaPublicOpError` | RSA public operation input shape/range failure | Fix representative length or reject input | +| `RsaPrivateOpError` | RSA private operation, padding, entropy, or fault-check failure | Reject input; do not expose reason to peer | +| `RsaEncryptionError` | RSA public encryption shape, padding, or entropy fails | Fix the input or entropy source | +| `RsaKeyGenerationError` | RSA key-generation policy or entropy fails | Adjust the key size or policy, or fix the entropy source | +| `RsaProtocolAlgorithmError` | Unsupported/confused COSE/TLS/X.509 RSA selector | Reject algorithm mapping | +| `AsconCxofCustomizationError` | Customization > 256 bytes | Shorten string | +| `InvalidHexError` | Hex decode failure | Fix input | +| `platform::expert::OverrideError` | Invalid, unsupported, or late detection override | Configure through `platform::expert::try_set_override` before first detection | ## Platform and dispatch -| Item | Purpose | -|------|---------| -| `platform::Caps` | 256-bit CPU capability set | -| `platform::Arch` | Detected architecture family | -| `platform::Detected` | Architecture plus capability set | -| `platform::Description` | Zero-allocation display wrapper for detected platform facts | -| `platform::DispatchInfo` | Shared dispatch metadata used by introspection modules | -| `platform::KernelIntrospect` | Trait for algorithms that can report selected kernels by input length | -| `platform::expert::OverrideError` | Expert detection override failure | +| Item | Purpose | +| --------------------------------- | --------------------------------------------------------------------- | +| `platform::Caps` | 256-bit CPU capability set | +| `platform::Arch` | Detected architecture family | +| `platform::Detected` | Architecture plus capability set | +| `platform::Description` | Zero-allocation display wrapper for detected platform facts | +| `platform::DispatchInfo` | Shared dispatch metadata used by introspection modules | +| `platform::KernelIntrospect` | Trait for algorithms that can report selected kernels by input length | +| `platform::expert::OverrideError` | Expert detection override failure | ## Utility -| Item | Purpose | -|------|---------| -| `ct::zeroize` | Volatile source-level overwrite plus compiler fence; see `secret-lifecycle.md` for the evidence boundary | -| `expert::DisplaySecret` | Explicitly dangerous hex display for secret keys | -| `SecretBytes` | Fixed-size secret owner that overwrites its owned bytes on drop | -| `SecretVec` | Variable-length secret owner that overwrites initialized storage on drop; ordinary extraction requires `into_unprotected_vec()` | +| Item | Purpose | +| ----------------------- | ------------------------------------------------------------------------------------------------------------------------------- | +| `ct::zeroize` | Volatile source-level overwrite plus compiler fence; see `secret-lifecycle.md` for the evidence boundary | +| `expert::DisplaySecret` | Explicitly dangerous hex display for secret keys | +| `SecretBytes` | Fixed-size secret owner that overwrites its owned bytes on drop | +| `SecretVec` | Variable-length secret owner that overwrites initialized storage on drop; ordinary extraction requires `into_unprotected_vec()` | Generic secret wrappers deliberately do not implement equality. Fixed-size keys, shared secrets, authentication tags, and keyed outputs compare only diff --git a/examples/README.md b/examples/README.md index 0b47f28a..b8cd4ae0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -95,17 +95,17 @@ surface requires the opt-in `diag` feature. ## Pattern reference -| To do this | See | -|---|---| -| Hash data (one-shot or streaming) | `basic` (digest section) | -| Compute and verify a MAC | `basic` (auth section) | -| Encrypt and decrypt with AEAD | `aead_seal_open`, `basic` (AEAD section) | -| Sign and verify messages | `signatures`, `rsa_pss_verify` | -| Encapsulate and decapsulate a KEM shared secret | `mlkem_encapsulation` | -| Hash a password and verify safely | `password_hashing` | -| Process a large file in parallel | `parallel` | -| Inspect runtime backend selection | `introspect` | -| Stream a digest through `std::io::Read` | `basic` (I/O adapters section) | +| To do this | See | +| ----------------------------------------------- | ---------------------------------------- | +| Hash data (one-shot or streaming) | `basic` (digest section) | +| Compute and verify a MAC | `basic` (auth section) | +| Encrypt and decrypt with AEAD | `aead_seal_open`, `basic` (AEAD section) | +| Sign and verify messages | `signatures`, `rsa_pss_verify` | +| Encapsulate and decapsulate a KEM shared secret | `mlkem_encapsulation` | +| Hash a password and verify safely | `password_hashing` | +| Process a large file in parallel | `parallel` | +| Inspect runtime backend selection | `introspect` | +| Stream a digest through `std::io::Read` | `basic` (I/O adapters section) | ## Beyond examples diff --git a/fuzz-packages/aead-aegis256/Cargo.toml b/fuzz-packages/aead-aegis256/Cargo.toml index 1ed6bb2b..65695c7a 100644 --- a/fuzz-packages/aead-aegis256/Cargo.toml +++ b/fuzz-packages/aead-aegis256/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "aegis256"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } aegis = "0.9" [workspace] diff --git a/fuzz-packages/aead-aes128gcm/Cargo.toml b/fuzz-packages/aead-aes128gcm/Cargo.toml index b71e3e56..d9ffcc7d 100644 --- a/fuzz-packages/aead-aes128gcm/Cargo.toml +++ b/fuzz-packages/aead-aes128gcm/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "aes-gcm"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } aes-gcm = { version = "0.11", default-features = false, features = ["alloc", "aes"] } [workspace] diff --git a/fuzz-packages/aead-aes128gcmsiv/Cargo.toml b/fuzz-packages/aead-aes128gcmsiv/Cargo.toml index e9b6ce9d..ba7e4363 100644 --- a/fuzz-packages/aead-aes128gcmsiv/Cargo.toml +++ b/fuzz-packages/aead-aes128gcmsiv/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "aes-gcm-siv"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } aes-gcm-siv = { version = "0.12", default-features = false, features = ["alloc", "aes"] } [workspace] diff --git a/fuzz-packages/aead-aes256gcm/Cargo.toml b/fuzz-packages/aead-aes256gcm/Cargo.toml index ef0a1c7f..392e3040 100644 --- a/fuzz-packages/aead-aes256gcm/Cargo.toml +++ b/fuzz-packages/aead-aes256gcm/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "aes-gcm"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } aes-gcm = { version = "0.11", default-features = false, features = ["alloc", "aes"] } [workspace] diff --git a/fuzz-packages/aead-aes256gcmsiv/Cargo.toml b/fuzz-packages/aead-aes256gcmsiv/Cargo.toml index aa480bc9..48caa694 100644 --- a/fuzz-packages/aead-aes256gcmsiv/Cargo.toml +++ b/fuzz-packages/aead-aes256gcmsiv/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "aes-gcm-siv"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } aes-gcm-siv = { version = "0.12", default-features = false, features = ["alloc", "aes"] } [workspace] diff --git a/fuzz-packages/aead-ascon128/Cargo.toml b/fuzz-packages/aead-ascon128/Cargo.toml index c21807fb..57718670 100644 --- a/fuzz-packages/aead-ascon128/Cargo.toml +++ b/fuzz-packages/aead-ascon128/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "ascon-aead"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } ascon-aead = { version = "0.6", default-features = false, features = ["alloc"] } [workspace] diff --git a/fuzz-packages/aead-chacha20poly1305/Cargo.toml b/fuzz-packages/aead-chacha20poly1305/Cargo.toml index 12ee9c78..b26b349f 100644 --- a/fuzz-packages/aead-chacha20poly1305/Cargo.toml +++ b/fuzz-packages/aead-chacha20poly1305/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "chacha20poly1305"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } chacha20poly1305 = { version = "0.11", default-features = false, features = ["alloc"] } [workspace] diff --git a/fuzz-packages/aead-nonce-counter/Cargo.toml b/fuzz-packages/aead-nonce-counter/Cargo.toml index 41c8533a..c4a0a3ca 100644 --- a/fuzz-packages/aead-nonce-counter/Cargo.toml +++ b/fuzz-packages/aead-nonce-counter/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "aes-gcm"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } [workspace] members = ["."] diff --git a/fuzz-packages/aead-xchacha20poly1305/Cargo.toml b/fuzz-packages/aead-xchacha20poly1305/Cargo.toml index b1b8bbb7..3fb487b6 100644 --- a/fuzz-packages/aead-xchacha20poly1305/Cargo.toml +++ b/fuzz-packages/aead-xchacha20poly1305/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "xchacha20poly1305"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["aead"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "aead", +] } chacha20poly1305 = { version = "0.11", default-features = false, features = ["alloc"] } [workspace] diff --git a/fuzz-packages/traits-io/Cargo.toml b/fuzz-packages/traits-io/Cargo.toml index 5bff8435..0c394911 100644 --- a/fuzz-packages/traits-io/Cargo.toml +++ b/fuzz-packages/traits-io/Cargo.toml @@ -10,7 +10,9 @@ cargo-fuzz = true [dependencies] libfuzzer-sys = "0.4" rscrypto = { path = "../..", features = ["std", "blake3", "crc32"] } -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = ["traits_io"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "../../fuzz/support", features = [ + "traits_io", +] } [workspace] members = ["."] diff --git a/fuzz/Cargo.toml b/fuzz/Cargo.toml index a60b0fb3..2fd985af 100644 --- a/fuzz/Cargo.toml +++ b/fuzz/Cargo.toml @@ -8,21 +8,30 @@ edition = "2024" cargo-fuzz = true [lints.rust] -unexpected_cfgs = { level = "deny", check-cfg = [ - 'cfg(fuzzing)', - 'cfg(rscrypto_internal_fuzzing)', -] } +unexpected_cfgs = { level = "deny", check-cfg = ['cfg(fuzzing)', 'cfg(rscrypto_internal_fuzzing)'] } [dependencies] libfuzzer-sys = "0.4" -rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "support", features = ["aead", "traits_io"] } +rscrypto_fuzz = { package = "rscrypto-fuzz-support", path = "support", features = [ + "aead", + "traits_io", +] } # Crate under test. The full-surface fuzz package activates every feature # that any individual scoped package under `fuzz-packages/` activates — the # union must stay in sync. When adding a feature to a scoped package, mirror # it here; CI's full-build smoke test surfaces drift as a build error rather # than as silent coverage loss. -rscrypto = { path = "..", features = ["std", "checksums", "hashes", "auth", "aead", "argon2", "scrypt", "phc-strings"] } +rscrypto = { path = "..", features = [ + "std", + "checksums", + "hashes", + "auth", + "aead", + "argon2", + "scrypt", + "phc-strings", +] } # ── Oracle crates for differential testing ──────────────────────────────────── # AEAD diff --git a/justfile b/justfile index a2dc6e0d..bd2c7de6 100644 --- a/justfile +++ b/justfile @@ -139,6 +139,26 @@ bench *args="": bench-quick *args="": @scripts/bench/bench.sh --quick {{ args }} +# Stable instruction/cache-cost benchmarks. Requires gungraun-runner and Valgrind. +bench-structural: + @command -v gungraun-runner >/dev/null || { echo "error: gungraun-runner is required" >&2; exit 1; } + @command -v valgrind >/dev/null || { echo "error: Valgrind is required" >&2; exit 1; } + cargo bench --locked --profile bench --features 'checksums,sha2,blake3' --bench structural + +# Record one Criterion profiling window with samply. +profile bench filter="" seconds="10": + @scripts/bench/profile.sh "{{ bench }}" "{{ filter }}" "{{ seconds }}" + +# Inspect optimized MIR, LLVM IR, assembly, WASM, or llvm-mca output. +perf-codegen *args="": + @command -v cargo-asm >/dev/null || { echo "error: cargo-show-asm is required" >&2; exit 1; } + cargo asm --locked --lib --features full {{ args }} + +# Attribute generic instantiation and LLVM IR volume. +perf-llvm-lines *args="": + @command -v cargo-llvm-lines >/dev/null || { echo "error: cargo-llvm-lines is required" >&2; exit 1; } + cargo llvm-lines --locked --release --lib --features full {{ args }} + # Maintenance # Release adapters not yet expressible as typed Cargo Rail release policy. diff --git a/scripts/README.md b/scripts/README.md index bc6beb14..9b4d8a92 100644 --- a/scripts/README.md +++ b/scripts/README.md @@ -6,61 +6,62 @@ the recipes reported by `just --list`. ## Entry Points (called from `justfile` or CI) -| Script | Callers | -|--------|---------| -| `check/check.sh` | `just check` | -| `check/check-all.sh` | `just check-all` | -| `check/check-feature-matrix.sh`| `just check-feature-matrix`, `scripts/check/check.sh`, `ci/run-rust-job.sh` | -| `check/asm-ledger.sh` | `scripts/check/check.sh` | -| `check/rsa-asm-provenance.sh` | `check/asm-ledger.sh`; direct `--archive PATH` reconstructs the three pinned RSA snapshots offline | -| `check/signature-asm-provenance.py` | `check/asm-ledger.sh`; direct `--upstream-repo PATH [--clang PATH]` reproduces the 36 pinned ECDSA, Ed25519, and X25519 snapshots | -| `check/hash-vector-provenance.py` | `scripts/check/check.sh`; optional exact upstream checkouts reproduce hash-vector corpora | -| `check/auth-vector-provenance.py` | `scripts/check/check.sh`; optional `--upstream-root PATH` reproduces the pinned C2SP/Wycheproof corpus | -| `check/zeroize-evidence.sh` | `just check-zeroize-evidence`, `scripts/check/check-all.sh` | -| `ci/ci-check.sh` | `just ci-check`, `ci/run-rust-job.sh` | -| `ci/native-check.sh` | `ci/run-rust-job.sh` | -| `test/test.sh` | `just test`, `just test-all`, `ci/run-rust-job.sh` | -| `test/test-feature-matrix.sh` | `just test-feature-matrix`, `scripts/check/check.sh`, `ci/run-rust-job.sh` | -| `test/test-miri.sh` | `just test-miri`, `ci/run-rust-job.sh` | -| `test/test-fuzz.sh` | `just test-fuzz`, `ci/run-rust-job.sh` | -| `test/test-fuzz-scheduler-test.sh` | `just check-actions` | -| `test/test-fuzz-asan.sh` | `just test-fuzz-asan`, `ci/run-rust-job.sh` | -| `test/test-rsa-leakage.sh` | `just test-rsa-leakage`, `ci/run-rust-job.sh` | -| `test/test-rsa-macos-asm.sh` | `just test-rsa-macos-asm` on a physical local Apple Silicon Mac | -| `test/test-coverage.sh` | `just test-coverage`, `just test-fuzz-coverage`, `weekly.yaml` | -| `bench/bench.sh` | `just bench`, `just bench-quick` | -| `ci/check-action-pins.sh` | `just check-actions`, `ci/ci-check.sh`, `ci/dependabot-smoke.sh` | -| `ci/check-action-pins-test.sh` | `just check-actions`, `ci/dependabot-smoke.sh` | -| `ci/tool-integrity-test.sh` | `just check-actions` | -| `ci/dependabot-smoke-test.sh` | `just check-actions` | -| `ci/check-ci-ownership.sh` | `just check-actions`, `ci/check-ci-ownership-test.sh` | -| `ci/check-ci-ownership-test.sh`| `just check-actions` | -| `ci/run-rust-job-test.sh` | `just check-actions` | -| `ci/emit-manual-matrix-test.sh`| `just check-actions` | -| `ci/changed-test-planning-test.sh` | `just check-actions` | -| `ci/check-worktree-test.sh` | `just check-actions` | -| `ci/pre-push-test.sh` | `just check-actions` | -| `ci/release-evidence-check.sh` | `just release-tag`, `release.yaml`, `ci/release-evidence-check-test.sh` | -| `ci/release-evidence-check-test.sh` | `just check-actions` | -| `ci/release-ct-recovery-check.sh` | `release.yaml`, `ci/release-ct-recovery-check-test.sh` | -| `ci/release-ct-recovery-check-test.sh` | `just check-actions` | -| `ci/repository-controls-evidence.sh` | `just release-tag`, `release.yaml`, `ci/repository-controls-evidence-test.sh` | -| `ci/repository-controls-evidence-test.sh` | `just check-actions` | -| `ci/package-release-source.sh` | `release.yaml`, `ci/release-identity-test.sh` | -| `ci/package-release-ct-evidence.sh` | `release.yaml` | -| `ci/release-package-guard.sh` | `ci/release-preflight.sh` | -| `ci/release-preflight.sh` | `release.yaml` | -| `ci/write-release-manifest.sh` | `release.yaml`, `ci/release-identity-test.sh` | -| `ci/release-identity-test.sh` | `just check-actions` | -| `ci/publish-immutable-release.sh` | `release.yaml`, `ci/publish-immutable-release-test.sh` | -| `ci/publish-immutable-release-test.sh` | `just check-actions` | -| `ci/release-recipes-test.sh` | `just check-actions` | -| `ci/pre-push.sh` | `just push`, `just push-full` | -| `ct/artifacts.sh` | `just ct`, `just ct-artifacts`, `scripts/ct/full.py` | -| `ct/dudect.sh` | `just ct-dudect`, `scripts/ct/full.py` | -| `ct/dudect_report_test.py` | `scripts/check/check.sh` | -| `ct/python.sh` | CT recipes, `ci/run-rust-job.sh`, and Python-backed CT, check, and release scripts | -| `update/update-all.sh` | `just update`, `just update-check` | +| Script | Callers | +| ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `check/check.sh` | `just check` | +| `check/check-all.sh` | `just check-all` | +| `check/check-feature-matrix.sh` | `just check-feature-matrix`, `scripts/check/check.sh`, `ci/run-rust-job.sh` | +| `check/asm-ledger.sh` | `scripts/check/check.sh` | +| `check/rsa-asm-provenance.sh` | `check/asm-ledger.sh`; direct `--archive PATH` reconstructs the three pinned RSA snapshots offline | +| `check/signature-asm-provenance.py` | `check/asm-ledger.sh`; direct `--upstream-repo PATH [--clang PATH]` reproduces the 36 pinned ECDSA, Ed25519, and X25519 snapshots | +| `check/hash-vector-provenance.py` | `scripts/check/check.sh`; optional exact upstream checkouts reproduce hash-vector corpora | +| `check/auth-vector-provenance.py` | `scripts/check/check.sh`; optional `--upstream-root PATH` reproduces the pinned C2SP/Wycheproof corpus | +| `check/zeroize-evidence.sh` | `just check-zeroize-evidence`, `scripts/check/check-all.sh` | +| `ci/ci-check.sh` | `just ci-check`, `ci/run-rust-job.sh` | +| `ci/native-check.sh` | `ci/run-rust-job.sh` | +| `test/test.sh` | `just test`, `just test-all`, `ci/run-rust-job.sh` | +| `test/test-feature-matrix.sh` | `just test-feature-matrix`, `scripts/check/check.sh`, `ci/run-rust-job.sh` | +| `test/test-miri.sh` | `just test-miri`, `ci/run-rust-job.sh` | +| `test/test-fuzz.sh` | `just test-fuzz`, `ci/run-rust-job.sh` | +| `test/test-fuzz-scheduler-test.sh` | `just check-actions` | +| `test/test-fuzz-asan.sh` | `just test-fuzz-asan`, `ci/run-rust-job.sh` | +| `test/test-rsa-leakage.sh` | `just test-rsa-leakage`, `ci/run-rust-job.sh` | +| `test/test-rsa-macos-asm.sh` | `just test-rsa-macos-asm` on a physical local Apple Silicon Mac | +| `test/test-coverage.sh` | `just test-coverage`, `just test-fuzz-coverage`, `weekly.yaml` | +| `bench/bench.sh` | `just bench`, `just bench-quick` | +| `bench/profile.sh` | `just profile` | +| `ci/check-action-pins.sh` | `just check-actions`, `ci/ci-check.sh`, `ci/dependabot-smoke.sh` | +| `ci/check-action-pins-test.sh` | `just check-actions`, `ci/dependabot-smoke.sh` | +| `ci/tool-integrity-test.sh` | `just check-actions` | +| `ci/dependabot-smoke-test.sh` | `just check-actions` | +| `ci/check-ci-ownership.sh` | `just check-actions`, `ci/check-ci-ownership-test.sh` | +| `ci/check-ci-ownership-test.sh` | `just check-actions` | +| `ci/run-rust-job-test.sh` | `just check-actions` | +| `ci/emit-manual-matrix-test.sh` | `just check-actions` | +| `ci/changed-test-planning-test.sh` | `just check-actions` | +| `ci/check-worktree-test.sh` | `just check-actions` | +| `ci/pre-push-test.sh` | `just check-actions` | +| `ci/release-evidence-check.sh` | `just release-tag`, `release.yaml`, `ci/release-evidence-check-test.sh` | +| `ci/release-evidence-check-test.sh` | `just check-actions` | +| `ci/release-ct-recovery-check.sh` | `release.yaml`, `ci/release-ct-recovery-check-test.sh` | +| `ci/release-ct-recovery-check-test.sh` | `just check-actions` | +| `ci/repository-controls-evidence.sh` | `just release-tag`, `release.yaml`, `ci/repository-controls-evidence-test.sh` | +| `ci/repository-controls-evidence-test.sh` | `just check-actions` | +| `ci/package-release-source.sh` | `release.yaml`, `ci/release-identity-test.sh` | +| `ci/package-release-ct-evidence.sh` | `release.yaml` | +| `ci/release-package-guard.sh` | `ci/release-preflight.sh` | +| `ci/release-preflight.sh` | `release.yaml` | +| `ci/write-release-manifest.sh` | `release.yaml`, `ci/release-identity-test.sh` | +| `ci/release-identity-test.sh` | `just check-actions` | +| `ci/publish-immutable-release.sh` | `release.yaml`, `ci/publish-immutable-release-test.sh` | +| `ci/publish-immutable-release-test.sh` | `just check-actions` | +| `ci/release-recipes-test.sh` | `just check-actions` | +| `ci/pre-push.sh` | `just push`, `just push-full` | +| `ct/artifacts.sh` | `just ct`, `just ct-artifacts`, `scripts/ct/full.py` | +| `ct/dudect.sh` | `just ct-dudect`, `scripts/ct/full.py` | +| `ct/dudect_report_test.py` | `scripts/check/check.sh` | +| `ct/python.sh` | CT recipes, `ci/run-rust-job.sh`, and Python-backed CT, check, and release scripts | +| `update/update-all.sh` | `just update`, `just update-check` | The optimized secret-lifecycle inspection performed by `check/zeroize-evidence.sh` is mapped to its source ownership and host-binary @@ -68,47 +69,49 @@ claim in [`docs/secret-lifecycle.md`](../docs/secret-lifecycle.md). ## Cross-platform Check Helpers -| Script | Callers | -|--------|---------| -| `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` | +| Script | Callers | +| -------------------------------------- | ------------------------------------------------------------ | +| `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 -| Script | Callers | -|--------|---------| -| `ci/run-bench.sh` | `scripts/bench/bench.sh`, `ci/mlkem-aarch64-gate.sh`, `ci/run-rust-job.sh` | -| `bench/blake3-gap-gate.sh` | `scripts/ci/run-bench.sh` | +| Script | Callers | +| --------------------------------- | -------------------------------------------------------------------------- | +| `ci/run-bench.sh` | `scripts/bench/bench.sh`, `ci/mlkem-aarch64-gate.sh`, `ci/run-rust-job.sh` | +| `bench/blake3-gap-gate.sh` | `scripts/ci/run-bench.sh` | +| `bench/benchmark_catalog.py` | `ci/run-bench.sh`, `bench/profile.sh`, `benchmark_catalog_test.py` | +| `bench/benchmark_catalog_test.py` | `scripts/check/check.sh` | ## CI-only (not surfaced via `just`) -| Script | Callers | -|--------|---------| -| `ci/install-tools.sh` | `.github/actions/setup/action.yaml` | -| `ci/install-codecov.sh` | `weekly.yaml` | -| `ci/setup-toolchain.sh` | `.github/actions/setup-toolchain/action.yaml` | -| `ci/run-rust-job.sh` | `.github/workflows/_rust-job.yaml` | -| `ci/dependabot-smoke.sh` | `ci/run-rust-job.sh` | -| `ci/emit-manual-matrix.sh` | `bench.yaml`, `ct.yaml` | -| `ci/mlkem-aarch64-gate.sh` | `ci/run-rust-job.sh` | -| `ci/nostd-wasm-suite.sh` | `ci/cross-targets.sh` | -| `ci/cross-targets.sh` | `ci/run-rust-job.sh` | +| Script | Callers | +| -------------------------- | --------------------------------------------- | +| `ci/install-tools.sh` | `.github/actions/setup/action.yaml` | +| `ci/install-codecov.sh` | `weekly.yaml` | +| `ci/setup-toolchain.sh` | `.github/actions/setup-toolchain/action.yaml` | +| `ci/run-rust-job.sh` | `.github/workflows/_rust-job.yaml` | +| `ci/dependabot-smoke.sh` | `ci/run-rust-job.sh` | +| `ci/emit-manual-matrix.sh` | `bench.yaml`, `ct.yaml` | +| `ci/mlkem-aarch64-gate.sh` | `ci/run-rust-job.sh` | +| `ci/nostd-wasm-suite.sh` | `ci/cross-targets.sh` | +| `ci/cross-targets.sh` | `ci/run-rust-job.sh` | ## Shared Libraries (sourced, not invoked) -| Script | Sourced by | -|--------|------------| -| `lib/common.sh` | `scripts/check/*.sh`, `scripts/test/*.sh`, `scripts/ci/ci-check.sh` | -| `lib/rail-plan.sh` | `scripts/lib/common.sh` | -| `lib/fuzz-packages.sh` | `scripts/test/test-fuzz.sh`, `scripts/test/test-coverage.sh` | -| `lib/feature-profiles.sh` | `scripts/check/check-feature-matrix.sh`, `scripts/test/test-feature-matrix.sh` | -| `lib/targets.sh` | `scripts/check/check-all.sh`, `scripts/check/check-linux.sh`, `scripts/check/check-ibm.sh` | -| `lib/target-matrix.sh` | `scripts/lib/targets.sh`, `_ci-suite.yaml` (target-matrix job) | -| `lib/toolchain.sh` | `.github/actions/setup-toolchain/action.yaml` | -| `lib/ci-tool-integrity.sh` | `ci/install-codecov.sh`, `ci/nostd-wasm-suite.sh` | +| Script | Sourced by | +| -------------------------- | ------------------------------------------------------------------------------------------ | +| `lib/common.sh` | `scripts/check/*.sh`, `scripts/test/*.sh`, `scripts/ci/ci-check.sh` | +| `lib/rail-plan.sh` | `scripts/lib/common.sh` | +| `lib/fuzz-packages.sh` | `scripts/test/test-fuzz.sh`, `scripts/test/test-coverage.sh` | +| `lib/feature-profiles.sh` | `scripts/check/check-feature-matrix.sh`, `scripts/test/test-feature-matrix.sh` | +| `lib/targets.sh` | `scripts/check/check-all.sh`, `scripts/check/check-linux.sh`, `scripts/check/check-ibm.sh` | +| `lib/target-matrix.sh` | `scripts/lib/targets.sh`, `_ci-suite.yaml` (target-matrix job) | +| `lib/toolchain.sh` | `.github/actions/setup-toolchain/action.yaml` | +| `lib/ci-tool-integrity.sh` | `ci/install-codecov.sh`, `ci/nostd-wasm-suite.sh` | ## CI tool integrity diff --git a/scripts/bench/benchmark_catalog.py b/scripts/bench/benchmark_catalog.py new file mode 100755 index 00000000..a14fd304 --- /dev/null +++ b/scripts/bench/benchmark_catalog.py @@ -0,0 +1,243 @@ +#!/usr/bin/env python3 +"""Query and validate rscrypto's benchmark identity catalog.""" + +from __future__ import annotations + +import argparse +import json +import re +import sys +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +CATALOG_PATH = ROOT / ".config" / "benchmark-matrix.json" + + +class CatalogError(ValueError): + pass + + +def normalize(value: str) -> str: + return re.sub(r"[^a-z0-9]", "", value.lower()) + + +def csv(values: list[str]) -> str: + return ",".join(values) + + +def csv_values(value: str) -> list[str]: + return [part.strip() for part in value.split(",") if part.strip()] + + +def load_catalog() -> dict: + with CATALOG_PATH.open(encoding="utf-8") as source: + catalog = json.load(source) + validate_catalog(catalog) + return catalog + + +def validate_catalog(catalog: dict) -> None: + if catalog.get("schema") != 1: + raise CatalogError("benchmark catalog schema must be 1") + + benches = catalog.get("benches") + algorithms = catalog.get("algorithms") + selectors = catalog.get("selectors") + crates = catalog.get("crates") + aliases = catalog.get("bench_aliases") + if not all(isinstance(value, dict) and value for value in (benches, algorithms, selectors, crates, aliases)): + raise CatalogError("benchmark catalog maps must be non-empty objects") + + normalized_algorithms: dict[str, str] = {} + for name, algorithm in algorithms.items(): + key = normalize(name) + if key in normalized_algorithms: + raise CatalogError(f"algorithm names normalize to the same selector: {name}, {normalized_algorithms[key]}") + normalized_algorithms[key] = name + bench = algorithm.get("bench") + if bench not in benches: + raise CatalogError(f"algorithm {name} references unknown bench {bench}") + if algorithm.get("crate") not in crates: + raise CatalogError(f"algorithm {name} references unknown crate {algorithm.get('crate')}") + if not isinstance(algorithm.get("filter"), str) or not algorithm["filter"]: + raise CatalogError(f"algorithm {name} needs a non-empty filter") + + valid_kinds = {"criterion", "gungraun"} + for name, bench in benches.items(): + if bench.get("kind") not in valid_kinds: + raise CatalogError(f"bench {name} has invalid kind {bench.get('kind')}") + binary = bench.get("binary") + if not isinstance(binary, str) or not binary: + raise CatalogError(f"bench {name} needs a binary") + if not (ROOT / "benches" / f"{binary}.rs").is_file(): + raise CatalogError(f"bench {name} references missing benches/{binary}.rs") + features = bench.get("features") + if not isinstance(features, list) or not features or any(not isinstance(item, str) or not item for item in features): + raise CatalogError(f"bench {name} needs a non-empty feature list") + if not isinstance(bench.get("required"), bool): + raise CatalogError(f"bench {name} needs a Boolean required field") + + for crate, defaults in crates.items(): + if not isinstance(defaults, list) or any(name not in benches for name in defaults): + raise CatalogError(f"crate {crate} references an unknown default bench") + + for selector, names in selectors.items(): + if normalize(selector) != selector: + raise CatalogError(f"selector key must already be normalized: {selector}") + if not isinstance(names, list) or not names or any(name not in algorithms for name in names): + raise CatalogError(f"selector {selector} references an unknown algorithm") + + all_algorithms = set(selectors.get("all", [])) + expected_algorithms = set(algorithms) - {"aead-diag"} + if all_algorithms != expected_algorithms: + raise CatalogError("the all selector must contain every non-diagnostic algorithm exactly once") + + for alias, targets in aliases.items(): + if not isinstance(targets, list) or not targets or any(target not in benches for target in targets): + raise CatalogError(f"bench alias {alias} references an unknown bench") + + +def resolve_selector(catalog: dict, selector: str) -> list[str] | None: + key = normalize(selector) + selected = catalog["selectors"].get(key) + if selected is not None: + return selected + for name in catalog["algorithms"]: + if normalize(name) == key: + return [name] + return None + + +def merged_features(catalog: dict, benches: list[str]) -> list[str]: + if not benches: + return catalog["default_features"] + merged: list[str] = [] + for name in benches: + bench = catalog["benches"].get(name) + if bench is None: + raise CatalogError(f"unknown bench: {name}") + for feature in bench["features"]: + if feature not in merged: + merged.append(feature) + return merged + + +def main() -> int: + parser = argparse.ArgumentParser(description=__doc__) + subparsers = parser.add_subparsers(dest="command", required=True) + subparsers.add_parser("validate") + + resolve = subparsers.add_parser("resolve-selector") + resolve.add_argument("selector") + + plan = subparsers.add_parser("plan-algorithm") + plan.add_argument("algorithm") + plan.add_argument("filter", nargs="?", default="") + + plans = subparsers.add_parser("plan-algorithms") + plans.add_argument("algorithms") + plans.add_argument("filter", nargs="?", default="") + + crate = subparsers.add_parser("crate-for-algorithm") + crate.add_argument("algorithm") + + crates = subparsers.add_parser("crates-for-algorithms") + crates.add_argument("algorithms") + + defaults = subparsers.add_parser("default-benches") + defaults.add_argument("crate") + + features = subparsers.add_parser("features") + features.add_argument("benches", nargs="?", default="") + + binary = subparsers.add_parser("binary") + binary.add_argument("bench") + + expand = subparsers.add_parser("expand-benches") + expand.add_argument("benches") + + subparsers.add_parser("required-benches") + subparsers.add_parser("criterion-binaries") + + kind = subparsers.add_parser("require-kind") + kind.add_argument("bench") + kind.add_argument("kind") + + args = parser.parse_args() + + try: + catalog = load_catalog() + if args.command == "validate": + print(f"validated {CATALOG_PATH.relative_to(ROOT)}") + elif args.command == "resolve-selector": + selected = resolve_selector(catalog, args.selector) + if selected is None: + return 3 + print(csv(selected)) + elif args.command == "plan-algorithm": + algorithm = catalog["algorithms"].get(args.algorithm) + if algorithm is None: + raise CatalogError(f"unknown algorithm: {args.algorithm}") + filter_value = args.filter or algorithm["filter"] + print(f"{algorithm['crate']}|{algorithm['bench']}|{filter_value}") + elif args.command == "plan-algorithms": + for name in csv_values(args.algorithms): + algorithm = catalog["algorithms"].get(name) + if algorithm is None: + raise CatalogError(f"unknown algorithm: {name}") + filter_value = args.filter or algorithm["filter"] + print(f"{algorithm['crate']}|{algorithm['bench']}|{filter_value}") + elif args.command == "crate-for-algorithm": + algorithm = catalog["algorithms"].get(args.algorithm) + if algorithm is None: + raise CatalogError(f"unknown algorithm: {args.algorithm}") + print(algorithm["crate"]) + elif args.command == "crates-for-algorithms": + selected: list[str] = [] + for name in csv_values(args.algorithms): + algorithm = catalog["algorithms"].get(name) + if algorithm is None: + raise CatalogError(f"unknown algorithm: {name}") + if algorithm["crate"] not in selected: + selected.append(algorithm["crate"]) + print(csv(selected)) + elif args.command == "default-benches": + print(csv(catalog["crates"].get(args.crate, []))) + elif args.command == "features": + print(csv(merged_features(catalog, csv_values(args.benches)))) + elif args.command == "binary": + bench = catalog["benches"].get(args.bench) + if bench is None: + raise CatalogError(f"unknown bench: {args.bench}") + print(bench["binary"]) + elif args.command == "expand-benches": + expanded: list[str] = [] + for token in csv_values(args.benches): + for target in catalog["bench_aliases"].get(token, [token]): + if target not in expanded: + expanded.append(target) + print(csv(expanded)) + elif args.command == "required-benches": + print(csv([name for name, bench in catalog["benches"].items() if bench["required"]])) + elif args.command == "criterion-binaries": + binaries: list[str] = [] + for bench in catalog["benches"].values(): + if bench["kind"] != "criterion": + continue + if bench["binary"] not in binaries: + binaries.append(bench["binary"]) + print(csv(binaries)) + elif args.command == "require-kind": + bench = catalog["benches"].get(args.bench) + if bench is None or bench["kind"] != args.kind: + actual = "missing" if bench is None else bench["kind"] + raise CatalogError(f"bench {args.bench} has kind {actual}, expected {args.kind}") + return 0 + except (CatalogError, json.JSONDecodeError) as error: + print(f"benchmark catalog error: {error}", file=sys.stderr) + return 2 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/bench/benchmark_catalog_test.py b/scripts/bench/benchmark_catalog_test.py new file mode 100755 index 00000000..c849ea11 --- /dev/null +++ b/scripts/bench/benchmark_catalog_test.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python3 +"""Contract tests for the benchmark catalog and Cargo benchmark targets.""" + +from __future__ import annotations + +import json +import subprocess +import sys +import tomllib +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[2] +CATALOG = ROOT / ".config" / "benchmark-matrix.json" +TOOL = ROOT / "scripts" / "bench" / "benchmark_catalog.py" + + +def fail(message: str) -> None: + raise AssertionError(message) + + +def query(*args: str, check: bool = True) -> subprocess.CompletedProcess[str]: + return subprocess.run( + [sys.executable, str(TOOL), *args], + cwd=ROOT, + check=check, + text=True, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + ) + + +def main() -> None: + query("validate") + with CATALOG.open(encoding="utf-8") as source: + catalog = json.load(source) + with (ROOT / "Cargo.toml").open("rb") as source: + cargo = tomllib.load(source) + + cargo_benches = {bench["name"]: bench for bench in cargo["bench"]} + catalog_binaries = {bench["binary"] for bench in catalog["benches"].values()} + if set(cargo_benches) != catalog_binaries: + fail( + "Cargo and benchmark catalog targets differ: " + f"Cargo-only={sorted(set(cargo_benches) - catalog_binaries)}, " + f"catalog-only={sorted(catalog_binaries - set(cargo_benches))}" + ) + + for name, bench in catalog["benches"].items(): + required = set(cargo_benches[bench["binary"]].get("required-features", [])) + selected = set(bench["features"]) + if not required <= selected: + fail(f"catalog bench {name} omits required features: {sorted(required - selected)}") + + expected = { + "checksum": "crc16-ccitt,crc16-ibm,crc24-openpgp,crc32-ieee,crc32c,crc64-xz,crc64-nvme", + "mlkem": "mlkem512,mlkem768,mlkem1024", + "sha512-256": "sha512-256", + } + for selector, algorithms in expected.items(): + actual = query("resolve-selector", selector).stdout.strip() + if actual != algorithms: + fail(f"selector {selector} resolved to {actual}, expected {algorithms}") + + unknown = query("resolve-selector", "raw-criterion-filter", check=False) + if unknown.returncode != 3 or unknown.stdout: + fail("unknown selectors must remain available as raw Criterion filters") + + if query("plan-algorithm", "sha512").stdout.strip() != "hashes|sha2|^sha512/": + fail("SHA-512 benchmark identity changed") + if query("plan-algorithm", "aead-diag").stdout.strip() != "aead|aead_diag|chacha20-poly1305/encrypt": + fail("AEAD diagnostic benchmark identity changed") + if query("binary", "aead_diag").stdout.strip() != "aead": + fail("AEAD diagnostic selector must use the aead benchmark binary") + + expanded = query("expand-benches", "checksum_comp,auth_comp").stdout.strip() + if expanded != "crc,auth": + fail(f"bench aliases expanded to {expanded}") + + features = query("features", "sha2,aead").stdout.strip().split(",") + if len(features) != len(set(features)) or not {"parallel", "sha2", "aes-gcm"} <= set(features): + fail("bench feature union is incomplete or duplicated") + + required = set(query("required-benches").stdout.strip().split(",")) + expected_required = {name for name, bench in catalog["benches"].items() if bench["required"]} + if required != expected_required: + fail("required benchmark targets are not derived from the catalog") + + criterion = set(query("criterion-binaries").stdout.strip().split(",")) + if "structural" in criterion or "aead" not in criterion: + fail("generic Criterion runs must exclude Gungraun and include the AEAD binary") + + print("benchmark catalog tests passed") + + +if __name__ == "__main__": + main() diff --git a/scripts/bench/profile.sh b/scripts/bench/profile.sh new file mode 100755 index 00000000..a880a9f0 --- /dev/null +++ b/scripts/bench/profile.sh @@ -0,0 +1,60 @@ +#!/usr/bin/env bash +# Build one Criterion benchmark and record its profiling window with samply. + +set -euo pipefail + +BENCH=${1:?usage: profile.sh [filter] [seconds]} +FILTER=${2:-} +PROFILE_SECONDS=${3:-10} +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)" +CATALOG="$SCRIPT_DIR/benchmark_catalog.py" + +[[ "$PROFILE_SECONDS" =~ ^[0-9]+([.][0-9]+)?$ ]] \ + && awk -v seconds="$PROFILE_SECONDS" 'BEGIN { exit !(seconds >= 1) }' || { + echo "error: profile duration must be numeric and at least one second" >&2 + exit 2 +} + +command -v samply >/dev/null 2>&1 || { + echo "error: samply is required; install the repository-pinned profile tool set" >&2 + exit 1 +} +command -v jq >/dev/null 2>&1 || { + echo "error: jq is required to resolve the benchmark executable" >&2 + exit 1 +} + +cd "$REPO_ROOT" +scripts/ct/python.sh "$CATALOG" require-kind "$BENCH" criterion +FEATURES=$(scripts/ct/python.sh "$CATALOG" features "$BENCH") +BINARY=$(scripts/ct/python.sh "$CATALOG" binary "$BENCH") + +ARTIFACT=$( + cargo bench --locked --profile bench --features "$FEATURES" --bench "$BINARY" \ + --no-run --message-format=json \ + | jq -r --arg name "$BINARY" ' + select(.reason == "compiler-artifact") + | select(.target.name == $name) + | select(.target.kind | index("bench")) + | .executable // empty + ' \ + | tail -n 1 +) + +[[ -n "$ARTIFACT" && -x "$ARTIFACT" ]] || { + echo "error: Cargo did not report an executable for benchmark $BINARY" >&2 + exit 1 +} + +COMMAND=("$ARTIFACT") +[[ -n "$FILTER" ]] && COMMAND+=("$FILTER") +COMMAND+=(--profile-time "$PROFILE_SECONDS" --noplot) + +PROFILE_DIR="$REPO_ROOT/target/profiles" +PROFILE_PATH="$PROFILE_DIR/${BENCH}-$(date -u +%Y%m%dT%H%M%SZ).json.gz" +mkdir -p "$PROFILE_DIR" + +echo "Profiling: ${COMMAND[*]}" +samply record --save-only --output "$PROFILE_PATH" "${COMMAND[@]}" +echo "Profile: $PROFILE_PATH" diff --git a/scripts/check/check.sh b/scripts/check/check.sh index 7dd75b74..f4dc5513 100755 --- a/scripts/check/check.sh +++ b/scripts/check/check.sh @@ -88,6 +88,14 @@ if ! "$SCRIPT_DIR/../ct/python.sh" "$SCRIPT_DIR/auth-vector-provenance.py" >"$LO fi ok +step "Checking benchmark catalog" +if ! "$SCRIPT_DIR/../ct/python.sh" "$SCRIPT_DIR/../bench/benchmark_catalog_test.py" >"$LOG_DIR/benchmark-catalog.log" 2>&1; then + fail + show_error "$LOG_DIR/benchmark-catalog.log" + exit 1 +fi +ok + step "Checking CT assembly scanner" if ! "$SCRIPT_DIR/../ct/python.sh" "$SCRIPT_DIR/../ct/asm_heuristics_test.py" >"$LOG_DIR/ct-asm-scanner.log" 2>&1; then fail diff --git a/scripts/ci/check-ci-ownership-test.sh b/scripts/ci/check-ci-ownership-test.sh index 5838cf1d..f1c81a93 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/v47\.0\.3/#/download/Latest/#' \ +sed -i.bak 's#/download/v48\.0\.0/#/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" diff --git a/scripts/ci/check-ci-ownership.sh b/scripts/ci/check-ci-ownership.sh index 5c102499..9cdc87c7 100755 --- a/scripts/ci/check-ci-ownership.sh +++ b/scripts/ci/check-ci-ownership.sh @@ -389,6 +389,8 @@ grep -Fq 'actual=$(dpkg-query -W -f=' "$INSTALL_TOOLS" \ || fail "APT packages must be validated against exact versions" grep -Fq 'ci_tool_download wasmtime' "$NOSTD_WASM" \ || fail "Wasmtime must use the direct archive integrity contract" +grep -Fq 'ci_tool_download wasm-tools' "$NOSTD_WASM" \ + || fail "wasm-tools must use the direct archive integrity contract" if grep -Eiq '(^|[^[:alnum:]_])zig([^[:alnum:]_]|$)' "$CROSS_SCRIPT"; then fail "cross-target CI must not depend on Zig" fi diff --git a/scripts/ci/install-tools.sh b/scripts/ci/install-tools.sh index e354cd52..174074c9 100755 --- a/scripts/ci/install-tools.sh +++ b/scripts/ci/install-tools.sh @@ -1,6 +1,6 @@ #!/usr/bin/env bash # Install CI tools through authenticated package-manager boundaries. -# Usage: install-tools.sh [standard|quality|release|semver|rail|ci|supply-chain|bench|ibm|fuzz|coverage|ct-linux|minimal|none] +# Usage: install-tools.sh [standard|quality|release|semver|rail|ci|supply-chain|bench|structural-bench|profile|ibm|fuzz|coverage|ct-linux|minimal|none] set -euo pipefail @@ -15,8 +15,12 @@ JUST_VERSION=1.58.0 ZIZMOR_VERSION=1.29.0 CARGO_CRITERION_VERSION=1.1.0 CRITCMP_VERSION=0.1.8 +GUNGRAUN_RUNNER_VERSION=0.19.4 +CARGO_SHOW_ASM_VERSION=0.2.62 +SAMPLY_VERSION=0.13.1 +CARGO_LLVM_LINES_VERSION=0.4.48 CARGO_FUZZ_VERSION=0.13.2 -CARGO_LLVM_COV_VERSION=0.8.7 +CARGO_LLVM_COV_VERSION=0.9.0 ACTIONLINT_VERSION=1.7.12 OPAM_REPOSITORY_COMMIT=49f6d620cf20ae0168cfcbeb2c33932e06cb4b74 @@ -331,6 +335,16 @@ case "$MODE" in install_cargo_tool critcmp "$CRITCMP_VERSION" install_cargo_tool just "$JUST_VERSION" ;; + structural-bench) + install_cargo_tool gungraun-runner "$GUNGRAUN_RUNNER_VERSION" + install_cargo_tool just "$JUST_VERSION" + ;; + profile) + install_cargo_tool cargo-show-asm "$CARGO_SHOW_ASM_VERSION" cargo-asm + install_cargo_tool samply "$SAMPLY_VERSION" + install_cargo_tool cargo-llvm-lines "$CARGO_LLVM_LINES_VERSION" + install_cargo_tool just "$JUST_VERSION" + ;; fuzz) install_cargo_tool cargo-fuzz "$CARGO_FUZZ_VERSION" install_cargo_tool just "$JUST_VERSION" @@ -353,7 +367,7 @@ case "$MODE" in ;; *) echo "Unknown mode: $MODE" >&2 - echo "Usage: install-tools.sh [standard|quality|release|rail|ci|supply-chain|bench|ibm|fuzz|coverage|ct-linux|minimal|none]" >&2 + echo "Usage: install-tools.sh [standard|quality|release|semver|rail|ci|supply-chain|bench|structural-bench|profile|ibm|fuzz|coverage|ct-linux|minimal|none]" >&2 exit 2 ;; esac diff --git a/scripts/ci/nostd-wasm-suite.sh b/scripts/ci/nostd-wasm-suite.sh index 070bc890..00fda157 100755 --- a/scripts/ci/nostd-wasm-suite.sh +++ b/scripts/ci/nostd-wasm-suite.sh @@ -48,18 +48,67 @@ install_wasmtime() { export PATH="$install_dir/bin:$PATH" } +install_wasm_tools() { + local platform tmpdir install_dir installed_version + tmpdir="$(mktemp -d)" + install_dir="${WASM_TOOLS_HOME:-$HOME/.wasm-tools}" + + ci_tool_download wasm-tools "$tmpdir" + platform="${CI_TOOL_HOST_ARCH}-${CI_TOOL_HOST_OS}" + echo "Installing wasm-tools $CI_TOOL_VERSION for $platform" + tar -xzf "$CI_TOOL_ARCHIVE_PATH" -C "$tmpdir" + mkdir -p "$install_dir/bin" + cp "$tmpdir/wasm-tools-${CI_TOOL_VERSION}-${platform}/wasm-tools" "$install_dir/bin/wasm-tools" + chmod +x "$install_dir/bin/wasm-tools" + + installed_version=$("$install_dir/bin/wasm-tools" --version) + if [[ "$installed_version" =~ ([0-9]+\.[0-9]+\.[0-9]+) ]]; then + installed_version=${BASH_REMATCH[1]} + else + echo "wasm-tools version mismatch: expected $CI_TOOL_VERSION, got $installed_version" >&2 + return 1 + fi + [[ "$installed_version" == "$CI_TOOL_VERSION" ]] || { + echo "wasm-tools version mismatch: expected $CI_TOOL_VERSION, got $installed_version" >&2 + return 1 + } + + rm -rf "$tmpdir" + export PATH="$install_dir/bin:$PATH" +} + +build_validate_run_wasm_vectors() { + local variant=$1 + local rustflags=$2 + local target_dir="$CI_TOOL_REPO_ROOT/target/wasm-runtime-vectors/$variant" + local artifact="$target_dir/$TARGET/debug/rscrypto-wasm-runtime-vectors.wasm" + local wat="$artifact.wat" + local manifest="tools/wasm-runtime-vectors/Cargo.toml" + + CARGO_TARGET_DIR="$target_dir" RUSTFLAGS="$rustflags" \ + cargo build --locked --manifest-path "$manifest" --target "$TARGET" + + wasm-tools validate "$artifact" + if [[ "$variant" == simd128 ]]; then + wasm-tools print "$artifact" >"$wat" + grep -Eq '\b(v128\.(load|store|const)|i(8x16|16x8|32x4|64x2)\.|f(32x4|64x2)\.)' "$wat" || { + echo "SIMD WASM artifact contains no SIMD instruction" >&2 + return 1 + } + fi + + wasmtime "$artifact" +} + run_wasm_runtime_vectors() { if [[ "$TARGET" != "wasm32-wasip1" ]]; then return fi install_wasmtime - export CARGO_TARGET_WASM32_WASIP1_RUNNER="wasmtime" - - local manifest="tools/wasm-runtime-vectors/Cargo.toml" - cargo run --locked --manifest-path "$manifest" --target "$TARGET" - RUSTFLAGS="-C target-feature=+simd128" \ - cargo run --locked --manifest-path "$manifest" --target "$TARGET" + install_wasm_tools + build_validate_run_wasm_vectors default "" + build_validate_run_wasm_vectors simd128 "-C target-feature=+simd128" } echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" diff --git a/scripts/ci/pre-push.sh b/scripts/ci/pre-push.sh index 6676e8c0..a9a4da5d 100755 --- a/scripts/ci/pre-push.sh +++ b/scripts/ci/pre-push.sh @@ -193,7 +193,7 @@ needs_host_checks() { return 0 fi - changed_file_matches '^scripts/check/|^scripts/lib/(common|rail-plan|feature-profiles)\.sh$|^scripts/test/(test-feature-matrix|test-fuzz)\.sh$' + changed_file_matches '^\.config/benchmark-matrix\.json$|^benches/structural\.rs$|^scripts/bench/(benchmark_catalog(_test)?\.py|profile\.sh)$|^scripts/check/|^scripts/lib/(common|rail-plan|feature-profiles)\.sh$|^scripts/test/(test-feature-matrix|test-fuzz)\.sh$' } run_actions_check() { diff --git a/scripts/ci/run-bench.sh b/scripts/ci/run-bench.sh index cec94cf6..fc66378a 100755 --- a/scripts/ci/run-bench.sh +++ b/scripts/ci/run-bench.sh @@ -108,384 +108,35 @@ ms_to_seconds() { awk -v ms="$ms" 'BEGIN { printf "%.3f", (ms + 0) / 1000.0 }' } -DEFAULT_CHECKSUM_ALGOS=( - "crc16-ccitt" - "crc16-ibm" - "crc24-openpgp" - "crc32-ieee" - "crc32c" - "crc64-xz" - "crc64-nvme" -) - -DEFAULT_HASH_ALGOS=( - "sha224" - "sha256" - "sha384" - "sha512" - "sha512-256" - "sha3-224" - "sha3-256" - "sha3-384" - "sha3-512" - "shake128" - "shake256" - "cshake256" - "blake2" - "blake3" - "xxh3" - "rapidhash" - "ascon-hash256" - "ascon-xof128" - "ascon-cxof128" -) - -DEFAULT_AUTH_ALGOS=( - "hmac-sha256" - "hmac-sha384" - "hmac-sha512" - "hkdf-sha256" - "hkdf-sha384" - "pbkdf2-sha256" - "pbkdf2-sha512" - "ecdsa-p256" - "ecdsa-p384" - "ed25519" - "x25519" - "mlkem512" - "mlkem768" - "mlkem1024" -) - -DEFAULT_SP800185_ALGOS=( - "kmac256" -) - -DEFAULT_PASSWORD_HASHING_ALGOS=( - "argon2d" - "argon2i" - "argon2id" - "scrypt" -) - -DEFAULT_RSA_ALGOS=( - "rsa" -) - -DEFAULT_AEAD_ALGOS=( - "xchacha20-poly1305" - "chacha20-poly1305" - "aes-256-gcm-siv" - "aes-128-gcm-siv" - "aes-256-gcm" - "aes-128-gcm" - "aegis-256" - "ascon-aead128" -) - -DIAG_AEAD_ALGOS=( - "aead-diag" -) - -ALL_KNOWN_ALGOS=( - "${DEFAULT_CHECKSUM_ALGOS[@]}" - "${DEFAULT_HASH_ALGOS[@]}" - "${DEFAULT_AUTH_ALGOS[@]}" - "${DEFAULT_SP800185_ALGOS[@]}" - "${DEFAULT_PASSWORD_HASHING_ALGOS[@]}" - "${DEFAULT_RSA_ALGOS[@]}" - "${DEFAULT_AEAD_ALGOS[@]}" -) - -ALL_REQUIRED_BENCHES=( - "crc" - "sha2" - "sha3" - "kmac_cshake" - "ascon" - "xxh3" - "rapidhash" - "blake2" - "blake3" - "auth" - "password_hashing" - "rsa" - "aead" -) - -checksum_filter_token() { - local algo="${1:-}" - case "$algo" in - crc16-ccitt) echo "crc16-ccitt" ;; - crc16-ibm) echo "crc16-ibm" ;; - crc24-openpgp) echo "crc24-openpgp" ;; - crc32-ieee) echo "^crc32/" ;; - crc32c) echo "crc32c" ;; - crc64-xz) echo "crc64-xz" ;; - crc64-nvme) echo "crc64-nvme" ;; - *) echo "$algo" ;; - esac -} - -hash_filter_token() { - local algo="${1:-}" - case "$algo" in - sha3-224) echo "sha3-224" ;; - sha3-256) echo "sha3-256" ;; - sha3-384) echo "sha3-384" ;; - sha3-512) echo "sha3-512" ;; - sha512) echo "^sha512/" ;; - sha512-256) echo "sha512-256" ;; - cshake256) echo "cshake256" ;; - - ascon-hash256) echo "ascon-hash256" ;; - ascon-xof128) echo "ascon-xof128" ;; - ascon-cxof128) echo "ascon-cxof128" ;; - xxh3) echo "xxh3" ;; - blake2) echo "^blake2/(rscrypto|rustcrypto|keyed|streaming)/" ;; - blake3) echo "blake3" ;; - *) echo "$algo" ;; - esac -} +BENCHMARK_CATALOG="scripts/bench/benchmark_catalog.py" -sp800185_filter_token() { - local algo="${1:-}" - case "$algo" in - kmac256) echo "kmac256" ;; - *) echo "$algo" ;; - esac -} - -password_hashing_filter_token() { - local algo="${1:-}" - case "$algo" in - argon2d) echo "^argon2d-" ;; - argon2i) echo "^argon2i-" ;; - argon2id) echo "^argon2id-" ;; - scrypt) echo "^scrypt-" ;; - *) echo "$algo" ;; - esac -} - -auth_filter_token() { - local algo="${1:-}" - case "$algo" in - hmac-sha256) echo "^hmac-sha256" ;; - hmac-sha384) echo "hmac-sha384" ;; - hmac-sha512) echo "hmac-sha512" ;; - hkdf-sha256) echo "^hkdf-sha256" ;; - hkdf-sha384) echo "hkdf-sha384" ;; - pbkdf2-sha256) echo "^pbkdf2-sha256/" ;; - pbkdf2-sha512) echo "^pbkdf2-sha512/" ;; - ecdsa-p256) echo "^ecdsa-p256/" ;; - ecdsa-p384) echo "^ecdsa-p384/" ;; - ed25519) echo "ed25519" ;; - x25519) echo "x25519" ;; - ml-kem|mlkem) echo "^mlkem(512|768|1024)/" ;; - ml-kem-512|mlkem512) echo "^mlkem512/" ;; - ml-kem-768|mlkem768) echo "^mlkem768/" ;; - ml-kem-1024|mlkem1024) echo "^mlkem1024/" ;; - *) echo "$algo" ;; - esac -} - -rsa_filter_token() { - local algo="${1:-}" - case "$algo" in - rsa) echo "rsa" ;; - *) echo "$algo" ;; - esac -} - -aead_filter_token() { - local algo="${1:-}" - case "$algo" in - xchacha20-poly1305) echo "xchacha20-poly1305" ;; - chacha20-poly1305) echo "^chacha20-poly1305/" ;; - aes-256-gcm-siv) echo "aes-256-gcm-siv" ;; - aes-128-gcm-siv) echo "aes-128-gcm-siv" ;; - aes-256-gcm) echo "^aes-256-gcm/" ;; - aes-128-gcm) echo "^aes-128-gcm/" ;; - aegis-256) echo "aegis-256" ;; - ascon-aead128) echo "ascon-aead128" ;; - *) echo "$algo" ;; - esac +catalog() { + scripts/ct/python.sh "$BENCHMARK_CATALOG" "$@" } default_benches_for_crate() { - local crate="${1:-}" - case "$crate" in - checksum) echo "crc" ;; - hashes) echo "sha2,sha3,kmac_cshake,ascon,xxh3,rapidhash,blake2,blake3" ;; - auth) echo "auth,kmac_cshake,password_hashing,rsa" ;; - rsa) echo "rsa" ;; - aead) echo "aead" ;; - *) echo "" ;; - esac -} - -merge_csvs() { - local -a parts=() - local -a merged=() - local csv - local token - - for csv in "$@"; do - [[ -z "$csv" ]] && continue - IFS=',' read -r -a parts <<< "$csv" - for token in "${parts[@]:+${parts[@]}}"; do - token="$(echo "$token" | xargs)" - [[ -z "$token" ]] && continue - append_unique "$token" merged - done - done - - if [[ "${#merged[@]}" -eq 0 ]]; then - echo "" - else - (IFS=','; echo "${merged[*]}") - fi + catalog default-benches "${1:-}" } bench_features_for_target() { - local bench="${1:-}" - case "$bench" in - crc) echo "parallel,checksums" ;; - sha2) echo "parallel,sha2,diag" ;; - sha3) echo "parallel,sha3" ;; - kmac_cshake) echo "parallel,sha3,kmac" ;; - ascon) echo "parallel,ascon-hash" ;; - xxh3) echo "parallel,xxh3" ;; - rapidhash) echo "parallel,rapidhash" ;; - aead_kernels) echo "parallel,sha2,chacha20poly1305,diag" ;; - blake2) echo "parallel,blake2b,blake2s" ;; - blake3) echo "parallel,blake3" ;; - auth) echo "parallel,hmac,hkdf,pbkdf2,ecdsa,ed25519,x25519,ml-kem,diag" ;; - password_hashing) echo "parallel,argon2,scrypt,phc-strings,getrandom" ;; - rsa) echo "parallel,rsa,diag" ;; - aead_diag) echo "parallel,sha2,aes-gcm,aes-gcm-siv,chacha20poly1305,xchacha20poly1305,aegis256,ascon-aead,diag" ;; - aead) echo "parallel,aes-gcm,aes-gcm-siv,chacha20poly1305,xchacha20poly1305,aegis256,ascon-aead" ;; - *) echo "parallel" ;; - esac + catalog features "${1:-}" } bench_binary_for_target() { - local bench="${1:-}" - case "$bench" in - aead_diag) echo "aead" ;; - *) echo "$bench" ;; - esac + catalog binary "${1:-}" } bench_features_for_invocation() { - local benches_csv="${1:-}" - local features="" - local -a benches=() - local bench - - if [[ -z "$benches_csv" ]]; then - echo "parallel,full" - return 0 - fi - - IFS=',' read -r -a benches <<< "$benches_csv" - for bench in "${benches[@]:+${benches[@]}}"; do - features="$(merge_csvs "$features" "$(bench_features_for_target "$bench")")" - done - - if [[ -z "$features" ]]; then - echo "parallel" - else - echo "$features" - fi + catalog features "${1:-}" } -bench_target_for_hash_algo() { - local algo="${1:-}" - case "$algo" in - sha224|sha256|sha384|sha512|sha512-256) echo "sha2" ;; - sha3-224|sha3-256|sha3-384|sha3-512|shake128|shake256) echo "sha3" ;; - cshake256) echo "kmac_cshake" ;; - ascon-hash256|ascon-xof128|ascon-cxof128) echo "ascon" ;; - xxh3) echo "xxh3" ;; - rapidhash) echo "rapidhash" ;; - blake2) echo "blake2" ;; - blake3) echo "blake3" ;; - *) return 1 ;; - esac -} - -append_algo_plan_row() { - local algo="${1:-}" +append_algo_plan_rows() { + local algorithms_csv="${1:-}" local raw_filter="${2:-}" - local bench="" - local crate="" - local token="" - - if array_contains "$algo" "${DEFAULT_CHECKSUM_ALGOS[@]}"; then - crate="checksum" - bench="crc" - token="${raw_filter:-$(checksum_filter_token "$algo")}" - PLAN_ROWS+=("$crate|$bench|$token") - return 0 - fi - - if array_contains "$algo" "${DEFAULT_HASH_ALGOS[@]}"; then - if ! bench="$(bench_target_for_hash_algo "$algo")"; then - return 0 - fi - crate="hashes" - token="${raw_filter:-$(hash_filter_token "$algo")}" - PLAN_ROWS+=("$crate|$bench|$token") - return 0 - fi - - if array_contains "$algo" "${DEFAULT_AUTH_ALGOS[@]}"; then - crate="auth" - bench="auth" - token="${raw_filter:-$(auth_filter_token "$algo")}" - PLAN_ROWS+=("$crate|$bench|$token") - return 0 - fi - - if array_contains "$algo" "${DEFAULT_SP800185_ALGOS[@]}"; then - crate="auth" - bench="kmac_cshake" - token="${raw_filter:-$(sp800185_filter_token "$algo")}" - PLAN_ROWS+=("$crate|$bench|$token") - return 0 - fi - - if array_contains "$algo" "${DEFAULT_PASSWORD_HASHING_ALGOS[@]}"; then - crate="auth" - bench="password_hashing" - token="${raw_filter:-$(password_hashing_filter_token "$algo")}" - PLAN_ROWS+=("$crate|$bench|$token") - return 0 - fi - - if array_contains "$algo" "${DEFAULT_RSA_ALGOS[@]}"; then - crate="auth" - bench="rsa" - token="${raw_filter:-$(rsa_filter_token "$algo")}" - PLAN_ROWS+=("$crate|$bench|$token") - return 0 - fi - - if array_contains "$algo" "${DIAG_AEAD_ALGOS[@]}"; then - crate="aead" - bench="aead_diag" - token="${raw_filter:-chacha20-poly1305/encrypt}" - PLAN_ROWS+=("$crate|$bench|$token") - return 0 - fi - - if array_contains "$algo" "${DEFAULT_AEAD_ALGOS[@]}"; then - crate="aead" - bench="aead" - token="${raw_filter:-$(aead_filter_token "$algo")}" - PLAN_ROWS+=("$crate|$bench|$token") - fi + local row + while IFS= read -r row; do + [[ -n "$row" ]] && PLAN_ROWS+=("$row") + done < <(catalog plan-algorithms "$algorithms_csv" "$raw_filter") } dedupe_plan_rows() { @@ -500,26 +151,8 @@ dedupe_plan_rows() { CRATES_INPUT="$(normalize_csv_lower "${BENCH_CRATES:-}")" BENCHES_INPUT="$(normalize_csv_lower "${BENCH_BENCHES:-}")" -# Expand bench shorthand and legacy aliases onto real bench targets. expand_bench_shorthand() { - local raw="$1" - [[ -n "$raw" ]] || return 0 - local -a expanded=() - local token - IFS=',' read -r -a tokens <<< "$raw" - for token in "${tokens[@]}"; do - case "$token" in - comp) expanded+=("crc" "sha2" "sha3" "kmac_cshake" "ascon" "auth" "aead" "xxh3" "rapidhash" "blake3") ;; - kernels) expanded+=("blake3") ;; - checksum_comp|checksum_kernels) expanded+=("crc") ;; - hashes_comp) expanded+=("sha2" "sha3" "kmac_cshake" "ascon" "xxh3" "rapidhash" "blake3") ;; - auth_comp) expanded+=("auth") ;; - aead_comp) expanded+=("aead") ;; - hashes_kernels) expanded+=("blake3") ;; - *) expanded+=("$token") ;; - esac - done - (IFS=','; echo "${expanded[*]}") + catalog expand-benches "$1" } BENCHES_INPUT="$(expand_bench_shorthand "$BENCHES_INPUT")" @@ -553,9 +186,12 @@ if [[ -n "$SAMPLE_SIZE_INPUT" && (! "$SAMPLE_SIZE_INPUT" =~ ^[0-9]+$ || "$SAMPLE exit 2 fi -if [[ -n "$PROFILE_TIME_SECS_INPUT" && ! "$PROFILE_TIME_SECS_INPUT" =~ ^[0-9]+(\.[0-9]+)?$ ]]; then - echo "error: BENCH_PROFILE_TIME_SECS must be numeric (got '$PROFILE_TIME_SECS_INPUT')" >&2 - exit 2 +if [[ -n "$PROFILE_TIME_SECS_INPUT" ]]; then + if [[ ! "$PROFILE_TIME_SECS_INPUT" =~ ^[0-9]+(\.[0-9]+)?$ ]] \ + || ! awk -v seconds="$PROFILE_TIME_SECS_INPUT" 'BEGIN { exit !(seconds >= 1) }'; then + echo "error: BENCH_PROFILE_TIME_SECS must be numeric and at least 1 (got '$PROFILE_TIME_SECS_INPUT')" >&2 + exit 2 + fi fi CRITERION_ARGS=() @@ -693,84 +329,23 @@ if [[ -n "$ONLY_INPUT" ]]; then IFS=',' read -r -a only_values <<< "$ONLY_INPUT" for selector in "${only_values[@]:+${only_values[@]}}"; do key="$(normalize_selector "$selector")" - case "$key" in - all) - HAS_ALL_SELECTOR="true" - for algo in "${ALL_KNOWN_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - ;; - auth) - for algo in "${DEFAULT_AUTH_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - for algo in "${DEFAULT_SP800185_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - for algo in "${DEFAULT_PASSWORD_HASHING_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - for algo in "${DEFAULT_RSA_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - ;; - rsa) - for algo in "${DEFAULT_RSA_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - ;; - aead) - for algo in "${DEFAULT_AEAD_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - ;; - aeaddiag|chacha20poly1305diag|chacha20poly1305par4) - append_unique "aead-diag" SELECTED_ALGOS - ;; - checksum|checksums) - for algo in "${DEFAULT_CHECKSUM_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - ;; - hashes|hash) - for algo in "${DEFAULT_HASH_ALGOS[@]}"; do append_unique "$algo" SELECTED_ALGOS; done - ;; - blake3) - append_unique "blake3" SELECTED_ALGOS - ;; - mlkem) - append_unique "mlkem512" SELECTED_ALGOS - append_unique "mlkem768" SELECTED_ALGOS - append_unique "mlkem1024" SELECTED_ALGOS - ;; - mlkem512) - append_unique "mlkem512" SELECTED_ALGOS - ;; - mlkem768) - append_unique "mlkem768" SELECTED_ALGOS - ;; - mlkem1024) - append_unique "mlkem1024" SELECTED_ALGOS - ;; - crc64|crc64nvme|crc64xz) - append_unique "crc64-xz" SELECTED_ALGOS - append_unique "crc64-nvme" SELECTED_ALGOS - ;; - crc32) - append_unique "crc32-ieee" SELECTED_ALGOS - append_unique "crc32c" SELECTED_ALGOS - ;; - crc16) - append_unique "crc16-ccitt" SELECTED_ALGOS - append_unique "crc16-ibm" SELECTED_ALGOS - ;; - *) - matched="false" - for algo in "${ALL_KNOWN_ALGOS[@]}"; do - if [[ "$(normalize_selector "$algo")" == "$key" ]]; then - append_unique "$algo" SELECTED_ALGOS - matched="true" - break - fi - done - if [[ "$matched" == "false" ]]; then - append_unique "$selector" RAW_FILTERS - fi - ;; - esac + if resolved_algorithms="$(catalog resolve-selector "$selector")"; then + [[ "$key" == "all" ]] && HAS_ALL_SELECTOR="true" + IFS=',' read -r -a resolved_values <<< "$resolved_algorithms" + for algo in "${resolved_values[@]:+${resolved_values[@]}}"; do + append_unique "$algo" SELECTED_ALGOS + done + else + append_unique "$selector" RAW_FILTERS + fi done # When an explicit raw Criterion filter is provided, treat it as the # authoritative benchmark matcher and avoid adding a second broad plan row # from BENCH_ONLY (e.g. "blake3"), which would run extra surfaces. if [[ -z "$FILTER_INPUT" ]]; then - for algo in "${SELECTED_ALGOS[@]:+${SELECTED_ALGOS[@]}}"; do - append_algo_plan_row "$algo" - done + selected_algorithms_csv="$(IFS=','; echo "${SELECTED_ALGOS[*]}")" + append_algo_plan_rows "$selected_algorithms_csv" fi fi @@ -786,10 +361,9 @@ if [[ "${#RAW_FILTERS[@]}" -gt 0 ]]; then raw_benches=() if [[ -z "$CRATES_INPUT" && -z "$BENCHES_INPUT" && "${#SELECTED_ALGOS[@]}" -gt 0 ]]; then + selected_algorithms_csv="$(IFS=','; echo "${SELECTED_ALGOS[*]}")" for filter in "${RAW_FILTERS[@]}"; do - for algo in "${SELECTED_ALGOS[@]:+${SELECTED_ALGOS[@]}}"; do - append_algo_plan_row "$algo" "$filter" - done + append_algo_plan_rows "$selected_algorithms_csv" "$filter" done dedupe_plan_rows else @@ -804,23 +378,8 @@ if [[ "${#RAW_FILTERS[@]}" -gt 0 ]]; then append_unique "$crate" raw_crates done elif [[ "${#SELECTED_ALGOS[@]}" -gt 0 ]]; then - for algo in "${SELECTED_ALGOS[@]:+${SELECTED_ALGOS[@]}}"; do - if array_contains "$algo" "${DEFAULT_CHECKSUM_ALGOS[@]}"; then - append_unique "checksum" raw_crates - elif array_contains "$algo" "${DEFAULT_HASH_ALGOS[@]}"; then - append_unique "hashes" raw_crates - elif array_contains "$algo" "${DEFAULT_AUTH_ALGOS[@]}"; then - append_unique "auth" raw_crates - elif array_contains "$algo" "${DEFAULT_SP800185_ALGOS[@]}"; then - append_unique "auth" raw_crates - elif array_contains "$algo" "${DEFAULT_PASSWORD_HASHING_ALGOS[@]}"; then - append_unique "auth" raw_crates - elif array_contains "$algo" "${DEFAULT_RSA_ALGOS[@]}"; then - append_unique "auth" raw_crates - elif array_contains "$algo" "${DEFAULT_AEAD_ALGOS[@]}"; then - append_unique "aead" raw_crates - fi - done + selected_algorithms_csv="$(IFS=','; echo "${SELECTED_ALGOS[*]}")" + IFS=',' read -r -a raw_crates <<< "$(catalog crates-for-algorithms "$selected_algorithms_csv")" if [[ "${#raw_crates[@]}" -eq 0 ]]; then raw_crates=("checksum" "hashes" "auth" "aead") fi @@ -858,7 +417,8 @@ fi dedupe_plan_rows if [[ "$HAS_ALL_SELECTOR" == "true" && -z "$CRATES_INPUT" && -z "$BENCHES_INPUT" ]]; then - for required_bench in "${ALL_REQUIRED_BENCHES[@]}"; do + IFS=',' read -r -a required_benches <<< "$(catalog required-benches)" + for required_bench in "${required_benches[@]}"; do found_bench="false" for row in "${PLAN_ROWS[@]:+${PLAN_ROWS[@]}}"; do IFS='|' read -r _ bench _ <<< "$row" @@ -980,6 +540,11 @@ BENCH_FLAGS=() if [[ -n "$BENCHES_INPUT" ]]; then IFS=',' read -r -a benches_values <<< "$BENCHES_INPUT" for bench in "${benches_values[@]:+${benches_values[@]}}"; do + BENCH_FLAGS+=(--bench "$(bench_binary_for_target "$bench")") + done +else + IFS=',' read -r -a criterion_binaries <<< "$(catalog criterion-binaries)" + for bench in "${criterion_binaries[@]}"; do BENCH_FLAGS+=(--bench "$bench") done fi diff --git a/scripts/ci/tool-integrity-test.sh b/scripts/ci/tool-integrity-test.sh index ef0c6418..3137d83c 100755 --- a/scripts/ci/tool-integrity-test.sh +++ b/scripts/ci/tool-integrity-test.sh @@ -81,8 +81,13 @@ cat >"$direct_bin/tar" <<'SH' set -euo pipefail printf 'tar %s\n' "$*" >>"$MOCK_COMMAND_LOG" destination="" +archive="" while [[ $# -gt 0 ]]; do case "$1" in + -xJf | -xzf) + archive=$2 + shift 2 + ;; -C) destination=$2 shift 2 @@ -90,20 +95,46 @@ while [[ $# -gt 0 ]]; do *) shift ;; esac done -[[ -n "$destination" ]] -root="$destination/wasmtime-v47.0.3-x86_64-linux" -mkdir -p "$root" -cat >"$root/wasmtime" <<'EOF' +[[ -n "$destination" && -n "$archive" ]] +case "${archive##*/}" in + wasmtime-*) + root="$destination/wasmtime-v48.0.0-x86_64-linux" + mkdir -p "$root" + cat >"$root/wasmtime" <<'EOF' +#!/usr/bin/env bash +if [[ "${1:-}" == --version ]]; then + printf 'wasmtime 48.0.0 (mock)\n' +else + printf 'wasmtime executed\n' >>"$MOCK_EXEC_LOG" +fi +EOF + chmod +x "$root/wasmtime" + ;; + wasm-tools-*) + root="$destination/wasm-tools-1.257.1-x86_64-linux" + mkdir -p "$root" + cat >"$root/wasm-tools" <<'EOF' #!/usr/bin/env bash -printf 'wasmtime 47.0.3 (mock)\n' -printf 'wasmtime executed\n' >>"$MOCK_EXEC_LOG" +case "${1:-}" in + --version) printf 'wasm-tools 1.257.1\n' ;; + validate) printf 'wasm-tools validated\n' >>"$MOCK_EXEC_LOG" ;; + print) printf '(module (func (drop (v128.const i32x4 0 0 0 0))))\n' ;; + *) exit 2 ;; +esac EOF -chmod +x "$root/wasmtime" + chmod +x "$root/wasm-tools" + ;; + *) exit 2 ;; +esac SH cat >"$direct_bin/cargo" <<'SH' #!/usr/bin/env bash printf 'cargo %s\n' "$*" >>"$MOCK_COMMAND_LOG" +if [[ "${1:-}" == build && -n "${CARGO_TARGET_DIR:-}" ]]; then + mkdir -p "$CARGO_TARGET_DIR/wasm32-wasip1/debug" + : >"$CARGO_TARGET_DIR/wasm32-wasip1/debug/rscrypto-wasm-runtime-vectors.wasm" +fi SH cat >"$direct_bin/rustup" <<'SH' @@ -216,21 +247,27 @@ wasmtime_artifact="$TMP_ROOT/wasmtime.tar.xz" printf 'authenticated mock Wasmtime archive\n' >"$wasmtime_artifact" wasmtime_digest=$(sha256_file "$wasmtime_artifact") set_manifest_digest "$direct_fixture" wasmtime linux x86_64 "$wasmtime_digest" +set_manifest_digest "$direct_fixture" wasm-tools linux x86_64 "$wasmtime_digest" wasmtime_home="$TMP_ROOT/wasmtime-home" +wasm_tools_home="$TMP_ROOT/wasm-tools-home" wasmtime_exec="$TMP_ROOT/wasmtime.exec" : >"$direct_log" ( cd "$direct_fixture" PATH="$direct_bin:$PATH" \ WASMTIME_HOME="$wasmtime_home" \ + WASM_TOOLS_HOME="$wasm_tools_home" \ MOCK_COMMAND_LOG="$direct_log" \ MOCK_DOWNLOAD_FILE="$wasmtime_artifact" \ MOCK_EXEC_LOG="$wasmtime_exec" \ scripts/ci/nostd-wasm-suite.sh wasm32-wasip1 shallow ) >/dev/null [[ -x "$wasmtime_home/bin/wasmtime" ]] || fail "verified Wasmtime was not installed" +[[ -x "$wasm_tools_home/bin/wasm-tools" ]] || fail "verified wasm-tools was not installed" grep -Fq 'tar -xJf' "$direct_log" || fail "verified Wasmtime was not extracted" +grep -Fq 'tar -xzf' "$direct_log" || fail "verified wasm-tools was not extracted" grep -Fqx 'wasmtime executed' "$wasmtime_exec" || fail "verified Wasmtime was not executed" +grep -Fqx 'wasm-tools validated' "$wasmtime_exec" || fail "verified wasm-tools did not validate artifacts" : >"$direct_log" bad_wasmtime_exec="$TMP_ROOT/bad-wasmtime.exec" @@ -238,6 +275,7 @@ if ( cd "$direct_fixture" PATH="$direct_bin:$PATH" \ WASMTIME_HOME="$TMP_ROOT/bad-wasmtime-home" \ + WASM_TOOLS_HOME="$TMP_ROOT/bad-wasm-tools-home" \ MOCK_COMMAND_LOG="$direct_log" \ MOCK_DOWNLOAD_FILE="$invalid_artifact" \ MOCK_EXEC_LOG="$bad_wasmtime_exec" \ @@ -299,7 +337,10 @@ version=${required#=} awk -v package="$package" '$1 != package' "$MOCK_CARGO_STATE" >"$MOCK_CARGO_STATE.tmp" printf '%s %s\n' "$package" "$version" >>"$MOCK_CARGO_STATE.tmp" mv "$MOCK_CARGO_STATE.tmp" "$MOCK_CARGO_STATE" -binary="$CARGO_HOME/bin/$package" +case "$package" in + cargo-show-asm) binary="$CARGO_HOME/bin/cargo-asm" ;; + *) binary="$CARGO_HOME/bin/$package" ;; +esac mkdir -p "$(dirname "$binary")" printf '#!/usr/bin/env bash\nprintf "%%s %%s\\n" %q %q\n' "$package" "$version" >"$binary" chmod +x "$binary" @@ -345,7 +386,7 @@ package_temp="$TMP_ROOT/package-temp" mkdir -p "$package_home/.cargo/bin" mkdir -p "$package_temp" : >"$package_log" -for mode in standard quality release semver rail ci supply-chain ibm bench fuzz coverage minimal none; do +for mode in standard quality release semver rail ci supply-chain ibm bench structural-bench profile fuzz coverage minimal none; do HOME="$package_home" \ RUNNER_TEMP="$package_temp" \ PATH="$package_bin:$PATH" \ @@ -548,8 +589,12 @@ for contract in \ 'zizmor =1.29.0' \ 'cargo-criterion =1.1.0' \ 'critcmp =0.1.8' \ + 'gungraun-runner =0.19.4' \ + 'cargo-show-asm =0.2.62' \ + 'samply =0.13.1' \ + 'cargo-llvm-lines =0.4.48' \ 'cargo-fuzz =0.13.2' \ - 'cargo-llvm-cov =0.8.7'; do + 'cargo-llvm-cov =0.9.0'; do package=${contract%% *} version=${contract#* } grep -Fq "cargo install --registry crates-io $package --locked --version $version --force" \ diff --git a/scripts/lib/ci-tool-integrity.sh b/scripts/lib/ci-tool-integrity.sh index aa756a5f..4a156fa4 100644 --- a/scripts/lib/ci-tool-integrity.sh +++ b/scripts/lib/ci-tool-integrity.sh @@ -33,7 +33,7 @@ ci_tool_validate_record() { local digest=$7 case "$tool" in - wasmtime | codecov) ;; + wasmtime | wasm-tools | codecov) ;; *) ci_tool_fail "unknown direct CI tool: $tool" || return ;; esac [[ "$version" =~ ^v?[0-9]+\.[0-9]+\.[0-9]+[-+A-Za-z0-9.]*$ ]] \ @@ -103,9 +103,9 @@ ci_tool_resolve() { ci_tool_validate_manifest() { local tool - for tool in wasmtime codecov; do + for tool in wasmtime wasm-tools codecov; do case "$tool" in - wasmtime) + wasmtime | wasm-tools) local expected_platforms=(linux:x86_64 linux:aarch64 macos:x86_64 macos:aarch64) ;; codecov) diff --git a/src/auth/mod.rs b/src/auth/mod.rs index f50505dc..37e1fc4e 100644 --- a/src/auth/mod.rs +++ b/src/auth/mod.rs @@ -323,6 +323,7 @@ pub use rsa::{ #[cfg(all(feature = "rsa", feature = "diag"))] pub use rsa::{ diag_rsa_import_pkcs8_private_key_der_stage, diag_rsa_private_component_validation_32, + diag_rsa_private_exponentiate_fixed_width, diag_rsa_private_exponentiate_fixed_width_with_scratch, diag_rsa_private_select_window_power_4, diag_rsa_validate_pkcs8_private_key_der, diag_rsa_validate_pkcs8_private_key_der_stage, }; diff --git a/src/auth/rsa.rs b/src/auth/rsa.rs index 248f5059..055b3ce7 100644 --- a/src/auth/rsa.rs +++ b/src/auth/rsa.rs @@ -118,6 +118,86 @@ const PRIVATE_FIXED_WINDOW_TABLE_ENTRIES: usize = 16; const RSA_KEYGEN_PUBLIC_EXPONENT: u64 = 65_537; #[cfg(feature = "getrandom")] const RSA_KEYGEN_MILLER_RABIN_ROUNDS: usize = 32; + +#[cfg(all(target_arch = "aarch64", not(miri)))] +#[must_use = "the guard must remain alive for the complete secret arithmetic operation"] +struct Aarch64DitGuard { + restore_disabled: bool, +} + +#[cfg(all(target_arch = "aarch64", not(miri)))] +impl Aarch64DitGuard { + #[inline] + fn supported() -> bool { + #[cfg(feature = "std")] + { + std::arch::is_aarch64_feature_detected!("dit") + } + #[cfg(not(feature = "std"))] + { + cfg!(target_feature = "dit") + } + } + + #[inline] + fn state_if_supported() -> Option { + if !Self::supported() { + return None; + } + + let state: u64; + // SAFETY: `supported` establishes FEAT_DIT before the DIT system register is + // accessed. `.inst 0xd53b42a8` encodes `mrs x8, DIT`; the explicit late + // output declares the complete register effect. MRS DIT is available at + // EL0, touches no memory or stack, and the conservative asm options keep it + // ordered with the guarded arithmetic. + unsafe { + core::arch::asm!( + ".inst 0xd53b42a8", + lateout("x8") state, + options(nostack, preserves_flags) + ); + } + Some(state) + } + + #[inline] + fn enter() -> Self { + let Some(previous) = Self::state_if_supported() else { + return Self { + restore_disabled: false, + }; + }; + let restore_disabled = previous == 0; + if restore_disabled { + // SAFETY: `state_if_supported` established FEAT_DIT. `.inst 0xd503415f` + // encodes `msr DIT, #1`; it has no register operands. MSR DIT is available + // at EL0, writes only PSTATE.DIT, and the guard restores the prior disabled + // state on every normal or unwinding exit. + unsafe { + core::arch::asm!(".inst 0xd503415f", options(nostack, preserves_flags)); + } + } + Self { restore_disabled } + } +} + +#[cfg(all(target_arch = "aarch64", not(miri)))] +impl Drop for Aarch64DitGuard { + #[inline] + fn drop(&mut self) { + if self.restore_disabled { + // SAFETY: `restore_disabled` can be true only after FEAT_DIT was + // established and this guard successfully enabled PSTATE.DIT. `.inst + // 0xd503405f` encodes `msr DIT, #0`; it restores the caller's prior state, + // has no register operands, and touches no memory. + unsafe { + core::arch::asm!(".inst 0xd503405f", options(nostack, preserves_flags)); + } + } + } +} + const RSA_IMPORT_MILLER_RABIN_BASES: [u16; 32] = [ 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, @@ -848,6 +928,67 @@ pub fn diag_rsa_public_operation_bitserial( clear_output_on_error(result, out) } +/// Apply the production RSA private fixed-window exponentiation loop to a caller-supplied public modulus. +/// +/// This diagnostic-only primitive isolates fixed-width secret-exponent handling +/// without exposing a private key. It is raw modular arithmetic without RSA +/// key validation, padding, or blinding and must not be used as a cryptographic +/// protocol API. +/// +/// # Errors +/// +/// Returns [`RsaPrivateOpError`] if the modulus is empty or even, if `exponent`, +/// `input`, or `out` is not exactly the modulus length, or if `input >= n`. +#[cfg(feature = "diag")] +#[doc(hidden)] +pub fn diag_rsa_private_exponentiate_fixed_width( + modulus: &[u8], + exponent: &[u8], + input: &[u8], + out: &mut [u8], +) -> Result<(), RsaPrivateOpError> { + let result = match modulus.last() { + Some(last) if last & 1 == 1 => private_component_modulus(modulus) + .and_then(|modulus| private_exponentiate_representative(&modulus, exponent, input, out)), + _ => Err(RsaPrivateOpError::RepresentativeOutOfRange), + }; + clear_output_on_error(result, out) +} + +/// Apply the production RSA private fixed-window exponentiation loop with reusable scratch. +/// +/// This diagnostic-only primitive uses the first precomputed CRT component +/// modulus from `key`, matching the steady-state production path without +/// timing component-modulus construction or heap allocation. It is raw +/// modular arithmetic without padding or blinding and must not be used as a +/// cryptographic protocol API. +/// +/// # Errors +/// +/// Returns [`RsaPrivateOpError`] if `scratch` has another public modulus width, +/// if `exponent`, `input`, or `out` is not exactly the first CRT component +/// width, or if `input` is outside that component modulus. +#[cfg(feature = "diag")] +#[doc(hidden)] +pub fn diag_rsa_private_exponentiate_fixed_width_with_scratch( + key: &RsaPrivateKey, + exponent: &[u8], + input: &[u8], + out: &mut [u8], + scratch: &mut RsaPrivateScratch, +) -> Result<(), RsaPrivateOpError> { + let result = scratch.ensure_len(key.signature_len()).and_then(|()| { + private_exponentiate_representative_with_scratch( + &key.components.prime_p_modulus, + exponent, + input, + out, + &mut scratch.exponent_scratch, + ) + }); + clear_output_on_error(result, out) +} + /// Apply the RSA public operation with product-then-reduce Montgomery multiplication. /// /// Diagnostic-only benchmark baseline for threshold selection. This forces the @@ -8831,6 +8972,9 @@ fn private_exponentiate_representative( input: &[u8], out: &mut [u8], ) -> Result<(), RsaPrivateOpError> { + #[cfg(all(target_arch = "aarch64", not(miri)))] + let _dit_guard = Aarch64DitGuard::enter(); + let bytes = modulus.bytes.len(); let limbs = modulus.limbs.len(); if input.len() != bytes || out.len() != bytes || exponent.len() != bytes { @@ -8909,6 +9053,9 @@ fn private_exponentiate_representative_with_scratch( out: &mut [u8], scratch: &mut RsaPrivateExponentScratch, ) -> Result<(), RsaPrivateOpError> { + #[cfg(all(target_arch = "aarch64", not(miri)))] + let _dit_guard = Aarch64DitGuard::enter(); + let bytes = modulus.bytes.len(); let limbs = modulus.limbs.len(); if input.len() != bytes || out.len() != bytes || exponent.len() != bytes { @@ -10472,6 +10619,19 @@ mod tests { use super::*; + #[test] + #[cfg(all(target_arch = "aarch64", not(miri)))] + fn aarch64_dit_guard_enables_and_restores_data_independent_timing() { + let Some(before) = Aarch64DitGuard::state_if_supported() else { + return; + }; + { + let _guard = Aarch64DitGuard::enter(); + assert_ne!(Aarch64DitGuard::state_if_supported(), Some(0)); + } + assert_eq!(Aarch64DitGuard::state_if_supported(), Some(before)); + } + #[cfg(feature = "getrandom")] const CAVP_KEYGEN_186_3_PROBABLE_PRIME: &str = include_str!("../../testdata/rsa/nist_cavp/rsa_keygen_186_3_probable_prime_subset.json"); diff --git a/src/hashes/fast/xxh3/stream.rs b/src/hashes/fast/xxh3/stream.rs index dd81a380..35fc73e2 100644 --- a/src/hashes/fast/xxh3/stream.rs +++ b/src/hashes/fast/xxh3/stream.rs @@ -636,7 +636,14 @@ mod tests { #[test] #[cfg(not(miri))] fn available_long_kernels_match_independent_oracle_across_alignments_and_tails() { - use super::super::kernels::{Xxh3KernelId, hash64_long_fn, hash128_long_fn, required_caps}; + #[cfg(any( + target_arch = "x86_64", + target_arch = "aarch64", + all(target_arch = "powerpc64", target_endian = "little"), + target_arch = "s390x" + ))] + use super::super::kernels::Xxh3KernelId; + use super::super::kernels::{hash64_long_fn, hash128_long_fn, required_caps}; #[cfg(target_arch = "x86_64")] let kernels = &[Xxh3KernelId::Avx2, Xxh3KernelId::Avx512][..]; diff --git a/testdata/ascon/README.md b/testdata/ascon/README.md index 070b2721..77c73fe0 100644 --- a/testdata/ascon/README.md +++ b/testdata/ascon/README.md @@ -3,8 +3,8 @@ The vectors in this directory come from the Ascon reference implementation at `ascon/ascon-c` commit `446347f21b209f3921c65ece70027c366cbe1693`: -| Local file | Upstream file | SHA-256 | -| --- | --- | --- | +| Local file | Upstream file | SHA-256 | +| ------------------ | --------------------------------------------------- | ------------------------------------------------------------------ | | `asconaead128.txt` | `crypto_aead/asconaead128/LWC_AEAD_KAT_128_128.txt` | `bbbc34692fe05e5fda0a3b025585622ab3e3747495e5e3655b29aae8c2a4bd33` | | `asconcxof128.txt` | `crypto_cxof/asconcxof128/LWC_CXOF_KAT_128_512.txt` | `abcbb0cc851a7f9cfc5ea2bcaf3eba5b2056e37fcb8ce541ceda1d1b960fc9dc` | diff --git a/tests/rsa_public_key.rs b/tests/rsa_public_key.rs index 86e95e1c..6f5dea71 100644 --- a/tests/rsa_public_key.rs +++ b/tests/rsa_public_key.rs @@ -36,6 +36,7 @@ use rsa::{ use rscrypto::RsaEncryptionError; #[cfg(feature = "diag")] use rscrypto::auth::rsa::{ + diag_rsa_private_exponentiate_fixed_width, diag_rsa_private_exponentiate_fixed_width_with_scratch, diag_rsa_public_operation_bitserial, diag_rsa_public_operation_cios, diag_rsa_public_operation_cios_portable, diag_rsa_public_operation_comba_product, diag_rsa_public_operation_product, diag_rsa_public_operation_window2_exponent, diag_rsa_verify_pkcs1v15_encoded, diag_rsa_verify_pss_encoded, @@ -3512,6 +3513,76 @@ fn public_operation_boundary_representatives_match_independent_reference_across_ } } +#[test] +#[cfg(feature = "diag")] +fn diagnostic_private_exponentiation_matches_independent_fixed_width_reference() { + let len = 128; + let modulus = vec![0xa5; len]; + let mut input = vec![0u8; len]; + *input.last_mut().expect("an RSA representative must be nonempty") = 2; + + let mut leading_zero_exponent = vec![0x5a; len]; + leading_zero_exponent[0] = 0; + let mut full_width_exponent = leading_zero_exponent.clone(); + full_width_exponent[0] = 1; + + for exponent in [full_width_exponent, leading_zero_exponent] { + let mut out = vec![0u8; len]; + diag_rsa_private_exponentiate_fixed_width(&modulus, &exponent, &input, &mut out) + .expect("fixed-width diagnostic exponentiation must accept the valid representative"); + let reference = + BigUint::from_bytes_be(&input).modpow(&BigUint::from_bytes_be(&exponent), &BigUint::from_bytes_be(&modulus)); + assert_eq!(out, left_pad_to_len(&reference.to_bytes_be(), len)); + } + + let mut out = vec![0xa5; len]; + assert_eq!( + diag_rsa_private_exponentiate_fixed_width(&modulus, &[1, 2, 3], &input, &mut out), + Err(rscrypto::RsaPrivateOpError::InvalidLength) + ); + assert!(out.iter().all(|&byte| byte == 0)); + + let mut even_modulus = modulus; + *even_modulus.last_mut().expect("an RSA modulus must be nonempty") &= !1; + let exponent = vec![1; len]; + out.fill(0xa5); + assert_eq!( + diag_rsa_private_exponentiate_fixed_width(&even_modulus, &exponent, &input, &mut out), + Err(rscrypto::RsaPrivateOpError::RepresentativeOutOfRange) + ); + assert!(out.iter().all(|&byte| byte == 0)); + + let rustcrypto_key = rustcrypto_fixture_private_key(); + let component_modulus = rustcrypto_key.primes()[0].clone(); + let private_der = rustcrypto_key + .to_pkcs8_der() + .expect("the fixed private key must encode as PKCS#8"); + let key = + RsaPrivateKey::from_pkcs8_der_with_policy(private_der.as_bytes(), &RsaPublicKeyPolicy::legacy_verification()) + .expect("the fixed private key must import for reusable-scratch diagnostics"); + let component_len = component_modulus.to_bytes_be().len(); + let mut component_input = vec![0u8; component_len]; + *component_input + .last_mut() + .expect("an RSA component representative must be nonempty") = 2; + let mut component_exponent = vec![0x5a; component_len]; + component_exponent[0] = 1; + let mut component_out = vec![0u8; component_len]; + let mut scratch = key.private_scratch(); + + diag_rsa_private_exponentiate_fixed_width_with_scratch( + &key, + &component_exponent, + &component_input, + &mut component_out, + &mut scratch, + ) + .expect("reusable-scratch diagnostic exponentiation must accept the valid representative"); + let reference = + BigUint::from_bytes_be(&component_input).modpow(&BigUint::from_bytes_be(&component_exponent), &component_modulus); + assert_eq!(component_out, left_pad_to_len(&reference.to_bytes_be(), component_len)); +} + #[test] fn public_operation_matches_independent_65537_vector() { let modulus = hex_to_vec( diff --git a/tools/ct-dudect/Cargo.toml b/tools/ct-dudect/Cargo.toml index 48fe052f..75c52b8d 100644 --- a/tools/ct-dudect/Cargo.toml +++ b/tools/ct-dudect/Cargo.toml @@ -18,7 +18,12 @@ codegen-units = 1 [dependencies] dudect-bencher = "0.7.0" -rscrypto = { path = "../..", default-features = false, features = ["std", "full", "parallel", "diag"] } +rscrypto = { path = "../..", default-features = false, features = [ + "std", + "full", + "parallel", + "diag", +] } rscrypto-ct-harness = { path = "../ct-harness" } [[bin]] diff --git a/tools/ct-dudect/src/main.rs b/tools/ct-dudect/src/main.rs index 479a2e95..a8275293 100644 --- a/tools/ct-dudect/src/main.rs +++ b/tools/ct-dudect/src/main.rs @@ -1,3 +1,5 @@ +use core::cell::RefCell; + use dudect_bencher::{BenchRng, Class, CtRunner, ctbench_main_with_seeds, rand::RngExt}; use rscrypto::aead::expert::AeadWithNonce; use rscrypto::{ @@ -30,7 +32,8 @@ use rscrypto::{ diag_mlkem512_keygen_secret_noise_digest, diag_mlkem768_keygen_secret_noise_digest, diag_mlkem1024_keygen_secret_noise_digest, diag_mlkem1024_multiply_ntts_accumulate_input_digest, diag_rsa_import_pkcs8_private_key_der_stage, diag_rsa_private_component_validation_32, - diag_rsa_validate_pkcs8_private_key_der, diag_rsa_validate_pkcs8_private_key_der_stage, + diag_rsa_private_exponentiate_fixed_width_with_scratch, diag_rsa_validate_pkcs8_private_key_der, + diag_rsa_validate_pkcs8_private_key_der_stage, }, traits::Kem as _, }; @@ -1563,6 +1566,27 @@ fn rsa_pkcs1v15_full_width_vs_short_canonical_crt_exponent(runner: &mut CtRunner } } +fn rsa_private_exponent_fixed_width_high_byte(runner: &mut CtRunner, rng: &mut BenchRng) { + let key = rsa_ct_fixture_key(RSA_CT_KEY_A_INDEX); + let len = key.signature_len().div_euclid(2); + let mut input = vec![0u8; len]; + *input.last_mut().expect("RSA representatives must be nonempty") = 2; + + let mut exponent = vec![0x5a; len]; + let state = RefCell::new((key.private_scratch(), vec![0u8; len])); + + for class in balanced_classes(rng, samples()) { + exponent[0] = u8::from(matches!(class, Class::Left)); + let exponent = core::hint::black_box(exponent.as_slice()); + runner.run_one(class, || { + let mut state = state.borrow_mut(); + let (scratch, out) = &mut *state; + let result = diag_rsa_private_exponentiate_fixed_width_with_scratch(&key, exponent, &input, out, scratch); + (result.is_ok(), core::hint::black_box(out.as_slice())[0]) + }); + } +} + fn rsa_oaep_decrypt_fixed_vs_random_plaintext(runner: &mut CtRunner, rng: &mut BenchRng) { let key = rsa_ct_fixture_key(RSA_CT_KEY_A_INDEX); let sig_len = key.signature_len(); @@ -2267,6 +2291,7 @@ ctbench_main_with_seeds!( rsa_pkcs1v15_full_width_vs_short_canonical_crt_exponent, Some(0x7273615f6372746c) ), + (rsa_private_exponent_fixed_width_high_byte, Some(0x7273615f65787068)), (rsa_oaep_decrypt_fixed_vs_random_plaintext, Some(0x7273615f6f616570)), (rsa_pkcs1v15_decrypt_fixed_vs_random_plaintext, Some(0x7273615f64656331)), (