BastiaanJansen/otp-java

TOTP Code always wrong, need help understanding

Closed this issue · 3 comments

Hello

I'm trying to implement a TOTP-Solution for Phone Logins for the STARFACE PBX as a Module.

The classes are all loaded by a Custom ClassLoader during runtime, which has caused issues with other implementations before.
I've tried this and the java-top from (https://github.com/samdjstevens/java-totp). in both cases int he end the TOTP Code from the Mobile app and the PBX never matched.

Some things to know.
The Variables are always passed between the functions with @InputVars and @OutputVars. Already set values are defined as "defaults"

The Module generates a secret for each user with a lenght of 256 Bits (32 Characters) and returns it as a string this is saved in a table on the pbx.

`

@InputVar(label="Secretlenght", description="",type=VariableType.NUMBER)
public Integer Secretlength=32;

@OutputVar(label="Generated_Secret", description="",type=VariableType.STRING)
public String Generated_Secret ="";
	    
StarfaceComponentProvider componentProvider = StarfaceComponentProvider.getInstance(); 
//##########################################################################################

//###################			Code Execution			############################	
@Override
public void execute(IRuntimeEnvironment context) throws Exception 
{
	Generated_Secret = new String(SecretGenerator.generate((Secretlength*8)));
}//END OF EXECUTION

`

On demand the PBX generates a QR Code / URI which can be loaded in the Authenticator app.
This is the example from my Test-Environment:

otpauth://totp/STARFACE%20TOTP%20262?secret=PNOW3KSIX3DICLBGLZ4LF2BXTD2Y5APS6XU2UAEFRVKSZNMTZMKA%3D%3D%3D%3D&issuer=STARFACE%20PBX&algorithm=SHA512&digits=6&period=30

Or in short:
Secret: PNOW3KSIX3DICLBGLZ4LF2BXTD2Y5APS6XU2UAEFRVKSZNMTZMKA====
Algorithm: SHA512
Digits: 6
Period: 30

This generated URI is accepted by my Microsoft Authenticator App without issues.

In order to login using the TOTP, the user has to type *77[LoginID]*[TOTP associated with that LoginID] into a phone
For Example *77262*123456

I'm using this code:

`
public class ValidateTOTP implements IBaseExecutable
{
// ##########################################################################################

@InputVar(label = "LoginID", description = "", type = VariableType.STRING)
public String LoginID = ""; 

@InputVar(label = "Secret", description = "", type = VariableType.STRING)
public String Secret = "";

@InputVar(label = "Code", description = "", type = VariableType.STRING)
public String Code = "";

@InputVar(label = "Digits", description = "", type = VariableType.NUMBER)
public Integer Digits = 6;

@InputVar(label = "Period", description = "", type = VariableType.NUMBER)
public Integer Period = 30;

@InputVar(label = "AllowedTimeDiscrepancy", description = "", type = VariableType.NUMBER)
public Integer AllowedTimeDiscrepancy = 30;

@InputVar(label = "Algorithm", description = "", valueByReferenceAllowed = true)
public HMACAlgorithm HA = HMACAlgorithm.SHA512;

@OutputVar(label = "isValid", description = "", type = VariableType.BOOLEAN)
public boolean isValid = false;

StarfaceComponentProvider componentProvider = StarfaceComponentProvider.getInstance();
// ##########################################################################################

// ################### Code Execution ############################
@Override
public void execute(IRuntimeEnvironment context) throws Exception
{
	Logger log = context.getLog();

	log.debug("Validating One Time Passcode for: " + LoginID + " -> " + Code + "-> " + HA.toString()+ " ->" + Digits);

	TOTPGenerator TOTP = new TOTPGenerator.Builder(Secret.getBytes())
			.withHOTPGenerator(builder -> {
	            builder.withPasswordLength(Digits);
	            builder.withAlgorithm(HA);
	        })
	        .withPeriod(Duration.ofSeconds(Period))
	        .build();
	
	String CodeNow = TOTP.now();
	log.debug(CodeNow +" <==> " + Code);
	
	log.debug(TOTP.getClock().getZone().getId() +" -> "+ TOTP.getPasswordLength() +" -> " + TOTP.getPeriod().toString());
	SimpleDateFormat SDF = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
	
	Date D = Date.from(TOTP.getClock().instant());
	log.debug("TOTP Time: " + SDF.format(D));
	
	isValid = TOTP.verify(Code, AllowedTimeDiscrepancy);
	
}// END OF EXECUTION

`

[2023-12-07T11:57:42,231] [DEBUG] [] [] [_CALL_ENTRYPOINT] Called Number:77262167741
[2023-12-07T11:57:42,232] [DEBUG] [] [] [_CALL_ENTRYPOINT] Triggered
[2023-12-07T11:57:42,232] [DEBUG] [] [] [_CALL_ENTRYPOINT] LoginID: 262
[2023-12-07T11:57:42,232] [DEBUG] [] [] [_CALL_ENTRYPOINT] Pin: 167741
[2023-12-07T11:57:42,232] [DEBUG] [] [] [ValidateTOTP] Validating One Time Passcode for: 262 -> 167741-> SHA512 ->6
[2023-12-07T11:57:42,232] [DEBUG] [] [] [ValidateTOTP] 279227 <==> 167741
[2023-12-07T11:57:42,232] [DEBUG] [] [] [ValidateTOTP] Europe/Berlin -> 6 -> PT30S
[2023-12-07T11:57:42,232] [DEBUG] [] [] [ValidateTOTP] TOTP Time: 07.12.2023 11:57:42
[2023-12-07T11:57:42,232] [DEBUG] [] [] [_CALL_ENTRYPOINT] 262 -> 167741 -> PNOW3KSIX3DICLBGLZ4LF2BXTD2Y5APS6XU2UAEFRVKSZNMTZMKA==== -> false
[2023-12-07T11:57:42,232] [DEBUG] [] [] [_CALL_ENTRYPOINT] Login incorrect!

The Time Shown on the PBX by the TOTP Clock is accurate and the same as my phone, but the TOTP still does not match.

I was wondering, if there was an easy way to debug this.

Or is there an issue with Converting the Secret Bytes to a String and back, that maybe causes this discrepancy?

Sincerely
Fabian95qw

Without knowing anything about PBX, this could be an issue with the secret. I would suggest trying to create a code on your phone and on PBX with a secret manually set.

//edit I might have found the issue i'll keep you posted if it is.
//edit2: Wasn't the solutions, the problem still persists.

Without knowing anything about PBX, this could be an issue with the secret. I would suggest trying to create a code on your phone and on PBX with a secret manually set.

Ok i tried doing everything Harcoded, except for the Code i've to provide from my phone:

@InputVar(label = "Code", description = "", type = VariableType.STRING)
	public String Code = "";
	
	StarfaceComponentProvider componentProvider = StarfaceComponentProvider.getInstance();
##########################################################################################

	// ################### Code Execution ############################
	@Override
	public void execute(IRuntimeEnvironment context) throws Exception
	{
		Logger log = context.getLog();

		String Secret = "AABBCCDD";
				
		TOTPGenerator TOTP = new TOTPGenerator.Builder(Secret.getBytes())
				.withHOTPGenerator(builder -> {
		            builder.withPasswordLength(6);
		            builder.withAlgorithm(HMACAlgorithm.SHA512);
		        })
		        .withPeriod(Duration.ofSeconds(30))
		        .build();
		
		log.debug(TOTP.now() +" <==> " + Code);
		log.debug(TOTP.verify(Code));
		
	}// END OF EXECUTION

And the OTP URI i used for my Phone:

otpauth://totp/Test?secret=AABBCCDD&issuer=Test&algorithm=SHA512&digits=6&period=30

And yet the Codes do not match.

[2023-12-11T11:53:22,121] [DEBUG] [] [] [TestTOTP] 756078 <==> 167543
[2023-12-11T11:53:22,122] [DEBUG] [] [] [TestTOTP] false

I'm pretty sure i still have some issues with the Time/TimeZone. but i don't really know how to debug it.

//edit 3:

The issue seems to be with my TOTP url. I did some testing with a my local machine with this code.
	public static void main(String[] args)
	{

		String Secret = "AABBCCDD";
		
		TOTPGenerator TOTP = new TOTPGenerator.Builder(Secret.getBytes())
				.withHOTPGenerator(builder -> {
		            builder.withPasswordLength(6);
		            builder.withAlgorithm(HMACAlgorithm.SHA512);
		        })
		        .withPeriod(Duration.ofSeconds(30))
		        .build();
		
		SimpleDateFormat SDF = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss");
		while(true)
		{

			System.out.println(SDF.format(Date.from(TOTP.getClock().instant()))+" "+TOTP.now());
			try
			{
				Thread.sleep(1000);
			}
			catch (InterruptedException e)
			{
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}

	}

And i compared them with the TOTP Code my PBX is generating so the issue is actually with the Phone.
It seems my TOTP Url is somewhat wrong, i tried it with both

otpauth://totp/Test?secret=AABBCCDD&issuer=Test&algorithm=SHA512&digits=6&period=30
and
otpauth://totp/Test?secret=AABBCCDD&issuer=Test&algorithm=HmacSHA512&digits=6&period=30

both are accepted by the Microsoft Authenticator, but both generate wrong codes.

I've about 3 Dozen other TOTP Authenticators in my Microsoft Authenticators which are all working fine.
Now i suspect, that i'm doing something wrong with my TOTP Url, but i've to do some more experiments.

Ok in the end it was very simple.

Microsoft Authenticator doesn't Support SHA512, and will default to SHA1 if an incompatible SHA-Algorithm is presented, which i find is a very stupid way to deal with this...

The issue is resolved.

Sincerely
Fabian95qw