Make volume_key FIPS compliant

Resolves: #2143223
This commit is contained in:
Jiri Kucera 2023-07-31 16:33:03 +02:00
parent 1c321d365c
commit 0f4313def9
3 changed files with 207 additions and 1 deletions

View File

@ -0,0 +1,146 @@
diff --git a/lib/crypto.c b/lib/crypto.c
index 4b9006a..5b562d1 100644
--- a/lib/crypto.c
+++ b/lib/crypto.c
@@ -69,6 +69,115 @@ error_from_pr (GError **error)
g_free (err);
}
+/*
+ * FIPS compliant implementation of PK11_ImportSymKey().
+ * Source: https://github.com/ceph/ceph/pull/27104/files
+ */
+static PK11SymKey *
+import_sym_key (PK11SlotInfo *slot, CK_MECHANISM_TYPE type, PK11Origin origin,
+ CK_ATTRIBUTE_TYPE operation, SECItem *key, void *wincx)
+{
+ CK_MECHANISM_TYPE wrap_mechanism = 0UL;
+ PK11SymKey *wrapping_key = NULL, *sym_key = NULL;
+ SECItem tmp_sec_item, wrapped_key_item, *raw_key_aligned = NULL;
+ PK11Context *wrap_key_crypt_context = NULL;
+ int block_size = 0;
+ size_t wrapped_key_size = 0;
+ unsigned char *wrapped_key = NULL;
+ int out_len = 0;
+ SECStatus ret = 0;
+
+ /* Fall back to PK11_ImportSymKey() if FIPS mode is disabled. */
+ if (PK11_IsFIPS () == PR_FALSE)
+ return PK11_ImportSymKey (slot, type, origin, operation, key, wincx);
+
+ /* Get the best mechanism for the wrapping operation. */
+ wrap_mechanism = PK11_GetBestWrapMechanism (slot);
+
+ /* Based on that mechanism, generate a symetric key <wrapping_key>. */
+ wrapping_key = PK11_KeyGen (slot, wrap_mechanism, NULL,
+ PK11_GetBestKeyLength (slot, wrap_mechanism),
+ NULL);
+ if (wrapping_key == NULL)
+ return NULL;
+
+ /* Create the context for the wrapping operation. The context contains:
+ * - <wrapping_key>
+ * - operation to perform (CKA_ENCRYPT)
+ */
+ memset (&tmp_sec_item, 0, sizeof (tmp_sec_item));
+ wrap_key_crypt_context = PK11_CreateContextBySymKey (wrap_mechanism,
+ CKA_ENCRYPT,
+ wrapping_key,
+ &tmp_sec_item);
+ if (wrap_key_crypt_context == NULL) {
+ PK11_FreeSymKey (wrapping_key);
+ return NULL;
+ }
+
+ /* Align <key> to the block size specified by the wrapping mechanism. */
+ block_size = PK11_GetBlockSize (wrap_mechanism, NULL);
+ raw_key_aligned = PK11_BlockData (key, block_size);
+ if (raw_key_aligned == NULL) {
+ PK11_DestroyContext (wrap_key_crypt_context, PR_TRUE);
+ PK11_FreeSymKey (wrapping_key);
+ return NULL;
+ }
+
+ /* Prepare for <key> wrap. First, allocate enough space for
+ * the wrapped <key>. Add the padding of the size of one block behind the
+ * aligned <key> to make sure the wrapping operation will not hit the wall.
+ */
+ wrapped_key_size = raw_key_aligned->len + block_size;
+ wrapped_key = g_try_malloc0 (wrapped_key_size);
+ if (wrapped_key == NULL) {
+ SECITEM_FreeItem (raw_key_aligned, PR_TRUE);
+ PK11_DestroyContext (wrap_key_crypt_context, PR_TRUE);
+ PK11_FreeSymKey (wrapping_key);
+ return NULL;
+ }
+
+ /* Do the wrap operation. <wrapped_key> is now a pair (<wrapping_key>, <key>)
+ * expressing that raw key <key> is now encrypted with the <wrapping_key>.
+ */
+ ret = PK11_CipherOp (wrap_key_crypt_context, wrapped_key, &out_len,
+ wrapped_key_size, raw_key_aligned->data,
+ raw_key_aligned->len);
+ if (ret != SECSuccess) {
+ g_free (wrapped_key);
+ SECITEM_FreeItem (raw_key_aligned, PR_TRUE);
+ PK11_DestroyContext (wrap_key_crypt_context, PR_TRUE);
+ PK11_FreeSymKey (wrapping_key);
+ return NULL;
+ }
+
+ /* Finish the wrapping operation and release no more needed resources. */
+ ret = PK11_Finalize (wrap_key_crypt_context);
+ SECITEM_FreeItem (raw_key_aligned, PR_TRUE);
+ PK11_DestroyContext (wrap_key_crypt_context, PR_TRUE);
+ if (ret != SECSuccess) {
+ g_free (wrapped_key);
+ PK11_FreeSymKey (wrapping_key);
+ return NULL;
+ }
+
+ /* Prepare for unwrapping the <key>. */
+ memset (&tmp_sec_item, 0, sizeof (tmp_sec_item));
+ memset (&wrapped_key_item, 0, sizeof (wrapped_key_item));
+ wrapped_key_item.data = wrapped_key;
+ wrapped_key_item.len = wrapped_key_size;
+
+ /* Unwrap the <key>. First, decrypt the <key> with the <wrapping_key> to get
+ * its raw form. Then make a symmetric key (<key>, <type>, <operation>). This
+ * makes a symmetric key from the raw <key> in a FIPS compliant manner.
+ */
+ sym_key = PK11_UnwrapSymKey (wrapping_key, wrap_mechanism, &tmp_sec_item,
+ &wrapped_key_item, type, operation, key->len);
+ g_free (wrapped_key);
+ PK11_FreeSymKey (wrapping_key);
+ return sym_key;
+}
+
/* LIBVK_PACKET_FORMAT_ASYMMETRIC */
/* Encrypt DATA of SIZE for CERT.
@@ -291,9 +400,9 @@ wrap_asymmetric (void **wrapped_secret, size_t *wrapped_secret_size,
CKM_GENERIC_SECRET_KEY_GEN. */
clear_secret_item.data = (void *)clear_secret_data;
clear_secret_item.len = clear_secret_size;
- secret_key = PK11_ImportSymKey (slot, CKM_GENERIC_SECRET_KEY_GEN,
- PK11_OriginUnwrap, CKA_WRAP,
- &clear_secret_item, pwfn_arg);
+ secret_key = import_sym_key (slot, CKM_GENERIC_SECRET_KEY_GEN,
+ PK11_OriginUnwrap, CKA_WRAP,
+ &clear_secret_item, pwfn_arg);
PK11_FreeSlot (slot);
if (secret_key == NULL)
{
@@ -471,9 +580,9 @@ wrap_symmetric (void **wrapped_secret, size_t *wrapped_secret_size, void **iv,
/* The disk encryption mechanism might not have a PKCS11 name, and we don't
really need to tell NSS specifics anyway, so just use
CKM_GENERIC_SECRET_KEY_GEN. */
- secret_key = PK11_ImportSymKey (slot, CKM_GENERIC_SECRET_KEY_GEN,
- PK11_OriginUnwrap, CKA_WRAP,
- &clear_secret_item, pwfn_arg);
+ secret_key = import_sym_key (slot, CKM_GENERIC_SECRET_KEY_GEN,
+ PK11_OriginUnwrap, CKA_WRAP,
+ &clear_secret_item, pwfn_arg);
PK11_FreeSlot (slot);
if (secret_key == NULL)
{

View File

@ -0,0 +1,49 @@
diff --git a/src/volume_key.c b/src/volume_key.c
index 24b70d6..9642072 100644
--- a/src/volume_key.c
+++ b/src/volume_key.c
@@ -17,6 +17,7 @@ Author: Miloslav Trmač <mitr@redhat.com> */
#include <config.h>
#include <assert.h>
+#include <errno.h>
#include <langinfo.h>
#include <locale.h>
#include <regex.h>
@@ -403,6 +404,7 @@ get_password (const char *prompt)
char buf[LINE_MAX], *p;
struct termios otermios;
gboolean echo_disabled;
+ int saved_errno = 0;
tty = fopen ("/dev/tty", "r+");
if (tty != NULL)
@@ -430,7 +432,8 @@ get_password (const char *prompt)
echo_disabled = tcsetattr (fileno (in_file), TCSAFLUSH, &ntermios) == 0;
}
- p = fgets(buf, sizeof(buf), in_file);
+ p = fgets (buf, sizeof (buf), in_file);
+ saved_errno = errno;
if (echo_disabled)
{
@@ -441,13 +444,15 @@ get_password (const char *prompt)
if (tty != NULL)
fclose (tty);
- if (p == NULL)
+ if (p == NULL) {
+ fprintf (stderr, "fgets: %s\n", strerror (saved_errno));
return NULL;
+ }
- p = strchr(buf, '\r');
+ p = strchr (buf, '\r');
if (p != NULL)
*p = '\0';
- p = strchr(buf, '\n');
+ p = strchr (buf, '\n');
if (p != NULL)
*p = '\0';

View File

@ -1,7 +1,7 @@
Summary: An utility for manipulating storage encryption keys and passphrases
Name: volume_key
Version: 0.3.11
Release: 5%{?dist}
Release: 6%{?dist}
# lib/{SECerrs,SSLerrs}.h are both licensed under MPLv1.1, GPLv2 and LGPLv2
License: GPLv2 and (MPLv1.1 or GPLv2 or LGPLv2)
Group: Applications/System
@ -12,6 +12,11 @@ Source0: https://releases.pagure.org/volume_key/volume_key-%{version}.tar.xz
# Add support for opening all types of LUKS devices (not just LUKS1)
# Resolves: rhbz#1626974
Patch0: volume_key-0.3.11-support_LUKS_all.patch
# Make volume_key working in FIPS mode
# Resolves: rhbz#2143223
Patch1: volume_key-0.3.11-FIPS.patch
# Diagnose patch to get more insight on whats wrong
Patch2: volume_key-0.3.11-show_get_password_error.patch
BuildRequires: cryptsetup-luks-devel, gettext-devel, glib2-devel, /usr/bin/gpg2
BuildRequires: gpgme-devel, libblkid-devel, nss-devel, python3-devel
# Needed by %%check:
@ -80,6 +85,8 @@ for other formats is possible, some formats are planned for future releases.
%prep
%setup -q
%patch0 -p1
%patch1 -p1
%patch2 -p1
%build
%configure
@ -128,6 +135,10 @@ rm -rf $RPM_BUILD_ROOT
%{python3_sitearch}/__pycache__/volume_key.*
%changelog
* Fri Jul 21 2023 Jiri Kucera <jkucera@redhat.com> - 0.3.11-6
- Make volume_key working in FIPS mode
Resolves: #2143223
* Wed Aug 14 2019 Jiri Kucera <jkucera@redhat.com> - 0.3.11-5
- Place %%find_lang to proper place
Resolves: #1665135