package edu.washington.cs.oneswarm.f2f.datagram; import java.nio.ByteBuffer; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; import java.security.NoSuchAlgorithmException; import java.security.NoSuchProviderException; import java.security.SecureRandom; import java.util.Arrays; import java.util.logging.Level; import java.util.logging.Logger; import javax.crypto.BadPaddingException; import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; import javax.crypto.ShortBufferException; public class DatagramEncrypter extends DatagramEncrytionBase { public final static Logger logger = Logger.getLogger(DatagramEncrypter.class.getName()); private long ctrRoundCount = 0; private final byte[] paddingBuffer = new byte[BLOCK_SIZE]; private final ByteBuffer paddingBB; public DatagramEncrypter() throws NoSuchAlgorithmException, NoSuchProviderException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException { SecureRandom random = new SecureRandom(); ivSpec = createCtrIvForAES(ctrRoundCount, random); // Create the encryption key and the cipher. key = createKeyForAES(AES_KEY_LENGTH, random); cipher = Cipher.getInstance(ENCR_ALGO); cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); // Create the mac key hmac_key = new byte[HMAC_KEY_LENGTH]; random.nextBytes(hmac_key); paddingBB = ByteBuffer.wrap(paddingBuffer); logger.fine("DatagramEncrypter created"); } public EncryptedPacket encrypt(ByteBuffer unencryptedPayload, byte[] destination) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException, IllegalStateException, InvalidKeyException, InvalidAlgorithmParameterException { return encrypt(new ByteBuffer[] { unencryptedPayload }, 1, destination); } public EncryptedPacket encrypt(ByteBuffer[] unencryptedPayload, int buffers, byte[] destination) throws ShortBufferException, IllegalBlockSizeException, BadPaddingException, IllegalStateException, InvalidKeyException, InvalidAlgorithmParameterException { EncryptedPacket packet = new EncryptedPacket(ctrRoundCount); ByteBuffer payloadBuffer = ByteBuffer.wrap(destination); // Write the counter value for decoding the packet. payloadBuffer.putLong(ctrRoundCount); int packetLength = SEQUENCE_NUMBER_BYTES; int dataBytes = 0; // Encrypt the payload. for (int i = 0; i < buffers; i++) { dataBytes += cipher.update(unencryptedPayload[i], payloadBuffer); } // Add the padding for this packet. preparePaddingBB(dataBytes); // Encrypt the padding into the packet. int paddingBytes = cipher.update(paddingBB, payloadBuffer); // Keep the counter field in sync with the aes internal counter. int inputBytes = dataBytes + paddingBytes; assert (inputBytes % BLOCK_SIZE == 0); ctrRoundCount += inputBytes / BLOCK_SIZE; packetLength += inputBytes; // Prepare the buffer for reading the current content. payloadBuffer.flip(); // Calculate the sha1 digest. sha1.reset(); sha1.update(hmac_key); sha1.update(payloadBuffer); // Add the sha1 digest payloadBuffer.limit(packetLength + HMAC_SIZE); payloadBuffer.position(packetLength); byte[] digestBytes = sha1.getDigest(); payloadBuffer.put(digestBytes); packetLength += digestBytes.length; // Prepare for reading. payloadBuffer.rewind(); packet.payload = payloadBuffer; packet.length = packetLength; if (logger.isLoggable(Level.FINEST)) { logger.finest(String.format("Packet encrypted, data_bytes=%d, padding_bytes=%d, " + "out_bytes=%d, packet_sequence_num=%d", dataBytes, paddingBytes, payloadBuffer.remaining(), packet.getSequenceNumber())); } return packet; } private void preparePaddingBB(int inputBytes) { byte paddingLen = calcPaddingLength(inputBytes); Arrays.fill(paddingBuffer, 0, paddingLen, paddingLen); paddingBB.clear(); paddingBB.limit(paddingLen); } public static byte calcPaddingLength(int byteNum) { byte paddingLen = (byte) (BLOCK_SIZE - byteNum % BLOCK_SIZE); if (paddingLen == 0) { paddingLen = BLOCK_SIZE; } return paddingLen; } public byte[] getKey() { return key.getEncoded(); } public byte[] getIv() { return ivSpec.getIV(); } public byte[] getHmac() { return hmac_key; } }