blob: 4a7cbe26e8cf016c3cde14e6cd029550ef2ff452 [file] [log] [blame]
/*
* Encrypt/decrypt plugin using DES-CBC.
*
* Copyright (c) 2010-2012 The Chromium OS Authors. All rights reserved.
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#define CONNMAN_API_SUBJECT_TO_CHANGE
#include <connman/crypto.h>
#include <connman/log.h>
#include <connman/option.h>
#include <connman/plugin.h>
#include <errno.h>
#include <glib.h>
#include <string.h>
#include <rpc/des_crypt.h>
#define BLOCK_SIZE 8
static gboolean have_keymatter = FALSE;
static char des_key[BLOCK_SIZE];
static char des_iv[BLOCK_SIZE];
/*
* This is prepended to the base64 encoded value so that we can alter the
* encryption or encoding schemes over time.
*/
static char version_2_prefix[] = "02:";
static int version_prefix_length = 3;
/*
* This is appended to the plaintext before encryption, and checked during
* decryption to detect decryption failures. This is at the end of the string
* rather than the beginning to simplify its removal during decryption.
* This was introduced in version 2.
*/
static char sentinel_suffix[] = "[ok]";
static int sentinel_length = 4;
static char *descbc_encrypt(const char *key, const char *value);
static char *descbc_decrypt(const char *key, const char *value);
static struct connman_crypto crypto_descbc = {
.name = "des-cbc",
.priority = CONNMAN_CRYPTO_PRIORITY_HIGH,
.encrypt_keyvalue = descbc_encrypt,
.decrypt_keyvalue = descbc_decrypt,
};
static char *descbc_encrypt(const char *key, const char *value)
{
/*
* NB: never encrypt; we'll fallback to something like rot47
* which doesn't depend on the owner key which may change due
* to rotation.
*/
return NULL;
}
static char *descbc_decrypt(const char *key, const char *value)
{
gsize ciphertext_size;
int rv;
int version = 1;
char *buf = NULL;
char local_iv[BLOCK_SIZE];
if (have_keymatter == FALSE)
return NULL;
if (g_str_has_prefix(value, version_2_prefix)) {
version = 2;
value += version_prefix_length;
}
/*
* Buf starts out a the ciphertext, but becomes the plaintext since the
* crypt operation happens in place. This function returns something
* that must g_free()d, even if it is handed a 0 length value.
*/
buf = (char *)g_base64_decode(value, &ciphertext_size);
/* If ciphertext is not a whole number of blocks, something is wrong. */
if (ciphertext_size == 0 || (ciphertext_size % BLOCK_SIZE != 0))
goto bad;
/*
* Make a copy of the IV, since this is also modified in place during
* the crypt.
*/
memcpy(local_iv, des_iv, BLOCK_SIZE);
rv = cbc_crypt(des_key, buf, ciphertext_size, DES_DECRYPT, local_iv);
if (DES_FAILED(rv)) {
connman_error("des-cbc: cbc_crypt failed.");
goto bad;
}
if (version == 2) {
if (g_str_has_suffix(buf, sentinel_suffix) == FALSE) {
connman_error("des-cbc: Decrypt failed: bad key?");
goto bad;
}
buf[strlen(buf) - sentinel_length] = '\0';
}
return buf;
bad:
g_free(buf);
return NULL;
}
static int load_keymatter(const char* keymatter_file)
{
gboolean result;
gchar *keymatter;
gsize keymatter_size;
connman_info("des-cbc: Reading keymatter file: %s", keymatter_file);
result = g_file_get_contents(keymatter_file, &keymatter,
&keymatter_size, NULL);
if (result == FALSE) {
connman_error("des-cbc: Error reading %s: %s",
keymatter_file, strerror(errno));
return -errno;
}
if (keymatter_size == 0) {
connman_error("des-cbc: Empty key file: %s", keymatter_file);
return -ENOENT;
}
if (keymatter_size < 2 * BLOCK_SIZE) {
connman_error("des-cbc: Keymatter file is too small, expected "
"at least %i bytes, found %zu",
2 * BLOCK_SIZE, keymatter_size);
return -ENOENT;
}
memcpy(des_key, keymatter + keymatter_size - BLOCK_SIZE, BLOCK_SIZE);
memcpy(des_iv, keymatter + keymatter_size - 2 * BLOCK_SIZE, BLOCK_SIZE);
g_free(keymatter);
return 0;
}
static int setup_keymatter(void)
{
const char *keymatter_file;
keymatter_file = connman_option_get_string("keymatter");
if (keymatter_file == NULL) {
connman_warn("des-cbc: No keymatter file specified");
return FALSE;
}
return (load_keymatter(keymatter_file) == 0);
}
static int descbc_init(void)
{
have_keymatter = setup_keymatter();
if (connman_crypto_register(&crypto_descbc) < 0) {
connman_error("des-cbc: Failed to register plugin");
return -1;
}
return 0;
}
static void descbc_finish(void)
{
connman_crypto_unregister(&crypto_descbc);
}
CONNMAN_PLUGIN_DEFINE(crypto_descbc, "DES-CBC Plugin", VERSION,
CONNMAN_PLUGIN_PRIORITY_DEFAULT, descbc_init,
descbc_finish)