Summary:
frameworks/base
keystore rewrite
keyguard integration with keystore on keyguard entry or keyguard change
KeyStore API simplification
packages/apps/Settings
Removed com.android.credentials.SET_PASSWORD intent support
Added keyguard requirement for keystore use
packages/apps/CertInstaller
Tracking KeyStore API changes
Fix for NPE in CertInstaller when certificate lacks basic constraints
packages/apps/KeyChain
Tracking KeyStore API changes
Details:
frameworks/base
Move keystore from C to C++ while rewriting password
implementation. Removed global variables. Added many comments.
cmds/keystore/Android.mk
cmds/keystore/keystore.h
cmds/keystore/keystore.c => cmds/keystore/keystore.cpp
cmds/keystore/keystore_cli.c => cmds/keystore/keystore_cli.cpp
Changed saveLockPattern and saveLockPassword to notify the keystore
on changes so that the keystore master key can be reencrypted when
the keyguard changes.
core/java/com/android/internal/widget/LockPatternUtils.java
Changed unlock screens to pass values for keystore unlock or initialization
policy/src/com/android/internal/policy/impl/PasswordUnlockScreen.java
policy/src/com/android/internal/policy/impl/PatternUnlockScreen.java
KeyStore API changes
- renamed test() to state(), which now return a State enum
- made APIs with byte[] key arguments private
- added new KeyStore.isEmpty used to determine if a keyguard is required
keystore/java/android/security/KeyStore.java
In addition to tracking KeyStore API changes, added new testIsEmpty
and improved some existing tests to validate expect values.
keystore/tests/src/android/security/KeyStoreTest.java
packages/apps/Settings
Removing com.android.credentials.SET_PASSWORD intent with the
removal of the ability to set an explicit keystore password now
that the keyguard value is used. Changed to ensure keyguard is
enabled for keystore install or unlock. Cleaned up interwoven
dialog handing into discrete dialog helper classes.
AndroidManifest.xml
src/com/android/settings/CredentialStorage.java
Remove layout for entering new password
res/layout/credentials_dialog.xml
Remove enable credentials checkbox
res/xml/security_settings_misc.xml
src/com/android/settings/SecuritySettings.java
Added ability to specify minimum quality key to ChooseLockGeneric
Activity. Used by CredentialStorage, but could also be used by
CryptKeeperSettings. Changed ChooseLockGeneric to understand
minimum quality for keystore in addition to DPM and device
encryption.
src/com/android/settings/ChooseLockGeneric.java
Changed to use getActivePasswordQuality from
getKeyguardStoredPasswordQuality based on experience in
CredentialStorage. Removed bogus class javadoc.
src/com/android/settings/CryptKeeperSettings.java
Tracking KeyStore API changes
src/com/android/settings/vpn/VpnSettings.java
src/com/android/settings/wifi/WifiSettings.java
Removing now unused string resources
res/values-af/strings.xml
res/values-am/strings.xml
res/values-ar/strings.xml
res/values-bg/strings.xml
res/values-ca/strings.xml
res/values-cs/strings.xml
res/values-da/strings.xml
res/values-de/strings.xml
res/values-el/strings.xml
res/values-en-rGB/strings.xml
res/values-es-rUS/strings.xml
res/values-es/strings.xml
res/values-fa/strings.xml
res/values-fi/strings.xml
res/values-fr/strings.xml
res/values-hr/strings.xml
res/values-hu/strings.xml
res/values-in/strings.xml
res/values-it/strings.xml
res/values-iw/strings.xml
res/values-ja/strings.xml
res/values-ko/strings.xml
res/values-lt/strings.xml
res/values-lv/strings.xml
res/values-ms/strings.xml
res/values-nb/strings.xml
res/values-nl/strings.xml
res/values-pl/strings.xml
res/values-pt-rPT/strings.xml
res/values-pt/strings.xml
res/values-rm/strings.xml
res/values-ro/strings.xml
res/values-ru/strings.xml
res/values-sk/strings.xml
res/values-sl/strings.xml
res/values-sr/strings.xml
res/values-sv/strings.xml
res/values-sw/strings.xml
res/values-th/strings.xml
res/values-tl/strings.xml
res/values-tr/strings.xml
res/values-uk/strings.xml
res/values-vi/strings.xml
res/values-zh-rCN/strings.xml
res/values-zh-rTW/strings.xml
res/values-zu/strings.xml
res/values/strings.xml
packages/apps/CertInstaller
Tracking KeyStore API changes
src/com/android/certinstaller/CertInstaller.java
Fix for NPE in CertInstaller when certificate lacks basic constraints
src/com/android/certinstaller/CredentialHelper.java
packages/apps/KeyChain
Tracking KeyStore API changes
src/com/android/keychain/KeyChainActivity.java
src/com/android/keychain/KeyChainService.java
support/src/com/android/keychain/tests/support/IKeyChainServiceTestSupport.aidl
support/src/com/android/keychain/tests/support/KeyChainServiceTestSupport.java
tests/src/com/android/keychain/tests/KeyChainServiceTest.java
Change-Id: Ic141fb5d4b43d12fe62cb1e29c7cbd891b4be35d
230 lines
6.4 KiB
Java
230 lines
6.4 KiB
Java
/*
|
|
* Copyright (C) 2009 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package android.security;
|
|
|
|
import android.net.LocalSocketAddress;
|
|
import android.net.LocalSocket;
|
|
|
|
import java.io.InputStream;
|
|
import java.io.IOException;
|
|
import java.io.OutputStream;
|
|
import java.io.UnsupportedEncodingException;
|
|
import java.nio.charset.Charsets;
|
|
import java.util.ArrayList;
|
|
|
|
/**
|
|
* @hide This should not be made public in its present form because it
|
|
* assumes that private and secret key bytes are available and would
|
|
* preclude the use of hardware crypto.
|
|
*/
|
|
public class KeyStore {
|
|
|
|
// ResponseCodes
|
|
private static final int NO_ERROR = 1;
|
|
private static final int LOCKED = 2;
|
|
private static final int UNINITIALIZED = 3;
|
|
private static final int SYSTEM_ERROR = 4;
|
|
private static final int PROTOCOL_ERROR = 5;
|
|
private static final int PERMISSION_DENIED = 6;
|
|
private static final int KEY_NOT_FOUND = 7;
|
|
private static final int VALUE_CORRUPTED = 8;
|
|
private static final int UNDEFINED_ACTION = 9;
|
|
private static final int WRONG_PASSWORD = 10;
|
|
|
|
// States
|
|
public enum State { UNLOCKED, LOCKED, UNINITIALIZED };
|
|
|
|
private static final LocalSocketAddress sAddress = new LocalSocketAddress(
|
|
"keystore", LocalSocketAddress.Namespace.RESERVED);
|
|
|
|
private int mError = NO_ERROR;
|
|
|
|
private KeyStore() {}
|
|
|
|
public static KeyStore getInstance() {
|
|
return new KeyStore();
|
|
}
|
|
|
|
public State state() {
|
|
execute('t');
|
|
switch (mError) {
|
|
case NO_ERROR: return State.UNLOCKED;
|
|
case LOCKED: return State.LOCKED;
|
|
case UNINITIALIZED: return State.UNINITIALIZED;
|
|
default: throw new AssertionError(mError);
|
|
}
|
|
}
|
|
|
|
private byte[] get(byte[] key) {
|
|
ArrayList<byte[]> values = execute('g', key);
|
|
return (values == null || values.isEmpty()) ? null : values.get(0);
|
|
}
|
|
|
|
public byte[] get(String key) {
|
|
return get(getBytes(key));
|
|
}
|
|
|
|
private boolean put(byte[] key, byte[] value) {
|
|
execute('i', key, value);
|
|
return mError == NO_ERROR;
|
|
}
|
|
|
|
public boolean put(String key, byte[] value) {
|
|
return put(getBytes(key), value);
|
|
}
|
|
|
|
private boolean delete(byte[] key) {
|
|
execute('d', key);
|
|
return mError == NO_ERROR;
|
|
}
|
|
|
|
public boolean delete(String key) {
|
|
return delete(getBytes(key));
|
|
}
|
|
|
|
private boolean contains(byte[] key) {
|
|
execute('e', key);
|
|
return mError == NO_ERROR;
|
|
}
|
|
|
|
public boolean contains(String key) {
|
|
return contains(getBytes(key));
|
|
}
|
|
|
|
public byte[][] saw(byte[] prefix) {
|
|
ArrayList<byte[]> values = execute('s', prefix);
|
|
return (values == null) ? null : values.toArray(new byte[values.size()][]);
|
|
}
|
|
|
|
public String[] saw(String prefix) {
|
|
byte[][] values = saw(getBytes(prefix));
|
|
if (values == null) {
|
|
return null;
|
|
}
|
|
String[] strings = new String[values.length];
|
|
for (int i = 0; i < values.length; ++i) {
|
|
strings[i] = toString(values[i]);
|
|
}
|
|
return strings;
|
|
}
|
|
|
|
public boolean reset() {
|
|
execute('r');
|
|
return mError == NO_ERROR;
|
|
}
|
|
|
|
private boolean password(byte[] password) {
|
|
execute('p', password);
|
|
return mError == NO_ERROR;
|
|
}
|
|
|
|
public boolean password(String password) {
|
|
return password(getBytes(password));
|
|
}
|
|
|
|
public boolean lock() {
|
|
execute('l');
|
|
return mError == NO_ERROR;
|
|
}
|
|
|
|
private boolean unlock(byte[] password) {
|
|
execute('u', password);
|
|
return mError == NO_ERROR;
|
|
}
|
|
|
|
public boolean unlock(String password) {
|
|
return unlock(getBytes(password));
|
|
}
|
|
|
|
public boolean isEmpty() {
|
|
execute('z');
|
|
return mError == KEY_NOT_FOUND;
|
|
}
|
|
|
|
public int getLastError() {
|
|
return mError;
|
|
}
|
|
|
|
private ArrayList<byte[]> execute(int code, byte[]... parameters) {
|
|
mError = PROTOCOL_ERROR;
|
|
|
|
for (byte[] parameter : parameters) {
|
|
if (parameter == null || parameter.length > 65535) {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
LocalSocket socket = new LocalSocket();
|
|
try {
|
|
socket.connect(sAddress);
|
|
|
|
OutputStream out = socket.getOutputStream();
|
|
out.write(code);
|
|
for (byte[] parameter : parameters) {
|
|
out.write(parameter.length >> 8);
|
|
out.write(parameter.length);
|
|
out.write(parameter);
|
|
}
|
|
out.flush();
|
|
socket.shutdownOutput();
|
|
|
|
InputStream in = socket.getInputStream();
|
|
if ((code = in.read()) != NO_ERROR) {
|
|
if (code != -1) {
|
|
mError = code;
|
|
}
|
|
return null;
|
|
}
|
|
|
|
ArrayList<byte[]> values = new ArrayList<byte[]>();
|
|
while (true) {
|
|
int i, j;
|
|
if ((i = in.read()) == -1) {
|
|
break;
|
|
}
|
|
if ((j = in.read()) == -1) {
|
|
return null;
|
|
}
|
|
byte[] value = new byte[i << 8 | j];
|
|
for (i = 0; i < value.length; i += j) {
|
|
if ((j = in.read(value, i, value.length - i)) == -1) {
|
|
return null;
|
|
}
|
|
}
|
|
values.add(value);
|
|
}
|
|
mError = NO_ERROR;
|
|
return values;
|
|
} catch (IOException e) {
|
|
// ignore
|
|
} finally {
|
|
try {
|
|
socket.close();
|
|
} catch (IOException e) {}
|
|
}
|
|
return null;
|
|
}
|
|
|
|
private static byte[] getBytes(String string) {
|
|
return string.getBytes(Charsets.UTF_8);
|
|
}
|
|
|
|
private static String toString(byte[] bytes) {
|
|
return new String(bytes, Charsets.UTF_8);
|
|
}
|
|
}
|