/**
* Copyright 2013-2015 Seagate Technology LLC.
*
* This Source Code Form is subject to the terms of the Mozilla
* Public License, v. 2.0. If a copy of the MPL was not
* distributed with this file, You can obtain one at
* https://mozilla.org/MP:/2.0/.
*
* This program is distributed in the hope that it will be useful,
* but is provided AS-IS, WITHOUT ANY WARRANTY; including without
* the implied warranty of MERCHANTABILITY, NON-INFRINGEMENT or
* FITNESS FOR A PARTICULAR PURPOSE. See the Mozilla Public
* License for more details.
*
* See www.openkinetic.org for more project information
*/
package com.seagate.kinetic.common.lib;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Logger;
import javax.crypto.Mac;
import com.google.protobuf.ByteString;
import com.google.protobuf.GeneratedMessage;
import com.seagate.kinetic.proto.Kinetic.Command;
import com.seagate.kinetic.proto.Kinetic.Message;
/**
* Hmac common library.
*
* @author Jim Hugues.
* @author chiaming
*
*/
public class Hmac {
static final Hmac h = new Hmac();
private final static Logger LOG = Logger.getLogger(Hmac.class.getName());
public static String toString(byte[] b) {
final int MAX_LENGTH = 50; // only include up to MAX_LENGTH bytes
StringWriter sw = new StringWriter();
int length = b.length;
if (length > MAX_LENGTH) {
length = MAX_LENGTH;
}
for (int i = 0; i < length; i++) {
sw.append(String.format("%02x ", b[i]));
}
return sw.toString();
}
public static String toString(ByteString s) {
byte[] b = s.toByteArray();
return toString(b);
}
private Hmac() {
}
static private void oops(String s) throws HmacException {
// oops(Message.Header.Status.INTERNAL_ERROR, s);
oops(Command.Status.StatusCode.INTERNAL_ERROR, s);
}
public class HmacException extends Exception {
private static final long serialVersionUID = 5201751340412081922L;
// Message.Header.Status status;
Command.Status.StatusCode status;
public HmacException(Command.Status.StatusCode status, String s) {
super(s);
this.status = status;
}
}
static private void oops(Command.Status.StatusCode status, String s)
throws HmacException {
throw h.new HmacException(status, s);
}
static private byte[] int32(int x) {
return ByteBuffer.allocate(4).order(ByteOrder.BIG_ENDIAN).putInt(x)
.array();
}
// private byte[] int64(long x) {
// return ByteBuffer.allocate(8).order(ByteOrder.BIG_ENDIAN).putLong(x)
// .array();
// }
static void lv(String name, Mac mac, GeneratedMessage m) {
byte[] bytes = m.toByteArray();
lv(name, mac, bytes);
// if (x.length > 0) {
// LOG.fine(name + toString(x));
// mac.update(int32(x.length));
// mac.update(x);
// }
}
static void lv(String name, Mac mac, byte[] bytes) {
if (bytes.length > 0) {
// LOG.fine(name + toString(bytes));
mac.update(int32(bytes.length));
mac.update(bytes);
}
}
// public static ByteString calc(KineticMessage im, Key key)
// throws HmacException {
//
// try {
//
// //Mac mac = getMacInstance (users, user);
//
// Mac mac = getMacInstance (key);
//
// lv("command", mac, im.getMessage().getCommand().toByteArray());
//
// ByteString result = ByteString.copyFrom(mac.doFinal());
//
// LOG.fine("Message Hmac :" + toString(result));
// return result;
//
// } catch (GeneralSecurityException e) {
// oops(e.getMessage());
// }
// return null; // should never get here...
// }
/**
* Calculate HMAC based on the specified bytes and key.
*
* @param bytes bytes for HMAC calculation
* @param key security key used to calculate HMAC
* @return byte string of hmac value
* @throws HmacException
*/
public static ByteString calc(byte[] bytes, Key key)
throws HmacException {
try {
Mac mac = getMacInstance (key);
lv("command", mac, bytes);
ByteString result = ByteString.copyFrom(mac.doFinal());
LOG.fine("Message Hmac :" + toString(result));
return result;
} catch (GeneralSecurityException e) {
oops(e.getMessage());
}
return null; // should never get here...
}
public static ByteString calcTag(KineticMessage im, Key key) {
ByteString result = null;
try {
// Message.Builder message = (Builder) im.getMessage();
Mac mac = getMacInstance(key);
byte[] value = im.getValue();
if (value == null) {
value = new byte[0];
}
lv("tag", mac, value);
result = ByteString.copyFrom(mac.doFinal());
LOG.fine("Message Tag Hmac :" + toString(result));
} catch (Exception e) {
LOG.warning(e.getMessage());
}
return result;
}
public static boolean check(KineticMessage km, Key key)
throws HmacException {
// get commnad bytes
byte[] bytes = km.getMessage().getCommandBytes().toByteArray();
// get expected hmac value
ByteString expected = km.getMessage().getHmacAuth().getHmac();
// calculate hmac and compare to expected value
if (check (bytes, key, expected)) {
return true;
}
LOG.warning("HMAC did not compare");
return false;
}
/**
* Check if the specified byte[] is equal to the expected hmac with the specified key.
* @param bytes
* @param key
* @param expectedHmac
* @return true or false
* @throws HmacException
*/
public static boolean check(byte[] bytes, Key key, ByteString expectedHmac)
throws HmacException {
if (calc(bytes, key).equals(expectedHmac)) {
return true;
}
LOG.warning("HMAC did not compare");
return false;
}
public static Mac getMacInstance (Key key) throws HmacException, NoSuchAlgorithmException, InvalidKeyException {
Mac mac = null;
//Key key = map.get(user);
if (key == null) {
oops("User not found");
}
mac = Mac.getInstance(key.getAlgorithm());
mac.init(key);
return mac;
}
}