Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merge branch 'ft_unlocking_rebased'

  • Loading branch information...
commit 663232df9d464690687484654a145091ed308f93 2 parents b3163da + f532278
@Ratler authored
View
4 pom.xml
@@ -24,7 +24,7 @@
<groupId>org.unitedid</groupId>
<artifactId>YubiHSM</artifactId>
- <version>0.9</version>
+ <version>0.9.1-dev</version>
<packaging>jar</packaging>
<inceptionYear>2011</inceptionYear>
<organization>
@@ -100,7 +100,7 @@
<version>2.8.1</version>
<configuration>
<includes>
- <include>**/ConfigureHSM.java</include>
+ <!-- <include>**/ConfigureHSM.java</include> -->
<include>**/MainTestSuite.java</include>
</includes>
</configuration>
View
66 src/main/java/org/unitedid/yhsm/YubiHSM.java
@@ -22,10 +22,10 @@
import org.slf4j.LoggerFactory;
import org.unitedid.yhsm.internal.*;
-import static org.unitedid.yhsm.utility.Utils.*;
-
import java.util.Map;
+import static org.unitedid.yhsm.utility.Utils.*;
+
/** <code>YubiHSM</code> the main class to use for YubiHSM commands */
public class YubiHSM {
/** Logger */
@@ -37,6 +37,9 @@
/** The hash length, default is 20 */
public static int minHashLength = 20;
+ /* YubiHSM sysinfo cache */
+ private Map<String, String> info;
+
/**
* Constructor
*
@@ -47,6 +50,7 @@
public YubiHSM(String device, float timeout) throws YubiHSMErrorException {
deviceHandler = DeviceHandlerFactory.get(device, timeout);
CommandHandler.reset(deviceHandler);
+ info = SystemInfoCmd.execute(deviceHandler);
}
/**
@@ -67,21 +71,19 @@ public String echo(String str) throws YubiHSMErrorException {
* @throws YubiHSMErrorException if the YubiHSM info command fail
*/
public Map<String, String> info() throws YubiHSMErrorException {
- return SystemInfoCmd.execute(deviceHandler);
+ return info;
}
/**
- * Get the firmware verseion and unique ID from the YubiHSM (string representation).
+ * Get the firmware version and unique ID from the YubiHSM (string representation).
*
* @return a string with version, protocol and unique ID
* @throws YubiHSMErrorException if the YubiHSM info command fail
*/
public String infoToString() throws YubiHSMErrorException {
- Map<String, String> info = SystemInfoCmd.execute(deviceHandler);
-
return String.format("Version %s.%s.%s Protocol=%s SysId: %s", info.get("major"), info.get("minor"),
- info.get("build"), info.get("protocol"),
- info.get("sysid"));
+ info.get("build"), info.get("protocol"),
+ info.get("sysid"));
}
/**
@@ -375,6 +377,39 @@ public boolean compareAES_ECB(int keyHandle, String cipherText, String plaintext
}
/**
+ * Generic key store unlock method that calls the appropriate unlock function for this YubiHSM.
+ *
+ * @param password the Master key/HSM password in hex format
+ * @return true if unlock/decrypt was successful, otherwise an YubiHSMCommandFailedException is thrown
+ * @throws YubiHSMCommandFailedException command failed exception
+ * @throws YubiHSMErrorException error exception
+ * @throws YubiHSMInputException argument exception
+ *
+ * @see #keyStoreDecrypt(String)
+ * @see #keyStorageUnlock(String)
+ */
+ public boolean unlock(String password) throws YubiHSMErrorException, YubiHSMCommandFailedException, YubiHSMInputException {
+ if (info().get("major").equals("0")) {
+ return keyStorageUnlock(password);
+ } else {
+ return keyStoreDecrypt(password);
+ }
+ }
+
+ /**
+ * Decrypt the YubiHSM key storage using the Master key.
+ *
+ * @param key the Master key in hex format (see output of automatic Master key generation during HSM configuration)
+ * @return true if unlock was successful, otherwise an YubiHSMCommandFailedException is thrown
+ * @throws YubiHSMCommandFailedException command failed exception
+ * @throws YubiHSMErrorException error exception
+ * @throws YubiHSMInputException argument exception
+ */
+ public boolean keyStoreDecrypt(String key) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
+ return KeyStoreDecryptCmd.execute(deviceHandler, key);
+ }
+
+ /**
* Unlock the YubiHSM key storage using the HSM password.
*
* @param password the password in hex format (see output of automatic password generation during HSM configuration)
@@ -388,6 +423,21 @@ public boolean keyStorageUnlock(String password) throws YubiHSMCommandFailedExce
}
/**
+ * Have the YubiHSM unlock the HSM operations (those involving the keystore) with a YubiKey OTP.
+ *
+ * @param device the YubiHSM device
+ * @param publicId the YubiKey public id (in hex)
+ * @param otp the YubiKey OTP (in hex)
+ * @return true if unlock was successful
+ * @throws YubiHSMErrorException error exceptions
+ * @throws YubiHSMInputException argument exceptions
+ * @throws YubiHSMCommandFailedException command failed exception
+ */
+ public boolean unlockOtp(String publicId, String otp) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
+ return HsmUnlockCmd.unlockOtp(deviceHandler, publicId, otp);
+ }
+
+ /**
* Validate OATH-HOTP OTP by a token whose seed is available to the YubiHSM through an AEAD.
*
* @param hsm the current hsm object
View
7 src/main/java/org/unitedid/yhsm/internal/Defines.java
@@ -71,7 +71,9 @@ private Defines() {}
final static public byte YSM_RANDOM_GENERATE = 0x24;
final static public byte YSM_RANDOM_RESEED = 0x25;
final static public byte YSM_SYSTEM_INFO_QUERY = 0x26;
- final static public byte YSM_KEY_STORAGE_UNLOCK = 0x27;
+ final static public byte YSM_KEY_STORAGE_UNLOCK = 0x27; /* Deprecated in 1.0 */
+ final static public byte YSM_HSM_UNLOCK = 0x28;
+ final static public byte YSM_KEY_STORE_DECRYPT = 0x29;
final static public byte YSM_MONITOR_EXIT = 0x7f;
/**
@@ -102,6 +104,8 @@ private Defines() {}
put(0x25, "YSM_RANDOM_RESEED");
put(0x26, "YSM_SYSTEM_INFO_QUERY");
put(0x27, "YSM_KEY_STORAGE_UNLOCK");
+ put(0x28, "YSM_HSM_UNLOCK");
+ put(0x29, "YSM_KEY_STORE_DECRYPT");
}});
/**
@@ -126,6 +130,7 @@ private Defines() {}
/** Last command executed successfully */
final static public byte YSM_STATUS_OK = (byte) 0x80;
final static public byte YSM_RESPONSE = (byte) 0x80;
+ final static public byte YSM_OTP_INVALID = (byte) 0x83;
final static public byte YSM_MISMATCH = (byte) 0x8b;
final static public byte YSM_KEY_STORAGE_LOCKED = (byte) 0x8a;
View
67 src/main/java/org/unitedid/yhsm/internal/HsmUnlockCmd.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2011 United ID. All rights reserved.
+ * Copyright (c) 2011 Yubico AB. All rights reserved.
+ *
+ * Licensed 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.
+ *
+ * @author Stefan Wold <stefan.wold@unitedid.org>
+ * @author Fredrik Thulin <fredrik@yubico.com>
+ */
+
+package org.unitedid.yhsm.internal;
+
+import static org.unitedid.yhsm.internal.Defines.*;
+import static org.unitedid.yhsm.utility.Utils.*;
+
+import org.unitedid.yhsm.utility.Utils;
+
+public class HsmUnlockCmd {
+
+ /** Constructor */
+ private HsmUnlockCmd() {}
+
+ /**
+ * Have the YubiHSM unlock the HSM operations (those involving the keystore) with a YubiKey OTP.
+ *
+ * @param device the YubiHSM device
+ * @param publicId the YubiKey public id
+ * @param otp the YubiKey OTP (in hex)
+ * @return true if unlock was successful
+ * @throws YubiHSMErrorException error exceptions
+ * @throws YubiHSMInputException argument exceptions
+ * @throws YubiHSMCommandFailedException command failed exception
+ */
+ public static boolean unlockOtp(DeviceHandler device, String publicId, String otp) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
+ byte[] idBA = validateByteArray("publicId", hexToByteArray(publicId), 0, YSM_AEAD_NONCE_SIZE, YSM_AEAD_NONCE_SIZE);
+ byte[] otpBA = validateByteArray("otp", hexToByteArray(otp), 0, YSM_OTP_SIZE, YSM_OTP_SIZE);
+ byte[] payload = Utils.concatAllArrays(idBA, otpBA);
+ return parseResult(CommandHandler.execute(device, YSM_HSM_UNLOCK, payload, true));
+ }
+
+ /**
+ * Parse the response from the YubiHSM for a previous command.
+ *
+ * @param result the result from the last command
+ * @return boolean indicating success
+ * @throws YubiHSMCommandFailedException command failed exception
+ */
+ private static boolean parseResult(byte[] result) throws YubiHSMCommandFailedException {
+ if (result[0] == YSM_STATUS_OK) {
+ return true;
+ } else if (result[0] == YSM_OTP_INVALID) {
+ return false;
+ } else {
+ throw new YubiHSMCommandFailedException("Command " + getCommandString(YSM_HSM_UNLOCK) + " failed: " + getCommandStatus(result[0]));
+ }
+ }
+}
View
44 src/main/java/org/unitedid/yhsm/internal/KeyStoreDecryptCmd.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2011 United ID. All rights reserved.
+ *
+ * Licensed 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.
+ *
+ * @author Stefan Wold <stefan.wold@unitedid.org>
+ */
+
+package org.unitedid.yhsm.internal;
+
+import static org.unitedid.yhsm.internal.Defines.*;
+import static org.unitedid.yhsm.utility.Utils.*;
+
+public class KeyStoreDecryptCmd {
+
+ /** Constructur */
+ private KeyStoreDecryptCmd() {}
+
+ public static boolean execute(DeviceHandler device, String key) throws YubiHSMInputException, YubiHSMErrorException, YubiHSMCommandFailedException {
+ byte[] keyBA = hexToByteArray(key);
+ keyBA = validateByteArray("key", keyBA, YSM_MAX_KEY_SIZE, 0, YSM_MAX_KEY_SIZE);
+ return parseResult(CommandHandler.execute(device, YSM_KEY_STORE_DECRYPT, keyBA, true));
+ }
+
+ private static boolean parseResult(byte[] result) throws YubiHSMCommandFailedException {
+ if (result[0] == YSM_STATUS_OK) {
+ return true;
+ } else if (result[0] == YSM_MISMATCH) {
+ return false;
+ } else {
+ throw new YubiHSMCommandFailedException("Command " + getCommandString(YSM_KEY_STORE_DECRYPT) + " failed: " + getCommandStatus(result[0]));
+ }
+ }
+}
View
16 src/test/java/org/unitedid/yhsm/ConfigureHSM.java
@@ -38,12 +38,26 @@ public void tearDown() throws Exception {
@Test
public void testConfigureHSM() throws Exception {
+ int major_version = new Integer(hsm.info().get("major"));
+
+ System.out.println("Exiting HSM monitor mode (requires YubiHSM in 'debug' mode)");
hsm.exitMonitorDebugMode();
- System.out.println(runCommand("hsm ffffffff\r\r2f6af1e667456bb94528e7987344515b\ryes", true));
+ System.out.println("Configuring YubiHSM for test suite (" + hsm.infoToString() + ")");
+ hsm.drainData();
+ System.out.println(runCommand("sysinfo", true));
+ if (major_version == 0) {
+ System.out.println(runCommand("hsm ffffffff\r\r2f6af1e667456bb94528e7987344515b\ryes", true));
+ } else {
+ char esc = 0x1b;
+ System.out.println(runCommand("hsm ffffffff\r\rftftftcccccb\r\r2f6af1e667456bb94528e7987344515b\ryes", true));
+ System.out.println(runCommand("dbload\r00001,ftftftcccccb,010203040506,0102030405060708090a0b0c0d0e0f\r\r" + esc, false));
+ }
System.out.println(runCommand("sysinfo", true));
hsm.drainData();
addKeys();
System.out.println(runCommand("keylist", true));
+ System.out.println(runCommand("keycommit", true));
+ System.out.println(runCommand("dblist", true));
deviceHandler.write("exit\r".getBytes());
Thread.sleep(50);
hsm.drainData();
View
2  src/test/java/org/unitedid/yhsm/MainTestSuite.java
@@ -24,7 +24,7 @@
import org.junit.runners.Suite;
@RunWith(Suite.class)
-@Suite.SuiteClasses({InternalTestSuite.class, UtilityTestSuite.class})
+@Suite.SuiteClasses({ConfigureHSM.class, InternalTestSuite.class, UtilityTestSuite.class})
public class MainTestSuite {
View
44 src/test/java/org/unitedid/yhsm/internal/KeyStorageUnlockTest.java
@@ -18,6 +18,11 @@
package org.unitedid.yhsm.internal;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
@@ -25,9 +30,6 @@
import org.junit.rules.ExpectedException;
import org.unitedid.yhsm.SetupCommon;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
public class KeyStorageUnlockTest extends SetupCommon {
@Rule
@@ -45,11 +47,43 @@ public void tearDown() throws Exception {
@Test
public void failedUnlockHsm() throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
- assertFalse(hsm.keyStorageUnlock("1111"));
+ assertFalse(hsm.unlock("1111"));
}
@Test
public void unlockHsm() throws Exception {
- assertTrue(hsm.keyStorageUnlock("2f6af1e667456bb94528e7987344515b"));
+ assertTrue(hsm.unlock("2f6af1e667456bb94528e7987344515b"));
+ }
+
+ @Test
+ public void otpUnlockHsm() throws Exception {
+ /* order is crucial here, that's why these are not made into separate tests */
+
+ if (new Integer(hsm.info().get("major")) > 0) {
+ /* Incorrect public id */
+ try {
+ hsm.unlockOtp("010000000000", "ffaaffaaffaaffaaffaaffaaffaaffaa");
+ fail("unlockOtp not expected to work");
+ } catch (YubiHSMCommandFailedException e) {
+ assertEquals("Command YSM_HSM_UNLOCK failed: YSM_INVALID_PARAMETER", e.getMessage());
+ }
+
+ /* Right public id, wrong OTP */
+ assertFalse(hsm.unlockOtp("4d4d4d000001", "ffaaffaaffaaffaaffaaffaaffaaffaa"));
+
+ /* Right public id, right OTP (for counter values 00002/001) */
+ assertTrue(hsm.unlockOtp("4d4d4d000001", "5f8e12aa57abb1677b88606eb2af63b9"));
+
+ /* Replay, will lock the HSM again */
+ try {
+ hsm.unlockOtp("4d4d4d000001", "5f8e12aa57abb1677b88606eb2af63b9");
+ fail("unlockOtp with same OTP not expected to work");
+ } catch (YubiHSMCommandFailedException e) {
+ assertEquals("Command YSM_HSM_UNLOCK failed: YSM_OTP_REPLAY", e.getMessage());
+ }
+
+ /* Right public id, new OTP (counter values 00002/002) */
+ assertTrue(hsm.unlockOtp("4d4d4d000001", "083aa5cd1a278ed2e9eb46971b479725"));
+ }
}
}
Please sign in to comment.
Something went wrong with that request. Please try again.