Home | History | Annotate | Download | only in ssl
      1 /*
      2  * Copyright (C) 2010 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 package libcore.javax.net.ssl;
     18 
     19 import java.io.IOException;
     20 import java.nio.ByteBuffer;
     21 import javax.net.ssl.SSLEngine;
     22 import javax.net.ssl.SSLEngineResult;
     23 import javax.net.ssl.SSLEngineResult.HandshakeStatus;
     24 import javax.net.ssl.SSLSession;
     25 import junit.framework.Assert;
     26 
     27 /**
     28  * TestSSLEnginePair is a convenience class for other tests that want
     29  * a pair of connected and handshaked client and server SSLEngines for
     30  * testing.
     31  */
     32 public final class TestSSLEnginePair extends Assert {
     33     public final TestSSLContext c;
     34     public final SSLEngine server;
     35     public final SSLEngine client;
     36 
     37     private TestSSLEnginePair(TestSSLContext c,
     38                               SSLEngine server,
     39                               SSLEngine client) {
     40         this.c = c;
     41         this.server = server;
     42         this.client = client;
     43     }
     44 
     45     public static TestSSLEnginePair create(Hooks hooks) throws IOException {
     46         return create(TestSSLContext.create(), hooks);
     47     }
     48 
     49     public static TestSSLEnginePair create(TestSSLContext c, Hooks hooks) throws IOException {
     50         SSLEngine[] engines = connect(c, hooks);
     51         return new TestSSLEnginePair(c, engines[0], engines[1]);
     52     }
     53 
     54     /**
     55      * Create a new connected server/client engine pair within a
     56      * existing SSLContext. Optionally specify clientCipherSuites to
     57      * allow forcing new SSLSession to test SSLSessionContext
     58      * caching. Optionally specify serverCipherSuites for testing
     59      * cipher suite negotiation.
     60      */
     61     public static SSLEngine[] connect(final TestSSLContext c,
     62                                       Hooks hooks) throws IOException {
     63         if (hooks == null) {
     64             hooks = new Hooks();
     65         }
     66 
     67         SSLSession session = c.clientContext.createSSLEngine().getSession();
     68 
     69         int packetBufferSize = session.getPacketBufferSize();
     70         ByteBuffer clientToServer = ByteBuffer.allocate(packetBufferSize);
     71         ByteBuffer serverToClient = ByteBuffer.allocate(packetBufferSize);
     72 
     73         int applicationBufferSize = session.getApplicationBufferSize();
     74         ByteBuffer scratch = ByteBuffer.allocate(applicationBufferSize);
     75 
     76         SSLEngine client = c.clientContext.createSSLEngine();
     77         SSLEngine server = c.serverContext.createSSLEngine();
     78         client.setUseClientMode(true);
     79         server.setUseClientMode(false);
     80         hooks.beforeBeginHandshake(client, server);
     81         client.beginHandshake();
     82         server.beginHandshake();
     83 
     84         while (true) {
     85             boolean clientDone = client.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
     86             boolean serverDone = server.getHandshakeStatus() == HandshakeStatus.NOT_HANDSHAKING;
     87             if (clientDone && serverDone) {
     88                 break;
     89             }
     90 
     91             boolean progress = false;
     92             if (!clientDone) {
     93                 progress |= handshakeCompleted(client,
     94                                                clientToServer,
     95                                                serverToClient,
     96                                                scratch);
     97             }
     98             if (!serverDone) {
     99                 progress |= handshakeCompleted(server,
    100                                                serverToClient,
    101                                                clientToServer,
    102                                                scratch);
    103             }
    104             if (!progress) {
    105                 // let caller detect the problem, but don't just hang here
    106                 break;
    107             }
    108         }
    109 
    110         return new SSLEngine[] { server, client };
    111     }
    112 
    113     public static class Hooks {
    114         void beforeBeginHandshake(SSLEngine client, SSLEngine server) {}
    115     }
    116 
    117     private static final ByteBuffer EMPTY_BYTE_BUFFER = ByteBuffer.allocate(0);
    118 
    119     private static boolean handshakeCompleted(SSLEngine engine,
    120                                               ByteBuffer output,
    121                                               ByteBuffer input,
    122                                               ByteBuffer scratch) throws IOException {
    123         try {
    124             // make the other side's output into our input
    125             input.flip();
    126 
    127             HandshakeStatus status = engine.getHandshakeStatus();
    128             switch (status) {
    129 
    130                 case NEED_TASK:
    131                     boolean progress = false;
    132                     while (true) {
    133                         Runnable runnable = engine.getDelegatedTask();
    134                         if (runnable == null) {
    135                             return progress;
    136                         }
    137                         runnable.run();
    138                         progress = true;
    139                     }
    140 
    141                 case NEED_UNWRAP:
    142                     // avoid underflow
    143                     if (input.remaining() == 0) {
    144                         return false;
    145                     }
    146                     SSLEngineResult unwrapResult = engine.unwrap(input, scratch);
    147                     assertEquals(SSLEngineResult.Status.OK, unwrapResult.getStatus());
    148                     assertEquals(0, scratch.position());
    149                     return true;
    150 
    151                 case NEED_WRAP:
    152                     // avoid possible overflow
    153                     if (output.remaining() != output.capacity()) {
    154                         return false;
    155                     }
    156                     SSLEngineResult wrapResult = engine.wrap(EMPTY_BYTE_BUFFER, output);
    157                     assertEquals(SSLEngineResult.Status.OK, wrapResult.getStatus());
    158                     return true;
    159 
    160                 case NOT_HANDSHAKING:
    161                     // should have been checked by caller before calling
    162                 case FINISHED:
    163                     // only returned by wrap/unrap status, not getHandshakeStatus
    164                     throw new IllegalStateException("Unexpected HandshakeStatus = " + status);
    165                 default:
    166                     throw new IllegalStateException("Unknown HandshakeStatus = " + status);
    167             }
    168         } finally {
    169             // shift consumed input, restore to output mode
    170             input.compact();
    171         }
    172     }
    173 }
    174