mirror of
https://github.com/beemdevelopment/Aegis.git
synced 2025-04-25 00:06:08 +00:00
Rewrite the HOTP/TOTP code to be more compact and support more digits
This commit is contained in:
parent
066a7447c0
commit
29aa81765b
7 changed files with 101 additions and 364 deletions
|
@ -1,189 +1,47 @@
|
|||
package com.beemdevelopment.aegis.crypto.otp;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
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 HOTP {
|
||||
private HOTP() {
|
||||
}
|
||||
|
||||
// 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)
|
||||
public static String generateOTP(byte[] secret, String algo, int digits, long counter)
|
||||
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);
|
||||
// }
|
||||
}
|
||||
SecretKeySpec key = new SecretKeySpec(secret, "RAW");
|
||||
|
||||
private static final int[] DIGITS_POWER
|
||||
// 0 1 2 3 4 5 6 7 8
|
||||
= {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000};
|
||||
// encode counter in big endian
|
||||
byte[] counterBytes = ByteBuffer.allocate(8)
|
||||
.order(ByteOrder.BIG_ENDIAN)
|
||||
.putLong(counter)
|
||||
.array();
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* @return A numeric String in base 10 that includes
|
||||
* {@link codeDigits} digits plus the optional checksum
|
||||
* digit if requested.
|
||||
* @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.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
// calculate the hash of the counter
|
||||
Mac mac = Mac.getInstance(algo);
|
||||
mac.init(key);
|
||||
byte[] hash = mac.doFinal(counterBytes);
|
||||
|
||||
// compute hmac hash
|
||||
byte[] hash = hmac_sha1(secret, text);
|
||||
|
||||
// put selected bytes into result int
|
||||
// truncate hash to get the HTOP value
|
||||
// http://tools.ietf.org/html/rfc4226#section-5.4
|
||||
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);
|
||||
long bin = ((hash[offset] & 0x7f) << 24)
|
||||
| ((hash[offset + 1] & 0xff) << 16)
|
||||
| ((hash[offset + 2] & 0xff) << 8)
|
||||
| (hash[offset + 3] & 0xff);
|
||||
long otp = bin % (long) Math.pow(10, digits);
|
||||
|
||||
int otp = binary % DIGITS_POWER[codeDigits];
|
||||
if (addChecksum) {
|
||||
otp = (otp * 10) + calcChecksum(otp, codeDigits);
|
||||
// prepend zeroes if needed
|
||||
StringBuilder res = new StringBuilder(Long.toString(otp));
|
||||
while (res.length() < digits) {
|
||||
res.insert(0, "0");
|
||||
}
|
||||
result = Integer.toString(otp);
|
||||
while (result.length() < digits) {
|
||||
result = "0" + result;
|
||||
}
|
||||
return result;
|
||||
|
||||
return res.toString();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,170 +1,21 @@
|
|||
package com.beemdevelopment.aegis.crypto.otp;
|
||||
|
||||
/**
|
||||
Copyright (c) 2011 IETF Trust and the persons identified as
|
||||
authors of the code. All rights reserved.
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
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 javax.crypto.Mac;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import java.math.BigInteger;
|
||||
|
||||
/**
|
||||
* This is an example implementation of the OATH
|
||||
* TOTP algorithm.
|
||||
* Visit www.openauthentication.org for more information.
|
||||
*
|
||||
* @author Johan Rydell, PortWise, Inc.
|
||||
*/
|
||||
public class TOTP {
|
||||
|
||||
private TOTP() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
public static String generateOTP(byte[] secret, String algo, int digits, long period, long seconds)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
long counter = (long) Math.floor((double) seconds / period);
|
||||
return HOTP.generateOTP(secret, algo, digits, counter);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(byte[] key,
|
||||
String time,
|
||||
int 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(byte[] key,
|
||||
String time,
|
||||
int 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(byte[] key,
|
||||
String time,
|
||||
int 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(byte[] key,
|
||||
String time,
|
||||
int returnDigits,
|
||||
String crypto) {
|
||||
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[] hash = hmac_sha(crypto, key, 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[returnDigits];
|
||||
|
||||
result = Integer.toString(otp);
|
||||
while (result.length() < returnDigits) {
|
||||
result = "0" + result;
|
||||
}
|
||||
return result;
|
||||
public static String generateOTP(byte[] secret, String algo, int digits, long period)
|
||||
throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
return generateOTP(secret, algo, digits, period, System.currentTimeMillis() / 1000);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,7 +28,7 @@ public class HotpInfo extends OtpInfo {
|
|||
@Override
|
||||
public String getOtp() {
|
||||
try {
|
||||
return HOTP.generateOTP(getSecret(), getCounter(), getDigits(), false, -1);
|
||||
return HOTP.generateOTP(getSecret(), getAlgorithm(true), getDigits(), getCounter());
|
||||
} catch (NoSuchAlgorithmException | InvalidKeyException e) {
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@ import com.beemdevelopment.aegis.crypto.otp.TOTP;
|
|||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
public class TotpInfo extends OtpInfo {
|
||||
private int _period;
|
||||
|
||||
|
@ -20,8 +23,11 @@ public class TotpInfo extends OtpInfo {
|
|||
|
||||
@Override
|
||||
public String getOtp() {
|
||||
String time = Long.toHexString(System.currentTimeMillis() / 1000 / getPeriod());
|
||||
return TOTP.generateTOTP(getSecret(), time, getDigits(), getAlgorithm(true));
|
||||
try {
|
||||
return TOTP.generateOTP(getSecret(), getAlgorithm(true), getDigits(), getPeriod());
|
||||
} catch (InvalidKeyException | NoSuchAlgorithmException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -134,7 +134,11 @@ public class EntryHolder extends RecyclerView.ViewHolder {
|
|||
|
||||
private void updateCode() {
|
||||
String otp = _entry.getInfo().getOtp();
|
||||
_profileCode.setText(otp.substring(0, otp.length() / 2) + " " + otp.substring(otp.length() / 2));
|
||||
int offset = 0;
|
||||
if (otp.length() % 2 != 0) {
|
||||
offset = 1;
|
||||
}
|
||||
_profileCode.setText(otp.substring(0, (otp.length() / 2) + offset) + " " + otp.substring(otp.length() / 2));
|
||||
}
|
||||
|
||||
public void hideCode() {
|
||||
|
|
|
@ -12,22 +12,22 @@ import static org.junit.Assert.*;
|
|||
public class HOTPTest {
|
||||
// https://tools.ietf.org/html/rfc4226#page-32
|
||||
private final String[] _vectors = {
|
||||
"755224", "287082",
|
||||
"359152", "969429",
|
||||
"338314", "254676",
|
||||
"287922", "162583",
|
||||
"399871", "520489"
|
||||
"755224", "287082",
|
||||
"359152", "969429",
|
||||
"338314", "254676",
|
||||
"287922", "162583",
|
||||
"399871", "520489"
|
||||
};
|
||||
|
||||
private final byte[] _secret = new byte[] {
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
|
||||
private final byte[] _secret = new byte[]{
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30,
|
||||
0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x30
|
||||
};
|
||||
|
||||
@Test
|
||||
public void vectorsMatch() throws InvalidKeyException, NoSuchAlgorithmException {
|
||||
for (int i = 0; i < _vectors.length; i++) {
|
||||
String otp = HOTP.generateOTP(_secret, i, 6, false, -1);
|
||||
String otp = HOTP.generateOTP(_secret, "HmacSHA1", 6, i);
|
||||
assertEquals(_vectors[i], otp);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,31 +3,49 @@ package com.beemdevelopment.aegis;
|
|||
import org.junit.Test;
|
||||
|
||||
import com.beemdevelopment.aegis.crypto.otp.TOTP;
|
||||
import com.beemdevelopment.aegis.encoding.Hex;
|
||||
import com.beemdevelopment.aegis.encoding.HexException;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
public class TOTPTest {
|
||||
private static class Vector {
|
||||
public long Time;
|
||||
public String OTP;
|
||||
public String Algo;
|
||||
|
||||
public Vector(long time, String otp, String algo) {
|
||||
Time = time;
|
||||
OTP = otp;
|
||||
Algo = algo;
|
||||
}
|
||||
}
|
||||
|
||||
// https://tools.ietf.org/html/rfc6238#appendix-B
|
||||
private final String[][] _vectors = {
|
||||
// time, OPT, algorithm
|
||||
{"0000000000000001", "94287082", "HmacSHA1"},
|
||||
{"0000000000000001", "46119246", "HmacSHA256"},
|
||||
{"0000000000000001", "90693936", "HmacSHA512"},
|
||||
{"00000000023523EC", "07081804", "HmacSHA1"},
|
||||
{"00000000023523EC", "68084774", "HmacSHA256"},
|
||||
{"00000000023523EC", "25091201", "HmacSHA512"},
|
||||
{"00000000023523ED", "14050471", "HmacSHA1"},
|
||||
{"00000000023523ED", "67062674", "HmacSHA256"},
|
||||
{"00000000023523ED", "99943326", "HmacSHA512"},
|
||||
{"000000000273EF07", "89005924", "HmacSHA1"},
|
||||
{"000000000273EF07", "91819424", "HmacSHA256"},
|
||||
{"000000000273EF07", "93441116", "HmacSHA512"},
|
||||
{"0000000003F940AA", "69279037", "HmacSHA1"},
|
||||
{"0000000003F940AA", "90698825", "HmacSHA256"},
|
||||
{"0000000003F940AA", "38618901", "HmacSHA512"},
|
||||
{"0000000027BC86AA", "65353130", "HmacSHA1"},
|
||||
{"0000000027BC86AA", "77737706", "HmacSHA256"},
|
||||
{"0000000027BC86AA", "47863826", "HmacSHA512"}
|
||||
private final Vector[] _vectors = {
|
||||
new Vector(59, "94287082", "HmacSHA1"),
|
||||
new Vector(59, "46119246", "HmacSHA256"),
|
||||
new Vector(59, "90693936", "HmacSHA512"),
|
||||
new Vector(1111111109, "07081804", "HmacSHA1"),
|
||||
new Vector(1111111109, "68084774", "HmacSHA256"),
|
||||
new Vector(1111111109, "25091201", "HmacSHA512"),
|
||||
new Vector(1111111111, "14050471", "HmacSHA1"),
|
||||
new Vector(1111111111, "67062674", "HmacSHA256"),
|
||||
new Vector(1111111111, "99943326", "HmacSHA512"),
|
||||
new Vector(1234567890, "89005924", "HmacSHA1"),
|
||||
new Vector(1234567890, "91819424", "HmacSHA256"),
|
||||
new Vector(1234567890, "93441116", "HmacSHA512"),
|
||||
new Vector(2000000000, "69279037", "HmacSHA1"),
|
||||
new Vector(2000000000, "90698825", "HmacSHA256"),
|
||||
new Vector(2000000000, "38618901", "HmacSHA512"),
|
||||
new Vector(20000000000L, "65353130", "HmacSHA1"),
|
||||
new Vector(20000000000L, "77737706", "HmacSHA256"),
|
||||
new Vector(20000000000L, "47863826", "HmacSHA512")
|
||||
};
|
||||
|
||||
private final byte[] _seed = new byte[]{
|
||||
|
@ -50,11 +68,11 @@ public class TOTPTest {
|
|||
};
|
||||
|
||||
@Test
|
||||
public void vectorsMatch() {
|
||||
for (String[] vector : _vectors) {
|
||||
public void vectorsMatch() throws NoSuchAlgorithmException, InvalidKeyException, HexException {
|
||||
for (Vector vector : _vectors) {
|
||||
byte[] seed;
|
||||
|
||||
switch (vector[2]) {
|
||||
switch (vector.Algo) {
|
||||
case "HmacSHA1":
|
||||
seed = _seed;
|
||||
break;
|
||||
|
@ -69,8 +87,8 @@ public class TOTPTest {
|
|||
return;
|
||||
}
|
||||
|
||||
String otp = TOTP.generateTOTP(seed, vector[0], 8, vector[2]);
|
||||
assertEquals(vector[1], otp);
|
||||
String otp = TOTP.generateOTP(seed, vector.Algo, 8, 30, vector.Time);
|
||||
assertEquals(vector.OTP, otp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue