1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use core::{mem, ptr};

use crate::Algorithm;

enum HmacKey {
    Sha1(lhash::HmacKey::<lhash::Sha1>),
    Sha256(lhash::HmacKey::<lhash::Sha256>),
    Sha512(lhash::HmacKey::<lhash::Sha512>),
}

#[derive(Copy, Clone)]
enum HmacOutput {
    Sha1(<lhash::Sha1 as lhash::Digest>::OutputType),
    Sha256(<lhash::Sha256 as lhash::Digest>::OutputType),
    Sha512(<lhash::Sha512 as lhash::Digest>::OutputType),
}

impl AsRef<[u8]> for HmacOutput {
    #[inline]
    fn as_ref(&self) -> &[u8] {
        match self {
            HmacOutput::Sha1(ref output) => output.as_ref(),
            HmacOutput::Sha256(ref output) => output.as_ref(),
            HmacOutput::Sha512(ref output) => output.as_ref(),
        }
    }
}

///HMAC based OTP algorithm that uses simple counter as input.
pub struct HOTP {
    ///HMAC key generated using `algorithm` and `secret`
    key: HmacKey
}

impl HOTP {
    #[inline]
    ///Initializes algorithm using provided `algorithm` and `secret`
    ///
    ///- `algorithm` - Generally acceptable are HMAC based on `sha-1`, `sha-256` and `sha-512`
    ///- `secret` - Raw bytes used to derive HMAC key. User is responsible to decode it before
    ///passing.
    pub fn new<T: AsRef<[u8]>>(algorithm: Algorithm, secret: T) -> Self {
        let secret = secret.as_ref();
        debug_assert_ne!(secret.len(), 0);

        Self {
            key: match algorithm {
                Algorithm::SHA1 => HmacKey::Sha1(lhash::HmacKey::new(secret)),
                Algorithm::SHA256 => HmacKey::Sha256(lhash::HmacKey::new(secret)),
                Algorithm::SHA512 => HmacKey::Sha512(lhash::HmacKey::new(secret)),
            }
        }
    }

    #[inline]
    ///Signs provided `counter` value using stored HMAC key.
    pub fn sign(&self, counter: u64) -> impl AsRef<[u8]> + Clone + Copy {
        let counter = counter.to_be_bytes();

        match self.key {
            HmacKey::Sha1(ref key) => HmacOutput::Sha1(key.sign(&counter)),
            HmacKey::Sha256(ref key) => HmacOutput::Sha256(key.sign(&counter)),
            HmacKey::Sha512(ref key) => HmacOutput::Sha512(key.sign(&counter)),
        }
    }

    ///Generates password as number from provided `counter` value with length of `digits`.
    ///
    ///Note that in this case you must handle missing padding yourself.
    pub fn generate_num(&self, counter: u64, digits: u8) -> u32 {
        const BASE: u32 = 10;

        let sign = self.sign(counter);
        let sign = sign.as_ref();

        let offset = (sign[sign.len() - 1] & 15) as usize;
        debug_assert!(offset + mem::size_of::<u32>() < sign.len());

        let snum = unsafe {
            let mut snum = mem::MaybeUninit::<u32>::uninit();
            ptr::copy_nonoverlapping(sign.as_ptr().add(offset), snum.as_mut_ptr() as _, 4);
            snum.assume_init().to_be() & 0x7fff_ffff
        };

        snum % BASE.pow(digits as u32)
    }

    unsafe fn generate_to_ptr(&self, counter: u64, dest: *mut u8, len: usize) {
        use core::fmt::{self, Write};

        struct WriteBuffer(*mut u8, usize);
        impl Write for WriteBuffer {
            #[inline]
            fn write_str(&mut self, text: &str) -> fmt::Result {
                //write! can call it multiple times hence remember
                let written_len = text.len();
                debug_assert!(written_len <= self.1);

                unsafe {
                    ptr::copy_nonoverlapping(text.as_ptr(), self.0, written_len);
                    self.0 = self.0.add(written_len);
                }
                self.1 = self.1 - written_len;

                Ok(())
            }
        }

        debug_assert_ne!(len, 0);
        debug_assert!(len <= u8::max_value() as _);
        debug_assert!(!dest.is_null());

        let snum = self.generate_num(counter, len as u8);

        let mut buffer = WriteBuffer(dest, len);
        let _ = write!(buffer, "{:0width$}", snum, width = len);
    }

    #[inline]
    ///Generates password based on provided `counter` value and writes it into provided `dest`.
    ///
    ///This always writes `dest.as_ref().len()`.
    ///
    ///Recommended buffer length is be within `6..8`
    pub fn generate_to<T: AsMut<[u8]>>(&self, counter: u64, mut dest: T) {
        let dest = dest.as_mut();
        unsafe {
            self.generate_to_ptr(counter, dest.as_mut_ptr(), dest.len())
        }
    }

    ///Checks whether provided `token` corresponds to `counter`.
    pub fn verify(&self, token: &str, counter: u64) -> bool {
        debug_assert!(token.len() <= u8::max_value() as _);

        let expected = match u32::from_str_radix(token, 10) {
            Ok(expected) => expected,
            Err(_) => return false,
        };

        self.generate_num(counter, token.len() as u8) == expected
    }
}