3333
3434import javax .crypto .spec .SecretKeySpec ;
3535
36+ import junitparams .JUnitParamsRunner ;
37+ import junitparams .Parameters ;
3638import org .junit .Test ;
39+ import org .junit .runner .RunWith ;
3740
41+ @ RunWith (JUnitParamsRunner .class )
3842public class TimeBasedOneTimePasswordGeneratorTest extends HmacOneTimePasswordGeneratorTest {
3943
4044 @ Override
@@ -54,93 +58,40 @@ public void testGetTimeStep() throws NoSuchAlgorithmException {
5458 }
5559
5660 /**
57- * Tests time-based one-time password generation using HMAC-SHA1 and the test vectors from
58- * <a href="https://tools.ietf.org/html/rfc6238#appendix-B">RFC 6238, Appendix B</a>.
61+ * Tests time-based one-time password generation using the test vectors from
62+ * <a href="https://tools.ietf.org/html/rfc6238#appendix-B">RFC 6238, Appendix B</a>. Note that the RFC
63+ * incorrectly states that the same key is used for all test vectors. The
64+ * <a href="https://www.rfc-editor.org/errata_search.php?rfc=6238&eid=2866">>errata</a> correctly points out that
65+ * different keys are used for each of the various HMAC algorithms.
5966 */
6067 @ Test
61- public void testGenerateOneTimePasswordSha1 () throws NoSuchAlgorithmException , InvalidKeyException {
62- final TimeBasedOneTimePasswordGenerator totp =
63- new TimeBasedOneTimePasswordGenerator (30 , TimeUnit .SECONDS , 8 , TimeBasedOneTimePasswordGenerator .TOTP_ALGORITHM_HMAC_SHA1 );
64-
65- final Key key ;
66- {
67- final String keyString = "12345678901234567890" ;
68- key = new SecretKeySpec (keyString .getBytes (StandardCharsets .US_ASCII ), "RAW" );
69- }
70-
71- final Map <Date , Integer > expectedPasswords = new HashMap <>();
72- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (59 )), 94287082 );
73- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1111111109 )), 7081804 );
74- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1111111111 )), 14050471 );
75- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1234567890 )), 89005924 );
76- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (2000000000 )), 69279037 );
77- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (20000000000L )), 65353130 );
68+ @ Parameters ({
69+ "59, 94287082, 12345678901234567890, HmacSHA1" ,
70+ "1111111109, 7081804, 12345678901234567890, HmacSHA1" ,
71+ "1111111111, 14050471, 12345678901234567890, HmacSHA1" ,
72+ "1234567890, 89005924, 12345678901234567890, HmacSHA1" ,
73+ "2000000000, 69279037, 12345678901234567890, HmacSHA1" ,
74+ "20000000000, 65353130, 12345678901234567890, HmacSHA1" ,
75+ "59, 46119246, 12345678901234567890123456789012, HmacSHA256" ,
76+ "1111111109, 68084774, 12345678901234567890123456789012, HmacSHA256" ,
77+ "1111111111, 67062674, 12345678901234567890123456789012, HmacSHA256" ,
78+ "1234567890, 91819424, 12345678901234567890123456789012, HmacSHA256" ,
79+ "2000000000, 90698825, 12345678901234567890123456789012, HmacSHA256" ,
80+ "20000000000, 77737706, 12345678901234567890123456789012, HmacSHA256" ,
81+ "59, 90693936, 1234567890123456789012345678901234567890123456789012345678901234, HmacSHA512" ,
82+ "1111111109, 25091201, 1234567890123456789012345678901234567890123456789012345678901234, HmacSHA512" ,
83+ "1111111111, 99943326, 1234567890123456789012345678901234567890123456789012345678901234, HmacSHA512" ,
84+ "1234567890, 93441116, 1234567890123456789012345678901234567890123456789012345678901234, HmacSHA512" ,
85+ "2000000000, 38618901, 1234567890123456789012345678901234567890123456789012345678901234, HmacSHA512" ,
86+ "20000000000, 47863826, 1234567890123456789012345678901234567890123456789012345678901234, HmacSHA512" })
87+ public void testGenerateOneTimePassword (final long epochSeconds , final int expectedOneTimePassword , final String keyString , final String algorithm ) throws Exception {
7888
79- this .validateOneTimePasswords (totp , key , expectedPasswords );
80- }
81-
82- /**
83- * Tests time-based one-time password generation using HMAC-SHA256 and the test vectors from
84- * <a href="https://tools.ietf.org/html/rfc6238#appendix-B">RFC 6238, Appendix B</a>.
85- */
86- @ Test
87- public void testGenerateOneTimePasswordSha256 () throws NoSuchAlgorithmException , InvalidKeyException {
8889 final TimeBasedOneTimePasswordGenerator totp =
89- new TimeBasedOneTimePasswordGenerator (30 , TimeUnit .SECONDS , 8 , TimeBasedOneTimePasswordGenerator .TOTP_ALGORITHM_HMAC_SHA256 );
90-
91- final Key key ;
92- {
93- // The RFC incorrectly states that the same key is used for all test vectors, but that's not actually true;
94- // see the errata (https://www.rfc-editor.org/errata_search.php?rfc=6238&eid=2866) for details
95- final String keyString = "12345678901234567890123456789012" ;
96- key = new SecretKeySpec (keyString .getBytes (StandardCharsets .US_ASCII ), "RAW" );
97- }
98-
99- final Map <Date , Integer > expectedPasswords = new HashMap <>();
100- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (59 )), 46119246 );
101- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1111111109 )), 68084774 );
102- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1111111111 )), 67062674 );
103- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1234567890 )), 91819424 );
104- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (2000000000 )), 90698825 );
105- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (20000000000L )), 77737706 );
106-
107- this .validateOneTimePasswords (totp , key , expectedPasswords );
108- }
109-
110- /**
111- * Tests time-based one-time password generation using HMAC-SHA512 and the test vectors from
112- * <a href="https://tools.ietf.org/html/rfc6238#appendix-B">RFC 6238, Appendix B</a>.
113- */
114- @ Test
115- public void testGenerateOneTimePasswordSha512 () throws NoSuchAlgorithmException , InvalidKeyException {
116- final TimeBasedOneTimePasswordGenerator totp =
117- new TimeBasedOneTimePasswordGenerator (30 , TimeUnit .SECONDS , 8 , TimeBasedOneTimePasswordGenerator .TOTP_ALGORITHM_HMAC_SHA512 );
118-
119- final Key key ;
120- {
121- // The RFC incorrectly states that the same key is used for all test vectors, but that's not actually true;
122- // see the errata (https://www.rfc-editor.org/errata_search.php?rfc=6238&eid=2866) for details
123- final String keyString = "1234567890123456789012345678901234567890123456789012345678901234" ;
124- key = new SecretKeySpec (keyString .getBytes (StandardCharsets .US_ASCII ), "RAW" );
125- }
126-
127- final Map <Date , Integer > expectedPasswords = new HashMap <>();
128- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (59 )), 90693936 );
129- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1111111109 )), 25091201 );
130- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1111111111 )), 99943326 );
131- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (1234567890 )), 93441116 );
132- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (2000000000 )), 38618901 );
133- expectedPasswords .put (new Date (TimeUnit .SECONDS .toMillis (20000000000L )), 47863826 );
134-
135- this .validateOneTimePasswords (totp , key , expectedPasswords );
136- }
90+ new TimeBasedOneTimePasswordGenerator (30 , TimeUnit .SECONDS , 8 , algorithm );
13791
138- private void validateOneTimePasswords (final TimeBasedOneTimePasswordGenerator totp , final Key key , final Map <Date , Integer > expectedPasswords ) throws InvalidKeyException {
139- for (final Map .Entry <Date , Integer > entry : expectedPasswords .entrySet ()) {
140- final Date date = entry .getKey ();
141- final int expectedPassword = entry .getValue ();
92+ final Key key = new SecretKeySpec (keyString .getBytes (StandardCharsets .US_ASCII ), "RAW" );
93+ final Date date = new Date (TimeUnit .SECONDS .toMillis (epochSeconds ));
14294
143- assertEquals (expectedPassword , totp .generateOneTimePassword (key , date ));
144- }
95+ assertEquals (expectedOneTimePassword , totp .generateOneTimePassword (key , date ));
14596 }
14697}
0 commit comments