diff options
Diffstat (limited to 'libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSRecordLayer.java')
-rw-r--r-- | libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSRecordLayer.java | 520 |
1 files changed, 0 insertions, 520 deletions
diff --git a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSRecordLayer.java b/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSRecordLayer.java deleted file mode 100644 index 6586599fb..000000000 --- a/libraries/spongycastle/core/src/main/java/org/spongycastle/crypto/tls/DTLSRecordLayer.java +++ /dev/null @@ -1,520 +0,0 @@ -package org.spongycastle.crypto.tls; - -import java.io.IOException; - -class DTLSRecordLayer - implements DatagramTransport -{ - private static final int RECORD_HEADER_LENGTH = 13; - private static final int MAX_FRAGMENT_LENGTH = 1 << 14; - private static final long TCP_MSL = 1000L * 60 * 2; - private static final long RETRANSMIT_TIMEOUT = TCP_MSL * 2; - - private final DatagramTransport transport; - private final TlsContext context; - private final TlsPeer peer; - - private final ByteQueue recordQueue = new ByteQueue(); - - private volatile boolean closed = false; - private volatile boolean failed = false; - private volatile ProtocolVersion discoveredPeerVersion = null; - private volatile boolean inHandshake; - private volatile int plaintextLimit; - private DTLSEpoch currentEpoch, pendingEpoch; - private DTLSEpoch readEpoch, writeEpoch; - - private DTLSHandshakeRetransmit retransmit = null; - private DTLSEpoch retransmitEpoch = null; - private long retransmitExpiry = 0; - - DTLSRecordLayer(DatagramTransport transport, TlsContext context, TlsPeer peer, short contentType) - { - this.transport = transport; - this.context = context; - this.peer = peer; - - this.inHandshake = true; - - this.currentEpoch = new DTLSEpoch(0, new TlsNullCipher(context)); - this.pendingEpoch = null; - this.readEpoch = currentEpoch; - this.writeEpoch = currentEpoch; - - setPlaintextLimit(MAX_FRAGMENT_LENGTH); - } - - void setPlaintextLimit(int plaintextLimit) - { - this.plaintextLimit = plaintextLimit; - } - - ProtocolVersion getDiscoveredPeerVersion() - { - return discoveredPeerVersion; - } - - ProtocolVersion resetDiscoveredPeerVersion() - { - ProtocolVersion result = discoveredPeerVersion; - discoveredPeerVersion = null; - return result; - } - - void initPendingEpoch(TlsCipher pendingCipher) - { - if (pendingEpoch != null) - { - throw new IllegalStateException(); - } - - /* - * TODO "In order to ensure that any given sequence/epoch pair is unique, implementations - * MUST NOT allow the same epoch value to be reused within two times the TCP maximum segment - * lifetime." - */ - - // TODO Check for overflow - this.pendingEpoch = new DTLSEpoch(writeEpoch.getEpoch() + 1, pendingCipher); - } - - void handshakeSuccessful(DTLSHandshakeRetransmit retransmit) - { - if (readEpoch == currentEpoch || writeEpoch == currentEpoch) - { - // TODO - throw new IllegalStateException(); - } - - if (retransmit != null) - { - this.retransmit = retransmit; - this.retransmitEpoch = currentEpoch; - this.retransmitExpiry = System.currentTimeMillis() + RETRANSMIT_TIMEOUT; - } - - this.inHandshake = false; - this.currentEpoch = pendingEpoch; - this.pendingEpoch = null; - } - - void resetWriteEpoch() - { - if (retransmitEpoch != null) - { - this.writeEpoch = retransmitEpoch; - } - else - { - this.writeEpoch = currentEpoch; - } - } - - public int getReceiveLimit() - throws IOException - { - return Math.min(this.plaintextLimit, - readEpoch.getCipher().getPlaintextLimit(transport.getReceiveLimit() - RECORD_HEADER_LENGTH)); - } - - public int getSendLimit() - throws IOException - { - return Math.min(this.plaintextLimit, - writeEpoch.getCipher().getPlaintextLimit(transport.getSendLimit() - RECORD_HEADER_LENGTH)); - } - - public int receive(byte[] buf, int off, int len, int waitMillis) - throws IOException - { - byte[] record = null; - - for (;;) - { - int receiveLimit = Math.min(len, getReceiveLimit()) + RECORD_HEADER_LENGTH; - if (record == null || record.length < receiveLimit) - { - record = new byte[receiveLimit]; - } - - try - { - if (retransmit != null && System.currentTimeMillis() > retransmitExpiry) - { - retransmit = null; - retransmitEpoch = null; - } - - int received = receiveRecord(record, 0, receiveLimit, waitMillis); - if (received < 0) - { - return received; - } - if (received < RECORD_HEADER_LENGTH) - { - continue; - } - int length = TlsUtils.readUint16(record, 11); - if (received != (length + RECORD_HEADER_LENGTH)) - { - continue; - } - - short type = TlsUtils.readUint8(record, 0); - - // TODO Support user-specified custom protocols? - switch (type) - { - case ContentType.alert: - case ContentType.application_data: - case ContentType.change_cipher_spec: - case ContentType.handshake: - case ContentType.heartbeat: - break; - default: - // TODO Exception? - continue; - } - - int epoch = TlsUtils.readUint16(record, 3); - - DTLSEpoch recordEpoch = null; - if (epoch == readEpoch.getEpoch()) - { - recordEpoch = readEpoch; - } - else if (type == ContentType.handshake && retransmitEpoch != null - && epoch == retransmitEpoch.getEpoch()) - { - recordEpoch = retransmitEpoch; - } - - if (recordEpoch == null) - { - continue; - } - - long seq = TlsUtils.readUint48(record, 5); - if (recordEpoch.getReplayWindow().shouldDiscard(seq)) - { - continue; - } - - ProtocolVersion version = TlsUtils.readVersion(record, 1); - if (discoveredPeerVersion != null && !discoveredPeerVersion.equals(version)) - { - continue; - } - - byte[] plaintext = recordEpoch.getCipher().decodeCiphertext( - getMacSequenceNumber(recordEpoch.getEpoch(), seq), type, record, RECORD_HEADER_LENGTH, - received - RECORD_HEADER_LENGTH); - - recordEpoch.getReplayWindow().reportAuthenticated(seq); - - if (plaintext.length > this.plaintextLimit) - { - continue; - } - - if (discoveredPeerVersion == null) - { - discoveredPeerVersion = version; - } - - switch (type) - { - case ContentType.alert: - { - if (plaintext.length == 2) - { - short alertLevel = plaintext[0]; - short alertDescription = plaintext[1]; - - peer.notifyAlertReceived(alertLevel, alertDescription); - - if (alertLevel == AlertLevel.fatal) - { - fail(alertDescription); - throw new TlsFatalAlert(alertDescription); - } - - // TODO Can close_notify be a fatal alert? - if (alertDescription == AlertDescription.close_notify) - { - closeTransport(); - } - } - else - { - // TODO What exception? - } - - continue; - } - case ContentType.application_data: - { - if (inHandshake) - { - // TODO Consider buffering application data for new epoch that arrives - // out-of-order with the Finished message - continue; - } - break; - } - case ContentType.change_cipher_spec: - { - // Implicitly receive change_cipher_spec and change to pending cipher state - - for (int i = 0; i < plaintext.length; ++i) - { - short message = TlsUtils.readUint8(plaintext, i); - if (message != ChangeCipherSpec.change_cipher_spec) - { - continue; - } - - if (pendingEpoch != null) - { - readEpoch = pendingEpoch; - } - } - - continue; - } - case ContentType.handshake: - { - if (!inHandshake) - { - if (retransmit != null) - { - retransmit.receivedHandshakeRecord(epoch, plaintext, 0, plaintext.length); - } - - // TODO Consider support for HelloRequest - continue; - } - break; - } - case ContentType.heartbeat: - { - // TODO[RFC 6520] - continue; - } - } - - /* - * NOTE: If we receive any non-handshake data in the new epoch implies the peer has - * received our final flight. - */ - if (!inHandshake && retransmit != null) - { - this.retransmit = null; - this.retransmitEpoch = null; - } - - System.arraycopy(plaintext, 0, buf, off, plaintext.length); - return plaintext.length; - } - catch (IOException e) - { - // NOTE: Assume this is a timeout for the moment - throw e; - } - } - } - - public void send(byte[] buf, int off, int len) - throws IOException - { - short contentType = ContentType.application_data; - - if (this.inHandshake || this.writeEpoch == this.retransmitEpoch) - { - contentType = ContentType.handshake; - - short handshakeType = TlsUtils.readUint8(buf, off); - if (handshakeType == HandshakeType.finished) - { - DTLSEpoch nextEpoch = null; - if (this.inHandshake) - { - nextEpoch = pendingEpoch; - } - else if (this.writeEpoch == this.retransmitEpoch) - { - nextEpoch = currentEpoch; - } - - if (nextEpoch == null) - { - // TODO - throw new IllegalStateException(); - } - - // Implicitly send change_cipher_spec and change to pending cipher state - - // TODO Send change_cipher_spec and finished records in single datagram? - byte[] data = new byte[]{ 1 }; - sendRecord(ContentType.change_cipher_spec, data, 0, data.length); - - writeEpoch = nextEpoch; - } - } - - sendRecord(contentType, buf, off, len); - } - - public void close() - throws IOException - { - if (!closed) - { - if (inHandshake) - { - warn(AlertDescription.user_canceled, "User canceled handshake"); - } - closeTransport(); - } - } - - void fail(short alertDescription) - { - if (!closed) - { - try - { - raiseAlert(AlertLevel.fatal, alertDescription, null, null); - } - catch (Exception e) - { - // Ignore - } - - failed = true; - - closeTransport(); - } - } - - void warn(short alertDescription, String message) - throws IOException - { - raiseAlert(AlertLevel.warning, alertDescription, message, null); - } - - private void closeTransport() - { - if (!closed) - { - /* - * RFC 5246 7.2.1. Unless some other fatal alert has been transmitted, each party is - * required to send a close_notify alert before closing the write side of the - * connection. The other party MUST respond with a close_notify alert of its own and - * close down the connection immediately, discarding any pending writes. - */ - - try - { - if (!failed) - { - warn(AlertDescription.close_notify, null); - } - transport.close(); - } - catch (Exception e) - { - // Ignore - } - - closed = true; - } - } - - private void raiseAlert(short alertLevel, short alertDescription, String message, Exception cause) - throws IOException - { - peer.notifyAlertRaised(alertLevel, alertDescription, message, cause); - - byte[] error = new byte[2]; - error[0] = (byte)alertLevel; - error[1] = (byte)alertDescription; - - sendRecord(ContentType.alert, error, 0, 2); - } - - private int receiveRecord(byte[] buf, int off, int len, int waitMillis) - throws IOException - { - if (recordQueue.size() > 0) - { - int length = 0; - if (recordQueue.size() >= RECORD_HEADER_LENGTH) - { - byte[] lengthBytes = new byte[2]; - recordQueue.read(lengthBytes, 0, 2, 11); - length = TlsUtils.readUint16(lengthBytes, 0); - } - - int received = Math.min(recordQueue.size(), RECORD_HEADER_LENGTH + length); - recordQueue.removeData(buf, off, received, 0); - return received; - } - - int received = transport.receive(buf, off, len, waitMillis); - if (received >= RECORD_HEADER_LENGTH) - { - int fragmentLength = TlsUtils.readUint16(buf, off + 11); - int recordLength = RECORD_HEADER_LENGTH + fragmentLength; - if (received > recordLength) - { - recordQueue.addData(buf, off + recordLength, received - recordLength); - received = recordLength; - } - } - - return received; - } - - private void sendRecord(short contentType, byte[] buf, int off, int len) - throws IOException - { - if (len > this.plaintextLimit) - { - throw new TlsFatalAlert(AlertDescription.internal_error); - } - - /* - * RFC 5264 6.2.1 Implementations MUST NOT send zero-length fragments of Handshake, Alert, - * or ChangeCipherSpec content types. - */ - if (len < 1 && contentType != ContentType.application_data) - { - throw new TlsFatalAlert(AlertDescription.internal_error); - } - - int recordEpoch = writeEpoch.getEpoch(); - long recordSequenceNumber = writeEpoch.allocateSequenceNumber(); - - byte[] ciphertext = writeEpoch.getCipher().encodePlaintext( - getMacSequenceNumber(recordEpoch, recordSequenceNumber), contentType, buf, off, len); - - // TODO Check the ciphertext length? - - byte[] record = new byte[ciphertext.length + RECORD_HEADER_LENGTH]; - TlsUtils.writeUint8(contentType, record, 0); - ProtocolVersion version = discoveredPeerVersion != null ? discoveredPeerVersion : context.getClientVersion(); - TlsUtils.writeVersion(version, record, 1); - TlsUtils.writeUint16(recordEpoch, record, 3); - TlsUtils.writeUint48(recordSequenceNumber, record, 5); - TlsUtils.writeUint16(ciphertext.length, record, 11); - System.arraycopy(ciphertext, 0, record, RECORD_HEADER_LENGTH, ciphertext.length); - - transport.send(record, 0, record.length); - } - - private static long getMacSequenceNumber(int epoch, long sequence_number) - { - return ((long)epoch << 48) | sequence_number; - } -} |