diff options
author | Peter Dettman <peter.dettman@bouncycastle.org> | 2013-11-17 15:54:45 +0400 |
---|---|---|
committer | Peter Dettman <peter.dettman@bouncycastle.org> | 2013-11-17 15:54:45 +0400 |
commit | 6a190004982f1e9456ccc9c1d6f0acdd37bd8dea (patch) | |
tree | c229d80233c359b23e3d1dbc91c4a53c3c0f779e /core/src | |
parent | 3e7f55745c18119e852bd31ce491c28f53c3387b (diff) |
Refactoring around DeferredHash so that for (D)TLS 1.2 we can snapshot
any/all hashes that might be needed for CertificateVerify.
Defer the actual CertificateVerify hash calculation at the server until
after we have seen the DigitallySigned.
Diffstat (limited to 'core/src')
10 files changed, 81 insertions, 82 deletions
diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java b/core/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java index a433577d..47379117 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/CombinedHash.java @@ -44,8 +44,9 @@ class CombinedHash { } - public void stopTracking() + public TlsHandshakeHash stopTracking() { + return new CombinedHash(this); } public Digest forkPRFHash() diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java index cfde57a1..325db550 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSClientProtocol.java @@ -153,13 +153,13 @@ public class DTLSClientProtocol recordLayer.initPendingEpoch(state.client.getCipher()); // NOTE: Calculated exclusive of the actual Finished message from the server - byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, - ExporterLabel.server_finished, handshake.getCurrentPRFHash()); + byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished, + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData); // NOTE: Calculated exclusive of the Finished message itself byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished, - handshake.getCurrentPRFHash()); + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); handshake.sendMessage(HandshakeType.finished, clientVerifyData); handshake.finish(); @@ -311,6 +311,8 @@ public class DTLSClientProtocol TlsProtocol.establishMasterSecret(state.clientContext, state.keyExchange); recordLayer.initPendingEpoch(state.client.getCipher()); + TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish(); + if (state.clientCredentials != null && state.clientCredentials instanceof TlsSignerCredentials) { TlsSignerCredentials signerCredentials = (TlsSignerCredentials)state.clientCredentials; @@ -318,18 +320,16 @@ public class DTLSClientProtocol * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 */ SignatureAndHashAlgorithm algorithm = null; - byte[] hash = handshake.getCurrentPRFHash(); + byte[] hash = TlsProtocol.getCurrentPRFHash(state.clientContext, prepareFinishHash, null); byte[] signature = signerCredentials.generateCertificateSignature(hash); DigitallySigned certificateVerify = new DigitallySigned(algorithm, signature); byte[] certificateVerifyBody = generateCertificateVerify(state, certificateVerify); handshake.sendMessage(HandshakeType.certificate_verify, certificateVerifyBody); } - handshake.getHandshakeHash().stopTracking(); - // NOTE: Calculated exclusive of the Finished message itself byte[] clientVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.client_finished, - handshake.getCurrentPRFHash()); + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); handshake.sendMessage(HandshakeType.finished, clientVerifyData); if (state.expectSessionTicket) @@ -346,8 +346,8 @@ public class DTLSClientProtocol } // NOTE: Calculated exclusive of the actual Finished message from the server - byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, - ExporterLabel.server_finished, handshake.getCurrentPRFHash()); + byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData(state.clientContext, ExporterLabel.server_finished, + TlsProtocol.getCurrentPRFHash(state.clientContext, handshake.getHandshakeHash(), null)); processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedServerVerifyData); handshake.finish(); diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java index b6bf085b..91ddee60 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSReliableHandshake.java @@ -41,11 +41,10 @@ class DTLSReliableHandshake return handshakeHash; } - byte[] getCurrentPRFHash() + TlsHandshakeHash prepareToFinish() { - Digest copyOfHash = handshakeHash.forkPRFHash(); - byte[] result = new byte[copyOfHash.getDigestSize()]; - copyOfHash.doFinal(result, 0); + TlsHandshakeHash result = handshakeHash; + this.handshakeHash = handshakeHash.stopTracking(); return result; } diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java index b7c3940b..257804d7 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/DTLSServerProtocol.java @@ -235,11 +235,6 @@ public class DTLSServerProtocol } } - if (!expectCertificateVerifyMessage(state)) - { - handshake.getHandshakeHash().stopTracking(); - } - if (clientMessage.getType() == HandshakeType.client_key_exchange) { processClientKeyExchange(state, clientMessage.getBody()); @@ -252,6 +247,8 @@ public class DTLSServerProtocol TlsProtocol.establishMasterSecret(state.serverContext, state.keyExchange); recordLayer.initPendingEpoch(state.server.getCipher()); + TlsHandshakeHash prepareFinishHash = handshake.prepareToFinish(); + /* * RFC 5246 7.4.8 This message is only sent following a client certificate that has signing * capability (i.e., all certificates except those containing fixed Diffie-Hellman @@ -259,17 +256,13 @@ public class DTLSServerProtocol */ if (expectCertificateVerifyMessage(state)) { - // TODO For TLS 1.2, this can't be calculated until we see what hash algorithm the sender used - byte[] certificateVerifyHash = handshake.getCurrentPRFHash(); byte[] certificateVerifyBody = handshake.receiveMessageBody(HandshakeType.certificate_verify); - processCertificateVerify(state, certificateVerifyBody, certificateVerifyHash); - - handshake.getHandshakeHash().stopTracking(); + processCertificateVerify(state, certificateVerifyBody, prepareFinishHash); } // NOTE: Calculated exclusive of the actual Finished message from the client - byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, - ExporterLabel.client_finished, handshake.getCurrentPRFHash()); + byte[] expectedClientVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.client_finished, + TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null)); processFinished(handshake.receiveMessageBody(HandshakeType.finished), expectedClientVerifyData); if (state.expectSessionTicket) @@ -281,7 +274,7 @@ public class DTLSServerProtocol // NOTE: Calculated exclusive of the Finished message itself byte[] serverVerifyData = TlsUtils.calculateVerifyData(state.serverContext, ExporterLabel.server_finished, - handshake.getCurrentPRFHash()); + TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null)); handshake.sendMessage(HandshakeType.finished, serverVerifyData); handshake.finish(); @@ -469,7 +462,7 @@ public class DTLSServerProtocol notifyClientCertificate(state, clientCertificate); } - protected void processCertificateVerify(ServerHandshakeState state, byte[] body, byte[] certificateVerifyHash) + protected void processCertificateVerify(ServerHandshakeState state, byte[] body, TlsHandshakeHash prepareFinishHash) throws IOException { ByteArrayInputStream buf = new ByteArrayInputStream(body); @@ -481,6 +474,9 @@ public class DTLSServerProtocol // Verify the CertificateVerify message contains a correct signature. try { + // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned + byte[] certificateVerifyHash = TlsProtocol.getCurrentPRFHash(state.serverContext, prepareFinishHash, null); + org.bouncycastle.asn1.x509.Certificate x509Cert = state.clientCertificate.getCertificateAt(0); SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo); diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java b/core/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java index 952e0b59..711172f1 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/DeferredHash.java @@ -27,6 +27,14 @@ class DeferredHash this.prfHashAlgorithm = null; } + private DeferredHash(Short prfHashAlgorithm, Digest prfHash) + { + this.buf = null; + this.hashes = new Hashtable(); + this.prfHashAlgorithm = prfHashAlgorithm; + hashes.put(prfHashAlgorithm, prfHash); + } + public void init(TlsContext context) { this.context = context; @@ -65,20 +73,22 @@ class DeferredHash checkStopBuffering(); } - public void stopTracking() + public TlsHandshakeHash stopTracking() { - if (hashes.size() > 1) + Digest prfHash = TlsUtils.cloneHash(prfHashAlgorithm.shortValue(), (Digest)hashes.get(prfHashAlgorithm)); + if (buf != null) { - Digest prfHash = (Digest)hashes.get(prfHashAlgorithm); - hashes = new Hashtable(); - hashes.put(prfHashAlgorithm, prfHash); + buf.updateDigest(prfHash); } - - checkStopBuffering(); + DeferredHash result = new DeferredHash(prfHashAlgorithm, prfHash); + result.init(context); + return result; } public Digest forkPRFHash() { + checkStopBuffering(); + if (buf != null) { Digest prfHash = TlsUtils.createHash(prfHashAlgorithm.shortValue()); diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java b/core/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java index c164cf05..cc6640be 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/RecordStream.java @@ -290,27 +290,16 @@ class RecordStream return handshakeHash; } - void updateHandshakeData(byte[] message, int offset, int len) + TlsHandshakeHash prepareToFinish() { - handshakeHash.update(message, offset, len); + TlsHandshakeHash result = handshakeHash; + this.handshakeHash = handshakeHash.stopTracking(); + return result; } - /** - * 'sender' only relevant to SSLv3 - */ - byte[] getCurrentPRFHash(byte[] sslSender) + void updateHandshakeData(byte[] message, int offset, int len) { - Digest d = handshakeHash.forkPRFHash(); - - if (TlsUtils.isSSL(context)) - { - if (sslSender != null) - { - d.update(sslSender, 0, sslSender.length); - } - } - - return doFinal(d); + handshakeHash.update(message, offset, len); } protected void safeClose() @@ -345,13 +334,6 @@ class RecordStream return contents; } - private static byte[] doFinal(Digest d) - { - byte[] bs = new byte[d.getDigestSize()]; - d.doFinal(bs, 0); - return bs; - } - private static void checkType(short type, short alertDescription) throws IOException { diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java index 85dd1168..2cea7584 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsClientProtocol.java @@ -372,6 +372,8 @@ public class TlsClientProtocol establishMasterSecret(getContext(), keyExchange); recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); + TlsHandshakeHash prepareFinishHash = recordStream.prepareToFinish(); + if (clientCreds != null && clientCreds instanceof TlsSignerCredentials) { TlsSignerCredentials signerCreds = (TlsSignerCredentials)clientCreds; @@ -379,7 +381,7 @@ public class TlsClientProtocol * TODO RFC 5246 4.7. digitally-signed element needs SignatureAndHashAlgorithm from TLS 1.2 */ SignatureAndHashAlgorithm algorithm = null; - byte[] hash = recordStream.getCurrentPRFHash(null); + byte[] hash = getCurrentPRFHash(getContext(), prepareFinishHash, null); byte[] signature = signerCreds.generateCertificateSignature(hash); DigitallySigned certificateVerify = new DigitallySigned(algorithm, signature); sendCertificateVerifyMessage(certificateVerify); @@ -387,8 +389,6 @@ public class TlsClientProtocol this.connection_state = CS_CERTIFICATE_VERIFY; } - this.recordStream.getHandshakeHash().stopTracking(); - sendChangeCipherSpecMessage(); sendFinishedMessage(); this.connection_state = CS_CLIENT_FINISHED; diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java index 9c866b1e..51ba28ca 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsHandshakeHash.java @@ -13,7 +13,7 @@ interface TlsHandshakeHash void sealHashAlgorithms(); - void stopTracking(); + TlsHandshakeHash stopTracking(); Digest forkPRFHash(); } diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java index 262e222e..1a97bd14 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsProtocol.java @@ -11,6 +11,7 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.Vector; +import org.bouncycastle.crypto.Digest; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.Integers; @@ -747,11 +748,11 @@ public abstract class TlsProtocol if (isServer) { return TlsUtils.calculateVerifyData(context, ExporterLabel.server_finished, - recordStream.getCurrentPRFHash(TlsUtils.SSL_SERVER)); + getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_SERVER)); } return TlsUtils.calculateVerifyData(context, ExporterLabel.client_finished, - recordStream.getCurrentPRFHash(TlsUtils.SSL_CLIENT)); + getCurrentPRFHash(getContext(), recordStream.getHandshakeHash(), TlsUtils.SSL_CLIENT)); } /** @@ -881,6 +882,23 @@ public abstract class TlsProtocol } } + /** + * 'sender' only relevant to SSLv3 + */ + protected static byte[] getCurrentPRFHash(TlsContext context, TlsHandshakeHash handshakeHash, byte[] sslSender) + { + Digest d = handshakeHash.forkPRFHash(); + + if (sslSender != null && TlsUtils.isSSL(context)) + { + d.update(sslSender, 0, sslSender.length); + } + + byte[] bs = new byte[d.getDigestSize()]; + d.doFinal(bs, 0); + return bs; + } + protected static Hashtable readExtensions(ByteArrayInputStream input) throws IOException { diff --git a/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java index 5256d2e4..b2c392c8 100644 --- a/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java +++ b/core/src/main/java/org/bouncycastle/crypto/tls/TlsServerProtocol.java @@ -23,7 +23,7 @@ public class TlsServerProtocol protected CertificateRequest certificateRequest = null; protected short clientCertificateType = -1; - protected byte[] certificateVerifyHash = null; + protected TlsHandshakeHash prepareFinishHash = null; public TlsServerProtocol(InputStream input, OutputStream output, SecureRandom secureRandom) { @@ -70,7 +70,7 @@ public class TlsServerProtocol this.keyExchange = null; this.serverCredentials = null; this.certificateRequest = null; - this.certificateVerifyHash = null; + this.prepareFinishHash = null; } protected AbstractTlsContext getContext() @@ -282,7 +282,7 @@ public class TlsServerProtocol * signing capability (i.e., all certificates except those containing fixed * Diffie-Hellman parameters). */ - if (this.certificateVerifyHash == null) + if (!expectCertificateVerifyMessage()) { throw new TlsFatalAlert(AlertDescription.unexpected_message); } @@ -290,8 +290,6 @@ public class TlsServerProtocol receiveCertificateVerifyMessage(buf); this.connection_state = CS_CERTIFICATE_VERIFY; - this.recordStream.getHandshakeHash().stopTracking(); - break; } default: @@ -305,7 +303,7 @@ public class TlsServerProtocol { case CS_CLIENT_KEY_EXCHANGE: { - if (this.certificateVerifyHash != null) + if (expectCertificateVerifyMessage()) { throw new TlsFatalAlert(AlertDescription.unexpected_message); } @@ -434,6 +432,9 @@ public class TlsServerProtocol // Verify the CertificateVerify message contains a correct signature. try { + // TODO For TLS 1.2, this needs to be the hash specified in the DigitallySigned + byte[] certificateVerifyHash = getCurrentPRFHash(getContext(), prepareFinishHash, null); + org.bouncycastle.asn1.x509.Certificate x509Cert = this.peerCertificate.getCertificateAt(0); SubjectPublicKeyInfo keyInfo = x509Cert.getSubjectPublicKeyInfo(); AsymmetricKeyParameter publicKey = PublicKeyFactory.createKey(keyInfo); @@ -441,7 +442,7 @@ public class TlsServerProtocol TlsSigner tlsSigner = TlsUtils.createTlsSigner(this.clientCertificateType); tlsSigner.init(getContext()); tlsSigner.verifyRawSignature(clientCertificateVerify.getAlgorithm(), - clientCertificateVerify.getSignature(), publicKey, this.certificateVerifyHash); + clientCertificateVerify.getSignature(), publicKey, certificateVerifyHash); } catch (Exception e) { @@ -568,20 +569,12 @@ public class TlsServerProtocol establishMasterSecret(getContext(), keyExchange); recordStream.setPendingConnectionState(getPeer().getCompression(), getPeer().getCipher()); + this.prepareFinishHash = recordStream.prepareToFinish(); + if (!expectSessionTicket) { sendChangeCipherSpecMessage(); } - - if (expectCertificateVerifyMessage()) - { - // TODO For TLS 1.2, this can't be calculated until we see what hash algorithm the sender used - this.certificateVerifyHash = recordStream.getCurrentPRFHash(null); - } - else - { - this.recordStream.getHandshakeHash().stopTracking(); - } } protected void sendCertificateRequestMessage(CertificateRequest certificateRequest) |