rsotp 0.1.0

A Rust library for generating and verifying one-time passwords
Documentation
mod base32;
mod sha1;

pub struct OTP {
    secret: String,
    digits: u8,
    #[allow(dead_code)] digest: String,
    #[allow(dead_code)] name: String,
    #[allow(dead_code)] issuer: String,
}

impl OTP {
    pub fn new(secret: String) -> OTP {
        OTP {
            secret: secret,
            digits: 6,
            digest: String::from("sha1"),
            name: String::new(),
            issuer: String::new(),
        }
    }

    pub fn generate_otp(&self, input: u64) -> String {
        let h = sha1::hmac_sha1(&self.byte_secret(), &self.int_to_bytes(input));
        let offset = h[19] as usize & 0x0f;
        let code = (h[offset] as u32 & 0x7f) << 24 |
            (h[offset + 1] as u32 & 0xff) << 16 |
            (h[offset + 2] as u32 & 0xff) << 8 |
            (h[offset + 3] as u32 & 0xff);
        let code = code % 10u32.pow(self.digits as u32);
        let mut r = code.to_string();
        let pending = ['0' as u8; 10];
        let i = self.digits as usize - r.len();
        use std::str;
        r.insert_str(0, str::from_utf8(&pending[..i]).unwrap());
        r
    }

    fn byte_secret(&self) -> Vec<u8> {
        base32::decode(&self.secret)
    }

    fn int_to_bytes(&self, v: u64) -> [u8; 8] {
        let mut b = [0u8; 8];
        b[0] = (v >> 56) as u8;
        b[1] = (v >> 48) as u8;
        b[2] = (v >> 40) as u8;
        b[3] = (v >> 32) as u8;
        b[4] = (v >> 24) as u8;
        b[5] = (v >> 16) as u8;
        b[6] = (v >> 8) as u8;
        b[7] = v as u8;
        b
    }
}

pub struct TOTP {
    otp : OTP,
    interval: u64,
}

impl TOTP {

    pub fn new(secret: String) -> TOTP {
        TOTP {
            otp : OTP::new(secret),
            interval: 30,
        }
    }

    pub fn at(&self, timestamp: u64) -> String {
        self.otp.generate_otp(timestamp / self.interval)
    }

    pub fn now(&self) -> String {
        use std::time::SystemTime;
        self.at(
            SystemTime::now()
                .duration_since(SystemTime::UNIX_EPOCH)
                .unwrap().as_secs()
        )
    }

    pub fn verfiy(&self, s : &str) -> bool {
        self.now().eq(s)
    }
}

pub struct HOTP {
    otp : OTP,
    init_count: u64,
}

impl HOTP {

    pub fn new(secret: String) -> HOTP {
        HOTP {
            otp : OTP::new(secret),
            init_count: 0,
        }
    }

    pub fn at(&self, count: u64) -> String {
        self.otp.generate_otp(self.init_count + count)
    }

    pub fn verfiy(&self, s : &str, count : u64) -> bool {
        self.at(count).eq(&s)
    }
}

#[cfg(test)]
mod tests {
    #[test]
    fn test_otp() {
        let otp = super::OTP::new(String::from("3O75UXLUVM5NE3HA"));
        assert_eq!(otp.generate_otp(123), "276083");
        assert_eq!(otp.generate_otp(0), "463950");
        assert_eq!(otp.generate_otp(9), "003954");
    }

    #[test]
    fn test_totp() {
        let otp = super::TOTP::new(String::from("3O75UXLUVM5NE3HA"));
        println!("{}", otp.now())
    }
}