diff options
Diffstat (limited to 'core/java')
| -rw-r--r-- | core/java/android/net/RoughtimeClient.java | 499 |
1 files changed, 0 insertions, 499 deletions
diff --git a/core/java/android/net/RoughtimeClient.java b/core/java/android/net/RoughtimeClient.java deleted file mode 100644 index cf4d8a2d3f5a..000000000000 --- a/core/java/android/net/RoughtimeClient.java +++ /dev/null @@ -1,499 +0,0 @@ -/* - * 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.os.SystemClock; -import android.util.Log; - -import java.io.IOException; -import java.net.DatagramPacket; -import java.net.DatagramSocket; -import java.net.InetAddress; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.security.MessageDigest; -import java.security.SecureRandom; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.HashMap; - -/** - * {@hide} - * - * Simple Roughtime client class for retrieving network time. - */ -public class RoughtimeClient -{ - private static final String TAG = "RoughtimeClient"; - private static final boolean ENABLE_DEBUG = true; - - private static final int ROUGHTIME_PORT = 5333; - - private static final int MIN_REQUEST_SIZE = 1024; - - private static final int NONCE_SIZE = 64; - - private static final int MAX_DATAGRAM_SIZE = 65507; - - private final SecureRandom random = new SecureRandom(); - - /** - * Tag values. Exposed for use in tests only. - */ - protected static enum Tag { - /** - * Nonce used to initiate a transaction. - */ - NONC(0x434e4f4e), - - /** - * Signed portion of a response. - **/ - SREP(0x50455253), - - /** - * Pad data. Always the largest tag lexicographically. - */ - PAD(0xff444150), - - /** - * A signature for a neighboring SREP. - */ - SIG(0x474953), - - /** - * Server certificate. - */ - CERT(0x54524543), - - /** - * Position in the Merkle tree. - */ - INDX(0x58444e49), - - /** - * Upward path in the Merkle tree. - */ - PATH(0x48544150), - - /** - * Midpoint of the time interval in the response. - */ - MIDP(0x5044494d), - - /** - * Radius of the time interval in the response. - */ - RADI(0x49444152), - - /** - * Root of the Merkle tree. - */ - ROOT(0x544f4f52), - - /** - * Delegation from the long term key to an online key. - */ - DELE(0x454c4544), - - /** - * Online public key. - */ - PUBK(0x4b425550), - - /** - * Earliest midpoint time the given PUBK can authenticate. - */ - MINT(0x544e494d), - - /** - * Latest midpoint time the given PUBK can authenticate. - */ - MAXT(0x5458414d); - - private final int value; - - Tag(int value) { - this.value = value; - } - - private int value() { - return value; - } - } - - /** - * A result retrieved from a roughtime server. - */ - private static class Result { - public long midpoint; - public int radius; - public long collectionTime; - } - - /** - * A Roughtime protocol message. Functionally a serializable map from Tags - * to byte arrays. - */ - protected static class Message { - private HashMap<Integer,byte[]> items = new HashMap<Integer,byte[]>(); - public int padSize = 0; - - public Message() {} - - /** - * Set the given data for the given tag. - */ - public void put(Tag tag, byte[] data) { - put(tag.value(), data); - } - - private void put(int tag, byte[] data) { - items.put(tag, data); - } - - /** - * Get the data associated with the given tag. - */ - public byte[] get(Tag tag) { - return items.get(tag.value()); - } - - /** - * Get the data associated with the given tag and decode it as a 64-bit - * integer. - */ - public long getLong(Tag tag) { - ByteBuffer b = ByteBuffer.wrap(get(tag)); - return b.getLong(); - } - - /** - * Get the data associated with the given tag and decode it as a 32-bit - * integer. - */ - public int getInt(Tag tag) { - ByteBuffer b = ByteBuffer.wrap(get(tag)); - return b.getInt(); - } - - /** - * Encode the given long value as a 64-bit little-endian value and - * associate it with the given tag. - */ - public void putLong(Tag tag, long l) { - ByteBuffer b = ByteBuffer.allocate(8); - b.putLong(l); - put(tag, b.array()); - } - - /** - * Encode the given int value as a 32-bit little-endian value and - * associate it with the given tag. - */ - public void putInt(Tag tag, int l) { - ByteBuffer b = ByteBuffer.allocate(4); - b.putInt(l); - put(tag, b.array()); - } - - /** - * Get a packed representation of this message suitable for the wire. - */ - public byte[] serialize() { - if (items.size() == 0) { - if (padSize > 4) - return new byte[padSize]; - else - return new byte[4]; - } - - int size = 0; - - ArrayList<Integer> offsets = new ArrayList<Integer>(); - ArrayList<Integer> tagList = new ArrayList<Integer>(items.keySet()); - Collections.sort(tagList); - - boolean first = true; - for (int tag : tagList) { - if (! first) { - offsets.add(size); - } - - first = false; - size += items.get(tag).length; - } - - ByteBuffer dataBuf = ByteBuffer.allocate(size); - dataBuf.order(ByteOrder.LITTLE_ENDIAN); - - int valueDataSize = size; - size += 4 + offsets.size() * 4 + tagList.size() * 4; - - int tagCount = items.size(); - - if (size < padSize) { - offsets.add(valueDataSize); - tagList.add(Tag.PAD.value()); - - if (size + 8 > padSize) { - size = size + 8; - } else { - size = padSize; - } - - tagCount += 1; - } - - ByteBuffer buf = ByteBuffer.allocate(size); - buf.order(ByteOrder.LITTLE_ENDIAN); - buf.putInt(tagCount); - - for (int offset : offsets) { - buf.putInt(offset); - } - - for (int tag : tagList) { - buf.putInt(tag); - - if (tag != Tag.PAD.value()) { - dataBuf.put(items.get(tag)); - } - } - - buf.put(dataBuf.array()); - - return buf.array(); - } - - /** - * Given a byte stream from the wire, unpack it into a Message object. - */ - public static Message deserialize(byte[] data) { - ByteBuffer buf = ByteBuffer.wrap(data); - buf.order(ByteOrder.LITTLE_ENDIAN); - - Message msg = new Message(); - - int count = buf.getInt(); - - if (count == 0) { - return msg; - } - - ArrayList<Integer> offsets = new ArrayList<Integer>(); - offsets.add(0); - - for (int i = 1; i < count; i++) { - offsets.add(buf.getInt()); - } - - ArrayList<Integer> tags = new ArrayList<Integer>(); - for (int i = 0; i < count; i++) { - int tag = buf.getInt(); - tags.add(tag); - } - - offsets.add(buf.remaining()); - - for (int i = 0; i < count; i++) { - int tag = tags.get(i); - int start = offsets.get(i); - int end = offsets.get(i+1); - byte[] content = new byte[end - start]; - - buf.get(content); - if (tag != Tag.PAD.value()) { - msg.put(tag, content); - } - } - - return msg; - } - - /** - * Send this message over the given socket to the given address and port. - */ - public void send(DatagramSocket socket, InetAddress address, int port) - throws IOException { - byte[] buffer = serialize(); - DatagramPacket message = new DatagramPacket(buffer, buffer.length, - address, port); - socket.send(message); - } - - /** - * Receive a Message object from the given socket. - */ - public static Message receive(DatagramSocket socket) - throws IOException { - byte[] buffer = new byte[MAX_DATAGRAM_SIZE]; - DatagramPacket packet = new DatagramPacket(buffer, buffer.length); - - socket.receive(packet); - - return deserialize(Arrays.copyOf(buffer, packet.getLength())); - } - } - - private MessageDigest messageDigest = null; - - private final ArrayList<Result> results = new ArrayList<Result>(); - private long lastRequest = 0; - - private Message createRequestMessage() { - byte[] nonce = new byte[NONCE_SIZE]; - random.nextBytes(nonce); // TODO: Chain nonces - - assert nonce.length == NONCE_SIZE : - "Nonce must be " + NONCE_SIZE + " bytes."; - - Message msg = new Message(); - - msg.put(Tag.NONC, nonce); - msg.padSize = MIN_REQUEST_SIZE; - - return msg; - } - - /** - * Contact the Roughtime server at the given address and port and collect a - * result time to add to our collection. - * - * @param host host name of the server. - * @param timeout network timeout in milliseconds. - * @return true if the transaction was successful. - */ - public boolean requestTime(String host, int timeout) { - InetAddress address = null; - try { - address = InetAddress.getByName(host); - } catch (Exception e) { - if (ENABLE_DEBUG) { - Log.d(TAG, "request time failed", e); - } - - return false; - } - return requestTime(address, ROUGHTIME_PORT, timeout); - } - - /** - * Contact the Roughtime server at the given address and port and collect a - * result time to add to our collection. - * - * @param address address for the server. - * @param port port to talk to the server on. - * @param timeout network timeout in milliseconds. - * @return true if the transaction was successful. - */ - public boolean requestTime(InetAddress address, int port, int timeout) { - - final long rightNow = SystemClock.elapsedRealtime(); - - if ((rightNow - lastRequest) > timeout) { - results.clear(); - } - - lastRequest = rightNow; - - DatagramSocket socket = null; - try { - if (messageDigest == null) { - messageDigest = MessageDigest.getInstance("SHA-512"); - } - - socket = new DatagramSocket(); - socket.setSoTimeout(timeout); - final long startTime = SystemClock.elapsedRealtime(); - Message request = createRequestMessage(); - request.send(socket, address, port); - final long endTime = SystemClock.elapsedRealtime(); - Message response = Message.receive(socket); - byte[] signedData = response.get(Tag.SREP); - Message signedResponse = Message.deserialize(signedData); - - final Result result = new Result(); - result.midpoint = signedResponse.getLong(Tag.MIDP); - result.radius = signedResponse.getInt(Tag.RADI); - result.collectionTime = (startTime + endTime) / 2; - - final byte[] root = signedResponse.get(Tag.ROOT); - final byte[] path = response.get(Tag.PATH); - final byte[] nonce = request.get(Tag.NONC); - final int index = response.getInt(Tag.INDX); - - if (! verifyNonce(root, path, nonce, index)) { - Log.w(TAG, "failed to authenticate roughtime response."); - return false; - } - - results.add(result); - } catch (Exception e) { - if (ENABLE_DEBUG) { - Log.d(TAG, "request time failed", e); - } - - return false; - } finally { - if (socket != null) { - socket.close(); - } - } - - return true; - } - - /** - * Verify that a reply message corresponds to the nonce sent in the request. - * - * @param root Root of the Merkle tree used to sign the nonce. Received in - * the ROOT tag of the reply. - * @param path Sibling hashes along the path to the root of the Merkle tree. - * Received in the PATH tag of the reply. - * @param nonce The nonce we sent in the request. - * @param index Bitfield indicating whether chunks of the path are left or - * right children. - * @return true if the verification is successful. - */ - private boolean verifyNonce(byte[] root, byte[] path, byte[] nonce, - int index) { - messageDigest.update(new byte[]{ 0 }); - byte[] hash = messageDigest.digest(nonce); - int pos = 0; - byte[] one = new byte[]{ 1 }; - - while (pos < path.length) { - messageDigest.update(one); - - if ((index&1) != 0) { - messageDigest.update(path, pos, 64); - hash = messageDigest.digest(hash); - } else { - messageDigest.update(hash); - messageDigest.update(path, pos, 64); - hash = messageDigest.digest(); - } - - pos += 64; - index >>>= 1; - } - - return Arrays.equals(root, hash); - } -} |
