Permalink
Browse files

Implement YSM_AEAD_YUBIKEY_OTP_DECODE.

  • Loading branch information...
1 parent dc87473 commit 3644ff47cd8e9613f1be9789610785d5fcbc8a7b @fredrikt fredrikt committed Oct 7, 2011
@@ -1,5 +1,6 @@
/*
* 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.
@@ -14,6 +15,14 @@
* limitations under the License.
*
* @author Stefan Wold <stefan.wold@unitedid.org>
+ * @author Fredrik Thulin <fredrik@yubico.com>
+ *
+ * Yubico AB has contributed code to access some functions in the YubiHSM :
+ *
+ * YSM_HSM_UNLOCK
+ * YSM_AEAD_YUBIKEY_OTP_DECODE
+ *
+ * as well as other minor changes, test cases and documentation.
*/
package org.unitedid.yhsm;
@@ -529,6 +538,21 @@ public boolean randomReseed(String seed) throws YubiHSMCommandFailedException, Y
}
/**
+ * Decrypt a YubiKey OTP using an AEAD.
+ *
+ * @param publicId the nonce used to generate the AEAD (YubiKey publicId)
+ * @param keyHandle a keyHandle with the permission YSM_AEAD_YUBIKEY_OTP_DECODE enabled
+ * @param aead the AEAD based on the token seed
+ * @param otp the token OTP (in hex)
+ * @return a map with the decrypted data fields
+ * @throws YubiHSMInputException argument exceptions
+ * @throws YubiHSMCommandFailedException command failed exception
+ * @throws YubiHSMErrorException error exception
+ */
+ public Map<String, Integer> decodeYubikeyOtp(String publicId, int keyHandle, String aead, String otp) throws YubiHSMInputException, YubiHSMErrorException, YubiHSMCommandFailedException {
+ return YubikeyOtpDecodeCmd.execute(deviceHandler, publicId, keyHandle, aead, otp);
+ }
+ /**
* Drain all remaining output from the YubiHSM, used for debugging.
*
* @return true if successful, false otherwise.
@@ -34,6 +34,7 @@ private Defines() {}
final static public int YSM_MAX_KEY_SIZE = 32;
final static public int YSM_AEAD_MAX_SIZE = YSM_DATA_BUF_SIZE + YSM_AEAD_MAC_SIZE;
final static public int YSM_SHA1_HASH_SIZE = 20;
+ final static public int YSM_PUBLIC_ID_SIZE = 6;
final static public int YSM_OTP_SIZE = 16;
final static public int YSM_BLOCK_SIZE = 16;
final static public int UID_SIZE = 6;
@@ -0,0 +1,82 @@
+/*
+ * 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 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 java.util.HashMap;
+import java.util.Map;
+
+public class YubikeyOtpDecodeCmd {
+
+ /** Constructor */
+ private YubikeyOtpDecodeCmd() {}
+
+ /**
+ * Load the content of an AEAD into the phantom key handle 0xffffffff.
+ *
+ * @param device the device handler
+ * @param nonce the nonce
+ * @param keyHandle the key handle with permission to use YSM_TEMP_KEY_LOAD
+ * @param aead the AEAD to load into the phantom key handle
+ * @return returns true if the AEAD was successfully loaded
+ * @throws YubiHSMCommandFailedException command fail exception
+ * @throws YubiHSMErrorException error exception
+ * @throws YubiHSMInputException argument exceptions
+ */
+ public static Map<String, Integer> execute(DeviceHandler device, String publicId, int keyHandle, String aead, String otp) throws YubiHSMInputException, YubiHSMErrorException, YubiHSMCommandFailedException {
+ byte[] publicIdBA = validateNonce(hexToByteArray(publicId), true);
+ byte[] otpBA = validateByteArray("otp", hexToByteArray(otp), 0, YSM_OTP_SIZE, 0);
+ byte[] aeadBA = validateByteArray("aead", hexToByteArray(aead), 0, YSM_YUBIKEY_AEAD_SIZE, 0);
+ byte[] cmdBuffer = concatAllArrays(publicIdBA, leIntToBA(keyHandle), otpBA, aeadBA);
+ byte[] result = CommandHandler.execute(device, YSM_AEAD_YUBIKEY_OTP_DECODE, cmdBuffer, true);
+
+ return parseResult(result, publicIdBA, keyHandle);
+ }
+ /**
+ * 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 Map<String, Integer> parseResult(byte[] data, byte[] publicIdBA, int keyHandle) throws YubiHSMCommandFailedException, YubiHSMErrorException {
+ Map<String, Integer> result = new HashMap<String, Integer>();
+
+ byte status = data[YSM_PUBLIC_ID_SIZE + 4 + 2 + 1 + 1 + 2];
+ if (status == YSM_STATUS_OK) {
+ validateCmdResponseBA("publicId", rangeOfByteArray(data, 0, YSM_PUBLIC_ID_SIZE), publicIdBA);
+ validateCmdResponseBA("keyHandle", rangeOfByteArray(data, YSM_PUBLIC_ID_SIZE, 4), leIntToBA(keyHandle));
+ int useCtr = leBAToBeShort(rangeOfByteArray(data, YSM_PUBLIC_ID_SIZE + 4, 2));
+ int sessionCtr = data[YSM_PUBLIC_ID_SIZE + 4 + 2] & 0xff;
+ int timestampHigh = data[YSM_PUBLIC_ID_SIZE + 4 + 2 + 1] & 0xff;
+ int timestampLow = leBAToBeShort(rangeOfByteArray(data, YSM_PUBLIC_ID_SIZE + 4 + 2 + 1 + 1, 2));
+ result.put("useCtr", useCtr);
+ result.put("sessionCtr", sessionCtr);
+ result.put("tsHigh", timestampHigh);
+ result.put("tsLow", timestampLow);
+ } else {
+ throw new YubiHSMCommandFailedException("Command " + getCommandString(YSM_AEAD_YUBIKEY_OTP_DECODE) + " failed: " + getCommandStatus(status));
+ }
+
+ return result;
+ }
+}
+
@@ -34,7 +34,8 @@
OathHOTPCmdTest.class,
NonceGetCmdTest.class,
RandomCmdTest.class,
- RandomReseedCmdTest.class})
+ RandomReseedCmdTest.class,
+ YubikeyOtpDecodeCmdTest.class})
public class InternalTestSuite {
@@ -0,0 +1,99 @@
+/*
+ * 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 Fredrik Thulin <fredrik@yubico.com>
+ */
+
+package org.unitedid.yhsm.internal;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.unitedid.yhsm.SetupCommon;
+import org.unitedid.yhsm.utility.Utils;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+public class YubikeyOtpDecodeCmdTest extends SetupCommon {
+
+ private final int keyHandle = 0x2000;
+ private final String publicId = "4d4d4d000001"; /* ftftftcccccb in modhex */
+ private final String privateId = "534543524554";
+ private final String key = "fcacd309a20ce1809c2db257f0e8d6ea";
+
+
+ @Rule
+ public ExpectedException thrown = ExpectedException.none();
+
+
+ @Override
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ }
+
+ @Override
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ @Test
+ public void testYubikeyDecode1() throws Exception {
+ byte[] secretBA = Utils.hexToByteArray(new String(key + privateId));
+ String aead = AEADCmd.generateAEAD(deviceHandler, publicId, keyHandle, secretBA).get("aead");
+ Map<String, Integer> result;
+ result = hsm.decodeYubikeyOtp(publicId, keyHandle, aead, "828e71152b15a4823bb34b6e6a5d4353");
+ Map<String, Integer> expected = new HashMap<String, Integer>() {{
+ put("useCtr", 1);
+ put("sessionCtr", 0);
+ put("tsHigh", 39);
+ put("tsLow", 24133);
+ }};
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testYubikeyDecode2() throws Exception {
+ byte[] secretBA = Utils.hexToByteArray(new String(key + privateId));
+ String aead = AEADCmd.generateAEAD(deviceHandler, publicId, keyHandle, secretBA).get("aead");
+ Map<String, Integer> result;
+ result = hsm.decodeYubikeyOtp(publicId, keyHandle, aead, "c91e8472c2a76459a2a8b81c32d44955");
+ Map<String, Integer> expected = new HashMap<String, Integer>() {{
+ put("useCtr", 2);
+ put("sessionCtr", 4);
+ put("tsHigh", 204);
+ put("tsLow", 28386);
+ }};
+ assertEquals(expected, result);
+ }
+
+ @Test
+ public void testYubikeyDecodeInvalid() throws Exception {
+ thrown.expect(YubiHSMCommandFailedException.class);
+ thrown.expectMessage("Command YSM_AEAD_YUBIKEY_OTP_DECODE failed: YSM_OTP_INVALID");
+
+ byte[] secretBA = Utils.hexToByteArray(new String(key + privateId));
+ String aead = AEADCmd.generateAEAD(deviceHandler, publicId, keyHandle, secretBA).get("aead");
+ hsm.decodeYubikeyOtp(publicId, keyHandle, aead, "000102030405060708090a0b0c0d0e0f");
+ }
+}

0 comments on commit 3644ff4

Please sign in to comment.