diff --git a/README.md b/README.md index 74f9bbd..689a66b 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ let totp = TOTP::new( 6, 1, 30, - "supersecret".to_owned().into_bytes(), + "supersecret", ); let time = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH).unwrap() @@ -55,7 +55,7 @@ let totp = TOTP::new( 6, 1, 30, - "supersecret".to_owned().into_bytes(), + "supersecret", ); let code = totp.get_qr("user@example.com", "my-org.com")?; println!("{}", code); diff --git a/src/lib.rs b/src/lib.rs index b0334dc..8544fdd 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ //! 6, //! 1, //! 30, -//! "supersecret".to_owned().into_bytes(), +//! "supersecret", //! ); //! let time = SystemTime::now() //! .duration_since(SystemTime::UNIX_EPOCH).unwrap() @@ -30,7 +30,7 @@ //! 6, //! 1, //! 30, -//! "supersecret".to_owned().into_bytes(), +//! "supersecret", //! ); //! let code = totp.get_qr("user@example.com", "my-org.com").unwrap(); //! println!("{}", code); @@ -39,8 +39,6 @@ #[cfg(feature = "serde_support")] use serde::{Deserialize, Serialize}; -use base32; - use byteorder::{BigEndian, ReadBytesExt}; use std::io::Cursor; @@ -67,7 +65,7 @@ pub enum Algorithm { /// TOTP holds informations as to how to generate an auth code and validate it. Its [secret](struct.TOTP.html#structfield.secret) field is sensitive data, treat it accordingly #[derive(Debug, Clone)] #[cfg_attr(feature = "serde_support", derive(Serialize, Deserialize))] -pub struct TOTP { +pub struct TOTP> { /// SHA-1 is the most widespread algorithm used, and for totp pursposes, SHA-1 hash collisions are [not a problem](https://tools.ietf.org/html/rfc4226#appendix-B.2) as HMAC-SHA-1 is not impacted. It's also the main one cited in [rfc-6238](https://tools.ietf.org/html/rfc6238#section-3) even though the [reference implementation](https://tools.ietf.org/html/rfc6238#appendix-A) permits the use of SHA-1, SHA-256 and SHA-512. Not all clients support other algorithms then SHA-1 pub algorithm: Algorithm, /// The number of digits composing the auth code. Per [rfc-4226](https://tools.ietf.org/html/rfc4226#section-5.3), this can oscilate between 6 and 8 digits @@ -77,18 +75,18 @@ pub struct TOTP { /// Duration in seconds of a step. The recommended value per [rfc-6238](https://tools.ietf.org/html/rfc6238#section-5.2) is 30 seconds pub 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 - pub secret: Vec, + pub secret: T, } -impl TOTP { +impl> TOTP { /// 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: Vec) -> TOTP { + pub fn new(algorithm: Algorithm, digits: usize, skew: u8, step: u64, secret: T) -> TOTP { TOTP { - algorithm: algorithm, - digits: digits, - skew: skew, - step: step, - secret: secret, + algorithm, + digits, + skew, + step, + secret, } } @@ -97,17 +95,17 @@ impl TOTP { let ctr = (time / self.step).to_be_bytes().to_vec(); match self.algorithm { Algorithm::SHA1 => { - let mut mac = HmacSha1::new_varkey(&self.secret).expect("no key"); + let mut mac = HmacSha1::new_varkey(self.secret.as_ref()).expect("no key"); mac.update(&ctr); mac.finalize().into_bytes().to_vec() } Algorithm::SHA256 => { - let mut mac = HmacSha256::new_varkey(&self.secret).expect("no key"); + let mut mac = HmacSha256::new_varkey(self.secret.as_ref()).expect("no key"); mac.update(&ctr); mac.finalize().into_bytes().to_vec() } Algorithm::SHA512 => { - let mut mac = HmacSha512::new_varkey(&self.secret).expect("no key"); + let mut mac = HmacSha512::new_varkey(self.secret.as_ref()).expect("no key"); mac.update(&ctr); mac.finalize().into_bytes().to_vec() } @@ -117,8 +115,8 @@ impl TOTP { /// Will generate a token according to the provided timestamp in seconds pub fn generate(&self, time: u64) -> String { let result: &[u8] = &self.sign(time); - let offset = (result.as_ref()[19] & 15) as usize; - let mut rdr = Cursor::new(result.as_ref()[offset..offset + 4].to_vec()); + let offset = (result[19] & 15) as usize; + let mut rdr = Cursor::new(result[offset..offset + 4].to_vec()); let result = rdr.read_u32::().unwrap() & 0x7fff_ffff; format!( "{1:00$}", @@ -141,7 +139,10 @@ impl TOTP { /// Will return the base32 representation of the secret, which might be useful when users want to manually add the secret to their authenticator pub fn get_secret_base32(&self) -> String { - base32::encode(base32::Alphabet::RFC4648 { padding: false }, &self.secret) + base32::encode( + base32::Alphabet::RFC4648 { padding: false }, + self.secret.as_ref(), + ) } /// Will generate a standard URL used to automatically add TOTP auths. Usually used with qr codes @@ -169,11 +170,7 @@ impl TOTP { /// /// It will also return an error in case it can't encode the qr into a png. This shouldn't happen unless either the qrcode library returns malformed data, or the image library doesn't encode the data correctly #[cfg(feature = "qr")] - pub fn get_qr( - &self, - label: &str, - issuer: &str, - ) -> Result> { + pub fn get_qr(&self, label: &str, issuer: &str) -> Result> { let url = self.get_url(label, issuer); let code = QrCode::new(&url)?; let mut vec = Vec::new(); @@ -195,57 +192,62 @@ mod tests { #[test] fn url_for_secret_matches() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret"); let url = totp.get_url("test_url", "totp-rs"); assert_eq!(url.as_str(), "otpauth://totp/test_url?secret=KRSXG5CTMVRXEZLU&issuer=totp-rs&digits=6&algorithm=SHA1"); } #[test] fn returns_base32() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret"); assert_eq!(totp.get_secret_base32().as_str(), "KRSXG5CTMVRXEZLU"); } #[test] fn generates_token() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret"); assert_eq!(totp.generate(1000).as_str(), "718996"); } #[test] fn generates_token_sha256() { - let totp = TOTP::new(Algorithm::SHA256, 6, 1, 1, String::from("TestSecret").into_bytes()); + let totp = TOTP::new(Algorithm::SHA256, 6, 1, 1, "TestSecret"); assert_eq!(totp.generate(1000).as_str(), "423657"); } #[test] fn generates_token_sha512() { - let totp = TOTP::new(Algorithm::SHA512, 6, 1, 1, String::from("TestSecret").into_bytes()); + let totp = TOTP::new(Algorithm::SHA512, 6, 1, 1, "TestSecret"); assert_eq!(totp.generate(1000).as_str(), "416767"); } #[test] fn checks_token() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret"); assert!(totp.check("718996", 1000)); } #[test] fn checks_token_with_skew() { - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); - assert!(totp.check("527544", 2000) && totp.check("712039", 2000) && totp.check("714250", 2000)); + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret"); + assert!( + totp.check("527544", 2000) && totp.check("712039", 2000) && totp.check("714250", 2000) + ); } #[test] #[cfg(feature = "qr")] fn generates_qr() { - use sha1::{Sha1, Digest}; + use sha1::{Digest, Sha1}; - let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, String::from("TestSecret").into_bytes()); + let totp = TOTP::new(Algorithm::SHA1, 6, 1, 1, "TestSecret"); let qr = totp.get_qr("test_url", "totp-rs").unwrap(); // Create hash from image let hash_digest = Sha1::digest(qr.as_bytes()); - assert_eq!(format!("{:x}", hash_digest).as_str(), "3abc0127e7a2b1013fb25c97ef14422c1fe9e878"); + assert_eq!( + format!("{:x}", hash_digest).as_str(), + "3abc0127e7a2b1013fb25c97ef14422c1fe9e878" + ); } }