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
@@ -22,61 +20,85 @@ pub struct HOTP {
22
20
/// The secret key used in the HMAC process.
23
21
///
24
22
/// Often given as a Base32 key, which can be conveniently initialize using
25
- /// the [`HOTP::from_base32`] initializers
23
+ /// the [`HOTP::from_base32`] constructors.
26
24
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 ,
27
30
}
28
31
29
32
/// All initializer implementations for the [`HOTP`] struct.
30
-
31
33
impl HOTP {
32
34
/// Creates a new HOTP instance with a byte-array representation
33
- /// of the secret
35
+ /// of the secret and the number of digits.
34
36
///
35
37
/// 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 {
38
40
HOTP {
39
41
secret : secret. to_vec ( ) ,
42
+ digits,
40
43
}
41
44
}
42
45
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)
48
49
}
49
50
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.
51
52
///
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.
53
72
///
54
73
/// # 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 )
61
77
}
62
78
}
63
79
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
+ }
65
86
87
+ /// All otp generation methods for the [`HOTP`] struct.
66
88
impl HOTP {
67
- /// Generates and returns the HOTP value
89
+ /// Generates and returns the HOTP value.
68
90
///
69
- /// Uses the given counter value with the specified digit count
91
+ /// Uses the given counter value.
70
92
///
71
93
/// # Panics
72
94
/// 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 {
74
96
let hash = hash_generic ( & counter. to_be_bytes ( ) , & self . secret , & MacDigest :: SHA1 ) ;
75
97
let offset = ( hash[ hash. len ( ) - 1 ] & 0xf ) as usize ;
76
98
let bytes: [ u8 ; 4 ] = hash[ offset..offset + 4 ]
77
99
. try_into ( )
78
100
. expect ( "Failed byte get" ) ;
79
101
80
- get_code ( bytes, digits)
102
+ get_code ( bytes, self . digits )
81
103
}
82
104
}
0 commit comments