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 android.conscrypt; 34 35 import java.nio.ByteBuffer; 36 import java.util.ArrayList; 37 import java.util.Collection; 38 import java.util.List; 39 import javax.net.ssl.SSLEngine; 40 import javax.net.ssl.SSLEngineResult; 41 import javax.net.ssl.SSLEngineResult.HandshakeStatus; 42 import javax.net.ssl.SSLException; 43 44 import junitparams.JUnitParamsRunner; 45 import junitparams.Parameters; 46 47 import android.perftests.utils.BenchmarkState; 48 import android.perftests.utils.PerfStatusReporter; 49 import androidx.test.filters.LargeTest; 50 51 import org.junit.Rule; 52 import org.junit.Test; 53 import org.junit.runner.RunWith; 54 55 /** 56 * Benchmark comparing handshake performance of various engine implementations to conscrypt. 57 */ 58 @RunWith(JUnitParamsRunner.class) 59 @LargeTest 60 public final class EngineHandshakePerfTest { 61 @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 62 63 /** 64 * Provider for the test configuration 65 */ 66 private class Config { 67 BufferType a_bufferType; 68 String c_cipher; 69 int d_rttMillis; Config(BufferType bufferType, String cipher, int rttMillis)70 Config(BufferType bufferType, 71 String cipher, 72 int rttMillis) { 73 a_bufferType = bufferType; 74 c_cipher = cipher; 75 d_rttMillis = rttMillis; 76 } bufferType()77 public BufferType bufferType() { 78 return a_bufferType; 79 } 80 cipher()81 public String cipher() { 82 return c_cipher; 83 } 84 rttMillis()85 public int rttMillis() { 86 return d_rttMillis; 87 } 88 } 89 getParams()90 public Collection getParams() { 91 final List<Object[]> params = new ArrayList<>(); 92 for (BufferType bufferType : BufferType.values()) { 93 params.add(new Object[] {new Config(bufferType, 94 "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", 100)}); 95 } 96 return params; 97 } 98 99 private static final ByteBuffer EMPTY_BUFFER = ByteBuffer.allocateDirect(0); 100 101 private EngineFactory engineFactory = new EngineFactory(); 102 private String cipher; 103 private int rttMillis; 104 105 private ByteBuffer clientApplicationBuffer; 106 private ByteBuffer clientPacketBuffer; 107 private ByteBuffer serverApplicationBuffer; 108 private ByteBuffer serverPacketBuffer; 109 setup(Config config)110 private void setup(Config config) throws Exception { 111 cipher = config.cipher(); 112 rttMillis = config.rttMillis(); 113 BufferType bufferType = config.bufferType(); 114 115 SSLEngine clientEngine = engineFactory.newClientEngine(cipher); 116 SSLEngine serverEngine = engineFactory.newServerEngine(cipher); 117 118 // Create the application and packet buffers for both endpoints. 119 clientApplicationBuffer = bufferType.newApplicationBuffer(clientEngine); 120 serverApplicationBuffer = bufferType.newApplicationBuffer(serverEngine); 121 clientPacketBuffer = bufferType.newPacketBuffer(clientEngine); 122 serverPacketBuffer = bufferType.newPacketBuffer(serverEngine); 123 124 engineFactory.dispose(clientEngine); 125 engineFactory.dispose(serverEngine); 126 } 127 128 @Test 129 @Parameters(method = "getParams") handshake(Config config)130 public void handshake(Config config) throws Exception { 131 setup(config); 132 SSLEngine client = engineFactory.newClientEngine(cipher); 133 SSLEngine server = engineFactory.newServerEngine(cipher); 134 clientApplicationBuffer.clear(); 135 clientPacketBuffer.clear(); 136 serverApplicationBuffer.clear(); 137 serverPacketBuffer.clear(); 138 139 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 140 while (state.keepRunning()) { 141 client.beginHandshake(); 142 server.beginHandshake(); 143 doHandshake(client, server); 144 } 145 146 engineFactory.dispose(client); 147 engineFactory.dispose(server); 148 } 149 doHandshake(SSLEngine client, SSLEngine server)150 private void doHandshake(SSLEngine client, SSLEngine server) throws SSLException { 151 152 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 153 while (state.keepRunning()) { 154 // Send as many client-to-server messages as possible 155 doHalfHandshake(client, server, clientPacketBuffer, serverApplicationBuffer); 156 157 if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING 158 && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) { 159 return; 160 } 161 162 // Do the same with server-to-client messages 163 doHalfHandshake(server, client, serverPacketBuffer, clientApplicationBuffer); 164 165 if (client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING 166 && server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING) { 167 return; 168 } 169 } 170 } 171 doHalfHandshake(SSLEngine sender, SSLEngine receiver, ByteBuffer senderPacketBuffer, ByteBuffer receiverApplicationBuffer)172 private void doHalfHandshake(SSLEngine sender, SSLEngine receiver, 173 ByteBuffer senderPacketBuffer, ByteBuffer receiverApplicationBuffer) 174 throws SSLException { 175 SSLEngineResult senderResult; 176 SSLEngineResult receiverResult; 177 178 do { 179 senderResult = sender.wrap(EMPTY_BUFFER, senderPacketBuffer); 180 runDelegatedTasks(senderResult, sender); 181 senderPacketBuffer.flip(); 182 receiverResult = receiver.unwrap(senderPacketBuffer, receiverApplicationBuffer); 183 runDelegatedTasks(receiverResult, receiver); 184 senderPacketBuffer.compact(); 185 } while (senderResult.getHandshakeStatus() == HandshakeStatus.NEED_WRAP); 186 187 if (rttMillis > 0) { 188 try { 189 Thread.sleep(rttMillis / 2); 190 } catch (InterruptedException e) { 191 throw new RuntimeException(e); 192 } 193 } 194 } 195 runDelegatedTasks(SSLEngineResult result, SSLEngine engine)196 private static void runDelegatedTasks(SSLEngineResult result, SSLEngine engine) { 197 if (result.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_TASK) { 198 for (;;) { 199 Runnable task = engine.getDelegatedTask(); 200 if (task == null) { 201 break; 202 } 203 task.run(); 204 } 205 } 206 } 207 }