diff --git a/ChangeLog b/ChangeLog index c8e1b00a..b2769491 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,8 @@ +2010-12-30 Milan Broz + * Add compile time crypto backends implementation + (gcrypt, OpenSSL, NSS and userspace Linux kernel crypto api). + * Currently NSS is lacking ripemd160, cannot provide full plain compatibility. + 2010-12-20 Milan Broz * Version 1.2.0. diff --git a/configure.in b/configure.in index e8c4cf47..5b0d44cc 100644 --- a/configure.in +++ b/configure.in @@ -39,9 +39,6 @@ AC_CHECK_LIB(uuid, uuid_clear, ,[AC_MSG_ERROR('You need the uuid library')]) AC_SUBST(UUID_LIBS, $LIBS) LIBS=$saved_LIBS - -AM_PATH_LIBGCRYPT(1.1.42,,[AC_MSG_ERROR('You need the gcrypt library')]) - AC_CHECK_FUNCS([posix_memalign]) AC_C_CONST @@ -64,6 +61,62 @@ AC_CHECK_LIB(popt, poptConfigFileToString,, AC_SUBST(POPT_LIBS, $LIBS) LIBS=$saved_LIBS +dnl ========================================================================== +dnl Crypto backend functions + +AC_DEFUN([CONFIGURE_GCRYPT], [ + AM_PATH_LIBGCRYPT(1.1.42,,[AC_MSG_ERROR('You need the gcrypt library')]) + + if test x$enable_static_cryptsetup = xyes; then + saved_LIBS=$LIBS + LIBS="$saved_LIBS $LIBGCRYPT_LIBS -static" + AC_CHECK_LIB(gcrypt, gcry_check_version,, + AC_MSG_ERROR([Cannot find static gcrypt library.]), + [-lgpg-error]) + LIBGCRYPT_STATIC_LIBS="$LIBGCRYPT_LIBS -lgpg-error" + LIBS=$saved_LIBS + fi + + CRYPTO_CFLAGS=$LIBGCRYPT_CFLAGS + CRYPTO_LIBS=$LIBGCRYPT_LIBS + CRYPTO_STATIC_LIBS=$LIBGCRYPT_STATIC_LIBS +]) + +AC_DEFUN([CONFIGURE_OPENSSL], [ + PKG_CHECK_MODULES([OPENSSL], [openssl >= 0.9.8],, + AC_MSG_ERROR('You need openssl library')) + CRYPTO_CFLAGS=$OPENSSL_CFLAGS + CRYPTO_LIBS=$OPENSSL_LIBS + + if test x$enable_static_cryptsetup = xyes; then + saved_PKG_CONFIG=$PKG_CONFIG + PKG_CONFIG="$PKG_CONFIG --static" + PKG_CHECK_MODULES([OPENSSL], [openssl]) + CRYPTO_STATIC_LIBS=$OPENSSL_LIBS + PKG_CONFIG=$saved_PKG_CONFIG + fi +]) + +AC_DEFUN([CONFIGURE_NSS], [ + if test x$enable_static_cryptsetup = xyes; then + AC_MSG_ERROR([Static build of cryptsetup is not supported with NSS.]), + fi + + PKG_CHECK_MODULES([NSS], [nss],, + AC_MSG_ERROR('You need nss library')) + CRYPTO_CFLAGS=$NSS_CFLAGS + CRYPTO_LIBS=$NSS_LIBS +]) + +AC_DEFUN([CONFIGURE_KERNEL], [ + AC_CHECK_HEADERS(linux/if_alg.h,, + [AC_MSG_ERROR('You need Linux kernel with userspace crypto interface.')]) +# AC_CHECK_DECLS([AF_ALG],, +# [AC_MSG_ERROR('You need Linux kernel with userspace crypto interface.')], +# [#include ]) + +]) + dnl ========================================================================== saved_LIBS=$LIBS @@ -106,17 +159,28 @@ if test "x$enable_udev" = xyes; then fi LIBS=$saved_LIBS +dnl Crypto backend configuration. +AC_ARG_WITH([crypto_backend], + AS_HELP_STRING([--with-crypto_backend], [crypto backend (gcrypt/openssl/nss/kernel) [gcrypt]]), + [], with_crypto_backend=gcrypt +) +case $with_crypto_backend in + gcrypt) CONFIGURE_GCRYPT([]) ;; + openssl) CONFIGURE_OPENSSL([]) ;; + nss) CONFIGURE_NSS([]) ;; + kernel) CONFIGURE_KERNEL([]) ;; + *) AC_MSG_ERROR([Unknown crypto backend.]) ;; +esac +AM_CONDITIONAL(CRYPTO_BACKEND_GCRYPT, test $with_crypto_backend = gcrypt) +AM_CONDITIONAL(CRYPTO_BACKEND_OPENSSL, test $with_crypto_backend = openssl) +AM_CONDITIONAL(CRYPTO_BACKEND_NSS, test $with_crypto_backend = nss) +AM_CONDITIONAL(CRYPTO_BACKEND_KERNEL, test $with_crypto_backend = kernel) + dnl Magic for cryptsetup.static build. if test x$enable_static_cryptsetup = xyes; then saved_PKG_CONFIG=$PKG_CONFIG PKG_CONFIG="$PKG_CONFIG --static" - LIBS="$saved_LIBS $LIBGCRYPT_LIBS -static" - AC_CHECK_LIB(gcrypt, gcry_check_version,, - AC_MSG_ERROR([Cannot find static gcrypt library.]), - [-lgpg-error]) - LIBGCRYPT_STATIC_LIBS="$LIBGCRYPT_LIBS -lgpg-error" - LIBS="$saved_LIBS -static" AC_CHECK_LIB(popt, poptGetContext,, AC_MSG_ERROR([Cannot find static popt library.])) @@ -141,7 +205,11 @@ fi AC_SUBST([DEVMAPPER_LIBS]) AC_SUBST([DEVMAPPER_STATIC_LIBS]) -AC_SUBST([LIBGCRYPT_STATIC_LIBS]) + +AC_SUBST([CRYPTO_CFLAGS]) +AC_SUBST([CRYPTO_LIBS]) +AC_SUBST([CRYPTO_STATIC_LIBS]) + AC_SUBST([LIBCRYPTSETUP_VERSION]) AC_SUBST([LIBCRYPTSETUP_VERSION_INFO]) @@ -184,6 +252,7 @@ dnl ========================================================================== AC_CONFIG_FILES([ Makefile lib/Makefile lib/libcryptsetup.pc +lib/crypto_backend/Makefile lib/luks1/Makefile src/Makefile po/Makefile.in diff --git a/lib/Makefile.am b/lib/Makefile.am index a624e5bf..aeebfc61 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -1,4 +1,4 @@ -SUBDIRS = luks1 +SUBDIRS = crypto_backend luks1 moduledir = $(libdir)/cryptsetup @@ -7,6 +7,7 @@ pkgconfig_DATA = libcryptsetup.pc INCLUDES = \ -I$(top_srcdir) \ + -I$(top_srcdir)/lib/crypto_backend \ -I$(top_srcdir)/lib/luks1 \ -DDATADIR=\""$(datadir)"\" \ -DLIBDIR=\""$(libdir)"\" \ @@ -25,12 +26,13 @@ libcryptsetup_la_LDFLAGS = \ -Wl,--version-script=$(top_srcdir)/lib/libcryptsetup.sym \ -version-info @LIBCRYPTSETUP_VERSION_INFO@ -libcryptsetup_la_CFLAGS = -Wall @LIBGCRYPT_CFLAGS@ +libcryptsetup_la_CFLAGS = -Wall @CRYPTO_CFLAGS@ libcryptsetup_la_LIBADD = \ @UUID_LIBS@ \ @DEVMAPPER_LIBS@ \ - @LIBGCRYPT_LIBS@ \ + @CRYPTO_LIBS@ \ + crypto_backend/libcrypto_backend.la \ luks1/libluks1.la libcryptsetup_la_SOURCES = \ @@ -47,7 +49,8 @@ libcryptsetup_la_SOURCES = \ libdevmapper.c \ volumekey.c \ random.c \ - gcrypt.c + gcrypt.c \ + crypto_backend.h include_HEADERS = libcryptsetup.h diff --git a/lib/crypto_backend/Makefile.am b/lib/crypto_backend/Makefile.am new file mode 100644 index 00000000..59ee5b27 --- /dev/null +++ b/lib/crypto_backend/Makefile.am @@ -0,0 +1,22 @@ +moduledir = $(libdir)/cryptsetup + +noinst_LTLIBRARIES = libcrypto_backend.la + +libcrypto_backend_la_CFLAGS = -Wall @CRYPTO_CFLAGS@ + +libcrypto_backend_la_SOURCES = crypto_backend.h + +if CRYPTO_BACKEND_GCRYPT +libcrypto_backend_la_SOURCES += crypto_gcrypt.c +endif +if CRYPTO_BACKEND_OPENSSL +libcrypto_backend_la_SOURCES += crypto_openssl.c +endif +if CRYPTO_BACKEND_NSS +libcrypto_backend_la_SOURCES += crypto_nss.c +endif +if CRYPTO_BACKEND_KERNEL +libcrypto_backend_la_SOURCES += crypto_kernel.c +endif + +INCLUDES = -D_GNU_SOURCE -I$(top_srcdir)/lib diff --git a/lib/crypto_backend/crypto_backend.h b/lib/crypto_backend/crypto_backend.h new file mode 100644 index 00000000..f411131b --- /dev/null +++ b/lib/crypto_backend/crypto_backend.h @@ -0,0 +1,33 @@ +#ifndef _CRYPTO_BACKEND_H +#define _CRYPTO_BACKEND_H + +#include "libcryptsetup.h" +#include "internal.h" + +struct crypt_hash; +struct crypt_hmac; + +int crypt_backend_init(void); + +#define CRYPT_BACKEND_KERNEL (1 << 0) /* Crypto uses kernel part, for benchmark */ + +uint32_t crypt_backend_flags(); + +/* HASH */ +int crypt_hash_size(const char *name); +int crypt_hash_init(struct crypt_hash **ctx, const char *name); +int crypt_hash_restart(struct crypt_hash *ctx); +int crypt_hash_write(struct crypt_hash *ctx, const char *buffer, size_t length); +int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length); +int crypt_hash_destroy(struct crypt_hash *ctx); + +/* HMAC */ +int crypt_hmac_size(const char *name); +int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, + const void *buffer, size_t length); +int crypt_hmac_restart(struct crypt_hmac *ctx); +int crypt_hmac_write(struct crypt_hmac *ctx, const char *buffer, size_t length); +int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length); +int crypt_hmac_destroy(struct crypt_hmac *ctx); + +#endif /* _CRYPTO_BACKEND_H */ diff --git a/lib/crypto_backend/crypto_gcrypt.c b/lib/crypto_backend/crypto_gcrypt.c new file mode 100644 index 00000000..fe0fba83 --- /dev/null +++ b/lib/crypto_backend/crypto_gcrypt.c @@ -0,0 +1,213 @@ +/* + * GCRYPT crypto backend implementation + * + * Copyright (C) 2010 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include "crypto_backend.h" + +#define GCRYPT_REQ_VERSION "1.1.42" + +struct crypt_hash { + gcry_md_hd_t hd; + int hash_id; + int hash_len; +}; + +struct crypt_hmac { + gcry_md_hd_t hd; + int hash_id; + int hash_len; +}; + +int crypt_backend_init(void) +{ + log_dbg("Initialising gcrypt crypto backend."); + if (!gcry_control (GCRYCTL_INITIALIZATION_FINISHED_P)) { + if (!gcry_check_version (GCRYPT_REQ_VERSION)) { + return -ENOSYS; + } + +/* FIXME: If gcrypt compiled to support POSIX 1003.1e capabilities, + * it drops all privileges during secure memory initialisation. + * For now, the only workaround is to disable secure memory in gcrypt. + * cryptsetup always need at least cap_sys_admin privilege for dm-ioctl + * and it locks its memory space anyway. + */ +#if 0 + log_dbg("Initializing crypto backend (secure memory disabled)."); + gcry_control (GCRYCTL_DISABLE_SECMEM); +#else + + gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); + gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0); + gcry_control (GCRYCTL_RESUME_SECMEM_WARN); +#endif + gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0); + } + + return 0; +} + +uint32_t crypt_backend_flags(void) +{ + return 0; +} + +/* HASH */ +int crypt_hash_size(const char *name) +{ + int hash_id = gcry_md_map_name(name); + + if (!hash_id) + return -EINVAL; + + return gcry_md_get_algo_dlen(hash_id); +} + +int crypt_hash_init(struct crypt_hash **ctx, const char *name) +{ + struct crypt_hash *h; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + h->hash_id = gcry_md_map_name(name); + if (!h->hash_id) { + free(h); + return -EINVAL; + } + + if (gcry_md_open(&h->hd, h->hash_id, 0)) { + free(h); + return -EINVAL; + } + + h->hash_len = gcry_md_get_algo_dlen(h->hash_id); + *ctx = h; + return 0; +} + +int crypt_hash_restart(struct crypt_hash *ctx) +{ + gcry_md_reset(ctx->hd); + return 0; +} + +int crypt_hash_write(struct crypt_hash *ctx, const char *buffer, size_t length) +{ + gcry_md_write(ctx->hd, buffer, length); + return 0; +} + +int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) +{ + unsigned char *hash; + + if (length > ctx->hash_len) + return -EINVAL; + + hash = gcry_md_read(ctx->hd, ctx->hash_id); + if (!hash) + return -EINVAL; + + memcpy(buffer, hash, length); + return 0; +} + +int crypt_hash_destroy(struct crypt_hash *ctx) +{ + gcry_md_close(ctx->hd); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +/* HMAC */ +int crypt_hmac_size(const char *name) +{ + return crypt_hash_size(name); +} + +int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, + const void *buffer, size_t length) +{ + struct crypt_hmac *h; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + h->hash_id = gcry_md_map_name(name); + if (!h->hash_id) { + free(h); + return -EINVAL; + } + + if (gcry_md_open(&h->hd, h->hash_id, GCRY_MD_FLAG_HMAC)) { + free(h); + return -EINVAL; + } + + if (gcry_md_setkey(h->hd, buffer, length)) { + gcry_md_close(h->hd); + free(h); + return -EINVAL; + } + + h->hash_len = gcry_md_get_algo_dlen(h->hash_id); + *ctx = h; + return 0; +} + +int crypt_hmac_restart(struct crypt_hmac *ctx) +{ + gcry_md_reset(ctx->hd); + return 0; +} + +int crypt_hmac_write(struct crypt_hmac *ctx, const char *buffer, size_t length) +{ + gcry_md_write(ctx->hd, buffer, length); + return 0; +} + +int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) +{ + unsigned char *hash; + + if (length > ctx->hash_len) + return -EINVAL; + + hash = gcry_md_read(ctx->hd, ctx->hash_id); + if (!hash) + return -EINVAL; + + memcpy(buffer, hash, length); + return 0; +} + +int crypt_hmac_destroy(struct crypt_hmac *ctx) +{ + gcry_md_close(ctx->hd); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} diff --git a/lib/crypto_backend/crypto_kernel.c b/lib/crypto_backend/crypto_kernel.c new file mode 100644 index 00000000..e3601409 --- /dev/null +++ b/lib/crypto_backend/crypto_kernel.c @@ -0,0 +1,294 @@ +/* + * Linux kernel userspace API crypto backend implementation + * + * Copyright (C) 2010 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "crypto_backend.h" + +/* FIXME: remove later */ +#ifndef AF_ALG +#define AF_ALG 38 +#define SOL_ALG 279 +#endif + +struct hash_alg { + const char *name; + const char *kernel_name; + int length; +}; + +static struct hash_alg hash_algs[] = { + { "sha1", "sha1", 20 }, + { "sha256", "sha256", 32 }, + { "sha512", "sha512", 64 }, + { "ripemd160", "rmd160", 20 }, + { "whirlpool", "wp512", 64 }, + { NULL, 0 } +}; + +struct crypt_hash { + int tfmfd; + int opfd; + int hash_len; +}; + +struct crypt_hmac { + int tfmfd; + int opfd; + int hash_len; +}; + +static int _socket_init(struct sockaddr_alg *sa, int *tfmfd, int *opfd) +{ + *tfmfd = socket(AF_ALG, SOCK_SEQPACKET, 0); + if (*tfmfd == -1) + goto bad; + + if (bind(*tfmfd, (struct sockaddr *)sa, sizeof(*sa)) == -1) + goto bad; + + *opfd = accept(*tfmfd, NULL, 0); + if (*opfd == -1) + goto bad; + + return 0; +bad: + if (*tfmfd != -1) { + close(*tfmfd); + *tfmfd = -1; + } + if (*opfd != -1) { + close(*opfd); + *opfd = -1; + } + return -EINVAL; +} + +int crypt_backend_init(void) +{ + struct utsname uts; + + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "hash", + .salg_name = "sha1", + }; + int tfmfd = -1, opfd = -1; + + log_dbg("Initialising kernel crypto API backend."); + + if (uname(&uts) == -1 || strcmp(uts.sysname, "Linux")) + return -EINVAL; + log_dbg("Kernel version %s %s.", uts.sysname, uts.release); + + if (_socket_init(&sa, &tfmfd, &opfd) < 0) + return -EINVAL; + + close(tfmfd); + close(opfd); + return 0; +} + +uint32_t crypt_backend_flags(void) +{ + return CRYPT_BACKEND_KERNEL; +} + +static struct hash_alg *_get_alg(const char *name) +{ + int i = 0; + + while (name && hash_algs[i].name) { + if (!strcmp(name, hash_algs[i].name)) + return &hash_algs[i]; + i++; + } + return NULL; +} + +/* HASH */ +int crypt_hash_size(const char *name) +{ + struct hash_alg *ha = _get_alg(name); + + return ha ? ha->length : -EINVAL; +} + +int crypt_hash_init(struct crypt_hash **ctx, const char *name) +{ + struct crypt_hash *h; + struct hash_alg *ha; + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "hash", + }; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + ha = _get_alg(name); + if (!ha) { + free(h); + return -EINVAL; + } + h->hash_len = ha->length; + + strncpy((char *)sa.salg_name, ha->kernel_name, sizeof(sa.salg_name)); + + if (_socket_init(&sa, &h->tfmfd, &h->opfd) < 0) { + free(h); + return -EINVAL; + } + + *ctx = h; + return 0; +} + +int crypt_hash_restart(struct crypt_hash *ctx) +{ + return 0; +} + +int crypt_hash_write(struct crypt_hash *ctx, const char *buffer, size_t length) +{ + ssize_t r; + + r = send(ctx->opfd, buffer, length, MSG_MORE); + if (r < 0 || r < length) + return -EIO; + + return 0; +} + +int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) +{ + ssize_t r; + + if (length > ctx->hash_len) + return -EINVAL; + + r = read(ctx->opfd, buffer, length); + if (r < 0) + return -EIO; + + return 0; +} + +int crypt_hash_destroy(struct crypt_hash *ctx) +{ + if (ctx->tfmfd != -1) + close(ctx->tfmfd); + if (ctx->opfd != -1) + close(ctx->opfd); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +/* HMAC */ +int crypt_hmac_size(const char *name) +{ + return crypt_hash_size(name); +} + +int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, + const void *buffer, size_t length) +{ + struct crypt_hmac *h; + struct hash_alg *ha; + struct sockaddr_alg sa = { + .salg_family = AF_ALG, + .salg_type = "hash", + }; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + ha = _get_alg(name); + if (!ha) { + free(h); + return -EINVAL; + } + h->hash_len = ha->length; + + snprintf((char *)sa.salg_name, sizeof(sa.salg_name), + "hmac(%s)", ha->kernel_name); + + if (_socket_init(&sa, &h->tfmfd, &h->opfd) < 0) { + free(h); + return -EINVAL; + } + + if (setsockopt(h->tfmfd, SOL_ALG, ALG_SET_KEY, buffer, length) == -1) { + crypt_hmac_destroy(h); + return -EINVAL; + } + + *ctx = h; + return 0; +} + +int crypt_hmac_restart(struct crypt_hmac *ctx) +{ + return 0; +} + +int crypt_hmac_write(struct crypt_hmac *ctx, const char *buffer, size_t length) +{ + ssize_t r; + + r = send(ctx->opfd, buffer, length, MSG_MORE); + if (r < 0 || r < length) + return -EIO; + + return 0; +} + +int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) +{ + ssize_t r; + + if (length > ctx->hash_len) + return -EINVAL; + + r = read(ctx->opfd, buffer, length); + if (r < 0) + return -EIO; + + return 0; +} + +int crypt_hmac_destroy(struct crypt_hmac *ctx) +{ + if (ctx->tfmfd != -1) + close(ctx->tfmfd); + if (ctx->opfd != -1) + close(ctx->opfd); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} diff --git a/lib/crypto_backend/crypto_nss.c b/lib/crypto_backend/crypto_nss.c new file mode 100644 index 00000000..eecc70af --- /dev/null +++ b/lib/crypto_backend/crypto_nss.c @@ -0,0 +1,264 @@ +/* + * NSS crypto backend implementation + * + * Copyright (C) 2010 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "crypto_backend.h" + +struct hash_alg { + const char *name; + SECOidTag oid; + CK_MECHANISM_TYPE ck_type; + int length; +}; + +static struct hash_alg hash_algs[] = { + { "sha1", SEC_OID_SHA1, CKM_SHA_1_HMAC, 20 }, + { "sha256", SEC_OID_SHA256, CKM_SHA256_HMAC, 32 }, + { "sha384", SEC_OID_SHA384, CKM_SHA384_HMAC, 48 }, + { "sha512", SEC_OID_SHA512, CKM_SHA512_HMAC, 64 }, +// { "ripemd160", SEC_OID_RIPEMD160, CKM_RIPEMD160_HMAC, 20 }, + { NULL, 0, 0, 0 } +}; + +struct crypt_hash { + PK11Context *md; + const struct hash_alg *hash; +}; + +struct crypt_hmac { + PK11Context *md; + PK11SymKey *key; + PK11SlotInfo *slot; + const struct hash_alg *hash; +}; + +static struct hash_alg *_get_alg(const char *name) +{ + int i = 0; + + while (name && hash_algs[i].name) { + if (!strcmp(name, hash_algs[i].name)) + return &hash_algs[i]; + i++; + } + return NULL; +} + +int crypt_backend_init(void) +{ + log_dbg("Initialising NSS crypto backend."); + if (NSS_NoDB_Init(".") != SECSuccess) + return -EINVAL; + + return 0; +} + +uint32_t crypt_backend_flags(void) +{ + return 0; +} + +/* HASH */ +int crypt_hash_size(const char *name) +{ + struct hash_alg *ha = _get_alg(name); + + return ha ? ha->length : -EINVAL; +} + +int crypt_hash_init(struct crypt_hash **ctx, const char *name) +{ + struct crypt_hash *h; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + h->hash = _get_alg(name); + if (!h->hash) { + free(h); + return -EINVAL; + } + + h->md = PK11_CreateDigestContext(h->hash->oid); + if (!h->md) { + free(h); + return -EINVAL; + } + + if (PK11_DigestBegin(h->md) != SECSuccess) { + PK11_DestroyContext(h->md, PR_TRUE); + free(h); + return -EINVAL; + } + + *ctx = h; + return 0; +} + +int crypt_hash_restart(struct crypt_hash *ctx) +{ + if (PK11_DigestBegin(ctx->md) != SECSuccess) + return -EINVAL; + + return 0; +} + +int crypt_hash_write(struct crypt_hash *ctx, const char *buffer, size_t length) +{ + if (PK11_DigestOp(ctx->md, (unsigned char *)buffer, length) != SECSuccess) + return -EINVAL; + + return 0; +} + +int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) +{ + unsigned char tmp[64]; + unsigned int tmp_len; + + if (length > ctx->hash->length) + return -EINVAL; + + if (PK11_DigestFinal(ctx->md, tmp, &tmp_len, length) != SECSuccess) + return -EINVAL; + + memcpy(buffer, tmp, length); + memset(tmp, 0, sizeof(tmp)); + + if (tmp_len < length) + return -EINVAL; + + return 0; +} + +int crypt_hash_destroy(struct crypt_hash *ctx) +{ + PK11_DestroyContext(ctx->md, PR_TRUE); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +/* HMAC */ +int crypt_hmac_size(const char *name) +{ + return crypt_hash_size(name); +} + +int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, + const void *buffer, size_t length) +{ + struct crypt_hmac *h; + SECItem keyItem; + SECItem noParams; + + keyItem.type = siBuffer; + keyItem.data = (unsigned char *)buffer; + keyItem.len = (int)length; + + noParams.type = siBuffer; + noParams.data = 0; + noParams.len = 0; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + memset(ctx, 0, sizeof(*ctx)); + + + h->hash = _get_alg(name); + if (!h->hash) + goto bad; + + h->slot = PK11_GetInternalKeySlot(); + if (!h->slot) + goto bad; + + h->key = PK11_ImportSymKey(h->slot, h->hash->ck_type, PK11_OriginUnwrap, + CKA_SIGN, &keyItem, NULL); + if (!h->key) + goto bad; + + h->md = PK11_CreateContextBySymKey(h->hash->ck_type, CKA_SIGN, h->key, + &noParams); + if (!h->md) + goto bad; + + if (PK11_DigestBegin(h->md) != SECSuccess) + goto bad; + + *ctx = h; + return 0; +bad: + crypt_hmac_destroy(h); + return -EINVAL; +} + +int crypt_hmac_restart(struct crypt_hmac *ctx) +{ + if (PK11_DigestBegin(ctx->md) != SECSuccess) + return -EINVAL; + + return 0; +} + +int crypt_hmac_write(struct crypt_hmac *ctx, const char *buffer, size_t length) +{ + if (PK11_DigestOp(ctx->md, (unsigned char *)buffer, length) != SECSuccess) + return -EINVAL; + + return 0; +} + +int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) +{ + unsigned char tmp[64]; + unsigned int tmp_len; + + if (length > ctx->hash->length) + return -EINVAL; + + if (PK11_DigestFinal(ctx->md, tmp, &tmp_len, length) != SECSuccess) + return -EINVAL; + + memcpy(buffer, tmp, length); + memset(tmp, 0, sizeof(tmp)); + + if (tmp_len < length) + return -EINVAL; + + return 0; +} + +int crypt_hmac_destroy(struct crypt_hmac *ctx) +{ + if (ctx->key) + PK11_FreeSymKey(ctx->key); + if (ctx->slot) + PK11_FreeSlot(ctx->slot); + if (ctx->md) + PK11_DestroyContext(ctx->md, PR_TRUE); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} diff --git a/lib/crypto_backend/crypto_openssl.c b/lib/crypto_backend/crypto_openssl.c new file mode 100644 index 00000000..5ef3845f --- /dev/null +++ b/lib/crypto_backend/crypto_openssl.c @@ -0,0 +1,195 @@ +/* + * OPENSSL crypto backend implementation + * + * Copyright (C) 2010 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 as published by the Free Software Foundation. + * + * This program 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 this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include "crypto_backend.h" + +struct crypt_hash { + EVP_MD_CTX md; + const EVP_MD *hash_id; + int hash_len; +}; + +struct crypt_hmac { + HMAC_CTX md; + const EVP_MD *hash_id; + int hash_len; +}; + +int crypt_backend_init(void) +{ + OpenSSL_add_all_digests(); + log_dbg("OpenSSL crypto backend initialized."); + return 0; +} + +uint32_t crypt_backend_flags(void) +{ + return 0; +} + +/* HASH */ +int crypt_hash_size(const char *name) +{ + const EVP_MD *hash_id = EVP_get_digestbyname(name); + + if (!hash_id) + return -EINVAL; + + return EVP_MD_size(hash_id); +} + +int crypt_hash_init(struct crypt_hash **ctx, const char *name) +{ + struct crypt_hash *h; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + h->hash_id = EVP_get_digestbyname(name); + if (!h->hash_id) { + free(h); + return -EINVAL; + } + + if (EVP_DigestInit(&h->md, h->hash_id) != 1) { + free(h); + return -EINVAL; + } + + h->hash_len = EVP_MD_size(h->hash_id); + *ctx = h; + return 0; +} + +int crypt_hash_restart(struct crypt_hash *ctx) +{ + if (EVP_DigestInit(&ctx->md, ctx->hash_id) != 1) + return -EINVAL; + + return 0; +} + +int crypt_hash_write(struct crypt_hash *ctx, const char *buffer, size_t length) +{ + if (EVP_DigestUpdate(&ctx->md, buffer, length) != 1) + return -EINVAL; + + return 0; +} + +int crypt_hash_final(struct crypt_hash *ctx, char *buffer, size_t length) +{ + unsigned char tmp[EVP_MAX_MD_SIZE]; + unsigned int tmp_len = 0; + + if (length > ctx->hash_len) + return -EINVAL; + + if (EVP_DigestFinal_ex(&ctx->md, tmp, &tmp_len) != 1) + return -EINVAL; + + memcpy(buffer, tmp, length); + memset(tmp, 0, sizeof(tmp)); + + if (tmp_len < length) + return -EINVAL; + + return 0; +} + +int crypt_hash_destroy(struct crypt_hash *ctx) +{ + EVP_MD_CTX_cleanup(&ctx->md); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} + +/* HMAC */ +int crypt_hmac_size(const char *name) +{ + return crypt_hash_size(name); +} + +int crypt_hmac_init(struct crypt_hmac **ctx, const char *name, + const void *buffer, size_t length) +{ + struct crypt_hmac *h; + + h = malloc(sizeof(*h)); + if (!h) + return -ENOMEM; + + h->hash_id = EVP_get_digestbyname(name); + if (!h->hash_id) { + free(h); + return -EINVAL; + } + + HMAC_CTX_init(&h->md); + HMAC_Init_ex(&h->md, buffer, length, h->hash_id, NULL); + + h->hash_len = EVP_MD_size(h->hash_id); + *ctx = h; + return 0; +} + +int crypt_hmac_restart(struct crypt_hmac *ctx) +{ + HMAC_Init_ex(&ctx->md, NULL, 0, ctx->hash_id, NULL); + return 0; +} + +int crypt_hmac_write(struct crypt_hmac *ctx, const char *buffer, size_t length) +{ + HMAC_Update(&ctx->md, buffer, length); + return 0; +} + +int crypt_hmac_final(struct crypt_hmac *ctx, char *buffer, size_t length) +{ + unsigned char tmp[EVP_MAX_MD_SIZE]; + unsigned int tmp_len = 0; + + if (length > ctx->hash_len) + return -EINVAL; + + HMAC_Final(&ctx->md, tmp, &tmp_len); + + memcpy(buffer, tmp, length); + memset(tmp, 0, sizeof(tmp)); + + if (tmp_len < length) + return -EINVAL; + + return 0; +} + +int crypt_hmac_destroy(struct crypt_hmac *ctx) +{ + HMAC_CTX_cleanup(&ctx->md); + memset(ctx, 0, sizeof(*ctx)); + free(ctx); + return 0; +} diff --git a/src/Makefile.am b/src/Makefile.am index 29eb840f..917adf3e 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -28,7 +28,7 @@ cryptsetup_static_SOURCES = $(cryptsetup_SOURCES) cryptsetup_static_CFLAGS = $(cryptsetup_CFLAGS) cryptsetup_static_LDFLAGS = -all-static cryptsetup_static_LDADD = $(cryptsetup_LDADD) \ - @LIBGCRYPT_STATIC_LIBS@ \ + @CRYPTO_STATIC_LIBS@ \ @DEVMAPPER_STATIC_LIBS@ \ @UUID_LIBS@ endif