remove the testfiles to the gitignore
This commit is contained in:
parent
66e8b9b2ab
commit
1c286dd2f0
1
.gitignore
vendored
1
.gitignore
vendored
@ -2,4 +2,3 @@
|
||||
/target/
|
||||
.classpath
|
||||
/.settings
|
||||
/src/test/
|
||||
|
108
src/test/java/test/OTPTest.java
Normal file
108
src/test/java/test/OTPTest.java
Normal file
@ -0,0 +1,108 @@
|
||||
package test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.TimeZone;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import com.cringe_studios.cringe_authenticator_library.OTP;
|
||||
import com.cringe_studios.cringe_authenticator_library.OTPAlgorithm;
|
||||
import com.cringe_studios.cringe_authenticator_library.OTPType;
|
||||
import com.cringe_studios.cringe_authenticator_library.impl.Base32;
|
||||
import com.cringe_studios.cringe_authenticator_library.impl.TOTP;
|
||||
|
||||
public class OTPTest {
|
||||
public static final boolean VERBOSE = true;
|
||||
|
||||
@Test
|
||||
public void hotpTest() {
|
||||
String secret = "3132333435363738393031323334353637383930"; // Secret =
|
||||
// 0x3132333435363738393031323334353637383930
|
||||
|
||||
String base32secret = Base32.encode(hexStr2Bytes(secret));
|
||||
|
||||
String[] expectedValues = { "755224", "287082", "359152", "969429", "338314", "254676", "287922", "162583",
|
||||
"399871", "520489" };
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
OTP testOTP1 = OTPType.HOTP.instance(base32secret, OTPAlgorithm.SHA1, 6, i, 30, false);
|
||||
String calculatedPin = testOTP1.getPin();
|
||||
|
||||
if(VERBOSE) {
|
||||
System.out.println(calculatedPin + " == " + expectedValues[i]);
|
||||
}
|
||||
|
||||
assertEquals(calculatedPin, expectedValues[i]);
|
||||
}
|
||||
}
|
||||
|
||||
private static byte[] hexStr2Bytes(String hex) {
|
||||
// Adding one byte to get the right conversion
|
||||
// Values starting with "0" can be converted
|
||||
byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
|
||||
|
||||
// Copy all the REAL bytes, not the "first"
|
||||
byte[] ret = new byte[bArray.length - 1];
|
||||
for (int i = 0; i < ret.length; i++)
|
||||
ret[i] = bArray[i + 1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static void totpTest() {
|
||||
// Seed for HMAC-SHA1 - 20 bytes
|
||||
String base16seed = "3132333435363738393031323334353637383930";
|
||||
// Seed for HMAC-SHA256 - 32 bytes
|
||||
String base16seed32 = "3132333435363738393031323334353637383930313233343536373839303132";
|
||||
// Seed for HMAC-SHA512 - 64 bytes
|
||||
String base16seed64 = "31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334";
|
||||
|
||||
String seed = Base32.encode(hexStr2Bytes(base16seed));
|
||||
String seed32 = Base32.encode(hexStr2Bytes(base16seed32));
|
||||
String seed64 = Base32.encode(hexStr2Bytes(base16seed64));
|
||||
|
||||
long testTime[] = { 59L, 1111111109L, 1111111111L, 1234567890L, 2000000000L, 20000000000L };
|
||||
|
||||
TOTP testOTP1 = (TOTP) OTPType.TOTP.instance(seed, OTPAlgorithm.SHA1, 8, 0, 30, false);
|
||||
TOTP testOTP256 = (TOTP) OTPType.TOTP.instance(seed32, OTPAlgorithm.SHA256, 8, 0, 30, false);
|
||||
TOTP testOTP512 = (TOTP) OTPType.TOTP.instance(seed64, OTPAlgorithm.SHA512, 8, 0, 30, false);
|
||||
|
||||
long X = 30;
|
||||
|
||||
String steps = "0";
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
try {
|
||||
System.out.println("+---------------+-----------------------+--------+--------+");
|
||||
System.out.println("| Time(sec) | Time (UTC format) | TOTP | Mode |");
|
||||
System.out.println("+---------------+-----------------------+--------+--------+");
|
||||
|
||||
for (int i = 0; i < testTime.length; i++) {
|
||||
String fmtTime = String.format("%1$-11s", testTime[i]);
|
||||
String utcTime = df.format(new Date(testTime[i] * 1000));
|
||||
|
||||
System.out.println(
|
||||
"| " + fmtTime + " | " + utcTime + " |" + testOTP1.getPinAt(testTime[i]) + "| SHA1 |");
|
||||
System.out.println(
|
||||
"| " + fmtTime + " | " + utcTime + " |" + testOTP256.getPinAt(testTime[i]) + "| SHA256 |");
|
||||
System.out.println(
|
||||
"| " + fmtTime + " | " + utcTime + " |" + testOTP512.getPinAt(testTime[i]) + "| SHA512 |");
|
||||
System.out.println("+---------------+-----------------------+--------+--------+");
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
// @org.junit.jupiter.api.Test
|
||||
// public void test() {
|
||||
// hotpTest();
|
||||
// totpTest();
|
||||
// assertEquals(1, 1);
|
||||
// }
|
||||
}
|
209
src/test/java/test/ogHOTP.java
Normal file
209
src/test/java/test/ogHOTP.java
Normal file
@ -0,0 +1,209 @@
|
||||
/*
|
||||
* OneTimePasswordAlgorithm.java
|
||||
* OATH Initiative,
|
||||
* HOTP one-time password algorithm
|
||||
*
|
||||
*/
|
||||
|
||||
/* Copyright (C) 2004, OATH. All rights reserved.
|
||||
*
|
||||
* License to copy and use this software is granted provided that it
|
||||
* is identified as the "OATH HOTP Algorithm" in all material
|
||||
* mentioning or referencing this software or this function.
|
||||
*
|
||||
* License is also granted to make and use derivative works provided
|
||||
* that such works are identified as
|
||||
* "derived from OATH HOTP algorithm"
|
||||
* in all material mentioning or referencing the derived work.
|
||||
*
|
||||
* OATH (Open AuTHentication) and its members make no
|
||||
* representations concerning either the merchantability of this
|
||||
* software or the suitability of this software for any particular
|
||||
* purpose.
|
||||
*
|
||||
* It is provided "as is" without express or implied warranty
|
||||
* of any kind and OATH AND ITS MEMBERS EXPRESSaLY DISCLAIMS
|
||||
* ANY WARRANTY OR LIABILITY OF ANY KIND relating to this software.
|
||||
*
|
||||
* These notices must be retained in any copies of any part of this
|
||||
* documentation and/or software.
|
||||
*/
|
||||
|
||||
package test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.File;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.FileInputStream;
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.Arrays;
|
||||
import java.security.InvalidKeyException;
|
||||
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
/**
|
||||
* This class contains static methods that are used to calculate the One-Time
|
||||
* Password (OTP) using JCE to provide the HMAC-SHA-1.
|
||||
*
|
||||
* @author Loren Hart
|
||||
* @version 1.0
|
||||
*/
|
||||
public class ogHOTP {
|
||||
private ogHOTP() {}
|
||||
|
||||
// These are used to calculate the check-sum digits.
|
||||
// 0 1 2 3 4 5 6 7 8 9
|
||||
private static final int[] doubleDigits = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
|
||||
|
||||
/**
|
||||
* Calculates the checksum using the credit card algorithm. This algorithm has
|
||||
* the advantage that it detects any single mistyped digit and any single
|
||||
* transposition of adjacent digits.
|
||||
*
|
||||
* @param num the number to calculate the checksum for
|
||||
* @param digits number of significant places in the number
|
||||
*
|
||||
* @return the checksum of num
|
||||
*/
|
||||
public static int calcChecksum(long num, int digits) {
|
||||
boolean doubleDigit = true;
|
||||
int total = 0;
|
||||
while (0 < digits--) {
|
||||
int digit = (int) (num % 10);
|
||||
num /= 10;
|
||||
if (doubleDigit) {
|
||||
digit = doubleDigits[digit];
|
||||
}
|
||||
total += digit;
|
||||
doubleDigit = !doubleDigit;
|
||||
}
|
||||
int result = total % 10;
|
||||
if (result > 0) {
|
||||
result = 10 - result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method uses the JCE to provide the HMAC-SHA-1 algorithm. HMAC computes a
|
||||
* Hashed Message Authentication Code and in this case SHA1 is the hash
|
||||
* algorithm used.
|
||||
*
|
||||
* @param keyBytes the bytes to use for the HMAC-SHA-1 key
|
||||
* @param text the message or text to be authenticated.
|
||||
*
|
||||
* @throws NoSuchAlgorithmException if no provider makes either HmacSHA1 or
|
||||
* HMAC-SHA-1 digest algorithms available.
|
||||
* @throws InvalidKeyException The secret provided was not a valid
|
||||
* HMAC-SHA-1 key.
|
||||
*
|
||||
*/
|
||||
|
||||
public static byte[] hmac_sha1(byte[] keyBytes, byte[] text) throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
// try {
|
||||
Mac hmacSha1;
|
||||
try {
|
||||
hmacSha1 = Mac.getInstance("HmacSHA1");
|
||||
} catch (NoSuchAlgorithmException nsae) {
|
||||
hmacSha1 = Mac.getInstance("HMAC-SHA-1");
|
||||
}
|
||||
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
|
||||
hmacSha1.init(macKey);
|
||||
return hmacSha1.doFinal(text);
|
||||
// } catch (GeneralSecurityException gse) {
|
||||
// throw new UndeclaredThrowableException(gse);
|
||||
// }
|
||||
}
|
||||
|
||||
private static final int[] DIGITS_POWER
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
= { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
||||
|
||||
/**
|
||||
* This method generates an OTP value for the given set of parameters.
|
||||
*
|
||||
* @param secret the shared secret
|
||||
* @param movingFactor the counter, time, or other value that changes on a
|
||||
* per use basis.
|
||||
* @param codeDigits the number of digits in the OTP, not including the
|
||||
* checksum, if any.
|
||||
* @param addChecksum a flag that indicates if a checksum digit
|
||||
* should be appended to the OTP.
|
||||
* @param truncationOffset the offset into the MAC result to begin truncation.
|
||||
* If this value is out of the range of 0 ... 15, then
|
||||
* dynamic truncation will be used. Dynamic truncation
|
||||
* is when the last 4 bits of the last byte of the MAC
|
||||
* are used to determine the start offset.
|
||||
* @throws NoSuchAlgorithmException if no provider makes either HmacSHA1 or
|
||||
* HMAC-SHA-1 digest algorithms available.
|
||||
* @throws InvalidKeyException The secret provided was not a valid
|
||||
* HMAC-SHA-1 key.
|
||||
*
|
||||
* @return A numeric String in base 10 that includes {@link codeDigits} digits
|
||||
* plus the optional checksum digit if requested.
|
||||
*/
|
||||
static public String generateOTP(byte[] secret, long movingFactor, int codeDigits, boolean addChecksum,
|
||||
int truncationOffset) throws NoSuchAlgorithmException, InvalidKeyException {
|
||||
// put movingFactor value into text byte array
|
||||
String result = null;
|
||||
int digits = addChecksum ? (codeDigits + 1) : codeDigits;
|
||||
byte[] text = new byte[8];
|
||||
for (int i = text.length - 1; i >= 0; i--) {
|
||||
text[i] = (byte) (movingFactor & 0xff);
|
||||
movingFactor >>= 8;
|
||||
}
|
||||
|
||||
// compute hmac hash
|
||||
byte[] hash = hmac_sha1(secret, text);
|
||||
|
||||
System.out.print(Arrays.toString(hash) + " ");
|
||||
|
||||
// put selected bytes into result int
|
||||
int offset = hash[hash.length - 1] & 0xf;
|
||||
if ((0 <= truncationOffset) && (truncationOffset < (hash.length - 4))) {
|
||||
offset = truncationOffset;
|
||||
}
|
||||
int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16)
|
||||
| ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
|
||||
|
||||
int otp = binary % DIGITS_POWER[codeDigits];
|
||||
if (addChecksum) {
|
||||
otp = (otp * 10) + calcChecksum(otp, codeDigits);
|
||||
}
|
||||
result = Integer.toString(otp);
|
||||
while (result.length() < digits) {
|
||||
result = "0" + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static byte[] hexStringToByteArray(String s) {
|
||||
int len = s.length();
|
||||
byte[] data = new byte[len / 2];
|
||||
for (int i = 0; i < len; i += 2) {
|
||||
data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)
|
||||
+ Character.digit(s.charAt(i+1), 16));
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String Secret = "3132333435363738393031323334353637383930"; // Secret = 0x3132333435363738393031323334353637383930
|
||||
|
||||
byte[] byteSecret = hexStringToByteArray(Secret);
|
||||
for(int i = 0; i < 10; i++) {
|
||||
try {
|
||||
System.out.println("" + i + generateOTP(byteSecret, i, 6, false, -1));
|
||||
} catch (InvalidKeyException e) {
|
||||
e.printStackTrace();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
212
src/test/java/test/ogTOTP.java
Normal file
212
src/test/java/test/ogTOTP.java
Normal file
@ -0,0 +1,212 @@
|
||||
package test;
|
||||
|
||||
/**
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as
|
||||
authors of the code. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, is permitted pursuant to, and subject to the license
|
||||
terms contained in, the Simplified BSD License set forth in Section
|
||||
4.c of the IETF Trust's Legal Provisions Relating to IETF Documents
|
||||
(http://trustee.ietf.org/license-info).
|
||||
*/
|
||||
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.math.BigInteger;
|
||||
import java.util.TimeZone;
|
||||
|
||||
/**
|
||||
* This is an example implementation of the OATH TOTP algorithm. Visit
|
||||
* www.openauthentication.org for more information.
|
||||
*
|
||||
* @author Johan Rydell, PortWise, Inc.
|
||||
*/
|
||||
|
||||
public class ogTOTP {
|
||||
|
||||
private ogTOTP() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method uses the JCE to provide the crypto algorithm. HMAC computes a
|
||||
* Hashed Message Authentication Code with the crypto hash algorithm as a
|
||||
* parameter.
|
||||
*
|
||||
* @param crypto: the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512)
|
||||
* @param keyBytes: the bytes to use for the HMAC key
|
||||
* @param text: the message or text to be authenticated
|
||||
*/
|
||||
|
||||
private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
|
||||
try {
|
||||
Mac hmac;
|
||||
hmac = Mac.getInstance(crypto);
|
||||
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
|
||||
hmac.init(macKey);
|
||||
return hmac.doFinal(text);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new UndeclaredThrowableException(gse);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts a HEX string to Byte[]
|
||||
*
|
||||
* @param hex: the HEX string
|
||||
*
|
||||
* @return: a byte array
|
||||
*/
|
||||
|
||||
private static byte[] hexStr2Bytes(String hex) {
|
||||
// Adding one byte to get the right conversion
|
||||
// Values starting with "0" can be converted
|
||||
byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
|
||||
|
||||
// Copy all the REAL bytes, not the "first"
|
||||
byte[] ret = new byte[bArray.length - 1];
|
||||
for (int i = 0; i < ret.length; i++)
|
||||
ret[i] = bArray[i + 1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static final int[] DIGITS_POWER
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
= { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes {@link truncationDigits}
|
||||
* digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP(String key, String time, String returnDigits) {
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA1");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes {@link truncationDigits}
|
||||
* digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP256(String key, String time, String returnDigits) {
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA256");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes {@link truncationDigits}
|
||||
* digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP512(String key, String time, String returnDigits) {
|
||||
return generateTOTP(key, time, returnDigits, "HmacSHA512");
|
||||
}
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
* @param crypto: the crypto function to use
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes {@link truncationDigits}
|
||||
* digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP(String key, String time, String returnDigits, String crypto) {
|
||||
int codeDigits = Integer.decode(returnDigits).intValue();
|
||||
String result = null;
|
||||
|
||||
// Using the counter
|
||||
// First 8 bytes are for the movingFactor
|
||||
// Compliant with base RFC 4226 (HOTP)
|
||||
while (time.length() < 16)
|
||||
time = "0" + time;
|
||||
|
||||
// Get the HEX in a Byte[]
|
||||
byte[] msg = hexStr2Bytes(time);
|
||||
byte[] k = hexStr2Bytes(key);
|
||||
|
||||
byte[] hash = hmac_sha(crypto, k, msg);
|
||||
|
||||
// put selected bytes into result int
|
||||
int offset = hash[hash.length - 1] & 0xf;
|
||||
|
||||
int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16)
|
||||
| ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
|
||||
|
||||
int otp = binary % DIGITS_POWER[codeDigits];
|
||||
|
||||
result = Integer.toString(otp);
|
||||
while (result.length() < codeDigits) {
|
||||
result = "0" + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Seed for HMAC-SHA1 - 20 bytes
|
||||
String seed = "3132333435363738393031323334353637383930";
|
||||
// Seed for HMAC-SHA256 - 32 bytes
|
||||
String seed32 = "3132333435363738393031323334353637383930" + "313233343536373839303132";
|
||||
// Seed for HMAC-SHA512 - 64 bytes
|
||||
String seed64 = "3132333435363738393031323334353637383930" + "3132333435363738393031323334353637383930"
|
||||
+ "3132333435363738393031323334353637383930" + "31323334";
|
||||
long T0 = 0;
|
||||
long X = 30;
|
||||
long testTime[] = { 59L, 1111111109L, 1111111111L, 1234567890L, 2000000000L, 20000000000L };
|
||||
|
||||
String steps = "0";
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
try {
|
||||
System.out.println("+---------------+-----------------------+" + "------------------+--------+--------+");
|
||||
System.out.println("| Time(sec) | Time (UTC format) " + "| Value of T(Hex) | TOTP | Mode |");
|
||||
System.out.println("+---------------+-----------------------+" + "------------------+--------+--------+");
|
||||
|
||||
for (int i = 0; i < testTime.length; i++) {
|
||||
long T = (testTime[i] - T0) / X;
|
||||
steps = Long.toHexString(T).toUpperCase();
|
||||
while (steps.length() < 16)
|
||||
steps = "0" + steps;
|
||||
String fmtTime = String.format("%1$-11s", testTime[i]);
|
||||
String utcTime = df.format(new Date(testTime[i] * 1000));
|
||||
System.out.print("| " + fmtTime + " | " + utcTime + " | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed, steps, "8", "HmacSHA1") + "| SHA1 |");
|
||||
System.out.print("| " + fmtTime + " | " + utcTime + " | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed32, steps, "8", "HmacSHA256") + "| SHA256 |");
|
||||
System.out.print("| " + fmtTime + " | " + utcTime + " | " + steps + " |");
|
||||
System.out.println(generateTOTP(seed64, steps, "8", "HmacSHA512") + "| SHA512 |");
|
||||
|
||||
System.out
|
||||
.println("+---------------+-----------------------+" + "------------------+--------+--------+");
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
System.out.println("Error : " + e);
|
||||
}
|
||||
}
|
||||
}
|
142
src/test/java/test/ogTOTPmod1.java
Normal file
142
src/test/java/test/ogTOTPmod1.java
Normal file
@ -0,0 +1,142 @@
|
||||
package test;
|
||||
|
||||
import java.lang.reflect.UndeclaredThrowableException;
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.math.BigInteger;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.TimeZone;
|
||||
|
||||
public class ogTOTPmod1 {
|
||||
|
||||
private ogTOTPmod1() {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method uses the JCE to provide the crypto algorithm. HMAC computes a
|
||||
* Hashed Message Authentication Code with the crypto hash algorithm as a
|
||||
* parameter.
|
||||
*
|
||||
* @param crypto: the crypto algorithm (HmacSHA1, HmacSHA256, HmacSHA512)
|
||||
* @param keyBytes: the bytes to use for the HMAC key
|
||||
* @param text: the message or text to be authenticated
|
||||
*/
|
||||
|
||||
private static byte[] hmac_sha(String crypto, byte[] keyBytes, byte[] text) {
|
||||
try {
|
||||
Mac hmac;
|
||||
hmac = Mac.getInstance(crypto);
|
||||
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
|
||||
hmac.init(macKey);
|
||||
return hmac.doFinal(text);
|
||||
} catch (GeneralSecurityException gse) {
|
||||
throw new UndeclaredThrowableException(gse);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts a HEX string to Byte[]
|
||||
*
|
||||
* @param hex: the HEX string
|
||||
*
|
||||
* @return: a byte array
|
||||
*/
|
||||
|
||||
private static byte[] hexStr2Bytes(String hex) {
|
||||
// Adding one byte to get the right conversion
|
||||
// Values starting with "0" can be converted
|
||||
byte[] bArray = new BigInteger("10" + hex, 16).toByteArray();
|
||||
|
||||
// Copy all the REAL bytes, not the "first"
|
||||
byte[] ret = new byte[bArray.length - 1];
|
||||
for (int i = 0; i < ret.length; i++)
|
||||
ret[i] = bArray[i + 1];
|
||||
return ret;
|
||||
}
|
||||
|
||||
private static final int[] DIGITS_POWER = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
|
||||
|
||||
/**
|
||||
* This method generates a TOTP value for the given set of parameters.
|
||||
*
|
||||
* @param key: the shared secret, HEX encoded
|
||||
* @param time: a value that reflects a time
|
||||
* @param returnDigits: number of digits to return
|
||||
* @param crypto: the crypto function to use
|
||||
*
|
||||
* @return: a numeric String in base 10 that includes {@link truncationDigits}
|
||||
* digits
|
||||
*/
|
||||
|
||||
public static String generateTOTP(String key, long timein, String crypto) {
|
||||
int codeDigits = 8;
|
||||
|
||||
long T = timein / 30;
|
||||
|
||||
// Get the HEX in a Byte[]
|
||||
byte[] msg = ByteBuffer.allocate(Long.BYTES).putLong(T).array();
|
||||
byte[] k = hexStr2Bytes(key);
|
||||
|
||||
byte[] hash = hmac_sha(crypto, k, msg);
|
||||
|
||||
// put selected bytes into result int
|
||||
int offset = hash[hash.length - 1] & 0xf;
|
||||
|
||||
int binary = ((hash[offset] & 0x7f) << 24) | ((hash[offset + 1] & 0xff) << 16)
|
||||
| ((hash[offset + 2] & 0xff) << 8) | (hash[offset + 3] & 0xff);
|
||||
|
||||
int otp = binary % DIGITS_POWER[codeDigits];
|
||||
|
||||
String result = Integer.toString(otp);
|
||||
while (result.length() < codeDigits) {
|
||||
result = "0" + result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Seed for HMAC-SHA1 - 20 bytes
|
||||
String seed = "3132333435363738393031323334353637383930";
|
||||
// Seed for HMAC-SHA256 - 32 bytes
|
||||
String seed32 = "3132333435363738393031323334353637383930313233343536373839303132";
|
||||
// Seed for HMAC-SHA512 - 64 bytes
|
||||
String seed64 = "31323334353637383930313233343536373839303132333435363738393031323334353637383930313233343536373839303132333435363738393031323334";
|
||||
long X = 30;
|
||||
long testTime[] = { 59L, 1111111109L, 1111111111L, 1234567890L, 2000000000L, 20000000000L };
|
||||
|
||||
// String steps = "0";
|
||||
DateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
|
||||
df.setTimeZone(TimeZone.getTimeZone("UTC"));
|
||||
|
||||
try {
|
||||
System.out.println("+---------------+-----------------------+------------------+--------+--------+");
|
||||
System.out.println("| Time(sec) | Time (UTC format) | Value of T(Hex) | TOTP | Mode |");
|
||||
System.out.println("+---------------+-----------------------+------------------+--------+--------+");
|
||||
|
||||
for (int i = 0; i < testTime.length; i++) {
|
||||
|
||||
long steps = testTime[i];
|
||||
|
||||
String fmtTime = String.format("%1$-11s", testTime[i]);
|
||||
String utcTime = df.format(new Date(testTime[i] * 1000));
|
||||
|
||||
System.out.println("| " + fmtTime + " | " + utcTime + " | " + steps + " |"
|
||||
+ generateTOTP(seed, steps, "HmacSHA1") + "| SHA1 |");
|
||||
System.out.println("| " + fmtTime + " | " + utcTime + " | " + steps + " |"
|
||||
+ generateTOTP(seed32, steps, "HmacSHA256") + "| SHA256 |");
|
||||
System.out.println("| " + fmtTime + " | " + utcTime + " | " + steps + " |"
|
||||
+ generateTOTP(seed64, steps, "HmacSHA512") + "| SHA512 |");
|
||||
|
||||
System.out
|
||||
.println("+---------------+-----------------------+" + "------------------+--------+--------+");
|
||||
}
|
||||
} catch (final Exception e) {
|
||||
System.out.println("Error : " + e);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user