mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
tpm2d: Add tpm2daemon code
* tpm2d: New directory. * Makefile.am (SUBDIRS): Add directory. * configure.ac: Detect libtss and decide whether to build tpm2d. * am/cmacros.am: Add a define. * util.h (GNUPG_MODULE_NAME_TPM2DAEMON): New. * common/homedir.c (gnupg_module_name): Add tpm2d. * common/mapstrings.c (macros): Add "TPM2DAEMON". * tools/gpgconf.h (GC_COMPONENT_TPM2DAEMON): New. * tools/gpgconf-comp.c (known_options_tpm2daemon): New. (gc_component): Add TPM2. (tpm2daemon_runtime_change): New. * tpm2d/Makefile.am: New. * tpm2d/command.c: New. * tpm2d/ibm-tss.h: New. * tpm2d/tpm2.c: New. * tpm2d/tpm2.h: New. * tpm2d/tpm2daemon.c: New. * tpm2d/tpm2daemon.h: New. --- This commit adds and plumbs in a tpm2daemon to the build to mirror the operation of scdaemon. The architecture of the code is that tpm2daemon.c itself is pretty much a clone of scd/scdaemon.c just with updated function prefixes (this argues there could be some further consolidation of the daemon handling code). Note that although this commit causes the daemon to be built and installed, nothing actually starts it or uses it yet. Command handling ---------------- command.c is copied from the command handler in scd.c except that the command implementation is now done in terms of tpm2 commands and the wire protocol is far simpler. The tpm2daemon only responds to 4 commands IMPORT: import a standard s-expression private key and export it to TPM2 format. This conversion cannot be undone and the private key now can *only* be used by the TPM2. To anyone who gets hold of the private key now, it's just an encrypted binary blob. PKSIGN: create a signature from the tpm2 key. The TPM2 form private key is retrieved by KEYDATA and the hash to be signed by EXTRA. Note there is no hash specifier because the tpm2 tss deduces the hash type from the length of the EXTRA data. This is actually a limitation of the tpm2 command API and it will be interesting to see how this fares if the tpm2 ever supports say sha3-256 hashes. PKDECRYPT: decrypt (RSA case) or derive (ECC case) a symmetric key. The tpm2 for private key is retrieved by KEYDATA and the information used to create the symmetric key by EXTRA. KILLTPM2D: stop the daemon All the tpm2 primitives used by command.c are in tpm2.h and all the tpm2 specific gunk is confined to tpm2.c, which is the only piece of this that actually does calls into the tss library. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> Changes from James' patch: - gpgconf: The displayed name is "TPM" and not "TPM2". That string is used by GUIs and should be something the user understands. For example we also use "network" instead of "Dirmngr". - Removed some commented includes. - Use 16 as emulation of GPG_ERR_SOURCE_TPM2. - Silenced a C90 compiler warning and flags unused parameters. - Removed "if HAVE_LIBS" from tpm2/Makefile.am and add missing files so that make distcheck works. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
0f191a0707
commit
62a7854816
@ -106,10 +106,15 @@ tests =
|
||||
else
|
||||
tests = tests
|
||||
endif
|
||||
if HAVE_LIBTSS
|
||||
tpm2d = tpm2d
|
||||
else
|
||||
tpm2d =
|
||||
endif
|
||||
|
||||
SUBDIRS = m4 common regexp kbx \
|
||||
${gpg} ${sm} ${agent} ${scd} ${g13} ${dirmngr} \
|
||||
tools po ${doc} ${tests}
|
||||
tools po ${doc} ${tests} ${tpm2d}
|
||||
|
||||
dist_doc_DATA = README
|
||||
|
||||
|
@ -44,6 +44,9 @@ endif
|
||||
if GNUPG_SCDAEMON_PGM
|
||||
AM_CPPFLAGS += -DGNUPG_DEFAULT_SCDAEMON="\"@GNUPG_SCDAEMON_PGM@\""
|
||||
endif
|
||||
if GNUPG_TPM2DAEMON_PGM
|
||||
AM_CPPFLAGS += -DGNUPG_DEFAULT_TPM2DAEMON="\"@GNUPG_TPM2DAEMON_PGM@\""
|
||||
endif
|
||||
if GNUPG_DIRMNGR_PGM
|
||||
AM_CPPFLAGS += -DGNUPG_DEFAULT_DIRMNGR="\"@GNUPG_DIRMNGR_PGM@\""
|
||||
endif
|
||||
|
@ -1153,6 +1153,13 @@ gnupg_module_name (int which)
|
||||
X(libexecdir, "scd", "scdaemon");
|
||||
#endif
|
||||
|
||||
case GNUPG_MODULE_NAME_TPM2DAEMON:
|
||||
#ifdef GNUPG_DEFAULT_TPM2DAEMON
|
||||
return GNUPG_DEFAULT_TPM2DAEMON;
|
||||
#else
|
||||
X(libexecdir, "tpm2d", TPM2DAEMON_NAME);
|
||||
#endif
|
||||
|
||||
case GNUPG_MODULE_NAME_DIRMNGR:
|
||||
#ifdef GNUPG_DEFAULT_DIRMNGR
|
||||
return GNUPG_DEFAULT_DIRMNGR;
|
||||
|
@ -50,6 +50,7 @@ static struct {
|
||||
{ "GPGSM", GPGSM_NAME },
|
||||
{ "GPG_AGENT", GPG_AGENT_NAME },
|
||||
{ "SCDAEMON", SCDAEMON_NAME },
|
||||
{ "TPM2DAEMON",TPM2DAEMON_NAME},
|
||||
{ "DIRMNGR", DIRMNGR_NAME },
|
||||
{ "G13", G13_NAME },
|
||||
{ "GPGCONF", GPGCONF_NAME },
|
||||
|
@ -293,6 +293,7 @@ char *_gnupg_socketdir_internal (int skip_checks, unsigned *r_info);
|
||||
#define GNUPG_MODULE_NAME_DIRMNGR_LDAP 11
|
||||
#define GNUPG_MODULE_NAME_GPGV 12
|
||||
#define GNUPG_MODULE_NAME_KEYBOXD 13
|
||||
#define GNUPG_MODULE_NAME_TPM2DAEMON 14
|
||||
const char *gnupg_module_name (int which);
|
||||
void gnupg_module_name_flush_some (void);
|
||||
void gnupg_set_builddir (const char *newdir);
|
||||
|
46
configure.ac
46
configure.ac
@ -101,6 +101,7 @@ have_gnutls=no
|
||||
have_sqlite=no
|
||||
have_npth=no
|
||||
have_libusb=no
|
||||
have_libtss=no
|
||||
have_system_resolver=no
|
||||
gnupg_have_ldap="n/a"
|
||||
|
||||
@ -184,6 +185,15 @@ show_gnupg_scdaemon_pgm="(default)"
|
||||
test -n "$GNUPG_SCDAEMON_PGM" && show_gnupg_scdaemon_pgm="$GNUPG_SCDAEMON_PGM"
|
||||
|
||||
|
||||
AC_ARG_WITH(tpm2daemon-pgm,
|
||||
[ --with-tpm2daemon-pgm=PATH Use PATH as the default for the tpm2daemon)],
|
||||
GNUPG_TPM2DAEMON_PGM="$withval", GNUPG_TPM2DAEMON_PGM="" )
|
||||
AC_SUBST(GNUPG_TPM2DAEMON_PGM)
|
||||
AM_CONDITIONAL(GNUPG_TPM2DAEMON_PGM, test -n "$GNUPG_TPM2DAEMON_PGM")
|
||||
show_gnupg_tpm2daemon_pgm="(default)"
|
||||
test -n "$GNUPG_TPM2DAEMON_PGM" && show_gnupg_tpm2daemon_pgm="$GNUPG_TPM2DAEMON_PGM"
|
||||
|
||||
|
||||
AC_ARG_WITH(dirmngr-pgm,
|
||||
[ --with-dirmngr-pgm=PATH Use PATH as the default for the dirmngr)],
|
||||
GNUPG_DIRMNGR_PGM="$withval", GNUPG_DIRMNGR_PGM="" )
|
||||
@ -1580,6 +1590,33 @@ fi
|
||||
AC_SUBST(NETLIBS)
|
||||
AC_SUBST(W32SOCKLIBS)
|
||||
|
||||
#
|
||||
# TPM libtss library .. don't compile TPM support if we don't have it
|
||||
#
|
||||
_save_libs="$LIBS"
|
||||
_save_cflags="$CFLAGS"
|
||||
LIBS=""
|
||||
AC_SEARCH_LIBS([TSS_Create], [tss ibmtss],have_libtss=yes,)
|
||||
if test "$have_libtss" = yes; then
|
||||
LIBTSS_CFLAGS="-DTPM_POSIX"
|
||||
CFLAGS="$CFLAGS ${LIBTSS_CFLAGS}"
|
||||
AC_CHECK_HEADER([tss2/tss.h],[AC_DEFINE(TSS_INCLUDE,tss2, [tss2 include location])], [
|
||||
AC_CHECK_HEADER([ibmtss/tss.h],[AC_DEFINE(TSS_INCLUDE,ibmtss, [ibmtss include location])], [
|
||||
AC_MSG_WARN([No TSS2 include directory found, disabling TPM support])
|
||||
have_libtss=no
|
||||
])
|
||||
])
|
||||
LIBTSS_LIBS=$LIBS
|
||||
AC_DEFINE(HAVE_LIBTSS, 1, [Defined if we have TPM2 support library])
|
||||
AC_SUBST(TSS_INCLUDE)
|
||||
fi
|
||||
LIBS="$_save_libs"
|
||||
CFLAGS="$_save_cflags"
|
||||
AC_SUBST(LIBTSS_LIBS)
|
||||
AC_SUBST(LIBTSS_CFLAGS)
|
||||
AM_CONDITIONAL(HAVE_LIBTSS, test "$have_libtss" = yes)
|
||||
AC_SUBST(HAVE_LIBTSS)
|
||||
|
||||
#
|
||||
# Setup gcc specific options
|
||||
#
|
||||
@ -1845,6 +1882,10 @@ AC_DEFINE_UNQUOTED(GPG_AGENT_NAME, "gpg-agent", [The name of the agent])
|
||||
AC_DEFINE_UNQUOTED(GPG_AGENT_DISP_NAME, "GPG Agent",
|
||||
[The displayed name of gpg-agent])
|
||||
|
||||
AC_DEFINE_UNQUOTED(TPM2DAEMON_NAME, "tpm2daemon", [The name of the TPM2 daemon])
|
||||
AC_DEFINE_UNQUOTED(TPM2DAEMON_DISP_NAME, "TPM2 Daemon",
|
||||
[The displayed name of TPM2 daemon])
|
||||
|
||||
AC_DEFINE_UNQUOTED(SCDAEMON_NAME, "scdaemon", [The name of the scdaemon])
|
||||
AC_DEFINE_UNQUOTED(SCDAEMON_DISP_NAME, "SCDaemon",
|
||||
[The displayed name of scdaemon])
|
||||
@ -1880,6 +1921,8 @@ AC_DEFINE_UNQUOTED(SCDAEMON_SOCK_NAME, "S.scdaemon",
|
||||
[The name of the SCdaemon socket])
|
||||
AC_DEFINE_UNQUOTED(KEYBOXD_SOCK_NAME, "S.keyboxd",
|
||||
[The name of the keyboxd socket])
|
||||
AC_DEFINE_UNQUOTED(TPM2DAEMON_SOCK_NAME, "S.tpm2daemon",
|
||||
[The name of the TPM2 daemon socket])
|
||||
AC_DEFINE_UNQUOTED(DIRMNGR_SOCK_NAME, "S.dirmngr",
|
||||
[The name of the dirmngr socket])
|
||||
AC_DEFINE_UNQUOTED(DIRMNGR_DEFAULT_KEYSERVER,
|
||||
@ -2040,6 +2083,7 @@ g10/Makefile
|
||||
sm/Makefile
|
||||
agent/Makefile
|
||||
scd/Makefile
|
||||
tpm2d/Makefile
|
||||
g13/Makefile
|
||||
dirmngr/Makefile
|
||||
tools/Makefile
|
||||
@ -2086,6 +2130,7 @@ echo "
|
||||
Default pinentry: $show_gnupg_pinentry_pgm
|
||||
Default scdaemon: $show_gnupg_scdaemon_pgm
|
||||
Default keyboxd: $show_gnupg_keyboxd_pgm
|
||||
Default tpm2daemon: $show_gnupg_tpm2daemon_pgm
|
||||
Default dirmngr: $show_gnupg_dirmngr_pgm
|
||||
|
||||
Dirmngr auto start: $dirmngr_auto_start
|
||||
@ -2094,6 +2139,7 @@ echo "
|
||||
TLS support: $use_tls_library
|
||||
TOFU support: $use_tofu
|
||||
Tor support: $show_tor_support
|
||||
TPM support: $have_libtss
|
||||
"
|
||||
if test "x${gpg_config_script_warn}" != x; then
|
||||
cat <<G10EOF
|
||||
|
@ -92,11 +92,11 @@ gc_error (int status, int errnum, const char *fmt, ...)
|
||||
/* Forward declaration. */
|
||||
static void gpg_agent_runtime_change (int killflag);
|
||||
static void scdaemon_runtime_change (int killflag);
|
||||
static void tpm2daemon_runtime_change (int killflag);
|
||||
static void dirmngr_runtime_change (int killflag);
|
||||
static void keyboxd_runtime_change (int killflag);
|
||||
|
||||
|
||||
|
||||
|
||||
/* STRING_ARRAY is a malloced array with malloced strings. It is used
|
||||
* a space to store strings so that other objects may point to these
|
||||
@ -378,6 +378,21 @@ static known_option_t known_options_scdaemon[] =
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
/* The known options of the GC_COMPONENT_TPM2DAEMON component. */
|
||||
static known_option_t known_options_tpm2daemon[] =
|
||||
{
|
||||
{ "verbose", GC_OPT_FLAG_LIST|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC },
|
||||
{ "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC },
|
||||
{ "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
|
||||
{ "debug-level", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED},
|
||||
{ "log-file", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED,
|
||||
GC_ARG_TYPE_FILENAME },
|
||||
{ "deny-admin", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC },
|
||||
{ "parent", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED },
|
||||
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
||||
/* The known options of the GC_COMPONENT_GPG component. */
|
||||
static known_option_t known_options_gpg[] =
|
||||
@ -627,6 +642,10 @@ static struct
|
||||
GNUPG_MODULE_NAME_SCDAEMON, SCDAEMON_NAME ".conf",
|
||||
known_options_scdaemon, scdaemon_runtime_change},
|
||||
|
||||
{ TPM2DAEMON_NAME, TPM2DAEMON_DISP_NAME, "gnupg", N_("TPM"),
|
||||
GNUPG_MODULE_NAME_TPM2DAEMON, TPM2DAEMON_NAME ".conf",
|
||||
known_options_tpm2daemon, tpm2daemon_runtime_change},
|
||||
|
||||
{ DIRMNGR_NAME, DIRMNGR_DISP_NAME, "gnupg", N_("Network"),
|
||||
GNUPG_MODULE_NAME_DIRMNGR, DIRMNGR_NAME ".conf",
|
||||
known_options_dirmngr, dirmngr_runtime_change },
|
||||
@ -753,6 +772,47 @@ scdaemon_runtime_change (int killflag)
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
tpm2daemon_runtime_change (int killflag)
|
||||
{
|
||||
gpg_error_t err = 0;
|
||||
const char *pgmname;
|
||||
const char *argv[9];
|
||||
pid_t pid = (pid_t)(-1);
|
||||
int i = 0;
|
||||
|
||||
(void)killflag; /* For scdaemon kill and reload are synonyms. */
|
||||
|
||||
/* We use "GETINFO app_running" to see whether the agent is already
|
||||
running and kill it only in this case. This avoids an explicit
|
||||
starting of the agent in case it is not yet running. There is
|
||||
obviously a race condition but that should not harm too much. */
|
||||
|
||||
pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT);
|
||||
if (!gnupg_default_homedir_p ())
|
||||
{
|
||||
argv[i++] = "--homedir";
|
||||
argv[i++] = gnupg_homedir ();
|
||||
}
|
||||
argv[i++] = "-s";
|
||||
argv[i++] = "--no-autostart";
|
||||
argv[i++] = "GETINFO tpm2d_running";
|
||||
argv[i++] = "/if ${! $?}";
|
||||
argv[i++] = "scd killtpm2cd";
|
||||
argv[i++] = "/end";
|
||||
argv[i++] = NULL;
|
||||
|
||||
if (!err)
|
||||
err = gnupg_spawn_process_fd (pgmname, argv, -1, -1, -1, &pid);
|
||||
if (!err)
|
||||
err = gnupg_wait_process (pgmname, pid, 1, NULL);
|
||||
if (err)
|
||||
gc_error (0, 0, "error running '%s %s': %s",
|
||||
pgmname, argv[4], gpg_strerror (err));
|
||||
gnupg_release_process (pid);
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
dirmngr_runtime_change (int killflag)
|
||||
{
|
||||
|
@ -67,6 +67,9 @@ typedef enum
|
||||
/* The Smardcard Daemon. */
|
||||
GC_COMPONENT_SCDAEMON,
|
||||
|
||||
/* The TPM2 Daemon. */
|
||||
GC_COMPONENT_TPM2DAEMON,
|
||||
|
||||
/* The LDAP Directory Manager for CRLs. */
|
||||
GC_COMPONENT_DIRMNGR,
|
||||
|
||||
|
18
tpm2d/Makefile.am
Normal file
18
tpm2d/Makefile.am
Normal file
@ -0,0 +1,18 @@
|
||||
AM_CPPFLAGS =
|
||||
|
||||
include $(top_srcdir)/am/cmacros.am
|
||||
|
||||
libexec_PROGRAMS = tpm2daemon
|
||||
|
||||
AM_CFLAGS = $(LIBGCRYPT_CFLAGS) \
|
||||
$(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) $(LIBTSS_CFLAGS)
|
||||
|
||||
tpm2daemon_SOURCES = \
|
||||
command.c \
|
||||
tpm2daemon.c \
|
||||
tpm2.c tpm2.h \
|
||||
tpm2daemon.h ibm-tss.h
|
||||
|
||||
tpm2daemon_LDADD = $(libcommonpth) \
|
||||
$(LIBGCRYPT_LIBS) $(KSBA_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
|
||||
$(GPG_ERROR_LIBS) $(LIBINTL) $(LIBICONV) $(LIBTSS_LIBS)
|
504
tpm2d/command.c
Normal file
504
tpm2d/command.c
Normal file
@ -0,0 +1,504 @@
|
||||
/* command.c - TPM2daemon command handler
|
||||
* Copyright (C) 2001, 2002, 2003, 2004, 2005,
|
||||
* 2007, 2008, 2009, 2011 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#ifdef USE_NPTH
|
||||
# include <npth.h>
|
||||
#endif
|
||||
|
||||
#include "tpm2daemon.h"
|
||||
#include "tpm2.h"
|
||||
#include <assuan.h>
|
||||
#include <ksba.h>
|
||||
#include "../common/asshelp.h"
|
||||
#include "../common/server-help.h"
|
||||
|
||||
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
|
||||
#define MAXLEN_PIN 100
|
||||
|
||||
/* Maximum allowed size of key data as used in inquiries. */
|
||||
#define MAXLEN_KEYDATA 4096
|
||||
|
||||
/* Maximum allowed total data size for SETDATA. */
|
||||
#define MAXLEN_SETDATA 4096
|
||||
|
||||
/* Maximum allowed size of certificate data as used in inquiries. */
|
||||
#define MAXLEN_CERTDATA 16384
|
||||
|
||||
|
||||
#define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
|
||||
|
||||
/* Data used to associate an Assuan context with local server data.
|
||||
This object describes the local properties of one session. */
|
||||
struct server_local_s
|
||||
{
|
||||
/* We keep a list of all active sessions with the anchor at
|
||||
SESSION_LIST (see below). This field is used for linking. */
|
||||
struct server_local_s *next_session;
|
||||
|
||||
/* This object is usually assigned to a CTRL object (which is
|
||||
globally visible). While enumerating all sessions we sometimes
|
||||
need to access data of the CTRL object; thus we keep a
|
||||
backpointer here. */
|
||||
ctrl_t ctrl_backlink;
|
||||
|
||||
/* The Assuan context used by this session/server. */
|
||||
assuan_context_t assuan_ctx;
|
||||
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
unsigned long event_signal; /* Or 0 if not used. */
|
||||
#else
|
||||
int event_signal; /* Or 0 if not used. */
|
||||
#endif
|
||||
|
||||
/* True if the card has been removed and a reset is required to
|
||||
continue operation. */
|
||||
int card_removed;
|
||||
|
||||
/* If set to true we will be terminate ourself at the end of the
|
||||
this session. */
|
||||
int stopme;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* To keep track of all running sessions, we link all active server
|
||||
contexts and the anchor in this variable. */
|
||||
static struct server_local_s *session_list;
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
reset_notify (assuan_context_t ctx, char *line)
|
||||
{
|
||||
(void) ctx;
|
||||
(void) line;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
option_handler (assuan_context_t ctx, const char *key, const char *value)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
|
||||
if (!strcmp (key, "event-signal"))
|
||||
{
|
||||
/* A value of 0 is allowed to reset the event signal. */
|
||||
#ifdef HAVE_W32_SYSTEM
|
||||
if (!*value)
|
||||
return gpg_error (GPG_ERR_ASS_PARAMETER);
|
||||
ctrl->server_local->event_signal = strtoul (value, NULL, 16);
|
||||
#else
|
||||
int i = *value? atoi (value) : -1;
|
||||
if (i < 0)
|
||||
return gpg_error (GPG_ERR_ASS_PARAMETER);
|
||||
ctrl->server_local->event_signal = i;
|
||||
#endif
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static gpg_error_t
|
||||
pin_cb (ctrl_t ctrl, const char *info, char **retstr)
|
||||
{
|
||||
assuan_context_t ctx = ctrl->ctx;
|
||||
char *command;
|
||||
int rc;
|
||||
unsigned char *value;
|
||||
size_t valuelen;
|
||||
|
||||
*retstr = NULL;
|
||||
log_debug ("asking for PIN '%s'\n", info);
|
||||
|
||||
rc = gpgrt_asprintf (&command, "NEEDPIN %s", info);
|
||||
if (rc < 0)
|
||||
return gpg_error (gpg_err_code_from_errno (errno));
|
||||
|
||||
/* Fixme: Write an inquire function which returns the result in
|
||||
secure memory and check all further handling of the PIN. */
|
||||
rc = assuan_inquire (ctx, command, &value, &valuelen, MAXLEN_PIN);
|
||||
xfree (command);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!valuelen)
|
||||
{
|
||||
/* We require that the returned value is an UTF-8 string */
|
||||
xfree (value);
|
||||
return gpg_error (GPG_ERR_INV_RESPONSE);
|
||||
}
|
||||
*retstr = (char*)value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char hlp_import[] =
|
||||
"IMPORT\n"
|
||||
"\n"
|
||||
"This command is used to convert a public and secret key to tpm format.\n"
|
||||
"keydata is communicated via an inquire KEYDATA command\n"
|
||||
"The keydata is expected to be the usual canonical encoded\n"
|
||||
"S-expression. The return will be a TPM format S-expression\n"
|
||||
"\n"
|
||||
"A PIN will be requested.";
|
||||
static gpg_error_t
|
||||
cmd_import (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
int rc;
|
||||
unsigned char *keydata;
|
||||
size_t keydatalen;
|
||||
TSS_CONTEXT *tssc;
|
||||
gcry_sexp_t s_key;
|
||||
unsigned char *shadow_info = NULL;
|
||||
size_t shadow_len;
|
||||
|
||||
line = skip_options (line);
|
||||
|
||||
if (*line)
|
||||
return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
|
||||
|
||||
/* Now get the actual keydata. */
|
||||
assuan_begin_confidential (ctx);
|
||||
rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA);
|
||||
assuan_end_confidential (ctx);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if ((rc = tpm2_start (&tssc)))
|
||||
goto out;
|
||||
gcry_sexp_new (&s_key, keydata, keydatalen, 0);
|
||||
rc = tpm2_import_key (ctrl, tssc, pin_cb, &shadow_info, &shadow_len,
|
||||
s_key, opt.parent);
|
||||
gcry_sexp_release (s_key);
|
||||
tpm2_end (tssc);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = assuan_send_data (ctx, shadow_info, shadow_len);
|
||||
|
||||
out:
|
||||
xfree (shadow_info);
|
||||
xfree (keydata);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const char hlp_pksign[] =
|
||||
"PKSIGN\n"
|
||||
"\n"
|
||||
"Get the TPM to produce a signature. KEYDATA will request the TPM\n"
|
||||
"form S-expression (returned by IMPORT) and EXTRA will be the hash\n"
|
||||
"to sign. The TPM currently deduces hash type from length.\n"
|
||||
"\n"
|
||||
"A PIN will be requested.";
|
||||
static gpg_error_t
|
||||
cmd_pksign (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
int rc;
|
||||
unsigned char *shadow_info;
|
||||
size_t len;
|
||||
TSS_CONTEXT *tssc;
|
||||
TPM_HANDLE key;
|
||||
TPMI_ALG_PUBLIC type;
|
||||
unsigned char *digest;
|
||||
size_t digestlen;
|
||||
unsigned char *sig;
|
||||
size_t siglen;
|
||||
|
||||
line = skip_options (line);
|
||||
|
||||
if (*line)
|
||||
return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
|
||||
|
||||
/* Now get the actual keydata. */
|
||||
rc = assuan_inquire (ctx, "KEYDATA", &shadow_info, &len, MAXLEN_KEYDATA);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = assuan_inquire (ctx, "EXTRA", &digest, &digestlen, MAXLEN_KEYDATA);
|
||||
if (rc)
|
||||
goto out_freeshadow;
|
||||
|
||||
rc = tpm2_start (&tssc);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = tpm2_load_key (tssc, shadow_info, &key, &type);
|
||||
if (rc)
|
||||
goto end_out;
|
||||
|
||||
rc = tpm2_sign (ctrl, tssc, key, pin_cb, type, digest, digestlen,
|
||||
&sig, &siglen);
|
||||
|
||||
tpm2_flush_handle (tssc, key);
|
||||
|
||||
end_out:
|
||||
tpm2_end (tssc);
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = assuan_send_data (ctx, sig, siglen);
|
||||
xfree (sig);
|
||||
|
||||
out:
|
||||
xfree (digest);
|
||||
out_freeshadow:
|
||||
xfree (shadow_info);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const char hlp_pkdecrypt[] =
|
||||
"PKDECRYPT\n"
|
||||
"Get the TPM to recover a symmetric key. KEYDATA will request the TPM\n"
|
||||
"form S-expression (returned by IMPORT) and EXTRA will be the input\n"
|
||||
"to derive or decrypt. The return will be the symmetric key\n"
|
||||
"\n"
|
||||
"\n"
|
||||
"A PIN will be requested.";
|
||||
static gpg_error_t
|
||||
cmd_pkdecrypt (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
int rc;
|
||||
unsigned char *shadow_info;
|
||||
size_t len;
|
||||
TSS_CONTEXT *tssc;
|
||||
TPM_HANDLE key;
|
||||
TPMI_ALG_PUBLIC type;
|
||||
unsigned char *crypto;
|
||||
size_t cryptolen;
|
||||
char *buf;
|
||||
size_t buflen;
|
||||
|
||||
line = skip_options (line);
|
||||
|
||||
if (*line)
|
||||
return set_error (GPG_ERR_ASS_PARAMETER, "additional parameters given");
|
||||
|
||||
/* Now get the actual keydata. */
|
||||
rc = assuan_inquire (ctx, "KEYDATA", &shadow_info, &len, MAXLEN_KEYDATA);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = assuan_inquire (ctx, "EXTRA", &crypto, &cryptolen, MAXLEN_KEYDATA);
|
||||
if (rc)
|
||||
goto out_freeshadow;
|
||||
|
||||
rc = tpm2_start (&tssc);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = tpm2_load_key (tssc, shadow_info, &key, &type);
|
||||
if (rc)
|
||||
goto end_out;
|
||||
|
||||
if (type == TPM_ALG_RSA)
|
||||
rc = tpm2_rsa_decrypt (ctrl, tssc, key, pin_cb, crypto,
|
||||
cryptolen, &buf, &buflen);
|
||||
else if (type == TPM_ALG_ECC)
|
||||
rc = tpm2_ecc_decrypt (ctrl, tssc, key, pin_cb, crypto,
|
||||
cryptolen, &buf, &buflen);
|
||||
|
||||
tpm2_flush_handle (tssc, key);
|
||||
|
||||
end_out:
|
||||
tpm2_end (tssc);
|
||||
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
rc = assuan_send_data (ctx, buf, buflen);
|
||||
xfree (buf);
|
||||
|
||||
out:
|
||||
xfree (crypto);
|
||||
out_freeshadow:
|
||||
xfree (shadow_info);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const char hlp_killtpm2d[] =
|
||||
"KILLTPM2D\n"
|
||||
"\n"
|
||||
"Commit suicide.";
|
||||
static gpg_error_t
|
||||
cmd_killtpm2d (assuan_context_t ctx, char *line)
|
||||
{
|
||||
ctrl_t ctrl = assuan_get_pointer (ctx);
|
||||
|
||||
(void)line;
|
||||
|
||||
ctrl->server_local->stopme = 1;
|
||||
assuan_set_flag (ctx, ASSUAN_FORCE_CLOSE, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* Tell the assuan library about our commands */
|
||||
static int
|
||||
register_commands (assuan_context_t ctx)
|
||||
{
|
||||
static struct {
|
||||
const char *name;
|
||||
assuan_handler_t handler;
|
||||
const char * const help;
|
||||
} table[] = {
|
||||
{ "IMPORT", cmd_import, hlp_import },
|
||||
{ "PKSIGN", cmd_pksign, hlp_pksign },
|
||||
{ "PKDECRYPT", cmd_pkdecrypt, hlp_pkdecrypt },
|
||||
{ "KILLTPM2D", cmd_killtpm2d, hlp_killtpm2d },
|
||||
{ NULL }
|
||||
};
|
||||
int i, rc;
|
||||
|
||||
for (i=0; table[i].name; i++)
|
||||
{
|
||||
rc = assuan_register_command (ctx, table[i].name, table[i].handler,
|
||||
table[i].help);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
assuan_set_hello_line (ctx, "GNU Privacy Guard's TPM2 server ready");
|
||||
|
||||
assuan_register_reset_notify (ctx, reset_notify);
|
||||
assuan_register_option_handler (ctx, option_handler);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/* Startup the server. If FD is given as -1 this is simple pipe
|
||||
server, otherwise it is a regular server. Returns true if there
|
||||
are no more active asessions. */
|
||||
int
|
||||
tpm2d_command_handler (ctrl_t ctrl, int fd)
|
||||
{
|
||||
int rc;
|
||||
assuan_context_t ctx = NULL;
|
||||
int stopme;
|
||||
|
||||
rc = assuan_new (&ctx);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to allocate assuan context: %s\n",
|
||||
gpg_strerror (rc));
|
||||
tpm2d_exit (2);
|
||||
}
|
||||
|
||||
if (fd == -1)
|
||||
{
|
||||
assuan_fd_t filedes[2];
|
||||
|
||||
filedes[0] = assuan_fdopen (0);
|
||||
filedes[1] = assuan_fdopen (1);
|
||||
rc = assuan_init_pipe_server (ctx, filedes);
|
||||
}
|
||||
else
|
||||
{
|
||||
rc = assuan_init_socket_server (ctx, INT2FD (fd),
|
||||
ASSUAN_SOCKET_SERVER_ACCEPTED);
|
||||
}
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to initialize the server: %s\n",
|
||||
gpg_strerror (rc));
|
||||
tpm2d_exit (2);
|
||||
}
|
||||
rc = register_commands (ctx);
|
||||
if (rc)
|
||||
{
|
||||
log_error ("failed to register commands with Assuan: %s\n",
|
||||
gpg_strerror (rc));
|
||||
tpm2d_exit (2);
|
||||
}
|
||||
assuan_set_pointer (ctx, ctrl);
|
||||
ctrl->ctx = ctx;
|
||||
|
||||
/* Allocate and initialize the server object. Put it into the list
|
||||
of active sessions. */
|
||||
ctrl->server_local = xcalloc (1, sizeof *ctrl->server_local);
|
||||
ctrl->server_local->next_session = session_list;
|
||||
session_list = ctrl->server_local;
|
||||
ctrl->server_local->ctrl_backlink = ctrl;
|
||||
ctrl->server_local->assuan_ctx = ctx;
|
||||
|
||||
/* Command processing loop. */
|
||||
for (;;)
|
||||
{
|
||||
rc = assuan_accept (ctx);
|
||||
if (rc == -1)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (rc)
|
||||
{
|
||||
log_info ("Assuan accept problem: %s\n", gpg_strerror (rc));
|
||||
break;
|
||||
}
|
||||
|
||||
rc = assuan_process (ctx);
|
||||
if (rc)
|
||||
{
|
||||
log_info ("Assuan processing failed: %s\n", gpg_strerror (rc));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* Release the server object. */
|
||||
if (session_list == ctrl->server_local)
|
||||
session_list = ctrl->server_local->next_session;
|
||||
else
|
||||
{
|
||||
struct server_local_s *sl;
|
||||
|
||||
for (sl=session_list; sl->next_session; sl = sl->next_session)
|
||||
if (sl->next_session == ctrl->server_local)
|
||||
break;
|
||||
if (!sl->next_session)
|
||||
BUG ();
|
||||
sl->next_session = ctrl->server_local->next_session;
|
||||
}
|
||||
stopme = ctrl->server_local->stopme;
|
||||
xfree (ctrl->server_local);
|
||||
ctrl->server_local = NULL;
|
||||
|
||||
/* Release the Assuan context. */
|
||||
assuan_release (ctx);
|
||||
|
||||
if (stopme)
|
||||
tpm2d_exit (0);
|
||||
|
||||
/* If there are no more sessions return true. */
|
||||
return !session_list;
|
||||
}
|
381
tpm2d/ibm-tss.h
Normal file
381
tpm2d/ibm-tss.h
Normal file
@ -0,0 +1,381 @@
|
||||
/*
|
||||
* Copyright (C) 2021 James Bottomley <James.Bottomley@HansenPartnership.com>
|
||||
*
|
||||
* Supporting TPM routines for the IBM TSS
|
||||
*/
|
||||
#ifndef _TPM2_IBM_TSS_H
|
||||
#define _TPM2_IBM_TSS_H
|
||||
|
||||
#define TSSINCLUDE(x) < TSS_INCLUDE/x >
|
||||
#include TSSINCLUDE(tss.h)
|
||||
#include TSSINCLUDE(tssutils.h)
|
||||
#include TSSINCLUDE(tssresponsecode.h)
|
||||
#include TSSINCLUDE(tssmarshal.h)
|
||||
#include TSSINCLUDE(Unmarshal_fp.h)
|
||||
#include TSSINCLUDE(tsscryptoh.h)
|
||||
|
||||
#define EXT_TPM_RH_OWNER TPM_RH_OWNER
|
||||
|
||||
#define VAL(X) X.val
|
||||
#define VAL_2B(X, MEMBER) X.t.MEMBER
|
||||
|
||||
static const char *tpm2_dir;
|
||||
|
||||
/* The TPM builds a small database of active files representing key
|
||||
* parameters used for authentication and session encryption. Make sure
|
||||
* they're contained in a separate directory to avoid stepping on any
|
||||
* other application uses of the TPM */
|
||||
static inline const char *
|
||||
tpm2_set_unique_tssdir (void)
|
||||
{
|
||||
char *prefix = getenv ("XDG_RUNTIME_DIR"), *template,
|
||||
*dir;
|
||||
int len = 0;
|
||||
|
||||
if (!prefix)
|
||||
prefix = "/tmp";
|
||||
|
||||
len = snprintf (NULL, 0, "%s/tss2.XXXXXX", prefix);
|
||||
if (len <= 0)
|
||||
return NULL;
|
||||
template = xtrymalloc (len + 1);
|
||||
if (!template)
|
||||
return NULL;
|
||||
|
||||
len++;
|
||||
len = snprintf (template, len, "%s/tss2.XXXXXX", prefix);
|
||||
|
||||
dir = mkdtemp (template);
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
static inline void
|
||||
tpm2_error (TPM_RC rc, const char *prefix)
|
||||
{
|
||||
const char *msg, *submsg, *num;
|
||||
|
||||
TSS_ResponseCode_toString (&msg, &submsg, &num, rc);
|
||||
log_error ("%s gave TPM2 Error: %s%s%s", prefix, msg, submsg, num);
|
||||
}
|
||||
|
||||
static inline int
|
||||
TSS_start (TSS_CONTEXT **tssc)
|
||||
{
|
||||
TPM_RC rc;
|
||||
|
||||
tpm2_dir = tpm2_set_unique_tssdir ();
|
||||
if (!tpm2_dir)
|
||||
/* make this non fatal */
|
||||
log_error ("Failed to set unique TPM directory\n");
|
||||
|
||||
rc = TSS_Create (tssc);
|
||||
if (rc)
|
||||
{
|
||||
tpm2_error (rc, "TSS_Create");
|
||||
return GPG_ERR_CARD;
|
||||
}
|
||||
rc = TSS_SetProperty (*tssc, TPM_DATA_DIR, tpm2_dir);
|
||||
if (rc)
|
||||
/* make this non fatal */
|
||||
tpm2_error (rc, "TSS_SetProperty");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_CreatePrimary (TSS_CONTEXT *tssContext, TPM_HANDLE primaryHandle,
|
||||
TPM2B_SENSITIVE_CREATE *inSensitive,
|
||||
TPM2B_PUBLIC *inPublic, TPM_HANDLE *objectHandle)
|
||||
{
|
||||
CreatePrimary_In in;
|
||||
CreatePrimary_Out out;
|
||||
TPM_RC rc;
|
||||
|
||||
in.primaryHandle = primaryHandle;
|
||||
in.inSensitive = *inSensitive;
|
||||
in.inPublic = *inPublic;
|
||||
/* no outside info */
|
||||
in.outsideInfo.t.size = 0;
|
||||
/* no PCR state */
|
||||
in.creationPCR.count = 0;
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
(RESPONSE_PARAMETERS *)&out,
|
||||
(COMMAND_PARAMETERS *)&in,
|
||||
NULL,
|
||||
TPM_CC_CreatePrimary,
|
||||
TPM_RS_PW, NULL, 0,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
*objectHandle = out.objectHandle;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_FlushContext (TSS_CONTEXT *tssContext, TPM_HANDLE flushHandle)
|
||||
{
|
||||
FlushContext_In in;
|
||||
TPM_RC rc;
|
||||
|
||||
in.flushHandle = flushHandle;
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
NULL,
|
||||
(COMMAND_PARAMETERS *)&in,
|
||||
NULL,
|
||||
TPM_CC_FlushContext,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_ReadPublic (TSS_CONTEXT *tssContext, TPM_HANDLE objectHandle,
|
||||
TPMT_PUBLIC *pub, TPM_HANDLE auth)
|
||||
{
|
||||
ReadPublic_In rin;
|
||||
ReadPublic_Out rout;
|
||||
TPM_RC rc;
|
||||
UINT32 flags = 0;
|
||||
|
||||
if (auth != TPM_RH_NULL)
|
||||
flags = TPMA_SESSION_ENCRYPT;
|
||||
|
||||
rin.objectHandle = objectHandle;
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
(RESPONSE_PARAMETERS *)&rout,
|
||||
(COMMAND_PARAMETERS *)&rin,
|
||||
NULL,
|
||||
TPM_CC_ReadPublic,
|
||||
auth, NULL, flags,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
tpm2_error (rc, "TPM2_ReadPublic");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (pub)
|
||||
*pub = rout.outPublic.publicArea;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_StartAuthSession (TSS_CONTEXT *tssContext, TPM_HANDLE tpmKey,
|
||||
TPM_HANDLE bind, TPM_SE sessionType,
|
||||
TPMT_SYM_DEF *symmetric, TPMI_ALG_HASH authHash,
|
||||
TPM_HANDLE *sessionHandle,
|
||||
const char *bindPassword)
|
||||
{
|
||||
StartAuthSession_In in;
|
||||
StartAuthSession_Out out;
|
||||
StartAuthSession_Extra extra;
|
||||
TPM_RC rc;
|
||||
|
||||
memset (&in, 0, sizeof(in));
|
||||
memset (&extra, 0 , sizeof(extra));
|
||||
|
||||
extra.bindPassword = bindPassword;
|
||||
|
||||
in.tpmKey = tpmKey;
|
||||
in.bind = bind;
|
||||
in.sessionType = sessionType;
|
||||
in.symmetric = *symmetric;
|
||||
in.authHash = authHash;
|
||||
|
||||
if (tpmKey != TPM_RH_NULL)
|
||||
{
|
||||
/*
|
||||
* For the TSS to use a key as salt, it must have
|
||||
* access to the public part. It does this by keeping
|
||||
* key files, but request the public part just to make
|
||||
* sure
|
||||
*/
|
||||
tpm2_ReadPublic (tssContext, tpmKey, NULL, TPM_RH_NULL);
|
||||
/*
|
||||
* don't care what rout returns, the purpose of the
|
||||
* operation was to get the public key parameters into
|
||||
* the tss so it can construct the salt
|
||||
*/
|
||||
}
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
(RESPONSE_PARAMETERS *)&out,
|
||||
(COMMAND_PARAMETERS *)&in,
|
||||
(EXTRA_PARAMETERS *)&extra,
|
||||
TPM_CC_StartAuthSession,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
*sessionHandle = out.sessionHandle;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_Sign (TSS_CONTEXT *tssContext, TPM_HANDLE keyHandle, DIGEST_2B *digest,
|
||||
TPMT_SIG_SCHEME *inScheme, TPMT_SIGNATURE *signature,
|
||||
TPM_HANDLE auth, const char *authVal)
|
||||
{
|
||||
Sign_In in;
|
||||
Sign_Out out;
|
||||
TPM_RC rc;
|
||||
|
||||
in.keyHandle = keyHandle;
|
||||
in.digest.t = *digest;
|
||||
in.inScheme = *inScheme;
|
||||
in.validation.tag = TPM_ST_HASHCHECK;
|
||||
in.validation.hierarchy = TPM_RH_NULL;
|
||||
in.validation.digest.t.size = 0;
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
(RESPONSE_PARAMETERS *)&out,
|
||||
(COMMAND_PARAMETERS *)&in,
|
||||
NULL,
|
||||
TPM_CC_Sign,
|
||||
auth, authVal, 0,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
*signature = out.signature;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_ECDH_ZGen (TSS_CONTEXT *tssContext, TPM_HANDLE keyHandle,
|
||||
TPM2B_ECC_POINT *inPoint, TPM2B_ECC_POINT *outPoint,
|
||||
TPM_HANDLE auth, const char *authVal)
|
||||
{
|
||||
ECDH_ZGen_In in;
|
||||
ECDH_ZGen_Out out;
|
||||
TPM_RC rc;
|
||||
|
||||
in.keyHandle = keyHandle;
|
||||
in.inPoint = *inPoint;
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
(RESPONSE_PARAMETERS *)&out,
|
||||
(COMMAND_PARAMETERS *)&in,
|
||||
NULL,
|
||||
TPM_CC_ECDH_ZGen,
|
||||
auth, authVal, TPMA_SESSION_ENCRYPT,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
*outPoint = out.outPoint;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_RSA_Decrypt (TSS_CONTEXT *tssContext, TPM_HANDLE keyHandle,
|
||||
PUBLIC_KEY_RSA_2B *cipherText, TPMT_RSA_DECRYPT *inScheme,
|
||||
PUBLIC_KEY_RSA_2B *message,
|
||||
TPM_HANDLE auth, const char *authVal, int flags)
|
||||
{
|
||||
RSA_Decrypt_In in;
|
||||
RSA_Decrypt_Out out;
|
||||
TPM_RC rc;
|
||||
|
||||
in.keyHandle = keyHandle;
|
||||
in.inScheme = *inScheme;
|
||||
in.cipherText.t = *cipherText;
|
||||
in.label.t.size = 0;
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
(RESPONSE_PARAMETERS *)&out,
|
||||
(COMMAND_PARAMETERS *)&in,
|
||||
NULL,
|
||||
TPM_CC_RSA_Decrypt,
|
||||
auth, authVal, flags,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
*message = out.message.t;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_Load (TSS_CONTEXT *tssContext, TPM_HANDLE parentHandle,
|
||||
PRIVATE_2B *inPrivate, TPM2B_PUBLIC *inPublic,
|
||||
TPM_HANDLE *objectHandle,
|
||||
TPM_HANDLE auth, const char *authVal)
|
||||
{
|
||||
Load_In in;
|
||||
Load_Out out;
|
||||
TPM_RC rc;
|
||||
|
||||
in.parentHandle = parentHandle;
|
||||
in.inPrivate.t = *inPrivate;
|
||||
in.inPublic = *inPublic;
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
(RESPONSE_PARAMETERS *)&out,
|
||||
(COMMAND_PARAMETERS *)&in,
|
||||
NULL,
|
||||
TPM_CC_Load,
|
||||
auth, authVal, 0,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
if (rc == TPM_RC_SUCCESS)
|
||||
*objectHandle = out.objectHandle;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_RC
|
||||
tpm2_Import (TSS_CONTEXT *tssContext, TPM_HANDLE parentHandle,
|
||||
DATA_2B *encryptionKey, TPM2B_PUBLIC *objectPublic,
|
||||
PRIVATE_2B *duplicate, ENCRYPTED_SECRET_2B *inSymSeed,
|
||||
TPMT_SYM_DEF_OBJECT *symmetricAlg, PRIVATE_2B *outPrivate,
|
||||
TPM_HANDLE auth, const char *authVal)
|
||||
{
|
||||
Import_In iin;
|
||||
Import_Out iout;
|
||||
TPM_RC rc;
|
||||
|
||||
iin.parentHandle = parentHandle;
|
||||
iin.encryptionKey.t = *encryptionKey;
|
||||
iin.objectPublic = *objectPublic;
|
||||
iin.duplicate.t = *duplicate;
|
||||
iin.inSymSeed.t = *inSymSeed;
|
||||
iin.symmetricAlg = *symmetricAlg;
|
||||
|
||||
rc = TSS_Execute (tssContext,
|
||||
(RESPONSE_PARAMETERS *)&iout,
|
||||
(COMMAND_PARAMETERS *)&iin,
|
||||
NULL,
|
||||
TPM_CC_Import,
|
||||
auth, authVal, TPMA_SESSION_DECRYPT,
|
||||
TPM_RH_NULL, NULL, 0);
|
||||
|
||||
*outPrivate = iout.outPrivate.t;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static inline TPM_HANDLE
|
||||
tpm2_handle_int (TSS_CONTEXT *tssContext, TPM_HANDLE h)
|
||||
{
|
||||
(void)tssContext;
|
||||
return h;
|
||||
}
|
||||
|
||||
static inline TPM_HANDLE
|
||||
tpm2_handle_ext (TSS_CONTEXT *tssContext, TPM_HANDLE h)
|
||||
{
|
||||
(void)tssContext;
|
||||
return h;
|
||||
}
|
||||
|
||||
static inline int
|
||||
tpm2_handle_mso (TSS_CONTEXT *tssContext, TPM_HANDLE h, UINT32 mso)
|
||||
{
|
||||
(void)tssContext;
|
||||
return (h >> 24) == mso;
|
||||
}
|
||||
|
||||
#endif
|
987
tpm2d/tpm2.c
Normal file
987
tpm2d/tpm2.c
Normal file
@ -0,0 +1,987 @@
|
||||
#include <config.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#include "tpm2.h"
|
||||
|
||||
#include "../common/i18n.h"
|
||||
#include "../common/sexp-parse.h"
|
||||
|
||||
int
|
||||
tpm2_start (TSS_CONTEXT **tssc)
|
||||
{
|
||||
return TSS_start(tssc);
|
||||
}
|
||||
|
||||
void
|
||||
tpm2_end (TSS_CONTEXT *tssc)
|
||||
{
|
||||
TSS_Delete (tssc);
|
||||
}
|
||||
|
||||
static TPM_HANDLE
|
||||
tpm2_get_parent (TSS_CONTEXT *tssc, TPM_HANDLE p)
|
||||
{
|
||||
TPM_RC rc;
|
||||
TPM2B_SENSITIVE_CREATE inSensitive;
|
||||
TPM2B_PUBLIC inPublic;
|
||||
TPM_HANDLE objectHandle;
|
||||
|
||||
p = tpm2_handle_int(tssc, p);
|
||||
if (tpm2_handle_mso(tssc, p, TPM_HT_PERSISTENT))
|
||||
return p; /* should only be permanent */
|
||||
|
||||
/* assume no hierarchy auth */
|
||||
VAL_2B (inSensitive.sensitive.userAuth, size) = 0;
|
||||
/* no sensitive date for storage keys */
|
||||
VAL_2B (inSensitive.sensitive.data, size) = 0;
|
||||
|
||||
/* public parameters for a P-256 EC key */
|
||||
inPublic.publicArea.type = TPM_ALG_ECC;
|
||||
inPublic.publicArea.nameAlg = TPM_ALG_SHA256;
|
||||
VAL (inPublic.publicArea.objectAttributes) =
|
||||
TPMA_OBJECT_NODA |
|
||||
TPMA_OBJECT_SENSITIVEDATAORIGIN |
|
||||
TPMA_OBJECT_USERWITHAUTH |
|
||||
TPMA_OBJECT_DECRYPT |
|
||||
TPMA_OBJECT_RESTRICTED |
|
||||
TPMA_OBJECT_FIXEDPARENT |
|
||||
TPMA_OBJECT_FIXEDTPM;
|
||||
|
||||
inPublic.publicArea.parameters.eccDetail.symmetric.algorithm = TPM_ALG_AES;
|
||||
inPublic.publicArea.parameters.eccDetail.symmetric.keyBits.aes = 128;
|
||||
inPublic.publicArea.parameters.eccDetail.symmetric.mode.aes = TPM_ALG_CFB;
|
||||
inPublic.publicArea.parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
|
||||
inPublic.publicArea.parameters.eccDetail.curveID = TPM_ECC_NIST_P256;
|
||||
inPublic.publicArea.parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
|
||||
|
||||
VAL_2B (inPublic.publicArea.unique.ecc.x, size) = 0;
|
||||
VAL_2B (inPublic.publicArea.unique.ecc.y, size) = 0;
|
||||
VAL_2B (inPublic.publicArea.authPolicy, size) = 0;
|
||||
|
||||
rc = tpm2_CreatePrimary (tssc, p, &inSensitive, &inPublic, &objectHandle);
|
||||
if (rc)
|
||||
{
|
||||
tpm2_error (rc, "TSS_CreatePrimary");
|
||||
return 0;
|
||||
}
|
||||
return objectHandle;
|
||||
}
|
||||
|
||||
void
|
||||
tpm2_flush_handle (TSS_CONTEXT *tssc, TPM_HANDLE h)
|
||||
{
|
||||
/* only flush volatile handles */
|
||||
if (tpm2_handle_mso(tssc, h, TPM_HT_PERSISTENT))
|
||||
return;
|
||||
|
||||
tpm2_FlushContext(tssc, h);
|
||||
}
|
||||
|
||||
static int
|
||||
tpm2_get_hmac_handle (TSS_CONTEXT *tssc, TPM_HANDLE *handle,
|
||||
TPM_HANDLE salt_key)
|
||||
{
|
||||
TPM_RC rc;
|
||||
TPMT_SYM_DEF symmetric;
|
||||
|
||||
symmetric.algorithm = TPM_ALG_AES;
|
||||
symmetric.keyBits.aes = 128;
|
||||
symmetric.mode.aes = TPM_ALG_CFB;
|
||||
|
||||
rc = tpm2_StartAuthSession(tssc, salt_key, TPM_RH_NULL, TPM_SE_HMAC,
|
||||
&symmetric, TPM_ALG_SHA256, handle, NULL);
|
||||
if (rc)
|
||||
{
|
||||
tpm2_error (rc, "TPM2_StartAuthSession");
|
||||
return GPG_ERR_CARD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
tpm2_pre_auth (ctrl_t ctrl, TSS_CONTEXT *tssc,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
TPM_HANDLE *ah, char **auth)
|
||||
{
|
||||
TPM_RC rc;
|
||||
int len;
|
||||
|
||||
rc = pin_cb (ctrl, _("TPM Key Passphrase"), auth);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
len = strlen(*auth);
|
||||
/*
|
||||
* TPMs can't accept a longer passphrase than the name algorithm.
|
||||
* We hard code the name algorithm to SHA256 so the max passphrase
|
||||
* length is 32
|
||||
*/
|
||||
if (len > 32)
|
||||
{
|
||||
log_error ("Truncating Passphrase to TPM allowed 32\n");
|
||||
(*auth)[32] = '\0';
|
||||
}
|
||||
|
||||
rc = tpm2_get_hmac_handle (tssc, ah, TPM_RH_NULL);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
tpm2_post_auth (TSS_CONTEXT *tssc, TPM_RC rc, TPM_HANDLE ah,
|
||||
char **auth, const char *cmd_str)
|
||||
{
|
||||
gcry_free (*auth);
|
||||
*auth = NULL;
|
||||
if (rc)
|
||||
{
|
||||
tpm2_error (rc, cmd_str);
|
||||
tpm2_flush_handle (tssc, ah);
|
||||
switch (rc & 0xFF)
|
||||
{
|
||||
case TPM_RC_BAD_AUTH:
|
||||
case TPM_RC_AUTH_FAIL:
|
||||
return GPG_ERR_BAD_PASSPHRASE;
|
||||
default:
|
||||
return GPG_ERR_CARD;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static unsigned char *
|
||||
make_tpm2_shadow_info (uint32_t parent, const char *pub, int pub_len,
|
||||
const char *priv, int priv_len, size_t *len)
|
||||
{
|
||||
gcry_sexp_t s_exp;
|
||||
char *info;
|
||||
|
||||
gcry_sexp_build (&s_exp, NULL, "(%u%b%b)", parent, pub_len, pub,
|
||||
priv_len, priv);
|
||||
|
||||
*len = gcry_sexp_sprint (s_exp, GCRYSEXP_FMT_CANON, NULL, 0);
|
||||
info = xtrymalloc (*len);
|
||||
if (!info)
|
||||
goto out;
|
||||
gcry_sexp_sprint (s_exp, GCRYSEXP_FMT_CANON, info, *len);
|
||||
|
||||
out:
|
||||
gcry_sexp_release (s_exp);
|
||||
return (unsigned char *)info;
|
||||
}
|
||||
|
||||
static gpg_error_t
|
||||
parse_tpm2_shadow_info (const unsigned char *shadow_info,
|
||||
uint32_t *parent,
|
||||
const char **pub, int *pub_len,
|
||||
const char **priv, int *priv_len)
|
||||
{
|
||||
const unsigned char *s;
|
||||
size_t n;
|
||||
int i;
|
||||
|
||||
s = shadow_info;
|
||||
if (*s != '(')
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
s++;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
*parent = 0;
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
*parent *= 10;
|
||||
*parent += atoi_1(s+i);
|
||||
}
|
||||
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
|
||||
*pub_len = n;
|
||||
*pub = s;
|
||||
|
||||
s += n;
|
||||
n = snext (&s);
|
||||
if (!n)
|
||||
return gpg_error (GPG_ERR_INV_SEXP);
|
||||
|
||||
*priv_len = n;
|
||||
*priv = s;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tpm2_load_key (TSS_CONTEXT *tssc, const unsigned char *shadow_info,
|
||||
TPM_HANDLE *key, TPMI_ALG_PUBLIC *type)
|
||||
{
|
||||
uint32_t parent;
|
||||
TPM_HANDLE parentHandle;
|
||||
PRIVATE_2B inPrivate;
|
||||
TPM2B_PUBLIC inPublic;
|
||||
const char *pub, *priv;
|
||||
int ret, pub_len, priv_len;
|
||||
TPM_RC rc;
|
||||
BYTE *buf;
|
||||
uint32_t size;
|
||||
|
||||
ret = parse_tpm2_shadow_info (shadow_info, &parent, &pub, &pub_len,
|
||||
&priv, &priv_len);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
parentHandle = tpm2_get_parent (tssc, parent);
|
||||
|
||||
buf = (BYTE *)priv;
|
||||
size = priv_len;
|
||||
TPM2B_PRIVATE_Unmarshal ((TPM2B_PRIVATE *)&inPrivate, &buf, &size);
|
||||
|
||||
buf = (BYTE *)pub;
|
||||
size = pub_len;
|
||||
TPM2B_PUBLIC_Unmarshal (&inPublic, &buf, &size, FALSE);
|
||||
|
||||
*type = inPublic.publicArea.type;
|
||||
|
||||
rc = tpm2_Load (tssc, parentHandle, &inPrivate, &inPublic, key,
|
||||
TPM_RS_PW, NULL);
|
||||
|
||||
tpm2_flush_handle (tssc, parentHandle);
|
||||
|
||||
if (rc != TPM_RC_SUCCESS)
|
||||
{
|
||||
tpm2_error (rc, "TPM2_Load");
|
||||
return GPG_ERR_CARD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tpm2_sign (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
TPMI_ALG_PUBLIC type,
|
||||
const unsigned char *digest, size_t digestlen,
|
||||
unsigned char **r_sig, size_t *r_siglen)
|
||||
{
|
||||
int ret;
|
||||
DIGEST_2B digest2b;
|
||||
TPMT_SIG_SCHEME inScheme;
|
||||
TPMT_SIGNATURE signature;
|
||||
TPM_HANDLE ah;
|
||||
char *auth;
|
||||
|
||||
/* The TPM insists on knowing the digest type, so
|
||||
* calculate that from the size */
|
||||
switch (digestlen)
|
||||
{
|
||||
case 20:
|
||||
inScheme.details.rsassa.hashAlg = TPM_ALG_SHA1;
|
||||
break;
|
||||
case 32:
|
||||
inScheme.details.rsassa.hashAlg = TPM_ALG_SHA256;
|
||||
break;
|
||||
case 48:
|
||||
inScheme.details.rsassa.hashAlg = TPM_ALG_SHA384;
|
||||
break;
|
||||
#ifdef TPM_ALG_SHA512
|
||||
case 64:
|
||||
inScheme.details.rsassa.hashAlg = TPM_ALG_SHA512;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
log_error ("Unknown signature digest length, cannot deduce hash type for TPM\n");
|
||||
return GPG_ERR_NO_SIGNATURE_SCHEME;
|
||||
}
|
||||
digest2b.size = digestlen;
|
||||
memcpy (digest2b.buffer, digest, digestlen);
|
||||
|
||||
if (type == TPM_ALG_RSA)
|
||||
inScheme.scheme = TPM_ALG_RSASSA;
|
||||
else if (type == TPM_ALG_ECC)
|
||||
inScheme.scheme = TPM_ALG_ECDSA;
|
||||
else
|
||||
return GPG_ERR_PUBKEY_ALGO;
|
||||
|
||||
ret = tpm2_pre_auth (ctrl, tssc, pin_cb, &ah, &auth);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = tpm2_Sign (tssc, key, &digest2b, &inScheme, &signature, ah, auth);
|
||||
ret = tpm2_post_auth (tssc, ret, ah, &auth, "TPM2_Sign");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (type == TPM_ALG_RSA)
|
||||
*r_siglen = VAL_2B (signature.signature.rsassa.sig, size);
|
||||
else if (type == TPM_ALG_ECC)
|
||||
*r_siglen = VAL_2B (signature.signature.ecdsa.signatureR, size)
|
||||
+ VAL_2B (signature.signature.ecdsa.signatureS, size);
|
||||
|
||||
*r_sig = xtrymalloc (*r_siglen);
|
||||
if (!r_sig)
|
||||
return GPG_ERR_ENOMEM;
|
||||
|
||||
if (type == TPM_ALG_RSA)
|
||||
{
|
||||
memcpy (*r_sig, VAL_2B (signature.signature.rsassa.sig, buffer),
|
||||
*r_siglen);
|
||||
}
|
||||
else if (type == TPM_ALG_ECC)
|
||||
{
|
||||
memcpy (*r_sig, VAL_2B (signature.signature.ecdsa.signatureR, buffer),
|
||||
VAL_2B (signature.signature.ecdsa.signatureR, size));
|
||||
memcpy (*r_sig + VAL_2B (signature.signature.ecdsa.signatureR, size),
|
||||
VAL_2B (signature.signature.ecdsa.signatureS, buffer),
|
||||
VAL_2B (signature.signature.ecdsa.signatureS, size));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sexp_to_tpm2_sensitive_ecc (TPMT_SENSITIVE *s, gcry_sexp_t key)
|
||||
{
|
||||
gcry_mpi_t d;
|
||||
gcry_sexp_t l;
|
||||
int rc = -1;
|
||||
size_t len;
|
||||
|
||||
s->sensitiveType = TPM_ALG_ECC;
|
||||
VAL_2B (s->seedValue, size) = 0;
|
||||
|
||||
l = gcry_sexp_find_token (key, "d", 0);
|
||||
if (!l)
|
||||
return rc;
|
||||
d = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
|
||||
gcry_sexp_release (l);
|
||||
len = sizeof (VAL_2B (s->sensitive.ecc, buffer));
|
||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, VAL_2B (s->sensitive.ecc, buffer),
|
||||
len, &len, d);
|
||||
VAL_2B (s->sensitive.ecc, size) = len;
|
||||
gcry_mpi_release (d);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* try to match the libgcrypt curve names to known TPM parameters.
|
||||
*
|
||||
* As of 2018 the TCG defined curves are only NIST
|
||||
* (192,224,256,384,521) Barreto-Naehring (256,638) and the Chinese
|
||||
* SM2 (256), which means only the NIST ones overlap with libgcrypt */
|
||||
static struct {
|
||||
const char *name;
|
||||
TPMI_ECC_CURVE c;
|
||||
} tpm2_curves[] = {
|
||||
{ "NIST P-192", TPM_ECC_NIST_P192 },
|
||||
{ "prime192v1", TPM_ECC_NIST_P192 },
|
||||
{ "secp192r1", TPM_ECC_NIST_P192 },
|
||||
{ "nistp192", TPM_ECC_NIST_P192 },
|
||||
{ "NIST P-224", TPM_ECC_NIST_P224 },
|
||||
{ "secp224r1", TPM_ECC_NIST_P224 },
|
||||
{ "nistp224", TPM_ECC_NIST_P224 },
|
||||
{ "NIST P-256", TPM_ECC_NIST_P256 },
|
||||
{ "prime256v1", TPM_ECC_NIST_P256 },
|
||||
{ "secp256r1", TPM_ECC_NIST_P256 },
|
||||
{ "nistp256", TPM_ECC_NIST_P256 },
|
||||
{ "NIST P-384", TPM_ECC_NIST_P384 },
|
||||
{ "secp384r1", TPM_ECC_NIST_P384 },
|
||||
{ "nistp384", TPM_ECC_NIST_P384 },
|
||||
{ "NIST P-521", TPM_ECC_NIST_P521 },
|
||||
{ "secp521r1", TPM_ECC_NIST_P521 },
|
||||
{ "nistp521", TPM_ECC_NIST_P521 },
|
||||
};
|
||||
|
||||
static int
|
||||
tpm2_ecc_curve (const char *curve_name, TPMI_ECC_CURVE *c)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < DIM (tpm2_curves); i++)
|
||||
if (strcmp (tpm2_curves[i].name, curve_name) == 0)
|
||||
break;
|
||||
if (i == DIM (tpm2_curves))
|
||||
{
|
||||
log_error ("curve %s does not match any available TPM curves\n", curve_name);
|
||||
return GPG_ERR_UNKNOWN_CURVE;
|
||||
}
|
||||
|
||||
*c = tpm2_curves[i].c;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sexp_to_tpm2_public_ecc (TPMT_PUBLIC *p, gcry_sexp_t key)
|
||||
{
|
||||
const char *q;
|
||||
gcry_sexp_t l;
|
||||
int rc = GPG_ERR_BAD_PUBKEY;
|
||||
size_t len;
|
||||
TPMI_ECC_CURVE curve;
|
||||
char *curve_name;
|
||||
|
||||
l = gcry_sexp_find_token (key, "curve", 0);
|
||||
if (!l)
|
||||
return rc;
|
||||
curve_name = gcry_sexp_nth_string (l, 1);
|
||||
if (!curve_name)
|
||||
goto out;
|
||||
rc = tpm2_ecc_curve (curve_name, &curve);
|
||||
gcry_free (curve_name);
|
||||
if (rc)
|
||||
goto out;
|
||||
gcry_sexp_release (l);
|
||||
|
||||
l = gcry_sexp_find_token (key, "q", 0);
|
||||
if (!l)
|
||||
return rc;
|
||||
q = gcry_sexp_nth_data (l, 1, &len);
|
||||
/* This is a point representation, the first byte tells you what
|
||||
* type. The only format we understand is uncompressed (0x04)
|
||||
* which has layout 0x04 | x | y */
|
||||
if (q[0] != 0x04)
|
||||
{
|
||||
log_error ("Point format for q is not uncompressed\n");
|
||||
goto out;
|
||||
}
|
||||
q++;
|
||||
len--;
|
||||
/* now should have to equal sized big endian point numbers */
|
||||
if ((len & 0x01) == 1)
|
||||
{
|
||||
log_error ("Point format for q has incorrect length\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
len >>= 1;
|
||||
|
||||
p->type = TPM_ALG_ECC;
|
||||
p->nameAlg = TPM_ALG_SHA256;
|
||||
VAL (p->objectAttributes) = TPMA_OBJECT_NODA |
|
||||
TPMA_OBJECT_SIGN |
|
||||
TPMA_OBJECT_DECRYPT |
|
||||
TPMA_OBJECT_USERWITHAUTH;
|
||||
VAL_2B (p->authPolicy, size) = 0;
|
||||
p->parameters.eccDetail.symmetric.algorithm = TPM_ALG_NULL;
|
||||
p->parameters.eccDetail.scheme.scheme = TPM_ALG_NULL;
|
||||
p->parameters.eccDetail.curveID = curve;
|
||||
p->parameters.eccDetail.kdf.scheme = TPM_ALG_NULL;
|
||||
memcpy (VAL_2B (p->unique.ecc.x, buffer), q, len);
|
||||
VAL_2B (p->unique.ecc.x, size) = len;
|
||||
memcpy (VAL_2B (p->unique.ecc.y, buffer), q + len, len);
|
||||
VAL_2B (p->unique.ecc.y, size) = len;
|
||||
out:
|
||||
gcry_sexp_release (l);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sexp_to_tpm2_sensitive_rsa (TPMT_SENSITIVE *s, gcry_sexp_t key)
|
||||
{
|
||||
gcry_mpi_t p;
|
||||
gcry_sexp_t l;
|
||||
int rc = -1;
|
||||
size_t len;
|
||||
|
||||
s->sensitiveType = TPM_ALG_RSA;
|
||||
VAL_2B (s->seedValue, size) = 0;
|
||||
|
||||
l = gcry_sexp_find_token (key, "p", 0);
|
||||
if (!l)
|
||||
return rc;
|
||||
p = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
|
||||
gcry_sexp_release (l);
|
||||
len = sizeof (VAL_2B (s->sensitive.rsa, buffer));
|
||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, VAL_2B (s->sensitive.rsa, buffer),
|
||||
len, &len, p);
|
||||
VAL_2B (s->sensitive.rsa, size) = len;
|
||||
gcry_mpi_release (p);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int
|
||||
sexp_to_tpm2_public_rsa (TPMT_PUBLIC *p, gcry_sexp_t key)
|
||||
{
|
||||
gcry_mpi_t n, e;
|
||||
gcry_sexp_t l;
|
||||
int rc = -1, i;
|
||||
size_t len;
|
||||
/* longer than an int */
|
||||
unsigned char ebuf[5];
|
||||
uint32_t exp = 0;
|
||||
|
||||
p->type = TPM_ALG_RSA;
|
||||
p->nameAlg = TPM_ALG_SHA256;
|
||||
VAL (p->objectAttributes) = TPMA_OBJECT_NODA |
|
||||
TPMA_OBJECT_DECRYPT |
|
||||
TPMA_OBJECT_SIGN |
|
||||
TPMA_OBJECT_USERWITHAUTH;
|
||||
VAL_2B (p->authPolicy, size) = 0;
|
||||
p->parameters.rsaDetail.symmetric.algorithm = TPM_ALG_NULL;
|
||||
p->parameters.rsaDetail.scheme.scheme = TPM_ALG_NULL;
|
||||
|
||||
l = gcry_sexp_find_token (key, "n", 0);
|
||||
if (!l)
|
||||
return rc;
|
||||
n = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
|
||||
gcry_sexp_release (l);
|
||||
len = sizeof (VAL_2B (p->unique.rsa, buffer));
|
||||
p->parameters.rsaDetail.keyBits = gcry_mpi_get_nbits (n);
|
||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, VAL_2B (p->unique.rsa, buffer),
|
||||
len, &len, n);
|
||||
VAL_2B (p->unique.rsa, size) = len;
|
||||
gcry_mpi_release (n);
|
||||
if (rc)
|
||||
return rc;
|
||||
rc = -1;
|
||||
l = gcry_sexp_find_token (key, "e", 0);
|
||||
if (!l)
|
||||
return rc;
|
||||
e = gcry_sexp_nth_mpi (l, 1, GCRYMPI_FMT_USG);
|
||||
gcry_sexp_release (l);
|
||||
len = sizeof (ebuf);
|
||||
rc = gcry_mpi_print (GCRYMPI_FMT_USG, ebuf, len, &len, e);
|
||||
gcry_mpi_release (e);
|
||||
if (rc)
|
||||
return rc;
|
||||
if (len > 4)
|
||||
return -1;
|
||||
|
||||
/* MPI are simply big endian integers, so convert to uint32 */
|
||||
for (i = 0; i < len; i++)
|
||||
{
|
||||
exp <<= 8;
|
||||
exp += ebuf[i];
|
||||
}
|
||||
if (exp == 0x10001)
|
||||
p->parameters.rsaDetail.exponent = 0;
|
||||
else
|
||||
p->parameters.rsaDetail.exponent = exp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
sexp_to_tpm2(TPMT_PUBLIC *p, TPMT_SENSITIVE *s, gcry_sexp_t s_skey)
|
||||
{
|
||||
gcry_sexp_t l1, l2;
|
||||
int rc = -1;
|
||||
|
||||
/* find the value of (private-key */
|
||||
l1 = gcry_sexp_nth (s_skey, 1);
|
||||
if (!l1)
|
||||
return rc;
|
||||
|
||||
l2 = gcry_sexp_find_token (l1, "rsa", 0);
|
||||
if (l2)
|
||||
{
|
||||
rc = sexp_to_tpm2_public_rsa (p, l2);
|
||||
if (!rc)
|
||||
rc = sexp_to_tpm2_sensitive_rsa (s, l2);
|
||||
}
|
||||
else
|
||||
{
|
||||
l2 = gcry_sexp_find_token (l1, "ecc", 0);
|
||||
if (!l2)
|
||||
goto out;
|
||||
rc = sexp_to_tpm2_public_ecc (p, l2);
|
||||
if (!rc)
|
||||
rc = sexp_to_tpm2_sensitive_ecc (s, l2);
|
||||
}
|
||||
|
||||
gcry_sexp_release (l2);
|
||||
|
||||
out:
|
||||
gcry_sexp_release (l1);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* copied from TPM implementation code */
|
||||
static TPM_RC
|
||||
tpm2_ObjectPublic_GetName (NAME_2B *name,
|
||||
TPMT_PUBLIC *tpmtPublic)
|
||||
{
|
||||
TPM_RC rc = 0;
|
||||
uint16_t written = 0;
|
||||
TPMT_HA digest;
|
||||
uint32_t sizeInBytes;
|
||||
uint8_t buffer[MAX_RESPONSE_SIZE];
|
||||
|
||||
/* marshal the TPMT_PUBLIC */
|
||||
if (rc == 0)
|
||||
{
|
||||
INT32 size = MAX_RESPONSE_SIZE;
|
||||
uint8_t *buffer1 = buffer;
|
||||
rc = TSS_TPMT_PUBLIC_Marshal (tpmtPublic, &written, &buffer1, &size);
|
||||
}
|
||||
/* hash the public area */
|
||||
if (rc == 0)
|
||||
{
|
||||
sizeInBytes = TSS_GetDigestSize (tpmtPublic->nameAlg);
|
||||
digest.hashAlg = tpmtPublic->nameAlg; /* Name digest algorithm */
|
||||
/* generate the TPMT_HA */
|
||||
rc = TSS_Hash_Generate (&digest, written, buffer, 0, NULL);
|
||||
}
|
||||
if (rc == 0)
|
||||
{
|
||||
TPMI_ALG_HASH nameAlgNbo;
|
||||
|
||||
/* copy the digest */
|
||||
memcpy (name->name + sizeof (TPMI_ALG_HASH),
|
||||
(uint8_t *)&digest.digest, sizeInBytes);
|
||||
/* copy the hash algorithm */
|
||||
nameAlgNbo = htons (tpmtPublic->nameAlg);
|
||||
memcpy (name->name, (uint8_t *)&nameAlgNbo, sizeof (TPMI_ALG_HASH));
|
||||
/* set the size */
|
||||
name->size = sizeInBytes + sizeof (TPMI_ALG_HASH);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Cut down version of Part 4 Supporting Routines 7.6.3.10
|
||||
*
|
||||
* Hard coded to symmetrically encrypt with aes128 as the inner
|
||||
* wrapper and no outer wrapper but with a prototype that allows
|
||||
* drop in replacement with a tss equivalent
|
||||
*/
|
||||
TPM_RC tpm2_SensitiveToDuplicate (TPMT_SENSITIVE *s,
|
||||
NAME_2B *name,
|
||||
TPM_ALG_ID nalg,
|
||||
TPMT_SYM_DEF_OBJECT *symdef,
|
||||
DATA_2B *innerkey,
|
||||
PRIVATE_2B *p)
|
||||
{
|
||||
BYTE *buf = p->buffer;
|
||||
|
||||
p->size = 0;
|
||||
memset (p, 0, sizeof (*p));
|
||||
|
||||
/* hard code AES CFB */
|
||||
if (symdef->algorithm == TPM_ALG_AES
|
||||
&& symdef->mode.aes == TPM_ALG_CFB)
|
||||
{
|
||||
TPMT_HA hash;
|
||||
const int hlen = TSS_GetDigestSize (nalg);
|
||||
TPM2B *digest = (TPM2B *)buf;
|
||||
TPM2B *s2b;
|
||||
int32_t size;
|
||||
unsigned char null_iv[AES_128_BLOCK_SIZE_BYTES];
|
||||
UINT16 bsize, written = 0;
|
||||
gcry_cipher_hd_t hd;
|
||||
|
||||
/* WARNING: don't use the static null_iv trick here:
|
||||
* the AES routines alter the passed in iv */
|
||||
memset (null_iv, 0, sizeof (null_iv));
|
||||
|
||||
/* reserve space for hash before the encrypted sensitive */
|
||||
bsize = sizeof (digest->size) + hlen;
|
||||
buf += bsize;
|
||||
p->size += bsize;
|
||||
s2b = (TPM2B *)buf;
|
||||
|
||||
/* marshal the digest size */
|
||||
buf = (BYTE *)&digest->size;
|
||||
bsize = hlen;
|
||||
size = 2;
|
||||
TSS_UINT16_Marshal (&bsize, &written, &buf, &size);
|
||||
|
||||
/* marshal the unencrypted sensitive in place */
|
||||
size = sizeof (*s);
|
||||
bsize = 0;
|
||||
buf = s2b->buffer;
|
||||
TSS_TPMT_SENSITIVE_Marshal (s, &bsize, &buf, &size);
|
||||
buf = (BYTE *)&s2b->size;
|
||||
size = 2;
|
||||
TSS_UINT16_Marshal (&bsize, &written, &buf, &size);
|
||||
|
||||
bsize = bsize + sizeof (s2b->size);
|
||||
p->size += bsize;
|
||||
|
||||
/* compute hash of unencrypted marshalled sensitive and
|
||||
* write to the digest buffer */
|
||||
hash.hashAlg = nalg;
|
||||
TSS_Hash_Generate (&hash, bsize, s2b,
|
||||
name->size, name->name,
|
||||
0, NULL);
|
||||
memcpy (digest->buffer, &hash.digest, hlen);
|
||||
gcry_cipher_open (&hd, GCRY_CIPHER_AES128,
|
||||
GCRY_CIPHER_MODE_CFB, GCRY_CIPHER_SECURE);
|
||||
gcry_cipher_setiv (hd, null_iv, sizeof (null_iv));
|
||||
gcry_cipher_setkey (hd, innerkey->buffer, innerkey->size);
|
||||
/* encrypt the hash and sensitive in-place */
|
||||
gcry_cipher_encrypt (hd, p->buffer, p->size, NULL, 0);
|
||||
gcry_cipher_close (hd);
|
||||
|
||||
}
|
||||
else if (symdef->algorithm == TPM_ALG_NULL)
|
||||
{
|
||||
/* Code is for debugging only, should never be used in production */
|
||||
TPM2B *s2b = (TPM2B *)buf;
|
||||
int32_t size = sizeof (*s);
|
||||
UINT16 bsize = 0, written = 0;
|
||||
|
||||
log_error ("Secret key sent to TPM unencrypted\n");
|
||||
buf = s2b->buffer;
|
||||
|
||||
/* marshal the unencrypted sensitive in place */
|
||||
TSS_TPMT_SENSITIVE_Marshal (s, &bsize, &buf, &size);
|
||||
buf = (BYTE *)&s2b->size;
|
||||
size = 2;
|
||||
TSS_UINT16_Marshal (&bsize, &written, &buf, &size);
|
||||
|
||||
p->size += bsize + sizeof (s2b->size);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_error ("Unknown symmetric algorithm\n");
|
||||
return TPM_RC_SYMMETRIC;
|
||||
}
|
||||
|
||||
return TPM_RC_SUCCESS;
|
||||
}
|
||||
|
||||
int
|
||||
tpm2_import_key (ctrl_t ctrl, TSS_CONTEXT *tssc,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
unsigned char **shadow_info, size_t *shadow_len,
|
||||
gcry_sexp_t s_skey, unsigned long parent)
|
||||
{
|
||||
TPM_HANDLE parentHandle;
|
||||
DATA_2B encryptionKey;
|
||||
TPM2B_PUBLIC objectPublic;
|
||||
PRIVATE_2B duplicate;
|
||||
ENCRYPTED_SECRET_2B inSymSeed;
|
||||
TPMT_SYM_DEF_OBJECT symmetricAlg;
|
||||
PRIVATE_2B outPrivate;
|
||||
NAME_2B name;
|
||||
const int aes_key_bits = 128;
|
||||
const int aes_key_bytes = aes_key_bits/8;
|
||||
|
||||
TPMT_SENSITIVE s;
|
||||
TPM_HANDLE ah;
|
||||
TPM_RC rc;
|
||||
|
||||
uint32_t size;
|
||||
uint16_t len;
|
||||
BYTE *buffer;
|
||||
int ret;
|
||||
char *passphrase;
|
||||
|
||||
char pub[sizeof (TPM2B_PUBLIC)];
|
||||
int pub_len;
|
||||
char priv[sizeof (TPM2B_PRIVATE)];
|
||||
int priv_len;
|
||||
|
||||
if (parent == 0)
|
||||
parent = EXT_TPM_RH_OWNER;
|
||||
|
||||
ret = sexp_to_tpm2 (&objectPublic.publicArea, &s, s_skey);
|
||||
if (ret)
|
||||
{
|
||||
log_error ("Failed to parse Key s-expression: key corrupt?\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* add an authorization password to the key which the TPM will check */
|
||||
|
||||
ret = pin_cb (ctrl, _("Please enter the TPM Authorization passphrase for the key."), &passphrase);
|
||||
if (ret)
|
||||
return ret;
|
||||
len = strlen(passphrase);
|
||||
if (len > TSS_GetDigestSize(objectPublic.publicArea.nameAlg))
|
||||
{
|
||||
len = TSS_GetDigestSize(objectPublic.publicArea.nameAlg);
|
||||
log_error ("Truncating Passphrase to TPM allowed %d\n", len);
|
||||
}
|
||||
VAL_2B (s.authValue, size) = len;
|
||||
memcpy (VAL_2B (s.authValue, buffer), passphrase, len);
|
||||
|
||||
/* We're responsible for securing the data in transmission to the
|
||||
* TPM here. The TPM provides parameter encryption via a session,
|
||||
* but only for the first parameter. For TPM2_Import, the first
|
||||
* parameter is a symmetric key used to encrypt the sensitive data,
|
||||
* so we must populate this key with random value and encrypt the
|
||||
* sensitive data with it */
|
||||
parentHandle = tpm2_get_parent (tssc, parent);
|
||||
tpm2_ObjectPublic_GetName (&name, &objectPublic.publicArea);
|
||||
gcry_randomize (encryptionKey.buffer,
|
||||
aes_key_bytes, GCRY_STRONG_RANDOM);
|
||||
encryptionKey.size = aes_key_bytes;
|
||||
|
||||
/* set random symSeed */
|
||||
inSymSeed.size = 0;
|
||||
symmetricAlg.algorithm = TPM_ALG_AES;
|
||||
symmetricAlg.keyBits.aes = aes_key_bits;
|
||||
symmetricAlg.mode.aes = TPM_ALG_CFB;
|
||||
|
||||
tpm2_SensitiveToDuplicate (&s, &name, objectPublic.publicArea.nameAlg,
|
||||
&symmetricAlg, &encryptionKey, &duplicate);
|
||||
|
||||
/* use salted parameter encryption to hide the key. First we read
|
||||
* the public parameters of the parent key and use them to agree an
|
||||
* encryption for the first parameter */
|
||||
rc = tpm2_get_hmac_handle (tssc, &ah, parentHandle);
|
||||
if (rc)
|
||||
{
|
||||
tpm2_flush_handle (tssc, parentHandle);
|
||||
return GPG_ERR_CARD;
|
||||
}
|
||||
|
||||
rc = tpm2_Import (tssc, parentHandle, &encryptionKey, &objectPublic,
|
||||
&duplicate, &inSymSeed, &symmetricAlg, &outPrivate,
|
||||
ah, NULL);
|
||||
tpm2_flush_handle (tssc, parentHandle);
|
||||
if (rc)
|
||||
{
|
||||
tpm2_error (rc, "TPM2_Import");
|
||||
/* failure means auth handle is not flushed */
|
||||
tpm2_flush_handle (tssc, ah);
|
||||
|
||||
if ((rc & 0xbf) == TPM_RC_VALUE)
|
||||
{
|
||||
log_error ("TPM cannot import RSA key: wrong size");
|
||||
return GPG_ERR_UNSUPPORTED_ALGORITHM;
|
||||
}
|
||||
else if ((rc & 0xbf) == TPM_RC_CURVE)
|
||||
{
|
||||
log_error ("TPM cannot import requested curve");
|
||||
return GPG_ERR_UNKNOWN_CURVE;
|
||||
}
|
||||
return GPG_ERR_CARD;
|
||||
}
|
||||
|
||||
size = sizeof (pub);
|
||||
buffer = pub;
|
||||
len = 0;
|
||||
TSS_TPM2B_PUBLIC_Marshal (&objectPublic,
|
||||
&len, &buffer, &size);
|
||||
pub_len = len;
|
||||
|
||||
size = sizeof (priv);
|
||||
buffer = priv;
|
||||
len = 0;
|
||||
TSS_TPM2B_PRIVATE_Marshal ((TPM2B_PRIVATE *)&outPrivate,
|
||||
&len, &buffer, &size);
|
||||
priv_len = len;
|
||||
|
||||
*shadow_info = make_tpm2_shadow_info (parent, pub, pub_len,
|
||||
priv, priv_len, shadow_len);
|
||||
return rc;
|
||||
}
|
||||
|
||||
int
|
||||
tpm2_ecc_decrypt (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
const char *ciphertext, int ciphertext_len,
|
||||
char **decrypt, size_t *decrypt_len)
|
||||
{
|
||||
TPM2B_ECC_POINT inPoint;
|
||||
TPM2B_ECC_POINT outPoint;
|
||||
TPM_HANDLE ah;
|
||||
char *auth;
|
||||
size_t len;
|
||||
int ret;
|
||||
|
||||
/* This isn't really a decryption per se. The ciphertext actually
|
||||
* contains an EC Point which we must multiply by the private key number.
|
||||
*
|
||||
* The reason is to generate a diffe helman agreement on a shared
|
||||
* point. This shared point is then used to generate the per
|
||||
* session encryption key.
|
||||
*/
|
||||
if (ciphertext[0] != 0x04)
|
||||
{
|
||||
log_error ("Decryption Shared Point format is not uncompressed\n");
|
||||
return GPG_ERR_ENCODING_PROBLEM;
|
||||
}
|
||||
if ((ciphertext_len & 0x01) != 1)
|
||||
{
|
||||
log_error ("Decryption Shared Point has incorrect length\n");
|
||||
return GPG_ERR_ENCODING_PROBLEM;
|
||||
}
|
||||
len = ciphertext_len >> 1;
|
||||
|
||||
memcpy (VAL_2B (inPoint.point.x, buffer), ciphertext + 1, len);
|
||||
VAL_2B (inPoint.point.x, size) = len;
|
||||
memcpy (VAL_2B (inPoint.point.y, buffer), ciphertext + 1 + len, len);
|
||||
VAL_2B (inPoint.point.y, size) = len;
|
||||
|
||||
ret = tpm2_pre_auth (ctrl, tssc, pin_cb, &ah, &auth);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = tpm2_ECDH_ZGen (tssc, key, &inPoint, &outPoint, ah, auth);
|
||||
ret = tpm2_post_auth (tssc, ret, ah, &auth, "TPM2_ECDH_ZGen");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*decrypt_len = VAL_2B (outPoint.point.x, size) +
|
||||
VAL_2B (outPoint.point.y, size) + 1;
|
||||
*decrypt = xtrymalloc (*decrypt_len);
|
||||
(*decrypt)[0] = 0x04;
|
||||
memcpy (*decrypt + 1, VAL_2B (outPoint.point.x, buffer),
|
||||
VAL_2B (outPoint.point.x, size));
|
||||
memcpy (*decrypt + 1 + VAL_2B (outPoint.point.x, size),
|
||||
VAL_2B (outPoint.point.y, buffer),
|
||||
VAL_2B (outPoint.point.y, size));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
tpm2_rsa_decrypt (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
const char *ciphertext, int ciphertext_len,
|
||||
char **decrypt, size_t *decrypt_len)
|
||||
{
|
||||
int ret;
|
||||
PUBLIC_KEY_RSA_2B cipherText;
|
||||
TPMT_RSA_DECRYPT inScheme;
|
||||
PUBLIC_KEY_RSA_2B message;
|
||||
TPM_HANDLE ah;
|
||||
char *auth;
|
||||
|
||||
inScheme.scheme = TPM_ALG_RSAES;
|
||||
/*
|
||||
* apparent gcrypt error: occasionally rsa ciphertext will
|
||||
* be one byte too long and have a leading zero
|
||||
*/
|
||||
if ((ciphertext_len & 1) == 1 && ciphertext[0] == 0)
|
||||
{
|
||||
log_info ("Fixing Wrong Ciphertext size %d\n", ciphertext_len);
|
||||
ciphertext_len--;
|
||||
ciphertext++;
|
||||
}
|
||||
cipherText.size = ciphertext_len;
|
||||
memcpy (cipherText.buffer, ciphertext, ciphertext_len);
|
||||
|
||||
ret = tpm2_pre_auth (ctrl, tssc, pin_cb, &ah, &auth);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = tpm2_RSA_Decrypt (tssc, key, &cipherText, &inScheme, &message,
|
||||
ah, auth, TPMA_SESSION_ENCRYPT);
|
||||
ret = tpm2_post_auth (tssc, ret, ah, &auth, "TPM2_RSA_Decrypt");
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
*decrypt_len = message.size;
|
||||
*decrypt = xtrymalloc (message.size);
|
||||
memcpy (*decrypt, message.buffer, message.size);
|
||||
|
||||
return 0;
|
||||
}
|
34
tpm2d/tpm2.h
Normal file
34
tpm2d/tpm2.h
Normal file
@ -0,0 +1,34 @@
|
||||
#ifndef _TPM2_H
|
||||
#define _TPM2_H
|
||||
|
||||
#include "../common/util.h"
|
||||
#include "ibm-tss.h"
|
||||
|
||||
int tpm2_start (TSS_CONTEXT **tssc);
|
||||
void tpm2_end (TSS_CONTEXT *tssc);
|
||||
void tpm2_flush_handle (TSS_CONTEXT *tssc, TPM_HANDLE h);
|
||||
int tpm2_load_key (TSS_CONTEXT *tssc, const unsigned char *shadow_info,
|
||||
TPM_HANDLE *key, TPMI_ALG_PUBLIC *type);
|
||||
int tpm2_sign (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
TPMI_ALG_PUBLIC type,
|
||||
const unsigned char *digest, size_t digestlen,
|
||||
unsigned char **r_sig, size_t *r_siglen);
|
||||
int tpm2_import_key (ctrl_t ctrl, TSS_CONTEXT *tssc,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
unsigned char **shadow_info, size_t *shadow_len,
|
||||
gcry_sexp_t s_skey, unsigned long parent);
|
||||
int tpm2_rsa_decrypt (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
const char *ciphertext, int ciphertext_len,
|
||||
char **decrypt, size_t *decrypt_len);
|
||||
int tpm2_ecc_decrypt (ctrl_t ctrl, TSS_CONTEXT *tssc, TPM_HANDLE key,
|
||||
gpg_error_t (*pin_cb)(ctrl_t ctrl, const char *info,
|
||||
char **retstr),
|
||||
const char *ciphertext, int ciphertext_len,
|
||||
char **decrypt, size_t *decrypt_len);
|
||||
|
||||
#endif
|
1289
tpm2d/tpm2daemon.c
Normal file
1289
tpm2d/tpm2daemon.c
Normal file
File diff suppressed because it is too large
Load Diff
98
tpm2d/tpm2daemon.h
Normal file
98
tpm2d/tpm2daemon.h
Normal file
@ -0,0 +1,98 @@
|
||||
/* tpm2daemon.h - Global definitions for the SCdaemon
|
||||
* Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
|
||||
*
|
||||
* This file is part of GnuPG.
|
||||
*
|
||||
* GnuPG is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* GnuPG 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, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#ifndef TPM2DAEMON_H
|
||||
#define TPM2DAEMON_H
|
||||
|
||||
#ifdef GPG_ERR_SOURCE_DEFAULT
|
||||
#error GPG_ERR_SOURCE_DEFAULT already defined
|
||||
#endif
|
||||
/* FIXME: Replace this hard coded value as soon as we require a newer
|
||||
* libgpg-error. */
|
||||
#define GPG_ERR_SOURCE_DEFAULT 16 /* GPG_ERR_SOURCE_TPM2 */
|
||||
#include <gpg-error.h>
|
||||
|
||||
#include <time.h>
|
||||
#include <gcrypt.h>
|
||||
#include "../common/util.h"
|
||||
#include "../common/sysutils.h"
|
||||
|
||||
/* Maximum length of a digest. */
|
||||
#define MAX_DIGEST_LEN 64
|
||||
|
||||
|
||||
|
||||
/* A large struct name "opt" to keep global flags. */
|
||||
EXTERN_UNLESS_MAIN_MODULE
|
||||
struct
|
||||
{
|
||||
unsigned int debug; /* Debug flags (DBG_foo_VALUE). */
|
||||
int verbose; /* Verbosity level. */
|
||||
int quiet; /* Be as quiet as possible. */
|
||||
unsigned long parent; /* TPM parent */
|
||||
} opt;
|
||||
|
||||
|
||||
#define DBG_MPI_VALUE 2 /* debug mpi details */
|
||||
#define DBG_CRYPTO_VALUE 4 /* debug low level crypto */
|
||||
#define DBG_IPC_VALUE 1024
|
||||
|
||||
#define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE)
|
||||
#define DBG_MEMORY (opt.debug & DBG_MEMORY_VALUE)
|
||||
|
||||
struct server_local_s;
|
||||
|
||||
struct server_control_s
|
||||
{
|
||||
/* Private data used to fire up the connection thread. We use this
|
||||
structure do avoid an extra allocation for just a few bytes. */
|
||||
struct {
|
||||
gnupg_fd_t fd;
|
||||
} thread_startup;
|
||||
|
||||
/* Local data of the server; used only in command.c. */
|
||||
struct server_local_s *server_local;
|
||||
|
||||
/* The application context used with this connection or NULL if none
|
||||
associated. Note that this is shared with the other connections:
|
||||
All connections accessing the same reader are using the same
|
||||
application context. */
|
||||
struct assuan_context_s *ctx;
|
||||
|
||||
/* Helper to store the value we are going to sign */
|
||||
struct
|
||||
{
|
||||
unsigned char *value;
|
||||
int valuelen;
|
||||
} in_data;
|
||||
};
|
||||
|
||||
typedef struct app_ctx_s *app_t;
|
||||
|
||||
/*-- tpm2daemon.c --*/
|
||||
void tpm2d_exit (int rc);
|
||||
|
||||
/*-- command.c --*/
|
||||
gpg_error_t initialize_module_command (void);
|
||||
int tpm2d_command_handler (ctrl_t, int);
|
||||
void send_client_notifications (app_t app, int removal);
|
||||
void tpm2d_kick_the_loop (void);
|
||||
int get_active_connection_count (void);
|
||||
|
||||
#endif /*TPM2DAEMON_H*/
|
Loading…
x
Reference in New Issue
Block a user