Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGener
To actually generate time-based one-time passwords, you'll need a key and a timestamp. Secure key management is beyond the scope of this document; for the purposes of an example, though, we'll generate a random key:

```java
final Key key;
final SecretKey key;
{
final KeyGenerator keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@
import org.openjdk.jmh.annotations.State;

import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;

@State(Scope.Benchmark)
public class HmacOneTimePasswordGeneratorBenchmark {

private HmacOneTimePasswordGenerator hotp;
private Key key;
private SecretKey key;

private int counter = 0;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@
package com.eatthepath.otp;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.ShortBufferException;
import java.nio.ByteBuffer;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;

Expand Down Expand Up @@ -153,7 +153,7 @@ private static String getFormatString(final int passwordLength) {
*
* @throws InvalidKeyException if the given key is inappropriate for initializing the {@link Mac} for this generator
*/
public int generateOneTimePassword(final Key key, final long counter) throws InvalidKeyException {
public int generateOneTimePassword(final SecretKey key, final long counter) throws InvalidKeyException {
final Mac mac = getMac();
final ByteBuffer buffer = ByteBuffer.allocate(mac.getMacLength());

Expand Down Expand Up @@ -196,7 +196,7 @@ private Mac getMac() {
*
* @see Locale#getDefault()
*/
public String generateOneTimePasswordString(final Key key, final long counter) throws InvalidKeyException {
public String generateOneTimePasswordString(final SecretKey key, final long counter) throws InvalidKeyException {
return this.generateOneTimePasswordString(key, counter, Locale.getDefault());
}

Expand All @@ -212,7 +212,7 @@ public String generateOneTimePasswordString(final Key key, final long counter) t
*
* @throws InvalidKeyException if the given key is inappropriate for initializing the {@link Mac} for this generator
*/
public String generateOneTimePasswordString(final Key key, final long counter, final Locale locale) throws InvalidKeyException {
public String generateOneTimePasswordString(final SecretKey key, final long counter, final Locale locale) throws InvalidKeyException {
return this.formatOneTimePassword(generateOneTimePassword(key, counter), locale);
}

Expand All @@ -235,7 +235,7 @@ public String generateOneTimePasswordString(final Key key, final long counter, f
*
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4226#section-7">HOTP: An HMAC-Based One-Time Password Algorithm (RFC 4226) - Security Requirements</a>
*/
public boolean validateOneTimePassword(final Key key, final long counter, final String oneTimePassword) throws InvalidKeyException {
public boolean validateOneTimePassword(final SecretKey key, final long counter, final String oneTimePassword) throws InvalidKeyException {
if (oneTimePassword == null) {
throw new NullPointerException("One-time password must not be null");
}
Expand Down Expand Up @@ -274,7 +274,7 @@ public boolean validateOneTimePassword(final Key key, final long counter, final
*
* @see <a href="https://datatracker.ietf.org/doc/html/rfc4226#section-7">HOTP: An HMAC-Based One-Time Password Algorithm (RFC 4226) - Security Requirements</a>
*/
public boolean validateOneTimePassword(final Key key, final long counter, final int oneTimePassword) throws InvalidKeyException {
public boolean validateOneTimePassword(final SecretKey key, final long counter, final int oneTimePassword) throws InvalidKeyException {
return generateOneTimePassword(key, counter) == oneTimePassword;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@
package com.eatthepath.otp;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.Instant;
Expand All @@ -34,7 +34,7 @@
* <a href="https://tools.ietf.org/html/rfc6238">RFC&nbsp;6238</a>.</p>
*
* <p>{@code TimeBasedOneTimePasswordGenerator} instances are thread-safe and may be shared between threads. Note that
* the {@link #generateOneTimePassword(Key, Instant)} method (and its relatives) are {@code synchronized}; in
* the {@link #generateOneTimePassword(SecretKey, Instant)} method (and its relatives) are {@code synchronized}; in
* multi-threaded applications that make heavy use of a shared {@code TimeBasedOneTimePasswordGenerator} instance,
* synchronization may become a performance bottleneck. In that case, callers may benefit from using one
* {@code TimeBasedOneTimePasswordGenerator} instance per thread (for example, with a {@link ThreadLocal}).</p>
Expand Down Expand Up @@ -158,7 +158,7 @@ private static Duration validateTimeStep(final Duration timeStep) {
*
* @throws InvalidKeyException if the given key is inappropriate for initializing the {@link Mac} for this generator
*/
public int generateOneTimePassword(final Key key, final Instant timestamp) throws InvalidKeyException {
public int generateOneTimePassword(final SecretKey key, final Instant timestamp) throws InvalidKeyException {
return this.hotp.generateOneTimePassword(key, getCounterValue(timestamp));
}

Expand All @@ -175,7 +175,7 @@ public int generateOneTimePassword(final Key key, final Instant timestamp) throw
*
* @see Locale#getDefault()
*/
public String generateOneTimePasswordString(final Key key, final Instant timestamp) throws InvalidKeyException {
public String generateOneTimePasswordString(final SecretKey key, final Instant timestamp) throws InvalidKeyException {
return this.generateOneTimePasswordString(key, timestamp, Locale.getDefault());
}

Expand All @@ -190,7 +190,7 @@ public String generateOneTimePasswordString(final Key key, final Instant timesta
*
* @throws InvalidKeyException if the given key is inappropriate for initializing the {@link Mac} for this generator
*/
public String generateOneTimePasswordString(final Key key, final Instant timestamp, final Locale locale) throws InvalidKeyException {
public String generateOneTimePasswordString(final SecretKey key, final Instant timestamp, final Locale locale) throws InvalidKeyException {
return this.hotp.formatOneTimePassword(this.generateOneTimePassword(key, timestamp), locale);
}

Expand All @@ -213,7 +213,7 @@ public String generateOneTimePasswordString(final Key key, final Instant timesta
*
* @see <a href="https://datatracker.ietf.org/doc/html/rfc6238#section-5">TOTP: Time-Based One-Time Password Algorithm (RFC 6238) - Security Considerations</a>
*/
public boolean validateOneTimePassword(final Key key, final Instant timestamp, final String oneTimePassword) throws InvalidKeyException {
public boolean validateOneTimePassword(final SecretKey key, final Instant timestamp, final String oneTimePassword) throws InvalidKeyException {
return hotp.validateOneTimePassword(key, getCounterValue(timestamp), oneTimePassword);
}

Expand All @@ -234,7 +234,7 @@ public boolean validateOneTimePassword(final Key key, final Instant timestamp, f
*
* @see <a href="https://datatracker.ietf.org/doc/html/rfc6238#section-5">TOTP: Time-Based One-Time Password Algorithm (RFC 6238) - Security Considerations</a>
*/
public boolean validateOneTimePassword(final Key key, final Instant timestamp, final int oneTimePassword) throws InvalidKeyException {
public boolean validateOneTimePassword(final SecretKey key, final Instant timestamp, final int oneTimePassword) throws InvalidKeyException {
return hotp.validateOneTimePassword(key, getCounterValue(timestamp), oneTimePassword);
}

Expand Down
4 changes: 2 additions & 2 deletions src/main/java/overview.html
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,12 @@ <h1>Usage</h1>

<p>To actually generate time-based one-time passwords, you'll need a secret key and a timestamp. Secure key management is beyond the scope of this document; for the purposes of an example, though, we'll generate a random key:</p>

<pre>final Key secretKey;
<pre>final SecretKey key;
{
final KeyGenerator keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm());
keyGenerator.init(160);

secretKey = keyGenerator.generateKey();
key = keyGenerator.generateKey();
}</pre>

<p>Armed with a secret key, we can deterministically generate one-time passwords for any timestamp:</p>
Expand Down
4 changes: 2 additions & 2 deletions src/test/java/com/eatthepath/otp/ExampleApp.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@

import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;

public class ExampleApp {
public static void main(final String[] args) throws NoSuchAlgorithmException, InvalidKeyException {
final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator();

final Key key;
final SecretKey key;
{
final KeyGenerator keyGenerator = KeyGenerator.getInstance(totp.getAlgorithm());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,10 @@
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;

import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
import java.util.Locale;
Expand All @@ -41,7 +41,7 @@

class HmacOneTimePasswordGeneratorTest {

private static final Key HOTP_KEY =
private static final SecretKey HOTP_KEY =
new SecretKeySpec("12345678901234567890".getBytes(StandardCharsets.US_ASCII),
HmacOneTimePasswordGenerator.HOTP_HMAC_ALGORITHM);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,10 +26,10 @@
import org.junit.jupiter.params.provider.MethodSource;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.Instant;
Expand Down Expand Up @@ -102,7 +102,7 @@ void generateOneTimePassword(final String algorithm, final byte[] keyBytes, fina
new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(30), 8, algorithm);

final Instant timestamp = Instant.ofEpochSecond(epochSeconds);
final Key key = new SecretKeySpec(keyBytes, algorithm);
final SecretKey key = new SecretKeySpec(keyBytes, algorithm);

assertEquals(expectedOneTimePassword, totp.generateOneTimePassword(key, timestamp));
}
Expand Down Expand Up @@ -139,7 +139,7 @@ void generateOneTimePasswordString(final String algorithm, final byte[] keyBytes
new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(30), 8, algorithm);

final Instant timestamp = Instant.ofEpochSecond(epochSeconds);
final Key key = new SecretKeySpec(keyBytes, algorithm);
final SecretKey key = new SecretKeySpec(keyBytes, algorithm);

assertEquals(expectedOneTimePassword, totp.generateOneTimePasswordString(key, timestamp));
}
Expand Down Expand Up @@ -176,7 +176,7 @@ void generateOneTimePasswordStringLocale(final String algorithm, final byte[] ke
new TimeBasedOneTimePasswordGenerator(Duration.ofSeconds(30), 8, algorithm);

final Instant timestamp = Instant.ofEpochSecond(epochSeconds);
final Key key = new SecretKeySpec(keyBytes, algorithm);
final SecretKey key = new SecretKeySpec(keyBytes, algorithm);

assertEquals(expectedOneTimePassword, totp.generateOneTimePasswordString(key, timestamp, locale));
}
Expand Down Expand Up @@ -210,7 +210,7 @@ private static Stream<Arguments> generateOneTimePasswordStringLocale() {
void validateOneTimePasswordInt() throws InvalidKeyException {
final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator();
final Instant timestamp = Instant.now();
final Key key =
final SecretKey key =
new SecretKeySpec(HMAC_SHA1_KEY_BYTES, TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1);

assertTrue(totp.validateOneTimePassword(key, timestamp, totp.generateOneTimePassword(key, timestamp)));
Expand All @@ -223,7 +223,7 @@ void validateOneTimePasswordInt() throws InvalidKeyException {
@Test
void validateOneTimePasswordString() throws InvalidKeyException {
final TimeBasedOneTimePasswordGenerator totp = new TimeBasedOneTimePasswordGenerator();
final Key key =
final SecretKey key =
new SecretKeySpec(HMAC_SHA1_KEY_BYTES, TimeBasedOneTimePasswordGenerator.TOTP_ALGORITHM_HMAC_SHA1);

// A timestamp of 1970-01-01T00:18:00Z with a default TOTP generator produces a one-time password of "003784"
Expand Down