Added weird shit... idk if it works, Tests don't work yet

This commit is contained in:
TheArrayser 2023-06-17 17:07:19 +02:00
parent 866f5aac0b
commit def350d1da
7 changed files with 212 additions and 39 deletions

2
.gitignore vendored
View File

@ -2,4 +2,4 @@
/target/
.classpath
/.settings
/src/test/

View File

@ -1,15 +1,26 @@
package com.cringe_studios.cringe_authenticator_library;
public interface OTP {
public abstract class OTP {
//TOTP
//HOTP
protected String secret;
protected OTPType type;
protected OTPAlgorithm algorithm;
protected long digits;
protected int digits;
protected long counter;
protected long period;
protected boolean checksum;
protected OTP(OTPType nType, String nSecret, OTPAlgorithm nAlgorithm, int nDigits, long nCounter, long nPeriodInSeconds, boolean nChecksum) {
this.secret = nSecret;
type = nType;
algorithm = nAlgorithm;
digits = nDigits;
counter = nCounter;
period = nPeriodInSeconds;
checksum = nChecksum;
}
//secret: required
//algorithm: optional (default SHA1)
@ -17,37 +28,39 @@ public interface OTP {
//OTPType: required
//period: optional (default 30)
//counter: required if hotp
public static OTP createNewOTP(OTPType type, String secret, OTPAlgorithm algorithm, long digits, long counter, long periodInSeconds) {
return null;
public static OTP createNewOTP(OTPType type, String secret, OTPAlgorithm algorithm, int digits, long counter, long periodInSeconds, boolean checksum) {
return type.instance(secret, algorithm, digits, counter, periodInSeconds, checksum);
}
public long getNextDueTime();
public abstract long getNextDueTime();
public String getPin();
public abstract String getPin();
public String updatePin();
default public String getSecret() {
public String getSecret() {
return this.secret;
}
default public OTPType getOTPType() {
public OTPType getOTPType() {
return this.type;
}
default public OTPAlgorithm getAlgorithm() {
public OTPAlgorithm getAlgorithm() {
return algorithm;
}
default public long getDigits() {
public int getDigits() {
return this.digits;
}
default public long getCounter() {
public long getCounter() {
return this.counter;
}
default public long getPeriod() {
public long getPeriod() {
return this.period;
}
public boolean hasChecksum() {
return checksum;
}
}

View File

@ -1,13 +1,35 @@
package com.cringe_studios.cringe_authenticator_library;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
public enum OTPAlgorithm{
SHA1,
SHA224,
SHA256,
SHA384,
SHA512;
SHA1("HmacSHA1"),
SHA224("HmacSHA224"),
SHA256("HmacSHA256"),
SHA384("HmacSHA384"),
SHA512("HmacSHA512");
private String javaName;
private OTPAlgorithm(String nJavaName) {
javaName = nJavaName;
}
public String getJavaName() {
return this.name();
return javaName;
}
public byte[] hash(byte[] keyBytes, byte[] text) throws InvalidKeyException, NoSuchAlgorithmException {
Mac hmac;
hmac = Mac.getInstance(this.getJavaName());
SecretKeySpec macKey = new SecretKeySpec(keyBytes, "RAW");
hmac.init(macKey);
return hmac.doFinal(text);
}
}

View File

@ -3,14 +3,24 @@ package com.cringe_studios.cringe_authenticator_library;
public enum OTPType {
HOTP("HMAC-based One-Time Password"),
TOTP("Time-based One-Time Password");
private String friendlyName;
private OTPType(String nFriendlyName){
private OTPType(String nFriendlyName) {
friendlyName = nFriendlyName;
}
public String getFriendlyName() {
return friendlyName;
}
public OTP instance(String secret, OTPAlgorithm algorithm, int digits, long counter, long periodInSeconds, boolean checksum) {
switch(this) {
case HOTP:
return new com.cringe_studios.cringe_authenticator_library.impl.HOTP(secret, algorithm, digits, counter, periodInSeconds, checksum);
case TOTP:
return new com.cringe_studios.cringe_authenticator_library.impl.TOTP(secret, algorithm, digits, counter, periodInSeconds, checksum);
default: return null;
}
}
}

View File

@ -1,13 +1,28 @@
package com.cringe_studios.cringe_authenticator_library.impl;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Mac;
import javax.crypto.MacSpi;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.crypto.dsig.spec.HMACParameterSpec;
import com.cringe_studios.cringe_authenticator_library.OTP;
import com.cringe_studios.cringe_authenticator_library.OTPAlgorithm;
import com.cringe_studios.cringe_authenticator_library.OTPType;
//https://datatracker.ietf.org/doc/html/rfc4226
public class HOTP implements OTP{
public class HOTP extends OTP {
protected static final int[] DIGITS_POWER
// 0 1 2 3 4 5 6 7 8
= { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000 };
public HOTP(String secret, OTPAlgorithm algorithm, int digits, long counter, long periodInSeconds, boolean checksum) {
super(OTPType.HOTP, secret, algorithm, digits, counter, periodInSeconds, checksum);
}
@Override
public long getNextDueTime() {
@ -20,10 +35,41 @@ public class HOTP implements OTP{
return null;
}
@Override
public String updatePin() {
// TODO Auto-generated method stub
return null;
public String generateOTP(int truncationOffset) throws NoSuchAlgorithmException, InvalidKeyException {
boolean addChecksum = this.hasChecksum();
byte[] secret = this.getSecret().getBytes(StandardCharsets.US_ASCII);
int codeDigits = this.getDigits();
long movingFactor = this.getCounter();
// 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 = this.getAlgorithm().hash(secret, text);
// 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) + HOTPChecksumProvider.calcChecksum(otp, codeDigits);
}
result = Integer.toString(otp);
while (result.length() < digits) {
result = "0" + result;
}
return result;
}
}

View File

@ -0,0 +1,28 @@
package com.cringe_studios.cringe_authenticator_library.impl;
public final class HOTPChecksumProvider {
// These are used to calculate the check-sum digits. 0 1 2 3 4 5 6 7 8 9
private static final int[] DOUBLE_DIGITS = { 0, 2, 4, 6, 8, 1, 3, 5, 7, 9 };
private HOTPChecksumProvider() {}
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 = DOUBLE_DIGITS[digit];
}
total += digit;
doubleDigit = !doubleDigit;
}
int result = total % 10;
if (result > 0) {
result = 10 - result;
}
return result;
}
}

View File

@ -1,16 +1,30 @@
package com.cringe_studios.cringe_authenticator_library.impl;
import java.math.BigInteger;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.time.Instant;
import com.cringe_studios.cringe_authenticator_library.OTP;
import com.cringe_studios.cringe_authenticator_library.OTPAlgorithm;
import com.cringe_studios.cringe_authenticator_library.OTPType;
//https://datatracker.ietf.org/doc/html/rfc6238
public class TOTP implements OTP{
public class TOTP extends HOTP {
public TOTP(String secret, OTPAlgorithm algorithm, int digits, long counter, long periodInSeconds, boolean checksum) {
super(secret, algorithm, digits, counter, periodInSeconds, checksum);
this.type = OTPType.TOTP;
}
@Override
public long getNextDueTime() {
// TODO Auto-generated method stub
//return this.period System.time;
return 0;
// e.g. current time: 20 seconds & current period: 30 seconds -> next due time:
// 30 seconds
return (this.getCounter() + 1) * this.getPeriod();
}
@Override
@ -20,9 +34,49 @@ public class TOTP implements OTP{
}
@Override
public String updatePin() {
// TODO Auto-generated method stub
return null;
public long getCounter() {
return Instant.now().getEpochSecond() / this.getPeriod();
}
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 String getPinAt(String key, String time) throws InvalidKeyException, NoSuchAlgorithmException {
int codeDigits = this.getDigits();
// 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 = this.getAlgorithm().hash(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;
}
}