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.UTFDataFormatException; 26import java.nio.charset.Charsets; 27import java.nio.charset.ModifiedUtf8; 28import java.util.ArrayList; 29import java.util.Date; 30 31/** 32 * @hide This should not be made public in its present form because it 33 * assumes that private and secret key bytes are available and would 34 * preclude the use of hardware crypto. 35 */ 36public class KeyStore { 37 38 // ResponseCodes 39 public static final int NO_ERROR = 1; 40 public static final int LOCKED = 2; 41 public static final int UNINITIALIZED = 3; 42 public static final int SYSTEM_ERROR = 4; 43 public static final int PROTOCOL_ERROR = 5; 44 public static final int PERMISSION_DENIED = 6; 45 public static final int KEY_NOT_FOUND = 7; 46 public static final int VALUE_CORRUPTED = 8; 47 public static final int UNDEFINED_ACTION = 9; 48 public static final int WRONG_PASSWORD = 10; 49 50 // States 51 public enum State { UNLOCKED, LOCKED, UNINITIALIZED }; 52 53 private static final LocalSocketAddress sAddress = new LocalSocketAddress( 54 "keystore", LocalSocketAddress.Namespace.RESERVED); 55 56 private int mError = NO_ERROR; 57 58 private KeyStore() {} 59 60 public static KeyStore getInstance() { 61 return new KeyStore(); 62 } 63 64 public State state() { 65 execute('t'); 66 switch (mError) { 67 case NO_ERROR: return State.UNLOCKED; 68 case LOCKED: return State.LOCKED; 69 case UNINITIALIZED: return State.UNINITIALIZED; 70 default: throw new AssertionError(mError); 71 } 72 } 73 74 private byte[] get(byte[] key) { 75 ArrayList<byte[]> values = execute('g', key); 76 return (values == null || values.isEmpty()) ? null : values.get(0); 77 } 78 79 public byte[] get(String key) { 80 return get(getKeyBytes(key)); 81 } 82 83 private boolean put(byte[] key, byte[] value) { 84 execute('i', key, value); 85 return mError == NO_ERROR; 86 } 87 88 public boolean put(String key, byte[] value) { 89 return put(getKeyBytes(key), value); 90 } 91 92 private boolean delete(byte[] key) { 93 execute('d', key); 94 return mError == NO_ERROR; 95 } 96 97 public boolean delete(String key) { 98 return delete(getKeyBytes(key)); 99 } 100 101 private boolean contains(byte[] key) { 102 execute('e', key); 103 return mError == NO_ERROR; 104 } 105 106 public boolean contains(String key) { 107 return contains(getKeyBytes(key)); 108 } 109 110 public byte[][] saw(byte[] prefix) { 111 ArrayList<byte[]> values = execute('s', prefix); 112 return (values == null) ? null : values.toArray(new byte[values.size()][]); 113 } 114 115 public String[] saw(String prefix) { 116 byte[][] values = saw(getKeyBytes(prefix)); 117 if (values == null) { 118 return null; 119 } 120 String[] strings = new String[values.length]; 121 for (int i = 0; i < values.length; ++i) { 122 strings[i] = toKeyString(values[i]); 123 } 124 return strings; 125 } 126 127 public boolean reset() { 128 execute('r'); 129 return mError == NO_ERROR; 130 } 131 132 private boolean password(byte[] password) { 133 execute('p', password); 134 return mError == NO_ERROR; 135 } 136 137 public boolean password(String password) { 138 return password(getPasswordBytes(password)); 139 } 140 141 public boolean lock() { 142 execute('l'); 143 return mError == NO_ERROR; 144 } 145 146 private boolean unlock(byte[] password) { 147 execute('u', password); 148 return mError == NO_ERROR; 149 } 150 151 public boolean unlock(String password) { 152 return unlock(getPasswordBytes(password)); 153 } 154 155 public boolean isEmpty() { 156 execute('z'); 157 return mError == KEY_NOT_FOUND; 158 } 159 160 private boolean generate(byte[] key) { 161 execute('a', key); 162 return mError == NO_ERROR; 163 } 164 165 public boolean generate(String key) { 166 return generate(getKeyBytes(key)); 167 } 168 169 private boolean importKey(byte[] keyName, byte[] key) { 170 execute('m', keyName, key); 171 return mError == NO_ERROR; 172 } 173 174 public boolean importKey(String keyName, byte[] key) { 175 return importKey(getKeyBytes(keyName), key); 176 } 177 178 private byte[] getPubkey(byte[] key) { 179 ArrayList<byte[]> values = execute('b', key); 180 return (values == null || values.isEmpty()) ? null : values.get(0); 181 } 182 183 public byte[] getPubkey(String key) { 184 return getPubkey(getKeyBytes(key)); 185 } 186 187 private boolean delKey(byte[] key) { 188 execute('k', key); 189 return mError == NO_ERROR; 190 } 191 192 public boolean delKey(String key) { 193 return delKey(getKeyBytes(key)); 194 } 195 196 private byte[] sign(byte[] keyName, byte[] data) { 197 final ArrayList<byte[]> values = execute('n', keyName, data); 198 return (values == null || values.isEmpty()) ? null : values.get(0); 199 } 200 201 public byte[] sign(String key, byte[] data) { 202 return sign(getKeyBytes(key), data); 203 } 204 205 private boolean verify(byte[] keyName, byte[] data, byte[] signature) { 206 execute('v', keyName, data, signature); 207 return mError == NO_ERROR; 208 } 209 210 public boolean verify(String key, byte[] data, byte[] signature) { 211 return verify(getKeyBytes(key), data, signature); 212 } 213 214 private boolean grant(byte[] key, byte[] uid) { 215 execute('x', key, uid); 216 return mError == NO_ERROR; 217 } 218 219 public boolean grant(String key, int uid) { 220 return grant(getKeyBytes(key), getUidBytes(uid)); 221 } 222 223 private boolean ungrant(byte[] key, byte[] uid) { 224 execute('y', key, uid); 225 return mError == NO_ERROR; 226 } 227 228 public boolean ungrant(String key, int uid) { 229 return ungrant(getKeyBytes(key), getUidBytes(uid)); 230 } 231 232 private long getmtime(byte[] key) { 233 final ArrayList<byte[]> values = execute('c', key); 234 if (values == null || values.isEmpty()) { 235 return -1L; 236 } 237 238 return Long.parseLong(new String(values.get(0))) * 1000L; 239 } 240 241 /** 242 * Returns the last modification time of the key in milliseconds since the 243 * epoch. Will return -1L if the key could not be found or other error. 244 */ 245 public long getmtime(String key) { 246 return getmtime(getKeyBytes(key)); 247 } 248 249 public int getLastError() { 250 return mError; 251 } 252 253 private ArrayList<byte[]> execute(int code, byte[]... parameters) { 254 mError = PROTOCOL_ERROR; 255 256 for (byte[] parameter : parameters) { 257 if (parameter == null || parameter.length > 65535) { 258 return null; 259 } 260 } 261 262 LocalSocket socket = new LocalSocket(); 263 try { 264 socket.connect(sAddress); 265 266 OutputStream out = socket.getOutputStream(); 267 out.write(code); 268 for (byte[] parameter : parameters) { 269 out.write(parameter.length >> 8); 270 out.write(parameter.length); 271 out.write(parameter); 272 } 273 out.flush(); 274 socket.shutdownOutput(); 275 276 InputStream in = socket.getInputStream(); 277 if ((code = in.read()) != NO_ERROR) { 278 if (code != -1) { 279 mError = code; 280 } 281 return null; 282 } 283 284 ArrayList<byte[]> values = new ArrayList<byte[]>(); 285 while (true) { 286 int i, j; 287 if ((i = in.read()) == -1) { 288 break; 289 } 290 if ((j = in.read()) == -1) { 291 return null; 292 } 293 byte[] value = new byte[i << 8 | j]; 294 for (i = 0; i < value.length; i += j) { 295 if ((j = in.read(value, i, value.length - i)) == -1) { 296 return null; 297 } 298 } 299 values.add(value); 300 } 301 mError = NO_ERROR; 302 return values; 303 } catch (IOException e) { 304 // ignore 305 } finally { 306 try { 307 socket.close(); 308 } catch (IOException e) {} 309 } 310 return null; 311 } 312 313 /** 314 * ModifiedUtf8 is used for key encoding to match the 315 * implementation of NativeCrypto.ENGINE_load_private_key. 316 */ 317 private static byte[] getKeyBytes(String string) { 318 try { 319 int utfCount = (int) ModifiedUtf8.countBytes(string, false); 320 byte[] result = new byte[utfCount]; 321 ModifiedUtf8.encode(result, 0, string); 322 return result; 323 } catch (UTFDataFormatException e) { 324 throw new RuntimeException(e); 325 } 326 } 327 328 private static String toKeyString(byte[] bytes) { 329 try { 330 return ModifiedUtf8.decode(bytes, new char[bytes.length], 0, bytes.length); 331 } catch (UTFDataFormatException e) { 332 throw new RuntimeException(e); 333 } 334 } 335 336 private static byte[] getPasswordBytes(String password) { 337 return password.getBytes(Charsets.UTF_8); 338 } 339 340 private static byte[] getUidBytes(int uid) { 341 return Integer.toString(uid).getBytes(Charsets.UTF_8); 342 } 343} 344