001 package edu.harvard.deas.hyperenc; 002 003 import java.io.Serializable; 004 import java.security.InvalidKeyException; 005 import java.security.Key; 006 import java.util.ArrayList; 007 import java.util.Arrays; 008 import java.util.List; 009 010 import javax.crypto.Mac; 011 import javax.crypto.spec.SecretKeySpec; 012 013 /** 014 * Represents a MAC computed with the Hyper-Encryption Message Authentication 015 * Code (HEMAC) algorithm. Also includes methods to compute and verify HEMACs. 016 * <p> 017 * HEMAC makes use of HMAC, specified in <a 018 * href="http://www.ietf.org/rfc/rfc2104.txt">RFC 2104</a>. HEMAC makes use of a 019 * cryptographic hash function <i>h</i> with output length <i>L</i>, and a 020 * secret key <i>K</i> of length <i>2L</i>. The HEMAC of a message <i>x</i> is 021 * computed as follows: <blockquote>HEMAC(<i>K</i>, <i>x</i>) = 022 * <i>K</i>[0:<i>L</i>] ^ HMAC<sub>h</sub>(<i>K</i>[<i>L</i>:2<i>L</i>], 023 * <i>x</i>)</blockquote> where ^ represents the XOR operation, and 024 * <i>K</i>[<i>i</i>:<i>j</i>] represents the <i>i</i>th through <i>j</i>th bits 025 * of <i>K</i> (including <i>i</i>, but excluding <i>j</i>). 026 * <p> 027 * HEMAC uses a similar naming scheme as HMAC: for example, if the underlying 028 * hash function <i>h</i> is SHA1, then the resulting HEMAC is called 029 * HEMAC-SHA1. 030 */ 031 public class HyperMAC implements Serializable { 032 private static final long serialVersionUID = 1L; 033 034 private final List<Integer> blockList; 035 private final byte[] mac; 036 037 /** 038 * Constructs a new HEMAC blob. 039 * 040 * @param blockList 041 * List of encryption block IDs used as the HEMAC key. 042 * @param mac 043 * The HEMAC value itself. 044 */ 045 public HyperMAC(List<Integer> blockList, byte[] mac) { 046 this.blockList = new ArrayList<Integer>(blockList); 047 this.mac = Arrays.copyOf(mac, mac.length); 048 } 049 050 /** 051 * Returns the list of IDs that identify the encryption blocks used for the 052 * key to this HEMAC. 053 * 054 * @return the encryption block ID list 055 */ 056 public List<Integer> getBlockList() { 057 return new ArrayList<Integer>(blockList); 058 } 059 060 /** 061 * Returns the HEMAC value. 062 * @return the HEMAC value, computed as described in this class 063 */ 064 public byte[] getMac() { 065 return Arrays.copyOf(mac, mac.length); 066 } 067 068 069 /** 070 * Computes the HEMAC of <code>msg</code> using the given secret key and HMAC 071 * instance. 072 * 073 * @param hmac 074 * A <code>Mac</code> instance representing the HMAC to be used by this 075 * HEMAC. This <code>Mac</code> object will be initialized using 076 * {@link Mac#init(Key)}, so any state it currently holds will be lost. 077 * @param key 078 * The secret key for this HEMAC. Must be exactly twice the length of 079 * the length of the output of <code>hmac</code>. 080 * @param msg 081 * The message for which to compute the HEMAC. 082 * @return the HEMAC of <code>msg</code>, using the specified HMAC and key. 083 * @throws InvalidKeyException 084 * if the provided key has incorrect length 085 */ 086 public static final byte[] computeMac(Mac hmac, byte[] key, byte[] msg) 087 throws InvalidKeyException { 088 if (hmac.getMacLength() * 2 != key.length) 089 throw new InvalidKeyException("Key length is " + key.length 090 + " bytes, but must be " + 2 * hmac.getMacLength() + " bytes."); 091 092 // Use the second half of the key as the key for the HMAC 093 Key hmacKey = new SecretKeySpec(key, key.length/2, key.length/2, "HEMAC"); 094 try { 095 hmac.init(hmacKey); 096 } catch (InvalidKeyException e) { 097 throw new IllegalArgumentException("Provided key is invalid for HMAC", e); 098 } 099 byte[] part = hmac.doFinal(msg); 100 101 // XOR the HMAC against the first half of the key 102 byte[] hemac = new byte[key.length / 2]; 103 for (int i = 0; i < key.length / 2; i++) 104 hemac[i] = (byte)(key[i] ^ part[i]); 105 return hemac; 106 } 107 }