samdjstevens / java-totp

A java library for implementing Time-based One Time Passwords for Multi-Factor Authentication.
MIT License
422 stars 103 forks source link

RFE: Validate method for additional security #34

Open ralscha opened 4 years ago

ralscha commented 4 years ago

For a secure TOTP I need a method to check the validity of 3 TOTP codes. In my example I check 24 hours ahead and behind. The 3 codes must exist in this period and they must be consecutive.

The method must accept the secret and 3 TOTP codes and returns two results.

Here an idea how this could look like:

    TimeProvider timeProvider = new SystemTimeProvider();
    CodeGenerator codeGenerator = new DefaultCodeGenerator();
    DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
    long periods = TimeUnit.HOURS.toSeconds(24) / 30; 
    //would be nice if I could use verifier.getTimePeriod(), instead of the hardcoded 30
    //or there is a method to set the discrepancy in seconds and the library calculates the periods

    verifier.setAllowedTimePeriodDiscrepancy((int)periods); 
    // setAllowedTimePeriodDiscrepancy does not accept long

    VerificationResult result = verifier.areCodesValid(secret, code1, code2, code3);
    //result should contain a boolean, if the codes are valid and consecutive
    //and it should return a time shift number (int). How many periods the codes are behind or ahead

For my example TOTP application I had to write my own TOTP verifier based on the now archived aerogear project. Would be nice if I could switch to your library.

See also this issue for more details why this method is needed.

samdjstevens commented 4 years ago

Hey,

Thanks for the RFE - I think this should be fairly trivial to implement and can see it being useful.

I'm not so sure about the return value needing to expose both validity, and time shift data - is allowing such a huge window of 24 hours really necessary?

When asking the user for 3 consecutive codes, would a period of say 5 minutes before/after not be enough for them to gather their 3 codes (as they generate every 30 seconds)?

ralscha commented 4 years ago

I'm not the expert, but I think a window of at least 1 hour because of summer/winter time makes sense. Maybe 24 hours is also a good idea when somebody changes the system clock (instead of the timezone) when he travels around. And according to the author of the issue there is no harm in checking such a large window.

I guess this window should be configurable anyway. A project can decide what makes sense and what not.

samdjstevens commented 4 years ago

I've started work on this here: https://github.com/samdjstevens/java-totp/pull/35

Returning the time period as well as the true/false of code(s) being valid does make the API a bit more verbose - and I don't think the time period differential will be used a great deal - but I think it's still acceptable, and I have done so in a backwards compatiple way.

With the PR, your solution would look something like this:

TimeProvider timeProvider = new SystemTimeProvider();
CodeGenerator codeGenerator = new DefaultCodeGenerator();
DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);
verifier.setAllowedTimePeriodDuration(Duration.ofHours(24));

VerifyResult result = verifier.verifyConsecutiveCodes(secret, code1, code2, code3);
boolean isValid = result.isValid();
int timePeriodDifference = result.getTimePeriodDifference();
ralscha commented 4 years ago

Hi. That looks great.

samdjstevens commented 4 years ago

Just an update on this - whilst looking into the required code changes for this request I stumbled across a few areas where I think the internal design/external API isn't quite right currently and should be improved.

Alongside implementing the above I have refactored the API in a non backwards compatible way, so shall be releasing this feature as part of the upcoming 2.0.0 release of the library.

The above example of the new feature will be slightly different now:

TimeProvider timeProvider = new SystemTimeProvider();
CodeGenerator codeGenerator = new DefaultCodeGenerator();
DefaultCodeVerifier verifier = new DefaultCodeVerifier(codeGenerator, timeProvider);

List<String> codes = ...

// Verify that the list of codes are valid, allowing for a time drift of +/- 24 hours
VerifyResult result = verifier.verifyConsecutiveCodes(secret, codes, Duration.ofHours(24));
boolean isValid = result.isValid();
Duration timeDrift = result.getTimeDrift();
// "Your clock is " + timeDrift.getSeconds() + " seconds out from the server."
digvijaydl commented 3 years ago

Hi, Thanks for wonderful library. When do you plan to release upcoming 2.0.0 library and what are new features? Also, want to know if this library complies with RFC4226 / RFC6238 standards?

Thanks in advance.