
org.unitedid.yhsm.internal.OATH Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of YubiHSM Show documentation
Show all versions of YubiHSM Show documentation
A java library (API/driver) for the YubiHSM
The newest version!
/*
* Copyright (c) 2011 - 2013 United ID.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.unitedid.yhsm.internal;
import org.unitedid.yhsm.YubiHSM;
import org.unitedid.yhsm.utility.IntRange;
import java.text.DecimalFormat;
import java.util.Arrays;
import static org.unitedid.yhsm.internal.Defines.YSM_TEMP_KEY_HANDLE;
import static org.unitedid.yhsm.utility.Utils.*;
/** OATH
implements OATH HOTP/TOTP validation*/
public class OATH {
/** Private constructor */
private OATH() {}
/**
* Validate OTP by a token whose seed is available to the YubiHSM through an AEAD.
*
* @param hsm the current hsm object
* @param keyHandle a keyHandle with the permission YSM_TEMP_KEY_LOAD enabled
* @param nonce the nonce used to generate the AEAD
* @param aead the AEAD based on the token seed
* @param counter the current OTP counter
* @param otp the token OTP
* @return return next counter value on success, 0 if the OTP couldn't be validated
* @throws YubiHSMInputException argument exceptions
* @throws YubiHSMCommandFailedException command failed exception
* @throws YubiHSMErrorException error exception
*/
public static synchronized String HOTP(YubiHSM hsm, int keyHandle, String nonce, String aead,
int counter, String otp)
throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
int otpLength = otp.trim().length();
if (otpLength < 6 || otpLength > 8) {
throw new YubiHSMInputException(
"OTP not of required length, should be between 6-8 digits long but was " + otpLength);
}
hsm.loadTemporaryKey(nonce, keyHandle, aead);
String hmac = hsm.generateHMACSHA1(longToByteArray(counter), YSM_TEMP_KEY_HANDLE, true, false).get("hash");
String code = truncate(hmac, otpLength);
return code;
}
/**
* Validate OATH-HOTP OTP by a token whose seed is available to the YubiHSM through an AEAD.
*
* @param hsm the current hsm object
* @param keyHandle a keyHandle with the permission YSM_TEMP_KEY_LOAD enabled
* @param nonce the nonce used to generate the AEAD
* @param aead the AEAD based on the token seed
* @param counter the current OTP counter
* @param otp the token OTP
* @param lookAhead the number of iterations to run to find the current users OTP
* @return return next counter value on success, 0 if the OTP couldn't be validated
* @throws YubiHSMInputException argument exceptions
* @throws YubiHSMCommandFailedException command failed exception
* @throws YubiHSMErrorException error exception
*/
public static int validateHOTP(YubiHSM hsm, int keyHandle, String nonce, String aead, int counter,
String otp, int lookAhead)
throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
lookAhead += counter;
for (; counter < lookAhead; counter++) {
if (HOTP(hsm, keyHandle, nonce, aead, counter, otp).equals(otp)) {
return counter + 1;
}
}
return 0;
}
/**
* Validate OATH-TOTP OTP by a token whose seed is available to the YubiHSM through an AEAD.
*
* @param hsm the current hsm object
* @param keyHandle a keyHandle with the permission YSM_TEMP_KEY_LOAD enabled
* @param nonce the nonce used to generate the AEAD
* @param aead the AEAD based on the token seed
* @param otp the token OTP
* @param period an integer giving the period between changes of the OTP value in seconds
* @param drift drift of the local clock to the client clock, can be used to adjust the time skew without
* changing the size of @backwardDrift and @forwardDrift
* @param backwardDrift the number of @period's we allow to backstep
* @param forwardDrift the number of @period's we allow to look ahead
* @return return boolean, true if the OTP validated, false if the OTP validation failed
* @throws YubiHSMInputException argument exceptions
* @throws YubiHSMCommandFailedException command failed exception
* @throws YubiHSMErrorException error exception
*/
public static boolean validateTOTP(YubiHSM hsm, int keyHandle, String nonce, String aead, String otp,
int period, int drift, int backwardDrift, int forwardDrift)
throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
long time = System.currentTimeMillis() / 1000;
int window = -(int) (time / period);
for (int i : new IntRange(Math.max(window, -backwardDrift), forwardDrift + 1)) {
int timeDrift = (drift + i) * period;
int t = (int) ((time + timeDrift) / period);
if (HOTP(hsm, keyHandle, nonce, aead, t, otp).equals(otp)) {
return true;
}
}
return false;
}
/**
* Truncate HMAC to an OTP code
*
* @param hmac the hmac
* @param otpLength the length of the OTP (6-8 digits)
* @return the OTP code
* @throws YubiHSMInputException argument exceptions
*/
public static String truncate(String hmac, int otpLength) throws YubiHSMInputException {
byte[] hmacBA = hexToByteArray(hmac);
validateByteArray("hmacBA", hmacBA, 0, 20, 0);
int offset = hmacBA[19] & 0xf;
int binCode = (hmacBA[offset] & 0x7f) << 24 |
(hmacBA[offset+1] & 0xff) << 16 |
(hmacBA[offset+2] & 0xff) << 8 |
(hmacBA[offset+3] & 0xff);
// Mmm leading zeros is a pain
char[] zeros = new char[otpLength];
Arrays.fill(zeros, '0');
DecimalFormat decimalFormat = new DecimalFormat(String.valueOf(zeros));
return decimalFormat.format(binCode % (Math.pow(10, otpLength)));
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy