/* * Copyright (c) 2011 United ID. * * 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. */ package org.unitedid.yhsm.internal; import org.unitedid.yhsm.YubiHSM; import java.util.HashMap; import java.util.Map; import static org.unitedid.yhsm.internal.Defines.*; import static org.unitedid.yhsm.utility.Utils.*; /** * <code>AEADCmd</code> implements AEAD commands for the YubiHSM */ public class AEADCmd { /** Constructor */ private AEADCmd() {} /** * Generate AEADCmd block from data for a specific key and nonce. * * @param device the YubiHSM device handler * @param nonce the nonce * @param keyHandle the key to use * @param data is the byte array to turn into an AEAD * @return a hash map with the AEAD and nonce * @throws YubiHSMInputException argument exceptions * @throws YubiHSMCommandFailedException command failed exception * @throws YubiHSMErrorException error exception */ public static Map<String, String> generateAEAD(DeviceHandler device, String nonce, int keyHandle, byte[] data) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException { byte[] nonceBA = validateNonce(hexToByteArray(nonce), true); byte[] newdata = validateByteArray("data", data, 0, 0, YubiHSM.minHashLength); byte[] cmdBuffer = concatAllArrays(nonceBA, leIntToBA(keyHandle), addLengthToData(newdata)); byte[] result = CommandHandler.execute(device, YSM_AEAD_GENERATE, cmdBuffer, true); return parseResult(result, nonce, keyHandle, YSM_AEAD_GENERATE); } /** * Generate a random AEAD block using the YubiHSM internal TRNG. * To generate a secret for a YubiKey use public_id as nonce. * * @param device the YubiHSM device handler * @param nonce the nonce or public_id * @param keyHandle the key to use * @param size the resulting byte length of the AEAD * @return a hash map with the AEAD and nonce * @throws YubiHSMInputException argument exceptions * @throws YubiHSMCommandFailedException command failed exception * @throws YubiHSMErrorException error exception */ public static Map<String, String> generateRandomAEAD(DeviceHandler device, String nonce, int keyHandle, int size) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException { byte[] nonceBA = validateNonce(hexToByteArray(nonce), true); byte[] len = {(byte) ((size << 24) >> 24)}; byte[] cmdBuffer = concatAllArrays(nonceBA, leIntToBA(keyHandle), len); byte[] result = CommandHandler.execute(device, YSM_RANDOM_AEAD_GENERATE, cmdBuffer, true); return parseResult(result, nonce, keyHandle, YSM_RANDOM_AEAD_GENERATE); } /** * Generate AEAD block of data buffer for a specific key. * After a key has been loaded into the internal data buffer, this command can be * used a number of times to get AEADs of the data buffer for different key handles. * For example, to encrypt a YubiKey secrets to one or more Yubico KSM's that * all have a YubiHSM attached to them. * * @param device the YubiHSM device handler * @param nonce the nonce * @param keyHandle the key to use * @return a hash map with the AEAD and nonce * @throws YubiHSMInputException argument exceptions * @throws YubiHSMCommandFailedException command failed exception * @throws YubiHSMErrorException error exception */ public static Map<String, String> generateBufferAEAD(DeviceHandler device, String nonce, int keyHandle) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException { byte[] nonceBA = validateNonce(hexToByteArray(nonce), true); byte[] cmdBuffer = concatAllArrays(nonceBA, leIntToBA(keyHandle)); byte[] result = CommandHandler.execute(device, YSM_BUFFER_AEAD_GENERATE, cmdBuffer, true); return parseResult(result, nonce, keyHandle, YSM_BUFFER_AEAD_GENERATE); } /** * Validate an AEAD using the YubiHSM, matching it against some known plain text. * Matching is done inside the YubiHSM so the decrypted AEAD is never exposed. * * @param device the YubiHSM device * @param nonce the nonce or public_id * @param keyHandle the key to use * @param aead the AEAD (hex string) * @param plaintext the plain text data * @return returns true if validation was a success, false if the validation failed * @throws YubiHSMInputException argument exceptions * @throws YubiHSMCommandFailedException command failed exception * @throws YubiHSMErrorException error exception */ public static boolean validateAEAD(DeviceHandler device, String nonce, int keyHandle, String aead, byte[] plaintext) throws YubiHSMInputException, YubiHSMCommandFailedException, YubiHSMErrorException { byte[] aeadBA = hexToByteArray(aead); byte[] plainBA = validateByteArray("plaintext", plaintext, 0, aeadBA.length - YSM_AEAD_MAC_SIZE, YubiHSM.minHashLength); byte[] plainAndAead = concatAllArrays(plainBA, aeadBA); if (plainAndAead.length > (YSM_MAX_PKT_SIZE - 0x10)) throw new YubiHSMInputException("Plaintext+aead too long"); byte[] nonceBA = validateNonce(hexToByteArray(nonce), true); byte[] cmdBuffer = concatAllArrays(nonceBA, leIntToBA(keyHandle), addLengthToData(plainAndAead)); byte[] result = CommandHandler.execute(device, YSM_AEAD_DECRYPT_CMP, cmdBuffer, true); return parseValidationResult(result, nonce, keyHandle); } /** * Parse the response from the YubiHSM for a previous command. * * @param data the data from the YubiHSM * @param nonce the original nonce * @param keyHandle the key used to generate AEAD * @param command the YubiHSM command executed * @return a hash map with the AEAD and nonce * @throws YubiHSMCommandFailedException command failed exception * @throws YubiHSMErrorException error exception */ private static Map<String, String> parseResult(byte[] data, String nonce, int keyHandle, byte command) throws YubiHSMCommandFailedException, YubiHSMErrorException { Map<String, String> result = new HashMap<String, String>(); if (data[10] == YSM_STATUS_OK) { byte[] aead = rangeOfByteArray(data, YSM_AEAD_NONCE_SIZE + 6, data[11]); validateCmdResponseBA("keyHandle", rangeOfByteArray(data, 6, 4), leIntToBA(keyHandle)); result.put("nonce", validateCmdResponseString("nonce", byteArrayToHex(rangeOfByteArray(data, 0, YSM_AEAD_NONCE_SIZE)), nonce)); result.put("aead", byteArrayToHex(aead)); } else { throw new YubiHSMCommandFailedException("Command " + getCommandString(command) + " failed: " + getCommandStatus(data[10])); } return result; } /** * Parse the AEAD validation response from the YubiHSM. * * @param data the data from the YubiHSM * @param nonce the original nonce * @param keyHandle the key used to validate the AEAD * @return returns true if validation was a success, false if the validation failed * @throws YubiHSMCommandFailedException command failed exception * @throws YubiHSMErrorException error exception */ private static boolean parseValidationResult(byte[] data, String nonce, int keyHandle) throws YubiHSMCommandFailedException, YubiHSMErrorException { validateCmdResponseBA("keyHandle", rangeOfByteArray(data, YSM_AEAD_NONCE_SIZE, 4), leIntToBA(keyHandle)); validateCmdResponseString("nonce", byteArrayToHex(new String(data, 0, YSM_AEAD_NONCE_SIZE).getBytes()), nonce); if (data[10] == YSM_STATUS_OK) { return true; } else if (data[10] == YSM_MISMATCH) { return false; } else { throw new YubiHSMCommandFailedException("Command " + getCommandString(YSM_AEAD_DECRYPT_CMP) + " failed: " + getCommandStatus(data[10])); } } }