Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,8 @@ component — see the conventions note in that file's header).

`rust/guest-provider/README.md` carries the timing-channel classification (classes
A–D) and this provider's policy: only class A–C algorithms are exported,
always via constant-time-variant implementations; class D algorithm
via constant-time-variant implementations with the recorded CBC-unpadding
exception; class D algorithm
interfaces (RSA private-key ops, ECDSA signing, …) are **never** exported by
the in-guest provider, so compositions requiring them fail at `wac plug`
time. Secret-free operations (hashing public data, signature *verification*)
Expand Down
13 changes: 7 additions & 6 deletions rust/core/src/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -345,12 +345,13 @@ impl CipherKeyMaterial {

/// CBC-decrypt and unpad (the `cbc` crate over the keyed block
/// cipher). Uniform failure: wrong-shape ciphertext and bad padding
/// are indistinguishable — every failure, including the crate's
/// `UnpadError`, renders as the mode's one fixed message.
/// `block-padding`'s unpad reads the final block with data-dependent
/// branches; what that timing can distinguish is bounded by the
/// uniform error and recorded in the in-guest provider's
/// timing-channel classification.
/// are indistinguishable in the *returned error* — every failure,
/// including the crate's `UnpadError`, renders as the mode's one
/// fixed message. `block-padding`'s unpad reads the final block with
/// data-dependent branches; a uniform returned error does not imply
/// uniform execution timing, and this path is not constant-time. See
/// the in-guest provider's timing-channel classification for the
/// accepted residual risk.
fn cbc_decrypt(&self, iv: [u8; BLOCK], ciphertext: &[u8]) -> Result<Vec<u8>, Error> {
if ciphertext.is_empty() || !ciphertext.len().is_multiple_of(BLOCK) {
return Err(self.mode.decrypt_failed());
Expand Down
11 changes: 10 additions & 1 deletion rust/guest-provider/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ what marks where secrets flow.
| --- | --- | --- | --- |
| HMAC-SHA-2 (256/384/512) and HMAC-SHA-1 | A | `hmac` + `sha2`/`sha1` (pure ARX-style arithmetic; constant-time `verify_slice`) | None beyond compiler correctness. SHA-1 appears only inside the HMAC-family constructions (`hmac-sha1`, `hkdf-sha1`, `pbkdf2-sha1`), where collision resistance is not load-bearing. |
| AES-GCM (128/256) | C + B | `aes-gcm` with the soft **fixsliced** AES backend (bitsliced, table-free) + masked-multiply GHASH | Constant-latency integer multiply; JIT does not pathologically rewrite straight-line arithmetic. |
| AES-CBC / AES-CTR (128/256, the `cipher` kind) | C | The same fixsliced `aes` block cipher; CBC chaining, arbitrary-width wrapping CTR, and the branch-free PKCS#7 unpad are assembled here | As AES-GCM's AES half. The CBC padding *verdict* is API-visible by design (WebCrypto parity; one uniform error) — the unpad accumulates it without early exits, so timing adds nothing beyond the verdict itself. |
| AES-CBC / AES-CTR (128/256, the `cipher` kind) | C (AES core; CBC unpadding excepted) | The same fixsliced `aes` block cipher; `cbc` owns CBC chaining, and arbitrary-width wrapping CTR is assembled locally | As AES-GCM's AES half. CBC's PKCS#7 unpadding is variable-time; see the exception below. |
| AES-KW (128/256, the `key-wrap` kind) | C | `aes-kw` (RFC 3394) over the same fixsliced `aes` block cipher | As AES-GCM's AES half. The unwrap *verdict* is API-visible by design (one detail-free `authentication-failed` for malformed lengths and bad ICVs alike); the ICV comparison is a fixed-size compare of non-secret-length data. |
| X25519 key agreement | B | `x25519-dalek` (curve25519-dalek's constant-time Montgomery ladder: limb-based multiply-accumulate, no secret-dependent branches or indices; the all-zero contributory check compares in constant time) | Constant-latency integer multiply. |
| ECDH P-256/P-384 (key agreement) | B | `p256`/`p384` (RustCrypto: complete Renes–Costello–Batina formulas, constant-time field and scalar arithmetic, no secret-dependent branches or indices; strict point validation at import) | Constant-latency integer multiply; JIT does not pathologically rewrite straight-line arithmetic. |
Expand All @@ -126,6 +126,15 @@ what marks where secrets flow.
| RSASSA-PKCS1-v1_5 / RSA-PSS (**verify only**) | exempt (secret-free) | `rsa` crate verification — public keys and public signatures | Signing and decryption are class D (per-message secrets and blinded private-key ops; the `rsa` crate's private-key operations additionally carry RUSTSEC-2023-0071, the Marvin timing sidechannel) — the RSA private-key interfaces (`rsassa-pkcs1-v15-sign`, `rsa-pss-sign`) are **not exported**. |
| RSA-OAEP (**neither half exported**) | D (decrypt); encrypt has no secret-free half | None — the `public-encryption` kind is exported with uninhabited key resources | Decryption is class D and the attack lineage's prime target (blinded private-key ops; the Marvin sidechannel, RUSTSEC-2023-0071). Encryption is *not* secret-free, unlike signature verification: the plaintext is the secret, and it transits general-purpose bignum arithmetic with no constant-time variant — so the kind has no exportable half at all. Exporting the kind makes compositions requiring `rsa-oaep-encrypt` or `rsa-oaep-decrypt` fail at `wac plug` time. |

CBC remains exported for fixed-format compatibility with an explicit
exception to the constant-time-variant policy: `block-padding`'s PKCS#7
unpadding branches on decrypted padding length and stops at the first
mismatch. Equal-sized malformed inputs can take different validation
paths despite returning the same error. Timing can therefore reveal more
than the padding verdict; the uniform error does not remove this risk.
This applies to both decryption and key unwrapping. Prefer AES-GCM where
the format is not fixed; do not use unauthenticated CBC plaintext.

Ed25519 and ECDSA signing both handle a per-message secret nonce scalar;
Ed25519's determinism (deriving it from a secret prefix and the message,
[RFC 8032, section 5.1.6](https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.6))
Expand Down
9 changes: 5 additions & 4 deletions wit/aes.wit
Original file line number Diff line number Diff line change
Expand Up @@ -127,10 +127,11 @@ interface aes-gcm {
/// influence future plaintexts (CWE-329; the BEAST class), not merely
/// unique: generate it fresh and randomly per message.
/// - CBC with visible decryption failures is padding-oracle-prone by
/// nature. The `cipher` kind's uniform-failure rule bounds what this
/// API reveals to the verdict itself; protocols that surface the
/// verdict per message to an active attacker remain at risk. Prefer
/// `aes-gcm` anywhere the format is not already fixed.
/// nature. The `cipher` kind's uniform-failure rule gives every
/// malformed input the same returned error; a uniform returned error
/// does not guarantee uniform execution timing, and protocols that
/// surface the verdict per message to an active attacker remain at
/// risk. Prefer `aes-gcm` anywhere the format is not already fixed.
interface aes-cbc {
use types.{error};
use cipher.{cipher-key, cipher-key-options};
Expand Down
3 changes: 2 additions & 1 deletion wit/webcrypto.wit
Original file line number Diff line number Diff line change
Expand Up @@ -394,7 +394,8 @@ interface aead {
/// condition (for AES-CBC, a bad final padding block among them) fails
/// `error.other` with no distinguishing detail. Implementations MUST
/// NOT reveal *why* a decryption failed — a distinguishable padding
/// verdict is a padding-oracle amplifier.
/// verdict is a padding-oracle amplifier. This is a contract on the
/// returned error, not a guarantee of uniform execution timing.
/// - IV discipline is the algorithm's contract (unpredictability for CBC,
/// per-key uniqueness for CTR counter blocks); the minting interface
/// documents it. The caller owns it, as with `aead` nonces.
Expand Down
Loading