blob: cd26804c453fa697745bbe0706f403296a0ad2f8 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import android.net.RoughtimeClient;
import android.util.Log;
import libcore.util.HexEncoding;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;
import java.security.MessageDigest;
import java.util.Arrays;
import junit.framework.TestCase;
public class RoughtimeClientTest extends TestCase {
private static final String TAG = "RoughtimeClientTest";
private static final long TEST_TIME = 8675309;
private static final int TEST_RADIUS = 42;
private final RoughtimeTestServer mServer = new RoughtimeTestServer();
private final RoughtimeClient mClient = new RoughtimeClient();
public void testBasicWorkingRoughtimeClientQuery() throws Exception {
mServer.shouldRespond(true);
assertTrue(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
assertEquals(1, mServer.numRequestsReceived());
assertEquals(1, mServer.numRepliesSent());
}
public void testDnsResolutionFailure() throws Exception {
mServer.shouldRespond(true);
assertFalse(mClient.requestTime("roughtime.server.doesnotexist.example", 5000));
}
public void testTimeoutFailure() throws Exception {
mServer.shouldRespond(false);
assertFalse(mClient.requestTime(mServer.getAddress(), mServer.getPort(), 500));
assertEquals(1, mServer.numRequestsReceived());
assertEquals(0, mServer.numRepliesSent());
}
private static MessageDigest md = null;
private static byte[] signedResponse(byte[] nonce) {
RoughtimeClient.Message signed = new RoughtimeClient.Message();
try {
if (md == null) {
md = MessageDigest.getInstance("SHA-512");
}
} catch(Exception e) {
return null;
}
md.update(new byte[]{0});
byte[] hash = md.digest(nonce);
signed.put(RoughtimeClient.Tag.ROOT, hash);
signed.putLong(RoughtimeClient.Tag.MIDP, TEST_TIME);
signed.putInt(RoughtimeClient.Tag.RADI, TEST_RADIUS);
return signed.serialize();
}
private static byte[] response(byte[] nonce) {
RoughtimeClient.Message msg = new RoughtimeClient.Message();
msg.put(RoughtimeClient.Tag.SREP, signedResponse(nonce));
msg.putInt(RoughtimeClient.Tag.INDX, 0);
msg.put(RoughtimeClient.Tag.PATH, new byte[0]);
return msg.serialize();
}
private static class RoughtimeTestServer {
private final Object mLock = new Object();
private final DatagramSocket mSocket;
private final InetAddress mAddress;
private final int mPort;
private int mRcvd;
private int mSent;
private Thread mListeningThread;
private boolean mShouldRespond = true;
public RoughtimeTestServer() {
mSocket = makeSocket();
mAddress = mSocket.getLocalAddress();
mPort = mSocket.getLocalPort();
Log.d(TAG, "testing server listening on (" + mAddress + ", " + mPort + ")");
mListeningThread = new Thread() {
public void run() {
while (true) {
byte[] buffer = new byte[2048];
DatagramPacket request = new DatagramPacket(buffer, buffer.length);
try {
mSocket.receive(request);
} catch (IOException e) {
Log.e(TAG, "datagram receive error: " + e);
break;
}
synchronized (mLock) {
mRcvd++;
if (! mShouldRespond) {
continue;
}
RoughtimeClient.Message msg =
RoughtimeClient.Message.deserialize(
Arrays.copyOf(buffer, request.getLength()));
byte[] nonce = msg.get(RoughtimeClient.Tag.NONC);
if (nonce.length != 64) {
Log.e(TAG, "Nonce is wrong length.");
}
try {
request.setData(response(nonce));
mSocket.send(request);
} catch (IOException e) {
Log.e(TAG, "datagram send error: " + e);
break;
}
mSent++;
}
}
mSocket.close();
}
};
mListeningThread.start();
}
private DatagramSocket makeSocket() {
DatagramSocket socket;
try {
socket = new DatagramSocket(0, InetAddress.getLoopbackAddress());
} catch (SocketException e) {
Log.e(TAG, "Failed to create test server socket: " + e);
return null;
}
return socket;
}
public void shouldRespond(boolean value) { mShouldRespond = value; }
public InetAddress getAddress() { return mAddress; }
public int getPort() { return mPort; }
public int numRequestsReceived() { synchronized (mLock) { return mRcvd; } }
public int numRepliesSent() { synchronized (mLock) { return mSent; } }
}
}