1
1
// Implementation of the HOTP standard according to RFC4226 by Tejas Mehta
2
2
3
- use crate :: util:: { get_code, hash_generic, MacDigest } ;
4
- use base32:: Alphabet ;
3
+ use crate :: util:: { base32_decode, get_code, hash_generic, MacDigest } ;
5
4
6
5
/// A HOTP Generator
7
6
///
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`].
11
9
///
12
10
/// # Example
13
11
/// See the top-level README for an example of HOTP usage
@@ -23,61 +21,85 @@ pub struct HOTP {
23
21
/// The secret key used in the HMAC process.
24
22
///
25
23
/// Often given as a Base32 key, which can be conveniently initialize using
26
- /// the [`HOTP::from_base32`] initializers
24
+ /// the [`HOTP::from_base32`] constructors.
27
25
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 ,
28
31
}
29
32
30
33
/// All initializer implementations for the [`HOTP`] struct.
31
-
32
34
impl HOTP {
33
35
/// Creates a new HOTP instance with a byte-array representation
34
- /// of the secret
36
+ /// of the secret and the number of digits.
35
37
///
36
38
/// 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 {
39
41
HOTP {
40
42
secret : secret. to_vec ( ) ,
43
+ digits,
41
44
}
42
45
}
43
46
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)
49
50
}
50
51
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.
52
53
///
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.
54
73
///
55
74
/// # 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 )
62
78
}
63
79
}
64
80
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
+ }
66
87
88
+ /// All otp generation methods for the [`HOTP`] struct.
67
89
impl HOTP {
68
- /// Generates and returns the HOTP value
90
+ /// Generates and returns the HOTP value.
69
91
///
70
- /// Uses the given counter value with the specified digit count
92
+ /// Uses the given counter value.
71
93
///
72
94
/// # Panics
73
95
/// 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 {
75
97
let hash = hash_generic ( & counter. to_be_bytes ( ) , & self . secret , & MacDigest :: SHA1 ) ;
76
98
let offset = ( hash[ hash. len ( ) - 1 ] & 0xf ) as usize ;
77
99
let bytes: [ u8 ; 4 ] = hash[ offset..offset + 4 ]
78
100
. try_into ( )
79
101
. expect ( "Failed byte get" ) ;
80
102
81
- get_code ( bytes, digits)
103
+ get_code ( bytes, self . digits )
82
104
}
83
105
}
0 commit comments