From b3ed9fe292e59ac55d39f33f22459199ee0e086d Mon Sep 17 00:00:00 2001 From: Jon Chambers Date: Sun, 24 May 2026 17:12:15 -0400 Subject: [PATCH] Simplify `Mac` instance creation --- .../otp/HmacOneTimePasswordGenerator.java | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/eatthepath/otp/HmacOneTimePasswordGenerator.java b/src/main/java/com/eatthepath/otp/HmacOneTimePasswordGenerator.java index 557a61f..cdeb8ac 100644 --- a/src/main/java/com/eatthepath/otp/HmacOneTimePasswordGenerator.java +++ b/src/main/java/com/eatthepath/otp/HmacOneTimePasswordGenerator.java @@ -36,11 +36,9 @@ * @author Jon Chambers */ public class HmacOneTimePasswordGenerator { - private final Mac prototypeMac; + private final String algorithm; private final int passwordLength; - private final int modDivisor; - private final String formatString; /** @@ -86,7 +84,14 @@ public HmacOneTimePasswordGenerator(final int passwordLength) { */ HmacOneTimePasswordGenerator(final int passwordLength, final String algorithm) throws UncheckedNoSuchAlgorithmException { try { - this.prototypeMac = Mac.getInstance(algorithm); + // Fail fast if the requested algorithm isn't supported + final Mac mac = Mac.getInstance(algorithm); + + if (mac.getMacLength() < 8) { + throw new IllegalArgumentException(algorithm + " has MAC length less than 8 bytes"); + } + + this.algorithm = algorithm; } catch (final NoSuchAlgorithmException e) { throw new UncheckedNoSuchAlgorithmException(e); } @@ -142,27 +147,20 @@ public int generateOneTimePassword(final Key key, final long counter) throws Inv mac.update(array, 0, 8); mac.doFinal(array, 0); } catch (final ShortBufferException e) { - // We allocated the buffer to (at least) match the size of the MAC length at construction time, so this - // should never happen. - throw new RuntimeException(e); + // We allocated the buffer's backing array to match the MAC length, so this should never happen. + throw new AssertionError("Generated MAC longer than self-reported MAC length", e); } final int offset = buffer.get(buffer.capacity() - 1) & 0x0f; - return (buffer.getInt(offset) & 0x7fffffff) % this.modDivisor; + return (buffer.getInt(offset) & 0x7fff_ffff) % this.modDivisor; } private Mac getMac() { try { - // Cloning is generally cheaper than `Mac.getInstance`, but isn't GUARANTEED to be supported. - return (Mac) this.prototypeMac.clone(); - } catch (CloneNotSupportedException e) { - try { - return Mac.getInstance(this.prototypeMac.getAlgorithm()); - } catch (final NoSuchAlgorithmException ex) { - // This should be impossible; we're getting the algorithm from a Mac that already exists, and so the - // algorithm must be supported. - throw new RuntimeException(ex); - } + return Mac.getInstance(algorithm); + } catch (final NoSuchAlgorithmException e) { + // We checked that we can instantiate a Mac with the given algorithm at construction time + throw new AssertionError("Previously-supported algorithm no longer found", e); } } @@ -288,6 +286,6 @@ public int getPasswordLength() { * @return the name of the HMAC algorithm used by this generator */ public String getAlgorithm() { - return this.prototypeMac.getAlgorithm(); + return this.algorithm; } }