Home | History | Annotate | Download | only in security
      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 
     17 package android.security;
     18 
     19 import android.net.LocalSocketAddress;
     20 import android.net.LocalSocket;
     21 
     22 import java.io.InputStream;
     23 import java.io.IOException;
     24 import java.io.OutputStream;
     25 import java.io.UnsupportedEncodingException;
     26 import java.nio.charset.Charsets;
     27 import 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  */
     34 public 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