/* See LICENSE for licensing and NOTICE for copyright. */
package org.cryptacular.generator;
import org.bouncycastle.crypto.Digest;
import org.bouncycastle.crypto.macs.HMac;
import org.bouncycastle.crypto.params.KeyParameter;
import org.cryptacular.util.ByteUtil;
/**
* Abstract base class for <a href="https://tools.ietf.org/html/rfc4226">HOTP</a> and <a
* href="https://tools.ietf.org/html/rfc6238">TOTP</a> OTP generation schemes.
*
* @author Middleware Services
*/
public abstract class AbstractOTPGenerator
{
/** Array of modulus values indexed per number of digits in OTP output. */
private static final int[] MODULUS = new int[] {
1,
10,
100,
1000,
10000,
100000,
1000000,
10000000,
100000000,
1000000000,
};
/** Number of digits in generated OTP. */
private int numberOfDigits = 6;
/** @return Number of digits in generated OTP. */
public int getNumberOfDigits()
{
return numberOfDigits;
}
/**
* Sets the numbers in the generated OTP.
*
* @param digits Number of digits in generated OTP. MUST be in the range 6 - 9. Default is 6.
*/
public void setNumberOfDigits(final int digits)
{
if (digits < 6 || digits > 9) {
throw new IllegalArgumentException("Number of generated digits must be in range 6-9.");
}
this.numberOfDigits = digits;
}
/**
* Internal OTP generation method.
*
* @param key Per-user key.
* @param count Counter moving factor.
*
* @return Integer OTP.
*/
protected int generateInternal(final byte[] key, final long count)
{
final HMac hmac = new HMac(getDigest());
final byte[] output = new byte[hmac.getMacSize()];
hmac.init(new KeyParameter(key));
hmac.update(ByteUtil.toBytes(count), 0, 8);
hmac.doFinal(output, 0);
return truncate(output) % MODULUS[numberOfDigits];
}
/** @return Digest algorithm used for HMAC operation. */
protected abstract Digest getDigest();
/**
* Truncates HMAC output onto an unsigned (i.e. 31-bit) integer.
*
* @param hmac HMAC output.
*
* @return Truncated output.
*/
private int truncate(final byte[] hmac)
{
final int offset = hmac[19] & 0xf;
return
(hmac[offset] & 0x7f) << 24 |
(hmac[offset + 1] & 0xff) << 16 |
(hmac[offset + 2] & 0xff) << 8 |
(hmac[offset + 3] & 0xff);
}
}