1 /* 2 * Copyright 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 /* 18 * Copyright 2017 The Netty Project 19 * 20 * The Netty Project licenses this file to you under the Apache License, 21 * version 2.0 (the "License"); you may not use this file except in compliance 22 * with the License. You may obtain a copy of the License at: 23 * 24 * http://www.apache.org/licenses/LICENSE-2.0 25 * 26 * Unless required by applicable law or agreed to in writing, software 27 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 28 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 29 * License for the specific language governing permissions and limitations 30 * under the License. 31 */ 32 33 package org.conscrypt; 34 35 import java.nio.ByteBuffer; 36 import javax.net.ssl.SSLEngine; 37 import javax.net.ssl.SSLEngineResult; 38 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 39 import javax.net.ssl.SSLException; 40 41 /** 42 * Benchmark comparing handshake performance of various engine implementations to conscrypt. 43 */ 44 public final class EngineHandshakeBenchmark { 45 /** 46 * Provider for the benchmark configuration 47 */ 48 interface Config { bufferType()49 BufferType bufferType(); engineFactory()50 EngineFactory engineFactory(); cipher()51 String cipher(); useAlpn()52 boolean useAlpn(); protocol()53 BenchmarkProtocol protocol(); rttMillis()54 int rttMillis(); 55 } 56 57 private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocateDirect(0); 58 59 private final EngineFactory engineFactory; 60 private final String cipher; 61 private final boolean useAlpn; 62 private final String[] protocols; 63 private final int rttMillis; 64 65 private final ByteBuffer clientApplicationBuffer; 66 private final ByteBuffer clientPacketBuffer; 67 private final ByteBuffer serverApplicationBuffer; 68 private final ByteBuffer serverPacketBuffer; 69 EngineHandshakeBenchmark(Config config)70 EngineHandshakeBenchmark(Config config) throws Exception { 71 engineFactory = config.engineFactory(); 72 cipher = config.cipher(); 73 useAlpn = config.useAlpn(); 74 protocols = config.protocol().getProtocols(); 75 rttMillis = config.rttMillis(); 76 BufferType bufferType = config.bufferType(); 77 78 SSLEngine clientEngine = engineFactory.newClientEngine(cipher, useAlpn); 79 SSLEngine serverEngine = engineFactory.newServerEngine(cipher, useAlpn); 80 81 // Create the application and packet buffers for both endpoints. 82 clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine); 83 serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine); 84 clientPacketBuffer = bufferType.newPacketBuffer(clientEngine); 85 serverPacketBuffer = bufferType.newPacketBuffer(serverEngine); 86 87 engineFactory.dispose(clientEngine); 88 engineFactory.dispose(serverEngine); 89 } 90 handshake()91 void handshake() throws SSLException { 92 SSLEngine client = engineFactory.newClientEngine(cipher, useAlpn); 93 SSLEngine server = engineFactory.newServerEngine(cipher, useAlpn); 94 clientApplicationBuffer.clear(); 95 clientPacketBuffer.clear(); 96 serverApplicationBuffer.clear(); 97 serverPacketBuffer.clear(); 98 99 client.setEnabledProtocols(protocols); 100 server.setEnabledProtocols(protocols); 101 102 client.beginHandshake(); 103 server.beginHandshake(); 104 105 doHandshake(client, server); 106 107 engineFactory.dispose(client); 108 engineFactory.dispose(server); 109 } 110 doHandshake(SSLEngine client, SSLEngine server)111 private void doHandshake(SSLEngine client, SSLEngine server) throws SSLException { 112 while (true) { 113 // Send as many client-to-server messages as possible 114 doHalfHandshake(client, server, clientPacketBuffer, serverApplicationBuffer); 115 116 if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING 117 && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) { 118 return; 119 } 120 121 // Do the same with server-to-client messages 122 doHalfHandshake(server, client, serverPacketBuffer, clientApplicationBuffer); 123 124 if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING 125 && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) { 126 return; 127 } 128 } 129 } 130 doHalfHandshake(SSLEngine sender, SSLEngine receiver, ByteBuffer senderPacketBuffer, ByteBuffer receiverApplicationBuffer)131 private void doHalfHandshake(SSLEngine sender, SSLEngine receiver, 132 ByteBuffer senderPacketBuffer, ByteBuffer receiverApplicationBuffer) 133 throws SSLException { 134 SSLEngineResult senderResult; 135 SSLEngineResult receiverResult; 136 137 do { 138 senderResult = sender.wrap(EMPTY_BUFFER, senderPacketBuffer); 139 runDelegatedTasks(senderResult, sender); 140 senderPacketBuffer.flip(); 141 receiverResult = receiver.unwrap(senderPacketBuffer, receiverApplicationBuffer); 142 runDelegatedTasks(receiverResult, receiver); 143 senderPacketBuffer.compact(); 144 } while (senderResult.getHandshakeStatus() == HandshakeStatus.NEED_WRAP); 145 146 if (rttMillis > 0) { 147 try { 148 Thread.sleep(rttMillis / 2); 149 } catch (InterruptedException e) { 150 throw new RuntimeException(e); 151 } 152 } 153 } 154 runDelegatedTasks(SSLEngineResult result, SSLEngine engine)155 private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) { 156 if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { 157 for (;;) { 158 Runnable task = engine.getDelegatedTask(); 159 if (task == null) { 160 break; 161 } 162 task.run(); 163 } 164 } 165 } 166 } 167