KeyStore.java revision 7e4b1a488dd02c4bf6156379e36834e9e01c5b1b
1/*
2 * Copyright (C) 2009 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
17package android.security;
18
19import android.net.LocalSocketAddress;
20import android.net.LocalSocket;
21
22import java.io.InputStream;
23import java.io.IOException;
24import java.io.OutputStream;
25import java.io.UnsupportedEncodingException;
26import java.nio.charset.Charsets;
27import java.util.ArrayList;
28
29/**
30 * @hide This should not be made public in its present form because it
31 * assumes that private and secret key bytes are available and would
32 * preclude the use of hardware crypto.
33 */
34public class KeyStore {
35
36    // ResponseCodes
37    public static final int NO_ERROR = 1;
38    public static final int LOCKED = 2;
39    public static final int UNINITIALIZED = 3;
40    public static final int SYSTEM_ERROR = 4;
41    public static final int PROTOCOL_ERROR = 5;
42    public static final int PERMISSION_DENIED = 6;
43    public static final int KEY_NOT_FOUND = 7;
44    public static final int VALUE_CORRUPTED = 8;
45    public static final int UNDEFINED_ACTION = 9;
46    public static final int WRONG_PASSWORD = 10;
47
48    // States
49    public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
50
51    private static final LocalSocketAddress sAddress = new LocalSocketAddress(
52            "keystore", LocalSocketAddress.Namespace.RESERVED);
53
54    private int mError = NO_ERROR;
55
56    private KeyStore() {}
57
58    public static KeyStore getInstance() {
59        return new KeyStore();
60    }
61
62    public State state() {
63        execute('t');
64        switch (mError) {
65            case NO_ERROR: return State.UNLOCKED;
66            case LOCKED: return State.LOCKED;
67            case UNINITIALIZED: return State.UNINITIALIZED;
68            default: throw new AssertionError(mError);
69        }
70    }
71
72    private byte[] get(byte[] key) {
73        ArrayList<byte[]> values = execute('g', key);
74        return (values == null || values.isEmpty()) ? null : values.get(0);
75    }
76
77    public byte[] get(String key) {
78        return get(getBytes(key));
79    }
80
81    private boolean put(byte[] key, byte[] value) {
82        execute('i', key, value);
83        return mError == NO_ERROR;
84    }
85
86    public boolean put(String key, byte[] value) {
87        return put(getBytes(key), value);
88    }
89
90    private boolean delete(byte[] key) {
91        execute('d', key);
92        return mError == NO_ERROR;
93    }
94
95    public boolean delete(String key) {
96        return delete(getBytes(key));
97    }
98
99    private boolean contains(byte[] key) {
100        execute('e', key);
101        return mError == NO_ERROR;
102    }
103
104    public boolean contains(String key) {
105        return contains(getBytes(key));
106    }
107
108    public byte[][] saw(byte[] prefix) {
109        ArrayList<byte[]> values = execute('s', prefix);
110        return (values == null) ? null : values.toArray(new byte[values.size()][]);
111    }
112
113    public String[] saw(String prefix) {
114        byte[][] values = saw(getBytes(prefix));
115        if (values == null) {
116            return null;
117        }
118        String[] strings = new String[values.length];
119        for (int i = 0; i < values.length; ++i) {
120            strings[i] = toString(values[i]);
121        }
122        return strings;
123    }
124
125    public boolean reset() {
126        execute('r');
127        return mError == NO_ERROR;
128    }
129
130    private boolean password(byte[] password) {
131        execute('p', password);
132        return mError == NO_ERROR;
133    }
134
135    public boolean password(String password) {
136        return password(getBytes(password));
137    }
138
139    public boolean lock() {
140        execute('l');
141        return mError == NO_ERROR;
142    }
143
144    private boolean unlock(byte[] password) {
145        execute('u', password);
146        return mError == NO_ERROR;
147    }
148
149    public boolean unlock(String password) {
150        return unlock(getBytes(password));
151    }
152
153    public boolean isEmpty() {
154        execute('z');
155        return mError == KEY_NOT_FOUND;
156    }
157
158    public int getLastError() {
159        return mError;
160    }
161
162    private ArrayList<byte[]> execute(int code, byte[]... parameters) {
163        mError = PROTOCOL_ERROR;
164
165        for (byte[] parameter : parameters) {
166            if (parameter == null || parameter.length > 65535) {
167                return null;
168            }
169        }
170
171        LocalSocket socket = new LocalSocket();
172        try {
173            socket.connect(sAddress);
174
175            OutputStream out = socket.getOutputStream();
176            out.write(code);
177            for (byte[] parameter : parameters) {
178                out.write(parameter.length >> 8);
179                out.write(parameter.length);
180                out.write(parameter);
181            }
182            out.flush();
183            socket.shutdownOutput();
184
185            InputStream in = socket.getInputStream();
186            if ((code = in.read()) != NO_ERROR) {
187                if (code != -1) {
188                    mError = code;
189                }
190                return null;
191            }
192
193            ArrayList<byte[]> values = new ArrayList<byte[]>();
194            while (true) {
195                int i, j;
196                if ((i = in.read()) == -1) {
197                    break;
198                }
199                if ((j = in.read()) == -1) {
200                    return null;
201                }
202                byte[] value = new byte[i << 8 | j];
203                for (i = 0; i < value.length; i += j) {
204                    if ((j = in.read(value, i, value.length - i)) == -1) {
205                        return null;
206                    }
207                }
208                values.add(value);
209            }
210            mError = NO_ERROR;
211            return values;
212        } catch (IOException e) {
213            // ignore
214        } finally {
215            try {
216                socket.close();
217            } catch (IOException e) {}
218        }
219        return null;
220    }
221
222    private static byte[] getBytes(String string) {
223        return string.getBytes(Charsets.UTF_8);
224    }
225
226    private static String toString(byte[] bytes) {
227        return new String(bytes, Charsets.UTF_8);
228    }
229}
230