Compare commits
9 Commits
c890731092
...
542ed7c97f
Author | SHA1 | Date |
---|---|---|
Michael Pfaff | 542ed7c97f | |
Cléo REBERT | 81c773f471 | |
Cléo REBERT | 606078ee1d | |
Cléo REBERT | 360b4e7611 | |
Cléo Rebert | f0b8934763 | |
Cléo REBERT | f7d0f136d1 | |
Cléo REBERT | 08c24dae96 | |
Cléo REBERT | c2ba6d190b | |
Cléo REBERT | e4e055dedd |
25
CHANGELOG.md
25
CHANGELOG.md
|
@ -1,3 +1,26 @@
|
|||
# [5.0.1](https://github.com/constantoine/totp-rs/releases/tag/v5.0.1) (31/03/2023)
|
||||
### Changes
|
||||
- Normalize dependencies specifications since cargo uses range dependency by default.
|
||||
|
||||
### Special thanks
|
||||
* [@bestia-dev](https://github.com/bestia-dev) for pointing out discrepancies in my dependency requirements.
|
||||
|
||||
# [5.0](https://github.com/constantoine/totp-rs/releases/tag/v5.0) (28/03/2023)
|
||||
### Breaking changes.
|
||||
- MSRV has been set to Rust `1.61`.
|
||||
- Removed `SecretParseError::Utf8Error`.
|
||||
|
||||
### Changes
|
||||
- Updated `base64` to `0.21`.
|
||||
- Updated `url` to `2.3`.
|
||||
- Updated `zeroize` to `1.6`.
|
||||
|
||||
### Note
|
||||
This major release is a very small one, and is mostly here to respect semver. No major change was done, it is mostly maintenance and cleanup.
|
||||
|
||||
### Special thanks
|
||||
* [@bestia-dev](https://github.com/bestia-dev) for opening #55.
|
||||
|
||||
# [4.2](https://github.com/constantoine/totp-rs/releases/tag/v4.2) (14/01/2023)
|
||||
### Changes
|
||||
- Optionnals parameters in generated URLs are no longer present if their're the default value. (#49)
|
||||
|
@ -22,7 +45,7 @@
|
|||
- Default features have been set to none.
|
||||
|
||||
### Changes
|
||||
- MSRV have been set to Rust `1.59`.
|
||||
- MSRV has been set to Rust `1.59`.
|
||||
- Updated `base64` crate to `0.20`.
|
||||
|
||||
### Breaking changes
|
||||
|
|
28
Cargo.toml
28
Cargo.toml
|
@ -1,8 +1,8 @@
|
|||
[package]
|
||||
name = "totp-rs"
|
||||
version = "4.2.0"
|
||||
version = "5.0.1"
|
||||
authors = ["Cleo Rebert <cleo.rebert@gmail.com>"]
|
||||
rust-version = "1.59"
|
||||
rust-version = "1.61"
|
||||
edition = "2021"
|
||||
readme = "README.md"
|
||||
license = "MIT"
|
||||
|
@ -25,15 +25,15 @@ steam = []
|
|||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"], optional = true }
|
||||
sha2 = "~0.10.2"
|
||||
sha1 = "~0.10.5"
|
||||
hmac = "~0.12.1"
|
||||
base32 = "~0.4"
|
||||
urlencoding = { version = "^2.1.0", optional = true}
|
||||
url = { version = "^2.2.2", optional = true }
|
||||
constant_time_eq = "0.2.4"
|
||||
qrcodegen = { version = "~1.8", optional = true }
|
||||
image = { version = "~0.24.2", features = ["png"], optional = true, default-features = false}
|
||||
base64 = { version = "~0.20", optional = true }
|
||||
rand = { version = "~0.8.5", features = ["std_rng", "std"], optional = true, default-features = false }
|
||||
zeroize = { version = "1.5.7", features = ["alloc", "derive"], optional = true }
|
||||
sha2 = "0.10"
|
||||
sha1 = "0.10"
|
||||
hmac = "0.12"
|
||||
base32 = "0.4"
|
||||
urlencoding = { version = "2.1", optional = true}
|
||||
url = { version = "2.3", optional = true }
|
||||
constant_time_eq = "0.2"
|
||||
qrcodegen = { version = "1.8", optional = true }
|
||||
image = { version = "0.24", features = ["png"], optional = true, default-features = false}
|
||||
base64 = { version = "0.21", optional = true }
|
||||
rand = { version = "0.8", features = ["std_rng", "std"], optional = true, default-features = false }
|
||||
zeroize = { version = "1.6", features = ["alloc", "derive"], optional = true }
|
|
@ -1,16 +1,18 @@
|
|||
#[cfg(all(feature = "gen_secret", feature = "otpauth"))]
|
||||
use totp_rs::{Algorithm, Secret, TOTP};
|
||||
use totp_rs::{Algorithm, Secret, TOTP, LabeledTOTP};
|
||||
|
||||
#[cfg(all(feature = "gen_secret", feature = "otpauth"))]
|
||||
fn main() {
|
||||
let secret = Secret::generate_secret();
|
||||
|
||||
let totp = TOTP::new(
|
||||
Algorithm::SHA1,
|
||||
6,
|
||||
1,
|
||||
30,
|
||||
secret.to_bytes().unwrap(),
|
||||
let totp = LabeledTOTP::new(
|
||||
TOTP::new(
|
||||
Algorithm::SHA1,
|
||||
6,
|
||||
1,
|
||||
30,
|
||||
secret.to_bytes().unwrap(),
|
||||
).unwrap(),
|
||||
None,
|
||||
"account".to_string(),
|
||||
)
|
||||
|
|
|
@ -1,24 +1,8 @@
|
|||
use totp_rs::{Rfc6238, TOTP};
|
||||
|
||||
#[cfg(feature = "otpauth")]
|
||||
fn main() {
|
||||
let mut rfc = Rfc6238::with_defaults("totp-sercret-123".as_bytes().to_vec()).unwrap();
|
||||
|
||||
// optional, set digits, issuer, account_name
|
||||
rfc.digits(8).unwrap();
|
||||
rfc.issuer("issuer".to_string());
|
||||
rfc.account_name("user-account".to_string());
|
||||
|
||||
// create a TOTP from rfc
|
||||
let totp = TOTP::from_rfc6238(rfc).unwrap();
|
||||
let code = totp.generate_current().unwrap();
|
||||
println!("code: {}", code);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn main() {
|
||||
let mut rfc = Rfc6238::with_defaults("totp-sercret-123".into()).unwrap();
|
||||
|
||||
// optional, set digits, issuer, account_name
|
||||
rfc.digits(8).unwrap();
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
use totp_rs::{Algorithm, Secret, TOTP};
|
||||
use totp_rs::{Algorithm, Secret, TOTP, LabeledTOTP};
|
||||
|
||||
#[cfg(feature = "otpauth")]
|
||||
fn main() {
|
||||
// create TOTP from base32 secret
|
||||
let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG"));
|
||||
|
@ -10,9 +9,12 @@ fn main() {
|
|||
1,
|
||||
30,
|
||||
secret_b32.to_bytes().unwrap(),
|
||||
Some("issuer".to_string()),
|
||||
"user-account".to_string(),
|
||||
)
|
||||
.and_then(|totp| LabeledTOTP::new(
|
||||
totp,
|
||||
"issuer".to_string(),
|
||||
"user-account".to_string(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
|
@ -37,9 +39,12 @@ fn main() {
|
|||
1,
|
||||
30,
|
||||
secret_raw.to_bytes().unwrap(),
|
||||
Some("issuer".to_string()),
|
||||
"user-account".to_string(),
|
||||
)
|
||||
.and_then(|totp| LabeledTOTP::new(
|
||||
totp,
|
||||
"issuer".to_string(),
|
||||
"user-account".to_string(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
println!("raw {} ; base32 {}", secret_raw, secret_raw.to_encoded());
|
||||
|
@ -48,34 +53,3 @@ fn main() {
|
|||
totp_raw.generate_current().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn main() {
|
||||
// create TOTP from base32 secret
|
||||
let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG"));
|
||||
let totp_b32 = TOTP::new(Algorithm::SHA1, 6, 1, 30, secret_b32.to_bytes().unwrap()).unwrap();
|
||||
|
||||
println!(
|
||||
"base32 {} ; raw {}",
|
||||
secret_b32,
|
||||
secret_b32.to_raw().unwrap()
|
||||
);
|
||||
println!(
|
||||
"code from base32:\t{}",
|
||||
totp_b32.generate_current().unwrap()
|
||||
);
|
||||
|
||||
// create TOTP from raw binary value
|
||||
let secret = [
|
||||
0x70, 0x6c, 0x61, 0x69, 0x6e, 0x2d, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2d, 0x73, 0x65,
|
||||
0x63, 0x72, 0x65, 0x74, 0x2d, 0x31, 0x32, 0x33,
|
||||
];
|
||||
let secret_raw = Secret::Raw(secret.to_vec());
|
||||
let totp_raw = TOTP::new(Algorithm::SHA1, 6, 1, 30, secret_raw.to_bytes().unwrap()).unwrap();
|
||||
|
||||
println!("raw {} ; base32 {}", secret_raw, secret_raw.to_encoded());
|
||||
println!(
|
||||
"code from raw secret:\t{}",
|
||||
totp_raw.generate_current().unwrap()
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,15 +1,12 @@
|
|||
#[cfg(feature = "steam")]
|
||||
use totp_rs::{Secret, TOTP};
|
||||
use totp_rs::{Secret, LabeledTOTP};
|
||||
|
||||
#[cfg(feature = "steam")]
|
||||
#[cfg(feature = "otpauth")]
|
||||
fn main() {
|
||||
// create TOTP from base32 secret
|
||||
let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG"));
|
||||
let totp_b32 = TOTP::new_steam(
|
||||
secret_b32.to_bytes().unwrap(),
|
||||
"user-account".to_string(),
|
||||
);
|
||||
let totp_b32 = LabeledTOTP::new_steam(secret_b32.to_bytes().unwrap(), "user-account".to_string());
|
||||
|
||||
println!(
|
||||
"base32 {} ; raw {}",
|
||||
|
@ -27,7 +24,7 @@ fn main() {
|
|||
fn main() {
|
||||
// create TOTP from base32 secret
|
||||
let secret_b32 = Secret::Encoded(String::from("OBWGC2LOFVZXI4TJNZTS243FMNZGK5BNGEZDG"));
|
||||
let totp_b32 = TOTP::new_steam(secret_b32.to_bytes().unwrap());
|
||||
let totp_b32 = LabeledTOTP::new_steam(secret_b32.to_bytes().unwrap());
|
||||
|
||||
println!(
|
||||
"base32 {} ; raw {}",
|
||||
|
@ -41,6 +38,4 @@ fn main() {
|
|||
}
|
||||
|
||||
#[cfg(not(feature = "steam"))]
|
||||
fn main() {
|
||||
|
||||
}
|
||||
fn main() {}
|
||||
|
|
|
@ -1,21 +1,5 @@
|
|||
use totp_rs::{Algorithm, TOTP};
|
||||
use totp_rs::{Algorithm, TOTP, LabeledTOTP};
|
||||
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn main() {
|
||||
let totp = TOTP::new(Algorithm::SHA1, 6, 1, 30, "my-secret".as_bytes().to_vec()).unwrap();
|
||||
|
||||
loop {
|
||||
println!(
|
||||
"code {}\t ttl {}\t valid until: {}",
|
||||
totp.generate_current().unwrap(),
|
||||
totp.ttl().unwrap(),
|
||||
totp.next_step_current().unwrap()
|
||||
);
|
||||
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "otpauth")]
|
||||
fn main() {
|
||||
let totp = TOTP::new(
|
||||
Algorithm::SHA1,
|
||||
|
@ -23,9 +7,12 @@ fn main() {
|
|||
1,
|
||||
30,
|
||||
"my-secret".as_bytes().to_vec(),
|
||||
Some("Github".to_string()),
|
||||
"constantoine@github.com".to_string(),
|
||||
)
|
||||
.and_then(|totp| LabeledTOTP::new(
|
||||
totp,
|
||||
"Github".to_string(),
|
||||
"constantoine@github.com".to_string(),
|
||||
))
|
||||
.unwrap();
|
||||
|
||||
loop {
|
||||
|
|
|
@ -1,45 +1,31 @@
|
|||
#[cfg(feature = "steam")]
|
||||
use crate::{Algorithm, TOTP};
|
||||
use crate::{Algorithm, TOTP, LabeledTOTP};
|
||||
|
||||
#[cfg(feature = "steam")]
|
||||
impl TOTP {
|
||||
#[cfg(feature = "otpauth")]
|
||||
impl LabeledTOTP {
|
||||
/// Will create a new instance of TOTP using the Steam algorithm with given parameters. See [the doc](struct.TOTP.html#fields) for reference as to how to choose those values
|
||||
///
|
||||
/// # Description
|
||||
/// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)`
|
||||
///
|
||||
/// ```rust
|
||||
/// use totp_rs::{Secret, TOTP};
|
||||
/// use totp_rs::{Secret, LabeledTOTP};
|
||||
/// let secret = Secret::Encoded("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".into());
|
||||
/// let totp = TOTP::new_steam(secret.to_bytes().unwrap(), "username".into());
|
||||
/// let totp = LabeledTOTP::new_steam(secret.to_bytes().unwrap(), "username".into());
|
||||
/// ```
|
||||
pub fn new_steam(secret: Vec<u8>, account_name: String) -> TOTP {
|
||||
pub fn new_steam(secret: Vec<u8>, account_name: String) -> Self {
|
||||
Self::new_unchecked(
|
||||
Algorithm::Steam,
|
||||
5,
|
||||
1,
|
||||
30,
|
||||
secret,
|
||||
Some("Steam".into()),
|
||||
TOTP::new_unchecked(
|
||||
Algorithm::Steam,
|
||||
5,
|
||||
1,
|
||||
30,
|
||||
secret,
|
||||
),
|
||||
"Steam".to_owned(),
|
||||
account_name,
|
||||
)
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
/// Will create a new instance of TOTP using the Steam algorithm with given parameters. See [the doc](struct.TOTP.html#fields) for reference as to how to choose those values
|
||||
///
|
||||
/// # Description
|
||||
/// * `secret`: expect a non-encoded value, to pass in base32 string use `Secret::Encoded(String)`
|
||||
///
|
||||
/// ```rust
|
||||
/// use totp_rs::{Secret, TOTP};
|
||||
/// let secret = Secret::Encoded("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567".to_string());
|
||||
/// let totp = TOTP::new_steam(secret.to_bytes().unwrap());
|
||||
/// ```
|
||||
pub fn new_steam(secret: Vec<u8>) -> TOTP {
|
||||
Self::new_unchecked(Algorithm::Steam, 5, 1, 30, secret)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(all(test, feature = "steam"))]
|
||||
|
@ -50,7 +36,7 @@ mod test {
|
|||
#[test]
|
||||
#[cfg(feature = "otpauth")]
|
||||
fn get_url_steam() {
|
||||
let totp = TOTP::new_steam("TestSecretSuperSecret".into(), "constantoine".into());
|
||||
let totp = LabeledTOTP::new_steam("TestSecretSuperSecret".into(), "constantoine".into());
|
||||
let url = totp.get_url();
|
||||
assert_eq!(url.as_str(), "otpauth://steam/Steam:constantoine?secret=KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ&digits=5&algorithm=SHA1&issuer=Steam");
|
||||
}
|
||||
|
|
843
src/lib.rs
843
src/lib.rs
File diff suppressed because it is too large
Load Diff
119
src/rfc.rs
119
src/rfc.rs
|
@ -1,4 +1,5 @@
|
|||
use crate::Algorithm;
|
||||
use crate::LabeledTOTP;
|
||||
use crate::TotpUrlError;
|
||||
use crate::TOTP;
|
||||
|
||||
|
@ -77,15 +78,6 @@ pub struct Rfc6238 {
|
|||
step: u64,
|
||||
/// As per [rfc-4226](https://tools.ietf.org/html/rfc4226#section-4) the secret should come from a strong source, most likely a CSPRNG. It should be at least 128 bits, but 160 are recommended
|
||||
secret: Vec<u8>,
|
||||
#[cfg(feature = "otpauth")]
|
||||
/// The "Github" part of "Github:constantoine@github.com". Must not contain a colon `:`
|
||||
/// For example, the name of your service/website.
|
||||
/// Not mandatory, but strongly recommended!
|
||||
issuer: Option<String>,
|
||||
#[cfg(feature = "otpauth")]
|
||||
/// The "constantoine@github.com" part of "Github:constantoine@github.com". Must not contain a colon `:`
|
||||
/// For example, the name of your user's account.
|
||||
account_name: String,
|
||||
}
|
||||
|
||||
impl Rfc6238 {
|
||||
|
@ -96,27 +88,6 @@ impl Rfc6238 {
|
|||
/// will return a [Rfc6238Error](enum.Rfc6238Error.html) when
|
||||
/// - `digits` is lower than 6 or higher than 8
|
||||
/// - `secret` is smaller than 128 bits (16 characters)
|
||||
#[cfg(feature = "otpauth")]
|
||||
pub fn new(
|
||||
digits: usize,
|
||||
secret: Vec<u8>,
|
||||
issuer: Option<String>,
|
||||
account_name: String,
|
||||
) -> Result<Rfc6238, Rfc6238Error> {
|
||||
assert_digits(&digits)?;
|
||||
assert_secret_length(secret.as_ref())?;
|
||||
|
||||
Ok(Rfc6238 {
|
||||
algorithm: Algorithm::SHA1,
|
||||
digits,
|
||||
skew: 1,
|
||||
step: 30,
|
||||
secret,
|
||||
issuer,
|
||||
account_name,
|
||||
})
|
||||
}
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
pub fn new(digits: usize, secret: Vec<u8>) -> Result<Rfc6238, Rfc6238Error> {
|
||||
assert_digits(&digits)?;
|
||||
assert_secret_length(secret.as_ref())?;
|
||||
|
@ -138,12 +109,6 @@ impl Rfc6238 {
|
|||
/// will return a [Rfc6238Error](enum.Rfc6238Error.html) when
|
||||
/// - `digits` is lower than 6 or higher than 8
|
||||
/// - `secret` is smaller than 128 bits (16 characters)
|
||||
#[cfg(feature = "otpauth")]
|
||||
pub fn with_defaults(secret: Vec<u8>) -> Result<Rfc6238, Rfc6238Error> {
|
||||
Rfc6238::new(6, secret, Some("".to_string()), "".to_string())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
pub fn with_defaults(secret: Vec<u8>) -> Result<Rfc6238, Rfc6238Error> {
|
||||
Rfc6238::new(6, secret)
|
||||
}
|
||||
|
@ -154,21 +119,8 @@ impl Rfc6238 {
|
|||
self.digits = value;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[cfg(feature = "otpauth")]
|
||||
/// Set the `issuer`
|
||||
pub fn issuer(&mut self, value: String) {
|
||||
self.issuer = Some(value);
|
||||
}
|
||||
|
||||
#[cfg(feature = "otpauth")]
|
||||
/// Set the `account_name`
|
||||
pub fn account_name(&mut self, value: String) {
|
||||
self.account_name = value;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
impl TryFrom<Rfc6238> for TOTP {
|
||||
type Error = TotpUrlError;
|
||||
|
||||
|
@ -178,8 +130,7 @@ impl TryFrom<Rfc6238> for TOTP {
|
|||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "otpauth")]
|
||||
impl TryFrom<Rfc6238> for TOTP {
|
||||
impl TryFrom<Rfc6238> for LabeledTOTP {
|
||||
type Error = TotpUrlError;
|
||||
|
||||
/// Try to create a [TOTP](struct.TOTP.html) from a [Rfc6238](struct.Rfc6238.html) config
|
||||
|
@ -190,35 +141,21 @@ impl TryFrom<Rfc6238> for TOTP {
|
|||
rfc.skew,
|
||||
rfc.step,
|
||||
rfc.secret,
|
||||
rfc.issuer,
|
||||
rfc.account_name,
|
||||
)
|
||||
).map(LabeledTOTP::from)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
#[cfg(feature = "otpauth")]
|
||||
use crate::TotpUrlError;
|
||||
|
||||
use super::{Rfc6238, TOTP};
|
||||
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
use super::Rfc6238Error;
|
||||
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
use crate::Secret;
|
||||
|
||||
const GOOD_SECRET: &str = "01234567890123456789";
|
||||
#[cfg(feature = "otpauth")]
|
||||
const ISSUER: Option<&str> = None;
|
||||
#[cfg(feature = "otpauth")]
|
||||
const ACCOUNT: &str = "valid-account";
|
||||
#[cfg(feature = "otpauth")]
|
||||
const INVALID_ACCOUNT: &str = ":invalid-account";
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn new_rfc_digits() {
|
||||
for x in 0..=20 {
|
||||
let rfc = Rfc6238::new(x, GOOD_SECRET.into());
|
||||
|
@ -232,7 +169,6 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn new_rfc_secret() {
|
||||
let mut secret = String::from("");
|
||||
for _ in 0..=20 {
|
||||
|
@ -255,7 +191,6 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn rfc_to_totp_ok() {
|
||||
let rfc = Rfc6238::new(8, GOOD_SECRET.into()).unwrap();
|
||||
let totp = TOTP::try_from(rfc);
|
||||
|
@ -269,7 +204,6 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn rfc_to_totp_ok_2() {
|
||||
let rfc = Rfc6238::with_defaults(
|
||||
Secret::Encoded("KRSXG5CTMVRXEZLUKN2XAZLSKNSWG4TFOQ".to_string())
|
||||
|
@ -287,51 +221,6 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "otpauth")]
|
||||
fn rfc_to_totp_fail() {
|
||||
let rfc = Rfc6238::new(
|
||||
8,
|
||||
GOOD_SECRET.as_bytes().to_vec(),
|
||||
ISSUER.map(str::to_string),
|
||||
INVALID_ACCOUNT.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let totp = TOTP::try_from(rfc);
|
||||
assert!(totp.is_err());
|
||||
assert!(matches!(totp.unwrap_err(), TotpUrlError::AccountName(_)))
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "otpauth")]
|
||||
fn rfc_to_totp_ok() {
|
||||
let rfc = Rfc6238::new(
|
||||
8,
|
||||
GOOD_SECRET.as_bytes().to_vec(),
|
||||
ISSUER.map(str::to_string),
|
||||
ACCOUNT.to_string(),
|
||||
)
|
||||
.unwrap();
|
||||
let totp = TOTP::try_from(rfc);
|
||||
assert!(totp.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "otpauth")]
|
||||
fn rfc_with_default_set_values() {
|
||||
let mut rfc = Rfc6238::with_defaults(GOOD_SECRET.as_bytes().to_vec()).unwrap();
|
||||
let ok = rfc.digits(8);
|
||||
assert!(ok.is_ok());
|
||||
assert_eq!(rfc.account_name, "");
|
||||
assert_eq!(rfc.issuer, Some("".to_string()));
|
||||
rfc.issuer("Github".to_string());
|
||||
rfc.account_name("constantoine".to_string());
|
||||
assert_eq!(rfc.account_name, "constantoine");
|
||||
assert_eq!(rfc.issuer, Some("Github".to_string()));
|
||||
assert_eq!(rfc.digits, 8)
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn rfc_with_default_set_values() {
|
||||
let mut rfc = Rfc6238::with_defaults(GOOD_SECRET.as_bytes().to_vec()).unwrap();
|
||||
let fail = rfc.digits(4);
|
||||
|
@ -344,7 +233,6 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn digits_error() {
|
||||
let error = crate::Rfc6238Error::InvalidDigits(9);
|
||||
assert_eq!(
|
||||
|
@ -354,7 +242,6 @@ mod tests {
|
|||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(not(feature = "otpauth"))]
|
||||
fn secret_length_error() {
|
||||
let error = Rfc6238Error::SecretTooSmall(120);
|
||||
assert_eq!(
|
||||
|
|
|
@ -78,16 +78,24 @@
|
|||
//! ```
|
||||
|
||||
use base32::{self, Alphabet};
|
||||
use std::string::FromUtf8Error;
|
||||
|
||||
use constant_time_eq::constant_time_eq;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum SecretParseError {
|
||||
ParseBase32,
|
||||
Utf8Error(FromUtf8Error),
|
||||
}
|
||||
|
||||
impl std::fmt::Display for SecretParseError {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
SecretParseError::ParseBase32 => write!(f, "Could not decode base32 secret."),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::error::Error for Secret {}
|
||||
|
||||
#[derive(Debug, Clone, Eq)]
|
||||
#[cfg_attr(feature = "zeroize", derive(zeroize::Zeroize, zeroize::ZeroizeOnDrop))]
|
||||
pub enum Secret {
|
||||
|
|
Loading…
Reference in New Issue