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
@@ -22,61 +20,85 @@ pub struct HOTP {
2220 /// The secret key used in the HMAC process.
2321 ///
2422 /// Often given as a Base32 key, which can be conveniently initialize using
25- /// the [`HOTP::from_base32`] initializers
23+ /// the [`HOTP::from_base32`] constructors.
2624 secret : Vec < u8 > ,
25+
26+ /// The number of digits of the code generated.
27+ ///
28+ /// This value defaults to 6 if not specified in a constructor.
29+ digits : u32 ,
2730}
2831
2932/// All initializer implementations for the [`HOTP`] struct.
30-
3133impl HOTP {
3234 /// Creates a new HOTP instance with a byte-array representation
33- /// of the secret
35+ /// of the secret and the number of digits.
3436 ///
3537 /// Since only SHA1 was specified in the reference implementation and
36- /// RFC specification, there's no need to initialize with a digest object
37- pub fn new ( secret : & [ u8 ] ) -> Self {
38+ /// RFC specification, there's no need to initialize with a digest object.
39+ pub fn new ( secret : & [ u8 ] , digits : u32 ) -> Self {
3840 HOTP {
3941 secret : secret. to_vec ( ) ,
42+ digits,
4043 }
4144 }
4245
43- /// Creates a new HOTP instance from a utf8-encoded string secret
44- ///
45- /// Internally calls [`HOTP::new`] with the string's byte representation
46- pub fn from_utf8 ( secret : & str ) -> Self {
47- HOTP :: new ( secret. as_bytes ( ) )
46+ /// Creates a new HOTP instance from an utf8-encoded string secret and the number of digits.
47+ pub fn new_from_utf8 ( secret : & str , digits : u32 ) -> Self {
48+ HOTP :: new ( secret. as_bytes ( ) , digits)
4849 }
4950
50- /// Creates a new HOTP instance from a base32-encoded string secret
51+ /// Creates a new HOTP instance from a base32-encoded string secret and the number of digits.
5152 ///
52- /// Internally calls [`HOTP::new`] after decoding the string
53+ /// # Panics
54+ /// This method panics if the provided string is not correctly base32 encoded.
55+ pub fn new_from_base32 ( secret : & str , digits : u32 ) -> Self {
56+ let decoded = base32_decode ( secret) . expect ( "Failed to decode base32 string" ) ;
57+ HOTP :: new ( & decoded, digits)
58+ }
59+
60+ /// Creates a new HOTP instance from a byte-array representation of the secret and
61+ /// a default number of 6 digits.
62+ pub fn default_from_secret ( secret : & [ u8 ] ) -> Self {
63+ HOTP :: new ( secret, 6 )
64+ }
65+
66+ /// Creates a new HOTP instance from an utf8-encoded string secret and a default number of 6 digits.
67+ pub fn default_from_utf8 ( secret : & str ) -> Self {
68+ HOTP :: new_from_utf8 ( secret, 6 )
69+ }
70+
71+ /// Creates a new HOTP instance from a base32-encoded string secret and a default number of 6 digits.
5372 ///
5473 /// # Panics
55- /// This method panics if the provided string is not correctly base32
56- /// encoded.
57- pub fn from_base32 ( secret : & str ) -> Self {
58- let decoded = base32:: decode ( Alphabet :: RFC4648 { padding : false } , secret)
59- . expect ( "Failed to decode base32 string" ) ;
60- HOTP :: new ( & decoded)
74+ /// This method panics if the provided string is not correctly base32 encoded.
75+ pub fn default_from_base32 ( secret : & str ) -> Self {
76+ HOTP :: new_from_base32 ( secret, 6 )
6177 }
6278}
6379
64- /// All otp generation methods for the [`HOTP`] struct.
80+ impl HOTP {
81+ /// Gets the number of digits of the code.
82+ pub fn get_digits ( & self ) -> u32 {
83+ self . digits
84+ }
85+ }
6586
87+ /// All otp generation methods for the [`HOTP`] struct.
6688impl HOTP {
67- /// Generates and returns the HOTP value
89+ /// Generates and returns the HOTP value.
6890 ///
69- /// Uses the given counter value with the specified digit count
91+ /// Uses the given counter value.
7092 ///
7193 /// # Panics
7294 /// This method panics if the hash's secret is incorrectly given.
73- pub fn get_otp ( & self , counter : u64 , digits : u32 ) -> u32 {
95+ pub fn get_otp ( & self , counter : u64 ) -> u32 {
7496 let hash = hash_generic ( & counter. to_be_bytes ( ) , & self . secret , & MacDigest :: SHA1 ) ;
7597 let offset = ( hash[ hash. len ( ) - 1 ] & 0xf ) as usize ;
7698 let bytes: [ u8 ; 4 ] = hash[ offset..offset + 4 ]
7799 . try_into ( )
78100 . expect ( "Failed byte get" ) ;
79101
80- get_code ( bytes, digits)
102+ get_code ( bytes, self . digits )
81103 }
82104}
0 commit comments