Permalink
Browse files

Add AES and HMAC operation on byte arrays of arbitrary size

  • Loading branch information...
1 parent de5a988 commit 2193cdc74a996d45f2b84ef8bd6401df00091801 @Tmr committed Mar 14, 2013
@@ -30,11 +30,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.unitedid.yhsm.internal.*;
+import static org.unitedid.yhsm.internal.Defines.*;
+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 */
@@ -278,6 +278,31 @@ public boolean loadTemporaryKey(String nonce, int keyHandle, String aead) throws
/**
* Generate HMAC SHA1 using a key handle in the YubiHSM.
*
+ * @param bytes the data used to generate the SHA1
+ * @param keyHandle the key handle to use in the YubiHSM
+ * @param toBuffer set to true to get the SHA1 stored into the internal buffer, for use in some other cryptographic operations.
+ * @return a map containing status and SHA1 hash
+ * @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
+ * @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
+ * @throws YubiHSMInputException if an argument does not validate
+ */
+ public byte[] generateHMACSHA1(byte[] bytes, int keyHandle, boolean toBuffer) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException {
+ byte[] result = {};
+ byte flags = YSM_HMAC_SHA1_RESET;
+ if (toBuffer)
+ flags |= YSM_HMAC_SHA1_TO_BUFFER;
+ for (int b = 0; b < bytes.length; b = b + YSM_DATA_BUF_SIZE) {
+ if ((bytes.length - b) <= YSM_DATA_BUF_SIZE)
+ flags |= YSM_HMAC_SHA1_FINAL;
+ result = HMACCmd.execHMACSHA1_Raw(deviceHandler, rangeOfByteArray(bytes, b, Math.min(bytes.length - b, YSM_DATA_BUF_SIZE)), keyHandle, flags);
+ flags &= (byte)0xFE; // remove YSM_HMAC_SHA1_RESET flag
+ }
+ return result;
+ }
+
+ /**
+ * Generate HMAC SHA1 using a key handle in the YubiHSM.
+ *
* @param data the data used to generate the SHA1
* @param keyHandle the key handle to use in the YubiHSM
* @param last set to false to not get a hash generated for the initial request
@@ -385,7 +410,25 @@ public boolean loadTemporaryKey(String nonce, int keyHandle, String aead) throws
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
*/
public String encryptAES_ECB(String plaintext, int keyHandle) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
- return AESECBCmd.encrypt(deviceHandler, keyHandle, plaintext);
+ return byteArrayToHex(this.encryptAES_ECB(plaintext.getBytes(), keyHandle));
+ }
+
+ /**
+ * AES ECB encrypt an array of bytes using a specific key handle.
+ *
+ * @param keyHandle the key handle to use when encrypting AES ECB
+ * @param bytes the bytes to encrypt
+ * @return an array of bytes
+ * @throws YubiHSMInputException if an argument does not validate
+ * @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
+ * @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
+ */
+ public byte[] encryptAES_ECB(byte[] bytes, int keyHandle) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
+ byte[] result = {};
+ for (int b = 0; b < bytes.length; b = b + YSM_BLOCK_SIZE) {
+ result = concatAllArrays(result, AESECBCmd.encryptBlock(deviceHandler, keyHandle, rangeOfByteArray(bytes, b, Math.min(bytes.length - b, YSM_BLOCK_SIZE))));
+ }
+ return result;
}
/**
@@ -399,7 +442,25 @@ public String encryptAES_ECB(String plaintext, int keyHandle) throws YubiHSMErro
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
*/
public String decryptAES_ECB(String cipherText, int keyHandle) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
- return AESECBCmd.decrypt(deviceHandler, keyHandle, cipherText);
+ return new String(this.decryptAES_ECB(hexToByteArray(cipherText), keyHandle)).trim();
+ }
+
+ /**
+ * AES ECB decrypt an array of bytes using a specific key handle.
+ *
+ * @param keyHandle the key handle to use when decrypting AES ECB
+ * @param cipherBytes the cipher string
+ * @return an array of bytes
+ * @throws YubiHSMInputException if an argument does not validate
+ * @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
+ * @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
+ */
+ public byte[] decryptAES_ECB(byte[] cipherBytes, int keyHandle) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
+ byte[] result = {};
+ for (int b = 0; b < cipherBytes.length; b = b + YSM_BLOCK_SIZE) {
+ result = concatAllArrays(result, AESECBCmd.decryptBlock(deviceHandler, keyHandle, rangeOfByteArray(cipherBytes, b, Math.min(cipherBytes.length - b, YSM_BLOCK_SIZE))));
+ }
+ return result;
}
/**
@@ -466,7 +527,6 @@ 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
@@ -28,39 +28,40 @@
private AESECBCmd() {}
/**
- * AES ECB encrypt a plaintext string using a specific key handle.
+ * AES ECB encrypt one block with size of YSM_BLOCK_SIZE bytes using a specific key handle.
*
* @param deviceHandler the device handler
* @param keyHandle the key handle to use when encrypting AES ECB
- * @param plaintext the plaintext string
+ * @param bytes the bytes to encrypt
* @return a hash string in hex format
* @throws YubiHSMInputException if an argument does not validate
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
*/
- public static String encrypt(DeviceHandler deviceHandler, int keyHandle, String plaintext) throws YubiHSMInputException, YubiHSMErrorException, YubiHSMCommandFailedException {
- byte[] cmdBuffer = concatAllArrays(leIntToBA(keyHandle), validateByteArray("plaintext", plaintext.getBytes(), YSM_BLOCK_SIZE, 0, YSM_BLOCK_SIZE));
+
+ public static byte[] encryptBlock(DeviceHandler deviceHandler, int keyHandle, byte[] bytes) throws YubiHSMInputException, YubiHSMErrorException, YubiHSMCommandFailedException {
+ byte[] cmdBuffer = concatAllArrays(leIntToBA(keyHandle), validateByteArray("bytes", bytes, YSM_BLOCK_SIZE, 0, YSM_BLOCK_SIZE));
byte[] result = CommandHandler.execute(deviceHandler, YSM_AES_ECB_BLOCK_ENCRYPT, cmdBuffer, true);
- return parseResult(result, keyHandle, YSM_AES_ECB_BLOCK_ENCRYPT, false);
+ return parseResult(result, keyHandle, YSM_AES_ECB_BLOCK_ENCRYPT);
}
/**
- * AES ECB decrypt a cipher text using a specific key handle.
+ * AES ECB decrypt a cipher array of bytes of size YSM_BLOCK_SIZE using a specific key handle.
*
* @param deviceHandler the device handler
* @param keyHandle the key handle to use when decrypting AES ECB
- * @param cipherText the cipher string
+ * @param cipherBytes the cipher string
* @return a plaintext string
* @throws YubiHSMInputException if an argument does not validate
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
*/
- public static String decrypt(DeviceHandler deviceHandler, int keyHandle, String cipherText) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
- byte[] cmdBuffer = concatAllArrays(leIntToBA(keyHandle), validateByteArray("cipherText", hexToByteArray(cipherText), 0, YSM_BLOCK_SIZE, 0));
+ public static byte[] decryptBlock(DeviceHandler deviceHandler, int keyHandle, byte[] cipherBytes) throws YubiHSMErrorException, YubiHSMInputException, YubiHSMCommandFailedException {
+ byte[] cmdBuffer = concatAllArrays(leIntToBA(keyHandle), validateByteArray("cipherBytes", cipherBytes, 0, YSM_BLOCK_SIZE, 0));
byte[] result = CommandHandler.execute(deviceHandler, YSM_AES_ECB_BLOCK_DECRYPT, cmdBuffer, true);
- return parseResult(result, keyHandle, YSM_AES_ECB_BLOCK_DECRYPT, true);
+ return parseResult(result, keyHandle, YSM_AES_ECB_BLOCK_DECRYPT);
}
/**
@@ -99,17 +100,16 @@ public static boolean compare(DeviceHandler deviceHandler, int keyHandle, String
* @param data the YubiHSM response data
* @param keyHandle the key handle used for the command
* @param command the YubiHSM command executed
- * @param decrypt a boolean used to toggle if we decrypt (if true plaintext is returned, otherwise a hex string)
- * @return a plaintext string or hex string
+ * @return an array of bytes with operation result
* @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
* @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
*/
- private static String parseResult(byte[] data, int keyHandle, byte command, boolean decrypt) throws YubiHSMErrorException, YubiHSMCommandFailedException {
+ private static byte[] parseResult(byte[] data, int keyHandle, byte command) throws YubiHSMErrorException, YubiHSMCommandFailedException {
validateCmdResponseBA("keyHandle", rangeOfByteArray(data, 0, 4), leIntToBA(keyHandle));
byte[] result = rangeOfByteArray(data, 4, YSM_BLOCK_SIZE);
if (data[20] == YSM_STATUS_OK) {
- return decrypt ? new String(result).trim() : byteArrayToHex(result);
+ return result;
} else {
throw new YubiHSMCommandFailedException("Command " + getCommandString(command) + " failed: " + getCommandStatus(data[20]));
}
@@ -60,11 +60,31 @@ private HMACCmd() {}
byte[] flagsBA = { flags };
byte[] cmdBuffer = concatAllArrays(leIntToBA(keyHandle), flagsBA, addLengthToData(dataBA));
byte[] result = CommandHandler.execute(deviceHandler, YSM_HMAC_SHA1_GENERATE, cmdBuffer, true);
-
return parseResult(result, keyHandle, last);
}
/**
+ * Generate HMAC SHA1 using a key handle in the YubiHSM.
+ *
+ * @param deviceHandler the device handler
+ * @param data the data used to generate the SHA1
+ * @param keyHandle the key handle to use in the YubiHSM
+ * @param flags the commands flags, send (byte) 0 to use defaults
+ * @return an array of bytes
+ * @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
+ * @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
+ * @throws YubiHSMInputException if an argument does not validate
+ */
+ public static byte[] execHMACSHA1_Raw(DeviceHandler deviceHandler, byte[] data, int keyHandle, byte flags) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException {
+ byte [] dataBA = validateByteArray("data", data, YSM_DATA_BUF_SIZE, 0, 0);
+ byte[] flagsBA = { flags };
+ byte[] cmdBuffer = concatAllArrays(leIntToBA(keyHandle), flagsBA, addLengthToData(dataBA));
+ byte[] result = CommandHandler.execute(deviceHandler, YSM_HMAC_SHA1_GENERATE, cmdBuffer, true);
+ boolean isLast = (flags & YSM_HMAC_SHA1_FINAL) == YSM_HMAC_SHA1_FINAL;
+ return parseResultRaw(result, keyHandle, isLast);
+ }
+
+ /**
* Add more input to the HMAC SHA1.
*
* @param deviceHandler the device handler
@@ -91,11 +111,9 @@ private HMACCmd() {}
byte[] flagsBA = { flags };
byte[] cmdBuffer = concatAllArrays(leIntToBA(keyHandle), flagsBA, addLengthToData(data));
byte[] result = CommandHandler.execute(deviceHandler, YSM_HMAC_SHA1_GENERATE, cmdBuffer, true);
-
return parseResult(result, keyHandle, last);
}
-
/**
* Parse the response from the YubiHSM for a previous command.
*
@@ -126,4 +144,30 @@ private HMACCmd() {}
return result;
}
+
+ /**
+ * Parse the response from the YubiHSM for a previous command.
+ *
+ * @param data the data from the YubiHSM
+ * @param keyHandle the key handle used for the command
+ * @param last the boolean if this was the final request
+ * @return array of bytes
+ * @throws YubiHSMCommandFailedException if the YubiHSM fail to execute the command
+ * @throws YubiHSMErrorException if validation fail for some values returned by the YubiHSM
+ * @throws YubiHSMInputException if an argument does not validate
+ */
+ private static byte[] parseResultRaw(byte[] data, int keyHandle, boolean last) throws YubiHSMErrorException, YubiHSMCommandFailedException, YubiHSMInputException {
+ if (data[4] == YSM_STATUS_OK) {
+ validateCmdResponseBA("keyHandle", rangeOfByteArray(data, 0, 4), leIntToBA(keyHandle));
+ if (last) {
+ return rangeOfByteArray(data, 6, data[5]);
+ } else {
+ byte[] zeroHash = { 0x00 };
+ zeroHash = validateByteArray("zeroHash", zeroHash, 0, 0, 20);
+ return zeroHash;
+ }
+ } else {
+ throw new YubiHSMCommandFailedException("Command " + getCommandString(YSM_HMAC_SHA1_GENERATE) + " failed: " + getCommandStatus(data[4]));
+ }
+ }
}

0 comments on commit 2193cdc

Please sign in to comment.