| /* |
| * 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) |