From bd056a9308275172b043e0a04953ff60369b964d Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sat, 9 May 2026 18:39:02 -0600 Subject: [PATCH] Migrate from `subtle` to `ctutils` See RustCrypto/meta#29 --- Cargo.lock | 6 +++--- ssh-cipher/Cargo.toml | 4 ++-- ssh-cipher/src/chacha20poly1305.rs | 2 +- ssh-encoding/Cargo.toml | 2 +- ssh-encoding/src/mpint.rs | 16 ++++++++-------- ssh-key/Cargo.toml | 4 ++-- ssh-key/src/ppk.rs | 2 +- ssh-key/src/private.rs | 4 ++-- ssh-key/src/private/dsa.rs | 6 +++--- ssh-key/src/private/ecdsa.rs | 6 +++--- ssh-key/src/private/ed25519.rs | 6 +++--- ssh-key/src/private/keypair.rs | 4 ++-- ssh-key/src/private/opaque.rs | 6 +++--- ssh-key/src/private/rsa.rs | 6 +++--- 14 files changed, 37 insertions(+), 37 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 096e9709..479facc3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -772,11 +772,11 @@ dependencies = [ "chacha20", "cipher", "ctr", + "ctutils", "des", "hex-literal", "poly1305", "ssh-encoding", - "subtle", "zeroize", ] @@ -796,11 +796,11 @@ dependencies = [ "base64ct", "bytes", "crypto-bigint", + "ctutils", "digest", "hex-literal", "pem-rfc7468", "ssh-derive", - "subtle", "zeroize", ] @@ -811,6 +811,7 @@ dependencies = [ "argon2", "bcrypt-pbkdf", "chacha20", + "ctutils", "dsa", "ed25519-dalek", "hex", @@ -828,7 +829,6 @@ dependencies = [ "signature", "ssh-cipher", "ssh-encoding", - "subtle", "zeroize", ] diff --git a/ssh-cipher/Cargo.toml b/ssh-cipher/Cargo.toml index c648fb7c..ce9ed80e 100644 --- a/ssh-cipher/Cargo.toml +++ b/ssh-cipher/Cargo.toml @@ -28,10 +28,10 @@ aes = { version = "0.9.0-rc.4", optional = true, default-features = false } aes-gcm = { version = "0.11.0-rc.3", optional = true, default-features = false, features = ["aes"] } cbc = { version = "0.2", optional = true } ctr = { version = "0.10.0-rc.4", optional = true, default-features = false } +ctutils = { version = "0.4", optional = true, default-features = false } chacha20 = { version = "0.10.0-rc.10", optional = true, default-features = false, features = ["cipher", "legacy"] } des = { version = "0.9.0-rc.3", optional = true, default-features = false } poly1305 = { version = "0.9.0-rc.6", optional = true, default-features = false } -subtle = { version = "2", optional = true, default-features = false } zeroize = { version = "1", optional = true, default-features = false } [dev-dependencies] @@ -41,7 +41,7 @@ hex-literal = "1" aes-cbc = ["dep:aes", "dep:cbc"] aes-ctr = ["dep:aes", "dep:ctr"] aes-gcm = ["dep:aead", "dep:aes", "dep:aes-gcm"] -chacha20poly1305 = ["dep:aead", "dep:chacha20", "dep:poly1305", "dep:subtle"] +chacha20poly1305 = ["dep:aead", "dep:chacha20", "dep:poly1305", "dep:ctutils"] tdes = ["dep:des", "dep:cbc"] zeroize = [ "dep:zeroize", diff --git a/ssh-cipher/src/chacha20poly1305.rs b/ssh-cipher/src/chacha20poly1305.rs index 3a5ba4af..261be64f 100644 --- a/ssh-cipher/src/chacha20poly1305.rs +++ b/ssh-cipher/src/chacha20poly1305.rs @@ -9,8 +9,8 @@ use aead::{ inout::InOutBuf, }; use cipher::{KeyIvInit, StreamCipher, StreamCipherSeek}; +use ctutils::CtEq; use poly1305::{Poly1305, universal_hash::UniversalHash}; -use subtle::ConstantTimeEq; #[cfg(feature = "zeroize")] use zeroize::{Zeroize, ZeroizeOnDrop}; diff --git a/ssh-encoding/Cargo.toml b/ssh-encoding/Cargo.toml index 70a5b320..e0536866 100644 --- a/ssh-encoding/Cargo.toml +++ b/ssh-encoding/Cargo.toml @@ -19,10 +19,10 @@ rust-version = "1.85" base64ct = { version = "1.8", optional = true } bigint = { package = "crypto-bigint", version = "0.7", optional = true, default-features = false, features = ["alloc"] } bytes = { version = "1", optional = true, default-features = false } +ctutils = { version = "0.4", optional = true, default-features = false } digest = { version = "0.11", optional = true, default-features = false } pem-rfc7468 = { version = "1", optional = true } ssh-derive = { version = "0.3.0-rc.0", optional = true } -subtle = { version = "2", optional = true, default-features = false } zeroize = { version = "1", optional = true, default-features = false } [dev-dependencies] diff --git a/ssh-encoding/src/mpint.rs b/ssh-encoding/src/mpint.rs index 91fb1b1d..7c479732 100644 --- a/ssh-encoding/src/mpint.rs +++ b/ssh-encoding/src/mpint.rs @@ -7,8 +7,8 @@ use core::fmt; #[cfg(feature = "bigint")] use crate::Uint; -#[cfg(feature = "subtle")] -use subtle::{Choice, ConstantTimeEq}; +#[cfg(feature = "ctutils")] +use ctutils::{Choice, CtEq}; #[cfg(any(feature = "bigint", feature = "zeroize"))] use zeroize::Zeroize; @@ -40,8 +40,8 @@ use zeroize::Zeroizing; /// | 80 | `00 00 00 02 00 80` /// |-1234 | `00 00 00 02 ed cc` /// | -deadbeef | `00 00 00 05 ff 21 52 41 11` -#[cfg_attr(not(feature = "subtle"), derive(Clone))] -#[cfg_attr(feature = "subtle", derive(Clone, Ord, PartialOrd))] // TODO: constant time (Partial)`Ord`? +#[cfg_attr(not(feature = "ctutils"), derive(Clone))] +#[cfg_attr(feature = "ctutils", derive(Clone, Ord, PartialOrd))] // TODO: constant time (Partial)`Ord`? pub struct Mpint { /// Inner big endian-serialized integer value inner: Box<[u8]>, @@ -112,17 +112,17 @@ impl AsRef<[u8]> for Mpint { } } -#[cfg(feature = "subtle")] -impl ConstantTimeEq for Mpint { +#[cfg(feature = "ctutils")] +impl CtEq for Mpint { fn ct_eq(&self, other: &Self) -> Choice { self.as_ref().ct_eq(other.as_ref()) } } -#[cfg(feature = "subtle")] +#[cfg(feature = "ctutils")] impl Eq for Mpint {} -#[cfg(feature = "subtle")] +#[cfg(feature = "ctutils")] impl PartialEq for Mpint { fn eq(&self, other: &Self) -> bool { self.ct_eq(other).into() diff --git a/ssh-key/Cargo.toml b/ssh-key/Cargo.toml index 67e41b99..b16dd3f2 100644 --- a/ssh-key/Cargo.toml +++ b/ssh-key/Cargo.toml @@ -27,12 +27,12 @@ features = ["zeroize"] [dependencies.encoding] version = "0.3.0-rc.8" package = "ssh-encoding" -features = ["base64", "digest", "pem", "subtle", "zeroize"] +features = ["base64", "digest", "pem", "ctutils", "zeroize"] [dependencies] +ctutils = { version = "0.4", default-features = false } sha2 = { version = "0.11", default-features = false } signature = { version = "3", default-features = false } -subtle = { version = "2", default-features = false } zeroize = { version = "1", default-features = false } # optional dependencies diff --git a/ssh-key/src/ppk.rs b/ssh-key/src/ppk.rs index d9e41d0f..682f3e42 100644 --- a/ssh-key/src/ppk.rs +++ b/ssh-key/src/ppk.rs @@ -17,9 +17,9 @@ use sha2::Sha256; use crate::private::KeypairData; use crate::public::KeyData; use crate::{Algorithm, Error, PublicKey}; +use ctutils::CtEq; use encoding::base64::{self, Base64, Encoding}; use encoding::{Decode, Encode, LabelError, Reader}; -use subtle::ConstantTimeEq; #[derive(Debug)] pub enum Kdf { diff --git a/ssh-key/src/private.rs b/ssh-key/src/private.rs index 674d0317..c3d2bc42 100644 --- a/ssh-key/src/private.rs +++ b/ssh-key/src/private.rs @@ -143,11 +143,11 @@ pub use self::sk::SkEcdsaSha2NistP256; use crate::{Algorithm, Cipher, Error, Fingerprint, HashAlg, Kdf, PublicKey, Result, public}; use cipher::Tag; use core::str; +use ctutils::{Choice, CtEq}; use encoding::{ CheckedSum, Decode, DecodePem, Encode, EncodePem, Reader, Writer, pem::{LineEnding, PemLabel}, }; -use subtle::{Choice, ConstantTimeEq}; #[cfg(feature = "alloc")] use { @@ -737,7 +737,7 @@ impl PrivateKey { } } -impl ConstantTimeEq for PrivateKey { +impl CtEq for PrivateKey { fn ct_eq(&self, other: &Self) -> Choice { // Constant-time with respect to private key data self.key_data.ct_eq(&other.key_data) diff --git a/ssh-key/src/private/dsa.rs b/ssh-key/src/private/dsa.rs index b443e7b6..85dc240d 100644 --- a/ssh-key/src/private/dsa.rs +++ b/ssh-key/src/private/dsa.rs @@ -2,8 +2,8 @@ use crate::{Error, Mpint, Result, public::DsaPublicKey}; use core::fmt; +use ctutils::{Choice, CtEq}; use encoding::{CheckedSum, Decode, Encode, Reader, Writer}; -use subtle::{Choice, ConstantTimeEq}; use zeroize::Zeroize; #[cfg(feature = "dsa")] @@ -51,7 +51,7 @@ impl AsRef<[u8]> for DsaPrivateKey { } } -impl ConstantTimeEq for DsaPrivateKey { +impl CtEq for DsaPrivateKey { fn ct_eq(&self, other: &Self) -> Choice { self.inner.ct_eq(&other.inner) } @@ -181,7 +181,7 @@ impl DsaKeypair { } } -impl ConstantTimeEq for DsaKeypair { +impl CtEq for DsaKeypair { fn ct_eq(&self, other: &Self) -> Choice { Choice::from((self.public == other.public) as u8) & self.private.ct_eq(&other.private) } diff --git a/ssh-key/src/private/ecdsa.rs b/ssh-key/src/private/ecdsa.rs index 8fb51fea..eebb32ed 100644 --- a/ssh-key/src/private/ecdsa.rs +++ b/ssh-key/src/private/ecdsa.rs @@ -2,9 +2,9 @@ use crate::{Algorithm, EcdsaCurve, Error, Result, public::EcdsaPublicKey}; use core::fmt; +use ctutils::{Choice, CtEq}; use encoding::{CheckedSum, Decode, Encode, Reader, Writer}; use sec1::consts::{U32, U48, U66}; -use subtle::{Choice, ConstantTimeEq}; use zeroize::Zeroize; #[cfg(feature = "rand_core")] @@ -106,7 +106,7 @@ impl AsRef<[u8; SIZE]> for EcdsaPrivateKey { } } -impl ConstantTimeEq for EcdsaPrivateKey { +impl CtEq for EcdsaPrivateKey { fn ct_eq(&self, other: &Self) -> Choice { self.as_ref().ct_eq(other.as_ref()) } @@ -282,7 +282,7 @@ impl EcdsaKeypair { } } -impl ConstantTimeEq for EcdsaKeypair { +impl CtEq for EcdsaKeypair { fn ct_eq(&self, other: &Self) -> Choice { let public_eq = Choice::from((EcdsaPublicKey::from(self) == EcdsaPublicKey::from(other)) as u8); diff --git a/ssh-key/src/private/ed25519.rs b/ssh-key/src/private/ed25519.rs index 06f8d0c4..08ef4e61 100644 --- a/ssh-key/src/private/ed25519.rs +++ b/ssh-key/src/private/ed25519.rs @@ -4,8 +4,8 @@ use crate::{Error, Result, public::Ed25519PublicKey}; use core::fmt; +use ctutils::{Choice, CtEq}; use encoding::{CheckedSum, Decode, Encode, Reader, Writer}; -use subtle::{Choice, ConstantTimeEq}; use zeroize::{Zeroize, Zeroizing}; #[cfg(feature = "rand_core")] @@ -45,7 +45,7 @@ impl AsRef<[u8; Self::BYTE_SIZE]> for Ed25519PrivateKey { } } -impl ConstantTimeEq for Ed25519PrivateKey { +impl CtEq for Ed25519PrivateKey { fn ct_eq(&self, other: &Self) -> Choice { self.as_ref().ct_eq(other.as_ref()) } @@ -192,7 +192,7 @@ impl Ed25519Keypair { } } -impl ConstantTimeEq for Ed25519Keypair { +impl CtEq for Ed25519Keypair { fn ct_eq(&self, other: &Self) -> Choice { Choice::from((self.public == other.public) as u8) & self.private.ct_eq(&other.private) } diff --git a/ssh-key/src/private/keypair.rs b/ssh-key/src/private/keypair.rs index ab82e6f4..f8a9d00d 100644 --- a/ssh-key/src/private/keypair.rs +++ b/ssh-key/src/private/keypair.rs @@ -2,8 +2,8 @@ use super::ed25519::Ed25519Keypair; use crate::{Algorithm, Error, Result, public}; +use ctutils::{Choice, CtEq}; use encoding::{CheckedSum, Decode, Encode, Reader, Writer}; -use subtle::{Choice, ConstantTimeEq}; #[cfg(feature = "alloc")] use { @@ -269,7 +269,7 @@ impl KeypairData { } } -impl ConstantTimeEq for KeypairData { +impl CtEq for KeypairData { fn ct_eq(&self, other: &Self) -> Choice { // Note: constant-time with respect to key *data* comparisons, not algorithms match (self, other) { diff --git a/ssh-key/src/private/opaque.rs b/ssh-key/src/private/opaque.rs index 28e74e2b..2e66aed4 100644 --- a/ssh-key/src/private/opaque.rs +++ b/ssh-key/src/private/opaque.rs @@ -14,8 +14,8 @@ use crate::{ }; use alloc::vec::Vec; use core::fmt; +use ctutils::{Choice, CtEq}; use encoding::{CheckedSum, Decode, Encode, Reader, Writer}; -use subtle::{Choice, ConstantTimeEq}; /// An opaque private key. /// @@ -118,7 +118,7 @@ impl Encode for OpaqueKeypair { } } -impl ConstantTimeEq for OpaqueKeypair { +impl CtEq for OpaqueKeypair { fn ct_eq(&self, other: &Self) -> Choice { Choice::from((self.public == other.public) as u8) & self.private.ct_eq(&other.private) } @@ -148,7 +148,7 @@ impl fmt::Debug for OpaqueKeypair { } } -impl ConstantTimeEq for OpaquePrivateKeyBytes { +impl CtEq for OpaquePrivateKeyBytes { fn ct_eq(&self, other: &Self) -> Choice { self.as_ref().ct_eq(other.as_ref()) } diff --git a/ssh-key/src/private/rsa.rs b/ssh-key/src/private/rsa.rs index 378cf175..ac3413c2 100644 --- a/ssh-key/src/private/rsa.rs +++ b/ssh-key/src/private/rsa.rs @@ -2,8 +2,8 @@ use crate::{Error, Mpint, Result, public::RsaPublicKey}; use core::fmt; +use ctutils::{Choice, CtEq}; use encoding::{CheckedSum, Decode, Encode, Reader, Writer}; -use subtle::{Choice, ConstantTimeEq}; use zeroize::Zeroize; #[cfg(feature = "rsa")] @@ -66,7 +66,7 @@ impl RsaPrivateKey { } } -impl ConstantTimeEq for RsaPrivateKey { +impl CtEq for RsaPrivateKey { fn ct_eq(&self, other: &Self) -> Choice { self.d.ct_eq(&other.d) & self.iqmp.ct_eq(&self.iqmp) @@ -163,7 +163,7 @@ impl RsaKeypair { } } -impl ConstantTimeEq for RsaKeypair { +impl CtEq for RsaKeypair { fn ct_eq(&self, other: &Self) -> Choice { Choice::from((self.public == other.public) as u8) & self.private.ct_eq(&other.private) }