/* * Copyright (c) 2011 - 2013 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 java.util.HashMap; import java.util.Map; import static org.unitedid.yhsm.internal.Defines.*; import static org.unitedid.yhsm.utility.Utils.*; /** <code>HMACCmd</code> implements the HMAC SHA1 commands for the YubiHSM. */ public class HMACCmd { /** Private constructor */ private HMACCmd() {} /** * 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 * @param last set to false to not get a hash generated for the initial request * @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 static Map<String, String> generateHMACSHA1(DeviceHandler deviceHandler, byte[] data, int keyHandle, byte flags, boolean last, boolean toBuffer) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException { byte [] dataBA = validateByteArray("data", data, YSM_MAX_PKT_SIZE - 6, 0, 0); if (flags == 0) { flags = YSM_HMAC_SHA1_RESET; if (last) { flags |= YSM_HMAC_SHA1_FINAL; } if (toBuffer) { flags |= YSM_HMAC_SHA1_TO_BUFFER; } } 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 * @param data the data to add before generating SHA1 * @param keyHandle the key handle to use in the YubiHSM * @param last set to false to not get a hash generated after this call * @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 static Map<String, String> next(DeviceHandler deviceHandler, byte[] data, int keyHandle, boolean last, boolean toBuffer) throws YubiHSMCommandFailedException, YubiHSMErrorException, YubiHSMInputException { byte flags; if (last) { flags = YSM_HMAC_SHA1_FINAL; } else { flags = 0x0; } if (toBuffer) { flags |= YSM_HMAC_SHA1_TO_BUFFER; } 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. * * @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 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 */ private static Map<String, String> parseResult(byte[] data, int keyHandle, boolean last) throws YubiHSMErrorException, YubiHSMCommandFailedException, YubiHSMInputException { Map<String, String> result = new HashMap<String, String>(); if (data[4] == YSM_STATUS_OK) { validateCmdResponseBA("keyHandle", rangeOfByteArray(data, 0, 4), leIntToBA(keyHandle)); if (last) { result.put("status", "OK"); result.put("hash", byteArrayToHex(rangeOfByteArray(data, 6, data[5]))); } else { byte[] zeroHash = { 0x00 }; zeroHash = validateByteArray("zeroHash", zeroHash, 0, 0, 20); result.put("status", "Expect more data"); result.put("hash", byteArrayToHex(zeroHash)); } } else { throw new YubiHSMCommandFailedException("Command " + getCommandString(YSM_HMAC_SHA1_GENERATE) + " failed: " + getCommandStatus(data[4])); } 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])); } } }