Home | History | Annotate | Download | only in cts
      1 /*
      2  * Copyright (C) 2018 The AndroCid 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 package android.security.cts;
     17 
     18 import android.content.res.Resources;
     19 import android.test.AndroidTestCase;
     20 import android.platform.test.annotations.SecurityTest;
     21 import android.test.InstrumentationTestCase;
     22 import android.content.Context;
     23 import android.os.AsyncTask;
     24 import android.os.Bundle;
     25 import android.util.Log;
     26 
     27 import java.io.InputStream;
     28 import java.io.IOException;
     29 import java.io.ByteArrayInputStream;
     30 import java.net.InetSocketAddress;
     31 import java.nio.ByteBuffer;
     32 import java.nio.channels.ClosedChannelException;
     33 import java.nio.channels.SelectionKey;
     34 import java.nio.channels.Selector;
     35 import java.nio.channels.ServerSocketChannel;
     36 import java.nio.channels.SocketChannel;
     37 import java.nio.channels.spi.SelectorProvider;
     38 import java.security.KeyManagementException;
     39 import java.security.KeyStore;
     40 import java.security.KeyStoreException;
     41 import java.security.NoSuchAlgorithmException;
     42 import java.security.SecureRandom;
     43 import java.security.UnrecoverableKeyException;
     44 import java.security.cert.CertificateException;
     45 import java.util.Formatter;
     46 import java.util.Iterator;
     47 import java.util.concurrent.ExecutorService;
     48 import java.util.concurrent.Executors;
     49 import java.util.regex.Pattern;
     50 
     51 import javax.net.ssl.KeyManagerFactory;
     52 import javax.net.ssl.SSLContext;
     53 import javax.net.ssl.SSLEngine;
     54 import javax.net.ssl.SSLEngineResult;
     55 import javax.net.ssl.SSLException;
     56 import javax.net.ssl.SSLSession;
     57 import javax.net.ssl.TrustManagerFactory;
     58 
     59 public class SSLConscryptPlainTextExposureTest extends InstrumentationTestCase {
     60 
     61   private TestSSLServer mTestSSLServer;
     62   private TestSSLConnection mTestSSLClient;
     63   public static Context context;
     64   public static String output = "";
     65   private final String pattern = ".*PLAIN TEXT EXPOSED.*";
     66 
     67   public void test_android_CVE_2017_13309() {
     68 
     69     context = getInstrumentation().getContext();
     70     mTestSSLServer = new TestSSLServer();
     71 
     72     try {
     73       Thread.sleep(1000);
     74     } catch (InterruptedException e) {
     75       e.printStackTrace();
     76     }
     77     mTestSSLClient = new TestSSLConnection();
     78     mTestSSLClient.StartConnect();
     79     assertFalse("Pattern found",
     80         Pattern.compile(pattern,
     81           Pattern.DOTALL).matcher(output).matches());
     82   }
     83 
     84   public static InputStream getResource(final int resource){
     85     return SSLConscryptPlainTextExposureTest.context.getResources().openRawResource(resource);
     86   }
     87 
     88   public static void setOutput(String data){
     89     SSLConscryptPlainTextExposureTest.output = data;
     90   }
     91 }
     92 
     93 
     94 class TestSSLConnection {
     95 
     96   public SSLContext sslc;
     97 
     98   public SocketChannel socketChannel;
     99   public SSLEngine clientEngine;
    100   public String remoteAddress = "127.0.0.1";
    101   public int port = 9000;
    102   public ByteBuffer[] dataOutAppBuffers = new ByteBuffer[3];
    103   public ByteBuffer dataOutNetBuffer;
    104   public ByteBuffer hsInAppBuffer, hsInNetBuffer, hsOutAppBuffer, hsOutNetBuffer;
    105   public boolean isHandshaked = false;
    106   public ExecutorService executor = Executors.newSingleThreadExecutor();
    107   public InputStream clientKey = null;
    108   public InputStream trustedCert = null;
    109 
    110   public void StartConnect() {
    111     KeyStore ks = null;
    112 
    113     clientKey = SSLConscryptPlainTextExposureTest.getResource(R.raw.cve_2017_13309_client);
    114     trustedCert = SSLConscryptPlainTextExposureTest.getResource(R.raw.cve_2017_13309_trustedcert);
    115 
    116     try {
    117       ks = KeyStore.getInstance(KeyStore.getDefaultType());
    118     } catch (KeyStoreException e) {
    119       e.printStackTrace();
    120     }
    121     KeyStore ts = null;
    122     try {
    123       ts = KeyStore.getInstance(KeyStore.getDefaultType());
    124     } catch (KeyStoreException e) {
    125       e.printStackTrace();
    126     }
    127 
    128     try {
    129       ks.load(clientKey, "pocclient".toCharArray());
    130     } catch (Exception e) {
    131       e.printStackTrace();
    132     }
    133 
    134     try {
    135       ts.load(trustedCert, "trusted".toCharArray());
    136     } catch (Exception e) {
    137       e.printStackTrace();
    138     }
    139 
    140     KeyManagerFactory kmf = null;
    141     try {
    142       kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    143     } catch (NoSuchAlgorithmException e) {
    144       e.printStackTrace();
    145     }
    146 
    147     try {
    148       kmf.init(ks, "keypass".toCharArray());
    149     } catch (Exception e) {
    150       e.printStackTrace();
    151     }
    152 
    153     TrustManagerFactory tmf = null;
    154     try {
    155       tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    156     } catch (NoSuchAlgorithmException e) {
    157       e.printStackTrace();
    158     }
    159     try {
    160       tmf.init(ts);
    161     } catch (KeyStoreException e) {
    162       e.printStackTrace();
    163     }
    164 
    165     SSLContext sslCtx = null;
    166     try {
    167       sslCtx = SSLContext.getInstance("TLSv1.2");
    168     } catch (NoSuchAlgorithmException e) {
    169       e.printStackTrace();
    170     }
    171 
    172     try {
    173       sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
    174     } catch (KeyManagementException e) {
    175       e.printStackTrace();
    176     }
    177 
    178     sslc = sslCtx;
    179 
    180     clientEngine = sslc.createSSLEngine(remoteAddress, port);
    181     clientEngine.setUseClientMode(true);
    182     SSLSession session = clientEngine.getSession();
    183 
    184     hsOutAppBuffer = ByteBuffer.allocate(4096);
    185     hsOutNetBuffer = ByteBuffer.allocate(session.getPacketBufferSize());
    186     hsInAppBuffer = ByteBuffer.allocate(4096);
    187     hsInNetBuffer = ByteBuffer.allocate(session.getPacketBufferSize());
    188     dataOutNetBuffer = ByteBuffer.allocate(session.getPacketBufferSize());
    189 
    190     try {
    191       socketChannel = SocketChannel.open();
    192     } catch (IOException e) {
    193       e.printStackTrace();
    194     }
    195     try {
    196       socketChannel.configureBlocking(false);
    197     } catch (IOException e) {
    198       e.printStackTrace();
    199     }
    200     try {
    201       socketChannel.connect(new InetSocketAddress(remoteAddress, port));
    202     } catch (IOException e) {
    203       e.printStackTrace();
    204     }
    205 
    206     try {
    207       while(!socketChannel.finishConnect()) {
    208       }
    209     } catch (IOException e) {
    210       e.printStackTrace();
    211     }
    212 
    213     try {
    214       clientEngine.beginHandshake();
    215     } catch (SSLException e) {
    216       e.printStackTrace();
    217     }
    218 
    219     try {
    220       isHandshaked = doHandshake(socketChannel, clientEngine);
    221     } catch (IOException e) {
    222       e.printStackTrace();
    223     }
    224 
    225     if(isHandshaked) {
    226       dataOutAppBuffers[0] = ByteBuffer.wrap("PLAIN TEXT EXPOSED".getBytes());
    227       dataOutAppBuffers[1] = ByteBuffer.wrap("PLAIN TEXT EXPOSED".getBytes());
    228       dataOutAppBuffers[2] = ByteBuffer.wrap("PLAIN TEXT EXPOSED".getBytes());
    229 
    230       while(dataOutAppBuffers[0].hasRemaining() || dataOutAppBuffers[1].hasRemaining() || dataOutAppBuffers[2].hasRemaining()) {
    231         dataOutNetBuffer.clear();
    232         SSLEngineResult result = null;
    233         try {
    234           result = clientEngine.wrap(dataOutAppBuffers, 0, 3, dataOutNetBuffer);
    235         } catch (SSLException e) {
    236           e.printStackTrace();
    237         }
    238         switch(result.getStatus()) {
    239           case OK:
    240             dataOutNetBuffer.flip();
    241             String outbuff = new String("");
    242             Formatter formatter = new Formatter();
    243             for(int i = 0; i < dataOutNetBuffer.limit(); i++) {
    244               outbuff += formatter.format("%02x ", dataOutNetBuffer.get(i)).toString();
    245             }
    246             String output = new String(dataOutNetBuffer.array());
    247             SSLConscryptPlainTextExposureTest.setOutput(output);
    248             break;
    249           case BUFFER_OVERFLOW:
    250             dataOutNetBuffer = enlargePacketBuffer(clientEngine, dataOutNetBuffer);
    251             break;
    252           case BUFFER_UNDERFLOW:
    253             try {
    254               throw new SSLException("Buffer underflow in sending data");
    255             } catch (SSLException e) {
    256               e.printStackTrace();
    257             }
    258           case CLOSED:
    259             try {
    260               closeConnection(socketChannel, clientEngine);
    261             } catch (IOException e) {
    262               e.printStackTrace();
    263             }
    264             return;
    265           default:
    266             throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
    267         }
    268       }
    269     }
    270 
    271     try {
    272       clientKey.close();
    273     } catch (IOException e){
    274       e.printStackTrace();
    275     }
    276 
    277     try{
    278       trustedCert.close();
    279     } catch (IOException e){
    280       e.printStackTrace();
    281     }
    282 
    283     try {
    284       shutdown();
    285     } catch (IOException e) {
    286       e.printStackTrace();
    287     }
    288   }
    289 
    290   public void shutdown() throws IOException {
    291     closeConnection(socketChannel, clientEngine);
    292     executor.shutdown();
    293   }
    294 
    295   public void closeConnection(SocketChannel socketChannel, SSLEngine engine) throws IOException {
    296     engine.closeOutbound();
    297     doHandshake(socketChannel, engine);
    298     socketChannel.close();
    299   }
    300 
    301   public boolean doHandshake(SocketChannel socketChannel, SSLEngine engine) throws IOException {
    302     SSLEngineResult result;
    303     SSLEngineResult.HandshakeStatus handshakeStatus;
    304     int appBufferSize = engine.getSession().getApplicationBufferSize();
    305 
    306     ByteBuffer srcAppData = ByteBuffer.allocate(appBufferSize);
    307     ByteBuffer dstAppData = ByteBuffer.allocate(appBufferSize);
    308 
    309     srcAppData.clear();
    310     dstAppData.clear();
    311 
    312     handshakeStatus = engine.getHandshakeStatus();
    313     while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED
    314         && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
    315       switch(handshakeStatus) {
    316         case NEED_UNWRAP:
    317           if(socketChannel.read(hsInNetBuffer) < 0) {
    318             if(engine.isInboundDone() && engine.isOutboundDone()) {
    319               return false;
    320             }
    321             try {
    322               engine.closeInbound();
    323             } catch (SSLException e) {
    324               Log.e("poc-test","Forced to close inbound. No proper SSL/TLS close notification message from peer");
    325             }
    326             engine.closeOutbound();
    327             handshakeStatus = engine.getHandshakeStatus();
    328             break;
    329           }
    330           hsInNetBuffer.flip();
    331           try {
    332             result = engine.unwrap(hsInNetBuffer, hsInAppBuffer);
    333             hsInNetBuffer.compact();
    334             handshakeStatus = result.getHandshakeStatus();
    335           } catch (SSLException sslException) {
    336             engine.closeOutbound();
    337             handshakeStatus = engine.getHandshakeStatus();
    338             break;
    339           }
    340           switch(result.getStatus()) {
    341             case OK:
    342               break;
    343             case BUFFER_OVERFLOW:
    344               hsInAppBuffer = enlargeApplicationBuffer(engine, hsInAppBuffer);
    345               break;
    346             case BUFFER_UNDERFLOW:
    347               hsInNetBuffer = handleBufferUnderflow(engine, hsInNetBuffer);
    348               break;
    349             case CLOSED:
    350               if (engine.isOutboundDone()) {
    351                 return false;
    352               } else {
    353                 engine.closeOutbound();
    354                 handshakeStatus = engine.getHandshakeStatus();
    355                 break;
    356               }
    357             default:
    358               throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
    359           }
    360         case NEED_WRAP:
    361           hsOutNetBuffer.clear();
    362           try {
    363             result = engine.wrap(hsOutAppBuffer, hsOutNetBuffer);
    364             handshakeStatus = result.getHandshakeStatus();
    365           } catch (SSLException sslException) {
    366             engine.closeOutbound();
    367             handshakeStatus = engine.getHandshakeStatus();
    368             break;
    369           }
    370           switch(result.getStatus()) {
    371             case OK:
    372               hsOutNetBuffer.flip();
    373               while(hsOutNetBuffer.hasRemaining()) {
    374                 socketChannel.write(hsOutNetBuffer);
    375               }
    376               break;
    377             case BUFFER_OVERFLOW:
    378               hsOutNetBuffer = enlargePacketBuffer(engine, hsOutNetBuffer);
    379               break;
    380             case BUFFER_UNDERFLOW:
    381               throw new SSLException("Buffer underflow in handshake and wrap");
    382             case CLOSED:
    383               try {
    384                 hsOutNetBuffer.flip();
    385                 while(hsOutNetBuffer.hasRemaining()) {
    386                   socketChannel.write(hsOutNetBuffer);
    387                 }
    388                 hsInNetBuffer.clear();
    389               } catch (Exception e) {
    390                 handshakeStatus = engine.getHandshakeStatus();
    391               }
    392               break;
    393             default:
    394               throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
    395           }
    396           break;
    397         case NEED_TASK:
    398           Runnable task;
    399           while((task = engine.getDelegatedTask()) != null) {
    400             executor.execute(task);
    401           }
    402           handshakeStatus = engine.getHandshakeStatus();
    403           break;
    404         case FINISHED:
    405           break;
    406         case NOT_HANDSHAKING:
    407           break;
    408         default:
    409           throw new IllegalStateException("Invalid SSL status: " + handshakeStatus);
    410       }
    411         }
    412     return true;
    413   }
    414 
    415   public ByteBuffer enlargePacketBuffer(SSLEngine engine, ByteBuffer buffer) {
    416     return enlargeBuffer(buffer, engine.getSession().getPacketBufferSize());
    417   }
    418 
    419   public ByteBuffer enlargeApplicationBuffer(SSLEngine engine, ByteBuffer buffer) {
    420     return enlargeBuffer(buffer, engine.getSession().getApplicationBufferSize());
    421   }
    422 
    423   public ByteBuffer enlargeBuffer(ByteBuffer buffer, int bufferSize) {
    424     if(bufferSize > buffer.capacity()) {
    425       buffer = ByteBuffer.allocate(bufferSize);
    426     }
    427     else {
    428       buffer = ByteBuffer.allocate(buffer.capacity() * 2);
    429     }
    430     return buffer;
    431   }
    432   public ByteBuffer handleBufferUnderflow(SSLEngine engine, ByteBuffer buffer) {
    433     if(engine.getSession().getPacketBufferSize() < buffer.limit()) {
    434       return buffer;
    435     }
    436     else {
    437       ByteBuffer replaceBuffer = enlargePacketBuffer(engine, buffer);
    438       buffer.flip();
    439       replaceBuffer.put(buffer);
    440       return replaceBuffer;
    441     }
    442   }
    443 }
    444 
    445 class TestSSLServer {
    446 
    447   public ServerRunnable serverRunnable;
    448   Thread server;
    449 
    450   public TestSSLServer() {
    451     serverRunnable = new ServerRunnable();
    452     server = new Thread(serverRunnable);
    453     server.start();
    454 
    455     try{
    456       Thread.sleep(1000);
    457     }catch(InterruptedException e){
    458       e.printStackTrace();
    459     }
    460   }
    461 
    462   protected void onCancelled(String result) {
    463     serverRunnable.stop();
    464   }
    465 }
    466 
    467 class ServerRunnable implements Runnable {
    468 
    469   SSLServer server;
    470   InputStream serverKey = null;
    471   InputStream trustedCert = null;
    472 
    473   public void run() {
    474     try {
    475       server = new SSLServer();
    476       server.runServer();
    477     }
    478     catch (Exception e) {
    479       e.printStackTrace();
    480     }
    481   }
    482 
    483   public void stop() {
    484     server.stopServer();
    485   }
    486 }
    487 
    488 class SSLServer {
    489   public SSLContext serverContext;
    490 
    491   public ByteBuffer hsInAppBuffer, hsInNetBuffer, hsOutAppBuffer, hsOutNetBuffer;
    492   public ByteBuffer dataInAppBuffer, dataInNetBuffer;
    493 
    494   final String hostAddress = "127.0.0.1";
    495   public int port = 9000;
    496   public boolean bActive = false;
    497 
    498   public Selector selector;
    499   InputStream serverKey = null;
    500   InputStream trustedCert = null;
    501   public ExecutorService executor = Executors.newSingleThreadExecutor();
    502 
    503   public void stopServer() {
    504     bActive = false;
    505     executor.shutdown();
    506     selector.wakeup();
    507   }
    508 
    509   public void runServer() {
    510     KeyStore ks = null;
    511 
    512     serverKey = SSLConscryptPlainTextExposureTest.getResource(R.raw.cve_2017_13309_server);
    513     trustedCert = SSLConscryptPlainTextExposureTest.getResource(R.raw.cve_2017_13309_trustedcert);
    514 
    515     try {
    516       ks = KeyStore.getInstance(KeyStore.getDefaultType());
    517     } catch (KeyStoreException e) {
    518       e.printStackTrace();
    519     }
    520     KeyStore ts = null;
    521     try {
    522       ts = KeyStore.getInstance(KeyStore.getDefaultType());
    523     } catch (KeyStoreException e) {
    524       e.printStackTrace();
    525     }
    526 
    527     try {
    528       ks.load(serverKey, "pocserver".toCharArray());
    529     } catch (Exception e) {
    530       e.printStackTrace();
    531     }
    532     try {
    533       ts.load(trustedCert, "trusted".toCharArray());
    534     } catch (Exception e) {
    535       e.printStackTrace();
    536     }
    537 
    538     KeyManagerFactory kmf = null;
    539     try {
    540       kmf = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());
    541     } catch (NoSuchAlgorithmException e) {
    542       e.printStackTrace();
    543     }
    544     try {
    545       kmf.init(ks, "keypass".toCharArray());
    546     } catch (Exception e) {
    547       e.printStackTrace();
    548     }
    549 
    550     TrustManagerFactory tmf = null;
    551     try {
    552       tmf = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());
    553     } catch (NoSuchAlgorithmException e) {
    554       e.printStackTrace();
    555     }
    556     try {
    557       tmf.init(ts);
    558     } catch (KeyStoreException e) {
    559       e.printStackTrace();
    560     }
    561 
    562     SSLContext sslCtx = null;
    563     try {
    564       sslCtx = SSLContext.getInstance("TLSv1.2");
    565     } catch (NoSuchAlgorithmException e) {
    566       e.printStackTrace();
    567     }
    568 
    569     try {
    570       sslCtx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), new SecureRandom());
    571     } catch (KeyManagementException e) {
    572       e.printStackTrace();
    573     }
    574 
    575     serverContext = sslCtx;
    576 
    577     SSLSession dummySession = serverContext.createSSLEngine().getSession();
    578 
    579     hsInAppBuffer = ByteBuffer.allocate(4096);
    580     hsInNetBuffer = ByteBuffer.allocate(dummySession.getPacketBufferSize());
    581     hsOutAppBuffer = ByteBuffer.allocate(4096);
    582     hsOutNetBuffer = ByteBuffer.allocate(dummySession.getPacketBufferSize());
    583     dataInAppBuffer = ByteBuffer.allocate(4096);
    584     dataInNetBuffer = ByteBuffer.allocate(dummySession.getPacketBufferSize());
    585     dummySession.invalidate();
    586 
    587     try {
    588       selector = SelectorProvider.provider().openSelector();
    589     } catch (IOException e) {
    590       e.printStackTrace();
    591     }
    592     ServerSocketChannel serverSocketChannel = null;
    593     try {
    594       serverSocketChannel = ServerSocketChannel.open();
    595     } catch (IOException e) {
    596       e.printStackTrace();
    597     }
    598     try {
    599       serverSocketChannel.configureBlocking(false);
    600     } catch (IOException e) {
    601       e.printStackTrace();
    602     }
    603     try {
    604       serverSocketChannel.socket().bind(new InetSocketAddress(hostAddress, port));
    605     } catch (IOException e) {
    606       e.printStackTrace();
    607     }
    608     try {
    609       serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
    610     } catch (ClosedChannelException e) {
    611       e.printStackTrace();
    612     }
    613 
    614     bActive = true;
    615 
    616     while(bActive) {
    617       try {
    618         selector.select();
    619       } catch (IOException e) {
    620         e.printStackTrace();
    621       }
    622       Iterator<SelectionKey> selectedKeys = selector.selectedKeys().iterator();
    623       while (selectedKeys.hasNext()) {
    624         SelectionKey key = selectedKeys.next();
    625         selectedKeys.remove();
    626         if (!key.isValid()) {
    627           continue;
    628         }
    629         if (key.isAcceptable()) {
    630           try {
    631             accept(key);
    632           } catch (Exception e) {
    633             e.printStackTrace();
    634           }
    635         } else if (key.isReadable()) {
    636           try {
    637             read((SocketChannel) key.channel(), (SSLEngine) key.attachment());
    638           } catch (IOException e) {
    639             e.printStackTrace();
    640           }
    641         }
    642       }
    643     }
    644   }
    645 
    646   public void accept(SelectionKey key) throws Exception{
    647     SocketChannel socketChannel = ((ServerSocketChannel)key.channel()).accept();
    648     socketChannel.configureBlocking(false);
    649 
    650     SSLEngine engine = serverContext.createSSLEngine();
    651     engine.setUseClientMode(false);
    652     engine.beginHandshake();
    653 
    654     socketChannel.register(selector, SelectionKey.OP_READ, engine);
    655 
    656     if(doHandshake(socketChannel, engine)) {
    657       socketChannel.register(selector, SelectionKey.OP_READ, engine);
    658     }
    659     else {
    660       socketChannel.close();
    661     }
    662   }
    663 
    664   public boolean doHandshake(SocketChannel socketChannel, SSLEngine engine) throws IOException {
    665     SSLEngineResult result = null;
    666     SSLEngineResult.HandshakeStatus handshakeStatus;
    667     int appBufferSize = engine.getSession().getApplicationBufferSize();
    668 
    669     ByteBuffer srcAppData = ByteBuffer.allocate(appBufferSize);
    670     ByteBuffer dstAppData = ByteBuffer.allocate(appBufferSize);
    671 
    672     srcAppData.clear();
    673     dstAppData.clear();
    674 
    675     handshakeStatus = engine.getHandshakeStatus();
    676     while (handshakeStatus != SSLEngineResult.HandshakeStatus.FINISHED
    677         && handshakeStatus != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
    678 
    679       switch(handshakeStatus) {
    680         case NEED_UNWRAP:
    681           if(socketChannel.read(hsInNetBuffer) < 0) {
    682             if(engine.isInboundDone() && engine.isOutboundDone()) {
    683               return false;
    684             }
    685             try {
    686               engine.closeInbound();
    687             } catch (SSLException e) {
    688               Log.e("server-poc-test","Forced to close inbound. No proper SSL/TLS close notification message from peer");
    689             }
    690             engine.closeOutbound();
    691             handshakeStatus = engine.getHandshakeStatus();
    692             break;
    693           }
    694           hsInNetBuffer.flip();
    695           try {
    696             result = engine.unwrap(hsInNetBuffer, hsInAppBuffer);
    697             hsInNetBuffer.compact();
    698             handshakeStatus = result.getHandshakeStatus();
    699           } catch (SSLException sslException) {
    700             engine.closeOutbound();
    701             handshakeStatus = engine.getHandshakeStatus();
    702             break;
    703           }
    704           switch(result.getStatus()) {
    705             case OK:
    706               break;
    707             case BUFFER_OVERFLOW:
    708               hsInAppBuffer = enlargeApplicationBuffer(engine, hsInAppBuffer);
    709               break;
    710             case BUFFER_UNDERFLOW:
    711               hsInNetBuffer = handleBufferUnderflow(engine, hsInNetBuffer);
    712               break;
    713             case CLOSED:
    714               if (engine.isOutboundDone()) {
    715                 return false;
    716               } else {
    717                 engine.closeOutbound();
    718                 handshakeStatus = engine.getHandshakeStatus();
    719                 break;
    720               }
    721             default:
    722               throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
    723           }
    724         case NEED_WRAP:
    725           hsOutNetBuffer.clear();
    726           try {
    727             result = engine.wrap(hsOutAppBuffer, hsOutNetBuffer);
    728             handshakeStatus = result.getHandshakeStatus();
    729           } catch (SSLException sslException) {
    730             engine.closeOutbound();
    731             handshakeStatus = engine.getHandshakeStatus();
    732             break;
    733           }
    734           switch(result.getStatus()) {
    735             case OK:
    736               hsOutNetBuffer.flip();
    737               while(hsOutNetBuffer.hasRemaining()) {
    738                 socketChannel.write(hsOutNetBuffer);
    739               }
    740               break;
    741             case BUFFER_OVERFLOW:
    742               hsOutNetBuffer = enlargePacketBuffer(engine, hsOutNetBuffer);
    743               break;
    744             case BUFFER_UNDERFLOW:
    745               throw new SSLException("Buffer underflow in handshake and wrap");
    746             case CLOSED:
    747               try {
    748                 hsOutNetBuffer.flip();
    749                 while(hsOutNetBuffer.hasRemaining()) {
    750                   socketChannel.write(hsOutNetBuffer);
    751                 }
    752                 hsInNetBuffer.clear();
    753               } catch (Exception e) {
    754                 handshakeStatus = engine.getHandshakeStatus();
    755               }
    756               break;
    757             default:
    758               throw new IllegalStateException("Invalid SSL status: " + result.getStatus());
    759           }
    760           break;
    761         case NEED_TASK:
    762           Runnable task;
    763           while((task = engine.getDelegatedTask()) != null) {
    764             executor.execute(task);
    765           }
    766           handshakeStatus = engine.getHandshakeStatus();
    767           break;
    768         case FINISHED:
    769           break;
    770         case NOT_HANDSHAKING:
    771           break;
    772         default:
    773           throw new IllegalStateException("Invalid SSL status: " + handshakeStatus);
    774       }
    775         }
    776     return true;
    777   }
    778 
    779   public ByteBuffer enlargePacketBuffer(SSLEngine engine, ByteBuffer buffer) {
    780     return enlargeBuffer(buffer, engine.getSession().getPacketBufferSize());
    781   }
    782 
    783   public ByteBuffer enlargeApplicationBuffer(SSLEngine engine, ByteBuffer buffer) {
    784     return enlargeBuffer(buffer, engine.getSession().getApplicationBufferSize());
    785   }
    786 
    787   public ByteBuffer enlargeBuffer(ByteBuffer buffer, int bufferSize) {
    788     if(bufferSize > buffer.capacity()) {
    789       buffer = ByteBuffer.allocate(bufferSize);
    790     }
    791     else {
    792       buffer = ByteBuffer.allocate(buffer.capacity() * 2);
    793     }
    794     return buffer;
    795   }
    796   public ByteBuffer handleBufferUnderflow(SSLEngine engine, ByteBuffer buffer) {
    797     if(engine.getSession().getPacketBufferSize() < buffer.limit()) {
    798       return buffer;
    799     }
    800     else {
    801       ByteBuffer replaceBuffer = enlargePacketBuffer(engine, buffer);
    802       buffer.flip();
    803       replaceBuffer.put(buffer);
    804       return replaceBuffer;
    805     }
    806   }
    807 
    808   public void read(SocketChannel socketChannel, SSLEngine engine) throws IOException {
    809     dataInNetBuffer.clear();
    810     int bytesRead = socketChannel.read(dataInNetBuffer);
    811     if(bytesRead > 0) {
    812       dataInNetBuffer.flip();
    813       while(dataInNetBuffer.hasRemaining()) {
    814         dataInAppBuffer.clear();
    815         SSLEngineResult result = engine.unwrap(dataInNetBuffer, dataInAppBuffer);
    816         switch(result.getStatus()) {
    817           case OK:
    818             dataInAppBuffer.flip();
    819             break;
    820           case BUFFER_OVERFLOW:
    821             dataInAppBuffer = enlargeApplicationBuffer(engine, dataInAppBuffer);
    822             break;
    823           case BUFFER_UNDERFLOW:
    824             dataInNetBuffer = handleBufferUnderflow(engine, dataInNetBuffer);
    825             break;
    826           case CLOSED:
    827             closeConnection(socketChannel, engine);
    828             return;
    829           default:
    830             throw new IllegalStateException("invalid SSL status: " + result.getStatus());
    831         }
    832       }
    833     }
    834     else if(bytesRead < 0) {
    835       handleEndOfStream(socketChannel, engine);
    836     }
    837   }
    838 
    839   public void handleEndOfStream(SocketChannel socketChannel, SSLEngine engine) throws IOException {
    840     try {
    841       engine.closeInbound();
    842     }
    843     catch (Exception e) {
    844       Log.e("server-poc-test", "Close inbound forced");
    845     }
    846     closeConnection(socketChannel, engine);
    847   }
    848 
    849   public void closeConnection(SocketChannel socketChannel, SSLEngine engine) throws IOException {
    850     try{
    851       serverKey.close();
    852     } catch (IOException e){
    853       e.printStackTrace();
    854     }
    855 
    856     try {
    857       trustedCert.close();
    858     } catch (IOException e){
    859       e.printStackTrace();
    860     }
    861 
    862     engine.closeOutbound();
    863     doHandshake(socketChannel, engine);
    864     socketChannel.close();
    865   }
    866 }
    867