Distinguish between recoverable and unrecoverable errors.

This commit is contained in:
akwizgran
2012-01-19 19:27:04 +00:00
parent 7eaa1f176f
commit 1f39bfef78
11 changed files with 73 additions and 34 deletions

View File

@@ -49,9 +49,9 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
// No error correction
IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
// Create the reader
// Create the reader - don't tolerate errors
Mac mac = crypto.getMac();
return new ConnectionReaderImpl(correcter, mac, macKey);
return new ConnectionReaderImpl(correcter, mac, macKey, false);
}
public ConnectionReader createConnectionReader(SegmentSource in,
@@ -80,8 +80,8 @@ class ConnectionReaderFactoryImpl implements ConnectionReaderFactory {
// No error correction
IncomingErrorCorrectionLayer correcter =
new NullIncomingErrorCorrectionLayer(decrypter);
// Create the reader
// Create the reader - don't tolerate errors
Mac mac = crypto.getMac();
return new ConnectionReaderImpl(correcter, mac, macKey);
return new ConnectionReaderImpl(correcter, mac, macKey, false);
}
}

View File

@@ -20,15 +20,17 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
private final IncomingErrorCorrectionLayer in;
private final Mac mac;
private final boolean tolerateErrors;
private final Frame frame;
private long frameNumber = 0L;
private int offset = 0, length = 0;
ConnectionReaderImpl(IncomingErrorCorrectionLayer in, Mac mac,
ErasableKey macKey) {
ErasableKey macKey, boolean tolerateErrors) {
this.in = in;
this.mac = mac;
this.tolerateErrors = tolerateErrors;
// Initialise the MAC
try {
mac.init(macKey);
@@ -47,7 +49,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
@Override
public int read() throws IOException {
while(length == 0) if(!readFrame()) return -1;
while(length == 0) if(!readValidFrame()) return -1;
int b = frame.getBuffer()[offset] & 0xff;
offset++;
length--;
@@ -61,7 +63,7 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
@Override
public int read(byte[] b, int off, int len) throws IOException {
while(length == 0) if(!readFrame()) return -1;
while(length == 0) if(!readValidFrame()) return -1;
len = Math.min(len, length);
System.arraycopy(frame.getBuffer(), offset, b, off, len);
offset += len;
@@ -69,33 +71,46 @@ class ConnectionReaderImpl extends InputStream implements ConnectionReader {
return len;
}
private boolean readFrame() throws IOException {
private boolean readValidFrame() throws IOException {
while(true) {
try {
return readFrame();
} catch(InvalidDataException e) {
if(tolerateErrors) continue;
throw new FormatException();
}
}
}
private boolean readFrame() throws IOException, InvalidDataException {
assert length == 0;
// Don't allow more than 2^32 frames to be read
if(frameNumber > MAX_32_BIT_UNSIGNED) throw new IllegalStateException();
if(frameNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalStateException();
// Read a frame
Collection<Long> window = Collections.singleton(frameNumber);
if(!in.readFrame(frame, window)) return false;
// Check that the frame number is correct and the length is legal
byte[] buf = frame.getBuffer();
if(!HeaderEncoder.validateHeader(buf, frameNumber))
throw new FormatException();
throw new InvalidDataException();
// Check that the payload and padding lengths are correct
int payload = HeaderEncoder.getPayloadLength(buf);
int padding = HeaderEncoder.getPaddingLength(buf);
if(frame.getLength() != FRAME_HEADER_LENGTH + payload + padding
+ MAC_LENGTH) throw new FormatException();
+ MAC_LENGTH) throw new InvalidDataException();
// Check that the padding is all zeroes
int paddingStart = FRAME_HEADER_LENGTH + payload;
for(int i = paddingStart; i < paddingStart + padding; i++) {
if(buf[i] != 0) throw new FormatException();
if(buf[i] != 0) throw new InvalidDataException();
}
// Check the MAC
int macStart = FRAME_HEADER_LENGTH + payload + padding;
mac.update(buf, 0, macStart);
byte[] expectedMac = mac.doFinal();
for(int i = 0; i < expectedMac.length; i++) {
if(expectedMac[i] != buf[macStart + i]) throw new FormatException();
if(expectedMac[i] != buf[macStart + i])
throw new InvalidDataException();
}
offset = FRAME_HEADER_LENGTH;
length = payload;

View File

@@ -9,6 +9,10 @@ interface IncomingEncryptionLayer {
/**
* Reads a segment, excluding its tag, into the given buffer. Returns false
* if no more segments can be read from the connection.
* @throws IOException if an unrecoverable error occurs and the connection
* must be closed.
* @throws InvalidDataException if a recoverable error occurs. The caller
* may choose whether to retry the read or close the connection.
*/
boolean readSegment(Segment s) throws IOException;
boolean readSegment(Segment s) throws IOException, InvalidDataException;
}

View File

@@ -9,6 +9,11 @@ interface IncomingErrorCorrectionLayer {
* Reads a frame into the given buffer. The frame number must be contained
* in the given window. Returns false if no more frames can be read from
* the connection.
* @throws IOException if an unrecoverable error occurs and the connection
* must be closed.
* @throws InvalidDataException if a recoverable error occurs. The caller
* may choose whether to retry the read or close the connection.
*/
boolean readFrame(Frame f, Collection<Long> window) throws IOException;
boolean readFrame(Frame f, Collection<Long> window) throws IOException,
InvalidDataException;
}

View File

@@ -11,7 +11,6 @@ import java.security.GeneralSecurityException;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import net.sf.briar.api.FormatException;
import net.sf.briar.api.crypto.ErasableKey;
import net.sf.briar.api.plugins.SegmentSource;
import net.sf.briar.api.transport.Segment;
@@ -32,22 +31,23 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
IncomingSegmentedEncryptionLayer(SegmentSource in, Cipher tagCipher,
Cipher segCipher, ErasableKey tagKey, ErasableKey segKey,
boolean tagEverySegment, Segment s) {
boolean tagEverySegment, Segment bufferedSegment) {
this.in = in;
this.tagCipher = tagCipher;
this.segCipher = segCipher;
this.tagKey = tagKey;
this.segKey = segKey;
this.tagEverySegment = tagEverySegment;
this.bufferedSegment = bufferedSegment;
blockSize = segCipher.getBlockSize();
if(blockSize < FRAME_HEADER_LENGTH)
throw new IllegalArgumentException();
iv = IvEncoder.encodeIv(0L, blockSize);
segment = new SegmentImpl();
bufferedSegment = s;
}
public boolean readSegment(Segment s) throws IOException {
public boolean readSegment(Segment s) throws IOException,
InvalidDataException {
boolean expectTag = tagEverySegment || firstSegment;
firstSegment = false;
try {
@@ -62,14 +62,14 @@ class IncomingSegmentedEncryptionLayer implements IncomingEncryptionLayer {
}
int offset = expectTag ? TAG_LENGTH : 0;
int length = segment.getLength();
if(length > MAX_SEGMENT_LENGTH) throw new FormatException();
if(length > MAX_SEGMENT_LENGTH) throw new InvalidDataException();
if(length < offset + FRAME_HEADER_LENGTH + MAC_LENGTH)
throw new FormatException();
throw new InvalidDataException();
byte[] ciphertext = segment.getBuffer();
// If a tag is expected then decrypt and validate it
if(expectTag) {
long seg = TagEncoder.decodeTag(ciphertext, tagCipher, tagKey);
if(seg == -1) throw new FormatException();
if(seg == -1) throw new InvalidDataException();
segmentNumber = seg;
}
// Decrypt the segment

View File

@@ -0,0 +1,7 @@
package net.sf.briar.transport;
/** An exception that indicates a recoverable formatting error. */
class InvalidDataException extends Exception {
private static final long serialVersionUID = 4455775710413826953L;
}

View File

@@ -16,7 +16,7 @@ class NullIncomingErrorCorrectionLayer implements IncomingErrorCorrectionLayer {
}
public boolean readFrame(Frame f, Collection<Long> window)
throws IOException {
throws IOException, InvalidDataException {
while(true) {
if(!in.readSegment(segment)) return false;
byte[] buf = segment.getBuffer();