3131 * <p>Generates HMAC-based one-time passwords (HOTP) as specified in
3232 * <a href="https://tools.ietf.org/html/rfc4226">RFC 4226</a>.</p>
3333 *
34- * <p>{@code HmacOneTimePasswordGenerator} instances are thread-safe and may be shared and re-used across multiple
35- * threads.</p>
34+ * <p>{@code HmacOneTimePasswordGenerator} instances are thread-safe and may be shared between threads.</p>
3635 *
3736 * @author <a href="https://github.com/jchambers">Jon Chambers</a>
3837 */
3938public class HmacOneTimePasswordGenerator {
40- private final String algorithm ;
39+ private final Mac mac ;
4140 private final int passwordLength ;
4241
4342 private final int modDivisor ;
@@ -92,6 +91,8 @@ public HmacOneTimePasswordGenerator(final int passwordLength) throws NoSuchAlgor
9291 * @throws NoSuchAlgorithmException if the given algorithm is not supported by the underlying JRE
9392 */
9493 protected HmacOneTimePasswordGenerator (final int passwordLength , final String algorithm ) throws NoSuchAlgorithmException {
94+ this .mac = Mac .getInstance (algorithm );
95+
9596 switch (passwordLength ) {
9697 case 6 : {
9798 this .modDivisor = 1_000_000 ;
@@ -114,10 +115,6 @@ protected HmacOneTimePasswordGenerator(final int passwordLength, final String al
114115 }
115116
116117 this .passwordLength = passwordLength ;
117-
118- // Our purpose here is just to throw an exception immediately if the algorithm is bogus.
119- Mac .getInstance (algorithm );
120- this .algorithm = algorithm ;
121118 }
122119
123120 /**
@@ -131,21 +128,13 @@ protected HmacOneTimePasswordGenerator(final int passwordLength, final String al
131128 *
132129 * @throws InvalidKeyException if the given key is inappropriate for initializing the {@link Mac} for this generator
133130 */
134- public int generateOneTimePassword (final Key key , final long counter ) throws InvalidKeyException {
135- final Mac mac ;
136-
137- try {
138- mac = Mac .getInstance (this .algorithm );
139- mac .init (key );
140- } catch (final NoSuchAlgorithmException e ) {
141- // This should never happen since we verify that the algorithm is legit in the constructor.
142- throw new RuntimeException (e );
143- }
131+ public synchronized int generateOneTimePassword (final Key key , final long counter ) throws InvalidKeyException {
132+ this .mac .init (key );
144133
145134 final ByteBuffer buffer = ByteBuffer .allocate (8 );
146135 buffer .putLong (0 , counter );
147136
148- final byte [] hmac = mac .doFinal (buffer .array ());
137+ final byte [] hmac = this . mac .doFinal (buffer .array ());
149138 final int offset = hmac [hmac .length - 1 ] & 0x0f ;
150139
151140 for (int i = 0 ; i < 4 ; i ++) {
@@ -174,6 +163,6 @@ public int getPasswordLength() {
174163 * @return the name of the HMAC algorithm used by this generator
175164 */
176165 public String getAlgorithm () {
177- return this .algorithm ;
166+ return this .mac . getAlgorithm () ;
178167 }
179168}
0 commit comments