diff options
-rw-r--r-- | Makefile.am | 5 | ||||
-rw-r--r-- | include/ncrypto/ncrypto.h | 41 | ||||
-rw-r--r-- | lib/ncrypto_local.c | 342 | ||||
-rw-r--r-- | tests/symm_ciphers.c | 174 |
4 files changed, 561 insertions, 1 deletions
diff --git a/Makefile.am b/Makefile.am index 2d4a0ab..961d8b8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -35,7 +35,7 @@ AM_CPPFLAGS = -I $(top_srcdir)/include $(GLIB_CFLAGS) $(NSS_CFLAGS) \ ## Targets lib_LTLIBRARIES = lib/libncrypto.la pkginclude_HEADERS = include/ncrypto/ncrypto.h -TESTS = tests/digests +TESTS = tests/digests tests/symm_ciphers ## Rules noinst_PROGRAMS = $(TESTS) @@ -45,3 +45,6 @@ lib_libncrypto_la_LDFLAGS = -version-info 0:0:0 $(OPENSSL_LIBS) tests_digests_LDADD = lib/libncrypto.la $(GLIB_LIBS) tests_digests_LDFLAGS = -no-install + +tests_symm_ciphers_LDADD = lib/libncrypto.la $(GLIB_LIBS) +tests_symm_ciphers_LDFLAGS = -no-install diff --git a/include/ncrypto/ncrypto.h b/include/ncrypto/ncrypto.h index af4f518..8da49c0 100644 --- a/include/ncrypto/ncrypto.h +++ b/include/ncrypto/ncrypto.h @@ -53,4 +53,45 @@ CK_RV ncr_digest_standalone (CK_MECHANISM_TYPE mech, void *dest, size_t *dest_size_ptr, const void *data, size_t data_size); +struct ncr_symm_cipher_session; + +struct ncr_symm_key; + +CK_RV ncr_symm_key_create (struct ncr_symm_key **key, CK_KEY_TYPE type, + const void *value, size_t value_size); +CK_RV ncr_symm_key_destroy (struct ncr_symm_key *key); + +/* Session lifetime management. */ +CK_RV ncr_symm_cipher_alloc (struct ncr_symm_cipher_session **sess, + CK_MECHANISM_TYPE mech); +CK_RV ncr_symm_cipher_free (struct ncr_symm_cipher_session *sess); +/* Use either ncr_symm_cipher_encrypt_{init,update,final} (), or + ncr_symm_cipher_{encrypt_init,encrypt} (). After finishing such a call + sequence, a new sequence can be started within the same session. Same for + decryption sequences. */ +CK_RV ncr_symm_cipher_encrypt_init (struct ncr_symm_cipher_session *sess, + struct ncr_symm_key *key, + const void *param, size_t param_size); +CK_RV ncr_symm_cipher_encrypt_update (struct ncr_symm_cipher_session *sess, + void *dest, size_t *dest_size_ptr, + const void *src, size_t src_size); +CK_RV ncr_symm_cipher_encrypt_final (struct ncr_symm_cipher_session *sess, + void *dest, size_t *dest_size_ptr, + const void *src, size_t src_size); +CK_RV ncr_symm_cipher_encrypt (struct ncr_symm_cipher_session *sess, void *dest, + size_t *dest_size_ptr, const void *src, + size_t src_size); +CK_RV ncr_symm_cipher_decrypt_init (struct ncr_symm_cipher_session *sess, + struct ncr_symm_key *key, + const void *param, size_t param_size); +CK_RV ncr_symm_cipher_decrypt_update (struct ncr_symm_cipher_session *sess, + void *dest, size_t *dest_size_ptr, + const void *src, size_t src_size); +CK_RV ncr_symm_cipher_decrypt_final (struct ncr_symm_cipher_session *sess, + void *dest, size_t *dest_size_ptr, + const void *src, size_t src_size); +CK_RV ncr_symm_cipher_decrypt (struct ncr_symm_cipher_session *sess, void *dest, + size_t *dest_size_ptr, const void *src, + size_t src_size); + #endif diff --git a/lib/ncrypto_local.c b/lib/ncrypto_local.c index 8c686b7..7080c68 100644 --- a/lib/ncrypto_local.c +++ b/lib/ncrypto_local.c @@ -26,6 +26,10 @@ POSSIBILITY OF SUCH DAMAGE. Red Hat author: Miloslav Trmač <mitr@redhat.com> */ #include <config.h> +#include <stdbool.h> +#include <stdint.h> +#include <string.h> + #include <glib.h> #include <openssl/evp.h> @@ -40,6 +44,47 @@ ckr_openssl (void) return CKR_GENERAL_ERROR; } + /* Symmetric keys */ + +struct ncr_symm_key +{ + CK_KEY_TYPE type; + size_t size; + uint8_t value[]; +}; + +CK_RV +ncr_symm_key_create (struct ncr_symm_key **key, CK_KEY_TYPE type, + const void *value, size_t value_size) +{ + struct ncr_symm_key *k; + + g_return_val_if_fail (key != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (value != NULL || value_size == 0, CKR_ARGUMENTS_BAD); + /* FIXME? validate "type" */ + /* FIXME: PKCS#11 modules should refuse keys with incorrect parity here */ + + k = malloc (sizeof (*k) + value_size); + if (k == NULL) + return CKR_HOST_MEMORY; + + k->type = type; + k->size = value_size; + memcpy (k->value, value, value_size); + *key = k; + return CKR_OK; +} + +CK_RV +ncr_symm_key_destroy (struct ncr_symm_key *key) +{ + g_return_val_if_fail (key != NULL, CKR_ARGUMENTS_BAD); + + memset (key->value, 0, key->size); /* FIXME: is this safe WRT optimization? */ + free (key); + return CKR_OK; +} + /* Digest handling */ static const EVP_MD * @@ -230,3 +275,300 @@ ncr_digest_standalone (CK_MECHANISM_TYPE mech, void *dest, return EVP_Digest(data, data_size, dest, NULL, md, NULL) != 0 ? CKR_OK : ckr_openssl (); } + + /* Symmetric encryption */ + +struct ncr_symm_cipher_session +{ + EVP_CIPHER_CTX ctx; + CK_MECHANISM_TYPE mech; + size_t padding_size; /* Additional space to reserve for padding */ + _Bool encrypting; + /* Debugging only */ + enum { NSCS_NEW, NSCS_INITIALIZED, NSCS_UPDATED, NSCS_FINISHED } state; +}; + +CK_RV +ncr_symm_cipher_alloc (struct ncr_symm_cipher_session **sess, + CK_MECHANISM_TYPE mech) +{ + struct ncr_symm_cipher_session *s; + + g_return_val_if_fail (sess != NULL, CKR_ARGUMENTS_BAD); + /* FIXME? Validate "mech" */ + + s = malloc (sizeof (*s)); + if (s == NULL) + return CKR_HOST_MEMORY; + + EVP_CIPHER_CTX_init (&s->ctx); + s->mech = mech; + s->state = NSCS_NEW; + *sess = s; + return CKR_OK; +} + +CK_RV +ncr_symm_cipher_free (struct ncr_symm_cipher_session *sess) +{ + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + + EVP_CIPHER_CTX_cleanup (&sess->ctx); + free (sess); + return CKR_OK; +} + +static CK_RV +symm_cipher_init (struct ncr_symm_cipher_session *sess, _Bool encrypt, + struct ncr_symm_key *key, const void *param, + size_t param_size) +{ + const EVP_CIPHER *type; + _Bool padding; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSCS_NEW || sess->state == NSCS_FINISHED, + CKR_OPERATION_ACTIVE); + g_return_val_if_fail (key != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (param != NULL || param_size == 0, CKR_ARGUMENTS_BAD); + + switch (sess->mech) + { +#define AES_SWITCH(MODE) \ + switch (key->size) \ + { \ + AES_ENTRY (MODE, 128); \ + AES_ENTRY (MODE, 192); \ + AES_ENTRY (MODE, 256); \ + default: \ + g_return_val_if_reached (CKR_KEY_SIZE_RANGE); \ + } +#define AES_ENTRY(MODE, SIZE) \ + case (SIZE) / 8: \ + type = EVP_aes_##SIZE##_##MODE (); \ + break; + + case CKM_AES_ECB: + g_return_val_if_fail (key->type == CKK_AES, CKR_KEY_TYPE_INCONSISTENT); + g_return_val_if_fail (param_size == 0, CKR_MECHANISM_PARAM_INVALID); + AES_SWITCH (ecb); + padding = false; + break; + + case CKM_AES_CBC: + case CKM_AES_CBC_PAD: + g_return_val_if_fail (key->type == CKK_AES, CKR_KEY_TYPE_INCONSISTENT); + g_return_val_if_fail (param_size == 16, CKR_MECHANISM_PARAM_INVALID); + AES_SWITCH (cbc); + padding = sess->mech == CKM_AES_CBC_PAD; + break; +#undef AES_ENTRY + + case CKM_DES3_ECB: + g_return_val_if_fail (key->type == CKK_DES3, CKR_KEY_TYPE_INCONSISTENT); + g_return_val_if_fail (key->size == 24, CKR_KEY_SIZE_RANGE); + g_return_val_if_fail (param_size == 0, CKR_MECHANISM_PARAM_INVALID); + type = EVP_des_ede3(); + padding = false; + break; + + case CKM_DES3_CBC: + case CKM_DES3_CBC_PAD: + g_return_val_if_fail (key->type == CKK_DES3, CKR_KEY_TYPE_INCONSISTENT); + g_return_val_if_fail (key->size == 24, CKR_KEY_SIZE_RANGE); + g_return_val_if_fail (param_size == 8, CKR_MECHANISM_PARAM_INVALID); + type = EVP_des_ede3_cbc(); + padding = sess->mech == CKM_DES3_CBC_PAD; + break; + + default: + g_return_val_if_reached (CKR_MECHANISM_INVALID); + } + + if (EVP_CipherInit_ex (&sess->ctx, type, NULL, key->value, + param_size != 0 ? param : NULL, encrypt ? 1 : 0) == 0) + return ckr_openssl (); + if (!padding && EVP_CIPHER_CTX_set_padding (&sess->ctx, 0) == 0) + return ckr_openssl (); + + sess->padding_size = padding ? EVP_CIPHER_block_size (type) : 0; + sess->encrypting = encrypt; + sess->state = NSCS_INITIALIZED; + return CKR_OK; +} + +static CK_RV +symm_cipher_update (struct ncr_symm_cipher_session *sess, _Bool encrypt, + void *dest, size_t *dest_size_ptr, const void *src, + size_t src_size) +{ + int outl; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->state == NSCS_INITIALIZED + || sess->state == NSCS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + g_return_val_if_fail (sess->encrypting == encrypt, + CKR_OPERATION_NOT_INITIALIZED); + + if (dest == NULL) + { + *dest_size_ptr = src_size + sess->padding_size; + return CKR_OK; + } + if (*dest_size_ptr < src_size) /* FIXME? this does not handle partial data */ + { + *dest_size_ptr = src_size; + g_return_val_if_reached (CKR_BUFFER_TOO_SMALL); + } + + g_return_val_if_fail (dest != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (src != NULL || src_size == 0, CKR_ARGUMENTS_BAD); + + if (EVP_CipherUpdate (&sess->ctx, dest, &outl, src, src_size) == 0) + return ckr_openssl (); + + *dest_size_ptr = outl; + sess->state = NSCS_UPDATED; + return CKR_OK; +} + +/* EVP_CipherUpdate + EVP_CipherFinal_ex */ +static CK_RV +do_symm_cipher_update_final (struct ncr_symm_cipher_session *sess, + _Bool encrypt, void *dest, size_t *dest_size_ptr, + const void *src, size_t src_size) +{ + int outl; + size_t dest_size; + + /* The caller has verified session and its state. */ + g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->encrypting == encrypt, + CKR_OPERATION_NOT_INITIALIZED); + + if (dest == NULL) + { + *dest_size_ptr = src_size + sess->padding_size; + return CKR_OK; + } + /* FIXME? this does not handle partial data or padding. */ + if (*dest_size_ptr < src_size) + { + *dest_size_ptr = src_size; + g_return_val_if_reached (CKR_BUFFER_TOO_SMALL); + } + + g_return_val_if_fail (dest != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (src != NULL || src_size == 0, CKR_ARGUMENTS_BAD); + + if (EVP_CipherUpdate (&sess->ctx, dest, &outl, src, src_size) == 0) + { + sess->state = NSCS_FINISHED; + return ckr_openssl (); + } + + dest_size = outl; + + if (EVP_CipherFinal_ex (&sess->ctx, (uint8_t *)dest + outl, &outl) == 0) + { + sess->state = NSCS_FINISHED; + return ckr_openssl (); + } + + *dest_size_ptr = dest_size + outl; + + sess->state = NSCS_FINISHED; + return CKR_OK; +} + +static CK_RV +symm_cipher_final (struct ncr_symm_cipher_session *sess, _Bool encrypt, + void *dest, size_t *dest_size_ptr, const void *src, + size_t src_size) +{ + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSCS_INITIALIZED + || sess->state == NSCS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + + return do_symm_cipher_update_final (sess, encrypt, dest, dest_size_ptr, src, + src_size); +} + +static CK_RV +symm_cipher (struct ncr_symm_cipher_session *sess, _Bool encrypt, void *dest, + size_t *dest_size_ptr, const void *src, size_t src_size) +{ + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSCS_INITIALIZED, + CKR_OPERATION_NOT_INITIALIZED); + + return do_symm_cipher_update_final (sess, encrypt, dest, dest_size_ptr, src, + src_size); +} + +CK_RV +ncr_symm_cipher_encrypt_init (struct ncr_symm_cipher_session *sess, + struct ncr_symm_key *key, const void *param, + size_t param_size) +{ + return symm_cipher_init (sess, true, key, param, param_size); +} + +CK_RV +ncr_symm_cipher_encrypt_update (struct ncr_symm_cipher_session *sess, + void *dest, size_t *dest_size_ptr, + const void *src, size_t src_size) +{ + return symm_cipher_update (sess, true, dest, dest_size_ptr, src, src_size); +} + +CK_RV +ncr_symm_cipher_encrypt_final (struct ncr_symm_cipher_session *sess, void *dest, + size_t *dest_size_ptr, const void *src, + size_t src_size) +{ + return symm_cipher_final (sess, true, dest, dest_size_ptr, src, src_size); +} + +CK_RV +ncr_symm_cipher_encrypt (struct ncr_symm_cipher_session *sess, void *dest, + size_t *dest_size_ptr, const void *src, + size_t src_size) +{ + return symm_cipher (sess, true, dest, dest_size_ptr, src, src_size); +} + +CK_RV +ncr_symm_cipher_decrypt_init (struct ncr_symm_cipher_session *sess, + struct ncr_symm_key *key, const void *param, + size_t param_size) +{ + return symm_cipher_init (sess, false, key, param, param_size); +} + +CK_RV +ncr_symm_cipher_decrypt_update (struct ncr_symm_cipher_session *sess, + void *dest, size_t *dest_size_ptr, + const void *src, size_t src_size) +{ + return symm_cipher_update (sess, false, dest, dest_size_ptr, src, src_size); +} + +CK_RV +ncr_symm_cipher_decrypt_final (struct ncr_symm_cipher_session *sess, void *dest, + size_t *dest_size_ptr, const void *src, + size_t src_size) +{ + return symm_cipher_final (sess, false, dest, dest_size_ptr, src, src_size); +} + +CK_RV +ncr_symm_cipher_decrypt (struct ncr_symm_cipher_session *sess, void *dest, + size_t *dest_size_ptr, const void *src, + size_t src_size) +{ + return symm_cipher (sess, false, dest, dest_size_ptr, src, src_size); +} diff --git a/tests/symm_ciphers.c b/tests/symm_ciphers.c new file mode 100644 index 0000000..ccf6fe5 --- /dev/null +++ b/tests/symm_ciphers.c @@ -0,0 +1,174 @@ +/* ncr_symm_cipher_* tests. + +Copyright 2010 Red Hat, Inc. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO +EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER +IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + +Red Hat author: Miloslav Trmač <mitr@redhat.com> */ + +#include <assert.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#include <glib.h> +#include <ncrypto/ncrypto.h> + +struct tv +{ + CK_MECHANISM_TYPE mech; + CK_KEY_TYPE key_type; + const uint8_t *key; + size_t key_size; + const uint8_t *input; + size_t input_size; + const uint8_t *output; + size_t output_size; +}; + +/* FIXME: Test non-ECB modes as well. */ +static const struct tv tvs[] = + { +#define TV(M, K, KEY, IN, OUT) \ + { \ + (M), (K), (const uint8_t *)(KEY), sizeof (KEY) - 1, \ + (const uint8_t *)(IN), sizeof (IN) - 1, (const uint8_t *)(OUT), \ + sizeof (OUT) - 1 \ + } + TV (CKM_AES_ECB, CKK_AES, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F", + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", + "\x69\xC4\xE0\xD8\x6A\x7B\x04\x30\xD8\xCD\xB7\x80\x70\xB4\xC5\x5A"), + TV (CKM_AES_ECB, CKK_AES, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17", + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", + "\xDD\xA9\x7C\xA4\x86\x4C\xDF\xE0\x6E\xAF\x70\xA0\xEC\x0D\x71\x91"), + TV (CKM_AES_ECB, CKK_AES, + "\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F", + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xAA\xBB\xCC\xDD\xEE\xFF", + "\x8E\xA2\xB7\xCA\x51\x67\x45\xBF\xEA\xFC\x49\x90\x4B\x49\x60\x89"), +#undef TV + }; + +int +main (void) +{ + struct ncr_symm_cipher_session *sess; + struct ncr_symm_key *key; + uint8_t dest[256]; + size_t i, j, dest_size; + CK_RV res; + + for (i = 0; i < G_N_ELEMENTS (tvs); i++) + { + res = ncr_symm_cipher_alloc (&sess, tvs[i].mech); + assert (res == CKR_OK); + + res = ncr_symm_key_create (&key, tvs[i].key_type, tvs[i].key, + tvs[i].key_size); + assert (res == CKR_OK); + + for (j = 0; j < 2; j++) + { + res = ncr_symm_cipher_encrypt_init (sess, key, NULL, 0); + assert (res == CKR_OK); + + dest_size = sizeof (dest); + res = ncr_symm_cipher_encrypt_update (sess, dest, &dest_size, + tvs[i].input, + tvs[i].input_size); + assert (res == CKR_OK); + assert (dest_size == tvs[i].output_size); + assert (memcmp (dest, tvs[i].output, dest_size) == 0); + + dest_size = sizeof (dest); + res = ncr_symm_cipher_encrypt_final (sess, dest, &dest_size, + NULL, 0); + assert (res == CKR_OK); + assert (dest_size == 0); + + res = ncr_symm_cipher_decrypt_init (sess, key, NULL, 0); + assert (res == CKR_OK); + + dest_size = sizeof (dest); + res = ncr_symm_cipher_decrypt_update (sess, dest, &dest_size, + tvs[i].output, + tvs[i].output_size); + assert (res == CKR_OK); + assert (dest_size == tvs[i].input_size); + assert (memcmp (dest, tvs[i].input, dest_size) == 0); + + dest_size = sizeof (dest); + res = ncr_symm_cipher_decrypt_final (sess, dest, &dest_size, + NULL, 0); + assert (res == CKR_OK); + assert (dest_size == 0); + } + + res = ncr_symm_key_destroy (key); + assert (res == CKR_OK); + + res = ncr_symm_cipher_free (sess); + assert (res == CKR_OK); + } + + for (i = 0; i < G_N_ELEMENTS (tvs); i++) + { + res = ncr_symm_cipher_alloc (&sess, tvs[i].mech); + assert (res == CKR_OK); + + res = ncr_symm_key_create (&key, tvs[i].key_type, tvs[i].key, + tvs[i].key_size); + assert (res == CKR_OK); + + for (j = 0; j < 2; j++) + { + res = ncr_symm_cipher_encrypt_init (sess, key, NULL, 0); + assert (res == CKR_OK); + + dest_size = sizeof (dest); + res = ncr_symm_cipher_encrypt (sess, dest, &dest_size, tvs[i].input, + tvs[i].input_size); + assert (res == CKR_OK); + assert (dest_size == tvs[i].output_size); + assert (memcmp (dest, tvs[i].output, dest_size) == 0); + + res = ncr_symm_cipher_decrypt_init (sess, key, NULL, 0); + assert (res == CKR_OK); + + dest_size = sizeof (dest); + res = ncr_symm_cipher_decrypt (sess, dest, &dest_size, tvs[i].output, + tvs[i].output_size); + assert (res == CKR_OK); + assert (dest_size == tvs[i].input_size); + assert (memcmp (dest, tvs[i].input, dest_size) == 0); + } + + res = ncr_symm_key_destroy (key); + assert (res == CKR_OK); + + res = ncr_symm_cipher_free (sess); + assert (res == CKR_OK); + } + + return EXIT_SUCCESS; +} |