144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh/*
244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * Copyright (C) 2009 The Android Open Source Project
344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh *
444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * Licensed under the Apache License, Version 2.0 (the "License");
544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * you may not use this file except in compliance with the License.
644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * You may obtain a copy of the License at
744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh *
844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh *      http://www.apache.org/licenses/LICENSE-2.0
944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh *
1044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * Unless required by applicable law or agreed to in writing, software
1144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * distributed under the License is distributed on an "AS IS" BASIS,
1244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * See the License for the specific language governing permissions and
1444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh * limitations under the License.
1544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh */
1644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
1744039172627d1c15737ea73836ad375559d76211Chia-chi Yehpackage android.security;
1844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
1944039172627d1c15737ea73836ad375559d76211Chia-chi Yehimport android.net.LocalSocketAddress;
2044039172627d1c15737ea73836ad375559d76211Chia-chi Yehimport android.net.LocalSocket;
2144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
2244039172627d1c15737ea73836ad375559d76211Chia-chi Yehimport java.io.InputStream;
2344039172627d1c15737ea73836ad375559d76211Chia-chi Yehimport java.io.IOException;
2444039172627d1c15737ea73836ad375559d76211Chia-chi Yehimport java.io.OutputStream;
255b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstromimport java.io.UTFDataFormatException;
2646703b099516c383a6882815bcf9cd4df0ec538dBrian Carlstromimport java.nio.charset.Charsets;
275b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstromimport java.nio.charset.ModifiedUtf8;
2844039172627d1c15737ea73836ad375559d76211Chia-chi Yehimport java.util.ArrayList;
29473c712b19bad992ab4eafcd43175fdce77b913dKenny Rootimport java.util.Date;
3044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
3144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh/**
3246703b099516c383a6882815bcf9cd4df0ec538dBrian Carlstrom * @hide This should not be made public in its present form because it
3346703b099516c383a6882815bcf9cd4df0ec538dBrian Carlstrom * assumes that private and secret key bytes are available and would
3446703b099516c383a6882815bcf9cd4df0ec538dBrian Carlstrom * preclude the use of hardware crypto.
3544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh */
3644039172627d1c15737ea73836ad375559d76211Chia-chi Yehpublic class KeyStore {
375cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom
385cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    // ResponseCodes
397e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int NO_ERROR = 1;
407e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int LOCKED = 2;
417e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int UNINITIALIZED = 3;
427e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int SYSTEM_ERROR = 4;
437e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int PROTOCOL_ERROR = 5;
447e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int PERMISSION_DENIED = 6;
457e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int KEY_NOT_FOUND = 7;
467e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int VALUE_CORRUPTED = 8;
477e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int UNDEFINED_ACTION = 9;
487e4b1a488dd02c4bf6156379e36834e9e01c5b1bBrian Carlstrom    public static final int WRONG_PASSWORD = 10;
495cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom
505cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    // States
515cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
5244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
5344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    private static final LocalSocketAddress sAddress = new LocalSocketAddress(
5444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            "keystore", LocalSocketAddress.Namespace.RESERVED);
5544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
5644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    private int mError = NO_ERROR;
5744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
5844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    private KeyStore() {}
5944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
6044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    public static KeyStore getInstance() {
6144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return new KeyStore();
6244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
6344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
645cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    public State state() {
6544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        execute('t');
665cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom        switch (mError) {
675cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom            case NO_ERROR: return State.UNLOCKED;
685cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom            case LOCKED: return State.LOCKED;
695cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom            case UNINITIALIZED: return State.UNINITIALIZED;
705cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom            default: throw new AssertionError(mError);
715cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom        }
7244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
7344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
745cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    private byte[] get(byte[] key) {
75d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh        ArrayList<byte[]> values = execute('g', key);
7634c47c855815d731e6deb55748ff690b0ec7b53fNick Kralevich        return (values == null || values.isEmpty()) ? null : values.get(0);
7744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
7844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
795cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    public byte[] get(String key) {
805b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return get(getKeyBytes(key));
8144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
8244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
835cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    private boolean put(byte[] key, byte[] value) {
8444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        execute('i', key, value);
8544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return mError == NO_ERROR;
8644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
8744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
885cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    public boolean put(String key, byte[] value) {
895b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return put(getKeyBytes(key), value);
9044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
9144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
925cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    private boolean delete(byte[] key) {
9344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        execute('d', key);
9444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return mError == NO_ERROR;
9544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
9644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
9744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    public boolean delete(String key) {
985b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return delete(getKeyBytes(key));
9944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
10044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
1015cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    private boolean contains(byte[] key) {
10244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        execute('e', key);
10344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return mError == NO_ERROR;
10444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
10544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
10644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    public boolean contains(String key) {
1075b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return contains(getKeyBytes(key));
10844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
10944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
110613fcc850686dfe71cec9809c3694be9cf02cdc7Chia-chi Yeh    public byte[][] saw(byte[] prefix) {
111d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh        ArrayList<byte[]> values = execute('s', prefix);
112d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh        return (values == null) ? null : values.toArray(new byte[values.size()][]);
11344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
11444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
115613fcc850686dfe71cec9809c3694be9cf02cdc7Chia-chi Yeh    public String[] saw(String prefix) {
1165b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        byte[][] values = saw(getKeyBytes(prefix));
11744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        if (values == null) {
11844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            return null;
11944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        }
12044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        String[] strings = new String[values.length];
12144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        for (int i = 0; i < values.length; ++i) {
1225b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom            strings[i] = toKeyString(values[i]);
12344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        }
12444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return strings;
12544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
12644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
12744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    public boolean reset() {
12844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        execute('r');
12944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return mError == NO_ERROR;
13044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
13144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
1325cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    private boolean password(byte[] password) {
1335cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom        execute('p', password);
13444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return mError == NO_ERROR;
13544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
13644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
13744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    public boolean password(String password) {
1385b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return password(getPasswordBytes(password));
13944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
14044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
14144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    public boolean lock() {
14244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        execute('l');
14344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return mError == NO_ERROR;
14444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
14544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
1465cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    private boolean unlock(byte[] password) {
14744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        execute('u', password);
14844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return mError == NO_ERROR;
14944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
15044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
15144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    public boolean unlock(String password) {
1525b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return unlock(getPasswordBytes(password));
15344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
15444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
1555cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    public boolean isEmpty() {
1565cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom        execute('z');
1575cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom        return mError == KEY_NOT_FOUND;
1585cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom    }
1595cfee3fabb3482c6a6df1c8b6f21e843cf214527Brian Carlstrom
1605423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    private boolean generate(byte[] key) {
1615423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        execute('a', key);
1625423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        return mError == NO_ERROR;
1635423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
1645423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
1655423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    public boolean generate(String key) {
1665b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return generate(getKeyBytes(key));
1675423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
1685423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
1695423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    private boolean importKey(byte[] keyName, byte[] key) {
1705423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        execute('m', keyName, key);
1715423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        return mError == NO_ERROR;
1725423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
1735423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
1745423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    public boolean importKey(String keyName, byte[] key) {
1755b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return importKey(getKeyBytes(keyName), key);
1765423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
1775423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
1785423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    private byte[] getPubkey(byte[] key) {
1795423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        ArrayList<byte[]> values = execute('b', key);
1805423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        return (values == null || values.isEmpty()) ? null : values.get(0);
1815423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
1825423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
1835423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    public byte[] getPubkey(String key) {
1845b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return getPubkey(getKeyBytes(key));
1855423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
1865423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
1875423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    private boolean delKey(byte[] key) {
1885423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        execute('k', key);
1895423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        return mError == NO_ERROR;
1905423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
1915423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
1925423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    public boolean delKey(String key) {
1935b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return delKey(getKeyBytes(key));
1945423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
1955423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
1965423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    private byte[] sign(byte[] keyName, byte[] data) {
1975423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        final ArrayList<byte[]> values = execute('n', keyName, data);
1985423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        return (values == null || values.isEmpty()) ? null : values.get(0);
1995423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
2005423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
2015423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    public byte[] sign(String key, byte[] data) {
2025b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return sign(getKeyBytes(key), data);
2035423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
2045423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
2055423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    private boolean verify(byte[] keyName, byte[] data, byte[] signature) {
2065423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        execute('v', keyName, data, signature);
2075423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        return mError == NO_ERROR;
2085423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
2095423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
2105423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    public boolean verify(String key, byte[] data, byte[] signature) {
2115b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return verify(getKeyBytes(key), data, signature);
2125423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
2135423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
2145423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    private boolean grant(byte[] key, byte[] uid) {
2155423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        execute('x', key, uid);
2165423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        return mError == NO_ERROR;
2175423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
2185423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
2195423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    public boolean grant(String key, int uid) {
2205b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom         return grant(getKeyBytes(key), getUidBytes(uid));
2215423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
2225423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
2235423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    private boolean ungrant(byte[] key, byte[] uid) {
2245423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        execute('y', key, uid);
2255423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root        return mError == NO_ERROR;
2265423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
2275423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
2285423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    public boolean ungrant(String key, int uid) {
2295b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return ungrant(getKeyBytes(key), getUidBytes(uid));
2305423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root    }
2315423e68d5dbe048ec6f042cce52a33f94184e9fbKenny Root
232473c712b19bad992ab4eafcd43175fdce77b913dKenny Root    private long getmtime(byte[] key) {
233473c712b19bad992ab4eafcd43175fdce77b913dKenny Root        final ArrayList<byte[]> values = execute('c', key);
234473c712b19bad992ab4eafcd43175fdce77b913dKenny Root        if (values == null || values.isEmpty()) {
235473c712b19bad992ab4eafcd43175fdce77b913dKenny Root            return -1L;
236473c712b19bad992ab4eafcd43175fdce77b913dKenny Root        }
237473c712b19bad992ab4eafcd43175fdce77b913dKenny Root
238473c712b19bad992ab4eafcd43175fdce77b913dKenny Root        return Long.parseLong(new String(values.get(0))) * 1000L;
239473c712b19bad992ab4eafcd43175fdce77b913dKenny Root    }
240473c712b19bad992ab4eafcd43175fdce77b913dKenny Root
241473c712b19bad992ab4eafcd43175fdce77b913dKenny Root    /**
242473c712b19bad992ab4eafcd43175fdce77b913dKenny Root     * Returns the last modification time of the key in milliseconds since the
243473c712b19bad992ab4eafcd43175fdce77b913dKenny Root     * epoch. Will return -1L if the key could not be found or other error.
244473c712b19bad992ab4eafcd43175fdce77b913dKenny Root     */
245473c712b19bad992ab4eafcd43175fdce77b913dKenny Root    public long getmtime(String key) {
246473c712b19bad992ab4eafcd43175fdce77b913dKenny Root        return getmtime(getKeyBytes(key));
247473c712b19bad992ab4eafcd43175fdce77b913dKenny Root    }
248473c712b19bad992ab4eafcd43175fdce77b913dKenny Root
24944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    public int getLastError() {
25044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return mError;
25144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
25244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
253d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh    private ArrayList<byte[]> execute(int code, byte[]... parameters) {
25444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        mError = PROTOCOL_ERROR;
25544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
25644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        for (byte[] parameter : parameters) {
25744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            if (parameter == null || parameter.length > 65535) {
25844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                return null;
25944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            }
26044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        }
26144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
26244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        LocalSocket socket = new LocalSocket();
26344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        try {
26444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            socket.connect(sAddress);
26544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
26644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            OutputStream out = socket.getOutputStream();
26744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            out.write(code);
26844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            for (byte[] parameter : parameters) {
26944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                out.write(parameter.length >> 8);
27044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                out.write(parameter.length);
27144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                out.write(parameter);
27244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            }
27344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            out.flush();
27444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            socket.shutdownOutput();
27544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
27644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            InputStream in = socket.getInputStream();
277f1ece5d0c16fa3e79390e41ad9bec020c77d7720Chia-chi Yeh            if ((code = in.read()) != NO_ERROR) {
278f1ece5d0c16fa3e79390e41ad9bec020c77d7720Chia-chi Yeh                if (code != -1) {
279f1ece5d0c16fa3e79390e41ad9bec020c77d7720Chia-chi Yeh                    mError = code;
280f1ece5d0c16fa3e79390e41ad9bec020c77d7720Chia-chi Yeh                }
28144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                return null;
28244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            }
28344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh
284d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh            ArrayList<byte[]> values = new ArrayList<byte[]>();
28544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            while (true) {
28644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                int i, j;
28744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                if ((i = in.read()) == -1) {
28844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                    break;
28944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                }
29044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                if ((j = in.read()) == -1) {
29144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                    return null;
29244039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                }
293d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh                byte[] value = new byte[i << 8 | j];
294d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh                for (i = 0; i < value.length; i += j) {
295d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh                    if ((j = in.read(value, i, value.length - i)) == -1) {
29644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                        return null;
29744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                    }
29844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                }
299d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh                values.add(value);
30044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            }
301f1ece5d0c16fa3e79390e41ad9bec020c77d7720Chia-chi Yeh            mError = NO_ERROR;
302d12feb97667498378a472c5a7895a9fcd8056ec5Chia-chi Yeh            return values;
30344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        } catch (IOException e) {
30444039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            // ignore
30544039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        } finally {
30644039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            try {
30744039172627d1c15737ea73836ad375559d76211Chia-chi Yeh                socket.close();
30844039172627d1c15737ea73836ad375559d76211Chia-chi Yeh            } catch (IOException e) {}
30944039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        }
31044039172627d1c15737ea73836ad375559d76211Chia-chi Yeh        return null;
31144039172627d1c15737ea73836ad375559d76211Chia-chi Yeh    }
31234c47c855815d731e6deb55748ff690b0ec7b53fNick Kralevich
3135b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom    /**
3145b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom     * ModifiedUtf8 is used for key encoding to match the
3155b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom     * implementation of NativeCrypto.ENGINE_load_private_key.
3165b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom     */
3175b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom    private static byte[] getKeyBytes(String string) {
3185b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        try {
3195b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom            int utfCount = (int) ModifiedUtf8.countBytes(string, false);
3205b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom            byte[] result = new byte[utfCount];
3215b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom            ModifiedUtf8.encode(result, 0, string);
3225b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom            return result;
3235b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        } catch (UTFDataFormatException e) {
3245b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom            throw new RuntimeException(e);
3255b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        }
3265b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom    }
3275b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom
3285b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom    private static String toKeyString(byte[] bytes) {
3295b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        try {
3305b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom            return ModifiedUtf8.decode(bytes, new char[bytes.length], 0, bytes.length);
3315b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        } catch (UTFDataFormatException e) {
3325b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom            throw new RuntimeException(e);
3335b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        }
3345b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom    }
3355b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom
3365b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom    private static byte[] getPasswordBytes(String password) {
3375b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return password.getBytes(Charsets.UTF_8);
33834c47c855815d731e6deb55748ff690b0ec7b53fNick Kralevich    }
33934c47c855815d731e6deb55748ff690b0ec7b53fNick Kralevich
3405b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom    private static byte[] getUidBytes(int uid) {
3415b1f037829bff93877a6257db69f4e7723a27e20Brian Carlstrom        return Integer.toString(uid).getBytes(Charsets.UTF_8);
34234c47c855815d731e6deb55748ff690b0ec7b53fNick Kralevich    }
34344039172627d1c15737ea73836ad375559d76211Chia-chi Yeh}
344