From 6871e0354348faa4b004c37e91d1f4eae5d2a681 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 8 Aug 2024 09:33:20 +0200
Subject: [PATCH 1/6] crypto: Add a function to check for OPAL support for a
 device

---
 docs/libblockdev-sections.txt  |  1 +
 src/lib/plugin_apis/crypto.api | 14 +++++++++++
 src/plugins/crypto.c           | 43 ++++++++++++++++++++++++++++++++++
 src/plugins/crypto.h           | 21 +++++++++++++++++
 4 files changed, 79 insertions(+)

diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt
index c58d11a6..68b456b4 100644
--- a/docs/libblockdev-sections.txt
+++ b/docs/libblockdev-sections.txt
@@ -125,6 +125,7 @@ bd_crypto_bitlk_open
 bd_crypto_bitlk_close
 bd_crypto_fvault2_open
 bd_crypto_fvault2_close
+bd_crypto_opal_is_supported
 BDCryptoTech
 BDCryptoTechMode
 bd_crypto_is_tech_avail
diff --git a/src/lib/plugin_apis/crypto.api b/src/lib/plugin_apis/crypto.api
index d35792bd..514d84bd 100644
--- a/src/lib/plugin_apis/crypto.api
+++ b/src/lib/plugin_apis/crypto.api
@@ -38,6 +38,7 @@ typedef enum {
     BD_CRYPTO_TECH_BITLK,
     BD_CRYPTO_TECH_KEYRING,
     BD_CRYPTO_TECH_FVAULT2,
+    BD_CRYPTO_TECH_SED_OPAL,
 } BDCryptoTech;
 
 typedef enum {
@@ -1308,4 +1309,17 @@ gboolean bd_crypto_fvault2_open (const gchar *device, const gchar *name, BDCrypt
  */
 gboolean bd_crypto_fvault2_close (const gchar *fvault2_device, GError **error);
 
+/**
+ * bd_crypto_opal_is_supported:
+ * @device: device to check for OPAL support
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: %TRUE if the given @device supports OPAL or %FALSE if not or
+ * failed to determine (the @error is populated with the error in such
+ * cases).
+ *
+ * Tech category: %BD_CRYPTO_TECH_SED_OPAL-%BD_CRYPTO_TECH_MODE_QUERY
+ */
+gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error);
+
 #endif  /* BD_CRYPTO_API */
diff --git a/src/plugins/crypto.c b/src/plugins/crypto.c
index ae84d53c..d84136ac 100644
--- a/src/plugins/crypto.c
+++ b/src/plugins/crypto.c
@@ -36,6 +36,8 @@
 #include <volume_key/libvolume_key.h>
 #endif
 
+#include <linux/sed-opal.h>
+
 #include "crypto.h"
 
 #ifdef __clang__
@@ -3411,3 +3413,44 @@ gboolean bd_crypto_fvault2_close (const gchar *fvault2_device, GError **error) {
     return _crypto_close (fvault2_device, "FVAULT2", error);
 }
 #endif
+
+/**
+ * bd_crypto_opal_is_supported:
+ * @device: device to check for OPAL support
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: %TRUE if the given @device supports OPAL or %FALSE if not or
+ * failed to determine (the @error is populated with the error in such
+ * cases).
+ *
+ * Tech category: %BD_CRYPTO_TECH_SED_OPAL-%BD_CRYPTO_TECH_MODE_QUERY
+ */
+gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error) {
+    gint fd = -1;
+    gint ret = 0;
+    struct opal_status st = ZERO_INIT;
+
+    fd = open (device, O_RDONLY|O_CLOEXEC);
+    if (fd == -1) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Failed to open the device '%s'", device);
+        return FALSE;
+    }
+
+    ret = ioctl (fd, IOC_OPAL_GET_STATUS, &st);
+    if (ret < 0) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Failed to get opal status for the device '%s': %s",
+                     device,
+                     strerror_l (-ret, c_locale));
+        close (fd);
+        return FALSE;
+    }
+
+    close (fd);
+
+    if (st.flags & (OPAL_FL_SUPPORTED|OPAL_FL_LOCKING_SUPPORTED|OPAL_FL_LOCKING_ENABLED))
+        return TRUE;
+    else
+        return FALSE;
+}
diff --git a/src/plugins/crypto.h b/src/plugins/crypto.h
index 468536a3..fc9c868b 100644
--- a/src/plugins/crypto.h
+++ b/src/plugins/crypto.h
@@ -41,6 +41,7 @@ typedef enum {
     BD_CRYPTO_TECH_BITLK,
     BD_CRYPTO_TECH_KEYRING,
     BD_CRYPTO_TECH_FVAULT2,
+    BD_CRYPTO_TECH_SED_OPAL,
 } BDCryptoTech;
 
 typedef enum {
@@ -144,6 +145,22 @@ typedef enum {
     BD_CRYPTO_INTEGRITY_OPEN_ALLOW_DISCARDS     = 1 << 5,
 } BDCryptoIntegrityOpenFlags;
 
+/**
+ * BDCryptoLUKSSEDOPALType:
+ * @BD_CRYPTO_LUKS_SED_OPAL_UNKNOWN: used for unknown/unsupported hardware encryption or when
+ *                                   error was raised when getting the information
+ * @BD_CRYPTO_LUKS_SED_OPAL_SW_ONLY: OPAL hardware encryption is not configured on this device
+ * @BD_CRYPTO_LUKS_SED_OPAL_HW_ONLY: only OPAL hardware encryption is configured on this device
+ * @BD_CRYPTO_LUKS_SED_OPAL_HW_AND_SW: both OPAL hardware encryption and software encryption
+ *                                     (using LUKS/dm-crypt) is configured on this device
+ */
+typedef enum {
+    BD_CRYPTO_LUKS_SED_OPAL_UNKNOWN = 0,
+    BD_CRYPTO_LUKS_SED_OPAL_SW_ONLY,
+    BD_CRYPTO_LUKS_SED_OPAL_HW_ONLY,
+    BD_CRYPTO_LUKS_SED_OPAL_HW_AND_SW,
+} BDCryptoLUKSSEDOPALType;
+
 /**
  * BDCryptoLUKSInfo:
  * @version: LUKS version
@@ -156,6 +173,7 @@ typedef enum {
  * @metadata_size: LUKS metadata size
  * @label: label of the LUKS device (valid only for LUKS 2)
  * @subsystem: subsystem of the LUKS device (valid only for LUKS 2)
+ * @hw_encryption: hardware encryption type
  */
 typedef struct BDCryptoLUKSInfo {
     BDCryptoLUKSVersion version;
@@ -167,6 +185,7 @@ typedef struct BDCryptoLUKSInfo {
     guint64 metadata_size;
     gchar *label;
     gchar *subsystem;
+    BDCryptoLUKSSEDOPALType hw_encryption;
 } BDCryptoLUKSInfo;
 
 void bd_crypto_luks_info_free (BDCryptoLUKSInfo *info);
@@ -296,4 +315,6 @@ gboolean bd_crypto_fvault2_close (const gchar *fvault2_device, GError **error);
 
 gboolean bd_crypto_escrow_device (const gchar *device, const gchar *passphrase, const gchar *cert_data, const gchar *directory, const gchar *backup_passphrase, GError **error);
 
+gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error);
+
 #endif  /* BD_CRYPTO */
-- 
2.45.2


From ebeeeb4c082faf68d9bf862bdbbbd682c7c520db Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 8 Aug 2024 09:35:19 +0200
Subject: [PATCH 2/6] crypto: Add a function to wipe a LUKS HW-OPAL device

In order to also destroy the OPAL locking range, we need a special
function and the OPAL admin passphrase.
---
 configure.ac                   |  2 +
 docs/libblockdev-sections.txt  |  1 +
 src/lib/plugin_apis/crypto.api | 14 ++++++
 src/plugins/crypto.c           | 88 ++++++++++++++++++++++++++++++++++
 src/plugins/crypto.h           |  1 +
 5 files changed, 106 insertions(+)

diff --git a/configure.ac b/configure.ac
index 5f710a95..7c3bc8ba 100644
--- a/configure.ac
+++ b/configure.ac
@@ -180,6 +180,8 @@ AS_IF([test "x$with_crypto" != "xno"],
             [AC_DEFINE([LIBCRYPTSETUP_24])], [])
       AS_IF([$PKG_CONFIG --atleast-version=2.6.0 libcryptsetup],
             [AC_DEFINE([LIBCRYPTSETUP_26])], [])
+      AS_IF([$PKG_CONFIG --atleast-version=2.7.0 libcryptsetup],
+            [AC_DEFINE([LIBCRYPTSETUP_27])], [])
       AS_IF([test "x$with_escrow" != "xno"],
             [LIBBLOCKDEV_PKG_CHECK_MODULES([NSS], [nss >= 3.18.0])
              LIBBLOCKDEV_CHECK_HEADER([volume_key/libvolume_key.h], [$GLIB_CFLAGS $NSS_CFLAGS], [libvolume_key.h not available])],
diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt
index 68b456b4..b189cb09 100644
--- a/docs/libblockdev-sections.txt
+++ b/docs/libblockdev-sections.txt
@@ -126,6 +126,7 @@ bd_crypto_bitlk_close
 bd_crypto_fvault2_open
 bd_crypto_fvault2_close
 bd_crypto_opal_is_supported
+bd_crypto_opal_wipe
 BDCryptoTech
 BDCryptoTechMode
 bd_crypto_is_tech_avail
diff --git a/src/lib/plugin_apis/crypto.api b/src/lib/plugin_apis/crypto.api
index 514d84bd..e5cb67f0 100644
--- a/src/lib/plugin_apis/crypto.api
+++ b/src/lib/plugin_apis/crypto.api
@@ -1322,4 +1322,18 @@ gboolean bd_crypto_fvault2_close (const gchar *fvault2_device, GError **error);
  */
 gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error);
 
+/**
+ * bd_crypto_opal_wipe_device:
+ * @device: LUKS HW-OPAL device to wipe
+ * @context: OPAL admin passphrase context
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether @device was successfully wiped or not.
+ *
+ * Supported @context types for this function: passphrase
+ *
+ * Tech category: %BD_CRYPTO_TECH_SED_OPAL-%BD_CRYPTO_TECH_MODE_MODIFY
+ */
+gboolean bd_crypto_opal_wipe_device (const gchar *device, BDCryptoKeyslotContext *context, GError **error);
+
 #endif  /* BD_CRYPTO_API */
diff --git a/src/plugins/crypto.c b/src/plugins/crypto.c
index d84136ac..404e3c69 100644
--- a/src/plugins/crypto.c
+++ b/src/plugins/crypto.c
@@ -3454,3 +3454,91 @@ gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error) {
     else
         return FALSE;
 }
+
+/**
+ * bd_crypto_opal_wipe_device:
+ * @device: LUKS HW-OPAL device to wipe
+ * @context: OPAL admin passphrase context
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Returns: whether @device was successfully wiped or not.
+ *
+ * Supported @context types for this function: passphrase
+ *
+ * Tech category: %BD_CRYPTO_TECH_SED_OPAL-%BD_CRYPTO_TECH_MODE_MODIFY
+ */
+#ifndef LIBCRYPTSETUP_27
+gboolean bd_crypto_opal_wipe_device (const gchar *device G_GNUC_UNUSED, BDCryptoKeyslotContext *context G_GNUC_UNUSED, GError **error) {
+    /* this will return FALSE and set error, because OPAL technology is not available */
+    return bd_crypto_is_tech_avail (BD_CRYPTO_TECH_SED_OPAL, BD_CRYPTO_TECH_MODE_QUERY, error);
+}
+#else
+gboolean bd_crypto_opal_wipe_device (const gchar *device, BDCryptoKeyslotContext *context, GError **error) {
+    gchar *key_buf = NULL;
+    gsize buf_len = 0;
+    struct crypt_device *cd = NULL;
+    gint ret = 0;
+    guint64 progress_id = 0;
+    GError *l_error = NULL;
+    gchar *msg = NULL;
+
+    msg = g_strdup_printf ("Started wiping '%s' LUKS HW-OPAL device", device);
+    progress_id = bd_utils_report_started (msg);
+    g_free (msg);
+
+    ret = crypt_init (&cd, device);
+    if (ret != 0) {
+        g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Failed to initialize device: %s", strerror_l (-ret, c_locale));
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    ret = crypt_load (cd, CRYPT_LUKS, NULL);
+    if (ret != 0) {
+        g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Failed to load device's parameters: %s", strerror_l (-ret, c_locale));
+        crypt_free (cd);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    ret = crypt_get_hw_encryption_type (cd);
+    if (ret != CRYPT_OPAL_HW_ONLY && ret != CRYPT_SW_AND_OPAL_HW) {
+        g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Device %s isn't a LUKS HW-OPAL device.", device);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        crypt_free (cd);
+        return FALSE;
+    }
+
+    if (context->type == BD_CRYPTO_KEYSLOT_CONTEXT_TYPE_PASSPHRASE) {
+        key_buf = (char *) context->u.passphrase.pass_data;
+        buf_len = context->u.passphrase.data_len;
+    } else {
+        g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_CONTEXT,
+                     "Only 'passphrase' context type is valid for OPAL wipe.");
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        crypt_free (cd);
+        return FALSE;
+    }
+
+    ret = crypt_wipe_hw_opal (cd, CRYPT_LUKS2_SEGMENT, key_buf, buf_len, 0);
+    if (ret != 0) {
+        g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_DEVICE,
+                     "Failed to wipe LUKS HW-OPAL device: %s", strerror_l (-ret, c_locale));
+        crypt_free (cd);
+        bd_utils_report_finished (progress_id, l_error->message);
+        g_propagate_error (error, l_error);
+        return FALSE;
+    }
+
+    crypt_free (cd);
+    bd_utils_report_finished (progress_id, "Completed");
+    return TRUE;
+}
+#endif
diff --git a/src/plugins/crypto.h b/src/plugins/crypto.h
index fc9c868b..eb690126 100644
--- a/src/plugins/crypto.h
+++ b/src/plugins/crypto.h
@@ -316,5 +316,6 @@ gboolean bd_crypto_fvault2_close (const gchar *fvault2_device, GError **error);
 gboolean bd_crypto_escrow_device (const gchar *device, const gchar *passphrase, const gchar *cert_data, const gchar *directory, const gchar *backup_passphrase, GError **error);
 
 gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error);
+gboolean bd_crypto_opal_wipe (const gchar *device, BDCryptoKeyslotContext *context, GError **error);
 
 #endif  /* BD_CRYPTO */
-- 
2.45.2


From 88105610f71397420cfd1f1a9da673422c514cf2 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 8 Aug 2024 09:43:30 +0200
Subject: [PATCH 3/6] crypto: Add information about HW encryption to
 BDCryptoLUKSInfo

---
 src/lib/plugin_apis/crypto.api | 19 +++++++++++++++++++
 src/plugins/crypto.c           | 27 +++++++++++++++++++++++++++
 src/plugins/crypto.h           | 26 +++++++++++++-------------
 3 files changed, 59 insertions(+), 13 deletions(-)

diff --git a/src/lib/plugin_apis/crypto.api b/src/lib/plugin_apis/crypto.api
index e5cb67f0..d179c977 100644
--- a/src/lib/plugin_apis/crypto.api
+++ b/src/lib/plugin_apis/crypto.api
@@ -364,6 +364,22 @@ typedef enum {
 #define BD_CRYPTO_TYPE_LUKS_INFO (bd_crypto_luks_info_get_type ())
 GType bd_crypto_luks_info_get_type();
 
+/**
+ * BDCryptoLUKSHWEncryptionType:
+ * @BD_CRYPTO_LUKS_HW_ENCRYPTION_UNKNOWN: used for unknown/unsupported hardware encryption or when
+ *                                        error was detected when getting the information
+ * @BD_CRYPTO_LUKS_HW_ENCRYPTION_SW_ONLY: hardware encryption is not configured on this device
+ * @BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_ONLY: only OPAL hardware encryption is configured on this device
+ * @BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW: both OPAL hardware encryption and software encryption
+ *                                               (using LUKS/dm-crypt) is configured on this device
+ */
+typedef enum {
+    BD_CRYPTO_LUKS_HW_ENCRYPTION_UNKNOWN = 0,
+    BD_CRYPTO_LUKS_HW_ENCRYPTION_SW_ONLY,
+    BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_ONLY,
+    BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW,
+} BDCryptoLUKSHWEncryptionType;
+
 /**
  * BDCryptoLUKSInfo:
  * @version: LUKS version
@@ -376,6 +392,7 @@ GType bd_crypto_luks_info_get_type();
  * @metadata_size: LUKS metadata size
  * @label: label of the LUKS device (valid only for LUKS 2)
  * @subsystem: subsystem of the LUKS device (valid only for LUKS 2)
+ * @hw_encryption: hardware encryption type
  */
 typedef struct BDCryptoLUKSInfo {
     BDCryptoLUKSVersion version;
@@ -387,6 +404,7 @@ typedef struct BDCryptoLUKSInfo {
     guint64 metadata_size;
     gchar *label;
     gchar *subsystem;
+    BDCryptoLUKSHWEncryptionType hw_encryption;
 } BDCryptoLUKSInfo;
 
 /**
@@ -429,6 +447,7 @@ BDCryptoLUKSInfo* bd_crypto_luks_info_copy (BDCryptoLUKSInfo *info) {
     new_info->metadata_size = info->metadata_size;
     new_info->label = g_strdup (info->label);
     new_info->subsystem = g_strdup (info->subsystem);
+    new_info->hw_encryption = info->hw_encryption;
 
     return new_info;
 }
diff --git a/src/plugins/crypto.c b/src/plugins/crypto.c
index 404e3c69..f61c7b4a 100644
--- a/src/plugins/crypto.c
+++ b/src/plugins/crypto.c
@@ -223,6 +223,7 @@ BDCryptoLUKSInfo* bd_crypto_luks_info_copy (BDCryptoLUKSInfo *info) {
     new_info->metadata_size = info->metadata_size;
     new_info->label = g_strdup (info->label);
     new_info->subsystem = g_strdup (info->subsystem);
+    new_info->hw_encryption = info->hw_encryption;
 
     return new_info;
 }
@@ -2274,6 +2275,32 @@ BDCryptoLUKSInfo* bd_crypto_luks_info (const gchar *device, GError **error) {
         info->subsystem = g_strdup ("");
     }
 
+#ifdef LIBCRYPTSETUP_27
+    ret = crypt_get_hw_encryption_type (cd);
+    if (ret < 0) {
+        info->hw_encryption = BD_CRYPTO_LUKS_HW_ENCRYPTION_UNKNOWN;
+        bd_utils_log_format (BD_UTILS_LOG_WARNING, "Failed to get HW encryption type: %s", strerror_l (-ret, c_locale));
+    } else {
+        switch (ret) {
+            case CRYPT_SW_ONLY:
+                info->hw_encryption = BD_CRYPTO_LUKS_HW_ENCRYPTION_SW_ONLY;
+                break;
+            case CRYPT_SW_AND_OPAL_HW:
+                info->hw_encryption = BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW;
+                break;
+            case CRYPT_OPAL_HW_ONLY:
+                info->hw_encryption = BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_ONLY;
+                break;
+            default:
+                bd_utils_log_format (BD_UTILS_LOG_WARNING, "Unknown HW encryption type: %d", ret);
+                info->hw_encryption = BD_CRYPTO_LUKS_HW_ENCRYPTION_UNKNOWN;
+                break;
+        }
+    }
+#else
+    info->hw_encryption = BD_CRYPTO_LUKS_HW_ENCRYPTION_UNKNOWN;
+#endif
+
     crypt_free (cd);
 
     return info;
diff --git a/src/plugins/crypto.h b/src/plugins/crypto.h
index eb690126..7b3e8884 100644
--- a/src/plugins/crypto.h
+++ b/src/plugins/crypto.h
@@ -146,20 +146,20 @@ typedef enum {
 } BDCryptoIntegrityOpenFlags;
 
 /**
- * BDCryptoLUKSSEDOPALType:
- * @BD_CRYPTO_LUKS_SED_OPAL_UNKNOWN: used for unknown/unsupported hardware encryption or when
- *                                   error was raised when getting the information
- * @BD_CRYPTO_LUKS_SED_OPAL_SW_ONLY: OPAL hardware encryption is not configured on this device
- * @BD_CRYPTO_LUKS_SED_OPAL_HW_ONLY: only OPAL hardware encryption is configured on this device
- * @BD_CRYPTO_LUKS_SED_OPAL_HW_AND_SW: both OPAL hardware encryption and software encryption
- *                                     (using LUKS/dm-crypt) is configured on this device
+ * BDCryptoLUKSHWEncryptionType:
+ * @BD_CRYPTO_LUKS_HW_ENCRYPTION_UNKNOWN: used for unknown/unsupported hardware encryption or when
+ *                                        error was detected when getting the information
+ * @BD_CRYPTO_LUKS_HW_ENCRYPTION_SW_ONLY: hardware encryption is not configured on this device
+ * @BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_ONLY: only OPAL hardware encryption is configured on this device
+ * @BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW: both OPAL hardware encryption and software encryption
+ *                                               (using LUKS/dm-crypt) is configured on this device
  */
 typedef enum {
-    BD_CRYPTO_LUKS_SED_OPAL_UNKNOWN = 0,
-    BD_CRYPTO_LUKS_SED_OPAL_SW_ONLY,
-    BD_CRYPTO_LUKS_SED_OPAL_HW_ONLY,
-    BD_CRYPTO_LUKS_SED_OPAL_HW_AND_SW,
-} BDCryptoLUKSSEDOPALType;
+    BD_CRYPTO_LUKS_HW_ENCRYPTION_UNKNOWN = 0,
+    BD_CRYPTO_LUKS_HW_ENCRYPTION_SW_ONLY,
+    BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_ONLY,
+    BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW,
+} BDCryptoLUKSHWEncryptionType;
 
 /**
  * BDCryptoLUKSInfo:
@@ -185,7 +185,7 @@ typedef struct BDCryptoLUKSInfo {
     guint64 metadata_size;
     gchar *label;
     gchar *subsystem;
-    BDCryptoLUKSSEDOPALType hw_encryption;
+    BDCryptoLUKSHWEncryptionType hw_encryption;
 } BDCryptoLUKSInfo;
 
 void bd_crypto_luks_info_free (BDCryptoLUKSInfo *info);
-- 
2.45.2


From 287dcb60d49ce78f219307ba3e13e2e44ba5e529 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 8 Aug 2024 09:53:01 +0200
Subject: [PATCH 4/6] crypto: Add support for creating new LUKS HW-OPAL devices

Most of the code is shared with bd_crypto_luks_format but I wanted
a separate function for the "OPAL technology" (and we cannot add
an extra parameter for the existing format function anyway).
---
 docs/libblockdev-sections.txt       |   1 +
 src/lib/plugin_apis/crypto.api      |  27 +++
 src/plugins/crypto.c                | 248 ++++++++++++++++++++++------
 src/plugins/crypto.h                |   3 +-
 src/python/gi/overrides/BlockDev.py |   7 +
 5 files changed, 239 insertions(+), 47 deletions(-)

diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt
index b189cb09..07f4a833 100644
--- a/docs/libblockdev-sections.txt
+++ b/docs/libblockdev-sections.txt
@@ -127,6 +127,7 @@ bd_crypto_fvault2_open
 bd_crypto_fvault2_close
 bd_crypto_opal_is_supported
 bd_crypto_opal_wipe
+bd_crypto_opal_format
 BDCryptoTech
 BDCryptoTechMode
 bd_crypto_is_tech_avail
diff --git a/src/lib/plugin_apis/crypto.api b/src/lib/plugin_apis/crypto.api
index d179c977..c73438fb 100644
--- a/src/lib/plugin_apis/crypto.api
+++ b/src/lib/plugin_apis/crypto.api
@@ -1355,4 +1355,31 @@ gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error);
  */
 gboolean bd_crypto_opal_wipe_device (const gchar *device, BDCryptoKeyslotContext *context, GError **error);
 
+/**
+ * bd_crypto_opal_format:
+ * @device: a device to format as LUKS HW-OPAL
+ * @cipher: (nullable): cipher specification (type-mode, e.g. "aes-xts-plain64") or %NULL to use the default
+ * @key_size: size of the volume key in bits or 0 to use the default
+ * @context: key slot context (passphrase/keyfile/token...) for this LUKS device
+ * @min_entropy: minimum random data entropy (in bits) required to format @device as LUKS
+ * @hw_encryption: type of hardware encryption (SW+HW or HW only)
+ * @opal_context: OPAL admin passphrase
+ * @extra: (nullable): extra arguments for LUKS format creation
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Formats the given @device as LUKS HW-OPAL according to the other parameters given. If
+ * @min_entropy is specified (greater than 0), the function waits for enough
+ * entropy to be available in the random data pool (WHICH MAY POTENTIALLY TAKE
+ * FOREVER).
+ *
+ * Supported @context types for this function: passphrase, key file
+ * Supported @opal_context types for this function: passphrase
+ *
+ * Returns: whether the given @device was successfully formatted as LUKS HW-OPAL or not
+ * (the @error contains the error in such cases)
+ *
+ * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_CREATE
+ */
+gboolean bd_crypto_opal_format (const gchar *device, const gchar *cipher, guint64 key_size, BDCryptoKeyslotContext *context, guint64 min_entropy, BDCryptoLUKSHWEncryptionType hw_encryption, BDCryptoKeyslotContext *opal_context, BDCryptoLUKSExtra *extra, GError **error);
+
 #endif  /* BD_CRYPTO_API */
diff --git a/src/plugins/crypto.c b/src/plugins/crypto.c
index f61c7b4a..39786ae5 100644
--- a/src/plugins/crypto.c
+++ b/src/plugins/crypto.c
@@ -51,6 +51,8 @@
 #define DEFAULT_LUKS_KEYSIZE_BITS 256
 #define DEFAULT_LUKS_CIPHER "aes-xts-plain64"
 
+#define DEFAULT_OPAL_KEYSIZE_BITS 256
+
 #define SQUARE_LOWER_LIMIT 136
 #define SQUARE_UPPER_LIMIT 426
 #define SQUARE_BYTES_TO_CHECK 512
@@ -441,6 +443,19 @@ gboolean bd_crypto_is_tech_avail (BDCryptoTech tech, guint64 mode, GError **erro
                 return FALSE;
             } else
                 return TRUE;
+        case BD_CRYPTO_TECH_SED_OPAL:
+#ifndef LIBCRYPTSETUP_27
+            g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL,
+                         "OPAL technology requires libcryptsetup >= 2.7.0");
+            return FALSE;
+#endif
+            ret = mode & (BD_CRYPTO_TECH_MODE_CREATE|BD_CRYPTO_TECH_MODE_QUERY|BD_CRYPTO_TECH_MODE_MODIFY);
+            if (ret != mode) {
+                g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL,
+                             "Only 'create', 'query' and 'modify' supported for OPAL");
+                return FALSE;
+            } else
+                return TRUE;
         default:
             g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL, "Unknown technology");
             return FALSE;
@@ -906,30 +921,22 @@ BDCryptoKeyslotContext* bd_crypto_keyslot_context_new_volume_key (const guint8 *
     return context;
 }
 
-/**
- * bd_crypto_luks_format:
- * @device: a device to format as LUKS
- * @cipher: (nullable): cipher specification (type-mode, e.g. "aes-xts-plain64") or %NULL to use the default
- * @key_size: size of the volume key in bits or 0 to use the default
- * @context: key slot context (passphrase/keyfile/token...) for this LUKS device
- * @min_entropy: minimum random data entropy (in bits) required to format @device as LUKS
- * @luks_version: whether to use LUKS v1 or LUKS v2
- * @extra: (nullable): extra arguments for LUKS format creation
- * @error: (out) (optional): place to store error (if any)
- *
- * Formats the given @device as LUKS according to the other parameters given. If
- * @min_entropy is specified (greater than 0), the function waits for enough
- * entropy to be available in the random data pool (WHICH MAY POTENTIALLY TAKE
- * FOREVER).
- *
- * Supported @context types for this function: passphrase, key file
- *
- * Returns: whether the given @device was successfully formatted as LUKS or not
- * (the @error) contains the error in such cases)
- *
- * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_CREATE
- */
-gboolean bd_crypto_luks_format (const gchar *device, const gchar *cipher, guint64 key_size, BDCryptoKeyslotContext *context, guint64 min_entropy, BDCryptoLUKSVersion luks_version, BDCryptoLUKSExtra *extra,GError **error) {
+
+
+gboolean _crypto_luks_format (const gchar *device,
+                              const gchar *cipher,
+                              guint64 key_size,
+                              BDCryptoKeyslotContext *context,
+                              guint64 min_entropy,
+                              BDCryptoLUKSVersion luks_version,
+                              BDCryptoLUKSExtra *extra,
+                              BDCryptoLUKSHWEncryptionType hw_encryption,
+#ifdef LIBCRYPTSETUP_27
+                              BDCryptoKeyslotContext *opal_context,
+#else
+                              BDCryptoKeyslotContext *opal_context G_GNUC_UNUSED,
+#endif
+                               GError **error) {
     struct crypt_device *cd = NULL;
     gint ret;
     gchar **cipher_specs = NULL;
@@ -942,6 +949,14 @@ gboolean bd_crypto_luks_format (const gchar *device, const gchar *cipher, guint6
     const gchar* crypt_version = NULL;
     GError *l_error = NULL;
 
+#ifdef LIBCRYPTSETUP_27
+    struct crypt_params_hw_opal opal_params = {
+		.user_key_size = DEFAULT_OPAL_KEYSIZE_BITS / 8
+	};
+
+    gboolean is_opal = (hw_encryption == BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_ONLY || hw_encryption == BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW);
+#endif
+
     msg = g_strdup_printf ("Started formatting '%s' as LUKS device", device);
     progress_id = bd_utils_report_started (msg);
     g_free (msg);
@@ -967,24 +982,32 @@ gboolean bd_crypto_luks_format (const gchar *device, const gchar *cipher, guint6
         return FALSE;
     }
 
-    cipher = cipher ? cipher : DEFAULT_LUKS_CIPHER;
-    cipher_specs = g_strsplit (cipher, "-", 2);
-    if (g_strv_length (cipher_specs) != 2) {
-        g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_SPEC,
-                     "Invalid cipher specification: '%s'", cipher);
-        crypt_free (cd);
-        g_strfreev (cipher_specs);
-        bd_utils_report_finished (progress_id, l_error->message);
-        g_propagate_error (error, l_error);
-        return FALSE;
-    }
+    if (hw_encryption == BD_CRYPTO_LUKS_HW_ENCRYPTION_SW_ONLY || hw_encryption == BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW) {
+        cipher = cipher ? cipher : DEFAULT_LUKS_CIPHER;
+        cipher_specs = g_strsplit (cipher, "-", 2);
+        if (g_strv_length (cipher_specs) != 2) {
+            g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_SPEC,
+                        "Invalid cipher specification: '%s'", cipher);
+            crypt_free (cd);
+            g_strfreev (cipher_specs);
+            bd_utils_report_finished (progress_id, l_error->message);
+            g_propagate_error (error, l_error);
+            return FALSE;
+        }
 
-    if (key_size == 0) {
-        if (g_str_has_prefix (cipher_specs[1], "xts-"))
-            key_size = DEFAULT_LUKS_KEYSIZE_BITS * 2;
-        else
-            key_size = DEFAULT_LUKS_KEYSIZE_BITS;
-    }
+        if (key_size == 0) {
+            if (g_str_has_prefix (cipher_specs[1], "xts-"))
+                key_size = DEFAULT_LUKS_KEYSIZE_BITS * 2;
+            else
+                key_size = DEFAULT_LUKS_KEYSIZE_BITS;
+        }
+    } else
+        cipher_specs = g_new0 (gchar*, 2);
+
+#ifdef LIBCRYPTSETUP_27
+    if (is_opal)
+        key_size += DEFAULT_OPAL_KEYSIZE_BITS;
+#endif
 
     /* key_size should be in bytes */
     key_size = key_size / 8;
@@ -1011,6 +1034,35 @@ gboolean bd_crypto_luks_format (const gchar *device, const gchar *cipher, guint6
         }
     }
 
+#ifdef LIBCRYPTSETUP_27
+    if (is_opal) {
+        if (opal_context->type == BD_CRYPTO_KEYSLOT_CONTEXT_TYPE_PASSPHRASE) {
+            opal_params.admin_key = (char *) opal_context->u.passphrase.pass_data;
+            opal_params.admin_key_size = opal_context->u.passphrase.data_len;
+        } else {
+            g_set_error (&l_error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_INVALID_CONTEXT,
+                         "Only 'passphrase' context type is valid for OPAL format.");
+            bd_utils_report_finished (progress_id, l_error->message);
+            g_propagate_error (error, l_error);
+            crypt_free (cd);
+            g_strfreev (cipher_specs);
+            return FALSE;
+        }
+    }
+
+    if (is_opal) {
+        /* XXX: workaround for a bug in cryptsetup where crypt_format_luks2_opal doesn't
+         *      initialize crypto backend leading to an abort during the call, setting
+         *      the pbkdf parameters will run the initialization so just get the default
+         *      params and set them
+         *      See also: https://gitlab.com/cryptsetup/cryptsetup/-/commit/42f4a68705384eeecb5b31b71cc8a8fe19bca916
+         */
+        const struct crypt_pbkdf_type *pbkdf_default;
+        pbkdf_default = crypt_get_pbkdf_default (CRYPT_LUKS2);
+        crypt_set_pbkdf_type (cd, pbkdf_default);
+    }
+#endif
+
     if (extra) {
         if (luks_version == BD_CRYPTO_LUKS_VERSION_LUKS1) {
 
@@ -1052,13 +1104,29 @@ gboolean bd_crypto_luks_format (const gchar *device, const gchar *cipher, guint6
             params.sector_size = extra->sector_size ? extra->sector_size : DEFAULT_LUKS2_SECTOR_SIZE;
             params.label = extra->label;
             params.subsystem = extra->subsystem;
-            ret = crypt_format (cd, crypt_version, cipher_specs[0], cipher_specs[1],
-                                NULL, NULL, key_size, &params);
+
+#ifdef LIBCRYPTSETUP_27
+            if (is_opal)
+                ret = crypt_format_luks2_opal (cd, cipher_specs[0], cipher_specs[1],
+                                               NULL, NULL, key_size, &params, &opal_params);
+            else
+#endif
+                ret = crypt_format (cd, crypt_version, cipher_specs[0], cipher_specs[1],
+                                    NULL, NULL, key_size, &params);
             g_free (pbkdf);
         }
-    } else
-        ret = crypt_format (cd, crypt_version, cipher_specs[0], cipher_specs[1],
-                            NULL, NULL, key_size, NULL);
+    } else {
+#ifdef LIBCRYPTSETUP_27
+        if (is_opal) {
+            struct crypt_params_luks2 params = ZERO_INIT;
+            params.pbkdf = crypt_get_pbkdf_default (CRYPT_LUKS2);
+            ret = crypt_format_luks2_opal (cd, cipher_specs[0], cipher_specs[1],
+                                            NULL, NULL, key_size, &params, &opal_params);
+        } else
+#endif
+            ret = crypt_format (cd, crypt_version, cipher_specs[0], cipher_specs[1],
+                                NULL, NULL, key_size, NULL);
+    }
     g_strfreev (cipher_specs);
 
     if (ret != 0) {
@@ -1123,6 +1191,33 @@ gboolean bd_crypto_luks_format (const gchar *device, const gchar *cipher, guint6
     return TRUE;
 }
 
+/**
+ * bd_crypto_luks_format:
+ * @device: a device to format as LUKS
+ * @cipher: (nullable): cipher specification (type-mode, e.g. "aes-xts-plain64") or %NULL to use the default
+ * @key_size: size of the volume key in bits or 0 to use the default
+ * @context: key slot context (passphrase/keyfile/token...) for this LUKS device
+ * @min_entropy: minimum random data entropy (in bits) required to format @device as LUKS
+ * @luks_version: whether to use LUKS v1 or LUKS v2
+ * @extra: (nullable): extra arguments for LUKS format creation
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Formats the given @device as LUKS according to the other parameters given. If
+ * @min_entropy is specified (greater than 0), the function waits for enough
+ * entropy to be available in the random data pool (WHICH MAY POTENTIALLY TAKE
+ * FOREVER).
+ *
+ * Supported @context types for this function: passphrase, key file
+ *
+ * Returns: whether the given @device was successfully formatted as LUKS or not
+ * (the @error) contains the error in such cases)
+ *
+ * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_CREATE
+ */
+gboolean bd_crypto_luks_format (const gchar *device, const gchar *cipher, guint64 key_size, BDCryptoKeyslotContext *context, guint64 min_entropy, BDCryptoLUKSVersion luks_version, BDCryptoLUKSExtra *extra,GError **error) {
+    return _crypto_luks_format (device, cipher, key_size, context, min_entropy, luks_version, extra, BD_CRYPTO_LUKS_HW_ENCRYPTION_SW_ONLY, NULL, error);
+}
+
 /**
  * bd_crypto_luks_open:
  * @device: the device to open
@@ -3569,3 +3664,64 @@ gboolean bd_crypto_opal_wipe_device (const gchar *device, BDCryptoKeyslotContext
     return TRUE;
 }
 #endif
+
+/**
+ * bd_crypto_opal_format:
+ * @device: a device to format as LUKS HW-OPAL
+ * @cipher: (nullable): cipher specification (type-mode, e.g. "aes-xts-plain64") or %NULL to use the default
+ * @key_size: size of the volume key in bits or 0 to use the default
+ * @context: key slot context (passphrase/keyfile/token...) for this LUKS device
+ * @min_entropy: minimum random data entropy (in bits) required to format @device as LUKS
+ * @hw_encryption: type of hardware encryption (SW+HW or HW only)
+ * @opal_context: OPAL admin passphrase
+ * @extra: (nullable): extra arguments for LUKS format creation
+ * @error: (out) (optional): place to store error (if any)
+ *
+ * Formats the given @device as LUKS HW-OPAL according to the other parameters given. If
+ * @min_entropy is specified (greater than 0), the function waits for enough
+ * entropy to be available in the random data pool (WHICH MAY POTENTIALLY TAKE
+ * FOREVER).
+ *
+ * Supported @context types for this function: passphrase, key file
+ * Supported @opal_context types for this function: passphrase
+ *
+ * Returns: whether the given @device was successfully formatted as LUKS HW-OPAL or not
+ * (the @error contains the error in such cases)
+ *
+ * Tech category: %BD_CRYPTO_TECH_LUKS-%BD_CRYPTO_TECH_MODE_CREATE
+ */
+#ifndef LIBCRYPTSETUP_27
+gboolean bd_crypto_opal_format (const gchar *device G_GNUC_UNUSED, const gchar *cipher G_GNUC_UNUSED, guint64 key_size G_GNUC_UNUSED, BDCryptoKeyslotContext *context G_GNUC_UNUSED,
+                                guint64 min_entropy G_GNUC_UNUSED, BDCryptoLUKSHWEncryptionType hw_encryption G_GNUC_UNUSED,
+                                BDCryptoKeyslotContext *opal_context G_GNUC_UNUSED, BDCryptoLUKSExtra *extra G_GNUC_UNUSED, GError **error) {
+    /* this will return FALSE and set error, because OPAL technology is not available */
+    return bd_crypto_is_tech_avail (BD_CRYPTO_TECH_SED_OPAL, BD_CRYPTO_TECH_MODE_CREATE, error);
+}
+#else
+gboolean bd_crypto_opal_format (const gchar *device, const gchar *cipher, guint64 key_size, BDCryptoKeyslotContext *context, guint64 min_entropy, BDCryptoLUKSHWEncryptionType hw_encryption,
+                                BDCryptoKeyslotContext *opal_context, BDCryptoLUKSExtra *extra, GError **error) {
+
+    gboolean ret = FALSE;
+
+    if (hw_encryption != BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_AND_SW && hw_encryption != BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_ONLY) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_FORMAT_FAILED,
+                     "Either hardware and software encryption or hardware encryption only must be selected for OPAL format");
+        return FALSE;
+    }
+
+    if (hw_encryption == BD_CRYPTO_LUKS_HW_ENCRYPTION_OPAL_HW_ONLY && cipher != NULL) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_FORMAT_FAILED,
+                     "Cipher cannot be specified for hardware encryption only OPAL devices");
+        return FALSE;
+    }
+
+    ret = bd_crypto_opal_is_supported (device, NULL);
+    if (!ret) {
+        g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_FORMAT_FAILED,
+                     "OPAL doesn't seem to be supported on %s", device);
+        return FALSE;
+    }
+
+    return _crypto_luks_format (device, cipher, key_size, context, min_entropy, BD_CRYPTO_LUKS_VERSION_LUKS2, extra, hw_encryption, opal_context, error);
+}
+#endif
diff --git a/src/plugins/crypto.h b/src/plugins/crypto.h
index 7b3e8884..40faaa9d 100644
--- a/src/plugins/crypto.h
+++ b/src/plugins/crypto.h
@@ -317,5 +317,6 @@ gboolean bd_crypto_escrow_device (const gchar *device, const gchar *passphrase,
 
 gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error);
 gboolean bd_crypto_opal_wipe (const gchar *device, BDCryptoKeyslotContext *context, GError **error);
-
+gboolean bd_crypto_opal_format (const gchar *device, const gchar *cipher, guint64 key_size, BDCryptoKeyslotContext *context, guint64 min_entropy, BDCryptoLUKSHWEncryptionType hw_encryption,
+                                BDCryptoKeyslotContext *opal_context, BDCryptoLUKSExtra *extra, GError **error);
 #endif  /* BD_CRYPTO */
diff --git a/src/python/gi/overrides/BlockDev.py b/src/python/gi/overrides/BlockDev.py
index d830e485..a4d363c0 100644
--- a/src/python/gi/overrides/BlockDev.py
+++ b/src/python/gi/overrides/BlockDev.py
@@ -386,6 +386,13 @@ def crypto_integrity_open(device, name, algorithm, context=None, flags=0, extra=
 __all__.append("crypto_integrity_open")
 
 
+_crypto_opal_format = BlockDev.crypto_opal_format
+@override(BlockDev.crypto_opal_format)
+def crypto_opal_format(device, cipher=None, key_size=0, context=None, min_entropy=0, opal_context=None, hw_encryption=BlockDev.CryptoLUKSHWEncryptionType.OPAL_HW_AND_SW, extra=None):
+    return _crypto_opal_format(device, cipher, key_size, context, min_entropy, hw_encryption, opal_context, extra)
+__all__.append("crypto_opal_format")
+
+
 _dm_create_linear = BlockDev.dm_create_linear
 @override(BlockDev.dm_create_linear)
 def dm_create_linear(map_name, device, length, uuid=None):
-- 
2.45.2


From 4959472fb37b59606674d53d3a5d3f1353ad3f08 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Thu, 8 Aug 2024 10:11:43 +0200
Subject: [PATCH 5/6] tests: Add a simple test case for LUKS HW-OPAL support

We unfortunately need a disk with OPAL support to actually test
something, so only a basic sanity check can be run in the CI.
---
 tests/crypto_test.py | 75 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 75 insertions(+)

diff --git a/tests/crypto_test.py b/tests/crypto_test.py
index 6fe15a07..42407ab2 100644
--- a/tests/crypto_test.py
+++ b/tests/crypto_test.py
@@ -32,6 +32,7 @@ def check_cryptsetup_version(version):
 
 HAVE_BITLK = check_cryptsetup_version("2.3.0")
 HAVE_FVAULT2 = check_cryptsetup_version("2.6.0")
+HAVE_OPAL = check_cryptsetup_version("2.7.0")
 
 
 class CryptoTestCase(unittest.TestCase):
@@ -1661,3 +1662,77 @@ class CryptoTestIntegrity(CryptoTestCase):
         succ = BlockDev.crypto_integrity_close(self._dm_name)
         self.assertTrue(succ)
         self.assertFalse(os.path.exists("/dev/mapper/%s" % self._dm_name))
+
+
+class CryptoTestLUKSOpal(CryptoTestCase):
+
+    @unittest.skipUnless(HAVE_OPAL, "OPAL not supported")
+    @tag_test(TestTags.SLOW)
+    def test_luks_opal_sanity(self):
+        """Basic sanity check for LUKS HW-OPAL support"""
+        with self.assertRaisesRegex(GLib.GError, r"Failed to get opal status for the device"):
+            BlockDev.crypto_opal_is_supported(self.loop_dev)
+
+        with self.assertRaisesRegex(GLib.GError, r"OPAL doesn't seem to be supported on"):
+            BlockDev.crypto_opal_format(self.loop_dev, context=BlockDev.CryptoKeyslotContext(passphrase="aaaaa"),
+                                        opal_context=BlockDev.CryptoKeyslotContext(passphrase="aaaaa"))
+
+        # "normal" LUKS device
+        self._luks_format(self.loop_dev, PASSWD, None)
+
+        with self.assertRaisesRegex(GLib.GError, r"isn't a LUKS HW-OPAL device"):
+            BlockDev.crypto_opal_wipe_device(self.loop_dev,
+                                             BlockDev.CryptoKeyslotContext(passphrase="aaaaa"))
+
+        info = BlockDev.crypto_luks_info(self.loop_dev)
+        self.assertEqual(info.hw_encryption, BlockDev.CryptoLUKSHWEncryptionType.SW_ONLY)
+
+    @unittest.skip("requires special hardware")
+    @tag_test(TestTags.SLOW)
+    def test_luks_opal_full(self):
+        """ Full LUKS HW-OPAL support test"""
+        # requires a disk that supports OPAL so this test case will be always skipped and
+        # exists only for manual testing purposes outside CI
+        DISK = ""
+        OPAL_PASSWD = "anaconda"
+        ctx = BlockDev.CryptoKeyslotContext(passphrase=PASSWD)            # LUKS passphrase
+        opal_ctx = BlockDev.CryptoKeyslotContext(passphrase=OPAL_PASSWD)  # OPAL admin passphrase
+
+        ret = BlockDev.crypto_opal_is_supported(DISK)
+        self.assertTrue(ret)
+
+        # OPAL only
+        ret = BlockDev.crypto_opal_format(DISK, context=ctx, opal_context=opal_ctx,
+                                          hw_encryption=BlockDev.CryptoLUKSHWEncryptionType.OPAL_HW_ONLY)
+        self.assertTrue(ret)
+
+        info = BlockDev.crypto_luks_info(DISK)
+        self.assertEqual(info.hw_encryption, BlockDev.CryptoLUKSHWEncryptionType.OPAL_HW_ONLY)
+        self.assertEqual(info.subsystem, "HW-OPAL")
+
+        succ = BlockDev.crypto_luks_open(DISK, "libblockdevTestLUKS", ctx, False)
+        self.assertTrue(succ)
+
+        succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
+        self.assertTrue(succ)
+
+        ret = BlockDev.crypto_opal_wipe_device(DISK, opal_ctx)
+        self.assertTrue(ret)
+
+        # OPAL + dm-crypt
+        ret = BlockDev.crypto_opal_format(DISK, context=ctx, opal_context=opal_ctx,
+                                          hw_encryption=BlockDev.CryptoLUKSHWEncryptionType.OPAL_HW_AND_SW)
+        self.assertTrue(ret)
+
+        info = BlockDev.crypto_luks_info(DISK)
+        self.assertEqual(info.hw_encryption, BlockDev.CryptoLUKSHWEncryptionType.OPAL_HW_AND_SW)
+        self.assertEqual(info.subsystem, "HW-OPAL")
+
+        succ = BlockDev.crypto_luks_open(DISK, "libblockdevTestLUKS", ctx, False)
+        self.assertTrue(succ)
+
+        succ = BlockDev.crypto_luks_close("libblockdevTestLUKS")
+        self.assertTrue(succ)
+
+        ret = BlockDev.crypto_opal_wipe_device(DISK, opal_ctx)
+        self.assertTrue(ret)
-- 
2.45.2


From 880527527e87c46828d4d35b23bffbac3b3fd7e2 Mon Sep 17 00:00:00 2001
From: Vojtech Trefny <vtrefny@redhat.com>
Date: Sat, 10 Aug 2024 14:36:00 +0200
Subject: [PATCH 6/6] crypto: Check for kernel SED OPAL support for OPAL
 operations

---
 configure.ac         |  2 ++
 src/plugins/crypto.c | 13 +++++++++++--
 2 files changed, 13 insertions(+), 2 deletions(-)

diff --git a/configure.ac b/configure.ac
index 7c3bc8ba..e5ab1363 100644
--- a/configure.ac
+++ b/configure.ac
@@ -182,6 +182,8 @@ AS_IF([test "x$with_crypto" != "xno"],
             [AC_DEFINE([LIBCRYPTSETUP_26])], [])
       AS_IF([$PKG_CONFIG --atleast-version=2.7.0 libcryptsetup],
             [AC_DEFINE([LIBCRYPTSETUP_27])], [])
+      AC_CHECK_HEADER([linux/sed-opal.h],
+                      [AC_DEFINE([HAVE_LINUX_OPAL])], [])
       AS_IF([test "x$with_escrow" != "xno"],
             [LIBBLOCKDEV_PKG_CHECK_MODULES([NSS], [nss >= 3.18.0])
              LIBBLOCKDEV_CHECK_HEADER([volume_key/libvolume_key.h], [$GLIB_CFLAGS $NSS_CFLAGS], [libvolume_key.h not available])],
diff --git a/src/plugins/crypto.c b/src/plugins/crypto.c
index 39786ae5..d8dc6d8f 100644
--- a/src/plugins/crypto.c
+++ b/src/plugins/crypto.c
@@ -36,7 +36,9 @@
 #include <volume_key/libvolume_key.h>
 #endif
 
+#ifdef HAVE_LINUX_OPAL
 #include <linux/sed-opal.h>
+#endif
 
 #include "crypto.h"
 
@@ -444,9 +446,9 @@ gboolean bd_crypto_is_tech_avail (BDCryptoTech tech, guint64 mode, GError **erro
             } else
                 return TRUE;
         case BD_CRYPTO_TECH_SED_OPAL:
-#ifndef LIBCRYPTSETUP_27
+#if !defined(LIBCRYPTSETUP_27) || !defined(HAVE_LINUX_OPAL)
             g_set_error (error, BD_CRYPTO_ERROR, BD_CRYPTO_ERROR_TECH_UNAVAIL,
-                         "OPAL technology requires libcryptsetup >= 2.7.0");
+                         "OPAL technology requires libcryptsetup >= 2.7.0 and kernel with SED OPAL support");
             return FALSE;
 #endif
             ret = mode & (BD_CRYPTO_TECH_MODE_CREATE|BD_CRYPTO_TECH_MODE_QUERY|BD_CRYPTO_TECH_MODE_MODIFY);
@@ -3547,6 +3549,12 @@ gboolean bd_crypto_fvault2_close (const gchar *fvault2_device, GError **error) {
  *
  * Tech category: %BD_CRYPTO_TECH_SED_OPAL-%BD_CRYPTO_TECH_MODE_QUERY
  */
+#ifndef HAVE_LINUX_OPAL
+gboolean bd_crypto_opal_is_supported (const gchar *device G_GNUC_UNUSED, GError **error) {
+    /* this will return FALSE and set error, because OPAL technology is not available */
+    return bd_crypto_is_tech_avail (BD_CRYPTO_TECH_SED_OPAL, BD_CRYPTO_TECH_MODE_QUERY, error);
+}
+#else
 gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error) {
     gint fd = -1;
     gint ret = 0;
@@ -3576,6 +3584,7 @@ gboolean bd_crypto_opal_is_supported (const gchar *device, GError **error) {
     else
         return FALSE;
 }
+#endif
 
 /**
  * bd_crypto_opal_wipe_device:
-- 
2.45.2

