forked from osmocom/wireshark
Add support for RSA decryption using PKCS #11 tokens
Add support for loading RSA private key files from PKCS #11 tokens, identified by PKCS #11 URIs. Add a new 'pkcs11_libs' UAT which can dynamically load PKCS #11 provider libraries that are not found by p11-kit. The configuration GUI will need additional code to discover available PKCS #11 tokens and will be added later. This feature requires GnuTLS 3.4 with PKCS #11 support, so Windows, macOS via Homebrew, Ubuntu 16.04, Debian Stretch. Not supported: RHEL7. Currently macOS via official packages disables PKCS #11 support, so that will also not work. Change-Id: I20646bfd69c6bd13c8c2d27cb65c164a4b0b7a66 Reviewed-on: https://code.wireshark.org/review/30855 Petri-Dish: Peter Wu <peter@lekensteyn.nl> Tested-by: Petri Dish Buildbot Reviewed-by: Peter Wu <peter@lekensteyn.nl>
This commit is contained in:
parent
53d8e6dcf8
commit
ac58eafa32
|
@ -59,8 +59,9 @@ before_install:
|
|||
- echo $TRAVIS_OS_NAME
|
||||
# macos
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then ./tools/macos-setup-brew.sh; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "osx" ]; then brew install softhsm; fi
|
||||
# linux
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo ./tools/debian-setup.sh --install-optional -q; fi
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo ./tools/debian-setup.sh --install-optional --install-test-deps -q; fi
|
||||
- sudo gem install asciidoctor --no-ri --no-rdoc
|
||||
- if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get install -y python3-pip; fi
|
||||
- sudo pip3 install pytest pytest-xdist
|
||||
|
|
|
@ -990,7 +990,7 @@ if(ENABLE_SMI)
|
|||
set(PACKAGELIST ${PACKAGELIST} SMI)
|
||||
endif()
|
||||
|
||||
# GNU SSL/TLS support
|
||||
# Support for TLS decryption using RSA private keys.
|
||||
if(ENABLE_GNUTLS)
|
||||
set(PACKAGELIST ${PACKAGELIST} GNUTLS)
|
||||
# Minimum version needed.
|
||||
|
@ -1167,6 +1167,22 @@ if(HAVE_LIBLUA)
|
|||
set(HAVE_LUA_H 1)
|
||||
set(HAVE_LUA 1)
|
||||
endif()
|
||||
if(GNUTLS_FOUND AND NOT GNUTLS_VERSION VERSION_LESS "3.4.0")
|
||||
# While all Linux and Windows builds have PKCS #11 support enabled,
|
||||
# macos-setup.sh explicitly disables it using --without-p11-kit.
|
||||
#
|
||||
# Require at least GnuTLS 3.4.0 such that public keys can be calculated
|
||||
# from PKCS #11 private keys.
|
||||
include(CheckSymbolExists)
|
||||
cmake_push_check_state()
|
||||
if(WIN32)
|
||||
set(CMAKE_REQUIRED_DEFINITIONS -Dssize_t=int)
|
||||
endif()
|
||||
set(CMAKE_REQUIRED_INCLUDES ${GNUTLS_INCLUDE_DIRS})
|
||||
set(CMAKE_REQUIRED_LIBRARIES ${GNUTLS_LIBRARIES})
|
||||
check_symbol_exists(gnutls_pkcs11_obj_list_import_url4 gnutls/pkcs11.h HAVE_GNUTLS_PKCS11)
|
||||
cmake_pop_check_state()
|
||||
endif()
|
||||
if(HAVE_LIBKERBEROS)
|
||||
set(HAVE_KERBEROS 1)
|
||||
endif()
|
||||
|
|
|
@ -69,7 +69,7 @@ option(ENABLE_SNAPPY "Build with Snappy compression support" ON)
|
|||
option(ENABLE_NGHTTP2 "Build with HTTP/2 header decompression support" ON)
|
||||
option(ENABLE_LUA "Build with Lua dissector support" ON)
|
||||
option(ENABLE_SMI "Build with libsmi snmp support" ON)
|
||||
option(ENABLE_GNUTLS "Build with GNU TLS support" ON)
|
||||
option(ENABLE_GNUTLS "Build with RSA decryption support" ON)
|
||||
if(WIN32)
|
||||
option(ENABLE_WINSPARKLE "Enable WinSparkle support" ON)
|
||||
endif()
|
||||
|
|
|
@ -29,6 +29,9 @@ install:
|
|||
# Py2 fails the test_tshark_unicode_display_filter test, so use Py3.
|
||||
- set PATH=C:\Python37-x64;C:\Python37-x64\Scripts;%PATH%
|
||||
- pip install pytest pytest-xdist
|
||||
- ps: | # For pkcs11 tests.
|
||||
Invoke-WebRequest -Uri https://github.com/disig/SoftHSM2-for-Windows/releases/download/v2.5.0/SoftHSM2-2.5.0.msi -OutFile $Env:WIRESHARK_BASE_DIR\SoftHSM2-2.5.0.msi
|
||||
& msiexec /qn /i $Env:WIRESHARK_BASE_DIR\SoftHSM2-2.5.0.msi
|
||||
|
||||
# Note: the NSIS installer lacks debug dlls for Debug builds.
|
||||
configuration: RelWithDebInfo
|
||||
|
|
|
@ -112,6 +112,9 @@
|
|||
/* Define to use GnuTLS library */
|
||||
#cmakedefine HAVE_LIBGNUTLS 1
|
||||
|
||||
/* Define to 1 if GnuTLS was built with pkcs11 support. */
|
||||
#cmakedefine HAVE_GNUTLS_PKCS11 1
|
||||
|
||||
/* Enable libnl support */
|
||||
#cmakedefine HAVE_LIBNL 1
|
||||
|
||||
|
|
|
@ -715,6 +715,9 @@ epan_get_compiled_version_info(GString *str)
|
|||
g_string_append(str, ", ");
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
g_string_append(str, "with GnuTLS " LIBGNUTLS_VERSION);
|
||||
#ifdef HAVE_GNUTLS_PKCS11
|
||||
g_string_append(str, " and PKCS #11 support");
|
||||
#endif /* HAVE_GNUTLS_PKCS11 */
|
||||
#else
|
||||
g_string_append(str, "without GnuTLS");
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
|
221
epan/secrets.c
221
epan/secrets.c
|
@ -51,6 +51,19 @@ static guint uat_num_rsa_privkeys;
|
|||
static void register_rsa_uats(void);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_GNUTLS_PKCS11
|
||||
/** PINs for PKCS #11 keys in rsa_privkeys. Must be cleared after rsa_privkeys. */
|
||||
static GSList *rsa_privkeys_pkcs11_pins;
|
||||
|
||||
typedef struct {
|
||||
char *library_path; /**< PKCS #11 library path. */
|
||||
} pkcs11_lib_record_t;
|
||||
|
||||
static uat_t *pkcs11_libs_uat;
|
||||
static pkcs11_lib_record_t *uat_pkcs11_libs;
|
||||
static guint uat_num_pkcs11_libs;
|
||||
#endif /* HAVE_GNUTLS_PKCS11 */
|
||||
|
||||
void
|
||||
secrets_init(void)
|
||||
{
|
||||
|
@ -69,6 +82,10 @@ secrets_cleanup(void)
|
|||
#ifdef HAVE_LIBGNUTLS
|
||||
g_hash_table_destroy(rsa_privkeys);
|
||||
rsa_privkeys = NULL;
|
||||
#ifdef HAVE_GNUTLS_PKCS11
|
||||
g_slist_free_full(rsa_privkeys_pkcs11_pins, g_free);
|
||||
rsa_privkeys_pkcs11_pins = NULL;
|
||||
#endif /* HAVE_GNUTLS_PKCS11 */
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
}
|
||||
|
||||
|
@ -125,6 +142,182 @@ rsa_privkey_add(const cert_key_id_t *key_id, gnutls_privkey_t pkey)
|
|||
g_htonl(dw[1]), g_htonl(dw[2]), g_htonl(dw[3]), g_htonl(dw[4]));
|
||||
}
|
||||
|
||||
#ifdef HAVE_GNUTLS_PKCS11
|
||||
/** Provides a fixed PIN to the caller (or failure if the fixed PIN is NULL). */
|
||||
static int
|
||||
set_pin_callback(void *userdata, int attempt _U_,
|
||||
const char *token_url _U_, const char *token_label _U_,
|
||||
unsigned int flags, char *pin, size_t pin_max)
|
||||
{
|
||||
const char *fixed_pin = (const char *)userdata;
|
||||
size_t fixed_pin_len = fixed_pin ? strlen(fixed_pin) : 0;
|
||||
|
||||
/* Fail if the PIN was not provided, wrong or too long. */
|
||||
if (!fixed_pin || (flags & GNUTLS_PIN_WRONG) || fixed_pin_len >= pin_max) {
|
||||
return GNUTLS_E_PKCS11_PIN_ERROR;
|
||||
}
|
||||
|
||||
memcpy(pin, fixed_pin, fixed_pin_len + 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Load private RSA keys from a PKCS #11 token. Returns zero on success and a
|
||||
* negative error code on failure.
|
||||
*/
|
||||
static int
|
||||
pkcs11_load_keys_from_token(const char *token_uri, const char *pin, char **err)
|
||||
{
|
||||
gnutls_pkcs11_obj_t *list = NULL;
|
||||
unsigned int nlist = 0;
|
||||
int ret;
|
||||
/* An empty/NULL PIN means that none is necessary. */
|
||||
char *fixed_pin = pin && pin[0] ? g_strdup(pin) : NULL;
|
||||
gboolean pin_in_use = FALSE;
|
||||
|
||||
/* Set PIN via a global callback since import_url can prompt for one. */
|
||||
gnutls_pkcs11_set_pin_function(set_pin_callback, fixed_pin);
|
||||
|
||||
/* This might already result in callback for the PIN. */
|
||||
ret = gnutls_pkcs11_obj_list_import_url4(&list, &nlist, token_uri,
|
||||
GNUTLS_PKCS11_OBJ_FLAG_PRIVKEY|GNUTLS_PKCS11_OBJ_FLAG_LOGIN);
|
||||
if (ret < 0) {
|
||||
*err = g_strdup_printf("Failed to iterate through objects for %s: %s", token_uri, gnutls_strerror(ret));
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < nlist; j++) {
|
||||
char *obj_uri = NULL;
|
||||
gnutls_privkey_t privkey = NULL;
|
||||
gnutls_pubkey_t pubkey = NULL;
|
||||
cert_key_id_t key_id;
|
||||
size_t size;
|
||||
|
||||
if (gnutls_pkcs11_obj_get_type(list[j]) != GNUTLS_PKCS11_OBJ_PRIVKEY) {
|
||||
/* Should not happen since we requested private keys only. */
|
||||
goto cont;
|
||||
}
|
||||
|
||||
ret = gnutls_pkcs11_obj_export_url(list[j], GNUTLS_PKCS11_URL_GENERIC, &obj_uri);
|
||||
if (ret < 0) {
|
||||
/* Should not happen either if the object is valid. */
|
||||
goto cont;
|
||||
}
|
||||
|
||||
ret = gnutls_privkey_init(&privkey);
|
||||
if (ret < 0) {
|
||||
/* Out of memory? */
|
||||
goto cont;
|
||||
}
|
||||
|
||||
/* Set the PIN to be used during decryption. */
|
||||
gnutls_privkey_set_pin_function(privkey, set_pin_callback, fixed_pin);
|
||||
|
||||
/* Can prompt for PIN. Can also invoke the token function set by
|
||||
* gnutls_pkcs11_set_token_function (if not set, it will just fail
|
||||
* immediately rather than retrying). */
|
||||
ret = gnutls_privkey_import_url(privkey, obj_uri, 0);
|
||||
if (ret < 0) {
|
||||
/* Bad PIN or some other system error? */
|
||||
g_debug("Failed to import private key %s: %s", obj_uri, gnutls_strerror(ret));
|
||||
goto cont;
|
||||
}
|
||||
|
||||
if (gnutls_privkey_get_pk_algorithm(privkey, NULL) != GNUTLS_PK_RSA) {
|
||||
g_debug("Skipping private key %s, not RSA.", obj_uri);
|
||||
goto cont;
|
||||
}
|
||||
|
||||
ret = gnutls_pubkey_init(&pubkey);
|
||||
if (ret < 0) {
|
||||
/* Out of memory? */
|
||||
goto cont;
|
||||
}
|
||||
|
||||
/* This requires GnuTLS 3.4.0 and will fail on older versions. */
|
||||
ret = gnutls_pubkey_import_privkey(pubkey, privkey, 0, 0);
|
||||
if (ret < 0) {
|
||||
g_debug("Failed to import public key %s: %s", obj_uri, gnutls_strerror(ret));
|
||||
goto cont;
|
||||
}
|
||||
|
||||
size = sizeof(key_id);
|
||||
ret = gnutls_pubkey_get_key_id(pubkey, GNUTLS_KEYID_USE_SHA1, key_id.key_id, &size);
|
||||
if (ret < 0 || size != sizeof(key_id)) {
|
||||
g_debug("Failed to calculate Key ID for %s: %s", obj_uri, gnutls_strerror(ret));
|
||||
goto cont;
|
||||
}
|
||||
|
||||
/* Remember the private key. */
|
||||
rsa_privkey_add(&key_id, privkey);
|
||||
privkey = NULL;
|
||||
pin_in_use = TRUE;
|
||||
|
||||
cont:
|
||||
gnutls_privkey_deinit(privkey);
|
||||
gnutls_pubkey_deinit(pubkey);
|
||||
gnutls_free(obj_uri);
|
||||
gnutls_pkcs11_obj_deinit(list[j]);
|
||||
}
|
||||
gnutls_free(list);
|
||||
if (pin_in_use) {
|
||||
/* Remember PINs such they can be freed later. */
|
||||
rsa_privkeys_pkcs11_pins = g_slist_prepend(rsa_privkeys_pkcs11_pins, fixed_pin);
|
||||
fixed_pin = NULL;
|
||||
}
|
||||
ret = 0;
|
||||
|
||||
cleanup:
|
||||
/* Forget about the PIN. */
|
||||
gnutls_pkcs11_set_pin_function(NULL, NULL);
|
||||
g_free(fixed_pin);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/** Load all libraries specified in a UAT. */
|
||||
static void
|
||||
uat_pkcs11_libs_load_all(void)
|
||||
{
|
||||
int ret;
|
||||
GString *err = NULL;
|
||||
|
||||
for (guint i = 0; i < uat_num_pkcs11_libs; i++) {
|
||||
const pkcs11_lib_record_t *rec = &uat_pkcs11_libs[i];
|
||||
const char *libname = rec->library_path;
|
||||
/* Note: should return success for already loaded libraries. */
|
||||
ret = gnutls_pkcs11_add_provider(libname, NULL);
|
||||
if (ret) {
|
||||
if (!err) {
|
||||
err = g_string_new("Error loading PKCS #11 libraries:");
|
||||
}
|
||||
g_string_append_printf(err, "\n%s: %s", libname, gnutls_strerror(ret));
|
||||
}
|
||||
}
|
||||
if (err) {
|
||||
report_failure("%s", err->str);
|
||||
g_string_free(err, TRUE);
|
||||
}
|
||||
}
|
||||
|
||||
UAT_FILENAME_CB_DEF(pkcs11_libs_uats, library_path, pkcs11_lib_record_t)
|
||||
|
||||
static void *
|
||||
uat_pkcs11_lib_copy_str_cb(void *dest, const void *source, size_t len _U_)
|
||||
{
|
||||
pkcs11_lib_record_t *d = (pkcs11_lib_record_t *)dest;
|
||||
const pkcs11_lib_record_t *s = (const pkcs11_lib_record_t *)source;
|
||||
d->library_path = g_strdup(s->library_path);
|
||||
return dest;
|
||||
}
|
||||
|
||||
static void
|
||||
uat_pkcs11_lib_free_str_cb(void *record)
|
||||
{
|
||||
pkcs11_lib_record_t *rec = (pkcs11_lib_record_t *)record;
|
||||
g_free(rec->library_path);
|
||||
}
|
||||
#endif /* HAVE_GNUTLS_PKCS11 */
|
||||
|
||||
UAT_FILENAME_CB_DEF(rsa_privkeys_uats, uri, rsa_privkey_record_t)
|
||||
UAT_CSTRING_CB_DEF(rsa_privkeys_uats, password, rsa_privkey_record_t)
|
||||
|
||||
|
@ -202,6 +395,10 @@ uat_rsa_privkeys_post_update(void)
|
|||
{
|
||||
/* Clear previous keys. */
|
||||
g_hash_table_remove_all(rsa_privkeys);
|
||||
#ifdef HAVE_GNUTLS_PKCS11
|
||||
g_slist_free_full(rsa_privkeys_pkcs11_pins, g_free);
|
||||
rsa_privkeys_pkcs11_pins = NULL;
|
||||
#endif /* HAVE_GNUTLS_PKCS11 */
|
||||
GString *errors = NULL;
|
||||
|
||||
for (guint i = 0; i < uat_num_rsa_privkeys; i++) {
|
||||
|
@ -210,6 +407,9 @@ uat_rsa_privkeys_post_update(void)
|
|||
char *err = NULL;
|
||||
|
||||
if (g_str_has_prefix(token_uri, "pkcs11:")) {
|
||||
#ifdef HAVE_GNUTLS_PKCS11
|
||||
pkcs11_load_keys_from_token(token_uri, rec->password, &err);
|
||||
#endif /* HAVE_GNUTLS_PKCS11 */
|
||||
} else {
|
||||
load_rsa_keyfile(token_uri, rec->password, &err);
|
||||
}
|
||||
|
@ -236,6 +436,27 @@ uat_rsa_privkeys_post_update(void)
|
|||
static void
|
||||
register_rsa_uats(void)
|
||||
{
|
||||
#ifdef HAVE_GNUTLS_PKCS11
|
||||
static uat_field_t uat_pkcs11_libs_fields[] = {
|
||||
UAT_FLD_FILENAME_OTHER(pkcs11_libs_uats, library_path, "Library Path", NULL, "PKCS #11 provider library file"),
|
||||
UAT_END_FIELDS
|
||||
};
|
||||
pkcs11_libs_uat = uat_new("PKCS #11 Provider Libraries",
|
||||
sizeof(pkcs11_lib_record_t),
|
||||
"pkcs11_libs", /* filename */
|
||||
FALSE, /* from_profile */
|
||||
&uat_pkcs11_libs, /* data_ptr */
|
||||
&uat_num_pkcs11_libs, /* numitems_ptr */
|
||||
0, /* does not directly affect dissection */
|
||||
NULL, /* Help section (currently a wiki page) */
|
||||
uat_pkcs11_lib_copy_str_cb, /* copy_cb */
|
||||
NULL, /* update_cb */
|
||||
uat_pkcs11_lib_free_str_cb, /* free_cb */
|
||||
uat_pkcs11_libs_load_all, /* post_update_cb */
|
||||
NULL, /* reset_cb */
|
||||
uat_pkcs11_libs_fields);
|
||||
#endif /* HAVE_GNUTLS_PKCS11 */
|
||||
|
||||
static uat_field_t uat_rsa_privkeys_fields[] = {
|
||||
UAT_FLD_FILENAME_OTHER(rsa_privkeys_uats, uri, "Keyfile or Token URI", NULL, "RSA Key File or PKCS #11 URI for token"),
|
||||
UAT_FLD_FILENAME_OTHER(rsa_privkeys_uats, password, "Password", NULL, "RSA Key File password or PKCS #11 Token PIN"),
|
||||
|
|
|
@ -145,12 +145,14 @@ def features(cmd_tshark, make_env):
|
|||
tshark_v = ''
|
||||
gcry_m = re.search(r'with +Gcrypt +([0-9]+\.[0-9]+)', tshark_v)
|
||||
return types.SimpleNamespace(
|
||||
have_x64='Compiled (64-bit)' in tshark_v,
|
||||
have_lua='with Lua' in tshark_v,
|
||||
have_nghttp2='with nghttp2' in tshark_v,
|
||||
have_kerberos='with MIT Kerberos' in tshark_v or 'with Heimdal Kerberos' in tshark_v,
|
||||
have_libgcrypt16=gcry_m and float(gcry_m.group(1)) >= 1.6,
|
||||
have_libgcrypt17=gcry_m and float(gcry_m.group(1)) >= 1.7,
|
||||
have_gnutls='with GnuTLS' in tshark_v,
|
||||
have_pkcs11='and PKCS #11 support' in tshark_v,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
-----BEGIN PRIVATE KEY-----
|
||||
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAOE/58jJArmp9xd6
|
||||
M42ttJk+tbzG5U47pHfj9wFVMoPPUP5/9Mfl9PGBm7GdnGxW//9wQrphEjrqrwya
|
||||
st6OBO5yb1DotLAl8FecN6eJxSTMPPS108A2D52kXdZohFgn8OjDhi3wR09L42vC
|
||||
1w7hPJOHIIuzPbywLauhqB7H/OazAgMBAAECgYEAgEnza0oBAVmqX3a8Ef9TEszC
|
||||
mWf2hd42SApQTjQF90iGaszZz1hLb5lP4ZNQ2eubFhMMDjbnOSMc2+Ln6RWd/Fb1
|
||||
Oy8mYkXYwPH+a2PHwa0yQ6IWuqRgZ09EijO+DxRlk8pn5hBj7YC8gU9hIce/PAkx
|
||||
z50FS6yEPT3U7xouJ9ECQQDm2BAoJasJBOKpYTHbPN4fhZ9m5DV65+nvu9ZgbxeR
|
||||
FfrBbYRva9KX9q/E03sSPkoEEHV21KYCMYz2g0FipqqXAkEA+cvGNz2upzwX8Ty5
|
||||
WKR323NHEVSMerEbbAQikamcCYnR5vdeJ2w3OMYNWTOQrNE9+Rk3gfaMCxwqER1l
|
||||
eW/0RQJAIzPLsvObk3KFRiMmQTKVBOWRm1UtuqJnEEHqvSXzyBI7/QdAbOVaZgYe
|
||||
Y7uERxHso5YG86oV7ruzrVvyuqKD3QJAH4do7XALq3AaVYiknFumBTz3q2hQkuvn
|
||||
2iprcpdF6q5KoCx45eDy12eoJ6oqiKWgfOCB8RV9d6mGZcKgHEPVQQJALFi49VGr
|
||||
qyX5SCcfS6c+XgX/VuJGS6eFxhpXHolAG3O1FmbQMKJ618bOGt8jYjGYR0bM5Kgd
|
||||
sDXwcu06rODBIA==
|
||||
-----END PRIVATE KEY-----
|
|
@ -10,7 +10,12 @@
|
|||
'''Decryption tests'''
|
||||
|
||||
import os.path
|
||||
import shutil
|
||||
import subprocess
|
||||
import subprocesstest
|
||||
import sys
|
||||
import sysconfig
|
||||
import types
|
||||
import unittest
|
||||
import fixtures
|
||||
|
||||
|
@ -862,3 +867,122 @@ class case_decrypt_knxip(subprocesstest.SubprocessTestCase):
|
|||
self.assertTrue(self.grepOutput('^IA 1[.]1[.]4 SeqNr 12345$'))
|
||||
self.assertTrue(self.grepOutput('^IA 2[.]1[.]0 key B0 B1 B2 B3 B4 B5 B6 B7 B8 B9 BA BB BC BD BE BF$'))
|
||||
self.assertTrue(self.grepOutput('^IA 2[.]1[.]0 SeqNr 1234$'))
|
||||
|
||||
|
||||
@fixtures.fixture(scope='session')
|
||||
def softhsm_paths(features):
|
||||
if sys.platform == 'win32':
|
||||
search_path = os.getenv('PATH') + r';C:\SoftHSM2\bin'
|
||||
else:
|
||||
search_path = None
|
||||
softhsm_tool = shutil.which('softhsm2-util', path=search_path)
|
||||
if not softhsm_tool:
|
||||
# Note: do not fallback to SoftHSMv1. While available on Ubuntu 14.04
|
||||
# (and 16.04), it is built with botan < 1.11.10 which causes a crash due
|
||||
# to a conflict with the GMP library that is also used by GnuTLS/nettle.
|
||||
# See https://github.com/randombit/botan/issues/1090
|
||||
fixtures.skip('SoftHSM is not found')
|
||||
# Find provider library path.
|
||||
bindir = os.path.dirname(softhsm_tool)
|
||||
libdir = os.path.join(os.path.dirname(bindir), 'lib')
|
||||
if sys.platform == 'win32':
|
||||
libdirs = [libdir, bindir]
|
||||
if features.have_x64:
|
||||
name = 'softhsm2-x64.dll'
|
||||
else:
|
||||
name = 'softhsm2.dll'
|
||||
else:
|
||||
# Debian/Ubuntu-specific paths
|
||||
madir = sysconfig.get_config_var('multiarchsubdir')
|
||||
libdir64_sub = os.path.join(libdir + '64', 'softhsm')
|
||||
libdir_sub = os.path.join(libdir, 'softhsm')
|
||||
libdirs = [os.path.join(libdir + madir, 'softhsm')] if madir else []
|
||||
libdirs += [libdir_sub, libdir64_sub]
|
||||
name = 'libsofthsm2.so'
|
||||
for libdir in libdirs:
|
||||
provider = os.path.join(libdir, name)
|
||||
if os.path.exists(provider):
|
||||
break
|
||||
else:
|
||||
# Even if p11-kit can automatically locate it, do not rely on it.
|
||||
fixtures.skip('SoftHSM provider library not detected')
|
||||
# Now check whether the import tool is usable. SoftHSM < 2.3.0 did not
|
||||
# set CKA_DECRYPT when using softhsm2-tool --import and therefore cannot be
|
||||
# used to import keys for decryption. Use GnuTLS p11tool as workaround.
|
||||
softhsm_version = subprocess.check_output([softhsm_tool, '--version'],
|
||||
universal_newlines=True).strip()
|
||||
use_p11tool = softhsm_version in ('2.0.0', '2.1.0', '2.2.0')
|
||||
if use_p11tool and not shutil.which('p11tool'):
|
||||
fixtures.skip('SoftHSM available, but GnuTLS p11tool is unavailable')
|
||||
return use_p11tool, softhsm_tool, provider
|
||||
|
||||
|
||||
@fixtures.fixture
|
||||
def softhsm(softhsm_paths, home_path, base_env):
|
||||
'''Creates a temporary SoftHSM token store (and set it in the environment),
|
||||
returns a function to populate that token store and the path to the PKCS #11
|
||||
provider library.'''
|
||||
use_p11tool, softhsm_tool, provider = softhsm_paths
|
||||
conf_path = os.path.join(home_path, 'softhsm-test.conf')
|
||||
db_path = os.path.join(home_path, 'softhsm-test-tokens')
|
||||
os.makedirs(db_path)
|
||||
with open(conf_path, 'w') as f:
|
||||
f.write('directories.tokendir = %s\n' % db_path)
|
||||
f.write('objectstore.backend = file\n')
|
||||
# Avoid syslog spam
|
||||
f.write('log.level = ERROR\n')
|
||||
base_env['SOFTHSM2_CONF'] = conf_path
|
||||
|
||||
tool_env = base_env.copy()
|
||||
if sys.platform == 'win32':
|
||||
# Ensure that softhsm2-util can find the library.
|
||||
tool_env['PATH'] += ';%s' % os.path.dirname(provider)
|
||||
|
||||
# Initialize tokens store.
|
||||
token_name = 'Wireshark-Test-Tokens'
|
||||
pin = 'Secret'
|
||||
subprocess.check_call([softhsm_tool, '--init-token', '--slot', '0',
|
||||
'--label', token_name, '--so-pin', 'Supersecret', '--pin', pin],
|
||||
env=tool_env)
|
||||
if use_p11tool:
|
||||
tool_env['GNUTLS_PIN'] = pin
|
||||
|
||||
# Arbitrary IDs and labels.
|
||||
ids = iter(range(0xab12, 0xffff))
|
||||
def import_key(keyfile):
|
||||
'''Returns a PKCS #11 URI to identify the imported key.'''
|
||||
label = os.path.basename(keyfile)
|
||||
obj_id = '%x' % next(ids)
|
||||
if not use_p11tool:
|
||||
tool_args = [softhsm_tool, '--import', keyfile, '--label', label,
|
||||
'--id', obj_id, '--pin', pin, '--token', token_name]
|
||||
else:
|
||||
# Fallback for SoftHSM < 2.3.0
|
||||
tool_args = ['p11tool', '--provider', provider, '--batch',
|
||||
'--login', '--write', 'pkcs11:token=%s' % token_name,
|
||||
'--load-privkey', keyfile, '--label', label, '--id', obj_id]
|
||||
subprocess.check_call(tool_args, env=tool_env)
|
||||
id_str = '%{}{}%{}{}'.format(*obj_id)
|
||||
return 'pkcs11:token=%s;id=%s;type=private' % (token_name, id_str)
|
||||
|
||||
return types.SimpleNamespace(import_key=import_key, provider=provider, pin=pin)
|
||||
|
||||
|
||||
@fixtures.mark_usefixtures('test_env')
|
||||
@fixtures.uses_fixtures
|
||||
class case_decrypt_pkcs11(subprocesstest.SubprocessTestCase):
|
||||
def test_tls_pkcs11(self, cmd_tshark, dirs, capture_file, features, softhsm):
|
||||
'''Check that a RSA key in a PKCS #11 token enables decryption.'''
|
||||
if not features.have_pkcs11:
|
||||
self.skipTest('Requires GnuTLS with PKCS #11 support.')
|
||||
key_file = os.path.join(dirs.key_dir, 'rsa-p-lt-q.p8')
|
||||
key_uri = softhsm.import_key(key_file)
|
||||
proc = self.assertRun((cmd_tshark,
|
||||
'-r', capture_file('rsa-p-lt-q.pcap'),
|
||||
'-o', 'uat:pkcs11_libs:"{}"'.format(softhsm.provider.replace('\\', '\\x5c')),
|
||||
'-o', 'uat:rsa_keys:"{}","{}"'.format(key_uri, softhsm.pin),
|
||||
'-Tfields',
|
||||
'-e', 'http.request.uri',
|
||||
'-Y', 'http',
|
||||
))
|
||||
self.assertIn('/', proc.stdout_str)
|
||||
|
|
|
@ -18,6 +18,7 @@ then
|
|||
printf "Usage: %s [--install-optional] [--install-deb-deps] [...other options...]\\n" "$0"
|
||||
printf "\\t--install-optional: install optional software as well\\n"
|
||||
printf "\\t--install-deb-deps: install packages required to build the .deb file\\n"
|
||||
printf "\\t--install-test-deps: install packages required to run all tests\\n"
|
||||
printf "\\t[other]: other options are passed as-is to apt\\n"
|
||||
exit 1
|
||||
fi
|
||||
|
@ -29,17 +30,24 @@ then
|
|||
exit 1
|
||||
fi
|
||||
|
||||
for op
|
||||
do
|
||||
if [ "$op" = "--install-optional" ]
|
||||
then
|
||||
ADDITIONAL=1
|
||||
elif [ "$op" = "--install-deb-deps" ]
|
||||
then
|
||||
DEBDEPS=1
|
||||
else
|
||||
OPTIONS="$OPTIONS $op"
|
||||
fi
|
||||
ADDITIONAL=0
|
||||
DEBDEPS=0
|
||||
TESTDEPS=0
|
||||
for arg; do
|
||||
case $arg in
|
||||
--install-optional)
|
||||
ADDITIONAL=1
|
||||
;;
|
||||
--install-deb-deps)
|
||||
DEBDEPS=1
|
||||
;;
|
||||
--install-test-deps)
|
||||
TESTDEPS=1
|
||||
;;
|
||||
*)
|
||||
OPTIONS="$OPTIONS $arg"
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
BASIC_LIST="libglib2.0-dev \
|
||||
|
@ -83,6 +91,8 @@ DEBDEPS_LIST="debhelper \
|
|||
libxml2-utils \
|
||||
quilt"
|
||||
|
||||
TESTDEPS_LIST=
|
||||
|
||||
# Adds package $2 to list variable $1 if the package is found.
|
||||
# If $3 is given, then this version requirement must be satisfied.
|
||||
add_package() {
|
||||
|
@ -122,7 +132,7 @@ echo "libssh-gcrypt-dev and libssh-dev are unavailable" >&2
|
|||
add_package ADDITIONAL_LIST libgnutls28-dev ||
|
||||
echo "libgnutls28-dev is unavailable" >&2
|
||||
|
||||
# mmdbresolve
|
||||
# Debian >= jessie-backports, Ubuntu >= 16.04
|
||||
add_package ADDITIONAL_LIST libmaxminddb-dev ||
|
||||
echo "libmaxminddb-dev is unavailable" >&2
|
||||
|
||||
|
@ -132,6 +142,18 @@ add_package DEBDEPS_LIST libsystemd-dev ||
|
|||
add_package DEBDEPS_LIST libsystemd-journal-dev ||
|
||||
echo "libsystemd-dev is unavailable"
|
||||
|
||||
# softhsm2 2.0.0: Ubuntu 16.04
|
||||
# softhsm2 2.2.0: Debian >= jessie-backports, Ubuntu 18.04
|
||||
# softhsm2 >= 2.4.0: Debian >= buster, Ubuntu >= 18.10
|
||||
if ! add_package TESTDEPS_LIST softhsm2 '>= 2.3.0'; then
|
||||
if add_package TESTDEPS_LIST softhsm2; then
|
||||
# If SoftHSM 2.3.0 is unavailble, install p11tool.
|
||||
TESTDEPS_LIST="$TESTDEPS_LIST gnutls-bin"
|
||||
else
|
||||
echo "softhsm2 is unavailable" >&2
|
||||
fi
|
||||
fi
|
||||
|
||||
ACTUAL_LIST=$BASIC_LIST
|
||||
|
||||
# Now arrange for optional support libraries
|
||||
|
@ -145,6 +167,11 @@ then
|
|||
ACTUAL_LIST="$ACTUAL_LIST $DEBDEPS_LIST"
|
||||
fi
|
||||
|
||||
if [ $TESTDEPS ]
|
||||
then
|
||||
ACTUAL_LIST="$ACTUAL_LIST $TESTDEPS_LIST"
|
||||
fi
|
||||
|
||||
apt-get update || exit 2
|
||||
# shellcheck disable=SC2086
|
||||
apt-get install $ACTUAL_LIST $OPTIONS || exit 2
|
||||
|
|
Loading…
Reference in New Issue