mirror of
https://github.com/gyurix1968/guacamole-client.git
synced 2025-09-05 12:47:41 +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
|
||||
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>
|
||||
</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>
|
||||
|
||||
</project>
|
||||
|
@@ -237,3 +237,40 @@ JSR-330 / Dependency Injection for Java (http://code.google.com/p/atinject/)
|
||||
License(s):
|
||||
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