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/
|
/target/
|
||||||
.classpath
|
.classpath
|
||||||
/.settings
|
/.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