You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@guacamole.apache.org by vn...@apache.org on 2018/02/05 18:04:25 UTC

[07/21] guacamole-client git commit: GUACAMOLE-96: Add TOTP generator implementation based on reference implementation from IETF.

GUACAMOLE-96: Add TOTP generator implementation based on reference implementation from IETF.

Project: http://git-wip-us.apache.org/repos/asf/guacamole-client/repo
Commit: http://git-wip-us.apache.org/repos/asf/guacamole-client/commit/b55e5617
Tree: http://git-wip-us.apache.org/repos/asf/guacamole-client/tree/b55e5617
Diff: http://git-wip-us.apache.org/repos/asf/guacamole-client/diff/b55e5617

Branch: refs/heads/master
Commit: b55e56179c656191d3363b9089e3e3f235351d83
Parents: 264fd24
Author: Michael Jumper <mj...@apache.org>
Authored: Mon Nov 20 00:22:26 2017 -0800
Committer: Michael Jumper <mj...@apache.org>
Committed: Sun Feb 4 19:45:17 2018 -0800

----------------------------------------------------------------------
 LICENSE                                         |  37 ++
 extensions/guacamole-auth-totp/pom.xml          |  15 +
 .../guacamole-auth-totp/src/licenses/LICENSE    |  37 ++
 .../bundled/totp-reference-impl-07/license.txt  |  28 ++
 .../apache/guacamole/totp/TOTPGenerator.java    | 402 +++++++++++++++++++
 .../guacamole/totp/TOTPGeneratorTest.java       | 168 ++++++++
 6 files changed, 687 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/b55e5617/LICENSE
----------------------------------------------------------------------
diff --git a/LICENSE b/LICENSE
index 2c1ab31..3fa304c 100644
--- a/LICENSE
+++ b/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.
+

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/b55e5617/extensions/guacamole-auth-totp/pom.xml
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-totp/pom.xml b/extensions/guacamole-auth-totp/pom.xml
index b7fa965..5b421d7 100644
--- a/extensions/guacamole-auth-totp/pom.xml
+++ b/extensions/guacamole-auth-totp/pom.xml
@@ -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>

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/b55e5617/extensions/guacamole-auth-totp/src/licenses/LICENSE
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-totp/src/licenses/LICENSE b/extensions/guacamole-auth-totp/src/licenses/LICENSE
index c6cbf77..e1fa0fc 100644
--- a/extensions/guacamole-auth-totp/src/licenses/LICENSE
+++ b/extensions/guacamole-auth-totp/src/licenses/LICENSE
@@ -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.
+

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/b55e5617/extensions/guacamole-auth-totp/src/licenses/bundled/totp-reference-impl-07/license.txt
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-totp/src/licenses/bundled/totp-reference-impl-07/license.txt b/extensions/guacamole-auth-totp/src/licenses/bundled/totp-reference-impl-07/license.txt
new file mode 100644
index 0000000..bb1afcd
--- /dev/null
+++ b/extensions/guacamole-auth-totp/src/licenses/bundled/totp-reference-impl-07/license.txt
@@ -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.
+

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/b55e5617/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/totp/TOTPGenerator.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/totp/TOTPGenerator.java b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/totp/TOTPGenerator.java
new file mode 100644
index 0000000..004c23b
--- /dev/null
+++ b/extensions/guacamole-auth-totp/src/main/java/org/apache/guacamole/totp/TOTPGenerator.java
@@ -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);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/guacamole-client/blob/b55e5617/extensions/guacamole-auth-totp/src/test/java/org/apache/guacamole/totp/TOTPGeneratorTest.java
----------------------------------------------------------------------
diff --git a/extensions/guacamole-auth-totp/src/test/java/org/apache/guacamole/totp/TOTPGeneratorTest.java b/extensions/guacamole-auth-totp/src/test/java/org/apache/guacamole/totp/TOTPGeneratorTest.java
new file mode 100644
index 0000000..9266ad7
--- /dev/null
+++ b/extensions/guacamole-auth-totp/src/test/java/org/apache/guacamole/totp/TOTPGeneratorTest.java
@@ -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.");
+        }
+
+    }
+
+}