VYPR
Moderate severityNVD Advisory· Published May 20, 2022· Updated Apr 23, 2025

Observable Timing Discrepancy in totp-rs

CVE-2022-29185

Description

totp-rs is a Rust library that permits the creation of 2FA authentification tokens per time-based one-time password (TOTP). Prior to version 1.1.0, token comparison was not constant time, and could theorically be used to guess value of an TOTP token, and thus reuse it in the same time window. The attacker would have to know the password beforehand nonetheless. Starting with patched version 1.1.0, the library uses constant-time comparison. There are currently no known workarounds.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

totp-rs Rust library before 1.1.0 used non-constant-time token comparison, enabling potential token reuse via timing side-channel.

Vulnerability

CVE-2022-29185 affects the totp-rs Rust library, versions prior to 1.1.0. The library implements time-based one-time password (TOTP) authentication as per RFC 6238. The vulnerability is that token comparison inside the check() function was not performed in constant time, meaning the comparison could leak timing information. An attacker could theorically use this side-channel to guess a valid TOTP token and reuse it within the same time window [1][2]. The fix was introduced in version 1.1.0, where the library uses constant_time_eq for comparisons [3][4].

Exploitation

To exploit this timing side-channel, an attacker would need to know the user's password beforehand (since TOTP is a second factor) and would also need to be able to observe the response times of token verification requests. The attacker would then send crafted TOTP tokens and measure the response timing to narrow down the correct value. This requires network proximity or the ability to make many time-precise requests, and assumes the attacker can intercept or observe the verification process [1].

Impact

Successful exploitation could allow an attacker to obtain a valid TOTP token for the current time window and thus bypass the second-factor authentication. This would compromise the confidentiality and integrity of the authentication mechanism, allowing session hijacking or unauthorized access to accounts protected by TOTP [1].

Mitigation

The vulnerability is fixed in totp-rs version 1.1.0, released on 2022-05-20. Users should update to this version or later [3][4]. The library now uses the constant_time_eq crate to ensure all token comparisons are performed in constant time, eliminating the timing side-channel. No workarounds are currently known [1].

AI Insight generated on May 21, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
totp-rscrates.io
< 1.1.01.1.0

Affected products

3

Patches

1
1f1e1a6fe722

Add constant-time token comparison and partialEq trait

https://github.com/constantoine/totp-rsCleo RebertApr 24, 2022via ghsa
3 files changed · +70 6
  • Cargo.toml+2 1 modified
    @@ -1,6 +1,6 @@
     [package]
     name = "totp-rs"
    -version = "1.0.0"
    +version = "1.1.0"
     authors = ["Cleo Rebert <cleo.rebert@gmail.com>"]
     edition = "2021"
     readme = "README.md"
    @@ -25,6 +25,7 @@ sha2 = "~0.10.2"
     sha-1 = "~0.10.0"
     hmac = "~0.12.1"
     base32 = "~0.4"
    +constant_time_eq = "~0.2.1"
     qrcode = { version = "~0.12", optional = true }
     image = { version = "~0.23.14", optional = true}
     base64 = { version = "~0.13", optional = true }
    
  • README.md+3 3 modified
    @@ -17,7 +17,7 @@ With optional feature "serde_support", library-defined types will be Deserialize
     Add it to your `Cargo.toml`:
     ```toml
     [dependencies]
    -totp-rs = "~1.0"
    +totp-rs = "~1.1"
     ```
     You can then do something like:
     ```Rust
    @@ -45,7 +45,7 @@ println!("{}", token);
     Add it to your `Cargo.toml`:
     ```toml
     [dependencies.totp-rs]
    -version = "~1.0"
    +version = "~1.1"
     features = ["qr"]
     ```
     You can then do something like:
    @@ -67,6 +67,6 @@ println!("{}", code);
     Add it to your `Cargo.toml`:
     ```toml
     [dependencies.totp-rs]
    -version = "~1.0"
    +version = "~1.1"
     features = ["serde_support"]
     ```
    
  • src/lib.rs+65 2 modified
    @@ -44,6 +44,8 @@
     //! # }
     //! ```
     
    +use constant_time_eq::constant_time_eq;
    +
     #[cfg(feature = "serde_support")]
     use serde::{Deserialize, Serialize};
     
    @@ -59,7 +61,7 @@ type HmacSha256 = hmac::Hmac<sha2::Sha256>;
     type HmacSha512 = hmac::Hmac<sha2::Sha512>;
     
     /// Algorithm enum holds the three standards algorithms for TOTP as per the [reference implementation](https://tools.ietf.org/html/rfc6238#appendix-A)
    -#[derive(Debug, Copy, Clone)]
    +#[derive(Debug, Copy, Clone, Eq, PartialEq)]
     #[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
     pub enum Algorithm {
         SHA1,
    @@ -117,6 +119,24 @@ pub struct TOTP<T = Vec<u8>> {
         pub secret: T,
     }
     
    +impl <T: AsRef<[u8]>> PartialEq for TOTP<T> {
    +    fn eq(&self, other: &Self) -> bool {
    +        if self.algorithm != other.algorithm {
    +            return false;
    +        }
    +        if self.digits != other.digits {
    +            return false;
    +        }
    +        if self.skew != other.skew {
    +            return false;
    +        }
    +        if self.step != other.step {
    +            return false;
    +        }
    +        constant_time_eq(self.secret.as_ref(), other.secret.as_ref())
    +    }
    +}
    +
     impl<T: AsRef<[u8]>> TOTP<T> {
         /// Will create a new instance of TOTP with given parameters. See [the doc](struct.TOTP.html#fields) for reference as to how to choose those values
         pub fn new(algorithm: Algorithm, digits: usize, skew: u8, step: u64, secret: T) -> TOTP<T> {
    @@ -154,7 +174,8 @@ impl<T: AsRef<[u8]>> TOTP<T> {
             let basestep = time / self.step - (self.skew as u64);
             for i in 0..self.skew * 2 + 1 {
                 let step_time = (basestep + (i as u64)) * (self.step as u64);
    -            if self.generate(step_time) == token {
    +            
    +            if constant_time_eq(self.generate(step_time).as_bytes(), token.as_bytes()) {
                     return true;
                 }
             }
    @@ -208,6 +229,48 @@ impl<T: AsRef<[u8]>> TOTP<T> {
     mod tests {
         use super::*;
     
    +    #[test]
    +    fn comparison_ok() {
    +        let reference = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
    +        let test = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
    +        assert_eq!(reference, test);
    +    }
    +
    +    #[test]
    +    fn comparison_different_algo() {
    +        let reference = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
    +        let test = TOTP::new(Algorithm::SHA256, 6, 1, 1, "TestSecret");
    +        assert_ne!(reference, test);
    +    }
    +
    +    #[test]
    +    fn comparison_different_digits() {
    +        let reference = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
    +        let test = TOTP::new(Algorithm::SHA1, 8, 1, 1, "TestSecret");
    +        assert_ne!(reference, test);
    +    }
    +
    +    #[test]
    +    fn comparison_different_skew() {
    +        let reference = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
    +        let test = TOTP::new(Algorithm::SHA1, 6, 0, 1, "TestSecret");
    +        assert_ne!(reference, test);
    +    }
    +
    +    #[test]
    +    fn comparison_different_step() {
    +        let reference = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
    +        let test = TOTP::new(Algorithm::SHA1, 6, 1, 30, "TestSecret");
    +        assert_ne!(reference, test);
    +    }
    +
    +    #[test]
    +    fn comparison_different_secret() {
    +        let reference = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
    +        let test = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecretL");
    +        assert_ne!(reference, test);
    +    }
    +
         #[test]
         fn url_for_secret_matches_sha1() {
             let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.