Merge pull request #15 from constantoine/constant_time_eq
Add constant-time token comparison and partialEq trait
This commit is contained in:
commit
7ecdf7d440
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "totp-rs"
|
name = "totp-rs"
|
||||||
version = "1.0.0"
|
version = "1.1.0"
|
||||||
authors = ["Cleo Rebert <cleo.rebert@gmail.com>"]
|
authors = ["Cleo Rebert <cleo.rebert@gmail.com>"]
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
|
@ -25,6 +25,7 @@ sha2 = "~0.10.2"
|
||||||
sha-1 = "~0.10.0"
|
sha-1 = "~0.10.0"
|
||||||
hmac = "~0.12.1"
|
hmac = "~0.12.1"
|
||||||
base32 = "~0.4"
|
base32 = "~0.4"
|
||||||
|
constant_time_eq = "~0.2.1"
|
||||||
qrcode = { version = "~0.12", optional = true }
|
qrcode = { version = "~0.12", optional = true }
|
||||||
image = { version = "~0.23.14", optional = true}
|
image = { version = "~0.23.14", optional = true}
|
||||||
base64 = { version = "~0.13", optional = true }
|
base64 = { version = "~0.13", optional = true }
|
||||||
|
|
|
@ -17,7 +17,7 @@ With optional feature "serde_support", library-defined types will be Deserialize
|
||||||
Add it to your `Cargo.toml`:
|
Add it to your `Cargo.toml`:
|
||||||
```toml
|
```toml
|
||||||
[dependencies]
|
[dependencies]
|
||||||
totp-rs = "~1.0"
|
totp-rs = "~1.1"
|
||||||
```
|
```
|
||||||
You can then do something like:
|
You can then do something like:
|
||||||
```Rust
|
```Rust
|
||||||
|
@ -45,7 +45,7 @@ println!("{}", token);
|
||||||
Add it to your `Cargo.toml`:
|
Add it to your `Cargo.toml`:
|
||||||
```toml
|
```toml
|
||||||
[dependencies.totp-rs]
|
[dependencies.totp-rs]
|
||||||
version = "~1.0"
|
version = "~1.1"
|
||||||
features = ["qr"]
|
features = ["qr"]
|
||||||
```
|
```
|
||||||
You can then do something like:
|
You can then do something like:
|
||||||
|
@ -67,6 +67,6 @@ println!("{}", code);
|
||||||
Add it to your `Cargo.toml`:
|
Add it to your `Cargo.toml`:
|
||||||
```toml
|
```toml
|
||||||
[dependencies.totp-rs]
|
[dependencies.totp-rs]
|
||||||
version = "~1.0"
|
version = "~1.1"
|
||||||
features = ["serde_support"]
|
features = ["serde_support"]
|
||||||
```
|
```
|
||||||
|
|
67
src/lib.rs
67
src/lib.rs
|
@ -44,6 +44,8 @@
|
||||||
//! # }
|
//! # }
|
||||||
//! ```
|
//! ```
|
||||||
|
|
||||||
|
use constant_time_eq::constant_time_eq;
|
||||||
|
|
||||||
#[cfg(feature = "serde_support")]
|
#[cfg(feature = "serde_support")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -59,7 +61,7 @@ type HmacSha256 = hmac::Hmac<sha2::Sha256>;
|
||||||
type HmacSha512 = hmac::Hmac<sha2::Sha512>;
|
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)
|
/// 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))]
|
#[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))]
|
||||||
pub enum Algorithm {
|
pub enum Algorithm {
|
||||||
SHA1,
|
SHA1,
|
||||||
|
@ -117,6 +119,24 @@ pub struct TOTP<T = Vec<u8>> {
|
||||||
pub secret: T,
|
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> {
|
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
|
/// 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> {
|
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);
|
let basestep = time / self.step - (self.skew as u64);
|
||||||
for i in 0..self.skew * 2 + 1 {
|
for i in 0..self.skew * 2 + 1 {
|
||||||
let step_time = (basestep + (i as u64)) * (self.step as u64);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -208,6 +229,48 @@ impl<T: AsRef<[u8]>> TOTP<T> {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
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]
|
#[test]
|
||||||
fn url_for_secret_matches_sha1() {
|
fn url_for_secret_matches_sha1() {
|
||||||
let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
|
let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret");
|
||||||
|
|
Loading…
Reference in New Issue