summaryrefslogtreecommitdiffstats
path: root/cmds/keystore/keymgmt.c
diff options
context:
space:
mode:
Diffstat (limited to 'cmds/keystore/keymgmt.c')
-rw-r--r--cmds/keystore/keymgmt.c365
1 files changed, 365 insertions, 0 deletions
diff --git a/cmds/keystore/keymgmt.c b/cmds/keystore/keymgmt.c
new file mode 100644
index 0000000..e4102a9
--- /dev/null
+++ b/cmds/keystore/keymgmt.c
@@ -0,0 +1,365 @@
+/*
+** Copyright 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 <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <openssl/aes.h>
+#include <openssl/evp.h>
+#include <cutils/log.h>
+
+#include "common.h"
+#include "keymgmt.h"
+
+static int retry_count = 0;
+static unsigned char iv[IV_LEN];
+static KEYSTORE_STATE state = BOOTUP;
+static AES_KEY encryptKey, decryptKey;
+
+inline void unlock_keystore(unsigned char *master_key)
+{
+ AES_set_encrypt_key(master_key, AES_KEY_LEN, &encryptKey);
+ AES_set_decrypt_key(master_key, AES_KEY_LEN, &decryptKey);
+ memset(master_key, 0, sizeof(master_key));
+ state = UNLOCKED;
+}
+
+inline void lock_keystore()
+{
+ memset(&encryptKey, 0 , sizeof(AES_KEY));
+ memset(&decryptKey, 0 , sizeof(AES_KEY));
+ state = LOCKED;
+}
+
+inline void get_encrypt_key(char *passwd, AES_KEY *key)
+{
+ unsigned char user_key[USER_KEY_LEN];
+ gen_key(passwd, user_key, USER_KEY_LEN);
+ AES_set_encrypt_key(user_key, AES_KEY_LEN, key);
+}
+
+inline void get_decrypt_key(char *passwd, AES_KEY *key)
+{
+ unsigned char user_key[USER_KEY_LEN];
+ gen_key(passwd, user_key, USER_KEY_LEN);
+ AES_set_decrypt_key(user_key, AES_KEY_LEN, key);
+}
+
+static int gen_random_blob(unsigned char *key, int size)
+{
+ int ret = 0;
+ int fd = open("/dev/urandom", O_RDONLY);
+ if (fd == -1) return -1;
+ if (read(fd, key, size) != size) ret = -1;
+ close(fd);
+ return ret;
+}
+
+static int encrypt_n_save(AES_KEY *enc_key, DATA_BLOB *blob,
+ const char *keyfile)
+{
+ int size, fd, ret = -1;
+ unsigned char enc_blob[MAX_BLOB_LEN];
+
+ char tmpfile[KEYFILE_LEN];
+ strcpy(tmpfile, keyfile);
+ strcat(tmpfile, ".tmp");
+
+ // prepare the blob
+ memcpy(blob->iv, iv, IV_LEN);
+ blob->blob_size = get_blob_size(blob);
+ memcpy(enc_blob, blob->blob, blob->blob_size);
+ AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char *)blob->blob,
+ blob->blob_size, enc_key, iv, AES_ENCRYPT);
+ // write to keyfile
+ size = data_blob_size(blob);
+ if ((fd = open(tmpfile, O_CREAT|O_RDWR)) == -1) return -1;
+ if (write(fd, blob, size) == size) ret = 0;
+ close(fd);
+ if (!ret) {
+ unlink(keyfile);
+ rename(tmpfile, keyfile);
+ chmod(keyfile, 0440);
+ }
+ return ret;
+}
+
+static int load_n_decrypt(const char *keyname, const char *keyfile,
+ AES_KEY *key, DATA_BLOB *blob)
+{
+ int fd, ret = -1;
+ if ((fd = open(keyfile, O_RDONLY)) == -1) return -1;
+ // get the encrypted blob and iv
+ if ((read(fd, blob->iv, sizeof(blob->iv)) != sizeof(blob->iv)) ||
+ (read(fd, &blob->blob_size, sizeof(uint32_t)) != sizeof(uint32_t)) ||
+ (blob->blob_size > MAX_BLOB_LEN)) {
+ goto err;
+ } else {
+ unsigned char enc_blob[MAX_BLOB_LEN];
+ if (read(fd, enc_blob, blob->blob_size) !=
+ (int) blob->blob_size) goto err;
+ // decrypt the blob
+ AES_cbc_encrypt((unsigned char *)enc_blob, (unsigned char*)blob->blob,
+ blob->blob_size, key, blob->iv, AES_DECRYPT);
+ if (strcmp(keyname, (char*)blob->keyname) == 0) ret = 0;
+ }
+err:
+ close(fd);
+ return ret;
+}
+
+static int store_master_key(char *upasswd, unsigned char *master_key)
+{
+ AES_KEY key;
+ DATA_BLOB blob;
+
+ // prepare the blob
+ strlcpy(blob.keyname, MASTER_KEY_TAG, USER_KEY_LEN);
+ blob.value_size = USER_KEY_LEN;
+ memcpy((void*)blob.value, (const void*)master_key, USER_KEY_LEN);
+
+ // generate the encryption key
+ get_encrypt_key(upasswd, &key);
+ return encrypt_n_save(&key, &blob, MASTER_KEY);
+}
+
+static int get_master_key(char *upasswd, unsigned char *master_key)
+{
+ AES_KEY key;
+ int size, ret = 0;
+ DATA_BLOB blob;
+
+ get_decrypt_key(upasswd, &key);
+ ret = load_n_decrypt(MASTER_KEY_TAG, MASTER_KEY, &key, &blob);
+ if (!ret) memcpy(master_key, blob.value, blob.value_size);
+ return ret;
+}
+
+static int create_master_key(char *upasswd)
+{
+ int ret;
+ unsigned char mpasswd[AES_KEY_LEN];
+ unsigned char master_key[USER_KEY_LEN];
+
+ gen_random_blob(mpasswd, AES_KEY_LEN);
+ gen_key((char*)mpasswd, master_key, USER_KEY_LEN);
+ if ((ret = store_master_key(upasswd, master_key)) == 0) {
+ unlock_keystore(master_key);
+ }
+ memset(master_key, 0, USER_KEY_LEN);
+ memset(mpasswd, 0, AES_KEY_LEN);
+
+ return ret;
+}
+
+static int change_passwd(char *data)
+{
+ unsigned char master_key[USER_KEY_LEN];
+ char *old_pass, *new_pass = NULL, *p, *delimiter=" ";
+ int ret, count = 0;
+ char *context = NULL;
+
+ old_pass = p = strtok_r(data, delimiter, &context);
+ while (p != NULL) {
+ count++;
+ new_pass = p;
+ p = strtok_r(NULL, delimiter, &context);
+ }
+ if (count != 2) return -1;
+ if ((ret = get_master_key(old_pass, master_key)) == 0) {
+ ret = store_master_key(new_pass, master_key);
+ retry_count = 0;
+ } else {
+ ret = MAX_RETRY_COUNT - ++retry_count;
+ if (ret == 0) {
+ retry_count = 0;
+ LOGE("passwd:reach max retry count, reset the keystore now.");
+ reset_keystore();
+ return -1;
+ }
+
+ }
+ return ret;
+}
+
+int remove_key(const char *namespace, const char *keyname)
+{
+ char keyfile[KEYFILE_LEN];
+
+ if (state != UNLOCKED) return -state;
+ sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+ return unlink(keyfile);
+}
+
+int put_key(const char *namespace, const char *keyname,
+ unsigned char *data, int size)
+{
+ DATA_BLOB blob;
+ uint32_t real_size;
+ char keyfile[KEYFILE_LEN];
+
+ if (state != UNLOCKED) {
+ LOGE("Can not store key with current state %d\n", state);
+ return -state;
+ }
+ sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+ // flatten the args
+ strcpy(blob.keyname, keyname);
+ blob.value_size = size;
+ memcpy(blob.value, data, size);
+ return encrypt_n_save(&encryptKey, &blob, keyfile);
+}
+
+int get_key(const char *namespace, const char *keyname,
+ unsigned char *data, int *size)
+{
+ int ret;
+ DATA_BLOB blob;
+ uint32_t blob_size;
+ char keyfile[KEYFILE_LEN];
+
+ if (state != UNLOCKED) {
+ LOGE("Can not retrieve key value with current state %d\n", state);
+ return -state;
+ }
+ sprintf(keyfile, KEYFILE_NAME, namespace, keyname);
+ ret = load_n_decrypt(keyname, keyfile, &decryptKey, &blob);
+ if (!ret) {
+ if ((blob.value_size > MAX_KEY_VALUE_LENGTH)) {
+ ret = -1;
+ } else {
+ *size = blob.value_size;
+ memcpy(data, blob.value, *size);
+ }
+ }
+ return ret;
+}
+
+int list_keys(const char *namespace, char reply[BUFFER_MAX])
+{
+ DIR *d;
+ struct dirent *de;
+
+ if (!namespace || ((d = opendir("."))) == NULL) {
+ LOGE("cannot open keystore dir or namespace is null\n");
+ return -1;
+ }
+ while ((de = readdir(d))) {
+ char *prefix, *name, *keyfile = de->d_name;
+ char *context = NULL;
+
+ if (de->d_type != DT_REG) continue;
+ if ((prefix = strtok_r(keyfile, NAME_DELIMITER, &context))
+ == NULL) continue;
+ if (strcmp(prefix, namespace)) continue;
+ if ((name = strtok_r(NULL, NAME_DELIMITER, &context)) == NULL) continue;
+ // append the key name into reply
+ if (reply[0] != 0) strlcat(reply, " ", BUFFER_MAX);
+ if (strlcat(reply, name, BUFFER_MAX) >= BUFFER_MAX) {
+ LOGE("too many files under keystore directory\n");
+ return -1;
+ }
+ }
+ closedir(d);
+ return 0;
+}
+
+int passwd(char *data)
+{
+ if (state == UNINITIALIZED) {
+ if (strchr(data, ' ')) return -1;
+ return create_master_key(data);
+ }
+ return change_passwd(data);
+}
+
+int lock()
+{
+ switch(state) {
+ case UNLOCKED:
+ lock_keystore();
+ case LOCKED:
+ return 0;
+ default:
+ return -1;
+ }
+}
+
+int unlock(char *passwd)
+{
+ unsigned char master_key[USER_KEY_LEN];
+ int ret = get_master_key(passwd, master_key);
+ if (!ret) {
+ unlock_keystore(master_key);
+ retry_count = 0;
+ } else {
+ ret = MAX_RETRY_COUNT - ++retry_count;
+ if (ret == 0) {
+ retry_count = 0;
+ LOGE("unlock:reach max retry count, reset the keystore now.");
+ reset_keystore();
+ return -1;
+ }
+ }
+ return ret;
+}
+
+KEYSTORE_STATE get_state()
+{
+ return state;
+}
+
+int reset_keystore()
+{
+ DIR *d;
+ struct dirent *de;
+
+ if ((d = opendir(".")) == NULL) {
+ LOGE("cannot open keystore dir\n");
+ return -1;
+ }
+ while ((de = readdir(d))) unlink(de->d_name);
+ closedir(d);
+ state = UNINITIALIZED;
+ LOGI("keystore is reset.");
+ return 0;
+}
+
+int init_keystore(const char *dir)
+{
+ int fd;
+
+ if (!dir) mkdir(dir, 0770);
+ if (!dir || chdir(dir)) {
+ LOGE("Can not open/create the keystore directory %s\n",
+ dir ? dir : "(null)");
+ return -1;
+ }
+ gen_random_blob(iv, IV_LEN);
+ if ((fd = open(MASTER_KEY, O_RDONLY)) == -1) {
+ state = UNINITIALIZED;
+ return 0;
+ }
+ close(fd);
+ state = LOCKED;
+ return 0;
+}