Skip to content

Commit

Permalink
Merge pull request #24 from amdelamar/customPeriod
Browse files Browse the repository at this point in the history
Allow custom periods in seconds in timeInHex
  • Loading branch information
amdelamar authored Sep 20, 2020
2 parents 5a9c809 + 3f99e6f commit f0ab0d4
Show file tree
Hide file tree
Showing 3 changed files with 56 additions and 3 deletions.
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ plugins {
}

group = 'com.amdelamar'
version = '1.2.2'
version = '1.3.0'
description = 'OTP (One Time Password) utility in Java. To enable two-factor authentication (2FA) using HMAC-based) or Time-based algorithms.'
sourceCompatibility = 1.8
targetCompatibility = 1.8
Expand Down
18 changes: 17 additions & 1 deletion src/main/java/com/amdelamar/jotp/OTP.java
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,23 @@ public static String timeInHex() throws IOException {
* @throws IOException when generating Unix time
*/
public static String timeInHex(long timeInMillis) throws IOException {
final long time = (long) Math.floor(Math.round(((double) timeInMillis) / 1000.0) / 30d);
return timeInHex(timeInMillis, 30);
}

/**
* A method to get a Unix Time converted to Hexadecimal using a token period.
* @param timeInMillis long (like <code>System.currentTimeMillis()</code>)
* @param periodInSec int seconds period for the time to be rounded down to
* @return String Hex time
* @throws IOException
*/
public static String timeInHex(long timeInMillis, int periodInSec) throws IOException {
double period = 1d;
if (periodInSec > 1) {
// ensure period is 1 or greater value
period = periodInSec;
}
final long time = (long) Math.floor(Math.round(((double) timeInMillis) / 1000d) / period);
final byte[] longBytes = ByteBuffer.allocate(Long.SIZE / Byte.SIZE)
.putLong(time)
.array();
Expand Down
39 changes: 38 additions & 1 deletion src/test/java/com/amdelamar/jotp/type/TOTPTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public void labelTests() {
}

@Test
public void totpTests() throws IllegalArgumentException, IOException, InterruptedException, InvalidKeyException,
public void defaultPeriod() throws IllegalArgumentException, IOException, InterruptedException, InvalidKeyException,
NoSuchAlgorithmException {
// Normally we'd use System.currentTimeMillis(), but
// because we can't control exactly when unit tests run
Expand All @@ -56,6 +56,43 @@ public void totpTests() throws IllegalArgumentException, IOException, Interrupte
assertNotEquals(code1, code3);
assertTrue(OTP.verify(secret, OTP.timeInHex(thirtyOneSecondsLater), code3, 6, Type.TOTP));
}

@Test
public void customPeriod() throws IllegalArgumentException, IOException, InterruptedException,
InvalidKeyException, NoSuchAlgorithmException {
// Normally we'd use System.currentTimeMillis(), but
// because we can't control exactly when unit tests run
// we might fail this test because the 30s window passes
// too early. So instead, we'll pick a specific point in
// time to make this test repeatable.
long time = 1600637701000L;
String secret = OTP.randomBase32(OTP.BYTES);

int period = 60; // 60sec instead of 30sec

String code1 = OTP.create(secret, OTP.timeInHex(time, period), 6, Type.TOTP);

// 60 sec window, so wait just a second
long oneSecondLater = time + 1000;

String code2 = OTP.create(secret, OTP.timeInHex(oneSecondLater, period), 6, Type.TOTP);
assertEquals(code1, code2);
assertTrue(OTP.verify(secret, OTP.timeInHex(oneSecondLater, period), code2, 6, Type.TOTP));

// If its beyond 30sec since the first OTP,
// then we will get the same base value.
long thirtyOneSecondsLater = oneSecondLater + 30000;
String code3 = OTP.create(secret, OTP.timeInHex(thirtyOneSecondsLater, period), 6, Type.TOTP);
assertEquals(code1, code3);
assertTrue(OTP.verify(secret, OTP.timeInHex(thirtyOneSecondsLater, period), code3, 6, Type.TOTP));

// If its beyond 60sec since the first OTP,
// then we will get a different base value.
long sixtyOneSecondsLater = oneSecondLater + 60000;
String code4 = OTP.create(secret, OTP.timeInHex(sixtyOneSecondsLater, period), 6, Type.TOTP);
assertNotEquals(code1, code4);
assertTrue(OTP.verify(secret, OTP.timeInHex(sixtyOneSecondsLater, period), code4, 6, Type.TOTP));
}

@Test
public void padLeft() throws InvalidKeyException, IllegalArgumentException, NoSuchAlgorithmException, IOException {
Expand Down

0 comments on commit f0ab0d4

Please sign in to comment.