protected DTLSTransport serverHandshake(ServerHandshakeState state, DTLSRecordLayer recordLayer) throws IOException { SecurityParameters securityParameters = state.serverContext.getSecurityParameters(); DTLSReliableHandshake handshake = new DTLSReliableHandshake(state.serverContext, recordLayer); DTLSReliableHandshake.Message clientMessage = handshake.receiveMessage(); { // NOTE: After receiving a record from the client, we discover the record layer version ProtocolVersion client_version = recordLayer.getDiscoveredPeerVersion(); // TODO Read RFCs for guidance on the expected record layer version number state.serverContext.setClientVersion(client_version); } if (clientMessage.getType() == HandshakeType.client_hello) { processClientHello(state, clientMessage.getBody()); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } { byte[] serverHelloBody = generateServerHello(state); if (state.maxFragmentLength >= 0) { int plainTextLimit = 1 << (8 + state.maxFragmentLength); recordLayer.setPlaintextLimit(plainTextLimit); } securityParameters.cipherSuite = state.selectedCipherSuite; securityParameters.compressionAlgorithm = state.selectedCompressionMethod; securityParameters.prfAlgorithm = TlsProtocol.getPRFAlgorithm(state.serverContext, state.selectedCipherSuite); /* * RFC 5264 7.4.9. Any cipher suite which does not explicitly specify verify_data_length * has a verify_data_length equal to 12. This includes all existing cipher suites. */ securityParameters.verifyDataLength = 12; handshake.sendMessage(HandshakeType.server_hello, serverHelloBody); } handshake.notifyHelloComplete(); Vector serverSupplementalData = state.server.getServerSupplementalData(); if (serverSupplementalData != null) { byte[] supplementalDataBody = generateSupplementalData(serverSupplementalData); handshake.sendMessage(HandshakeType.supplemental_data, supplementalDataBody); } state.keyExchange = state.server.getKeyExchange(); state.keyExchange.init(state.serverContext); state.serverCredentials = state.server.getCredentials(); Certificate serverCertificate = null; if (state.serverCredentials == null) { state.keyExchange.skipServerCredentials(); } else { state.keyExchange.processServerCredentials(state.serverCredentials); serverCertificate = state.serverCredentials.getCertificate(); byte[] certificateBody = generateCertificate(serverCertificate); handshake.sendMessage(HandshakeType.certificate, certificateBody); } // TODO[RFC 3546] Check whether empty certificates is possible, allowed, or excludes // CertificateStatus if (serverCertificate == null || serverCertificate.isEmpty()) { state.allowCertificateStatus = false; } if (state.allowCertificateStatus) { CertificateStatus certificateStatus = state.server.getCertificateStatus(); if (certificateStatus != null) { byte[] certificateStatusBody = generateCertificateStatus(state, certificateStatus); handshake.sendMessage(HandshakeType.certificate_status, certificateStatusBody); } } byte[] serverKeyExchange = state.keyExchange.generateServerKeyExchange(); if (serverKeyExchange != null) { handshake.sendMessage(HandshakeType.server_key_exchange, serverKeyExchange); } if (state.serverCredentials != null) { state.certificateRequest = state.server.getCertificateRequest(); if (state.certificateRequest != null) { state.keyExchange.validateCertificateRequest(state.certificateRequest); byte[] certificateRequestBody = generateCertificateRequest(state, state.certificateRequest); handshake.sendMessage(HandshakeType.certificate_request, certificateRequestBody); TlsUtils.trackHashAlgorithms( handshake.getHandshakeHash(), state.certificateRequest.getSupportedSignatureAlgorithms()); } } handshake.sendMessage(HandshakeType.server_hello_done, TlsUtils.EMPTY_BYTES); handshake.getHandshakeHash().sealHashAlgorithms(); clientMessage = handshake.receiveMessage(); if (clientMessage.getType() == HandshakeType.supplemental_data) { processClientSupplementalData(state, clientMessage.getBody()); clientMessage = handshake.receiveMessage(); } else { state.server.processClientSupplementalData(null); } if (state.certificateRequest == null) { state.keyExchange.skipClientCredentials(); } else { if (clientMessage.getType() == HandshakeType.certificate) { processClientCertificate(state, clientMessage.getBody()); clientMessage = handshake.receiveMessage(); } else { if (TlsUtils.isTLSv12(state.serverContext)) { /* * RFC 5246 If no suitable certificate is available, the client MUST send a * certificate message containing no certificates. * * NOTE: In previous RFCs, this was SHOULD instead of MUST. */ throw new TlsFatalAlert(AlertDescription.unexpected_message); } notifyClientCertificate(state, Certificate.EMPTY_CHAIN); } } if (clientMessage.getType() == HandshakeType.client_key_exchange) { processClientKeyExchange(state, clientMessage.getBody()); } else { throw new TlsFatalAlert(AlertDescription.unexpected_message); } 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 * parameters). */ if (expectCertificateVerifyMessage(state)) { byte[] certificateVerifyBody = handshake.receiveMessageBody(HandshakeType.certificate_verify); processCertificateVerify(state, certificateVerifyBody, prepareFinishHash); } // NOTE: Calculated exclusive of the actual Finished message from the client 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) { NewSessionTicket newSessionTicket = state.server.getNewSessionTicket(); byte[] newSessionTicketBody = generateNewSessionTicket(state, newSessionTicket); handshake.sendMessage(HandshakeType.session_ticket, newSessionTicketBody); } // NOTE: Calculated exclusive of the Finished message itself byte[] serverVerifyData = TlsUtils.calculateVerifyData( state.serverContext, ExporterLabel.server_finished, TlsProtocol.getCurrentPRFHash(state.serverContext, handshake.getHandshakeHash(), null)); handshake.sendMessage(HandshakeType.finished, serverVerifyData); handshake.finish(); state.server.notifyHandshakeComplete(); return new DTLSTransport(recordLayer); }
private void processHandshakeMessage(short type, byte[] buf) throws IOException { ByteArrayInputStream is = new ByteArrayInputStream(buf); switch (type) { case HandshakeType.certificate: { switch (connection_state) { case CS_SERVER_HELLO_RECEIVED: { // Parse the Certificate message and send to cipher suite Certificate serverCertificate = Certificate.parse(is); assertEmpty(is); this.keyExchange.processServerCertificate(serverCertificate); this.authentication = tlsClient.getAuthentication(); this.authentication.notifyServerCertificate(serverCertificate); break; } default: this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); } connection_state = CS_SERVER_CERTIFICATE_RECEIVED; break; } case HandshakeType.finished: switch (connection_state) { case CS_SERVER_CHANGE_CIPHER_SPEC_RECEIVED: /* * Read the checksum from the finished message, it has always 12 * bytes for TLS 1.0 and 36 for SSLv3. */ boolean isTls = tlsClientContext.getServerVersion().getFullVersion() >= ProtocolVersion.TLSv10.getFullVersion(); int checksumLength = isTls ? 12 : 36; byte[] serverVerifyData = new byte[checksumLength]; TlsUtils.readFully(serverVerifyData, is); assertEmpty(is); /* * Calculate our own checksum. */ byte[] expectedServerVerifyData = TlsUtils.calculateVerifyData( tlsClientContext, "server finished", rs.getCurrentHash(TlsUtils.SSL_SERVER)); /* * Compare both checksums. */ if (!Arrays.constantTimeAreEqual(expectedServerVerifyData, serverVerifyData)) { /* * Wrong checksum in the finished message. */ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); } connection_state = CS_DONE; /* * We are now ready to receive application data. */ this.appDataReady = true; break; default: this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); } break; case HandshakeType.server_hello: switch (connection_state) { case CS_CLIENT_HELLO_SEND: /* * Read the server hello message */ ProtocolVersion server_version = TlsUtils.readVersion(is); ProtocolVersion client_version = this.tlsClientContext.getClientVersion(); if (server_version.getFullVersion() > client_version.getFullVersion()) { this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } this.tlsClientContext.setServerVersion(server_version); this.tlsClient.notifyServerVersion(server_version); /* * Read the server random */ securityParameters.serverRandom = new byte[32]; TlsUtils.readFully(securityParameters.serverRandom, is); byte[] sessionID = TlsUtils.readOpaque8(is); if (sessionID.length > 32) { this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } this.tlsClient.notifySessionID(sessionID); /* * Find out which CipherSuite the server has chosen and check that * it was one of the offered ones. */ int selectedCipherSuite = TlsUtils.readUint16(is); if (!arrayContains(offeredCipherSuites, selectedCipherSuite) || selectedCipherSuite == CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV) { this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } this.tlsClient.notifySelectedCipherSuite(selectedCipherSuite); /* * Find out which CompressionMethod the server has chosen and check that * it was one of the offered ones. */ short selectedCompressionMethod = TlsUtils.readUint8(is); if (!arrayContains(offeredCompressionMethods, selectedCompressionMethod)) { this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } this.tlsClient.notifySelectedCompressionMethod(selectedCompressionMethod); /* * RFC3546 2.2 The extended server hello message format MAY be * sent in place of the server hello message when the client has * requested extended functionality via the extended client hello * message specified in Section 2.1. ... Note that the extended * server hello message is only sent in response to an extended * client hello message. This prevents the possibility that the * extended server hello message could "break" existing TLS 1.0 * clients. */ /* * TODO RFC 3546 2.3 If [...] the older session is resumed, then * the server MUST ignore extensions appearing in the client * hello, and send a server hello containing no extensions. */ // Integer -> byte[] Hashtable serverExtensions = new Hashtable(); if (is.available() > 0) { // Process extensions from extended server hello byte[] extBytes = TlsUtils.readOpaque16(is); ByteArrayInputStream ext = new ByteArrayInputStream(extBytes); while (ext.available() > 0) { Integer extType = Integers.valueOf(TlsUtils.readUint16(ext)); byte[] extValue = TlsUtils.readOpaque16(ext); /* * RFC 5746 Note that sending a "renegotiation_info" * extension in response to a ClientHello containing only * the SCSV is an explicit exception to the prohibition in * RFC 5246, Section 7.4.1.4, on the server sending * unsolicited extensions and is only allowed because the * client is signaling its willingness to receive the * extension via the TLS_EMPTY_RENEGOTIATION_INFO_SCSV * SCSV. TLS implementations MUST continue to comply with * Section 7.4.1.4 for all other extensions. */ if (!extType.equals(EXT_RenegotiationInfo) && clientExtensions.get(extType) == null) { /* * RFC 3546 2.3 Note that for all extension types * (including those defined in future), the extension * type MUST NOT appear in the extended server hello * unless the same extension type appeared in the * corresponding client hello. Thus clients MUST abort * the handshake if they receive an extension type in * the extended server hello that they did not request * in the associated (extended) client hello. */ this.failWithError(AlertLevel.fatal, AlertDescription.unsupported_extension); } if (serverExtensions.containsKey(extType)) { /* * RFC 3546 2.3 Also note that when multiple * extensions of different types are present in the * extended client hello or the extended server hello, * the extensions may appear in any order. There MUST * NOT be more than one extension of the same type. */ this.failWithError(AlertLevel.fatal, AlertDescription.illegal_parameter); } serverExtensions.put(extType, extValue); } } assertEmpty(is); /* * RFC 5746 3.4. When a ServerHello is received, the client MUST * check if it includes the "renegotiation_info" extension: */ { boolean secure_negotiation = serverExtensions.containsKey(EXT_RenegotiationInfo); /* * If the extension is present, set the secure_renegotiation * flag to TRUE. The client MUST then verify that the length * of the "renegotiated_connection" field is zero, and if it * is not, MUST abort the handshake (by sending a fatal * handshake_failure alert). */ if (secure_negotiation) { byte[] renegExtValue = (byte[]) serverExtensions.get(EXT_RenegotiationInfo); if (!Arrays.constantTimeAreEqual( renegExtValue, createRenegotiationInfo(emptybuf))) { this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); } } tlsClient.notifySecureRenegotiation(secure_negotiation); } if (clientExtensions != null) { tlsClient.processServerExtensions(serverExtensions); } this.keyExchange = tlsClient.getKeyExchange(); connection_state = CS_SERVER_HELLO_RECEIVED; break; default: this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); } break; case HandshakeType.server_hello_done: switch (connection_state) { case CS_SERVER_HELLO_RECEIVED: // There was no server certificate message; check it's OK this.keyExchange.skipServerCertificate(); this.authentication = null; // NB: Fall through to next case label case CS_SERVER_CERTIFICATE_RECEIVED: // There was no server key exchange message; check it's OK this.keyExchange.skipServerKeyExchange(); // NB: Fall through to next case label case CS_SERVER_KEY_EXCHANGE_RECEIVED: case CS_CERTIFICATE_REQUEST_RECEIVED: assertEmpty(is); connection_state = CS_SERVER_HELLO_DONE_RECEIVED; TlsCredentials clientCreds = null; if (certificateRequest == null) { this.keyExchange.skipClientCredentials(); } else { clientCreds = this.authentication.getClientCredentials(certificateRequest); if (clientCreds == null) { this.keyExchange.skipClientCredentials(); boolean isTls = tlsClientContext.getServerVersion().getFullVersion() >= ProtocolVersion.TLSv10.getFullVersion(); if (isTls) { sendClientCertificate(Certificate.EMPTY_CHAIN); } else { sendAlert(AlertLevel.warning, AlertDescription.no_certificate); } } else { this.keyExchange.processClientCredentials(clientCreds); sendClientCertificate(clientCreds.getCertificate()); } } /* * Send the client key exchange message, depending on the key * exchange we are using in our CipherSuite. */ sendClientKeyExchange(); connection_state = CS_CLIENT_KEY_EXCHANGE_SEND; /* * Calculate the master_secret */ byte[] pms = this.keyExchange.generatePremasterSecret(); securityParameters.masterSecret = TlsUtils.calculateMasterSecret(this.tlsClientContext, pms); // TODO Is there a way to ensure the data is really overwritten? /* * RFC 2246 8.1. The pre_master_secret should be deleted from * memory once the master_secret has been computed. */ Arrays.fill(pms, (byte) 0); if (clientCreds != null && clientCreds instanceof TlsSignerCredentials) { TlsSignerCredentials signerCreds = (TlsSignerCredentials) clientCreds; byte[] md5andsha1 = rs.getCurrentHash(null); byte[] clientCertificateSignature = signerCreds.generateCertificateSignature(md5andsha1); sendCertificateVerify(clientCertificateSignature); connection_state = CS_CERTIFICATE_VERIFY_SEND; } /* * Now, we send change cipher state */ byte[] cmessage = new byte[1]; cmessage[0] = 1; rs.writeMessage(ContentType.change_cipher_spec, cmessage, 0, cmessage.length); connection_state = CS_CLIENT_CHANGE_CIPHER_SPEC_SEND; /* * Initialize our cipher suite */ rs.clientCipherSpecDecided(tlsClient.getCompression(), tlsClient.getCipher()); /* * Send our finished message. */ byte[] clientVerifyData = TlsUtils.calculateVerifyData( tlsClientContext, "client finished", rs.getCurrentHash(TlsUtils.SSL_CLIENT)); ByteArrayOutputStream bos = new ByteArrayOutputStream(); TlsUtils.writeUint8(HandshakeType.finished, bos); TlsUtils.writeOpaque24(clientVerifyData, bos); byte[] message = bos.toByteArray(); rs.writeMessage(ContentType.handshake, message, 0, message.length); this.connection_state = CS_CLIENT_FINISHED_SEND; break; default: this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); } break; case HandshakeType.server_key_exchange: { switch (connection_state) { case CS_SERVER_HELLO_RECEIVED: // There was no server certificate message; check it's OK this.keyExchange.skipServerCertificate(); this.authentication = null; // NB: Fall through to next case label case CS_SERVER_CERTIFICATE_RECEIVED: this.keyExchange.processServerKeyExchange(is); assertEmpty(is); break; default: this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); } this.connection_state = CS_SERVER_KEY_EXCHANGE_RECEIVED; break; } case HandshakeType.certificate_request: { switch (connection_state) { case CS_SERVER_CERTIFICATE_RECEIVED: // There was no server key exchange message; check it's OK this.keyExchange.skipServerKeyExchange(); // NB: Fall through to next case label case CS_SERVER_KEY_EXCHANGE_RECEIVED: { if (this.authentication == null) { /* * RFC 2246 7.4.4. It is a fatal handshake_failure alert * for an anonymous server to request client identification. */ this.failWithError(AlertLevel.fatal, AlertDescription.handshake_failure); } int numTypes = TlsUtils.readUint8(is); short[] certificateTypes = new short[numTypes]; for (int i = 0; i < numTypes; ++i) { certificateTypes[i] = TlsUtils.readUint8(is); } byte[] authorities = TlsUtils.readOpaque16(is); assertEmpty(is); Vector authorityDNs = new Vector(); ByteArrayInputStream bis = new ByteArrayInputStream(authorities); while (bis.available() > 0) { byte[] dnBytes = TlsUtils.readOpaque16(bis); authorityDNs.addElement( X500Name.getInstance(ASN1Primitive.fromByteArray(dnBytes))); } this.certificateRequest = new CertificateRequest(certificateTypes, authorityDNs); this.keyExchange.validateCertificateRequest(this.certificateRequest); break; } default: this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); } this.connection_state = CS_CERTIFICATE_REQUEST_RECEIVED; break; } case HandshakeType.hello_request: /* * RFC 2246 7.4.1.1 Hello request This message will be ignored by the * client if the client is currently negotiating a session. This message * may be ignored by the client if it does not wish to renegotiate a * session, or the client may, if it wishes, respond with a * no_renegotiation alert. */ if (connection_state == CS_DONE) { // Renegotiation not supported yet sendAlert(AlertLevel.warning, AlertDescription.no_renegotiation); } break; case HandshakeType.client_key_exchange: case HandshakeType.certificate_verify: case HandshakeType.client_hello: default: // We do not support this! this.failWithError(AlertLevel.fatal, AlertDescription.unexpected_message); break; } }