Skip to content

Commit d6a9c47

Browse files
committed
Reuse a local byte array instead of repeatedly instantiating new ones.
1 parent 6864c71 commit d6a9c47

File tree

1 file changed

+29
-15
lines changed

1 file changed

+29
-15
lines changed

src/main/java/com/eatthepath/otp/HmacOneTimePasswordGenerator.java

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,12 @@
2020

2121
package com.eatthepath.otp;
2222

23-
import java.nio.ByteBuffer;
23+
import javax.crypto.Mac;
24+
import javax.crypto.ShortBufferException;
2425
import java.security.InvalidKeyException;
2526
import java.security.Key;
2627
import java.security.NoSuchAlgorithmException;
2728

28-
import javax.crypto.Mac;
29-
3029
/**
3130
* <p>Generates HMAC-based one-time passwords (HOTP) as specified in
3231
* <a href="https://tools.ietf.org/html/rfc4226">RFC&nbsp;4226</a>.</p>
@@ -39,6 +38,7 @@ public class HmacOneTimePasswordGenerator {
3938
private final Mac mac;
4039
private final int passwordLength;
4140

41+
private final byte[] buffer;
4242
private final int modDivisor;
4343

4444
/**
@@ -115,6 +115,9 @@ protected HmacOneTimePasswordGenerator(final int passwordLength, final String al
115115
}
116116

117117
this.passwordLength = passwordLength;
118+
119+
// We need at least 8 bytes to store the 64-bit counter value
120+
this.buffer = new byte[Math.max(8, this.mac.getMacLength())];
118121
}
119122

120123
/**
@@ -131,21 +134,32 @@ protected HmacOneTimePasswordGenerator(final int passwordLength, final String al
131134
public synchronized int generateOneTimePassword(final Key key, final long counter) throws InvalidKeyException {
132135
this.mac.init(key);
133136

134-
final ByteBuffer buffer = ByteBuffer.allocate(8);
135-
buffer.putLong(0, counter);
136-
137-
final byte[] hmac = this.mac.doFinal(buffer.array());
138-
final int offset = hmac[hmac.length - 1] & 0x0f;
139-
140-
for (int i = 0; i < 4; i++) {
141-
// Note that we're re-using the first four bytes of the buffer here; we just ignore the latter four from
142-
// here on out.
143-
buffer.put(i, hmac[i + offset]);
137+
this.buffer[0] = (byte) ((counter & 0xff00000000000000L) >>> 56);
138+
this.buffer[1] = (byte) ((counter & 0x00ff000000000000L) >>> 48);
139+
this.buffer[2] = (byte) ((counter & 0x0000ff0000000000L) >>> 40);
140+
this.buffer[3] = (byte) ((counter & 0x000000ff00000000L) >>> 32);
141+
this.buffer[4] = (byte) ((counter & 0x00000000ff000000L) >>> 24);
142+
this.buffer[5] = (byte) ((counter & 0x0000000000ff0000L) >>> 16);
143+
this.buffer[6] = (byte) ((counter & 0x000000000000ff00L) >>> 8);
144+
this.buffer[7] = (byte) (counter & 0x00000000000000ffL);
145+
146+
this.mac.update(this.buffer, 0, 8);
147+
148+
try {
149+
this.mac.doFinal(this.buffer, 0);
150+
} catch (final ShortBufferException e) {
151+
// We allocated the buffer to (at least) match the size of the MAC length at construction time, so this
152+
// should never happen.
153+
throw new RuntimeException(e);
144154
}
145155

146-
final int hotp = buffer.getInt(0) & 0x7fffffff;
156+
final int offset = this.buffer[this.mac.getMacLength() - 1] & 0x0f;
147157

148-
return hotp % this.modDivisor;
158+
return ((this.buffer[offset] & 0x7f) << 24 |
159+
(this.buffer[offset + 1] & 0xff) << 16 |
160+
(this.buffer[offset + 2] & 0xff) << 8 |
161+
(this.buffer[offset + 3] & 0xff)) %
162+
this.modDivisor;
149163
}
150164

151165
/**

0 commit comments

Comments
 (0)