[1/4] libcamera: pub_key: Add ML-DSA-65 signature algorithm for PQC compliance
diff mbox series

Message ID 20260408075540.53309-2-hpa@redhat.com
State New
Headers show
Series
  • Implement ML-DSA-65 for Post-Quantum Cryptographic compliance
Related show

Commit Message

Kate Hsuan April 8, 2026, 7:55 a.m. UTC
As quantum computing advances, traditional signature algorithms are
becoming vulnerable. To ensure long-term data security, this change
implements ML-DSA-65, the primary Post-Quantum Cryptography (PQC)
standard finalized by NIST. This addition prepares for the transition
away from RSA, which is slated for deprecation by 2035.

Link: https://csrc.nist.gov/projects/post-quantum-cryptography/post-quantum-cryptography-standardization/evaluation-criteria/security-(evaluation-criteria)
Link: https://nvlpubs.nist.gov/nistpubs/ir/2024/NIST.IR.8547.ipd.pdf
Signed-off-by: Kate Hsuan <hpa@redhat.com>
---
 src/libcamera/pub_key.cpp | 55 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 52 insertions(+), 3 deletions(-)

Patch
diff mbox series

diff --git a/src/libcamera/pub_key.cpp b/src/libcamera/pub_key.cpp
index f1d73a5c..20f13600 100644
--- a/src/libcamera/pub_key.cpp
+++ b/src/libcamera/pub_key.cpp
@@ -14,8 +14,13 @@ 
 #include <openssl/x509.h>
 #elif HAVE_GNUTLS
 #include <gnutls/abstract.h>
+#include <gnutls/gnutls.h>
 #endif
 
+#include "libcamera/internal/pub_key.h"
+#include <libcamera/base/log.h>
+#include <libcamera/base/utils.h>
+
 /**
  * \file pub_key.h
  * \brief Public key signature verification
@@ -23,17 +28,24 @@ 
 
 namespace libcamera {
 
+LOG_DEFINE_CATEGORY(PubKey);
 /**
  * \class PubKey
  * \brief Public key wrapper for signature verification
  *
  * The PubKey class wraps a public key and implements signature verification. It
- * only supports RSA keys and the RSA-SHA256 signature algorithm.
+ * supports RSA keys with the RSA-SHA256 signature algorithm, or ML-DSA-65 keys
+ * as specified in NIST FIPS 204. The signature algorithm is determined in
+ * compile time.
  */
 
 /**
  * \brief Construct a PubKey from key data
  * \param[in] key Key data encoded in DER format
+ *
+ * The signature algorithm is determined in the compile
+ * Supported key types are RSA (verified with RSA-SHA256) and ML-DSA-65
+ * (verified as ML-DSA-65 according to FIPS 204).
  */
 PubKey::PubKey([[maybe_unused]] Span<const uint8_t> key)
 	: valid_(false)
@@ -83,7 +95,8 @@  PubKey::~PubKey()
  * \param[in] sig The signature
  *
  * Verify that the signature \a sig matches the signed \a data for the public
- * key. The signture algorithm is hardcoded to RSA-SHA256.
+ * key. The signature algorithm is determined in compile time. RSA keys use
+ * RSA-SHA256, while ML-DSA keys use ML-DSA-65 mentioned in FIPS 204.
  *
  * \return True if the signature is valid, false otherwise
  */
@@ -94,6 +107,30 @@  bool PubKey::verify([[maybe_unused]] Span<const uint8_t> data,
 		return false;
 
 #if HAVE_CRYPTO
+
+#if WITH_FIPS
+	/* ML-DSA */
+	EVP_MD_CTX *ctx_dsa = EVP_MD_CTX_new();
+	if (!ctx_dsa) {
+		LOG(PubKey, Error) << "Initialize context for ML-DSA failed";
+		return false;
+	}
+
+	if (EVP_DigestVerifyInit(ctx_dsa, nullptr, nullptr, nullptr,
+				 pubkey_) <= 0) {
+		EVP_MD_CTX_free(ctx_dsa);
+		LOG(PubKey, Error) << "Initialize ML-DSA verification failed";
+		return false;
+	}
+
+	int ret = EVP_DigestVerify(ctx_dsa, sig.data(), sig.size(),
+				   data.data(), data.size());
+	EVP_MD_CTX_free(ctx_dsa);
+	LOG(PubKey, Error) << "Verify with ML-DSA-65: " << ret;
+	return ret == 1;
+#else
+	/* RSA with SHA-256 */
+
 	/*
 	 * Create and initialize a public key algorithm context for signature
 	 * verification.
@@ -117,7 +154,11 @@  bool PubKey::verify([[maybe_unused]] Span<const uint8_t> data,
 	int ret = EVP_PKEY_verify(ctx, sig.data(), sig.size(), digest,
 				  SHA256_DIGEST_LENGTH);
 	EVP_PKEY_CTX_free(ctx);
+
+	LOG(PubKey, Error) << "Verify with RSA-SHA256: " << ret;
 	return ret == 1;
+#endif
+
 #elif HAVE_GNUTLS
 	const gnutls_datum_t gnuTlsData{
 		const_cast<unsigned char *>(data.data()),
@@ -129,9 +170,17 @@  bool PubKey::verify([[maybe_unused]] Span<const uint8_t> data,
 		static_cast<unsigned int>(sig.size())
 	};
 
-	int ret = gnutls_pubkey_verify_data2(pubkey_, GNUTLS_SIGN_RSA_SHA256, 0,
+#if WITH_FIPS
+	int ret = gnutls_pubkey_verify_data2(pubkey_, GNUTLS_SIGN_MLDSA65, 0,
 					     &gnuTlsData, &gnuTlsSig);
+	LOG(PubKey, Error) << "Verify with ML-DSA-65: " << ret;
+	return ret >= 0;
+#else
+	int ret = gnutls_pubkey_verify_data2(pubkey_, GNUTLS_SIGN_RSA_SHA256,
+					     0, &gnuTlsData, &gnuTlsSig);
+	LOG(PubKey, Error) << "Verify with RSA-SHA256: " << ret;
 	return ret >= 0;
+#endif
 #else
 	return false;
 #endif