diff options
author | Kenny Root <kroot@google.com> | 2012-02-15 15:04:01 -0800 |
---|---|---|
committer | Kenny Root <kroot@google.com> | 2012-02-15 15:55:21 -0800 |
commit | a87de87dcd36d9c7696587365aa3d1545e33c01b (patch) | |
tree | ff01477a7761363ff70eee6506138284a0546950 /cmds | |
parent | 5b1b57f0f540e8efe5bebdc219738d40ebfce5b9 (diff) | |
download | frameworks_native-a87de87dcd36d9c7696587365aa3d1545e33c01b.zip frameworks_native-a87de87dcd36d9c7696587365aa3d1545e33c01b.tar.gz frameworks_native-a87de87dcd36d9c7696587365aa3d1545e33c01b.tar.bz2 |
Move keystore to system/security repo
Move keystore to system/security at revision
a91203b08350b2fc7efda5b1eab39e7541476b3a
Change-Id: I7dbd625b864e9c63489b08e9fd28dfb22da81072
Diffstat (limited to 'cmds')
-rw-r--r-- | cmds/keystore/Android.mk | 32 | ||||
-rw-r--r-- | cmds/keystore/keystore.cpp | 810 | ||||
-rw-r--r-- | cmds/keystore/keystore.h | 43 | ||||
-rw-r--r-- | cmds/keystore/keystore_cli.cpp | 95 | ||||
-rw-r--r-- | cmds/keystore/keystore_get.h | 80 | ||||
-rwxr-xr-x | cmds/keystore/test-keystore | 273 |
6 files changed, 0 insertions, 1333 deletions
diff --git a/cmds/keystore/Android.mk b/cmds/keystore/Android.mk deleted file mode 100644 index 5a9b979..0000000 --- a/cmds/keystore/Android.mk +++ /dev/null @@ -1,32 +0,0 @@ -# -# 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. -# - -LOCAL_PATH:= $(call my-dir) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := keystore.cpp -LOCAL_C_INCLUDES := external/openssl/include -LOCAL_SHARED_LIBRARIES := libcutils libcrypto -LOCAL_MODULE:= keystore -include $(BUILD_EXECUTABLE) - -include $(CLEAR_VARS) -LOCAL_SRC_FILES := keystore_cli.cpp -LOCAL_C_INCLUDES := external/openssl/include -LOCAL_SHARED_LIBRARIES := libcutils libcrypto -LOCAL_MODULE:= keystore_cli -LOCAL_MODULE_TAGS := debug -include $(BUILD_EXECUTABLE) diff --git a/cmds/keystore/keystore.cpp b/cmds/keystore/keystore.cpp deleted file mode 100644 index 2c9cb35..0000000 --- a/cmds/keystore/keystore.cpp +++ /dev/null @@ -1,810 +0,0 @@ -/* - * 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. - */ - -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <unistd.h> -#include <signal.h> -#include <errno.h> -#include <dirent.h> -#include <fcntl.h> -#include <limits.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <sys/stat.h> -#include <sys/time.h> -#include <arpa/inet.h> - -#include <openssl/aes.h> -#include <openssl/evp.h> -#include <openssl/md5.h> - -#define LOG_TAG "keystore" -#include <cutils/log.h> -#include <cutils/sockets.h> -#include <private/android_filesystem_config.h> - -#include "keystore.h" - -/* KeyStore is a secured storage for key-value pairs. In this implementation, - * each file stores one key-value pair. Keys are encoded in file names, and - * values are encrypted with checksums. The encryption key is protected by a - * user-defined password. To keep things simple, buffers are always larger than - * the maximum space we needed, so boundary checks on buffers are omitted. */ - -#define KEY_SIZE ((NAME_MAX - 15) / 2) -#define VALUE_SIZE 32768 -#define PASSWORD_SIZE VALUE_SIZE - -struct Value { - int length; - uint8_t value[VALUE_SIZE]; -}; - -/* Here is the encoding of keys. This is necessary in order to allow arbitrary - * characters in keys. Characters in [0-~] are not encoded. Others are encoded - * into two bytes. The first byte is one of [+-.] which represents the first - * two bits of the character. The second byte encodes the rest of the bits into - * [0-o]. Therefore in the worst case the length of a key gets doubled. Note - * that Base64 cannot be used here due to the need of prefix match on keys. */ - -static int encode_key(char* out, uid_t uid, const Value* key) { - int n = snprintf(out, NAME_MAX, "%u_", uid); - out += n; - const uint8_t* in = key->value; - int length = key->length; - for (int i = length; i > 0; --i, ++in, ++out) { - if (*in >= '0' && *in <= '~') { - *out = *in; - } else { - *out = '+' + (*in >> 6); - *++out = '0' + (*in & 0x3F); - ++length; - } - } - *out = '\0'; - return n + length; -} - -static int decode_key(uint8_t* out, char* in, int length) { - for (int i = 0; i < length; ++i, ++in, ++out) { - if (*in >= '0' && *in <= '~') { - *out = *in; - } else { - *out = (*in - '+') << 6; - *out |= (*++in - '0') & 0x3F; - --length; - } - } - *out = '\0'; - return length; -} - -static size_t readFully(int fd, uint8_t* data, size_t size) { - size_t remaining = size; - while (remaining > 0) { - ssize_t n = TEMP_FAILURE_RETRY(read(fd, data, size)); - if (n == -1 || n == 0) { - return size-remaining; - } - data += n; - remaining -= n; - } - return size; -} - -static size_t writeFully(int fd, uint8_t* data, size_t size) { - size_t remaining = size; - while (remaining > 0) { - ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, size)); - if (n == -1 || n == 0) { - return size-remaining; - } - data += n; - remaining -= n; - } - return size; -} - -class Entropy { -public: - Entropy() : mRandom(-1) {} - ~Entropy() { - if (mRandom != -1) { - close(mRandom); - } - } - - bool open() { - const char* randomDevice = "/dev/urandom"; - mRandom = ::open(randomDevice, O_RDONLY); - if (mRandom == -1) { - ALOGE("open: %s: %s", randomDevice, strerror(errno)); - return false; - } - return true; - } - - bool generate_random_data(uint8_t* data, size_t size) { - return (readFully(mRandom, data, size) == size); - } - -private: - int mRandom; -}; - -/* Here is the file format. There are two parts in blob.value, the secret and - * the description. The secret is stored in ciphertext, and its original size - * can be found in blob.length. The description is stored after the secret in - * plaintext, and its size is specified in blob.info. The total size of the two - * parts must be no more than VALUE_SIZE bytes. The first three bytes of the - * file are reserved for future use and are always set to zero. Fields other - * than blob.info, blob.length, and blob.value are modified by encryptBlob() - * and decryptBlob(). Thus they should not be accessed from outside. */ - -struct __attribute__((packed)) blob { - uint8_t reserved[3]; - uint8_t info; - uint8_t vector[AES_BLOCK_SIZE]; - uint8_t encrypted[0]; - uint8_t digest[MD5_DIGEST_LENGTH]; - uint8_t digested[0]; - int32_t length; // in network byte order when encrypted - uint8_t value[VALUE_SIZE + AES_BLOCK_SIZE]; -}; - -class Blob { -public: - Blob(uint8_t* value, int32_t valueLength, uint8_t* info, uint8_t infoLength) { - mBlob.length = valueLength; - memcpy(mBlob.value, value, valueLength); - - mBlob.info = infoLength; - memcpy(mBlob.value + valueLength, info, infoLength); - } - - Blob(blob b) { - mBlob = b; - } - - Blob() {} - - uint8_t* getValue() { - return mBlob.value; - } - - int32_t getLength() { - return mBlob.length; - } - - uint8_t getInfo() { - return mBlob.info; - } - - ResponseCode encryptBlob(const char* filename, AES_KEY *aes_key, Entropy* entropy) { - if (!entropy->generate_random_data(mBlob.vector, AES_BLOCK_SIZE)) { - return SYSTEM_ERROR; - } - - // data includes the value and the value's length - size_t dataLength = mBlob.length + sizeof(mBlob.length); - // pad data to the AES_BLOCK_SIZE - size_t digestedLength = ((dataLength + AES_BLOCK_SIZE - 1) - / AES_BLOCK_SIZE * AES_BLOCK_SIZE); - // encrypted data includes the digest value - size_t encryptedLength = digestedLength + MD5_DIGEST_LENGTH; - // move info after space for padding - memmove(&mBlob.encrypted[encryptedLength], &mBlob.value[mBlob.length], mBlob.info); - // zero padding area - memset(mBlob.value + mBlob.length, 0, digestedLength - dataLength); - - mBlob.length = htonl(mBlob.length); - MD5(mBlob.digested, digestedLength, mBlob.digest); - - uint8_t vector[AES_BLOCK_SIZE]; - memcpy(vector, mBlob.vector, AES_BLOCK_SIZE); - AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, - aes_key, vector, AES_ENCRYPT); - - memset(mBlob.reserved, 0, sizeof(mBlob.reserved)); - size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob); - size_t fileLength = encryptedLength + headerLength + mBlob.info; - - const char* tmpFileName = ".tmp"; - int out = open(tmpFileName, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR); - if (out == -1) { - return SYSTEM_ERROR; - } - size_t writtenBytes = writeFully(out, (uint8_t*) &mBlob, fileLength); - if (close(out) != 0) { - return SYSTEM_ERROR; - } - if (writtenBytes != fileLength) { - unlink(tmpFileName); - return SYSTEM_ERROR; - } - return (rename(tmpFileName, filename) == 0) ? NO_ERROR : SYSTEM_ERROR; - } - - ResponseCode decryptBlob(const char* filename, AES_KEY *aes_key) { - int in = open(filename, O_RDONLY); - if (in == -1) { - return (errno == ENOENT) ? KEY_NOT_FOUND : SYSTEM_ERROR; - } - // fileLength may be less than sizeof(mBlob) since the in - // memory version has extra padding to tolerate rounding up to - // the AES_BLOCK_SIZE - size_t fileLength = readFully(in, (uint8_t*) &mBlob, sizeof(mBlob)); - if (close(in) != 0) { - return SYSTEM_ERROR; - } - size_t headerLength = (mBlob.encrypted - (uint8_t*) &mBlob); - if (fileLength < headerLength) { - return VALUE_CORRUPTED; - } - - ssize_t encryptedLength = fileLength - (headerLength + mBlob.info); - if (encryptedLength < 0 || encryptedLength % AES_BLOCK_SIZE != 0) { - return VALUE_CORRUPTED; - } - AES_cbc_encrypt(mBlob.encrypted, mBlob.encrypted, encryptedLength, aes_key, - mBlob.vector, AES_DECRYPT); - size_t digestedLength = encryptedLength - MD5_DIGEST_LENGTH; - uint8_t computedDigest[MD5_DIGEST_LENGTH]; - MD5(mBlob.digested, digestedLength, computedDigest); - if (memcmp(mBlob.digest, computedDigest, MD5_DIGEST_LENGTH) != 0) { - return VALUE_CORRUPTED; - } - - ssize_t maxValueLength = digestedLength - sizeof(mBlob.length); - mBlob.length = ntohl(mBlob.length); - if (mBlob.length < 0 || mBlob.length > maxValueLength) { - return VALUE_CORRUPTED; - } - if (mBlob.info != 0) { - // move info from after padding to after data - memmove(&mBlob.value[mBlob.length], &mBlob.value[maxValueLength], mBlob.info); - } - return NO_ERROR; - } - -private: - struct blob mBlob; -}; - -class KeyStore { -public: - KeyStore(Entropy* entropy) : mEntropy(entropy), mRetry(MAX_RETRY) { - if (access(MASTER_KEY_FILE, R_OK) == 0) { - setState(STATE_LOCKED); - } else { - setState(STATE_UNINITIALIZED); - } - } - - State getState() { - return mState; - } - - int8_t getRetry() { - return mRetry; - } - - ResponseCode initialize(Value* pw) { - if (!generateMasterKey()) { - return SYSTEM_ERROR; - } - ResponseCode response = writeMasterKey(pw); - if (response != NO_ERROR) { - return response; - } - setupMasterKeys(); - return NO_ERROR; - } - - ResponseCode writeMasterKey(Value* pw) { - uint8_t passwordKey[MASTER_KEY_SIZE_BYTES]; - generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, mSalt); - AES_KEY passwordAesKey; - AES_set_encrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey); - Blob masterKeyBlob(mMasterKey, sizeof(mMasterKey), mSalt, sizeof(mSalt)); - return masterKeyBlob.encryptBlob(MASTER_KEY_FILE, &passwordAesKey, mEntropy); - } - - ResponseCode readMasterKey(Value* pw) { - int in = open(MASTER_KEY_FILE, O_RDONLY); - if (in == -1) { - return SYSTEM_ERROR; - } - - // we read the raw blob to just to get the salt to generate - // the AES key, then we create the Blob to use with decryptBlob - blob rawBlob; - size_t length = readFully(in, (uint8_t*) &rawBlob, sizeof(rawBlob)); - if (close(in) != 0) { - return SYSTEM_ERROR; - } - // find salt at EOF if present, otherwise we have an old file - uint8_t* salt; - if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) { - salt = (uint8_t*) &rawBlob + length - SALT_SIZE; - } else { - salt = NULL; - } - uint8_t passwordKey[MASTER_KEY_SIZE_BYTES]; - generateKeyFromPassword(passwordKey, MASTER_KEY_SIZE_BYTES, pw, salt); - AES_KEY passwordAesKey; - AES_set_decrypt_key(passwordKey, MASTER_KEY_SIZE_BITS, &passwordAesKey); - Blob masterKeyBlob(rawBlob); - ResponseCode response = masterKeyBlob.decryptBlob(MASTER_KEY_FILE, &passwordAesKey); - if (response == SYSTEM_ERROR) { - return SYSTEM_ERROR; - } - if (response == NO_ERROR && masterKeyBlob.getLength() == MASTER_KEY_SIZE_BYTES) { - // if salt was missing, generate one and write a new master key file with the salt. - if (salt == NULL) { - if (!generateSalt()) { - return SYSTEM_ERROR; - } - response = writeMasterKey(pw); - } - if (response == NO_ERROR) { - memcpy(mMasterKey, masterKeyBlob.getValue(), MASTER_KEY_SIZE_BYTES); - setupMasterKeys(); - } - return response; - } - if (mRetry <= 0) { - reset(); - return UNINITIALIZED; - } - --mRetry; - switch (mRetry) { - case 0: return WRONG_PASSWORD_0; - case 1: return WRONG_PASSWORD_1; - case 2: return WRONG_PASSWORD_2; - case 3: return WRONG_PASSWORD_3; - default: return WRONG_PASSWORD_3; - } - } - - bool reset() { - clearMasterKeys(); - setState(STATE_UNINITIALIZED); - - DIR* dir = opendir("."); - struct dirent* file; - - if (!dir) { - return false; - } - while ((file = readdir(dir)) != NULL) { - unlink(file->d_name); - } - closedir(dir); - return true; - } - - bool isEmpty() { - DIR* dir = opendir("."); - struct dirent* file; - if (!dir) { - return true; - } - bool result = true; - while ((file = readdir(dir)) != NULL) { - if (isKeyFile(file->d_name)) { - result = false; - break; - } - } - closedir(dir); - return result; - } - - void lock() { - clearMasterKeys(); - setState(STATE_LOCKED); - } - - ResponseCode get(const char* filename, Blob* keyBlob) { - return keyBlob->decryptBlob(filename, &mMasterKeyDecryption); - } - - ResponseCode put(const char* filename, Blob* keyBlob) { - return keyBlob->encryptBlob(filename, &mMasterKeyEncryption, mEntropy); - } - -private: - static const char* MASTER_KEY_FILE; - static const int MASTER_KEY_SIZE_BYTES = 16; - static const int MASTER_KEY_SIZE_BITS = MASTER_KEY_SIZE_BYTES * 8; - - static const int MAX_RETRY = 4; - static const size_t SALT_SIZE = 16; - - Entropy* mEntropy; - - State mState; - int8_t mRetry; - - uint8_t mMasterKey[MASTER_KEY_SIZE_BYTES]; - uint8_t mSalt[SALT_SIZE]; - - AES_KEY mMasterKeyEncryption; - AES_KEY mMasterKeyDecryption; - - void setState(State state) { - mState = state; - if (mState == STATE_NO_ERROR || mState == STATE_UNINITIALIZED) { - mRetry = MAX_RETRY; - } - } - - bool generateSalt() { - return mEntropy->generate_random_data(mSalt, sizeof(mSalt)); - } - - bool generateMasterKey() { - if (!mEntropy->generate_random_data(mMasterKey, sizeof(mMasterKey))) { - return false; - } - if (!generateSalt()) { - return false; - } - return true; - } - - void setupMasterKeys() { - AES_set_encrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyEncryption); - AES_set_decrypt_key(mMasterKey, MASTER_KEY_SIZE_BITS, &mMasterKeyDecryption); - setState(STATE_NO_ERROR); - } - - void clearMasterKeys() { - memset(mMasterKey, 0, sizeof(mMasterKey)); - memset(mSalt, 0, sizeof(mSalt)); - memset(&mMasterKeyEncryption, 0, sizeof(mMasterKeyEncryption)); - memset(&mMasterKeyDecryption, 0, sizeof(mMasterKeyDecryption)); - } - - static void generateKeyFromPassword(uint8_t* key, ssize_t keySize, Value* pw, uint8_t* salt) { - size_t saltSize; - if (salt != NULL) { - saltSize = SALT_SIZE; - } else { - // pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found - salt = (uint8_t*) "keystore"; - // sizeof = 9, not strlen = 8 - saltSize = sizeof("keystore"); - } - PKCS5_PBKDF2_HMAC_SHA1((char*) pw->value, pw->length, salt, saltSize, 8192, keySize, key); - } - - static bool isKeyFile(const char* filename) { - return ((strcmp(filename, MASTER_KEY_FILE) != 0) - && (strcmp(filename, ".") != 0) - && (strcmp(filename, "..") != 0)); - } -}; - -const char* KeyStore::MASTER_KEY_FILE = ".masterkey"; - -/* Here is the protocol used in both requests and responses: - * code [length_1 message_1 ... length_n message_n] end-of-file - * where code is one byte long and lengths are unsigned 16-bit integers in - * network order. Thus the maximum length of a message is 65535 bytes. */ - -static int recv_code(int sock, int8_t* code) { - return recv(sock, code, 1, 0) == 1; -} - -static int recv_message(int sock, uint8_t* message, int length) { - uint8_t bytes[2]; - if (recv(sock, &bytes[0], 1, 0) != 1 || - recv(sock, &bytes[1], 1, 0) != 1) { - return -1; - } else { - int offset = bytes[0] << 8 | bytes[1]; - if (length < offset) { - return -1; - } - length = offset; - offset = 0; - while (offset < length) { - int n = recv(sock, &message[offset], length - offset, 0); - if (n <= 0) { - return -1; - } - offset += n; - } - } - return length; -} - -static int recv_end_of_file(int sock) { - uint8_t byte; - return recv(sock, &byte, 1, 0) == 0; -} - -static void send_code(int sock, int8_t code) { - send(sock, &code, 1, 0); -} - -static void send_message(int sock, uint8_t* message, int length) { - uint16_t bytes = htons(length); - send(sock, &bytes, 2, 0); - send(sock, message, length, 0); -} - -/* Here are the actions. Each of them is a function without arguments. All - * information is defined in global variables, which are set properly before - * performing an action. The number of parameters required by each action is - * fixed and defined in a table. If the return value of an action is positive, - * it will be treated as a response code and transmitted to the client. Note - * that the lengths of parameters are checked when they are received, so - * boundary checks on parameters are omitted. */ - -static const ResponseCode NO_ERROR_RESPONSE_CODE_SENT = (ResponseCode) 0; - -static ResponseCode test(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { - return (ResponseCode) keyStore->getState(); -} - -static ResponseCode get(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { - char filename[NAME_MAX]; - encode_key(filename, uid, keyName); - Blob keyBlob; - ResponseCode responseCode = keyStore->get(filename, &keyBlob); - if (responseCode != NO_ERROR) { - return responseCode; - } - send_code(sock, NO_ERROR); - send_message(sock, keyBlob.getValue(), keyBlob.getLength()); - return NO_ERROR_RESPONSE_CODE_SENT; -} - -static ResponseCode insert(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value* val) { - char filename[NAME_MAX]; - encode_key(filename, uid, keyName); - Blob keyBlob(val->value, val->length, NULL, 0); - return keyStore->put(filename, &keyBlob); -} - -static ResponseCode del(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { - char filename[NAME_MAX]; - encode_key(filename, uid, keyName); - return (unlink(filename) && errno != ENOENT) ? SYSTEM_ERROR : NO_ERROR; -} - -static ResponseCode exist(KeyStore* keyStore, int sock, uid_t uid, Value* keyName, Value*) { - char filename[NAME_MAX]; - encode_key(filename, uid, keyName); - if (access(filename, R_OK) == -1) { - return (errno != ENOENT) ? SYSTEM_ERROR : KEY_NOT_FOUND; - } - return NO_ERROR; -} - -static ResponseCode saw(KeyStore* keyStore, int sock, uid_t uid, Value* keyPrefix, Value*) { - DIR* dir = opendir("."); - if (!dir) { - return SYSTEM_ERROR; - } - char filename[NAME_MAX]; - int n = encode_key(filename, uid, keyPrefix); - send_code(sock, NO_ERROR); - - struct dirent* file; - while ((file = readdir(dir)) != NULL) { - if (!strncmp(filename, file->d_name, n)) { - char* p = &file->d_name[n]; - keyPrefix->length = decode_key(keyPrefix->value, p, strlen(p)); - send_message(sock, keyPrefix->value, keyPrefix->length); - } - } - closedir(dir); - return NO_ERROR_RESPONSE_CODE_SENT; -} - -static ResponseCode reset(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { - return keyStore->reset() ? NO_ERROR : SYSTEM_ERROR; -} - -/* Here is the history. To improve the security, the parameters to generate the - * master key has been changed. To make a seamless transition, we update the - * file using the same password when the user unlock it for the first time. If - * any thing goes wrong during the transition, the new file will not overwrite - * the old one. This avoids permanent damages of the existing data. */ - -static ResponseCode password(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value*) { - switch (keyStore->getState()) { - case STATE_UNINITIALIZED: { - // generate master key, encrypt with password, write to file, initialize mMasterKey*. - return keyStore->initialize(pw); - } - case STATE_NO_ERROR: { - // rewrite master key with new password. - return keyStore->writeMasterKey(pw); - } - case STATE_LOCKED: { - // read master key, decrypt with password, initialize mMasterKey*. - return keyStore->readMasterKey(pw); - } - } - return SYSTEM_ERROR; -} - -static ResponseCode lock(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { - keyStore->lock(); - return NO_ERROR; -} - -static ResponseCode unlock(KeyStore* keyStore, int sock, uid_t uid, Value* pw, Value* unused) { - return password(keyStore, sock, uid, pw, unused); -} - -static ResponseCode zero(KeyStore* keyStore, int sock, uid_t uid, Value*, Value*) { - return keyStore->isEmpty() ? KEY_NOT_FOUND : NO_ERROR; -} - -/* Here are the permissions, actions, users, and the main function. */ - -enum perm { - TEST = 1, - GET = 2, - INSERT = 4, - DELETE = 8, - EXIST = 16, - SAW = 32, - RESET = 64, - PASSWORD = 128, - LOCK = 256, - UNLOCK = 512, - ZERO = 1024, -}; - -static const int MAX_PARAM = 2; - -static const State STATE_ANY = (State) 0; - -static struct action { - ResponseCode (*run)(KeyStore* keyStore, int sock, uid_t uid, Value* param1, Value* param2); - int8_t code; - State state; - uint32_t perm; - int lengths[MAX_PARAM]; -} actions[] = { - {test, 't', STATE_ANY, TEST, {0, 0}}, - {get, 'g', STATE_NO_ERROR, GET, {KEY_SIZE, 0}}, - {insert, 'i', STATE_NO_ERROR, INSERT, {KEY_SIZE, VALUE_SIZE}}, - {del, 'd', STATE_ANY, DELETE, {KEY_SIZE, 0}}, - {exist, 'e', STATE_ANY, EXIST, {KEY_SIZE, 0}}, - {saw, 's', STATE_ANY, SAW, {KEY_SIZE, 0}}, - {reset, 'r', STATE_ANY, RESET, {0, 0}}, - {password, 'p', STATE_ANY, PASSWORD, {PASSWORD_SIZE, 0}}, - {lock, 'l', STATE_NO_ERROR, LOCK, {0, 0}}, - {unlock, 'u', STATE_LOCKED, UNLOCK, {PASSWORD_SIZE, 0}}, - {zero, 'z', STATE_ANY, ZERO, {0, 0}}, - {NULL, 0 , STATE_ANY, 0, {0, 0}}, -}; - -static struct user { - uid_t uid; - uid_t euid; - uint32_t perms; -} users[] = { - {AID_SYSTEM, ~0, ~0}, - {AID_VPN, AID_SYSTEM, GET}, - {AID_WIFI, AID_SYSTEM, GET}, - {AID_ROOT, AID_SYSTEM, GET}, - {~0, ~0, TEST | GET | INSERT | DELETE | EXIST | SAW}, -}; - -static ResponseCode process(KeyStore* keyStore, int sock, uid_t uid, int8_t code) { - struct user* user = users; - struct action* action = actions; - int i; - - while (~user->uid && user->uid != uid) { - ++user; - } - while (action->code && action->code != code) { - ++action; - } - if (!action->code) { - return UNDEFINED_ACTION; - } - if (!(action->perm & user->perms)) { - return PERMISSION_DENIED; - } - if (action->state != STATE_ANY && action->state != keyStore->getState()) { - return (ResponseCode) keyStore->getState(); - } - if (~user->euid) { - uid = user->euid; - } - Value params[MAX_PARAM]; - for (i = 0; i < MAX_PARAM && action->lengths[i] != 0; ++i) { - params[i].length = recv_message(sock, params[i].value, action->lengths[i]); - if (params[i].length < 0) { - return PROTOCOL_ERROR; - } - } - if (!recv_end_of_file(sock)) { - return PROTOCOL_ERROR; - } - return action->run(keyStore, sock, uid, ¶ms[0], ¶ms[1]); -} - -int main(int argc, char* argv[]) { - int controlSocket = android_get_control_socket("keystore"); - if (argc < 2) { - ALOGE("A directory must be specified!"); - return 1; - } - if (chdir(argv[1]) == -1) { - ALOGE("chdir: %s: %s", argv[1], strerror(errno)); - return 1; - } - - Entropy entropy; - if (!entropy.open()) { - return 1; - } - if (listen(controlSocket, 3) == -1) { - ALOGE("listen: %s", strerror(errno)); - return 1; - } - - signal(SIGPIPE, SIG_IGN); - - KeyStore keyStore(&entropy); - int sock; - while ((sock = accept(controlSocket, NULL, 0)) != -1) { - struct timeval tv; - tv.tv_sec = 3; - setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv)); - setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv)); - - struct ucred cred; - socklen_t size = sizeof(cred); - int credResult = getsockopt(sock, SOL_SOCKET, SO_PEERCRED, &cred, &size); - if (credResult != 0) { - ALOGW("getsockopt: %s", strerror(errno)); - } else { - int8_t request; - if (recv_code(sock, &request)) { - State old_state = keyStore.getState(); - ResponseCode response = process(&keyStore, sock, cred.uid, request); - if (response == NO_ERROR_RESPONSE_CODE_SENT) { - response = NO_ERROR; - } else { - send_code(sock, response); - } - ALOGI("uid: %d action: %c -> %d state: %d -> %d retry: %d", - cred.uid, - request, response, - old_state, keyStore.getState(), - keyStore.getRetry()); - } - } - close(sock); - } - ALOGE("accept: %s", strerror(errno)); - return 1; -} diff --git a/cmds/keystore/keystore.h b/cmds/keystore/keystore.h deleted file mode 100644 index 5ae3d24..0000000 --- a/cmds/keystore/keystore.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * 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. - */ - -#ifndef __KEYSTORE_H__ -#define __KEYSTORE_H__ - -// note state values overlap with ResponseCode for the purposes of the state() API -enum State { - STATE_NO_ERROR = 1, - STATE_LOCKED = 2, - STATE_UNINITIALIZED = 3, -}; - -enum ResponseCode { - NO_ERROR = STATE_NO_ERROR, // 1 - LOCKED = STATE_LOCKED, // 2 - UNINITIALIZED = STATE_UNINITIALIZED, // 3 - SYSTEM_ERROR = 4, - PROTOCOL_ERROR = 5, - PERMISSION_DENIED = 6, - KEY_NOT_FOUND = 7, - VALUE_CORRUPTED = 8, - UNDEFINED_ACTION = 9, - WRONG_PASSWORD_0 = 10, - WRONG_PASSWORD_1 = 11, - WRONG_PASSWORD_2 = 12, - WRONG_PASSWORD_3 = 13, // MAX_RETRY = 4 -}; - -#endif diff --git a/cmds/keystore/keystore_cli.cpp b/cmds/keystore/keystore_cli.cpp deleted file mode 100644 index dcd3bcb..0000000 --- a/cmds/keystore/keystore_cli.cpp +++ /dev/null @@ -1,95 +0,0 @@ -/* - * 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. - */ - -#include <stdio.h> -#include <stdint.h> -#include <string.h> -#include <sys/types.h> -#include <sys/socket.h> - -#include <cutils/sockets.h> - -#include "keystore.h" - -static const char* responses[] = { - NULL, - /* [NO_ERROR] = */ "No error", - /* [LOCKED] = */ "Locked", - /* [UNINITIALIZED] = */ "Uninitialized", - /* [SYSTEM_ERROR] = */ "System error", - /* [PROTOCOL_ERROR] = */ "Protocol error", - /* [PERMISSION_DENIED] = */ "Permission denied", - /* [KEY_NOT_FOUND] = */ "Key not found", - /* [VALUE_CORRUPTED] = */ "Value corrupted", - /* [UNDEFINED_ACTION] = */ "Undefined action", - /* [WRONG_PASSWORD] = */ "Wrong password (last chance)", - /* [WRONG_PASSWORD + 1] = */ "Wrong password (2 tries left)", - /* [WRONG_PASSWORD + 2] = */ "Wrong password (3 tries left)", - /* [WRONG_PASSWORD + 3] = */ "Wrong password (4 tries left)", -}; - -int main(int argc, char* argv[]) -{ - if (argc < 2) { - printf("Usage: %s action [parameter ...]\n", argv[0]); - return 0; - } - - int sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_STREAM); - if (sock == -1) { - puts("Failed to connect"); - return 1; - } - - send(sock, argv[1], 1, 0); - uint8_t bytes[65536]; - for (int i = 2; i < argc; ++i) { - uint16_t length = strlen(argv[i]); - bytes[0] = length >> 8; - bytes[1] = length; - send(sock, &bytes, 2, 0); - send(sock, argv[i], length, 0); - } - shutdown(sock, SHUT_WR); - - uint8_t code; - if (recv(sock, &code, 1, 0) != 1) { - puts("Failed to receive"); - return 1; - } - printf("%d %s\n", code , responses[code] ? responses[code] : "Unknown"); - int i; - while ((i = recv(sock, &bytes[0], 1, 0)) == 1) { - int length; - int offset; - if ((i = recv(sock, &bytes[1], 1, 0)) != 1) { - puts("Failed to receive"); - return 1; - } - length = bytes[0] << 8 | bytes[1]; - for (offset = 0; offset < length; offset += i) { - i = recv(sock, &bytes[offset], length - offset, 0); - if (i <= 0) { - puts("Failed to receive"); - return 1; - } - } - fwrite(bytes, 1, length, stdout); - puts(""); - } - return 0; -} diff --git a/cmds/keystore/keystore_get.h b/cmds/keystore/keystore_get.h deleted file mode 100644 index 4b4923e..0000000 --- a/cmds/keystore/keystore_get.h +++ /dev/null @@ -1,80 +0,0 @@ -/* - * 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. - */ - -#ifndef __KEYSTORE_GET_H__ -#define __KEYSTORE_GET_H__ - -#include <stdio.h> -#include <stdint.h> -#include <unistd.h> -#include <sys/types.h> -#include <sys/socket.h> - -#include <cutils/sockets.h> - -#define KEYSTORE_MESSAGE_SIZE 65535 - -#ifdef __cplusplus -extern "C" { -#endif - -/* This function is provided for native components to get values from keystore. - * Users are required to link against libcutils. Keys and values are 8-bit safe. - * The first two arguments are the key and its length. The third argument - * specifies the buffer to store the retrieved value, which must be an array of - * KEYSTORE_MESSAGE_SIZE bytes. This function returns the length of the value or - * -1 if an error happens. */ -static int keystore_get(const char *key, int length, char *value) -{ - uint8_t bytes[2] = {length >> 8, length}; - uint8_t code = 'g'; - int sock; - - if (length < 0 || length > KEYSTORE_MESSAGE_SIZE) { - return -1; - } - sock = socket_local_client("keystore", ANDROID_SOCKET_NAMESPACE_RESERVED, - SOCK_STREAM); - if (sock == -1) { - return -1; - } - if (send(sock, &code, 1, 0) == 1 && send(sock, bytes, 2, 0) == 2 && - send(sock, key, length, 0) == length && shutdown(sock, SHUT_WR) == 0 && - recv(sock, &code, 1, 0) == 1 && code == /* NO_ERROR */ 1 && - recv(sock, &bytes[0], 1, 0) == 1 && recv(sock, &bytes[1], 1, 0) == 1) { - int offset = 0; - length = bytes[0] << 8 | bytes[1]; - while (offset < length) { - int n = recv(sock, &value[offset], length - offset, 0); - if (n <= 0) { - length = -1; - break; - } - offset += n; - } - } else { - length = -1; - } - - close(sock); - return length; -} - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/cmds/keystore/test-keystore b/cmds/keystore/test-keystore deleted file mode 100755 index 3be51b3..0000000 --- a/cmds/keystore/test-keystore +++ /dev/null @@ -1,273 +0,0 @@ -#!/bin/bash -# -# Copyright 2011, 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. - -set -e - -prefix=$0 -log_file=$prefix.log -baseline_file=$prefix.baseline - -function cleanup_output() { - rm -f $log_file - rm -f $baseline_file -} - -function log() { - echo "$@" - append $log_file \# "$@" - append $baseline_file \# "$@" -} - -function expect() { - append $baseline_file "$@" -} - -function append() { - declare -r file=$1 - shift - echo "$@" >> $file -} - -function run() { - # strip out carriage returns from adb - # strip out date/time from ls -l - "$@" | tr --delete '\r' | sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} +[0-9]{1,2}:[0-9]{2} //' >> $log_file -} - -function keystore() { - declare -r user=$1 - shift - run adb shell su $user keystore_cli "$@" -} - -function list_keystore_directory() { - run adb shell ls -al /data/misc/keystore -} - -function compare() { - log "comparing $baseline_file and $log_file" - diff $baseline_file $log_file || (log $tag FAILED && exit 1) -} - -function test_basic() { - - # - # reset - # - log "reset keystore as system user" - keystore system r - expect "1 No error" - list_keystore_directory - - # - # basic tests as system/root - # - log "root does not have permission to run test" - keystore root t - expect "6 Permission denied" - - log "but system user does" - keystore system t - expect "3 Uninitialized" - list_keystore_directory - - log "password is now bar" - keystore system p bar - expect "1 No error" - list_keystore_directory - expect "-rw------- keystore keystore 84 .masterkey" - - log "no error implies initialized and unlocked" - keystore system t - expect "1 No error" - - log "saw with no argument" - keystore system s - expect "5 Protocol error" - - log "saw nothing" - keystore system s "" - expect "1 No error" - - log "add key baz" - keystore system i baz quux - expect "1 No error" - - log "1000 is uid of system" - list_keystore_directory - expect "-rw------- keystore keystore 84 .masterkey" - expect "-rw------- keystore keystore 52 1000_baz" - - log "saw baz" - keystore system s "" - expect "1 No error" - expect "baz" - - log "get baz" - keystore system g baz - expect "1 No error" - expect "quux" - - log "root can read system user keys (as can wifi or vpn users)" - keystore root g baz - expect "1 No error" - expect "quux" - - # - # app user tests - # - - # app_0 has uid 10000, as seen below - log "other uses cannot see the system keys" - keystore app_0 g baz - expect "7 Key not found" - - log "app user cannot use reset, password, lock, unlock" - keystore app_0 r - expect "6 Permission denied" - keystore app_0 p - expect "6 Permission denied" - keystore app_0 l - expect "6 Permission denied" - keystore app_0 u - expect "6 Permission denied" - - log "install app_0 key" - keystore app_0 i 0x deadbeef - expect 1 No error - list_keystore_directory - expect "-rw------- keystore keystore 84 .masterkey" - expect "-rw------- keystore keystore 52 10000_0x" - expect "-rw------- keystore keystore 52 1000_baz" - - log "get with no argument" - keystore app_0 g - expect "5 Protocol error" - - keystore app_0 g 0x - expect "1 No error" - expect "deadbeef" - - keystore app_0 i fred barney - expect "1 No error" - - keystore app_0 s "" - expect "1 No error" - expect "0x" - expect "fred" - - log "note that saw returns the suffix of prefix matches" - keystore app_0 s fr # fred - expect "1 No error" - expect "ed" # fred - - # - # lock tests - # - log "lock the store as system" - keystore system l - expect "1 No error" - keystore system t - expect "2 Locked" - - log "saw works while locked" - keystore app_0 s "" - expect "1 No error" - expect "0x" - expect "fred" - - log "...but cannot read keys..." - keystore app_0 g 0x - expect "2 Locked" - - log "...but they can be deleted." - keystore app_0 e 0x - expect "1 No error" - keystore app_0 d 0x - expect "1 No error" - keystore app_0 e 0x - expect "7 Key not found" - - # - # password - # - log "wrong password" - keystore system u foo - expect "13 Wrong password (4 tries left)" - log "right password" - keystore system u bar - expect "1 No error" - - log "make the password foo" - keystore system p foo - expect "1 No error" - - # - # final reset - # - log "reset wipes everything for all users" - keystore system r - expect "1 No error" - list_keystore_directory - - keystore system t - expect "3 Uninitialized" - -} - -function test_4599735() { - # http://b/4599735 - log "start regression test for b/4599735" - keystore system r - expect "1 No error" - - keystore system p foo - expect "1 No error" - - keystore system i baz quux - expect "1 No error" - - keystore root g baz - expect "1 No error" - expect "quux" - - keystore system l - expect "1 No error" - - keystore system p foo - expect "1 No error" - - log "after unlock, regression led to result of '8 Value corrupted'" - keystore root g baz - expect "1 No error" - expect "quux" - - keystore system r - expect "1 No error" - log "end regression test for b/4599735" -} - -function main() { - cleanup_output - log $tag START - test_basic - test_4599735 - compare - log $tag PASSED - cleanup_output -} - -main |