mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-05 20:57:40 +00:00
GUACAMOLE-96: Add TOTP generator implementation based on reference implementation from IETF.
This commit is contained in:
37
LICENSE
37
LICENSE
@@ -254,3 +254,40 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
||||||
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
TOTP Reference Implementation (https://tools.ietf.org/id/draft-mraihi-totp-timebased-07.html#Section-Reference-Impl)
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Verson: 07
|
||||||
|
From: 'IETF Trust' (http://trustee.ietf.org/license-info)
|
||||||
|
License(s):
|
||||||
|
BSD 3-clause (extensions/guacamole-auth-duo/src/licenses/bundled/totp-reference-impl-07/license.txt)
|
||||||
|
|
||||||
|
Copyright (c) 2011 IETF Trust and the persons identified as authors
|
||||||
|
of the code. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of Internet Society, IETF or IETF Trust, nor the names
|
||||||
|
of specific contributors, may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
@@ -154,6 +154,21 @@
|
|||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
|
<!-- Guava - Utility Library -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.google.guava</groupId>
|
||||||
|
<artifactId>guava</artifactId>
|
||||||
|
<version>18.0</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<!-- JUnit -->
|
||||||
|
<dependency>
|
||||||
|
<groupId>junit</groupId>
|
||||||
|
<artifactId>junit</artifactId>
|
||||||
|
<version>4.12</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
@@ -237,3 +237,40 @@ JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/)
|
|||||||
License(s):
|
License(s):
|
||||||
Apache v2.0 (bundled/javax.inject-1/LICENSE-2.0.txt)
|
Apache v2.0 (bundled/javax.inject-1/LICENSE-2.0.txt)
|
||||||
|
|
||||||
|
|
||||||
|
TOTP Reference Implementation (https://tools.ietf.org/id/draft-mraihi-totp-timebased-07.html#Section-Reference-Impl)
|
||||||
|
-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Verson: 07
|
||||||
|
From: 'IETF Trust' (http://trustee.ietf.org/license-info)
|
||||||
|
License(s):
|
||||||
|
BSD 3-clause (bundled/totp-reference-impl-07/license.txt)
|
||||||
|
|
||||||
|
Copyright (c) 2011 IETF Trust and the persons identified as authors
|
||||||
|
of the code. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of Internet Society, IETF or IETF Trust, nor the names
|
||||||
|
of specific contributors, may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
@@ -0,0 +1,28 @@
|
|||||||
|
Copyright (c) 2011 IETF Trust and the persons identified as authors
|
||||||
|
of the code. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
- Redistributions of source code must retain the above copyright notice, this
|
||||||
|
list of conditions and the following disclaimer.
|
||||||
|
|
||||||
|
- Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
this list of conditions and the following disclaimer in the documentation
|
||||||
|
and/or other materials provided with the distribution.
|
||||||
|
|
||||||
|
- Neither the name of Internet Society, IETF or IETF Trust, nor the names
|
||||||
|
of specific contributors, may be used to endorse or promote products
|
||||||
|
derived from this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
||||||
|
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||||
|
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
@@ -0,0 +1,402 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.apache.guacamole.totp;
|
||||||
|
|
||||||
|
import com.google.common.primitives.Longs;
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import java.security.Key;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import javax.crypto.Mac;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: This TOTP implementation is based on the TOTP reference implementation
|
||||||
|
* provided by the IETF Trust at:
|
||||||
|
*
|
||||||
|
* https://tools.ietf.org/id/draft-mraihi-totp-timebased-07.html#Section-Reference-Impl
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011 IETF Trust and the persons identified as authors
|
||||||
|
* of the code. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Internet Society, IETF or IETF Trust, nor the names
|
||||||
|
* of specific contributors, may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generator which uses the TOTP algorithm to generate authentication codes.
|
||||||
|
*/
|
||||||
|
public class TOTPGenerator {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default time to use as the basis for comparison when transforming
|
||||||
|
* provided TOTP timestamps into counter values required for HOTP, in
|
||||||
|
* seconds since midnight, 1970-01-01, UTC (UNIX epoch).
|
||||||
|
*/
|
||||||
|
public static final long DEFAULT_START_TIME = 0;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default frequency at which new TOTP codes should be generated (and
|
||||||
|
* old codes invalidated), in seconds.
|
||||||
|
*/
|
||||||
|
public static final long DEFAULT_TIME_STEP = 30;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The TOTP generation mode. The mode dictates the hash function which
|
||||||
|
* should be used to generate authentication codes, as well as the required
|
||||||
|
* key size.
|
||||||
|
*/
|
||||||
|
private final Mode mode;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The shared key to use to generate authentication codes. The size
|
||||||
|
* required for this key depends on the generation mode.
|
||||||
|
*/
|
||||||
|
private final Key key;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of codes to generate, in digits.
|
||||||
|
*/
|
||||||
|
private final int length;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The base time against which the timestamp specified for each TOTP
|
||||||
|
* should be compared to produce the corresponding HOTP counter value, in
|
||||||
|
* seconds since midnight, 1970-01-01, UTC (UNIX epoch). This is the value
|
||||||
|
* value referred to as "T0" in the TOTP specification.
|
||||||
|
*/
|
||||||
|
private final long startTime;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The frequency that new TOTP codes should be generated and invalidated,
|
||||||
|
* in seconds. This is the value referred to as "X" in the TOTP
|
||||||
|
* specification.
|
||||||
|
*/
|
||||||
|
private final long timeStep;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The operating mode for TOTP, defining the hash algorithm to be used.
|
||||||
|
*/
|
||||||
|
public enum Mode {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TOTP mode which generates hashes using SHA1. TOTP in SHA1 mode
|
||||||
|
* requires 160-bit keys.
|
||||||
|
*/
|
||||||
|
SHA1("HmacSHA1"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TOTP mode which generates hashes using SHA256. TOTP in SHA256 mode
|
||||||
|
* requires 256-bit keys.
|
||||||
|
*/
|
||||||
|
SHA256("HmacSHA256"),
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TOTP mode which generates hashes using SHA512. TOTP in SHA512 mode
|
||||||
|
* requires 512-bit keys.
|
||||||
|
*/
|
||||||
|
SHA512("HmacSHA512");
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The name of the HMAC algorithm which the TOTP implementation should
|
||||||
|
* use when operating in this mode, in the format required by
|
||||||
|
* Mac.getInstance().
|
||||||
|
*/
|
||||||
|
private final String algorithmName;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new TOTP operating mode which is associated with the
|
||||||
|
* given HMAC algorithm.
|
||||||
|
*
|
||||||
|
* @param algorithmName
|
||||||
|
* The name of the HMAC algorithm which the TOTP implementation
|
||||||
|
* should use when operating in this mode, in the format required
|
||||||
|
* by Mac.getInstance().
|
||||||
|
*/
|
||||||
|
private Mode(String algorithmName) {
|
||||||
|
this.algorithmName = algorithmName;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the name of the HMAC algorithm which the TOTP implementation
|
||||||
|
* should use when operating in this mode. The name returned will be
|
||||||
|
* in the format required by Mac.getInstance().
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The name of the HMAC algorithm which the TOTP implementation
|
||||||
|
* should use.
|
||||||
|
*/
|
||||||
|
public String getAlgorithmName() {
|
||||||
|
return algorithmName;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new TOTP generator which uses the given shared key to generate
|
||||||
|
* authentication codes. The provided generation mode dictates the size of
|
||||||
|
* the key required, while the given start time and time step dictate how
|
||||||
|
* timestamps provided for code generation are converted to the counter
|
||||||
|
* value used by HOTP (the algorithm which forms the basis of TOTP).
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* The shared key to use to generate authentication codes.
|
||||||
|
*
|
||||||
|
* @param mode
|
||||||
|
* The mode in which the TOTP algorithm should operate.
|
||||||
|
*
|
||||||
|
* @param length
|
||||||
|
* The length of the codes to generate, in digits. As required
|
||||||
|
* by the specification, this value MUST be at least 6 but no greater
|
||||||
|
* than 8.
|
||||||
|
*
|
||||||
|
* @param startTime
|
||||||
|
* The base time against which the timestamp specified for each TOTP
|
||||||
|
* should be compared to produce the corresponding HOTP counter value,
|
||||||
|
* in seconds since midnight, 1970-01-01, UTC (UNIX epoch). This is the
|
||||||
|
* value referred to as "T0" in the TOTP specification.
|
||||||
|
*
|
||||||
|
* @param timeStep
|
||||||
|
* The frequency that new TOTP codes should be generated and
|
||||||
|
* invalidated, in seconds. This is the value referred to as "X" in the
|
||||||
|
* TOTP specification.
|
||||||
|
*
|
||||||
|
* @throws InvalidKeyException
|
||||||
|
* If the provided key is invalid for the requested TOTP mode.
|
||||||
|
*/
|
||||||
|
public TOTPGenerator(byte[] key, Mode mode, int length, long startTime,
|
||||||
|
long timeStep) throws InvalidKeyException {
|
||||||
|
|
||||||
|
// Validate length is within spec
|
||||||
|
if (length < 6 || length > 8)
|
||||||
|
throw new IllegalArgumentException("TOTP codes must be at least 6 "
|
||||||
|
+ "digits and no more than 8 digits.");
|
||||||
|
|
||||||
|
this.key = new SecretKeySpec(key, "RAW");
|
||||||
|
this.mode = mode;
|
||||||
|
this.length = length;
|
||||||
|
this.startTime = startTime;
|
||||||
|
this.timeStep = timeStep;
|
||||||
|
|
||||||
|
// Verify key validity
|
||||||
|
getMacInstance(this.mode, this.key);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new TOTP generator which uses the given shared key to generate
|
||||||
|
* authentication codes. The provided generation mode dictates the size of
|
||||||
|
* the key required. The start time and time step used to produce the
|
||||||
|
* counter value used by HOTP (the algorithm which forms the basis of TOTP)
|
||||||
|
* are set to the default values recommended by the TOTP specification (0
|
||||||
|
* and 30 respectively).
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* The shared key to use to generate authentication codes.
|
||||||
|
*
|
||||||
|
* @param mode
|
||||||
|
* The mode in which the TOTP algorithm should operate.
|
||||||
|
*
|
||||||
|
* @param length
|
||||||
|
* The length of the codes to generate, in digits. As required
|
||||||
|
* by the specification, this value MUST be at least 6 but no greater
|
||||||
|
* than 8.
|
||||||
|
*
|
||||||
|
* @throws InvalidKeyException
|
||||||
|
* If the provided key is invalid for the requested TOTP mode.
|
||||||
|
*/
|
||||||
|
public TOTPGenerator(byte[] key, Mode mode, int length)
|
||||||
|
throws InvalidKeyException {
|
||||||
|
this(key, mode, length, DEFAULT_START_TIME, DEFAULT_TIME_STEP);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a new Mac instance which produces message authentication codes
|
||||||
|
* using the given secret key and the algorithm required by the given TOTP
|
||||||
|
* mode.
|
||||||
|
*
|
||||||
|
* @param mode
|
||||||
|
* The TOTP mode which dictates the HMAC algorithm to be used.
|
||||||
|
*
|
||||||
|
* @param key
|
||||||
|
* The secret key to use to produce message authentication codes.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A new Mac instance which produces message authentication codes
|
||||||
|
* using the given secret key and the algorithm required by the given
|
||||||
|
* TOTP mode.
|
||||||
|
*
|
||||||
|
* @throws InvalidKeyException
|
||||||
|
* If the provided key is invalid for the requested TOTP mode.
|
||||||
|
*/
|
||||||
|
private static Mac getMacInstance(Mode mode, Key key)
|
||||||
|
throws InvalidKeyException {
|
||||||
|
|
||||||
|
try {
|
||||||
|
Mac hmac = Mac.getInstance(mode.getAlgorithmName());
|
||||||
|
hmac.init(key);
|
||||||
|
return hmac;
|
||||||
|
}
|
||||||
|
catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new UnsupportedOperationException("Support for the HMAC "
|
||||||
|
+ "algorithm required for TOTP in " + mode + " mode is "
|
||||||
|
+ "missing.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculates the HMAC for the given message using the key and algorithm
|
||||||
|
* provided when this TOTPGenerator was created.
|
||||||
|
*
|
||||||
|
* @param message
|
||||||
|
* The message to calculate the HMAC of.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The HMAC of the given message.
|
||||||
|
*/
|
||||||
|
private byte[] getHMAC(byte[] message) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
return getMacInstance(mode, key).doFinal(message);
|
||||||
|
}
|
||||||
|
catch (InvalidKeyException e) {
|
||||||
|
|
||||||
|
// As the key is verified during construction of the TOTPGenerator,
|
||||||
|
// this should never happen
|
||||||
|
throw new IllegalStateException("Provided key became invalid after "
|
||||||
|
+ "passing validation.", e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given an arbitrary integer value, returns a code containing the decimal
|
||||||
|
* representation of that value and exactly the given number of digits. If
|
||||||
|
* the given value has more than the desired number of digits, leading
|
||||||
|
* digits will be truncated to reduce the length. If the given value has
|
||||||
|
* fewer than the desired number of digits, leading zeroes will be added to
|
||||||
|
* increase the length.
|
||||||
|
*
|
||||||
|
* @param value
|
||||||
|
* The value to convert into a decimal code of the given length.
|
||||||
|
*
|
||||||
|
* @param length
|
||||||
|
* The number of digits to include in the code.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* A code containing the decimal value of the given integer, truncated
|
||||||
|
* or padded such that exactly the given number of digits are present.
|
||||||
|
*/
|
||||||
|
private String toCode(int value, int length) {
|
||||||
|
|
||||||
|
// Convert value to simple integer string
|
||||||
|
String valueString = Integer.toString(value);
|
||||||
|
|
||||||
|
// If the resulting string is too long, truncate to the last N digits
|
||||||
|
if (valueString.length() > length)
|
||||||
|
return valueString.substring(valueString.length() - length);
|
||||||
|
|
||||||
|
// Otherwise, add zeroes until the desired length is reached
|
||||||
|
StringBuilder builder = new StringBuilder(length);
|
||||||
|
for (int i = valueString.length(); i < length; i++)
|
||||||
|
builder.append('0');
|
||||||
|
|
||||||
|
// Return the padded integer string
|
||||||
|
builder.append(valueString);
|
||||||
|
return builder.toString();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a TOTP code of the given length using the given absolute
|
||||||
|
* timestamp rather than the current system time.
|
||||||
|
*
|
||||||
|
* @param time
|
||||||
|
* The absolute timestamp to use to generate the TOTP code, in seconds
|
||||||
|
* since midnight, 1970-01-01, UTC (UNIX epoch).
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The TOTP code which corresponds to the given timestamp, having
|
||||||
|
* exactly the given length.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* If the given length is invalid as defined by the TOTP specification.
|
||||||
|
*/
|
||||||
|
public String generate(long time) {
|
||||||
|
|
||||||
|
// Calculate HOTP counter value based on provided time
|
||||||
|
long counter = (time - startTime) / timeStep;
|
||||||
|
byte[] hash = getHMAC(Longs.toByteArray(counter));
|
||||||
|
|
||||||
|
// Calculate HOTP value as defined by section 5.2 of RFC 4226:
|
||||||
|
// https://tools.ietf.org/html/rfc4226#section-5.2
|
||||||
|
int offset = hash[hash.length - 1] & 0xF;
|
||||||
|
int binary
|
||||||
|
= ((hash[offset] & 0x7F) << 24)
|
||||||
|
| ((hash[offset + 1] & 0xFF) << 16)
|
||||||
|
| ((hash[offset + 2] & 0xFF) << 8)
|
||||||
|
| (hash[offset + 3] & 0xFF);
|
||||||
|
|
||||||
|
// Truncate or pad the value accordingly
|
||||||
|
return toCode(binary, length);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a TOTP code of the given length using the current system time.
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
* The TOTP code which corresponds to the current system time, having
|
||||||
|
* exactly the given length.
|
||||||
|
*
|
||||||
|
* @throws IllegalArgumentException
|
||||||
|
* If the given length is invalid as defined by the TOTP specification.
|
||||||
|
*/
|
||||||
|
public String generate() {
|
||||||
|
return generate(System.currentTimeMillis() / 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@@ -0,0 +1,168 @@
|
|||||||
|
/*
|
||||||
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
|
* or more contributor license agreements. See the NOTICE file
|
||||||
|
* distributed with this work for additional information
|
||||||
|
* regarding copyright ownership. The ASF licenses this file
|
||||||
|
* to you 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.apache.guacamole.totp;
|
||||||
|
|
||||||
|
import java.security.InvalidKeyException;
|
||||||
|
import org.junit.Test;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* NOTE: The tests for this TOTP implementation is based on the TOTP reference
|
||||||
|
* implementation provided by the IETF Trust at:
|
||||||
|
*
|
||||||
|
* https://tools.ietf.org/id/draft-mraihi-totp-timebased-07.html#Section-Reference-Impl
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2011 IETF Trust and the persons identified as authors
|
||||||
|
* of the code. All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* - Redistributions of source code must retain the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* - Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* - Neither the name of Internet Society, IETF or IETF Trust, nor the names
|
||||||
|
* of specific contributors, may be used to endorse or promote products
|
||||||
|
* derived from this software without specific prior written permission.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS”
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||||
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||||
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||||
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||||
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||||
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||||
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||||
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||||
|
* POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test which verifies the correctness of the TOTPGenerator class against the
|
||||||
|
* test inputs and results provided in the IETF reference implementation and
|
||||||
|
* spec for TOTP:
|
||||||
|
*
|
||||||
|
* https://tools.ietf.org/id/draft-mraihi-totp-timebased-07.html#Section-Test-Vectors
|
||||||
|
*/
|
||||||
|
public class TOTPGeneratorTest {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the results of generating authentication codes using the TOTP
|
||||||
|
* algorithm in SHA1 mode.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGenerateSHA1() {
|
||||||
|
|
||||||
|
// 160-bit key consisting of the bytes "12345678901234567890" repeated
|
||||||
|
// as necessary
|
||||||
|
final byte[] key = {
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final TOTPGenerator totp = new TOTPGenerator(key, TOTPGenerator.Mode.SHA1, 8);
|
||||||
|
assertEquals("94287082", totp.generate(59));
|
||||||
|
assertEquals("07081804", totp.generate(1111111109));
|
||||||
|
assertEquals("14050471", totp.generate(1111111111));
|
||||||
|
assertEquals("89005924", totp.generate(1234567890));
|
||||||
|
assertEquals("69279037", totp.generate(2000000000));
|
||||||
|
assertEquals("65353130", totp.generate(20000000000L));
|
||||||
|
}
|
||||||
|
catch (InvalidKeyException e) {
|
||||||
|
fail("SHA1 test key is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the results of generating authentication codes using the TOTP
|
||||||
|
* algorithm in SHA256 mode.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGenerateSHA256() {
|
||||||
|
|
||||||
|
// 256-bit key consisting of the bytes "12345678901234567890" repeated
|
||||||
|
// as necessary
|
||||||
|
final byte[] key = {
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final TOTPGenerator totp = new TOTPGenerator(key, TOTPGenerator.Mode.SHA256, 8);
|
||||||
|
assertEquals("46119246", totp.generate(59));
|
||||||
|
assertEquals("68084774", totp.generate(1111111109));
|
||||||
|
assertEquals("67062674", totp.generate(1111111111));
|
||||||
|
assertEquals("91819424", totp.generate(1234567890));
|
||||||
|
assertEquals("90698825", totp.generate(2000000000));
|
||||||
|
assertEquals("77737706", totp.generate(20000000000L));
|
||||||
|
}
|
||||||
|
catch (InvalidKeyException e) {
|
||||||
|
fail("SHA256 test key is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Verifies the results of generating authentication codes using the TOTP
|
||||||
|
* algorithm in SHA512 mode.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void testGenerateSHA512() {
|
||||||
|
|
||||||
|
// 512-bit key consisting of the bytes "12345678901234567890" repeated
|
||||||
|
// as necessary
|
||||||
|
final byte[] key = {
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',
|
||||||
|
'1', '2', '3', '4'
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
final TOTPGenerator totp = new TOTPGenerator(key, TOTPGenerator.Mode.SHA512, 8);
|
||||||
|
assertEquals("90693936", totp.generate(59));
|
||||||
|
assertEquals("25091201", totp.generate(1111111109));
|
||||||
|
assertEquals("99943326", totp.generate(1111111111));
|
||||||
|
assertEquals("93441116", totp.generate(1234567890));
|
||||||
|
assertEquals("38618901", totp.generate(2000000000));
|
||||||
|
assertEquals("47863826", totp.generate(20000000000L));
|
||||||
|
}
|
||||||
|
catch (InvalidKeyException e) {
|
||||||
|
fail("SHA512 test key is invalid.");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user