Giter VIP home page Giter VIP logo

otp-java's Introduction

Hi there ๐Ÿ‘‹

otp-java's People

Contributors

bastiaanjansen avatar wietsejt avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

otp-java's Issues

TOTPGenerator with ".withPeriod()" using less than a second parameter?

If we create a TOTPGenerator with ".withPeriod()" of less than a second (e.g., ".withPeriod(Duration.ofMillis(1))" ), we get an exception:

java.lang.ArithmeticException: / by zero
at com.bastiaanjansen.otp.TOTPGenerator.calculateCounter(TOTPGenerator.java:164)
at com.bastiaanjansen.otp.TOTPGenerator.generate(TOTPGenerator.java:49)
.
.

The reason we are trying this is that, from our testing, it appears that when the TOTPGenerator .generate() method is called, it will generate the same/identical code if the calls are within that Duration.ofSeconds(), i.e., if the the generator is created with .withPeriod(DurationofSeconds(10)), all calls to .generate(), within a 10 second period, return the same code.

We are looking at OTP-Java to generate OTP email codes, so that would mean that if we had requests (from different users) for several OTP email codes within a period of time, then the OTP-Java .generate() method would potentially provide us with the same code for several users.

Am I misinterpreting how this works?

Thanks,
Jim

Flexability when validating TOTP code.

From wikipedia

Some authenticators allow values that should have been generated before or after the current time in order to account for slight clock skews, network latency and user delays.

Is this possible using your library to allow this flexability?

If so, could you post some code showing how this is possible.
Thanks

Documentation Regarding Delay Window for Verification of ToTP

Hello
I would like to use your library for verifying ToTP. I see that the verification method allows delayWindow parameter.
Can you please add some documentation regarding what are the units for the delayWindow? Is it in milliseconds, seconds, minutes or something else?

Thanks.

Codes not being generated correctly

Hey,

probably this is an issue on my end, but I didnt find a discussion forum here.

I try to generate codes, but the codes dont seem to be correct. I think it is related to any time zone issues or something. I am located in germany, means UTC+1, and I use the following code to generate the codes:

String code1 = totp.now(Clock.system(TimeZone.getTimeZone("UTC+1").toZoneId()));

It then generates a code, but when I try to verify it it fails.
E.g. current codes are (the second one is with 30 seconds delay):
image

But correct one would be:
image

Do you have any ideas on that?

Thanks!

TOTPGenerator .withPeriod() question

If we create a TOTPGenerator with ".withPeriod()" of less than a second (e.g., ".withPeriod(Duration.ofMillis(1))" ), we get an exception:

java.lang.ArithmeticException: / by zero
at com.bastiaanjansen.otp.TOTPGenerator.calculateCounter(TOTPGenerator.java:164)
at com.bastiaanjansen.otp.TOTPGenerator.generate(TOTPGenerator.java:49)
.
.

The reason we are trying this is that, from our testing, it appears that when the TOTPGenerator .generate() method is called, it will generate the same/identical code if the calls are within that Duration.ofSeconds(), i.e., if the the generator is created with .withPeriod(DurationofSeconds(10)), all calls to .generate(), within a 10 second period, return the same code.

We are looking at OTP-Java to generate OTP email codes, so that would mean that if we had requests (from different users) for several OTP email codes within a period of time, then the OTP-Java .generate() method would potentially provide us with the same code for several users.

Am I misinterpreting how this works?

Thanks,
Jim

TOTP Code always wrong, need help understanding

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

How to ensure TOTP is one-time in a distributed system.

Consider a clustered RESTful application that generates and validates TOTP using this library.

Is it sufficient to use the same seed across all replicas of the application in order to produce a TOTP that would be validated by any other replica node? In other words, is it safe to assume that each replica node, given the same configuration, should produce the same TOTP and should be able to validate the TOTP produced by any other node?

If the above is true, how to deal with the fact that once the OTP is used on one node, being it a one-time password, no other replica node should accept it?

Is this something users would need to build on top of the library? How about defining a pluggable strategy that would allow your users to store the generated TOTP in a shared storage, for example a self-expiring distributed cache based on hazelcast? If a used token was stored in a shared map (user -> token) until it expires and is removed, no other node would be able to use it.

Validation not works properly if timestep was given in hours or days

Hi,
If i give timestep as hours or days by converting to milliseconds, it was not validating properly(For example if i generate otp ar today 8 Am and it was expiring at day after tomorrow 5:30 AM (GMT time)) and also it takes the time zone calculation as well. Request you to update the code in this issue. I have used HMACSHA512 algorithm with 6 digits. Tried with 30 days but its validating upto 20 days only.

HMAC SHA 256 for TOTP verification

Hello, I was able to get the following code working.

TOTPGenerator totpGen = new TOTPGenerator.Builder(myKey) .withHOTPGenerator(builder -> { builder.withPasswordLength(6); builder.withAlgorithm(HMACAlgorithm.SHA1); }) .withPeriod(Duration.ofSeconds(30)) .build();

Since SHA1 is deprecated I wanted to switch to SHA256. I switched to use your SecretGenerator class for this like so...

byte[] secretKeyBytes = SecretGenerator.generate(256); Base32 base32 = new Base32(); String secretKey = base32.encodeAsString(secretKeyBytes);

This secret key is added to my otp string like so. I have set the algorithm to SHA256. I have tried keeping the secret as is, and removing the padding.

otpauth://totp/testCreator:user?secret=KJFVIRCKGRLVERRUI4ZUEMSUJQ2TIR2CJNITGMSQKI2FQR2JKNATMVKPGZDEOVSJG5LEENRWLJKFAWSUI5AT2PJ5HU&issuer=testCreator&algorithm=SHA256

otpauth://totp/testCreator:user?secret=LJEU4RJXIJMUWSSHINLDON2ZKRME2TKLJZCEKSSVLI2UYVKCI5LTOU2SKNMVISBSKBFVCT2IKBBEYM2FGRIT2PJ5HU======&issuer=testCreator&algorithm=SHA256

When I switch my validation code to use SHA256 as written below my codes never validate. I am guessing this is user error but if you have any ideas or an example it would be most appreciated.

TOTPGenerator totpGen = new TOTPGenerator.Builder(myKey) .withHOTPGenerator(builder -> { builder.withPasswordLength(6); builder.withAlgorithm(HMACAlgorithm.SHA256); }) .withPeriod(Duration.ofSeconds(30)) .build();

Thanks!

Generated secret too long

hi~

thank you for your very useful library!

There are some strange stuff about SecretGenerator Class,
it generate to much long bits than expected..

I think this code is weird.

byte[] bytes = new byte[bits * 8];

    /**
     * Generate an OTP base32 secret
     *
     * @param bits length, this should match the length of the HMAC algorithm type:
     *             SHA1: 160 bits
     *             SHA256: 256 bits
     *             SHA512: 512 bits
     * @return generated secret
     */
    public static byte[] generate(final int bits) {
        if (bits <= 0)
            throw new IllegalArgumentException("Bits must be higher than 0");

        byte[] bytes = new byte[bits * 8];
        SecureRandom random = new SecureRandom();
        random.nextBytes(bytes);

        Base32 encoder = new Base32();
        return encoder.encode(bytes);
    }

And I have one question about Custom Key.
is it okey to generate the combination key with custom seed ( like ID, email etc ) without using SecretGenerator.class
even if bit length is matched with algorithm type

for example,
in case SHA1 - [email protected]_MkpBoD5YCtZi/aTS1EdEHqJneNfsw3oG4.... ( 160bit )
in case SHA256 - [email protected]_MkpBoD5YCtZi/aTS1EdEHqJneNfsw3oG4dqhukGy9l .... ( 256bit )

Difference between Authy and generated code.

Hi,

I'm trying to build my own OTP app using your library, and I'm using my current app (Authy) to check if the codes are the same.

Parsing the OTPAuth URI gives a different code from what Authy returns. I even tried building the TOTP from scratch using the URI content, and trying the different algorithms, but I never had the same code.

What would be the issue here (if it is an issue) ?

Thanks.

Issuer is not properly escaped in path segment of URI

Take the following code:

byte[] secret = SecretGenerator.generate();
TOTPGenerator totp = TOTPGenerator.withDefaultValues(secret);
URI uri = totp.getURI("Acme Co", "myuser");
System.out.println(uri);

This will throw a java.net.URISyntaxException: Illegal character in path at index 19: otpauth://totp/Acme Co:myuser?period=30&digits=6&secret=<secret>&issuer=Acme+Co&algorithm=SHA1

If I escaped the issuer myself:

byte[] secret = SecretGenerator.generate();
TOTPGenerator totp = TOTPGenerator.withDefaultValues(secret);
URI uri = totp.getURI("Acme%20Co", "myuser");
System.out.println(uri);

It outputs otpauth://totp/Acme%20Co:myuser?period=30&digits=6&secret=<secret>&issuer=Acme%2520Co&algorithm=SHA1
Notice the double escaping of issuer=Acme%2520Co in the query parameters.

The path section (both issuer and account) should be escaped when generating the URI.

Does not generate same OTP for same secret and time duration

Java Version: OpenJDK 15
OS: Ubuntu Linux

For the application I am working with, I need to be able to generate the same code for a 15 minute interval. However, this library seems to only allow a max time of 5 minutes before it changes the code.

Here is the code I am using

    @Autowired
    private Cache<String, byte[]> otpKeyCache;

    @Override
    public String generate(String username, String emailAddress)
    {
        var otpGenerator = getOtpGenerator(emailAddress);
        var now = Instant.now();
        var otp = otpGenerator.now();

        logger.info(String.format("Time Step is: %d", otpGenerator.getPeriod().getSeconds()));
        logger.info(String.format("Code for 0 seconds: %s", otp));
        logger.info(String.format("Code for 60 seconds: %s", otpGenerator.at(now.plusSeconds(60))));
        logger.info(String.format("Code for 120 seconds: %s", otpGenerator.at(now.plusSeconds(120))));
        logger.info(String.format("Code for 180 seconds: %s", otpGenerator.at(now.plusSeconds(180))));
        logger.info(String.format("Code for 240 seconds: %s", otpGenerator.at(now.plusSeconds(240))));
        logger.info(String.format("Code for 300 seconds: %s", otpGenerator.at(now.plusSeconds(300))));
        logger.info(String.format("Code for 360 seconds: %s", otpGenerator.at(now.plusSeconds(360))));
        logger.info(String.format("Code for 420 seconds: %s", otpGenerator.at(now.plusSeconds(420))));
        logger.info(String.format("Code for 480 seconds: %s", otpGenerator.at(now.plusSeconds(480))));
        logger.info(String.format("Code for 540 seconds: %s", otpGenerator.at(now.plusSeconds(540))));
        logger.info(String.format("Code for 600 seconds: %s", otpGenerator.at(now.plusSeconds(600))));

        return otp;
    }

    private TOTP getOtpGenerator(String emailAddress)
    {
        var secret = getSecret(emailAddress);

        return createOtpGenerator(secret);
    }

    private byte[] getSecret(String emailAddress)
    {
        var secret = otpKeyCache.getIfPresent(emailAddress);

        if (secret == null)
        {
            secret = SecretGenerator.generate();
            otpKeyCache.put(emailAddress, secret);
        }

        return secret;
    }

    private TOTP createOtpGenerator(byte[] secret)
    {
        var builder = new TOTP.Builder(secret);

        return builder.withPasswordLength(6)
                      .withAlgorithm(HMACAlgorithm.SHA512)
                      .withPeriod(Duration.ofMillis(otpDurationMs))
                      .build();
    }

The attached image shows the code in execution. Please note that this is the first time this method is executed, so it is not getting a "cached" secret.

otp-code-mismatch2

(JAVA-S0324) Private method is never called

Description

This private method is never called. Although it is possible that the method will be invoked through reflection, it is more likely that the method is never used, and should be removed. Unless this method is intended to be used with reflection, it is recommended to remove it to increase โ€ฆ

Occurrences

There is 1 occurrence of this issue in the repository.

See all occurrences on DeepSource โ†’ deepsource.io/gh/BastiaanJansen/OTP-Java/issue/JAVA-S0324/occurrences/

Android or Desktop?

Your library is for android developers or Java desktop applications developers?
Cause I want to use it for my Java Swing desktop application development.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.