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