11// Implementation of the HOTP standard according to RFC4226 by Tejas Mehta
22
3- use crate :: util:: { get_code, hash_generic, MacDigest } ;
4- use base32:: Alphabet ;
3+ use crate :: util:: { base32_decode, get_code, hash_generic, MacDigest } ;
54
65/// A HOTP Generator
76///
8- /// Follows the specification listed in [RFC4226]. Needs a secret on
9- /// initialization, with other single generation-specific items being
10- /// provided when [`HOTP::get_otp`] is called.
7+ /// Follows the specification listed in [RFC4226]. Needs a secret and a number of digits on initialization.
8+ /// The HOTP can then be generated using [`HOTP::get_otp`].
119///
1210/// # Example
1311/// See the top-level README for an example of HOTP usage
@@ -23,61 +21,85 @@ pub struct HOTP {
2321 /// The secret key used in the HMAC process.
2422 ///
2523 /// Often given as a Base32 key, which can be conveniently initialize using
26- /// the [`HOTP::from_base32`] initializers
24+ /// the [`HOTP::from_base32`] constructors.
2725 secret : Vec < u8 > ,
26+
27+ /// The number of digits of the code generated.
28+ ///
29+ /// This value defaults to 6 if not specified in a constructor.
30+ digits : u32 ,
2831}
2932
3033/// All initializer implementations for the [`HOTP`] struct.
31-
3234impl HOTP {
3335 /// Creates a new HOTP instance with a byte-array representation
34- /// of the secret
36+ /// of the secret and the number of digits.
3537 ///
3638 /// Since only SHA1 was specified in the reference implementation and
37- /// RFC specification, there's no need to initialize with a digest object
38- pub fn new ( secret : & [ u8 ] ) -> Self {
39+ /// RFC specification, there's no need to initialize with a digest object.
40+ pub fn new ( secret : & [ u8 ] , digits : u32 ) -> Self {
3941 HOTP {
4042 secret : secret. to_vec ( ) ,
43+ digits,
4144 }
4245 }
4346
44- /// Creates a new HOTP instance from a utf8-encoded string secret
45- ///
46- /// Internally calls [`HOTP::new`] with the string's byte representation
47- pub fn from_utf8 ( secret : & str ) -> Self {
48- HOTP :: new ( secret. as_bytes ( ) )
47+ /// Creates a new HOTP instance from an utf8-encoded string secret and the number of digits.
48+ pub fn new_from_utf8 ( secret : & str , digits : u32 ) -> Self {
49+ HOTP :: new ( secret. as_bytes ( ) , digits)
4950 }
5051
51- /// Creates a new HOTP instance from a base32-encoded string secret
52+ /// Creates a new HOTP instance from a base32-encoded string secret and the number of digits.
5253 ///
53- /// Internally calls [`HOTP::new`] after decoding the string
54+ /// # Panics
55+ /// This method panics if the provided string is not correctly base32 encoded.
56+ pub fn new_from_base32 ( secret : & str , digits : u32 ) -> Self {
57+ let decoded = base32_decode ( secret) . expect ( "Failed to decode base32 string" ) ;
58+ HOTP :: new ( & decoded, digits)
59+ }
60+
61+ /// Creates a new HOTP instance from a byte-array representation of the secret and
62+ /// a default number of 6 digits.
63+ pub fn default_from_secret ( secret : & [ u8 ] ) -> Self {
64+ HOTP :: new ( secret, 6 )
65+ }
66+
67+ /// Creates a new HOTP instance from an utf8-encoded string secret and a default number of 6 digits.
68+ pub fn default_from_utf8 ( secret : & str ) -> Self {
69+ HOTP :: new_from_utf8 ( secret, 6 )
70+ }
71+
72+ /// Creates a new HOTP instance from a base32-encoded string secret and a default number of 6 digits.
5473 ///
5574 /// # Panics
56- /// This method panics if the provided string is not correctly base32
57- /// encoded.
58- pub fn from_base32 ( secret : & str ) -> Self {
59- let decoded = base32:: decode ( Alphabet :: RFC4648 { padding : false } , secret)
60- . expect ( "Failed to decode base32 string" ) ;
61- HOTP :: new ( & decoded)
75+ /// This method panics if the provided string is not correctly base32 encoded.
76+ pub fn default_from_base32 ( secret : & str ) -> Self {
77+ HOTP :: new_from_base32 ( secret, 6 )
6278 }
6379}
6480
65- /// All otp generation methods for the [`HOTP`] struct.
81+ impl HOTP {
82+ /// Gets the number of digits of the code.
83+ pub fn get_digits ( & self ) -> u32 {
84+ self . digits
85+ }
86+ }
6687
88+ /// All otp generation methods for the [`HOTP`] struct.
6789impl HOTP {
68- /// Generates and returns the HOTP value
90+ /// Generates and returns the HOTP value.
6991 ///
70- /// Uses the given counter value with the specified digit count
92+ /// Uses the given counter value.
7193 ///
7294 /// # Panics
7395 /// This method panics if the hash's secret is incorrectly given.
74- pub fn get_otp ( & self , counter : u64 , digits : u32 ) -> u32 {
96+ pub fn get_otp ( & self , counter : u64 ) -> u32 {
7597 let hash = hash_generic ( & counter. to_be_bytes ( ) , & self . secret , & MacDigest :: SHA1 ) ;
7698 let offset = ( hash[ hash. len ( ) - 1 ] & 0xf ) as usize ;
7799 let bytes: [ u8 ; 4 ] = hash[ offset..offset + 4 ]
78100 . try_into ( )
79101 . expect ( "Failed byte get" ) ;
80102
81- get_code ( bytes, digits)
103+ get_code ( bytes, self . digits )
82104 }
83105}
0 commit comments