PacketWriter is implemented by two classes: PacketWriterImpl and

PacketEncrypter. The separation allows authentication and encryption
to be tested separately.
This commit is contained in:
akwizgran
2011-08-09 17:50:54 +01:00
parent e9d0021f56
commit f3f0c223c4
9 changed files with 274 additions and 13 deletions

View File

@@ -8,8 +8,10 @@ import java.security.NoSuchProviderException;
import java.security.Security;
import java.security.Signature;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.Mac;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import net.sf.briar.api.crypto.CryptoComponent;
@@ -24,9 +26,11 @@ class CryptoComponentImpl implements CryptoComponent {
private static final String KEY_PAIR_ALGO = "ECDSA";
private static final int KEY_PAIR_KEYSIZE = 256; // Bits
private static final String MAC_ALGO = "HMacSHA256";
private static final String PACKET_CIPHER_ALGO = "AES/CTR/NoPadding";
private static final String SECRET_KEY_ALGO = "AES";
private static final int SECRET_KEY_KEYSIZE = 256; // Bits
private static final String SIGNATURE_ALGO = "ECDSA";
private static final String TAG_CIPHER_ALGO = "AES/ECB/NoPadding";
private final KeyParser keyParser;
private final KeyPairGenerator keyPairGenerator;
@@ -81,6 +85,18 @@ class CryptoComponentImpl implements CryptoComponent {
}
}
public Cipher getPacketCipher() {
try {
return Cipher.getInstance(PACKET_CIPHER_ALGO, PROVIDER);
} catch(NoSuchAlgorithmException impossible) {
throw new RuntimeException(impossible);
} catch(NoSuchPaddingException impossible) {
throw new RuntimeException(impossible);
} catch(NoSuchProviderException impossible) {
throw new RuntimeException(impossible);
}
}
public Signature getSignature() {
try {
return Signature.getInstance(SIGNATURE_ALGO, PROVIDER);
@@ -90,4 +106,16 @@ class CryptoComponentImpl implements CryptoComponent {
throw new RuntimeException(impossible);
}
}
public Cipher getTagCipher() {
try {
return Cipher.getInstance(TAG_CIPHER_ALGO, PROVIDER);
} catch(NoSuchAlgorithmException impossible) {
throw new RuntimeException(impossible);
} catch(NoSuchPaddingException impossible) {
throw new RuntimeException(impossible);
} catch(NoSuchProviderException impossible) {
throw new RuntimeException(impossible);
}
}
}

View File

@@ -0,0 +1,13 @@
package net.sf.briar.transport;
import java.io.IOException;
import java.io.OutputStream;
interface PacketEncrypter {
OutputStream getOutputStream();
void writeTag(byte[] tag) throws IOException;
void finishPacket() throws IOException;
}

View File

@@ -0,0 +1,86 @@
package net.sf.briar.transport;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
class PacketEncrypterImpl extends FilterOutputStream
implements PacketEncrypter {
private final OutputStream out;
private final Cipher tagCipher, packetCipher;
private final SecretKey packetKey;
PacketEncrypterImpl(OutputStream out, Cipher tagCipher,
Cipher packetCipher, SecretKey tagKey, SecretKey packetKey) {
super(out);
this.out = out;
this.tagCipher = tagCipher;
this.packetCipher = packetCipher;
this.packetKey = packetKey;
try {
tagCipher.init(Cipher.ENCRYPT_MODE, tagKey);
} catch(InvalidKeyException e) {
throw new IllegalArgumentException(e);
}
if(tagCipher.getOutputSize(16) != 16)
throw new IllegalArgumentException();
}
public OutputStream getOutputStream() {
return this;
}
public void writeTag(byte[] tag) throws IOException {
if(tag.length != 16) throw new IllegalArgumentException();
IvParameterSpec iv = new IvParameterSpec(tag);
try {
out.write(tagCipher.doFinal(tag));
packetCipher.init(Cipher.ENCRYPT_MODE, packetKey, iv);
} catch(BadPaddingException badCipher) {
throw new IOException(badCipher);
} catch(IllegalBlockSizeException badCipher) {
throw new RuntimeException(badCipher);
} catch(InvalidAlgorithmParameterException badIv) {
throw new RuntimeException(badIv);
} catch(InvalidKeyException badKey) {
throw new RuntimeException(badKey);
}
}
public void finishPacket() throws IOException {
try {
out.write(packetCipher.doFinal());
} catch(BadPaddingException badCipher) {
throw new IOException(badCipher);
} catch(IllegalBlockSizeException badCipher) {
throw new RuntimeException(badCipher);
}
}
@Override
public void write(int b) throws IOException {
byte[] ciphertext = packetCipher.update(new byte[] {(byte) b});
if(ciphertext != null) out.write(ciphertext);
}
@Override
public void write(byte[] b) throws IOException {
byte[] ciphertext = packetCipher.update(b);
if(ciphertext != null) out.write(ciphertext);
}
@Override
public void write(byte[] b, int off, int len) throws IOException {
byte[] ciphertext = packetCipher.update(b, off, len);
if(ciphertext != null) out.write(ciphertext);
}
}

View File

@@ -13,6 +13,7 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
private static final int MAX_16_BIT_UNSIGNED = 65535; // 2^16 - 1
private static final long MAX_32_BIT_UNSIGNED = 4294967295L; // 2^32 - 1
private final PacketEncrypter encrypter;
private final Mac mac;
private final int transportIdentifier;
private final long connectionNumber;
@@ -20,9 +21,10 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
private long packetNumber = 0L;
private boolean betweenPackets = true;
PacketWriterImpl(OutputStream out, Mac mac, int transportIdentifier,
long connectionNumber) {
super(out);
PacketWriterImpl(PacketEncrypter encrypter, Mac mac,
int transportIdentifier, long connectionNumber) {
super(encrypter.getOutputStream());
this.encrypter = encrypter;
this.mac = mac;
if(transportIdentifier < 0) throw new IllegalArgumentException();
if(transportIdentifier > MAX_16_BIT_UNSIGNED)
@@ -65,6 +67,7 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
private void writeMac() throws IOException {
out.write(mac.doFinal());
encrypter.finishPacket();
betweenPackets = true;
}
@@ -78,14 +81,15 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
writeUint32(connectionNumber, tag, 4);
// Encode the packet number as an unsigned 32-bit integer
writeUint32(packetNumber, tag, 8);
// Write the tag to the underlying output stream and the MAC
out.write(tag);
// Write the tag to the encrypter and start calculating the MAC
encrypter.writeTag(tag);
mac.update(tag);
packetNumber++;
betweenPackets = false;
}
private void writeUint16(int i, byte[] b, int offset) {
// Package access for testing
static void writeUint16(int i, byte[] b, int offset) {
assert i >= 0;
assert i <= MAX_16_BIT_UNSIGNED;
assert b.length >= offset + 2;
@@ -93,7 +97,8 @@ class PacketWriterImpl extends FilterOutputStream implements PacketWriter {
b[offset + 1] = (byte) (i & 0xFF);
}
private void writeUint32(long i, byte[] b, int offset) {
// Package access for testing
static void writeUint32(long i, byte[] b, int offset) {
assert i >= 0L;
assert i <= MAX_32_BIT_UNSIGNED;
assert b.length >= offset + 4;