diff -ruNp 610-encryption.patch-old/kernel/power/suspend2_core/encryption.c 610-encryption.patch-new/kernel/power/suspend2_core/encryption.c --- 610-encryption.patch-old/kernel/power/suspend2_core/encryption.c 1970-01-01 10:00:00.000000000 +1000 +++ 610-encryption.patch-new/kernel/power/suspend2_core/encryption.c 2005-07-05 23:54:31.000000000 +1000 @@ -0,0 +1,598 @@ +/* + * kernel/power/suspend2_core/encryption.c + * + * Copyright (C) 2003-2005 Nigel Cunningham <[EMAIL PROTECTED]> + * + * This file is released under the GPLv2. + * + * This file contains data encryption routines for suspend, + * using cryptoapi transforms. + * + * ToDo: + * - Apply min/max_keysize the cipher changes. + * - Test. + */ + +#include <linux/suspend.h> +#include <linux/module.h> +#include <linux/highmem.h> +#include <linux/vmalloc.h> +#include <linux/crypto.h> +#include <asm/scatterlist.h> + +#include "suspend.h" +#include "plugins.h" +#include "proc.h" +#include "suspend2_common.h" +#include "utility.h" +#include "io.h" + +#define S2C_WRITE 0 +#define S2C_READ 1 + +static struct suspend_plugin_ops s2_encryption_ops; +static struct suspend_plugin_ops * next_driver; + +static char s2_encryptor_name[32]; +static struct crypto_tfm * s2_encryptor_transform; +static char s2_encryptor_key[256]; +static int s2_key_len; +static char s2_encryptor_iv[256]; +static int s2_encryptor_mode; +static int s2_encryptor_save_key_and_iv; + +static u8 *page_buffer = NULL; +static unsigned int bufofs; + +static struct scatterlist s2_crypt_sg[PAGE_SIZE/8]; + +/* ---- Local buffer management ---- */ + +/* allocate_local_buffer + * + * Description: Allocates a page of memory for buffering output. + * Returns: Int: Zero if successful, -ENONEM otherwise. + */ + +static int allocate_local_buffer(void) +{ + if (!page_buffer) { + int i; + + page_buffer = (char *) get_zeroed_page(GFP_ATOMIC); + + if (!page_buffer) { + printk(KERN_ERR + "Failed to allocate the page buffer for " + "suspend2 encryption driver.\n"); + return -ENOMEM; + } + + for (i=0; i < (PAGE_SIZE / s2_key_len); i++) { + s2_crypt_sg[i].page = virt_to_page(page_buffer); + s2_crypt_sg[i].offset = s2_key_len * i; + s2_crypt_sg[i].length = s2_key_len; + } + } + + return 0; +} + +/* free_local_buffer + * + * Description: Frees memory allocated for buffering output. + */ + +static inline void free_local_buffer(void) +{ + if (page_buffer) + free_pages((unsigned long) page_buffer, 0); + + page_buffer = NULL; +} + +/* suspend2_crypto_cleanup + * + * Description: Frees memory allocated for our labours. + */ + +static void suspend2_crypto_cleanup(void) +{ + if (s2_encryptor_transform) { + crypto_free_tfm(s2_encryptor_transform); + s2_encryptor_transform = NULL; + } +} + +/* suspend2_crypto_prepare + * + * Description: Prepare to do some work by allocating buffers and transforms. + * Returns: Int: Zero if successful, -ENONEM otherwise. + */ + +static int s2_encrypt_crypto_prepare(int mode) +{ + if (!*s2_encryptor_name) { + printk("Suspend2: Encryptor enabled but no name set.\n"); + return 1; + } + + if (!(s2_encryptor_transform = crypto_alloc_tfm(s2_encryptor_name, + 1 << s2_encryptor_mode))) { + printk("Suspend2: Failed to initialise the encryption transform (%s, mode %d).\n", + s2_encryptor_name, s2_encryptor_mode); + return 1; + } + + if (mode) + bufofs = PAGE_SIZE; + else + bufofs = 0; + + s2_key_len = strlen(s2_encryptor_key); + + if (crypto_cipher_setkey(s2_encryptor_transform, s2_encryptor_key, + s2_key_len)) { + printk("%d is an invalid key length for cipher %s.\n", + s2_key_len, + s2_encryptor_name); + return 1; + } + + if (!mode) { + crypto_cipher_set_iv(s2_encryptor_transform, + s2_encryptor_iv, + crypto_tfm_alg_ivsize(s2_encryptor_transform)); + } + + return 0; +} + +/* ---- Exported functions ---- */ + +/* write_init() + * + * Description: Allocate buffers and prepare to encrypt data. + * Arguments: Stream_number: Ignored. + * Returns: Zero on success, -ENOMEM if unable to vmalloc. + */ + +static int s2_encrypt_write_init(int stream_number) +{ + int result; + + next_driver = get_next_filter(&s2_encryption_ops); + + if (!next_driver) { + printk("Encryption Driver: Argh! No one wants my output!"); + return -ECHILD; + } + + if ((result = s2_encrypt_crypto_prepare(S2C_WRITE))) { + SET_RESULT_STATE(SUSPEND_ENCRYPTION_SETUP_FAILED); + suspend2_crypto_cleanup(); + return result; + } + + if ((result = allocate_local_buffer())) + return result; + + /* Only reset the stats if starting to write an image */ + if (stream_number == 2) + bytes_in = bytes_out = 0; + + bufofs = 0; + + return 0; +} + +/* s2_encrypt_write_chunk() + * + * Description: Encrypt a page of data, buffering output and passing on + * filled pages to the next plugin in the pipeline. + * Arguments: Buffer_page: Pointer to a buffer of size PAGE_SIZE, + * containing data to be encrypted. + * Returns: 0 on success. Otherwise the error is that returned by later + * plugins, -ECHILD if we have a broken pipeline or -EIO if + * zlib errs. + */ + +static int s2_encrypt_write_chunk(struct page * buffer_page) +{ + int ret; + unsigned int len; + u16 len_written; + char * buffer_start; + + if (!s2_encryptor_transform) + return next_driver->ops.filter.write_chunk(buffer_page); + + buffer_start = kmap(buffer_page); + memcpy(page_buffer, buffer_start, PAGE_SIZE); + kunmap(buffer_page); + + bytes_in += PAGE_SIZE; + + len = PAGE_SIZE; + + ret = crypto_cipher_encrypt(s2_encryptor_transform, + s2_crypt_sg, s2_crypt_sg, PAGE_SIZE); + + if (ret) { + printk("Encryption failed.\n"); + return -EIO; + } + + len_written = (u16) len; + + ret = next_driver->ops.filter.write_chunk(virt_to_page(page_buffer)); + + return ret; +} + +/* write_cleanup() + * + * Description: Write unflushed data and free workspace. + * Returns: Result of writing last page. + */ + +static int s2_encrypt_write_cleanup(void) +{ + suspend2_crypto_cleanup(); + free_local_buffer(); + + return 0; +} + +/* read_init() + * + * Description: Prepare to read a new stream of data. + * Arguments: int: Section of image about to be read. + * Returns: int: Zero on success, error number otherwise. + */ + +static int s2_encrypt_read_init(int stream_number) +{ + int result; + + next_driver = get_next_filter(&s2_encryption_ops); + + if (!next_driver) { + printk("Encryption Driver: Argh! No one wants " + "to feed me data!"); + return -ECHILD; + } + + if ((result = s2_encrypt_crypto_prepare(S2C_READ))) { + SET_RESULT_STATE(SUSPEND_ENCRYPTION_SETUP_FAILED); + suspend2_crypto_cleanup(); + return result; + } + + if ((result = allocate_local_buffer())) + return result; + + bufofs = PAGE_SIZE; + + return 0; +} + +/* s2_encrypt_read_chunk() + * + * Description: Retrieve data from later plugins and deencrypt it until the + * input buffer is filled. + * Arguments: Buffer_start: Pointer to a buffer of size PAGE_SIZE. + * Sync: Whether the previous plugin (or core) wants its + * data synchronously. + * Returns: Zero if successful. Error condition from me or from downstream + * on failure. + */ + +static int s2_encrypt_read_chunk(struct page * buffer_page, int sync) +{ + int ret; + char * buffer_start; + + if (!s2_encryptor_transform) + return next_driver->ops.filter.read_chunk(buffer_page, sync); + + /* + * All our reads must be synchronous - we can't deencrypt + * data that hasn't been read yet. + */ + + if ((ret = next_driver->ops.filter.read_chunk( + virt_to_page(page_buffer), SUSPEND_SYNC)) < 0) { + printk("Failed to read an encrypted block.\n"); + return ret; + } + + ret = crypto_cipher_decrypt(s2_encryptor_transform, + s2_crypt_sg, s2_crypt_sg, PAGE_SIZE); + + if (ret) + printk("Decrypt function returned %d.\n", ret); + + buffer_start = kmap(buffer_page); + memcpy(buffer_start, page_buffer, PAGE_SIZE); + kunmap(buffer_page); + return ret; +} + +/* read_cleanup() + * + * Description: Clean up after reading part or all of a stream of data. + * Returns: int: Always zero. Never fails. + */ + +static int s2_encrypt_read_cleanup(void) +{ + suspend2_crypto_cleanup(); + free_local_buffer(); + return 0; +} + +/* s2_encrypt_print_debug_stats + * + * Description: Print information to be recorded for debugging purposes into a + * buffer. + * Arguments: buffer: Pointer to a buffer into which the debug info will be + * printed. + * size: Size of the buffer. + * Returns: Number of characters written to the buffer. + */ + +static int s2_encrypt_print_debug_stats(char * buffer, int size) +{ + int len; + + len = suspend_snprintf(buffer, size, "- Encryptor %s enabled.\n", + s2_encryptor_name); + return len; +} + +/* encryption_memory_needed + * + * Description: Tell the caller how much memory we need to operate during + * suspend/resume. + * Returns: Unsigned long. Maximum number of bytes of memory required for + * operation. + */ + +static unsigned long s2_encrypt_memory_needed(void) +{ + return PAGE_SIZE; +} + +static unsigned long s2_encrypt_storage_needed(void) +{ + return 2 * sizeof(unsigned long) + sizeof(int); +} + +/* s2_encrypt_save_config_info + * + * Description: Save informaton needed when reloading the image at resume time. + * Arguments: Buffer: Pointer to a buffer of size PAGE_SIZE. + * Returns: Number of bytes used for saving our data. + */ + +static int s2_encrypt_save_config_info(char * buffer) +{ + int buf_offset, str_size; + + str_size = strlen(s2_encryptor_name); + *buffer = (char) str_size; + strncpy(buffer + 1, s2_encryptor_name, str_size + 1); + buf_offset = str_size + 2; + + *(buffer + buf_offset) = (char) s2_encryptor_mode; + buf_offset++; + + *(buffer + buf_offset) = (char) s2_encryptor_save_key_and_iv; + buf_offset++; + + if (s2_encryptor_save_key_and_iv) { + + str_size = strlen(s2_encryptor_key); + *(buffer + buf_offset) = (char) str_size; + strncpy(buffer + buf_offset + 1, s2_encryptor_key, str_size + 1); + + buf_offset+= str_size + 2; + + str_size = strlen(s2_encryptor_iv); + *(buffer + buf_offset) = (char) str_size; + strncpy(buffer + buf_offset + 1, s2_encryptor_iv, str_size + 1); + + buf_offset += str_size + 2; + } + + return buf_offset; +} + +/* s2_encrypt_load_config_info + * + * Description: Reload information needed for deencrypting the image at + * resume time. + * Arguments: Buffer: Pointer to the start of the data. + * Size: Number of bytes that were saved. + */ + +static void s2_encrypt_load_config_info(char * buffer, int size) +{ + int buf_offset, str_size; + + str_size = (int) *buffer; + strncpy(s2_encryptor_name, buffer + 1, str_size + 1); + buf_offset = str_size + 2; + + s2_encryptor_mode = (int) *(buffer + buf_offset); + buf_offset++; + + s2_encryptor_save_key_and_iv = (int) *(buffer + buf_offset); + buf_offset++; + + if (s2_encryptor_save_key_and_iv) { + str_size = (int) *(buffer + buf_offset); + strncpy(s2_encryptor_key, buffer + buf_offset + 1, str_size + 1); + + buf_offset+= str_size + 2; + + str_size = (int) *(buffer + buf_offset); + strncpy(s2_encryptor_iv, buffer + buf_offset + 1, str_size + 1); + + buf_offset += str_size + 2; + } else { + *s2_encryptor_key = 0; + *s2_encryptor_iv = 0; + } + + if (buf_offset != size) { + printk("Suspend Encryptor config info size mismatch (%d != %d): settings ignored.\n", + buf_offset, size); + *s2_encryptor_key = 0; + *s2_encryptor_iv = 0; + } + return; +} + +static void s2_encryptor_disable_if_empty(void) +{ + s2_encryption_ops.disabled = !(*s2_encryptor_name); +} + +static int s2_encrypt_initialise(int starting_cycle) +{ + if (starting_cycle) + s2_encryptor_disable_if_empty(); + + return 0; +} +/* + * data for our proc entries. + */ + +static struct suspend_proc_data proc_params[] = { + { + .filename = "encryptor", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_STRING, + .data = { + .string = { + .variable = s2_encryptor_name, + .max_length = 31, + } + }, + .write_proc = s2_encryptor_disable_if_empty, + }, + + { + .filename = "encryption_mode", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_INTEGER, + .data = { + .integer = { + .variable = &s2_encryptor_mode, + .minimum = 0, + .maximum = 3, + } + } + }, + + { + .filename = "encryption_save_key_and_iv", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_INTEGER, + .data = { + .integer = { + .variable = &s2_encryptor_save_key_and_iv, + .minimum = 0, + .maximum = 1, + } + } + }, + + { + .filename = "encryption_key", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_STRING, + .data = { + .string = { + .variable = s2_encryptor_key, + .max_length = 255, + } + } + }, + + { + .filename = "encryption_iv", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_STRING, + .data = { + .string = { + .variable = s2_encryptor_iv, + .max_length = 255, + } + } + }, + + { + .filename = "disable_encryption", + .permissions = PROC_RW, + .type = SUSPEND_PROC_DATA_INTEGER, + .data = { + .integer = { + .variable = &s2_encryption_ops.disabled, + .minimum = 0, + .maximum = 1, + } + } + }, + +}; + +/* + * Ops structure. + */ + +static struct suspend_plugin_ops s2_encryption_ops = { + .type = FILTER_PLUGIN, + .name = "Encryptor", + .module = THIS_MODULE, + .memory_needed = s2_encrypt_memory_needed, + .print_debug_info = s2_encrypt_print_debug_stats, + .save_config_info = s2_encrypt_save_config_info, + .load_config_info = s2_encrypt_load_config_info, + .storage_needed = s2_encrypt_storage_needed, + + .initialise = s2_encrypt_initialise, + + .write_init = s2_encrypt_write_init, + .write_cleanup = s2_encrypt_write_cleanup, + .read_init = s2_encrypt_read_init, + .read_cleanup = s2_encrypt_read_cleanup, + + .ops = { + .filter = { + .write_chunk = s2_encrypt_write_chunk, + .read_chunk = s2_encrypt_read_chunk, + } + } +}; + +/* ---- Registration ---- */ + +static __init int s2_encrypt_load(void) +{ + int result; + int i, numfiles = sizeof(proc_params) / sizeof(struct suspend_proc_data); + + printk("Software Suspend Encryption Driver loading.\n"); + if (!(result = suspend_register_plugin(&s2_encryption_ops))) { + for (i=0; i< numfiles; i++) + suspend_register_procfile(&proc_params[i]); + } else + printk("Software Suspend Encryption Driver unable to register!\n"); + return result; +} + +late_initcall(s2_encrypt_load);
- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [EMAIL PROTECTED] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/