=== modified file 'conf/common.rmk' --- conf/common.rmk 2009-08-14 13:31:09 +0000 +++ conf/common.rmk 2009-06-21 10:02:28 +0000 @@ -617,3 +617,9 @@ setjmp_mod_SOURCES = lib/$(target_cpu)/setjmp.S setjmp_mod_ASFLAGS = $(COMMON_ASFLAGS) setjmp_mod_LDFLAGS = $(COMMON_LDFLAGS) + +# For crypto.mod +pkglib_MODULES += crypto.mod +crypto_mod_SOURCES = crypto/crypto.c +crypto_mod_CFLAGS = $(COMMON_CFLAGS) +crypto_mod_LDFLAGS = $(COMMON_LDFLAGS) === added directory 'crypto' === added file 'crypto/crypto.c' --- crypto/crypto.c 1970-01-01 00:00:00 +0000 +++ crypto/crypto.c 2009-06-21 10:02:28 +0000 @@ -0,0 +1,389 @@ +/* + * crypto.c - Strong cryptography API for GRUB + * + * Copyright (C) 2007 Simon Peter + * Thanks to Raoul Boenisch for the initial idea. + * Modified by Michael Gorven + */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#define MAX(a, b) (a > b ? a : b) + + +static char *hexchars = "0123456789ABCDEF"; +void print_buffer(char *label, unsigned char *buffer, grub_size_t len) +{ + grub_size_t i; + grub_printf(label); + for (i = 0; i < len; i++) + grub_printf("%c%c", hexchars[(buffer[i]>>4)&0xF], hexchars[buffer[i]&0xF] ); + grub_printf("\n"); +} + + +struct cipher_list +{ + grub_cipher_t cipher; + struct cipher_list *next; +}; + +static struct cipher_list *cipher_list = NULL; + +grub_err_t +grub_crypto_hash (grub_cipher_params_t params, char *hash, + const char *payload, unsigned int size) +{ + return params->cipher->u.hash.fn (params, hash, payload, size); +} + +static grub_cipher_t +get_cipher (const char *name, grub_cipher_type_t type) +{ + struct cipher_list *i; + + for (i = cipher_list; i != NULL; i = i->next) + if (!grub_strcmp (i->cipher->name, name) && i->cipher->type == type) + return i->cipher; + + return NULL; +} + +grub_err_t +grub_crypto_new_cipher (grub_cipher_params_t * params, const char *name, + grub_cipher_type_t type) +{ + grub_cipher_t cipher = get_cipher (name, type); + + if (!cipher) { + grub_dl_t mod = NULL; + mod = grub_dl_load(name); + if (mod) + cipher = get_cipher(name, type); + } + + grub_err_t err; + + if (cipher == NULL) + { + const char *errstr = "Illegal cipher type"; + + switch (type) + { + case GRUB_CIPHER_TYPE_NONE: + break; + + case GRUB_CIPHER_TYPE_CIPHER: + errstr = "Unknown cipher"; + break; + + case GRUB_CIPHER_TYPE_HASH: + errstr = "Unknown hash"; + break; + } + + return grub_error (GRUB_ERR_BAD_ARGUMENT, errstr); + } + + *params = grub_malloc (sizeof (struct grub_cipher_params)); + if (*params == NULL) + return grub_errno; + + if (cipher->init) + { + if ((err = cipher->init (*params)) != GRUB_ERR_NONE) + { + grub_free (*params); + return err; + } + } + else + { + unsigned int i, keysize = 0; + + /* Set keysize to largest */ + for (i = 0; i < cipher->keysizes_length; i++) + keysize = MAX (keysize, cipher->keysizes[i]); + + (*params)->keysize = keysize; + (*params)->u.cipher.mode = GRUB_CIPHER_MODE_ECB; + } + + (*params)->cipher = cipher; + return GRUB_ERR_NONE; +} + +void +grub_crypto_delete_cipher (grub_cipher_params_t params) +{ + if (params->cipher->deinit) + params->cipher->deinit (params); + + grub_free (params); +} + +int +grub_crypto_cipher_iterate (grub_crypto_hook hook, void *data) +{ + struct cipher_list *i; + + for (i = cipher_list; i != NULL; i = i->next) + if (hook (i->cipher, data)) + return 1; + + return 0; +} + +void +grub_crypto_cipher_register (grub_cipher_t cipher) +{ + struct cipher_list *newcipher = grub_malloc (sizeof (struct cipher_list)); + + if (!newcipher) + return; /* out of memory! */ + + newcipher->cipher = cipher; + newcipher->next = cipher_list; + cipher_list = newcipher; +} + +void +grub_crypto_cipher_unregister (grub_cipher_t cipher) +{ + struct cipher_list *i, **prev; + + for (i = cipher_list, prev = &cipher_list; i != NULL; + prev = &i->next, i = i->next) + if (!grub_strcmp (i->cipher->name, cipher->name)) + break; + + if (!i) + { + grub_printf ("BUG: Trying to unregister a non-registered cipher!\n"); + return; + } + + /* Remove cipher from list */ + *prev = i->next; + grub_free (i); +} + +grub_err_t +grub_crypto_set_key (grub_cipher_params_t params, const char *key) +{ + if (params->cipher->u.cipher.set_key) + return params->cipher->u.cipher.set_key (params, key); + else + return GRUB_ERR_NONE; +} + +grub_err_t +grub_crypto_encrypt (grub_cipher_params_t params, char *out, + char *in, grub_size_t outsize, grub_size_t insize) +{ + grub_size_t i; + + if (!params->cipher->u.cipher.encrypt_block) + return GRUB_ERR_NOT_IMPLEMENTED_YET; + + if (insize % params->cipher->blocksize != 0) + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Size must be multiple of cipher block size"); + + switch (params->u.cipher.mode) { + + case GRUB_CIPHER_MODE_ECB: + for (i = 0; i < insize; i += params->cipher->blocksize) + params->cipher->u.cipher.encrypt_block(params, (char*) out + i, (char*) in + i); + return GRUB_ERR_NONE; + + default: + return GRUB_ERR_NOT_IMPLEMENTED_YET; + } +} + +grub_err_t +grub_crypto_decrypt (grub_cipher_params_t params, char *out, + char *in, grub_size_t outsize, grub_size_t insize) +{ + grub_size_t i, j; + unsigned char temp[params->cipher->blocksize]; + unsigned char prevcipher[params->cipher->blocksize]; + + if (!params->cipher->u.cipher.decrypt_block) + return GRUB_ERR_NOT_IMPLEMENTED_YET; + + if (insize % params->cipher->blocksize != 0) + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Size must be multiple of cipher block size"); + + switch (params->u.cipher.mode) { + + case GRUB_CIPHER_MODE_ECB: + for (i = 0; i < insize; i += params->cipher->blocksize) { + params->cipher->u.cipher.decrypt_block(params, (char*) out + i, (char*) in + i); + } + return 0; + + case GRUB_CIPHER_MODE_CBC_PLAIN: + case GRUB_CIPHER_MODE_CBC_ESSIV: + if (!params->u.cipher.iv) + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Initialisation vector not specified"); + + grub_memcpy(prevcipher, params->u.cipher.iv, params->cipher->blocksize); + + for (i = 0; i < insize; i += params->cipher->blocksize) { + params->cipher->u.cipher.decrypt_block(params, (char*) temp, (char*) in+i); + for (j = 0; j < params->cipher->blocksize; j++) + temp[j] = temp[j] ^ prevcipher[j]; + + grub_memcpy(prevcipher, in+i, params->cipher->blocksize); + grub_memcpy(out+i, temp, params->cipher->blocksize); + } + + break; + } + + return 0; +} + +grub_err_t +grub_crypto_decrypt_inplace (grub_cipher_params_t params, char *buf, + grub_size_t size) +{ + return grub_crypto_decrypt(params, buf, buf, size, size); +} + +grub_err_t +grub_crypto_encrypt_inplace (grub_cipher_params_t params, char *buf, + grub_size_t size) +{ + if (params->cipher->u.cipher.encrypt_inplace) + return params->cipher->u.cipher.encrypt_inplace (params, buf, size); + else + return GRUB_ERR_NOT_IMPLEMENTED_YET; +} + +/***** None cipher interface ***********************************************/ + +static grub_err_t +cipher_none_crypt (grub_cipher_params_t unused, char **out, const char *in, + grub_size_t * outsize, grub_size_t insize) +{ + (void) unused; + if (!(*out = grub_malloc (insize))) + return grub_errno; + *out = grub_memcpy (*out, in, insize); + *outsize = insize; + return 0; +} + +static grub_err_t +cipher_none_crypt_inplace (grub_cipher_params_t unused, char *buf, + grub_size_t size) +{ + (void) unused; + (void) buf; + (void) size; + return 0; +} + +static grub_err_t +hash_none_fn (grub_cipher_params_t params, char *out, const char *in, + grub_size_t insize) +{ + params->keysize = insize; + grub_memcpy (out, in, insize); + return 0; +} + +static struct grub_cipher grub_cipher_none = { + .name = "none", + .type = GRUB_CIPHER_TYPE_CIPHER, + .keysizes_length = 0, + + .u.cipher = { + .encrypt = cipher_none_crypt, + .decrypt = cipher_none_crypt, + .decrypt_inplace = cipher_none_crypt_inplace, + .encrypt_inplace = cipher_none_crypt_inplace} +}; + +static struct grub_cipher grub_hash_none = { + .name = "none", + .type = GRUB_CIPHER_TYPE_HASH, + .keysizes_length = 0, + .u.hash.fn = hash_none_fn +}; + +static const struct grub_arg_option options[] = {}; + + +static grub_err_t +grub_cmd_hash(struct grub_arg_list *state, int argc, char **args) +{ + char digest[64]; + grub_cipher_params_t hash; + grub_err_t err; + int i; + + if (argc < 2) + return grub_error (GRUB_ERR_BAD_ARGUMENT, "Hash and data required"); + + hash=grub_malloc(sizeof(struct grub_cipher_params)); + + err = grub_crypto_new_cipher(&hash, args[0], GRUB_CIPHER_TYPE_HASH); + if (err) + return grub_error(GRUB_ERR_BAD_ARGUMENT, "Hash doesn't exist"); + + err = grub_crypto_hash(hash, digest, args[1], grub_strlen(args[1])); + + for (i = 0; i < hash->keysize; i++) + grub_printf("%c%c", hexchars[(digest[i]>>4)&0xF], hexchars[digest[i]&0xF] ); + grub_printf("\n"); + + return GRUB_ERR_NONE; +} + + +/***** GRUB module (de-)initialization *************************************/ + +GRUB_MOD_INIT (crypto) +{ + grub_crypto_cipher_register (&grub_cipher_none); + grub_crypto_cipher_register (&grub_hash_none); + grub_register_command("hash", grub_cmd_hash, + "hash [HASH] [STRING]", "Computes a hash"); +} + +GRUB_MOD_FINI (crypto) +{ + grub_crypto_cipher_unregister (&grub_hash_none); + grub_crypto_cipher_unregister (&grub_cipher_none); + grub_unregister_command("hash"); +} + +/* vi: set et sw=2 sts=2: */ === added file 'include/grub/crypto.h' --- include/grub/crypto.h 1970-01-01 00:00:00 +0000 +++ include/grub/crypto.h 2009-06-21 10:02:28 +0000 @@ -0,0 +1,137 @@ +/* + * crypto.h - GRUB cryptographic API + * + * Copyright (C) 2007 Simon Peter + */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2007 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see . + */ + +#ifndef CRYPTO_H +#define CRYPTO_H + +#include + +enum grub_cipher_type +{ + GRUB_CIPHER_TYPE_NONE = 0, + GRUB_CIPHER_TYPE_CIPHER, + GRUB_CIPHER_TYPE_HASH +}; + +enum grub_cipher_mode +{ + GRUB_CIPHER_MODE_ECB = 0, + GRUB_CIPHER_MODE_CBC_PLAIN, + GRUB_CIPHER_MODE_CBC_ESSIV +}; + +typedef enum grub_cipher_type grub_cipher_type_t; +typedef enum grub_cipher_mode grub_cipher_mode_t; + +struct grub_cipher_params; +typedef struct grub_cipher_params *grub_cipher_params_t; + +struct grub_cipher +{ + const char *name; + grub_cipher_type_t type; + const unsigned int *keysizes, keysizes_length; + const unsigned int blocksize; + + grub_err_t (*init) (grub_cipher_params_t); + void (*deinit) (grub_cipher_params_t); + + union + { + struct + { + const unsigned int *ivsizes, ivsizes_length; + + grub_err_t (*set_key) (grub_cipher_params_t, const char *key); + grub_err_t (*encrypt) (grub_cipher_params_t, char **out, + const char *in, grub_size_t * outsize, + grub_size_t insize); + grub_err_t (*decrypt) (grub_cipher_params_t, char **out, + const char *in, grub_size_t * outsize, + grub_size_t insize); + grub_err_t (*decrypt_block) (grub_cipher_params_t, char *out, char *in); + grub_err_t (*encrypt_block) (grub_cipher_params_t, char *out, char *in); + grub_err_t (*decrypt_inplace) (grub_cipher_params_t, char *buf, + grub_size_t size); + grub_err_t (*encrypt_inplace) (grub_cipher_params_t, char *buf, + grub_size_t size); + } cipher; + + struct + { + grub_err_t (*fn) (grub_cipher_params_t, char *out, const char *in, + grub_size_t insize); + } hash; + } u; +}; + +typedef struct grub_cipher *grub_cipher_t; + +struct grub_cipher_params +{ + grub_cipher_t cipher; + void *private; + unsigned int keysize; + + union + { + struct + { + unsigned char *iv; + unsigned int iv_length; + grub_cipher_mode_t mode; + } cipher; + } u; +}; + +typedef int (*grub_crypto_hook) (grub_cipher_t, void *); + +grub_err_t EXPORT_FUNC (grub_crypto_encrypt) (grub_cipher_params_t params, + char *out, char *in, + grub_size_t outsize, + grub_size_t insize); +grub_err_t EXPORT_FUNC (grub_crypto_decrypt) (grub_cipher_params_t params, + char *out, char *in, + grub_size_t outsize, + grub_size_t insize); +grub_err_t EXPORT_FUNC (grub_crypto_decrypt_inplace) (grub_cipher_params_t + params, char *buf, + grub_size_t size); +grub_err_t EXPORT_FUNC (grub_crypto_encrypt_inplace) (grub_cipher_params_t + params, char *buf, + grub_size_t size); +grub_err_t EXPORT_FUNC (grub_crypto_set_key) (grub_cipher_params_t params, + const char *key); +int EXPORT_FUNC (grub_crypto_cipher_iterate) (grub_crypto_hook hook, + void *data); +grub_err_t EXPORT_FUNC (grub_crypto_hash) (grub_cipher_params_t params, + char *hash, const char *payload, + unsigned int size); +grub_err_t EXPORT_FUNC (grub_crypto_new_cipher) (grub_cipher_params_t * + params, const char *name, + grub_cipher_type_t type); +void EXPORT_FUNC (grub_crypto_delete_cipher) (grub_cipher_params_t params); +void EXPORT_FUNC (grub_crypto_cipher_register) (grub_cipher_t cipher); +void EXPORT_FUNC (grub_crypto_cipher_unregister) (grub_cipher_t cipher); + +#endif