Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -86,29 +86,31 @@ public Hashtable getClientExtensions()
*/
if (TlsUtils.isSignatureAlgorithmsExtensionAllowed(clientVersion))
{
// TODO Provide a way for the user to specify the acceptable hash/signature algorithms.
// TODO Provide a way for the user to specify the acceptable
// hash/signature algorithms.

short[] hashAlgorithms = new short[]{ HashAlgorithm.sha512, HashAlgorithm.sha384, HashAlgorithm.sha256,
HashAlgorithm.sha224, HashAlgorithm.sha1 };
HashAlgorithm.sha224, HashAlgorithm.sha1 };

// TODO Sort out ECDSA signatures and add them as the preferred option here
// TODO Sort out ECDSA signatures and add them as the preferred
// option here
short[] signatureAlgorithms = new short[]{ SignatureAlgorithm.rsa };

this.supportedSignatureAlgorithms = new Vector();
for (int i = 0; i < hashAlgorithms.length; ++i)
{
for (int j = 0; j < signatureAlgorithms.length; ++j)
{
this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(hashAlgorithms[i],
signatureAlgorithms[j]));
this.supportedSignatureAlgorithms
.addElement(new SignatureAndHashAlgorithm(hashAlgorithms[i], signatureAlgorithms[j]));
}
}

/*
* RFC 5264 7.4.3. Currently, DSA [DSS] may only be used with SHA-1.
*/
this.supportedSignatureAlgorithms.addElement(new SignatureAndHashAlgorithm(HashAlgorithm.sha1,
SignatureAlgorithm.dsa));
this.supportedSignatureAlgorithms
.addElement(new SignatureAndHashAlgorithm(HashAlgorithm.sha1, SignatureAlgorithm.dsa));

clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions);

Expand All @@ -129,7 +131,7 @@ public Hashtable getClientExtensions()
*/
this.namedCurves = new int[]{ NamedCurve.secp256r1, NamedCurve.secp384r1 };
this.clientECPointFormats = new short[]{ ECPointFormat.uncompressed,
ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };
ECPointFormat.ansiX962_compressed_prime, ECPointFormat.ansiX962_compressed_char2, };

clientExtensions = TlsExtensionsUtils.ensureExtensionsInitialised(clientExtensions);

Expand All @@ -156,7 +158,7 @@ public void notifyServerVersion(ProtocolVersion serverVersion)

public short[] getCompressionMethods()
{
return new short[]{CompressionMethod._null};
return new short[]{ CompressionMethod._null };
}

public void notifySessionID(byte[] sessionID)
Expand Down Expand Up @@ -238,8 +240,18 @@ public TlsCompression getCompression()
}
}

public void notifyNewSessionTicket(NewSessionTicket newSessionTicket)
public void notifyNewSessionTicket(NewSessionTicket newSessionTicket, SecurityParameters securityParameters)
throws IOException
{
}

public NewSessionTicket getNewSessionTicket()
{
return null;
}

public SecurityParameters getSecurityParameters()
{
return null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -602,8 +602,15 @@ protected void processNewSessionTicket(ClientHandshakeState state, byte[] body)
NewSessionTicket newSessionTicket = NewSessionTicket.parse(buf);

TlsProtocol.assertEmpty(buf);

state.client.notifyNewSessionTicket(newSessionTicket);

/*
* RFC 5077 - notify client so it can save the ticket and security
* parameters for session resumption.
*/
SecurityParameters securityParameters = new SecurityParameters();
securityParameters.copySecurityParametersFrom(state.clientContext.getSecurityParameters());

state.client.notifyNewSessionTicket(newSessionTicket, securityParameters);
}

protected Certificate processServerCertificate(ClientHandshakeState state, byte[] body)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ public void encode(OutputStream output)
TlsUtils.writeUint32(ticketLifetimeHint, output);
TlsUtils.writeOpaque16(ticket, output);
}

public void encodeWithoutLifetime(OutputStream output) throws IOException
{
// just raw ticket bytes
// length will be added later
output.write(ticket);
}

/**
* Parse a {@link NewSessionTicket} from an {@link InputStream}.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,24 @@ public class SecurityParameters
boolean truncatedHMac = false;
boolean encryptThenMAC = false;
boolean extendedMasterSecret = false;

/**
* Copies the security parameters from another instance if it is not null,
* otherwise this is a no-op.
*
* @param other
*/
void copySecurityParametersFrom(SecurityParameters other)
{
if (other != null) {
this.entity = other.entity;
this.cipherSuite = other.cipherSuite;
this.compressionAlgorithm = other.compressionAlgorithm;
this.prfAlgorithm = other.prfAlgorithm;
this.verifyDataLength = other.verifyDataLength;
this.masterSecret = Arrays.clone(other.masterSecret);
}
}

void clear()
{
Expand Down
22 changes: 20 additions & 2 deletions core/src/main/java/org/bouncycastle/crypto/tls/TlsClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,27 @@ Vector getClientSupplementalData()
* ticket is opaque to the client and clients MUST NOT examine the ticket under the assumption
* that it complies with e.g. <i>RFC 5077 4. Recommended Ticket Construction</i>.
*
* @param newSessionTicket The ticket.
* @param newSessionTicket
* The ticket.
* @param sessionParameters
* @throws IOException
*/
void notifyNewSessionTicket(NewSessionTicket newSessionTicket)
void notifyNewSessionTicket(NewSessionTicket newSessionTicket, SecurityParameters securityParameters)
throws IOException;

/**
*
* @return a {@link NewSessionTicket}
*/
NewSessionTicket getNewSessionTicket();

/**
* In the case of TLS resumption using session tickets, {@link TlsClient#getSessionToResume()}
* may return a null TlsSession. Use this method to retrieve the security parameters needed for
* session resumption.
*
* @return A {@link SecurityParameters} object
* @throws IOException
*/
SecurityParameters getSecurityParameters();
}
114 changes: 105 additions & 9 deletions core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.bouncycastle.crypto.tls;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
Expand Down Expand Up @@ -252,7 +253,31 @@ protected void handleHandshakeMessage(short type, byte[] data)

if (this.resumedSession)
{
this.securityParameters.masterSecret = Arrays.clone(this.sessionParameters.getMasterSecret());
/*
* RFC 5077 - The master secret to use depends on whether
* resumed session using session id or ticket.
*
* If this is resumed session, we expect either session
* parameters to be available or the client to provide us
* with security parameters. If neither gives us master
* secret, we raise an alert.
*/

if (this.sessionParameters != null)
{
this.securityParameters.masterSecret = Arrays.clone(this.sessionParameters.getMasterSecret());
}
else if (this.tlsClient.getNewSessionTicket() != null
&& this.tlsClient.getSecurityParameters() != null)
{
this.securityParameters.masterSecret = Arrays.clone(this.tlsClient.getSecurityParameters()
.getMasterSecret());
}
else
{
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}

this.recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher());

sendChangeCipherSpecMessage();
Expand Down Expand Up @@ -577,7 +602,13 @@ protected void receiveNewSessionTicketMessage(ByteArrayInputStream buf)

assertEmpty(buf);

tlsClient.notifyNewSessionTicket(newSessionTicket);
/*
* RFC 5077 - notify client so it can save the ticket and security
* parameters for session resumption.
*/
SecurityParameters securityParameters = new SecurityParameters();
securityParameters.copySecurityParametersFrom(this.securityParameters);
tlsClient.notifyNewSessionTicket(newSessionTicket, securityParameters);
}

protected void receiveServerHelloMessage(ByteArrayInputStream buf)
Expand Down Expand Up @@ -617,10 +648,12 @@ protected void receiveServerHelloMessage(ByteArrayInputStream buf)
}

this.tlsClient.notifySessionID(this.selectedSessionID);

this.resumedSession = this.selectedSessionID.length > 0 && this.tlsSession != null
&& Arrays.areEqual(this.selectedSessionID, this.tlsSession.getSessionID());


/*
* RFC 5077 - can resume either using session id or using
* NewSessionTicket
*/
this.resumedSession = canResumeUsingSessionId() || canResumeUsingNewSessionTicket();
/*
* Find out which CipherSuite the server has chosen and check that it was one of the offered
* ones, and is a valid selection for the negotiated version.
Expand Down Expand Up @@ -765,14 +798,49 @@ protected void receiveServerHelloMessage(ByteArrayInputStream buf)
Hashtable sessionClientExtensions = clientExtensions, sessionServerExtensions = serverExtensions;
if (this.resumedSession)
{
if (selectedCipherSuite != this.sessionParameters.getCipherSuite()
|| selectedCompressionMethod != this.sessionParameters.getCompressionAlgorithm())
/*
* RFC 5077 - We will have session parameters only if there is a TLS
* session (with id). In the case of session tickets, there is no
* TLS session. So the check to ensure whether the selected cipher
* suite and compression algorithm match with expected ones depends
* on whether session resumption using session id or ticket is being
* done. In the case of session id, expected cipher suite and
* compression algorithm are from the session parameters. In the
* case of session ticket, they are from the security parameters
* (expected to be stored in the client).
*/

int expectedCipherSuite = -1;
short expectedCompressionAlgorithm = -1;

if (this.selectedSessionID != null && this.selectedSessionID.length > 0)
{
expectedCipherSuite = this.sessionParameters.getCipherSuite();
expectedCompressionAlgorithm = this.sessionParameters.getCompressionAlgorithm();
}
else if (this.tlsClient.getNewSessionTicket() != null && this.tlsClient.getSecurityParameters() != null)
{
expectedCipherSuite = this.tlsClient.getSecurityParameters().getCipherSuite();
expectedCompressionAlgorithm = this.tlsClient.getSecurityParameters().getCompressionAlgorithm();
}
else
{
throw new TlsFatalAlert(AlertDescription.handshake_failure);
}

if (selectedCipherSuite != expectedCipherSuite || selectedCompressionMethod != expectedCompressionAlgorithm)
{
throw new TlsFatalAlert(AlertDescription.illegal_parameter);
}

sessionClientExtensions = null;
sessionServerExtensions = this.sessionParameters.readServerExtensions();

// RFC 5077 - this.sessionParameters can be null if resuming using
// session tickets
if (this.sessionParameters != null)
{
sessionServerExtensions = this.sessionParameters.readServerExtensions();
}

this.securityParameters.extendedMasterSecret = TlsExtensionsUtils.hasExtendedMasterSecretExtension(sessionServerExtensions);
}
Expand Down Expand Up @@ -919,6 +987,23 @@ protected void sendClientHelloMessage()
}

TlsUtils.writeUint8ArrayWithUint8Length(offeredCompressionMethods, message);

/*
* RFC 5077 - If the client supports session ticket extension and it has
* a ticket, then put the ticket in the client hello.
*/
byte[] sessionTicketExtData = TlsUtils.getExtensionData(clientExtensions, EXT_SessionTicket);
NewSessionTicket sessionTicket = tlsClient.getNewSessionTicket();

boolean sessionTicketExtSupported = sessionTicketExtData != null;
boolean sessionTicketPresent = sessionTicket != null;

if (sessionTicketExtSupported && sessionTicketPresent)
{
ByteArrayOutputStream output = new ByteArrayOutputStream();
sessionTicket.encodeWithoutLifetime(output);
clientExtensions.put(EXT_SessionTicket, output.toByteArray());
}

if (clientExtensions != null)
{
Expand All @@ -937,4 +1022,15 @@ protected void sendClientKeyExchangeMessage()

message.writeToRecordStream();
}

private boolean canResumeUsingSessionId()
{
return this.selectedSessionID.length > 0 && this.tlsSession != null
&& Arrays.areEqual(this.selectedSessionID, this.tlsSession.getSessionID());
}

private boolean canResumeUsingNewSessionTicket()
{
return this.tlsClient.getNewSessionTicket() != null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public class TlsExtensionsUtils
public static final Integer EXT_server_name = Integers.valueOf(ExtensionType.server_name);
public static final Integer EXT_status_request = Integers.valueOf(ExtensionType.status_request);
public static final Integer EXT_truncated_hmac = Integers.valueOf(ExtensionType.truncated_hmac);
public static final Integer EXT_session_tickets = Integer.valueOf(ExtensionType.session_ticket);

public static Hashtable ensureExtensionsInitialised(Hashtable extensions)
{
Expand Down Expand Up @@ -60,6 +61,11 @@ public static void addTruncatedHMacExtension(Hashtable extensions)
{
extensions.put(EXT_truncated_hmac, createTruncatedHMacExtension());
}

public static void addSessionTicketExtension(Hashtable extensions)
{
extensions.put(EXT_session_tickets, createEmptyExtensionData());
}

public static HeartbeatExtension getHeartbeatExtension(Hashtable extensions)
throws IOException
Expand Down
Loading