diff options
-rw-r--r-- | .gitignore | 2 | ||||
-rw-r--r-- | Makefile.am | 4 | ||||
-rw-r--r-- | lib/ncrypto_alg.c | 1272 | ||||
-rw-r--r-- | lib/ncrypto_local.c | 809 |
4 files changed, 1276 insertions, 811 deletions
@@ -12,4 +12,6 @@ /build /config.h.in /configure +# For builds with other kernels +/include/linux /m4
\ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 7019a5f..fd2eaf2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -41,8 +41,8 @@ TESTS = tests/digests tests/private_keys tests/rsa \ ## Rules noinst_PROGRAMS = $(TESTS) -lib_libncrypto_la_SOURCES = lib/internal.h lib/ncrypto.c lib/ncrypto_local.c \ - lib/ncrypto_nss.c +lib_libncrypto_la_SOURCES = lib/internal.h lib/ncrypto.c lib/ncrypto_alg.c \ + lib/ncrypto_local.c lib/ncrypto_nss.c lib_libncrypto_la_LDFLAGS = -version-info 0:0:0 $(NSS_LIBS) $(OPENSSL_LIBS) tests_digests_LDADD = lib/libncrypto.la $(GLIB_LIBS) diff --git a/lib/ncrypto_alg.c b/lib/ncrypto_alg.c new file mode 100644 index 0000000..bc72383 --- /dev/null +++ b/lib/ncrypto_alg.c @@ -0,0 +1,1272 @@ +/* AF_ALG crypto implementation. + +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 <config.h> + +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <glib.h> +#include <linux/if_alg.h> + +#include <ncrypto/ncrypto.h> + +#include "internal.h" + +/* This is in <linux/socket.h>, but not exposed by it to user-space. */ +#define AF_ALG 38 /* Algorithm sockets */ +#define SOL_ALG 279 + + /* Helpers */ + +static CK_RV +ckr_errno (void) +{ + /* FIXME: Something better */ + return CKR_GENERAL_ERROR; +} + + /* Digest handling */ + +/* Semantically, md_size_ptr should be "size_t *", but values will fit into + "int *" just as fine. */ +static CK_RV +digest_params_from_mech (CK_MECHANISM_TYPE mech, + const struct sockaddr_alg **sa_ptr, int *md_size_ptr) +{ + const struct sockaddr_alg *sa; + int md_size; + + switch (mech) + { +#define E(MECH, NAME, SIZE) \ + case CKM_##MECH: \ + { \ + static const struct sockaddr_alg addr = \ + { .salg_family = AF_ALG, .salg_type = "hash", .salg_name = (NAME) }; \ + \ + sa = &addr; \ + md_size = (SIZE); \ + break; \ + } \ + + E (MD5, "md5", 16); + E (SHA_1, "sha1", 20); + E (SHA224, "sha224", 28); + E (SHA256, "sha256", 32); + E (SHA384, "sha384", 48); + E (SHA512, "sha512", 64); +#undef E + default: + g_return_val_if_reached (CKR_MECHANISM_INVALID); + } + *sa_ptr = sa; + *md_size_ptr = md_size; + return CKR_OK; +} + +struct ncr_digest_session +{ + int fd; + int md_size; /* size_t semantically */ + /* Debugging only */ + enum { NDS_NEW, NDS_INITIALIZED, NDS_UPDATED, NDS_FINISHED } state; +}; + +CK_RV +ncr_digest_alloc (struct ncr_digest_session **sess, CK_MECHANISM_TYPE mech) +{ + struct ncr_digest_session *s; + const struct sockaddr_alg *sa; + int top_fd; + CK_RV res; + + g_return_val_if_fail (sess != NULL, CKR_ARGUMENTS_BAD); + + s = malloc (sizeof (*s)); + if (s == NULL) + return CKR_HOST_MEMORY; + + res = digest_params_from_mech (mech, &sa, &s->md_size); + if (res != CKR_OK) + goto err_s; + + top_fd = socket (AF_ALG, SOCK_SEQPACKET, 0); + if (top_fd == -1) + { + res = ckr_errno (); + goto err_s; + } + if (bind (top_fd, (struct sockaddr *)sa, sizeof (*sa)) != 0) + { + res = ckr_errno (); + goto err_top_fd; + } + + s->fd = accept (top_fd, NULL, 0); + if (s->fd == -1) + { + res = ckr_errno (); + goto err_top_fd; + } + (void)close (top_fd); + + s->state = NDS_NEW; + *sess = s; + return CKR_OK; + + err_top_fd: + (void)close (top_fd); + err_s: + free (s); + return res; +} + +CK_RV +ncr_digest_free (struct ncr_digest_session *sess) +{ + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + + (void)close (sess->fd); + free (sess); + return CKR_OK; +} + +CK_RV +ncr_digest_clone (struct ncr_digest_session **clone, + struct ncr_digest_session *sess) +{ + struct ncr_digest_session *c; + + g_return_val_if_fail (clone != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NDS_INITIALIZED + || sess->state == NDS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + + c = malloc (sizeof (*c)); + if (c == NULL) + return CKR_HOST_MEMORY; + + c->fd = accept (sess->fd, NULL, 0); + if (c->fd == -1) + { + free (c); + return ckr_errno (); + } + c->state = sess->state; + c->md_size = sess->md_size; + *clone = c; + return CKR_OK; +} + +CK_RV +ncr_digest_init (struct ncr_digest_session *sess) +{ + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NDS_NEW || sess->state == NDS_FINISHED, + CKR_OPERATION_ACTIVE); + /* Nothing to do. */ + sess->state = NDS_INITIALIZED; + return CKR_OK; +} + +CK_RV +ncr_digest_update (struct ncr_digest_session *sess, const void *data, + size_t size) +{ + ssize_t res; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (size <= SSIZE_MAX, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->state == NDS_INITIALIZED + || sess->state == NDS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + + res = sendto (sess->fd, data, size, MSG_MORE, NULL, 0); + if (res == -1) + return ckr_errno (); + if (res != (ssize_t)size) /* "size" overflow verified above */ + return CKR_GENERAL_ERROR; /* What? */ + + sess->state = NDS_UPDATED; + return CKR_OK; +} + +CK_RV +ncr_digest_final (struct ncr_digest_session *sess, void *dest, + size_t *size_ptr) +{ + ssize_t res; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NDS_INITIALIZED + || sess->state == NDS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + g_return_val_if_fail (size_ptr != NULL, CKR_ARGUMENTS_BAD); + + if (dest == NULL) + { + *size_ptr = sess->md_size; + return CKR_OK; + } + if (*size_ptr < (size_t)sess->md_size) + { + *size_ptr = sess->md_size; + return CKR_BUFFER_TOO_SMALL; + } + *size_ptr = sess->md_size; + + res = write (sess->fd, "", 0); + if (res != -1) + res = read (sess->fd, dest, sess->md_size); + sess->state = NDS_FINISHED; + if (res == sess->md_size) + return CKR_OK; + if (res == -1) + return ckr_errno (); + return CKR_GENERAL_ERROR; /* What? */ +} + +CK_RV +ncr_digest (struct ncr_digest_session *sess, void *dest, size_t *dest_size_ptr, + const void *data, size_t data_size) +{ + ssize_t res; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NDS_INITIALIZED, + CKR_OPERATION_NOT_INITIALIZED); + g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); + + if (dest == NULL) + { + *dest_size_ptr = sess->md_size; + return CKR_OK; + } + if (*dest_size_ptr < (size_t)sess->md_size) + { + *dest_size_ptr = sess->md_size; + return CKR_BUFFER_TOO_SMALL; + } + *dest_size_ptr = sess->md_size; + + g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (data_size <= SSIZE_MAX, CKR_ARGUMENTS_BAD); + + sess->state = NDS_FINISHED; + + res = write (sess->fd, data, data_size); + if (res == (ssize_t)data_size) /* "data_size" overflow verified above */ + { + res = read (sess->fd, dest, sess->md_size); + if (res == sess->md_size) + return CKR_OK; + } + if (res == -1) + return ckr_errno (); + return CKR_GENERAL_ERROR; /* What? */ +} + +CK_RV +ncr_digest_standalone (CK_MECHANISM_TYPE mech, void *dest, + size_t *dest_size_ptr, const void *data, + size_t data_size) +{ + const struct sockaddr_alg *sa; + int top_fd, fd, md_size; + ssize_t res; + CK_RV ret; + + g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); + + ret = digest_params_from_mech (mech, &sa, &md_size); + if (ret != CKR_OK) + return ret; + + if (dest == NULL) + { + *dest_size_ptr = md_size; + return CKR_OK; + } + if (*dest_size_ptr < (size_t)md_size) + { + *dest_size_ptr = md_size; + return CKR_BUFFER_TOO_SMALL; + } + *dest_size_ptr = md_size; + + g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (data_size <= SSIZE_MAX, CKR_ARGUMENTS_BAD); + + top_fd = socket (AF_ALG, SOCK_SEQPACKET, 0); + if (top_fd == -1) + return ckr_errno (); + if (bind (top_fd, (struct sockaddr *)sa, sizeof (*sa)) != 0) + goto err_top_fd; + fd = accept (top_fd, NULL, 0); + if (fd == -1) + goto err_top_fd; + (void)close (top_fd); + + res = write (fd, data, data_size); + if (res == (ssize_t)data_size) /* "data_size" overflow verified above */ + { + res = read (fd, dest, md_size); + if (res == md_size) + { + ret = CKR_OK; + goto end; + } + } + if (res == -1) + ret = ckr_errno (); + else + ret = CKR_GENERAL_ERROR; /* What? */ + + end: + close (fd); + return ret; + + err_top_fd: + ret = ckr_errno (); + (void)close (top_fd); + return ret; +} + + /* Symmetric signature handling */ + +#define MAX_HMAC_SIZE 64 + +/* In the kernel, digests and HMAC are both .salg_type = "hash", but we need + to treat them differently: For digest sessions, we only need to keep the + slave file descriptor, but for HMAC we need the parent to be able to change + the key. */ + +struct ncr_symm_signature_session +{ + int parent_fd, child_fd; + const struct sockaddr_alg *sa; + int md_size; /* size_t semantically */ + bool signing; + /* Debugging only */ + enum { NSSS_NEW, NSSS_INITIALIZED, NSSS_UPDATED, NSSS_FINISHED } state; +}; + +static CK_RV +symm_signature_params_from_mech (struct ncr_symm_signature_session *sess, + CK_MECHANISM_TYPE mech) +{ + const struct sockaddr_alg *sa; + int md_size; + + switch (mech) + { +#define E(MECH, NAME, SIZE) \ + case CKM_##MECH: \ + { \ + static const struct sockaddr_alg addr = \ + { .salg_family = AF_ALG, .salg_type = "hash", .salg_name = (NAME) }; \ + G_STATIC_ASSERT ((SIZE) <= MAX_HMAC_SIZE); \ + \ + sa = &addr; \ + md_size = (SIZE); \ + break; \ + } + E (MD5_HMAC, "hmac(md5)", 16); + E (SHA_1_HMAC, "hmac(sha1)", 20); + E (SHA224_HMAC, "hmac(sha224)", 28); + E (SHA256_HMAC, "hmac(sha256)", 32); + E (SHA384_HMAC, "hmac(sha384)", 48); + E (SHA512_HMAC, "hmac(sha512)", 64); +#undef E + default: + g_return_val_if_reached (CKR_MECHANISM_INVALID); + } + sess->sa = sa; + sess->md_size = md_size; + return CKR_OK; +} + +CK_RV +ncr_symm_signature_alloc (struct ncr_symm_signature_session **sess, + CK_MECHANISM_TYPE mech) +{ + struct ncr_symm_signature_session *s; + CK_RV res; + + g_return_val_if_fail (sess != NULL, CKR_ARGUMENTS_BAD); + + s = malloc (sizeof (*s)); + if (s == NULL) + return CKR_HOST_MEMORY; + + res = symm_signature_params_from_mech (s, mech); + if (res != CKR_OK) + goto err_s; + + s->parent_fd = socket (AF_ALG, SOCK_SEQPACKET, 0); + if (s->parent_fd == -1) + { + res = ckr_errno (); + goto err_s; + } + if (bind (s->parent_fd, (struct sockaddr *)s->sa, sizeof (*s->sa)) != 0) + { + res = ckr_errno (); + goto err_parent_fd; + } + + s->child_fd = -1; + s->state = NSSS_NEW; + *sess = s; + return CKR_OK; + + err_parent_fd: + (void)close (s->parent_fd); + err_s: + free (s); + return res; +} + +CK_RV +ncr_symm_signature_free (struct ncr_symm_signature_session *sess) +{ + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + + if (sess->child_fd != -1) + (void)close (sess->child_fd); + (void)close (sess->parent_fd); + free (sess); + return CKR_OK; +} + +CK_RV +ncr_symm_signature_clone (struct ncr_symm_signature_session **clone, + struct ncr_symm_signature_session *sess) +{ + struct ncr_symm_signature_session *c; + int new_sess_parent_fd; + CK_RV res; + + g_return_val_if_fail (clone != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSSS_INITIALIZED + || sess->state == NSSS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + + c = malloc (sizeof (*c)); + if (c == NULL) + return CKR_HOST_MEMORY; + + /* This is a little complicated: parent_fd holds the "transform", which is + parametrized by the key. A mere accept () on child_fd would create two + sockets sharing a parent_fd, and reinitializing a session on one with a + different key would change the underlying key of the other. + + Therefore, create an entirely new parent_fd for _both_ clones. The + "old" parent_fd will be implicitly referred to by child_fd of both clones, + but any session reinitialization will use the new parent_fd values. */ + new_sess_parent_fd = socket (AF_ALG, SOCK_SEQPACKET, 0); + if (new_sess_parent_fd == -1) + { + res = ckr_errno (); + goto err_c; + } + if (bind (new_sess_parent_fd, (struct sockaddr *)sess->sa, + sizeof (*sess->sa)) != 0) + { + res = ckr_errno (); + goto err_new_sess_parent_fd; + } + + c->parent_fd = socket (AF_ALG, SOCK_SEQPACKET, 0); + if (c->parent_fd == -1) + { + res = ckr_errno (); + goto err_new_sess_parent_fd; + } + if (bind (c->parent_fd, (struct sockaddr *)sess->sa, sizeof (*sess->sa)) != 0) + { + res = ckr_errno (); + goto err_c_parent_fd; + } + + c->child_fd = accept (sess->child_fd, NULL, 0); + if (c->child_fd == -1) + { + res = ckr_errno (); + goto err_c_parent_fd; + } + + (void)close (sess->parent_fd); + sess->parent_fd = new_sess_parent_fd; + c->state = sess->state; + c->sa = sess->sa; + c->md_size = sess->md_size; + c->signing = sess->signing; + *clone = c; + return CKR_OK; + + err_c_parent_fd: + (void)close (c->parent_fd); + err_new_sess_parent_fd: + (void)close (new_sess_parent_fd); + err_c: + free (c); + return res; +} + +static CK_RV +symm_signature_init (struct ncr_symm_signature_session *sess, bool sign, + struct ncr_symm_key *key) +{ + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSSS_NEW || sess->state == NSSS_FINISHED, + CKR_OPERATION_ACTIVE); + g_return_val_if_fail (key != NULL, CKR_KEY_HANDLE_INVALID); + /* We need key->size to fit into socklen_t. There is no SOCKLEN_MAX, but + it is at least INT32_MAX, which is plenty. */ + g_return_val_if_fail (key->size <= INT32_MAX, CKR_KEY_SIZE_RANGE); + + /* This is not assured, but holds for supported mechanisms. */ + g_return_val_if_fail (key->type == CKK_GENERIC_SECRET, + CKR_KEY_TYPE_INCONSISTENT); + + if (setsockopt (sess->parent_fd, SOL_ALG, ALG_SET_KEY, key->value, + key->size) != 0) + return ckr_errno (); + + sess->child_fd = accept (sess->parent_fd, NULL, 0); + if (sess->child_fd == -1) + return ckr_errno (); + + sess->signing = sign; + sess->state = NSSS_INITIALIZED; + return CKR_OK; +} + +static CK_RV +symm_signature_update (struct ncr_symm_signature_session *sess, bool sign, + const void *data, size_t size) +{ + ssize_t res; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (size <= SSIZE_MAX, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->state == NSSS_INITIALIZED + || sess->state == NSSS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + g_return_val_if_fail (sess->signing == sign, CKR_OPERATION_NOT_INITIALIZED); + + res = sendto (sess->child_fd, data, size, MSG_MORE, NULL, 0); + if (res == -1) + return ckr_errno (); + if (res != (ssize_t)size) /* "size" overflow verified above */ + return CKR_GENERAL_ERROR; /* What? */ + + sess->state = NSSS_UPDATED; + return CKR_OK; +} + +CK_RV +ncr_symm_signature_sign_init (struct ncr_symm_signature_session *sess, + struct ncr_symm_key *key) +{ + return symm_signature_init (sess, true, key); +} + +CK_RV +ncr_symm_signature_sign_update (struct ncr_symm_signature_session *sess, + const void *data, size_t size) +{ + return symm_signature_update (sess, true, data, size); +} + +CK_RV +ncr_symm_signature_sign_final (struct ncr_symm_signature_session *sess, + void *dest, size_t *size_ptr) +{ + int res; + CK_RV ret; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSSS_INITIALIZED + || sess->state == NSSS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + g_return_val_if_fail (size_ptr != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->signing == true, CKR_OPERATION_NOT_INITIALIZED); + + if (dest == NULL) + { + *size_ptr = sess->md_size; + return CKR_OK; + } + if (*size_ptr < (size_t)sess->md_size) + { + *size_ptr = sess->md_size; + return CKR_BUFFER_TOO_SMALL; + } + *size_ptr = sess->md_size; + + res = write (sess->child_fd, "", 0); + if (res != -1) + res = read (sess->child_fd, dest, sess->md_size); + if (res == sess->md_size) + ret = CKR_OK; + else if (res == -1) + ret = ckr_errno (); + else + ret = CKR_GENERAL_ERROR; /* What? */ + + (void)close (sess->child_fd); + sess->child_fd = -1; + sess->state = NSSS_FINISHED; + return ret; +} + +CK_RV +ncr_symm_signature_sign (struct ncr_symm_signature_session *sess, void *dest, + size_t *dest_size_ptr, const void *data, + size_t data_size) +{ + int res; + CK_RV ret; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSSS_INITIALIZED, + CKR_OPERATION_NOT_INITIALIZED); + g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->signing == true, CKR_OPERATION_NOT_INITIALIZED); + + if (dest == NULL) + { + *dest_size_ptr = sess->md_size; + return CKR_OK; + } + if (*dest_size_ptr < (size_t)sess->md_size) + { + *dest_size_ptr = sess->md_size; + return CKR_BUFFER_TOO_SMALL; + } + *dest_size_ptr = sess->md_size; + + g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (data_size <= SSIZE_MAX, CKR_ARGUMENTS_BAD); + + res = write (sess->child_fd, data, data_size); + if (res == (ssize_t)data_size) /* "data_size" overflow verified above */ + { + res = read (sess->child_fd, dest, sess->md_size); + if (res == sess->md_size) + { + ret = CKR_OK; + goto end; + } + } + if (res == -1) + ret = ckr_errno (); + else + ret = CKR_GENERAL_ERROR; /* What? */ + + end: + (void)close (sess->child_fd); + sess->child_fd = -1; + sess->state = NSSS_FINISHED; + return ret; +} + +CK_RV +ncr_symm_signature_verify_init (struct ncr_symm_signature_session *sess, + struct ncr_symm_key *key) +{ + return symm_signature_init (sess, false, key); +} + +CK_RV +ncr_symm_signature_verify_update (struct ncr_symm_signature_session *sess, + const void *data, size_t size) +{ + return symm_signature_update (sess, false, data, size); +} + +CK_RV +ncr_symm_signature_verify_final (struct ncr_symm_signature_session *sess, + const void *signature, size_t size) +{ + uint8_t buf[MAX_HMAC_SIZE]; + int res; + CK_RV ret; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSSS_INITIALIZED + || sess->state == NSSS_UPDATED, + CKR_OPERATION_NOT_INITIALIZED); + g_return_val_if_fail (signature != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->signing == false, CKR_OPERATION_NOT_INITIALIZED); + + res = write (sess->child_fd, "", 0); + if (res != -1) + res = read (sess->child_fd, buf, sess->md_size); + if (res == sess->md_size) + { + if (size != (size_t)sess->md_size) + ret = CKR_SIGNATURE_LEN_RANGE; + else if (memcmp (signature, buf, size) != 0) + ret = CKR_SIGNATURE_INVALID; + else + ret = CKR_OK; + } + else if (res == -1) + ret = ckr_errno (); + else + ret = CKR_GENERAL_ERROR; /* What? */ + + (void)close (sess->child_fd); + sess->child_fd = -1; + sess->state = NSSS_FINISHED; + return ret; +} + +CK_RV +ncr_symm_signature_verify (struct ncr_symm_signature_session *sess, + const void *signature, size_t signature_size, + const void *data, size_t data_size) +{ + uint8_t buf[MAX_HMAC_SIZE]; + int res; + CK_RV ret; + + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + g_return_val_if_fail (sess->state == NSSS_INITIALIZED, + CKR_OPERATION_NOT_INITIALIZED); + g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (data_size <= SSIZE_MAX, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (signature != NULL, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->signing == false, CKR_OPERATION_NOT_INITIALIZED); + + res = write (sess->child_fd, data, data_size); + if (res == (ssize_t)data_size) /* "data_size" overflow verified above */ + { + res = read (sess->child_fd, buf, sess->md_size); + if (res == sess->md_size) + { + if (signature_size != (size_t)sess->md_size) + ret = CKR_SIGNATURE_LEN_RANGE; + else if (memcmp (signature, buf, signature_size) != 0) + ret = CKR_SIGNATURE_INVALID; + else + ret = CKR_OK; + goto end; + } + } + if (res == -1) + ret = ckr_errno (); + else + ret = CKR_GENERAL_ERROR; /* What? */ + end: + (void)close (sess->child_fd); + sess->child_fd = -1; + sess->state = NSSS_FINISHED; + return ret; +} + + /* Symmetric encryption */ + +static CK_RV +cipher_params_from_mech (CK_MECHANISM_TYPE mech, + const struct sockaddr_alg **sa_ptr) +{ + const struct sockaddr_alg *sa; + + switch (mech) + { +#define E(MECH, NAME) \ + case CKM_##MECH: \ + { \ + static const struct sockaddr_alg addr = \ + { \ + .salg_family = AF_ALG, .salg_type = "skcipher", \ + .salg_name = (NAME) \ + }; \ + \ + sa = &addr; \ + break; \ + } \ + + E(AES_ECB, "ecb(aes)"); + E(AES_CBC, "cbc(aes)"); + E(DES3_ECB, "ecb(des3_ede)"); + E(DES3_CBC, "cbc(des3_ede)"); + + default: + g_return_val_if_reached (CKR_MECHANISM_INVALID); + } + *sa_ptr = sa; + return CKR_OK; +} + +struct ncr_symm_cipher_session +{ + int parent_fd, child_fd; + CK_MECHANISM_TYPE mech; + 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; + const struct sockaddr_alg *sa; + CK_RV res; + + g_return_val_if_fail (sess != NULL, CKR_ARGUMENTS_BAD); + + res = cipher_params_from_mech (mech, &sa); + if (res != CKR_OK) + return res; + + s = malloc (sizeof (*s)); + if (s == NULL) + return CKR_HOST_MEMORY; + + s->parent_fd = socket (AF_ALG, SOCK_SEQPACKET, 0); + if (s->parent_fd == -1) + { + res = ckr_errno (); + goto err_s; + } + if (bind (s->parent_fd, (struct sockaddr *)sa, sizeof (*sa)) != 0) + { + res = ckr_errno (); + goto err_parent_fd; + } + + s->child_fd = -1; + s->mech = mech; + s->state = NSCS_NEW; + *sess = s; + return CKR_OK; + + err_parent_fd: + (void)close (s->parent_fd); + err_s: + free (s); + return res; +} + +CK_RV +ncr_symm_cipher_free (struct ncr_symm_cipher_session *sess) +{ + g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); + + if (sess->child_fd != -1) + (void)close (sess->child_fd); + (void)close (sess->parent_fd); + free (sess); + return CKR_OK; +} + +CK_RV +ncr_symm_cipher_change_iv (struct ncr_symm_cipher_session *sess, const void *iv, + size_t iv_size) +{ + uint8_t cmsg_buf[4096]; /* The size is arbitrary. */ + size_t cmsg_space; + struct msghdr msg; + struct cmsghdr *cmsg; + struct af_alg_iv iv_header; + + 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); + g_return_val_if_fail (iv != NULL || iv_size == 0, CKR_ARGUMENTS_BAD); + /* Implicitly restricts iv_size so that it fits into iv_header.ivlen */ + cmsg_space = CMSG_SPACE (sizeof (iv_header) + iv_size); + g_return_val_if_fail (cmsg_space > iv_size && cmsg_space <= sizeof (cmsg_buf), + CKR_MECHANISM_PARAM_INVALID); + + /* The IV might be left pending until the first actual data arrives, saving + one system call, but we prefer reporting invalid IV immediately. */ + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + msg.msg_control = cmsg_buf; + msg.msg_controllen = cmsg_space; + + iv_header.ivlen = iv_size; + cmsg = CMSG_FIRSTHDR (&msg); + g_return_val_if_fail (cmsg != NULL, CKR_GENERAL_ERROR); /* What? */ + cmsg->cmsg_len = CMSG_LEN (sizeof (iv_header) + iv_size); + cmsg->cmsg_level = SOL_ALG; + cmsg->cmsg_type = ALG_SET_IV; + memcpy (CMSG_DATA (cmsg), &iv_header, sizeof (iv_header)); + memcpy (CMSG_DATA (cmsg) + sizeof (iv_header), iv, iv_size); + + if (sendmsg (sess->child_fd, &msg, MSG_MORE) != 0) + return ckr_errno (); + + 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) +{ + uint8_t cmsg_buf[4096]; /* The size is arbitrary. */ + size_t cmsg_space; + struct msghdr msg; + struct cmsghdr *cmsg; + struct af_alg_iv iv; + + 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_KEY_HANDLE_INVALID); + /* We need key->size to fit into socklen_t. There is no SOCKLEN_MAX, but + it is at least INT32_MAX, which is plenty. */ + g_return_val_if_fail (key->size <= INT32_MAX, CKR_KEY_SIZE_RANGE); + g_return_val_if_fail (param != NULL || param_size == 0, CKR_ARGUMENTS_BAD); + /* Implicitly restricts param_size so that it fits into iv.ivlen */ + cmsg_space = CMSG_SPACE (sizeof (iv) + param_size); + g_return_val_if_fail (cmsg_space > param_size + && cmsg_space <= sizeof (cmsg_buf), + CKR_MECHANISM_PARAM_INVALID); + + switch (sess->mech) + { + case CKM_AES_ECB: + case CKM_AES_CBC: + g_return_val_if_fail (key->type == CKK_AES, CKR_KEY_TYPE_INCONSISTENT); + break; + + case CKM_DES3_ECB: + case CKM_DES3_CBC: + g_return_val_if_fail (key->type == CKK_DES3, CKR_KEY_TYPE_INCONSISTENT); + break; + + default: + g_return_val_if_reached (CKR_MECHANISM_INVALID); + } + + if (setsockopt (sess->parent_fd, SOL_ALG, ALG_SET_KEY, key->value, + key->size) != 0) + return ckr_errno (); + + sess->child_fd = accept (sess->parent_fd, NULL, 0); + if (sess->child_fd == -1) + return ckr_errno (); + + /* The IV might be left pending until the first actual data arrives, saving + one system call, but we prefer reporting invalid IV immediately. */ + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = NULL; + msg.msg_iovlen = 0; + msg.msg_control = cmsg_buf; + msg.msg_controllen = cmsg_space; + + iv.ivlen = param_size; + cmsg = CMSG_FIRSTHDR (&msg); + g_return_val_if_fail (cmsg != NULL, CKR_GENERAL_ERROR); /* What? */ + cmsg->cmsg_len = CMSG_LEN (sizeof (iv) + param_size); + cmsg->cmsg_level = SOL_ALG; + cmsg->cmsg_type = ALG_SET_IV; + memcpy (CMSG_DATA (cmsg), &iv, sizeof (iv)); + memcpy (CMSG_DATA (cmsg) + sizeof (iv), param, param_size); + + if (sendmsg (sess->child_fd, &msg, MSG_MORE) != 0) + { + CK_RV res; + + res = ckr_errno (); + (void)close (sess->child_fd); + sess->child_fd = -1; + return res; + } + + 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) +{ + size_t done; + uint32_t alg_op; + uint8_t cmsg_buf[CMSG_SPACE (sizeof (alg_op))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + + 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 (src_size <= SSIZE_MAX, 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; + return CKR_OK; + } + if (*dest_size_ptr < src_size) + { + *dest_size_ptr = src_size; + return CKR_BUFFER_TOO_SMALL; + } + + g_return_val_if_fail (src != NULL || src_size == 0, CKR_ARGUMENTS_BAD); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof (cmsg_buf); + + alg_op = sess->encrypting ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; + cmsg = CMSG_FIRSTHDR (&msg); + cmsg->cmsg_len = CMSG_LEN (sizeof (alg_op)); + cmsg->cmsg_level = SOL_ALG; + cmsg->cmsg_type = ALG_SET_OP; + memcpy (CMSG_DATA (cmsg), &alg_op, sizeof (alg_op)); + + done = 0; + while (done < src_size) + { + ssize_t run, ret; + + iov.iov_base = (uint8_t *)src + done; + iov.iov_len = src_size - done; + run = sendmsg (sess->child_fd, &msg, MSG_DONTWAIT | MSG_MORE); + if (run < 0) + /* The IV may have already been changed if some partial data was + previously accepted, too bad. */ + return ckr_errno (); + g_return_val_if_fail (run != 0, CKR_GENERAL_ERROR); /* What? */ + ret = recvfrom (sess->child_fd, (uint8_t *)dest + done, run, MSG_DONTWAIT, + NULL, 0); + if (ret < 0) + return ckr_errno (); + g_return_val_if_fail (ret == run, CKR_GENERAL_ERROR); + done += run; + } + + *dest_size_ptr = src_size; + 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) +{ + size_t done; + uint32_t alg_op; + uint8_t cmsg_buf[CMSG_SPACE (sizeof (alg_op))]; + struct msghdr msg; + struct cmsghdr *cmsg; + struct iovec iov; + CK_RV res; + + /* 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 (src_size <= SSIZE_MAX, CKR_ARGUMENTS_BAD); + g_return_val_if_fail (sess->encrypting == encrypt, + CKR_OPERATION_NOT_INITIALIZED); + + if (dest == NULL) + { + *dest_size_ptr = src_size; + return CKR_OK; + } + if (*dest_size_ptr < src_size) + { + *dest_size_ptr = src_size; + return CKR_BUFFER_TOO_SMALL; + } + + g_return_val_if_fail (src != NULL || src_size == 0, CKR_ARGUMENTS_BAD); + + msg.msg_name = NULL; + msg.msg_namelen = 0; + msg.msg_iov = &iov; + msg.msg_iovlen = 1; + msg.msg_control = cmsg_buf; + msg.msg_controllen = sizeof (cmsg_buf); + + alg_op = sess->encrypting ? ALG_OP_ENCRYPT : ALG_OP_DECRYPT; + cmsg = CMSG_FIRSTHDR (&msg); + cmsg->cmsg_len = CMSG_LEN (sizeof (alg_op)); + cmsg->cmsg_level = SOL_ALG; + cmsg->cmsg_type = ALG_SET_OP; + memcpy (CMSG_DATA (cmsg), &alg_op, sizeof (alg_op)); + + done = 0; + while (done < src_size) + { + ssize_t run, ret; + + iov.iov_base = (uint8_t *)src + done; + iov.iov_len = src_size - done; + run = sendmsg (sess->child_fd, &msg, MSG_DONTWAIT /* no MSG_MORE */); + if (run < 0) + { + /* The IV may have already been changed if some partial data was + previously accepted, too bad. */ + res = ckr_errno (); + goto err; + } + g_return_val_if_fail (run != 0, CKR_GENERAL_ERROR); /* What? */ + ret = recvfrom (sess->child_fd, (uint8_t *)dest + done, run, MSG_DONTWAIT, + NULL, 0); + if (ret < 0) + { + res = ckr_errno (); + goto err; + } + g_return_val_if_fail (ret == run, CKR_GENERAL_ERROR); + done += run; + } + + *dest_size_ptr = src_size; + res = CKR_OK; + err: + (void)close (sess->child_fd); + sess->child_fd = -1; + sess->state = NSCS_FINISHED; + return res; +} + +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/lib/ncrypto_local.c b/lib/ncrypto_local.c index c4af43a..5a138fc 100644 --- a/lib/ncrypto_local.c +++ b/lib/ncrypto_local.c @@ -31,8 +31,6 @@ Red Hat author: Miloslav Trmač <mitr@redhat.com> */ #include <string.h> #include <glib.h> -#include <openssl/evp.h> -#include <openssl/hmac.h> #include <openssl/rand.h> #include <ncrypto/ncrypto.h> @@ -237,810 +235,3 @@ ncr_symm_key_destroy (struct ncr_symm_key *key) free (key); return CKR_OK; } - - /* Digest handling */ - -static const EVP_MD * -digest_evp_from_mech (CK_MECHANISM_TYPE mech) -{ - switch (mech) - { -#define E(M, E) \ - case CKM_##M: \ - return EVP_##E (); - E (MD5, md5); - E (SHA_1, sha1); - E (SHA224, sha224); - E (SHA256, sha256); - E (SHA384, sha384); - E (SHA512, sha512); -#undef E - default: - g_return_val_if_reached (NULL); - } -} - -struct ncr_digest_session -{ - EVP_MD_CTX ctx; - const EVP_MD *md; - size_t md_size; - /* Debugging only */ - enum { NDS_NEW, NDS_INITIALIZED, NDS_UPDATED, NDS_FINISHED } state; -}; - -CK_RV -ncr_digest_alloc (struct ncr_digest_session **sess, CK_MECHANISM_TYPE mech) -{ - struct ncr_digest_session *s; - const EVP_MD *md; - - g_return_val_if_fail (sess != NULL, CKR_ARGUMENTS_BAD); - - md = digest_evp_from_mech (mech); - if (md == NULL) - return CKR_MECHANISM_INVALID; - - s = malloc (sizeof (*s)); - if (s == NULL) - return CKR_HOST_MEMORY; - - EVP_MD_CTX_init (&s->ctx); - s->state = NDS_NEW; - s->md = md; - s->md_size = EVP_MD_size (md); - *sess = s; - return CKR_OK; -} - -CK_RV -ncr_digest_free (struct ncr_digest_session *sess) -{ - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - - EVP_MD_CTX_cleanup (&sess->ctx); - free (sess); - return CKR_OK; -} - -CK_RV -ncr_digest_clone (struct ncr_digest_session **clone, - struct ncr_digest_session *sess) -{ - struct ncr_digest_session *c; - - g_return_val_if_fail (clone != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NDS_INITIALIZED - || sess->state == NDS_UPDATED, - CKR_OPERATION_NOT_INITIALIZED); - - c = malloc (sizeof (*c)); - if (c == NULL) - return CKR_HOST_MEMORY; - - EVP_MD_CTX_init (&c->ctx); - if (EVP_MD_CTX_copy_ex (&c->ctx, &sess->ctx) == 0) - { - free (c); - return ckr_openssl (); - } - c->state = sess->state; - c->md = sess->md; - c->md_size = sess->md_size; - *clone = c; - return CKR_OK; -} - -CK_RV -ncr_digest_init (struct ncr_digest_session *sess) -{ - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NDS_NEW || sess->state == NDS_FINISHED, - CKR_OPERATION_ACTIVE); - - if (EVP_DigestInit_ex (&sess->ctx, sess->md, NULL) == 0) - return ckr_openssl (); - - sess->state = NDS_INITIALIZED; - return CKR_OK; -} - -CK_RV -ncr_digest_update (struct ncr_digest_session *sess, const void *data, - size_t size) -{ - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (sess->state == NDS_INITIALIZED - || sess->state == NDS_UPDATED, - CKR_OPERATION_NOT_INITIALIZED); - - if (EVP_DigestUpdate (&sess->ctx, data, size) == 0) - return ckr_openssl (); - - sess->state = NDS_UPDATED; - return CKR_OK; -} - -CK_RV -ncr_digest_final (struct ncr_digest_session *sess, void *dest, - size_t *size_ptr) -{ - int res; - - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NDS_INITIALIZED - || sess->state == NDS_UPDATED, - CKR_OPERATION_NOT_INITIALIZED); - g_return_val_if_fail (size_ptr != NULL, CKR_ARGUMENTS_BAD); - - if (dest == NULL) - { - *size_ptr = sess->md_size; - return CKR_OK; - } - if (*size_ptr < sess->md_size) - { - *size_ptr = sess->md_size; - return CKR_BUFFER_TOO_SMALL; - } - *size_ptr = sess->md_size; - - res = EVP_DigestFinal_ex (&sess->ctx, dest, NULL); - sess->state = NDS_FINISHED; - return res ? CKR_OK : ckr_openssl (); -} - -CK_RV -ncr_digest (struct ncr_digest_session *sess, void *dest, size_t *dest_size_ptr, - const void *data, size_t data_size) -{ - int res; - - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NDS_INITIALIZED, - CKR_OPERATION_NOT_INITIALIZED); - g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); - - if (dest == NULL) - { - *dest_size_ptr = sess->md_size; - return CKR_OK; - } - if (*dest_size_ptr < sess->md_size) - { - *dest_size_ptr = sess->md_size; - return CKR_BUFFER_TOO_SMALL; - } - *dest_size_ptr = sess->md_size; - - g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); - - res = (EVP_DigestUpdate (&sess->ctx, data, data_size) != 0 - && EVP_DigestFinal_ex (&sess->ctx, dest, NULL) != 0); - sess->state = NDS_FINISHED; - return res ? CKR_OK : ckr_openssl (); -} - -CK_RV -ncr_digest_standalone (CK_MECHANISM_TYPE mech, void *dest, - size_t *dest_size_ptr, const void *data, - size_t data_size) -{ - const EVP_MD *md; - size_t md_size; - - g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); - - md = digest_evp_from_mech (mech); - if (md == NULL) - return CKR_MECHANISM_INVALID; - - md_size = EVP_MD_size (md); - if (dest == NULL) - { - *dest_size_ptr = md_size; - return CKR_OK; - } - if (*dest_size_ptr < md_size) - { - *dest_size_ptr = md_size; - return CKR_BUFFER_TOO_SMALL; - } - *dest_size_ptr = md_size; - - g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); - - 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; - 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; -} - -CK_RV -ncr_symm_cipher_change_iv (struct ncr_symm_cipher_session *sess, const void *iv, - size_t iv_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); - g_return_val_if_fail (iv != NULL || iv_size == 0, CKR_ARGUMENTS_BAD); - - g_return_val_if_fail (iv_size - == (unsigned)EVP_CIPHER_CTX_iv_length (&sess->ctx), - CKR_MECHANISM_PARAM_INVALID); - g_assert (iv_size <= sizeof (sess->ctx.oiv) - && iv_size <= sizeof (sess->ctx.iv)); - - memcpy (sess->ctx.oiv, iv, iv_size); - memcpy (sess->ctx.iv, iv, iv_size); - 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; - - 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_KEY_HANDLE_INVALID); - 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); - break; - - case CKM_AES_CBC: - 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); - 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 (); - break; - - case CKM_DES3_CBC: - 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 (); - 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 (EVP_CIPHER_CTX_set_padding (&sess->ctx, 0) == 0) - return ckr_openssl (); - - 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; - return CKR_OK; - } - if (*dest_size_ptr < src_size) /* FIXME? this does not handle partial data */ - { - *dest_size_ptr = src_size; - return CKR_BUFFER_TOO_SMALL; - } - - 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; - return CKR_OK; - } - if (*dest_size_ptr < src_size) - { - *dest_size_ptr = src_size; - return CKR_BUFFER_TOO_SMALL; - } - - 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); -} - - /* Symmetric signature handling */ - -static const EVP_MD * -symm_signature_evp_from_hmac_mech (CK_MECHANISM_TYPE mech) -{ - switch (mech) - { -#define E(M, E) \ - case CKM_##M##_HMAC: \ - return EVP_##E (); - E (MD5, md5); - E (SHA_1, sha1); - E (SHA224, sha224); - E (SHA256, sha256); - E (SHA384, sha384); - E (SHA512, sha512); -#undef E - default: - g_return_val_if_reached (NULL); - } -} - -struct ncr_symm_signature_session -{ - HMAC_CTX ctx; - const EVP_MD *md; - size_t md_size; - bool signing; - /* Debugging only */ - enum { NSSS_NEW, NSSS_INITIALIZED, NSSS_UPDATED, NSSS_FINISHED } state; -}; - -CK_RV -ncr_symm_signature_alloc (struct ncr_symm_signature_session **sess, - CK_MECHANISM_TYPE mech) -{ - struct ncr_symm_signature_session *s; - const EVP_MD *md; - - g_return_val_if_fail (sess != NULL, CKR_ARGUMENTS_BAD); - - md = symm_signature_evp_from_hmac_mech (mech); - if (md == NULL) - return CKR_MECHANISM_INVALID; - - s = malloc (sizeof (*s)); - if (s == NULL) - return CKR_HOST_MEMORY; - - HMAC_CTX_init (&s->ctx); - s->state = NSSS_NEW; - s->md = md; - s->md_size = EVP_MD_size (md); - g_assert (s->md_size <= EVP_MAX_MD_SIZE); - *sess = s; - return CKR_OK; -} - -CK_RV -ncr_symm_signature_free (struct ncr_symm_signature_session *sess) -{ - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - - HMAC_CTX_cleanup (&sess->ctx); - free (sess); - return CKR_OK; -} - -CK_RV -ncr_symm_signature_clone (struct ncr_symm_signature_session **clone, - struct ncr_symm_signature_session *sess) -{ - struct ncr_symm_signature_session *c; - - g_return_val_if_fail (clone != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NSSS_INITIALIZED - || sess->state == NSSS_UPDATED, - CKR_OPERATION_NOT_INITIALIZED); - - c = malloc (sizeof (*c)); - if (c == NULL) - return CKR_HOST_MEMORY; - - /* HMAC_CTX_copy is undocumented, and seems not to need MD_CTX_init, but - openssl internally calls HMAC_CTX_init before HMAC_CTX_copy, so we do as - well. */ - HMAC_CTX_init (&c->ctx); - if (HMAC_CTX_copy (&c->ctx, &sess->ctx) == 0) - { - free (c); - return ckr_openssl (); - } - c->state = sess->state; - c->md = sess->md; - c->md_size = sess->md_size; - c->signing = sess->signing; - *clone = c; - return CKR_OK; -} - -static CK_RV -symm_signature_init (struct ncr_symm_signature_session *sess, bool sign, - struct ncr_symm_key *key) -{ - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NSSS_NEW || sess->state == NSSS_FINISHED, - CKR_OPERATION_ACTIVE); - g_return_val_if_fail (key != NULL, CKR_KEY_HANDLE_INVALID); - - /* This is not assured, but holds for supported mechanisms. */ - g_return_val_if_fail (key->type == CKK_GENERIC_SECRET, - CKR_KEY_TYPE_INCONSISTENT); - - if (HMAC_Init_ex (&sess->ctx, key->value, key->size, sess->md, NULL) == 0) - return ckr_openssl (); - - sess->signing = sign; - sess->state = NSSS_INITIALIZED; - return CKR_OK; -} - -static CK_RV -symm_signature_update (struct ncr_symm_signature_session *sess, bool sign, - const void *data, size_t size) -{ - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (sess->state == NSSS_INITIALIZED - || sess->state == NSSS_UPDATED, - CKR_OPERATION_NOT_INITIALIZED); - g_return_val_if_fail (sess->signing == sign, CKR_OPERATION_NOT_INITIALIZED); - - if (HMAC_Update (&sess->ctx, data, size) == 0) - return ckr_openssl (); - - sess->state = NSSS_UPDATED; - return CKR_OK; -} - -CK_RV -ncr_symm_signature_sign_init (struct ncr_symm_signature_session *sess, - struct ncr_symm_key *key) -{ - return symm_signature_init (sess, true, key); -} - -CK_RV -ncr_symm_signature_sign_update (struct ncr_symm_signature_session *sess, - const void *data, size_t size) -{ - return symm_signature_update (sess, true, data, size); -} - -CK_RV -ncr_symm_signature_sign_final (struct ncr_symm_signature_session *sess, - void *dest, size_t *size_ptr) -{ - int res; - - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NSSS_INITIALIZED - || sess->state == NSSS_UPDATED, - CKR_OPERATION_NOT_INITIALIZED); - g_return_val_if_fail (size_ptr != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (sess->signing == true, CKR_OPERATION_NOT_INITIALIZED); - - if (dest == NULL) - { - *size_ptr = sess->md_size; - return CKR_OK; - } - if (*size_ptr < sess->md_size) - { - *size_ptr = sess->md_size; - return CKR_BUFFER_TOO_SMALL; - } - *size_ptr = sess->md_size; - - res = HMAC_Final (&sess->ctx, dest, NULL); - sess->state = NSSS_FINISHED; - return res ? CKR_OK : ckr_openssl (); -} - -CK_RV -ncr_symm_signature_sign (struct ncr_symm_signature_session *sess, void *dest, - size_t *dest_size_ptr, const void *data, - size_t data_size) -{ - int res; - - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NSSS_INITIALIZED, - CKR_OPERATION_NOT_INITIALIZED); - g_return_val_if_fail (dest_size_ptr != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (sess->signing == true, CKR_OPERATION_NOT_INITIALIZED); - - if (dest == NULL) - { - *dest_size_ptr = sess->md_size; - return CKR_OK; - } - if (*dest_size_ptr < sess->md_size) - { - *dest_size_ptr = sess->md_size; - return CKR_BUFFER_TOO_SMALL; - } - *dest_size_ptr = sess->md_size; - - g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); - - res = (HMAC_Update (&sess->ctx, data, data_size) != 0 - && HMAC_Final (&sess->ctx, dest, NULL) != 0); - sess->state = NSSS_FINISHED; - return res ? CKR_OK : ckr_openssl (); -} - -CK_RV -ncr_symm_signature_verify_init (struct ncr_symm_signature_session *sess, - struct ncr_symm_key *key) -{ - return symm_signature_init (sess, false, key); -} - -CK_RV -ncr_symm_signature_verify_update (struct ncr_symm_signature_session *sess, - const void *data, size_t size) -{ - return symm_signature_update (sess, false, data, size); -} - -CK_RV -ncr_symm_signature_verify_final (struct ncr_symm_signature_session *sess, - const void *signature, size_t size) -{ - uint8_t buf[EVP_MAX_MD_SIZE]; - int res; - - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NSSS_INITIALIZED - || sess->state == NSSS_UPDATED, - CKR_OPERATION_NOT_INITIALIZED); - g_return_val_if_fail (signature != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (sess->signing == false, CKR_OPERATION_NOT_INITIALIZED); - - res = HMAC_Final (&sess->ctx, buf, NULL); - sess->state = NSSS_FINISHED; - if (res == 0) - return ckr_openssl (); - if (size != sess->md_size) - return CKR_SIGNATURE_LEN_RANGE; - if (memcmp (signature, buf, size) != 0) - return CKR_SIGNATURE_INVALID; - return CKR_OK; -} - -CK_RV -ncr_symm_signature_verify (struct ncr_symm_signature_session *sess, - const void *signature, size_t signature_size, - const void *data, size_t data_size) -{ - uint8_t buf[EVP_MAX_MD_SIZE]; - int res; - - g_return_val_if_fail (sess != NULL, CKR_SESSION_HANDLE_INVALID); - g_return_val_if_fail (sess->state == NSSS_INITIALIZED, - CKR_OPERATION_NOT_INITIALIZED); - g_return_val_if_fail (data != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (signature != NULL, CKR_ARGUMENTS_BAD); - g_return_val_if_fail (sess->signing == false, CKR_OPERATION_NOT_INITIALIZED); - - res = (HMAC_Update (&sess->ctx, data, data_size) != 0 - && HMAC_Final (&sess->ctx, buf, NULL) != 0); - sess->state = NSSS_FINISHED; - if (res == 0) - return ckr_openssl (); - if (signature_size != sess->md_size) - return CKR_SIGNATURE_LEN_RANGE; - if (memcmp (signature, buf, signature_size) != 0) - return CKR_SIGNATURE_INVALID; - return CKR_OK; -} |