diff --git a/score/crypto/src/backend/BUILD b/score/crypto/src/backend/BUILD index 2c893eb03..da92afd71 100644 --- a/score/crypto/src/backend/BUILD +++ b/score/crypto/src/backend/BUILD @@ -38,7 +38,7 @@ bool_flag( bool_flag( name = "score_crypto_score_primula_enabled", - build_setting_default = False, + build_setting_default = True, visibility = ["//visibility:public"], ) diff --git a/score/crypto/src/backend/README.md b/score/crypto/src/backend/README.md index 6076cf022..6f2b0886e 100644 --- a/score/crypto/src/backend/README.md +++ b/score/crypto/src/backend/README.md @@ -48,7 +48,7 @@ Backend enable/disable is controlled via `bool_flag` targets. All flags are in | `score_crypto_pkcs11_backend_enabled` | `True` | `--//score/crypto/src/backend:score_crypto_pkcs11_backend_enabled=False` | | `score_crypto_score_backend_enabled` | `True` | `--//score/crypto/src/backend:score_crypto_score_backend_enabled=False` | | `score_crypto_score_openssl_enabled` | `True` | `--//score/crypto/src/backend:score_crypto_score_openssl_enabled=False` | -| `score_crypto_score_primula_enabled` | `False` | `--//score/crypto/src/backend:score_crypto_score_primula_enabled=True` | +| `score_crypto_score_primula_enabled` | `True` | `--//score/crypto/src/backend:score_crypto_score_primula_enabled=False` | `score_crypto_score_backend_enabled` is the master gate for the score provider Individual sub-backend flags (`score_crypto_score_openssl_enabled`, etc.) have diff --git a/score/crypto/src/backend/score_provider/BUILD b/score/crypto/src/backend/score_provider/BUILD index d2aad81d5..4389caefd 100644 --- a/score/crypto/src/backend/score_provider/BUILD +++ b/score/crypto/src/backend/score_provider/BUILD @@ -56,7 +56,12 @@ cc_library( "//score/crypto/src/daemon/provider/score_provider:score_backend_adapter", "//score/crypto/src/backend/score_provider/openssl:openssl_backend_define", # To add a new score backend: add its *_backend_define dep here (step 3). - ], + ] + select({ + "//score/crypto/src/backend:primula_backend_active": [ + "//score/crypto/src/backend/score_provider/iav_primula:primula_backend_define", + ], + "//conditions:default": [], + }), ) # Heavy: aggregates all active sub-backend adapter targets. @@ -72,5 +77,10 @@ cc_library( deps = [ "//score/crypto/src/backend/score_provider/openssl:openssl_backend", # To add a new score backend: add its *_backend dep here (step 4). - ], + ] + select({ + "//score/crypto/src/backend:primula_backend_active": [ + "//score/crypto/src/backend/score_provider/iav_primula:primula_backend", + ], + "//conditions:default": [], + }), ) diff --git a/score/crypto/src/backend/score_provider/active_backends_list.hpp b/score/crypto/src/backend/score_provider/active_backends_list.hpp index 0a960159f..5cfcf15e6 100644 --- a/score/crypto/src/backend/score_provider/active_backends_list.hpp +++ b/score/crypto/src/backend/score_provider/active_backends_list.hpp @@ -25,7 +25,7 @@ #endif #ifdef SCORE_BACKEND_PRIMULA_ENABLED -#include "score/crypto/src/backend/score_provider/primula/primula_backend_adapter.hpp" +#include "score/crypto/src/backend/score_provider/iav_primula/primula_backend_adapter.hpp" #endif namespace score::crypto::backend::score_provider diff --git a/score/crypto/src/backend/score_provider/iav_primula/BUILD b/score/crypto/src/backend/score_provider/iav_primula/BUILD new file mode 100644 index 000000000..844f6c2f4 --- /dev/null +++ b/score/crypto/src/backend/score_provider/iav_primula/BUILD @@ -0,0 +1,39 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") + +cc_library( + name = "primula_backend_adapter", + srcs = ["primula_backend_adapter.cpp"], + hdrs = ["primula_backend_adapter.hpp"], + target_compatible_with = select({ + "//score/crypto/src/backend:primula_backend_active": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + visibility = ["//score/crypto/src/backend/score_provider:__pkg__"], + deps = [ + "//score/crypto/src/daemon/provider/score_provider:score_backend_adapter", + "//score/crypto/src/daemon/provider/score_provider:provider_iav_primula_library", + ], +) + +cc_library( + name = "primula_backend_define", + defines = ["SCORE_BACKEND_PRIMULA_ENABLED=1"], + target_compatible_with = select({ + "//score/crypto/src/backend:primula_backend_active": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + visibility = [ + "//score/crypto/src/backend:__pkg__", + "//score/crypto/src/backend/score_provider:__pkg__", + ], +) + +cc_library( + name = "primula_backend", + target_compatible_with = select({ + "//score/crypto/src/backend:primula_backend_active": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + visibility = ["//score/crypto/src/backend/score_provider:__pkg__"], + deps = [":primula_backend_adapter", ":primula_backend_define"], +) diff --git a/score/crypto/src/backend/score_provider/iav_primula/primula_backend_adapter.cpp b/score/crypto/src/backend/score_provider/iav_primula/primula_backend_adapter.cpp new file mode 100644 index 000000000..94fd5711a --- /dev/null +++ b/score/crypto/src/backend/score_provider/iav_primula/primula_backend_adapter.cpp @@ -0,0 +1,37 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "primula_backend_adapter.hpp" + +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.hpp" + +namespace score::crypto::backend::score_provider::primula +{ + +daemon::provider::score_provider::ProviderCreator PrimulaBackendAdapter::GetProviderCreator() const +{ + using namespace daemon::provider::score_provider; + + return ProviderCreator{ + .backend_id = "primula", + .backend_name = "PRIMULA", + // IAV-Primula provides post-quantum algorithms only. Mark it as SPECIALIZED + // so that it is not selected for generic SOFTWARE algorithms such as SHA-256 + // or HMAC, which are provided by OpenSSL. + .provider_type = "SPECIALIZED", + .create_provider = []() -> std::unique_ptr { + return std::make_unique(); + }}; +} + +} // namespace score::crypto::backend::score_provider::primula diff --git a/score/crypto/src/backend/score_provider/iav_primula/primula_backend_adapter.hpp b/score/crypto/src/backend/score_provider/iav_primula/primula_backend_adapter.hpp new file mode 100644 index 000000000..93ce40e4c --- /dev/null +++ b/score/crypto/src/backend/score_provider/iav_primula/primula_backend_adapter.hpp @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file primula_backend_adapter.hpp +/// @brief Backend adapter for the IAV-Primula score provider. + +#ifndef SCORE_CRYPTO_SRC_BACKEND_PROVIDER_IAV_PRIMULA_ADAPTER_HPP +#define SCORE_CRYPTO_SRC_BACKEND_PROVIDER_IAV_PRIMULA_ADAPTER_HPP + +#include "score/crypto/src/daemon/provider/score_provider/score_backend_adapter.hpp" + +namespace score::crypto::backend::score_provider::primula +{ + +/// @brief IAV-Primula backend adapter for the score provider family. +/// +/// Provides factory creation metadata for the IAV-Primula post-quantum +/// crypto backend. This adapter is included at compile time via +/// backend/score_provider/active_backends_list.hpp when the Primula backend +/// is enabled in the Bazel configuration. +/// +/// The IAV-Primula backend implementation lives in: +/// - daemon/provider/score_provider/iav_primula/iav_primula_provider.* +/// - daemon/provider/score_provider/iav_primula/operations/* +/// +/// This adapter provides backend metadata and creates the corresponding +/// provider instance. Cryptographic operations are implemented by the +/// IAV-Primula provider and its handlers. +class PrimulaBackendAdapter final : public daemon::provider::score_provider::IBackendProviderAdapter +{ + public: + PrimulaBackendAdapter() = default; + ~PrimulaBackendAdapter() override = default; + + PrimulaBackendAdapter(const PrimulaBackendAdapter&) = delete; + PrimulaBackendAdapter& operator=(const PrimulaBackendAdapter&) = delete; + PrimulaBackendAdapter(PrimulaBackendAdapter&&) = delete; + PrimulaBackendAdapter& operator=(PrimulaBackendAdapter&&) = delete; + + /// @brief Get the provider creator for the IAV-Primula backend. + /// + /// Returns: + /// - backend_id: "primula" + /// - backend_name: "PRIMULA" + /// - provider_type: "SPECIALIZED" + /// - create_provider: constructs and returns an IavPrimulaProvider + [[nodiscard]] daemon::provider::score_provider::ProviderCreator GetProviderCreator() const override; +}; + +} // namespace score::crypto::backend::score_provider::primula + +#endif // SCORE_CRYPTO_SRC_BACKEND_PROVIDER_IAV_PRIMULA_ADAPTER_HPP diff --git a/score/crypto/src/daemon/common/BUILD b/score/crypto/src/daemon/common/BUILD index 1cfebd98d..5ffa19f33 100644 --- a/score/crypto/src/daemon/common/BUILD +++ b/score/crypto/src/daemon/common/BUILD @@ -46,6 +46,9 @@ cc_library( "//score/crypto/src/daemon/key_management:key_management_operations", "//score/crypto/src/daemon/provider/handler:hash_handler_operations", "//score/crypto/src/daemon/provider/handler:mac_handler_operations", + "//score/crypto/src/daemon/provider/handler:sign_handler_operations", + "//score/crypto/src/daemon/provider/handler:verify_handler_operations", + "//score/crypto/src/daemon/provider/handler:kem_handler_operations", "@score_baselibs//score/mw/log", ], ) diff --git a/score/crypto/src/daemon/common/actors.hpp b/score/crypto/src/daemon/common/actors.hpp index bd7b3e417..06735c7f1 100644 --- a/score/crypto/src/daemon/common/actors.hpp +++ b/score/crypto/src/daemon/common/actors.hpp @@ -27,6 +27,9 @@ inline constexpr OperationActor OP_ACTOR_PROVIDER = 3; inline constexpr OperationActor OP_ACTOR_HASH_HANDLER = 4; inline constexpr OperationActor OP_ACTOR_KEY_MANAGEMENT = 5; inline constexpr OperationActor OP_ACTOR_MAC_HANDLER = 6; +inline constexpr OperationActor OP_ACTOR_SIGN_HANDLER = 7; +inline constexpr OperationActor OP_ACTOR_VERIFY_HANDLER = 8; +inline constexpr OperationActor OP_ACTOR_KEM_HANDLER = 9; // Starting point for custom actors inline constexpr OperationActor CUSTOM_ACTOR_START = 1 << (std::numeric_limits::digits - 1); diff --git a/score/crypto/src/daemon/common/algorithm_info.hpp b/score/crypto/src/daemon/common/algorithm_info.hpp index 1c8c62bcf..6c45fb5f4 100644 --- a/score/crypto/src/daemon/common/algorithm_info.hpp +++ b/score/crypto/src/daemon/common/algorithm_info.hpp @@ -14,7 +14,9 @@ #ifndef SCORE_CRYPTO_SRC_DAEMON_COMMON_ALGORITHM_INFO_HPP #define SCORE_CRYPTO_SRC_DAEMON_COMMON_ALGORITHM_INFO_HPP +#include #include +#include #include #include @@ -31,14 +33,14 @@ struct HashAlgorithmInfo std::size_t digest_size; ///< Output size in bytes }; -inline constexpr HashAlgorithmInfo kHashAlgorithms[] = { +inline constexpr std::array kHashAlgorithms{{ {"SHA256", 32U}, {"SHA384", 48U}, {"SHA512", 64U}, {"SHA224", 28U}, {"SHA1", 20U}, {"MD5", 16U}, -}; +}}; /// @brief Look up digest size by algorithm name. /// @return digest size in bytes, or std::nullopt if unknown. @@ -64,11 +66,11 @@ struct MacAlgorithmInfo std::size_t mac_size; ///< Output tag size in bytes }; -inline constexpr MacAlgorithmInfo kMacAlgorithms[] = { +inline constexpr std::array kMacAlgorithms = {{ {"HMAC-SHA256", 32U}, {"HMAC-SHA384", 48U}, {"HMAC-SHA512", 64U}, -}; +}}; /// @brief Look up MAC output size by algorithm name. /// @return MAC size in bytes, or std::nullopt if unknown. @@ -94,7 +96,72 @@ struct KeyAlgorithmInfo std::size_t key_size; ///< Default key size in bytes }; -inline constexpr KeyAlgorithmInfo kKeyAlgorithms[] = { +// --------------------------------------------------------------------------- +// Post-quantum algorithm properties +// --------------------------------------------------------------------------- + +/// @brief Identifies the operation family implemented by a PQC algorithm. +enum class PqcAlgorithmKind : std::uint8_t +{ + kSignature, + kKem, +}; + +/// @brief Fixed-size properties for standardized PQC parameter sets. +/// +/// The sizes describe the byte representation used at the provider boundary. +/// The provider remains responsible for validating the actual encoding. +struct PqcAlgorithmInfo +{ + std::string_view name; ///< Standardized algorithm identifier. + PqcAlgorithmKind kind; ///< Signature or KEM algorithm. + std::size_t public_key_size; ///< Public key size in bytes. + std::size_t private_key_size; ///< Private key size in bytes. + std::size_t signature_or_ciphertext_size; ///< Signature or ciphertext size in bytes; zero when not applicable. + std::size_t shared_secret_size; ///< Shared-secret size in bytes; zero for signature algorithms. +}; + +inline constexpr std::array kPqcAlgorithms = {{ + // ML-DSA: public key, private key, and signature sizes from FIPS 204. + {"ML-DSA-44", PqcAlgorithmKind::kSignature, 1312U, 2560U, 2420U, 0U}, + {"ML-DSA-65", PqcAlgorithmKind::kSignature, 1952U, 4032U, 3309U, 0U}, + {"ML-DSA-87", PqcAlgorithmKind::kSignature, 2592U, 4896U, 4627U, 0U}, + + // ML-KEM: public key, private key, ciphertext, and shared-secret sizes + // from FIPS 203. + {"ML-KEM-512", PqcAlgorithmKind::kKem, 800U, 1632U, 768U, 32U}, + {"ML-KEM-768", PqcAlgorithmKind::kKem, 1184U, 2400U, 1088U, 32U}, + {"ML-KEM-1024", PqcAlgorithmKind::kKem, 1568U, 3168U, 1568U, 32U}, +}}; + +/// @brief Look up a standardized PQC algorithm by its textual identifier. +[[nodiscard]] inline constexpr std::optional LookupPqcAlgorithm(std::string_view algorithm) noexcept +{ + for (const auto& entry : kPqcAlgorithms) + { + if (entry.name == algorithm) + { + return entry; + } + } + return std::nullopt; +} + +/// @brief Return whether the identifier names a PQC signature algorithm. +[[nodiscard]] inline constexpr bool IsPqcSignatureAlgorithm(std::string_view algorithm) noexcept +{ + const auto info = LookupPqcAlgorithm(algorithm); + return info.has_value() && info->kind == PqcAlgorithmKind::kSignature; +} + +/// @brief Return whether the identifier names a PQC KEM algorithm. +[[nodiscard]] inline constexpr bool IsPqcKemAlgorithm(std::string_view algorithm) noexcept +{ + const auto info = LookupPqcAlgorithm(algorithm); + return info.has_value() && info->kind == PqcAlgorithmKind::kKem; +} + +inline constexpr std::array kKeyAlgorithms = {{ {"HMAC-SHA256", 32U}, {"HMAC-SHA384", 48U}, {"HMAC-SHA512", 64U}, @@ -106,7 +173,7 @@ inline constexpr KeyAlgorithmInfo kKeyAlgorithms[] = { {"AES-256-GCM", 32U}, {"AES-128-CMAC", 16U}, {"AES-256-CMAC", 32U}, -}; +}}; /// @brief Look up default key size by algorithm name. /// @return key size in bytes, or std::nullopt if unknown. diff --git a/score/crypto/src/daemon/common/operation_names.hpp b/score/crypto/src/daemon/common/operation_names.hpp index c553f747f..721150a68 100644 --- a/score/crypto/src/daemon/common/operation_names.hpp +++ b/score/crypto/src/daemon/common/operation_names.hpp @@ -31,8 +31,8 @@ /// @endcode /// /// ### Design notes -/// - Header-only: all functions are constexpr, no runtime tables. -/// - Covers all first-party actors and their operations. +/// - Header-only: lookup functions are constexpr and do not use runtime tables. +/// - Covers the operation namespaces currently registered by this component. /// - Falls back to "" / "" for future or custom values, /// while still printing the numeric ids so nothing is lost. @@ -42,7 +42,10 @@ // Operation constants — these headers only depend on types.hpp (no circular risk). #include "score/crypto/src/daemon/key_management/interfaces/key_management_operations.hpp" #include "score/crypto/src/daemon/provider/handler/operations/hash_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/kem_handler_operations.hpp" #include "score/crypto/src/daemon/provider/handler/operations/mac_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/sign_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/verify_handler_operations.hpp" #include "score/mw/log/logging.h" #include @@ -52,6 +55,7 @@ namespace score::crypto::daemon::common { /// @brief Returns the symbolic name for a registered OperationActor value. +/// @return Symbolic actor name, or "" for unknown actors. constexpr std::string_view ActorName(OperationActor actor) noexcept { switch (actor) @@ -68,6 +72,12 @@ constexpr std::string_view ActorName(OperationActor actor) noexcept return "KEY_MGMT"; case actors::OP_ACTOR_MAC_HANDLER: return "MAC_HANDLER"; + case actors::OP_ACTOR_SIGN_HANDLER: + return "SIGN_HANDLER"; + case actors::OP_ACTOR_VERIFY_HANDLER: + return "VERIFY_HANDLER"; + case actors::OP_ACTOR_KEM_HANDLER: + return "KEM_HANDLER"; default: return ""; } @@ -77,6 +87,8 @@ constexpr std::string_view ActorName(OperationActor actor) noexcept /// /// The actor context is required because the same action integer has different meanings /// across actors (e.g., action=1 is CTX_CREATE for MEDIATOR but HASH_INIT for HASH_HANDLER). +/// @return Symbolic operation name, or an actor-specific unknown-operation marker +/// if the action is not registered. constexpr std::string_view ActionName(OperationActor actor, OperationAction action) noexcept { // Mediator action constants (mediator_operations.hpp cannot be included here without @@ -144,6 +156,57 @@ constexpr std::string_view ActionName(OperationActor actor, OperationAction acti return ""; } + case actors::OP_ACTOR_SIGN_HANDLER: + switch (action) + { + case provider::handler::sign_handler_operations::SIGN_INIT: + return "SIGN_INIT"; + case provider::handler::sign_handler_operations::SIGN_UPDATE: + return "SIGN_UPDATE"; + case provider::handler::sign_handler_operations::SIGN_FINALIZE: + return "SIGN_FINALIZE"; + case provider::handler::sign_handler_operations::SIGN_SS: + return "SIGN_SS"; + case provider::handler::sign_handler_operations::SIGN_GET_SIGNATURE_SIZE: + return "SIGN_GET_SIGNATURE_SIZE"; + case provider::handler::sign_handler_operations::SIGN_RESET: + return "SIGN_RESET"; + default: + return ""; + } + + case actors::OP_ACTOR_VERIFY_HANDLER: + switch (action) + { + case provider::handler::verify_handler_operations::VERIFY_INIT: + return "VERIFY_INIT"; + case provider::handler::verify_handler_operations::VERIFY_UPDATE: + return "VERIFY_UPDATE"; + case provider::handler::verify_handler_operations::VERIFY_FINALIZE: + return "VERIFY_FINALIZE"; + case provider::handler::verify_handler_operations::VERIFY_SS: + return "VERIFY_SS"; + case provider::handler::verify_handler_operations::VERIFY_RESET: + return "VERIFY_RESET"; + default: + return ""; + } + + case actors::OP_ACTOR_KEM_HANDLER: + switch (action) + { + case provider::handler::kem_handler_operations::KEM_KEYGEN: + return "KEM_KEYGEN"; + case provider::handler::kem_handler_operations::KEM_ENCAPSULATE: + return "KEM_ENCAPSULATE"; + case provider::handler::kem_handler_operations::KEM_DECAPSULATE: + return "KEM_DECAPSULATE"; + case provider::handler::kem_handler_operations::KEM_RESET: + return "KEM_RESET"; + default: + return ""; + } + case actors::OP_ACTOR_KEY_MANAGEMENT: switch (action) { diff --git a/score/crypto/src/daemon/common/types.hpp b/score/crypto/src/daemon/common/types.hpp index 9f3bdfdf0..f484aa4a0 100644 --- a/score/crypto/src/daemon/common/types.hpp +++ b/score/crypto/src/daemon/common/types.hpp @@ -41,6 +41,9 @@ using MediatorId = std::string; // ProviderName: Human-readable identifier used at configuration and setup time using ProviderName = std::string; +/// @brief Configuration name identifying the IAV-Primula provider. +inline const ProviderName kProviderNameIavPrimula{"IAV_PRIMULA"}; + // ProviderId: Numeric identifier assigned at runtime by ProviderManager using ProviderId = std::uint16_t; diff --git a/score/crypto/src/daemon/provider/handler/BUILD b/score/crypto/src/daemon/provider/handler/BUILD index 040b0fdb1..2e4a0a57e 100644 --- a/score/crypto/src/daemon/provider/handler/BUILD +++ b/score/crypto/src/daemon/provider/handler/BUILD @@ -99,3 +99,24 @@ cc_library( visibility = ["//:__subpackages__"], deps = ["//score/crypto/src/daemon/common"], ) + +cc_library( + name = "sign_handler_operations", + hdrs = ["operations/sign_handler_operations.hpp"], + visibility = ["//:__subpackages__"], + deps = ["//score/crypto/src/daemon/common"], +) + +cc_library( + name = "verify_handler_operations", + hdrs = ["operations/verify_handler_operations.hpp"], + visibility = ["//:__subpackages__"], + deps = ["//score/crypto/src/daemon/common"], +) + +cc_library( + name = "kem_handler_operations", + hdrs = ["operations/kem_handler_operations.hpp"], + visibility = ["//:__subpackages__"], + deps = ["//score/crypto/src/daemon/common"], +) diff --git a/score/crypto/src/daemon/provider/handler/operations/kem_handler_operations.hpp b/score/crypto/src/daemon/provider/handler/operations/kem_handler_operations.hpp new file mode 100644 index 000000000..9576cdbfc --- /dev/null +++ b/score/crypto/src/daemon/provider/handler/operations/kem_handler_operations.hpp @@ -0,0 +1,42 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file kem_handler_operations.hpp +/// @brief Operation identifiers for provider-neutral KEM handlers. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_KEM_HANDLER_OPERATIONS_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_KEM_HANDLER_OPERATIONS_HPP + +#include "score/crypto/src/daemon/common/types.hpp" +#include + +namespace score::crypto::daemon::provider::handler::kem_handler_operations +{ +/// @brief Generate a KEM key pair. +inline constexpr common::OperationAction KEM_KEYGEN = 1; +/// @brief Encapsulate a shared secret using a peer public key. +inline constexpr common::OperationAction KEM_ENCAPSULATE = 2; +/// @brief Decapsulate a ciphertext with the bound private key. +inline constexpr common::OperationAction KEM_DECAPSULATE = 3; +/// @brief Reset the KEM handler state. +inline constexpr common::OperationAction KEM_RESET = 4; + +/// @brief First operation identifier reserved for custom KEM operations. +/// +/// The highest bit separates custom operation identifiers from the +/// built-in KEM handler operations. +inline constexpr common::OperationAction KEM_CUSTOM_OP_START = + 1 << (std::numeric_limits::digits - 1); +} // namespace score::crypto::daemon::provider::handler::kem_handler_operations + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_KEM_HANDLER_OPERATIONS_HPP diff --git a/score/crypto/src/daemon/provider/handler/operations/sign_handler_operations.hpp b/score/crypto/src/daemon/provider/handler/operations/sign_handler_operations.hpp new file mode 100644 index 000000000..f8c67e070 --- /dev/null +++ b/score/crypto/src/daemon/provider/handler/operations/sign_handler_operations.hpp @@ -0,0 +1,46 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file sign_handler_operations.hpp +/// @brief Operation identifiers for provider-neutral signature handlers. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_SIGN_HANDLER_OPERATIONS_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_SIGN_HANDLER_OPERATIONS_HPP + +#include "score/crypto/src/daemon/common/types.hpp" +#include + +namespace score::crypto::daemon::provider::handler::sign_handler_operations +{ +/// @brief Initialize a streaming signature operation. +inline constexpr common::OperationAction SIGN_INIT = 1; +/// @brief Add a message fragment to the signature operation. +inline constexpr common::OperationAction SIGN_UPDATE = 2; +/// @brief Finalize a streaming signature operation. +inline constexpr common::OperationAction SIGN_FINALIZE = 3; +/// @brief Sign a complete message in one operation. +inline constexpr common::OperationAction SIGN_SS = 4; +/// @brief Query the signature size for the selected algorithm. +inline constexpr common::OperationAction SIGN_GET_SIGNATURE_SIZE = 5; +/// @brief Reset the signature operation state. +inline constexpr common::OperationAction SIGN_RESET = 6; + +/// @brief First operation identifier reserved for custom signature operations. +/// +/// The highest bit separates custom operation identifiers from the +/// built-in signature handler operations. +inline constexpr common::OperationAction SIGN_CUSTOM_OP_START = + 1 << (std::numeric_limits::digits - 1); +} // namespace score::crypto::daemon::provider::handler::sign_handler_operations + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_SIGN_HANDLER_OPERATIONS_HPP diff --git a/score/crypto/src/daemon/provider/handler/operations/verify_handler_operations.hpp b/score/crypto/src/daemon/provider/handler/operations/verify_handler_operations.hpp new file mode 100644 index 000000000..751942571 --- /dev/null +++ b/score/crypto/src/daemon/provider/handler/operations/verify_handler_operations.hpp @@ -0,0 +1,44 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file verify_handler_operations.hpp +/// @brief Operation identifiers for provider-neutral signature verification handlers. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_VERIFY_HANDLER_OPERATIONS_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_VERIFY_HANDLER_OPERATIONS_HPP + +#include "score/crypto/src/daemon/common/types.hpp" +#include + +namespace score::crypto::daemon::provider::handler::verify_handler_operations +{ +/// @brief Initialize a streaming verification operation. +inline constexpr common::OperationAction VERIFY_INIT = 1; +/// @brief Add a message fragment to the verification operation. +inline constexpr common::OperationAction VERIFY_UPDATE = 2; +/// @brief Verify a signature against the accumulated message. +inline constexpr common::OperationAction VERIFY_FINALIZE = 3; +/// @brief Verify a complete message in one operation. +inline constexpr common::OperationAction VERIFY_SS = 4; +/// @brief Reset the verification operation state. +inline constexpr common::OperationAction VERIFY_RESET = 5; + +/// @brief First operation identifier reserved for custom verification operations. +/// +/// The highest bit separates custom operation identifiers from the +/// built-in verification handler operations. +inline constexpr common::OperationAction VERIFY_CUSTOM_OP_START = + 1 << (std::numeric_limits::digits - 1); +} // namespace score::crypto::daemon::provider::handler::verify_handler_operations + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_HANDLER_VERIFY_HANDLER_OPERATIONS_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/BUILD b/score/crypto/src/daemon/provider/score_provider/BUILD index ee5ffaa6d..3ea2de572 100644 --- a/score/crypto/src/daemon/provider/score_provider/BUILD +++ b/score/crypto/src/daemon/provider/score_provider/BUILD @@ -58,6 +58,48 @@ cc_library( ], ) +cc_library( + name = "provider_iav_primula_library", + srcs = [ + "iav_primula/iav_primula_provider.cpp", + "iav_primula/operations/factory/iav_primula_handler_factory.cpp", + "iav_primula/key_management/iav_primula_key_factory.cpp", + "iav_primula/key_management/iav_primula_key_handler.cpp", + "iav_primula/operations/sign/iav_primula_sign_handler.cpp", + "iav_primula/operations/verify/iav_primula_verify_handler.cpp", + "iav_primula/operations/kem/iav_primula_kem_handler.cpp", + ], + hdrs = [ + "iav_primula/iav_primula_provider.hpp", + "iav_primula/operations/factory/iav_primula_handler_factory.hpp", + "iav_primula/key_management/iav_primula_key_factory.hpp", + "iav_primula/key_management/iav_primula_key_handler.hpp", + "iav_primula/operations/sign/iav_primula_sign_handler.hpp", + "iav_primula/operations/verify/iav_primula_verify_handler.hpp", + "iav_primula/operations/kem/iav_primula_kem_handler.hpp", + ], + target_compatible_with = select({ + "//score/crypto/src/backend:primula_backend_active": [], + "//conditions:default": ["@platforms//:incompatible"], + }), + visibility = [ + "//score/crypto/src/backend/score_provider/iav_primula:__pkg__", + "//score/crypto/src/daemon/provider/score_provider:__subpackages__", + ], + deps = [ + "//score/crypto/src/daemon/common:algorithm_info", + "//score/crypto/src/daemon/key_management:key_management_headers", + "//score/crypto/src/daemon/provider/handler:crypto_handler_factory_headers", + "//score/crypto/src/daemon/provider/score_provider", + "//score/crypto/src/daemon/provider/score_provider/operations/factory:score_handler_factory", + "//score/crypto/src/daemon/provider/score_provider/operations/kem:score_kem_handler", + "//score/crypto/src/daemon/provider/score_provider/operations/sign:score_sign_handler", + "//score/crypto/src/daemon/provider/score_provider/operations/verify:score_verify_handler", + "//score/iav_primula:iav_primula_ffi_headers", + "//score/iav_primula:iav_primula_staticlib", + ], +) + # Top-level factory for the score interface family. cc_library( name = "score_provider_factory", diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.cpp new file mode 100644 index 000000000..6b77f6258 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.cpp @@ -0,0 +1,75 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/factory/iav_primula_handler_factory.hpp" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +bool IavPrimulaProvider::InitialiseBackend(const ProviderInitContext& ctx) +{ + const bool initialized = true; + if (initialized) + { + m_factory = std::make_shared(ctx.numeric_id); + } + return initialized; +} + +void IavPrimulaProvider::Shutdown() +{ + if (!IsInitialized()) + { + return; + } + + m_factory.reset(); + m_keyManagementService.reset(); + + // TODO implement cleanup of the Primula handler ressources on shutdown if required + + // Base class resets factory and flags. + ScoreProvider::Shutdown(); +} + +std::shared_ptr IavPrimulaProvider::GetKeyFactory() +{ + return m_factory; +} + +std::shared_ptr IavPrimulaProvider::GetKeySlotHandler( + const key_management::KeySlotConfig& config) +{ + // TODO: return Primula-specific key slot handler if supported. + return ScoreProvider::GetKeySlotHandler(config); +} + +void IavPrimulaProvider::SetKeyManagementService(std::shared_ptr service) +{ + m_keyManagementService = std::move(service); +} + +/// @brief Create the IAV-Primula-specific handler factory. +/// +/// The provider exposes only the generic handler-factory interface to the +/// daemon. PQC implementation details remain below that boundary. +std::shared_ptr IavPrimulaProvider::CreateHandlerFactory() +{ + return std::make_shared(m_factory, + nullptr, // TODO This needs to be a key slot handler for primula + m_keyManagementService); +} + +} // namespace score::crypto::daemon::provider::score_provider::iav_primula diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.hpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.hpp new file mode 100644 index 000000000..576d8b210 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.hpp @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file iav_primula_provider.hpp +/// @brief IAV-Primula provider implementation for the score interface. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_PROVIDER_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_PROVIDER_HPP + +#include + +#include "score/crypto/src/daemon/provider/score_provider/score_provider.hpp" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +/// @brief IAV-Primula is a provider for post-quantum cryptographic algorithms. +/// +/// Inherits ScoreProvider for provider lifecycle management and lazy handler +/// factory creation. Provides IAV-Primula-specific key management and +/// cryptographic operation handlers without exposing implementation details +/// to the daemon core. +class IavPrimulaProvider final : public ::score::crypto::daemon::provider::score_provider::ScoreProvider +{ + public: + IavPrimulaProvider() = default; + ~IavPrimulaProvider() override = default; + + IavPrimulaProvider(const IavPrimulaProvider&) = delete; + IavPrimulaProvider& operator=(const IavPrimulaProvider&) = delete; + IavPrimulaProvider(IavPrimulaProvider&&) = delete; + IavPrimulaProvider& operator=(IavPrimulaProvider&&) = delete; + + // --- IProvider lifecycle (IAV-Primula-specific) --- + void Shutdown() override; + [[nodiscard]] bool InitialiseBackend(const ProviderInitContext& ctx) override; + + // --- Key management capability --- + std::shared_ptr GetKeyFactory() override; + std::shared_ptr GetKeySlotHandler( + const key_management::KeySlotConfig& config) override; + void SetKeyManagementService(std::shared_ptr service) override; + + protected: + /// @brief Creates the IAV-Primula-specific handler factory. + [[nodiscard]] std::shared_ptr CreateHandlerFactory() override; + + private: + std::shared_ptr m_factory; + std::shared_ptr m_keyManagementService; +}; + +} // namespace score::crypto::daemon::provider::score_provider::iav_primula + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_PROVIDER_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.cpp new file mode 100644 index 000000000..7fa31f4da --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.cpp @@ -0,0 +1,122 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.hpp" +#include "score/crypto/src/daemon/common/algorithm_info.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/iav_primula/include/iav_primula_ffi.h" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ +namespace +{ +/// @brief Map a PQC algorithm identifier to the IAV-Primula enum value. +/// +/// @return The corresponding IAV-Primula algorithm, or +/// kUnsupportedAlgorithm if the identifier is unknown. +Expected Algorithm(common::AlgorithmId algorithmId) +{ + auto algorithmDescription = std::string_view{algorithmId.data(), algorithmId.size()}; + if (algorithmDescription == "ML-DSA-44") + { + return IavAlgorithmMlDsa44; + } + if (algorithmDescription == "ML-DSA-65") + { + return IavAlgorithmMlDsa65; + } + if (algorithmDescription == "ML-DSA-87") + { + return IavAlgorithmMlDsa87; + } + if (algorithmDescription == "ML-KEM-512") + { + return IavAlgorithmMlKem512; + } + if (algorithmDescription == "ML-KEM-768") + { + return IavAlgorithmMlKem768; + } + if (algorithmDescription == "ML-KEM-1024") + { + return IavAlgorithmMlKem1024; + } + return make_unexpected(common::DaemonErrorCode::kUnsupportedAlgorithm); +} +} // namespace + +Expected IavPrimulaKeyFactory::GenerateKey( + const key_management::KeyGenerationRequest& r) +{ + auto info = common::LookupPqcAlgorithm(std::string_view{r.algorithm.data(), r.algorithm.size()}); + if (!info || + ((info->kind != common::PqcAlgorithmKind::kSignature) && (info->kind != common::PqcAlgorithmKind::kKem))) + { + return make_unexpected(common::DaemonErrorCode::kUnsupportedAlgorithm); + } + + auto algorithm = Algorithm(r.algorithm); + if (!algorithm.has_value()) + { + return make_unexpected(algorithm.error()); + } + + iav_primula_key_handle* key = nullptr; + const auto generate_status = (info->kind == common::PqcAlgorithmKind::kKem) + ? iav_kem_keypair_generate(algorithm.value(), &key) + : iav_keypair_generate(algorithm.value(), &key); + if (generate_status != IavStatusOk || key == nullptr) + { + return make_unexpected(common::DaemonErrorCode::kOperationFailed); + } + + std::vector pub(info->public_key_size); + std::size_t n = pub.size(); + const auto export_status = (info->kind == common::PqcAlgorithmKind::kKem) + ? iav_kem_public_key_export(key, pub.data(), &n) + : iav_public_key_export(key, pub.data(), &n); + if (export_status != IavStatusOk || n != pub.size()) + { + iav_key_destroy(key); + return make_unexpected(common::DaemonErrorCode::kOperationFailed); + } + + key_management::ProviderKeyHandle h{}; + h.opaque_id = reinterpret_cast(key); + h.provider_id = m_provider_id; + h.permissions = r.permissions; + h.is_asymmetric = true; + h.algorithm = r.algorithm; + h.key_size = info->private_key_size; + return std::make_shared(key, std::move(pub), h); +} + +Expected IavPrimulaKeyFactory::ImportKey( + const key_management::KeyImportRequest& r) +{ + auto info = common::LookupPqcAlgorithm(std::string_view{r.algorithm.data(), r.algorithm.size()}); + if (!info || + ((info->kind != common::PqcAlgorithmKind::kSignature) && (info->kind != common::PqcAlgorithmKind::kKem)) || + !r.key_data || (r.key_data_size != info->public_key_size)) + return make_unexpected(common::DaemonErrorCode::kInvalidArgument); + + std::vector pub(r.key_data, r.key_data + r.key_data_size); + key_management::ProviderKeyHandle h{}; + h.provider_id = m_provider_id; + h.permissions = r.permissions; + h.is_asymmetric = true; + h.algorithm = r.algorithm; + h.key_size = info->public_key_size; + return std::make_shared(nullptr, std::move(pub), h); +} +} // namespace score::crypto::daemon::provider::score_provider::iav_primula diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.hpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.hpp new file mode 100644 index 000000000..d2e159b15 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.hpp @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +/// @file iav_primula_key_factory.hpp +/// @brief Key factory for IAV-Primula provider keys. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEY_FACTORY_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEY_FACTORY_HPP + +#include "score/crypto/src/daemon/key_management/interfaces/i_key_factory.hpp" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +/// @brief IAV-Primula key factory for post-quantum algorithms. +/// +/// Creates and imports key handlers for the supported ML-DSA and ML-KEM +/// parameter sets. Each generated key is associated with the provider ID +/// supplied during construction. +class IavPrimulaKeyFactory final : public key_management::IKeyFactory +{ + public: + /// @brief Create a key factory for the specified provider instance. + /// + /// @param id Numeric identifier assigned to the provider. + explicit IavPrimulaKeyFactory(common::ProviderId providerId) : m_provider_id(providerId) {} + + /// @brief Generate an IAV-Primula key pair. + /// + /// Supports the ML-DSA and ML-KEM parameter sets defined by the provider. + /// The returned handler owns the native key handle and the exported public + /// key material. + [[nodiscard]] Expected GenerateKey( + const key_management::KeyGenerationRequest&) override; + + /// @brief Import public key material for an IAV-Primula algorithm. + /// + /// Supports public keys for the ML-DSA and ML-KEM parameter sets defined by + /// the provider. The key material is copied into the returned key handler. + [[nodiscard]] Expected ImportKey( + const key_management::KeyImportRequest&) override; + + private: + common::ProviderId m_provider_id{}; ///< Numeric identifier of the owning provider instance. +}; +} // namespace score::crypto::daemon::provider::score_provider::iav_primula + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEY_FACTORY_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.cpp new file mode 100644 index 000000000..8ba170b79 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.cpp @@ -0,0 +1,77 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/crypto/src/api/common/types.hpp" +#include + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ +IavPrimulaKeyHandler::IavPrimulaKeyHandler(iav_primula_key_handle* key, + std::vector pub, + const key_management::ProviderKeyHandle& handle) noexcept + : m_native_key{key}, m_public_key{std::move(pub)}, m_handle{handle} +{ +} + +IavPrimulaKeyHandler::~IavPrimulaKeyHandler() +{ + static_cast(Release()); +} + +const key_management::ProviderKeyHandle& IavPrimulaKeyHandler::GetHandle() const noexcept +{ + return m_handle; +} + +common::ProviderId IavPrimulaKeyHandler::GetProviderId() const noexcept +{ + return m_handle.provider_id; +} + +Expected IavPrimulaKeyHandler::Release() +{ + if (!m_released) + { + // Release the native key handle and wipe cached key material exactly once. + if (m_native_key != nullptr) + { + iav_key_destroy(m_native_key); + } + m_native_key = nullptr; + std::fill(m_public_key.begin(), m_public_key.end(), 0U); + m_public_key.clear(); + m_released = true; + } + return std::monostate{}; +} + +Expected IavPrimulaKeyHandler::Export() const +{ + // Export only the cached public key after checking permissions and release state. + if (!score::crypto::HasPermission(m_handle.permissions, score::crypto::KeyOperationPermission::kExport) || + m_released) + { + return make_unexpected(common::DaemonErrorCode::kKeyOperationNotPermitted); + } + + key_management::SecureKeyBytes out(m_public_key.size()); + std::copy(m_public_key.begin(), m_public_key.end(), out.bytes.begin()); + return out; +} + +const std::uint8_t* IavPrimulaKeyHandler::GetPublicKey(std::size_t& size) const noexcept +{ + size = m_released ? 0U : m_public_key.size(); + return size == 0U ? nullptr : m_public_key.data(); +} +} // namespace score::crypto::daemon::provider::score_provider::iav_primula diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp new file mode 100644 index 000000000..69bfc5dbd --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp @@ -0,0 +1,90 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ +/// @file iav_primula_key_handler.hpp +/// @brief Key handler for IAV-Primula provider keys. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEY_HANDLER_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEY_HANDLER_HPP + +#include "score/crypto/src/daemon/key_management/interfaces/i_key_handler.hpp" +#include "score/iav_primula/include/iav_primula_ffi.h" +#include + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +/// @brief Key handler for IAV-Primula post-quantum keys. +/// +/// Owns an optional native IAV-Primula key handle and a cached copy of the +/// public key. The native handle is destroyed and the cached key material is +/// cleared when Release() is called or the handler is destroyed. +/// +/// Imported public keys do not have a native key handle. +class IavPrimulaKeyHandler final : public key_management::IKeyHandler +{ + public: + /// @brief Create a key handler for an IAV-Primula key. + /// + /// The handler takes ownership of native_key and takes a copy of the + /// provider key metadata. native_key may be nullptr for imported public + /// keys. + IavPrimulaKeyHandler(iav_primula_key_handle* native_key, + std::vector public_key, + const key_management::ProviderKeyHandle& handle) noexcept; + ~IavPrimulaKeyHandler() override; + IavPrimulaKeyHandler(const IavPrimulaKeyHandler&) = delete; + IavPrimulaKeyHandler& operator=(const IavPrimulaKeyHandler&) = delete; + IavPrimulaKeyHandler(const IavPrimulaKeyHandler&&) = delete; + IavPrimulaKeyHandler& operator=(const IavPrimulaKeyHandler&&) = delete; + /// @brief Return the provider metadata associated with the key. + /// + /// The returned metadata contains the provider ID, algorithm, permissions, + /// and key properties. It does not expose the native IAV-Primula key handle. + [[nodiscard]] const key_management::ProviderKeyHandle& GetHandle() const noexcept override; + /// @brief Release the native key handle and cached public key material. + /// + /// This operation is idempotent and can safely be called more than once. + [[nodiscard]] Expected Release() override; + /// @brief Export the cached public key. + /// + /// Export requires the corresponding key permission and is not allowed + /// after the key has been released. + [[nodiscard]] Expected Export() const override; + /// @brief Return the numeric identifier of the owning provider instance. + /// + /// The identifier is stored in the provider key metadata and is used to + /// associate the key with the provider that created or imported it. + [[nodiscard]] common::ProviderId GetProviderId() const noexcept override; + /// @brief Return the native IAV-Primula key handle. + /// + /// Returns nullptr when the key was imported as public key material or has + /// already been released. The returned handle remains owned by this handler. + [[nodiscard]] iav_primula_key_handle* GetNativeHandle() const noexcept + { + return m_native_key; + } + /// @brief Return a non-owning pointer to the cached public key. + /// + /// The pointer remains valid until the key is released or the handler is + /// destroyed. Returns nullptr and sets size to zero after release. + [[nodiscard]] const std::uint8_t* GetPublicKey(std::size_t& size) const noexcept; + + private: + iav_primula_key_handle* m_native_key; ///< Owned native key handle, or nullptr for public-only keys. + std::vector m_public_key; ///< Cached public key material. + key_management::ProviderKeyHandle m_handle; ///< Provider metadata associated with the key. + bool m_released{false}; ///< Whether the key material has already been released. +}; +} // namespace score::crypto::daemon::provider::score_provider::iav_primula + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEY_HANDLER_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/factory/iav_primula_handler_factory.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/factory/iav_primula_handler_factory.cpp new file mode 100644 index 000000000..08cda26d8 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/factory/iav_primula_handler_factory.cpp @@ -0,0 +1,87 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/factory/iav_primula_handler_factory.hpp" +#include "score/crypto/src/api/common/error_domain.hpp" +#include "score/crypto/src/daemon/common/algorithm_info.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/kem/kem_executor.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/sign/sign_executor.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/verify/verify_executor.hpp" + +#include +#include + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +namespace +{ +/// @brief Create a score error result for an unsupported algorithm. +/// +/// The returned result contains no handler and uses the +/// kUnsupportedAlgorithm error code. +::score::Result MakeUnsupportedAlgorithmError(const std::string& message) +{ + const ::score::result::Error error( + static_cast<::score::result::ErrorCode>(::score::crypto::CryptoErrorCode::kUnsupportedAlgorithm), + ::score::crypto::kCryptoErrorDomain, + message); + return ::score::Result(::score::unexpect, error); +} +} // namespace + +IavPrimulaHandlerFactory::IavPrimulaHandlerFactory(std::shared_ptr key_factory, + std::shared_ptr slot_handler, + key_management::KeyManagementService::Sptr km_service) + : ScoreHandlerFactory(std::move(key_factory), std::move(slot_handler), std::move(km_service)) +{ +} + +::score::Result IavPrimulaHandlerFactory::CreateSignHandler( + const common::AlgorithmId& algorithm) +{ + if (!common::IsPqcSignatureAlgorithm(algorithm)) + { + return MakeUnsupportedAlgorithmError("Algorithm is not a supported iavPrimula signature algorithm: " + + algorithm); + } + + return std::make_shared(std::make_unique(), algorithm); +} + +::score::Result IavPrimulaHandlerFactory::CreateVerifyHandler( + const common::AlgorithmId& algorithm) +{ + if (!common::IsPqcSignatureAlgorithm(algorithm)) + { + return MakeUnsupportedAlgorithmError("Algorithm is not a supported iavPrimula verification algorithm: " + + algorithm); + } + + return std::make_shared(std::make_unique(), algorithm); +} + +::score::Result IavPrimulaHandlerFactory::CreateKemHandler(const common::AlgorithmId& algorithm) +{ + if (!common::IsPqcKemAlgorithm(algorithm)) + { + return MakeUnsupportedAlgorithmError("Algorithm is not a supported iavPrimula KEM algorithm: " + algorithm); + } + + return std::make_shared(std::make_unique(), algorithm); +} + +} // namespace score::crypto::daemon::provider::score_provider::iav_primula diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/factory/iav_primula_handler_factory.hpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/factory/iav_primula_handler_factory.hpp new file mode 100644 index 000000000..b290f56e8 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/factory/iav_primula_handler_factory.hpp @@ -0,0 +1,76 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file iav_primula_handler_factory.hpp +/// @brief Handler factory for IAV-Primula operations. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_HANDLER_FACTORY_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_HANDLER_FACTORY_HPP + +#include "score/crypto/src/daemon/provider/score_provider/operations/factory/score_handler_factory.hpp" +#include "score/result/result.h" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +/// @brief Factory for IAV-Primula SIGN, VERIFY, and KEM handlers. +/// +/// Validates the requested PQC algorithm and creates the corresponding +/// IAV-Primula handler with its provider-neutral operation executor. +/// Unsupported algorithm families result in kUnsupportedAlgorithm. +class IavPrimulaHandlerFactory final + : public ::score::crypto::daemon::provider::score_provider::operations::factory::ScoreHandlerFactory +{ + public: + /// @brief Create an IAV-Primula handler factory. + /// + /// @param key_factory Factory used for provider-specific key operations. + /// @param slot_handler Key-slot handler used for persistent key operations. + /// @param km_service Key-management service shared with the handlers. + IavPrimulaHandlerFactory(std::shared_ptr key_factory, + std::shared_ptr slot_handler, + key_management::KeyManagementService::Sptr km_service); + + ~IavPrimulaHandlerFactory() override = default; + + IavPrimulaHandlerFactory(const IavPrimulaHandlerFactory&) = delete; + IavPrimulaHandlerFactory& operator=(const IavPrimulaHandlerFactory&) = delete; + IavPrimulaHandlerFactory(IavPrimulaHandlerFactory&&) = delete; + IavPrimulaHandlerFactory& operator=(IavPrimulaHandlerFactory&&) = delete; + + protected: + /// @brief Create a handler for a supported ML-DSA signature algorithm. + /// + /// @return A signature handler, or kUnsupportedAlgorithm if the algorithm + /// is not an ML-DSA signature algorithm. + [[nodiscard]] ::score::Result<::score::crypto::daemon::provider::handler::Handler::Sptr> CreateSignHandler( + const common::AlgorithmId& algorithm) override; + + /// @brief Create a handler for a supported ML-DSA verification algorithm. + /// + /// @return A verification handler, or kUnsupportedAlgorithm if the + /// algorithm is not an ML-DSA signature algorithm. + [[nodiscard]] ::score::Result<::score::crypto::daemon::provider::handler::Handler::Sptr> CreateVerifyHandler( + const common::AlgorithmId& algorithm) override; + + /// @brief Create a handler for a supported ML-KEM algorithm. + /// + /// @return A KEM handler, or kUnsupportedAlgorithm if the algorithm is not + /// an ML-KEM algorithm. + [[nodiscard]] ::score::Result<::score::crypto::daemon::provider::handler::Handler::Sptr> CreateKemHandler( + const common::AlgorithmId& algorithm) override; +}; + +} // namespace score::crypto::daemon::provider::score_provider::iav_primula + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_HANDLER_FACTORY_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.cpp new file mode 100644 index 000000000..f7d77ee66 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.cpp @@ -0,0 +1,151 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.hpp" + +#include "score/crypto/src/daemon/common/algorithm_info.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/kem/kem_executor.hpp" + +#include +#include + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +IavPrimulaKemHandler::IavPrimulaKemHandler(std::unique_ptr executor, + common::AlgorithmId algorithm) + : ScoreKemHandler(std::move(executor), std::move(algorithm)) +{ +} + +Expected IavPrimulaKemHandler::GetAlgorithm() const noexcept +{ + if (m_algorithm == "ML-KEM-512") + { + return IavAlgorithmMlKem512; + } + if (m_algorithm == "ML-KEM-768") + { + return IavAlgorithmMlKem768; + } + if (m_algorithm == "ML-KEM-1024") + { + return IavAlgorithmMlKem1024; + } + return make_unexpected(common::DaemonErrorCode::kUnsupportedAlgorithm); +} + +Expected IavPrimulaKemHandler::InitializeContext( + const handler::InitializationParams& init_params) +{ + // Bind an optional IAV-Primula key. A key is required later for + // decapsulation, but not for key generation or encapsulation. + m_key = nullptr; + if (init_params.bound_key_handler != nullptr) + { + const auto* key = dynamic_cast(init_params.bound_key_handler); + if (key == nullptr) + { + return make_unexpected(common::DaemonErrorCode::kInvalidArgument); + } + m_key = key->GetNativeHandle(); + } + return {}; +} + +Expected IavPrimulaKemHandler::GenerateKeyPair() +{ + auto algorithm = GetAlgorithm(); + if (!algorithm.has_value()) + { + return make_unexpected(algorithm.error()); + } + + // Generate a temporary KEM key pair, export its public key, and release the + // native key handle before returning the public key. + iav_primula_key_handle* key = nullptr; + if (iav_kem_keypair_generate(algorithm.value(), &key) != IavStatusOk || key == nullptr) + { + return make_unexpected(common::DaemonErrorCode::kOperationFailed); + } + + const auto info = common::LookupPqcAlgorithm(m_algorithm); + std::vector public_key(info->public_key_size); + std::size_t length = public_key.size(); + const auto status = iav_kem_public_key_export(key, public_key.data(), &length); + iav_key_destroy(key); + if (status != IavStatusOk || length != public_key.size()) + { + return make_unexpected(common::DaemonErrorCode::kOperationFailed); + } + return common::ResponseParameters{common::OwnedBuffer{std::move(public_key)}}; +} + +Expected IavPrimulaKemHandler::Encapsulate( + const common::RequestParameter& request) +{ + const auto* public_key = std::get_if>(&request); + const auto info = common::LookupPqcAlgorithm(m_algorithm); + auto algorithm = GetAlgorithm(); + if ((public_key == nullptr) || !info.has_value() || !algorithm.has_value() || + (public_key->size() != info->public_key_size)) + { + return make_unexpected(common::DaemonErrorCode::kInvalidArgument); + } + + // Encapsulate a shared secret using the supplied public key. The response + // contains the ciphertext followed by the shared secret. + std::vector ciphertext(info->signature_or_ciphertext_size); + std::vector secret(info->shared_secret_size); + std::size_t ciphertext_length = ciphertext.size(); + std::size_t secret_length = secret.size(); + const auto status = iav_kem_encapsulate(algorithm.value(), + public_key->data(), + public_key->size(), + ciphertext.data(), + &ciphertext_length, + secret.data(), + &secret_length); + if ((status != IavStatusOk) || (ciphertext_length != ciphertext.size()) || (secret_length != secret.size())) + { + return make_unexpected(common::DaemonErrorCode::kOperationFailed); + } + return common::ResponseParameters{common::OwnedBuffer{std::move(ciphertext)}, + common::OwnedBuffer{std::move(secret)}}; +} + +Expected IavPrimulaKemHandler::Decapsulate( + const common::RequestParameter& request) +{ + const auto* ciphertext = std::get_if>(&request); + const auto info = common::LookupPqcAlgorithm(m_algorithm); + if ((m_key == nullptr) || (ciphertext == nullptr) || !info.has_value() || + (ciphertext->size() != info->signature_or_ciphertext_size)) + { + return make_unexpected(common::DaemonErrorCode::kInvalidArgument); + } + + // Decapsulate the ciphertext with the native key bound during context + // initialization and return the resulting shared secret. + std::vector secret(info->shared_secret_size); + std::size_t length = secret.size(); + const auto status = iav_kem_decapsulate(m_key, ciphertext->data(), ciphertext->size(), secret.data(), &length); + if ((status != IavStatusOk) || (length != secret.size())) + { + return make_unexpected(common::DaemonErrorCode::kOperationFailed); + } + return common::ResponseParameters{common::OwnedBuffer{std::move(secret)}}; +} + +} // namespace score::crypto::daemon::provider::score_provider::iav_primula diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.hpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.hpp new file mode 100644 index 000000000..09dcba415 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.hpp @@ -0,0 +1,81 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file iav_primula_kem_handler.hpp +/// @brief IAV-Primula implementation of the KEM handler. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEM_HANDLER_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEM_HANDLER_HPP + +#include "score/crypto/src/daemon/provider/score_provider/operations/kem/score_kem_handler.hpp" +#include "score/iav_primula/include/iav_primula_ffi.h" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +/// @brief IAV-Primula implementation of the provider-neutral KEM handler. +class IavPrimulaKemHandler final : public operations::kem::ScoreKemHandler +{ + public: + /// @brief Create an IAV-Primula KEM handler. + /// + /// Takes ownership of the operation executor and stores the selected + /// ML-KEM algorithm. + /// + /// @param executor Provider-neutral executor for KEM operations. + /// @param algorithm ML-KEM algorithm identifier. + IavPrimulaKemHandler(std::unique_ptr executor, common::AlgorithmId algorithm); + ~IavPrimulaKemHandler() override = default; + + IavPrimulaKemHandler(const IavPrimulaKemHandler&) = delete; + IavPrimulaKemHandler& operator=(const IavPrimulaKemHandler&) = delete; + IavPrimulaKemHandler(const IavPrimulaKemHandler&&) = delete; + IavPrimulaKemHandler& operator=(const IavPrimulaKemHandler&&) = delete; + + /// @brief Bind an optional IAV-Primula key to the operation context. + /// + /// A native key is required for decapsulation but not for key generation + /// or encapsulation. + /// + /// @param init_params Context initialization parameters, including an + /// optional bound key handler. + [[nodiscard]] Expected InitializeContext( + const handler::InitializationParams& init_params) override; + /// @brief Generate a KEM key pair and return its public key. + /// + /// The native private key is released before the operation returns. + [[nodiscard]] Expected GenerateKeyPair() override; + /// @brief Encapsulate a shared secret using a public key. + /// + /// The response contains the ciphertext followed by the shared secret. + /// + /// @param request Request containing the peer public key. + [[nodiscard]] Expected Encapsulate( + const common::RequestParameter& request) override; + /// @brief Decapsulate a ciphertext using the bound private key. + /// + /// @param request Request containing the ciphertext. + [[nodiscard]] Expected Decapsulate( + const common::RequestParameter& request) override; + + private: + /// @brief Map the configured algorithm to the corresponding IAV-Primula enum. + /// + /// @return The mapped algorithm, or kUnsupportedAlgorithm if unsupported. + [[nodiscard]] Expected GetAlgorithm() const noexcept; + iav_primula_key_handle* m_key{nullptr}; ///< Non-owning handle borrowed from the bound key handler. +}; + +} // namespace score::crypto::daemon::provider::score_provider::iav_primula + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_KEM_HANDLER_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.cpp new file mode 100644 index 000000000..80788b357 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.cpp @@ -0,0 +1,191 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.hpp" +#include "score/crypto/src/daemon/common/algorithm_info.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/sign/sign_executor.hpp" + +#include "score/mw/log/logging.h" + +#include +#include +#include + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +using common::DaemonErrorCode; +using common::ResponseParameters; + +/// @brief Return the expected signature size for a PQC signature algorithm. +/// +/// @return Signature size in bytes, or zero if the algorithm is not a +/// supported signature algorithm. +std::size_t SignatureSizeForAlgorithm(const common::AlgorithmId& algorithm) +{ + const auto info = common::LookupPqcAlgorithm(algorithm); + if (info.has_value() && (info->kind == common::PqcAlgorithmKind::kSignature)) + { + return info->signature_or_ciphertext_size; + } + return 0U; +} + +IavPrimulaSignHandler::IavPrimulaSignHandler(std::unique_ptr executor, + common::AlgorithmId algorithm) + : ScoreSignHandler{std::move(executor), std::move(algorithm)} +{ +} + +Expected IavPrimulaSignHandler::ValidateAlgorithm() const +{ + if (SignatureSizeForAlgorithm(m_algorithm) == 0U) + { + return make_unexpected(DaemonErrorCode::kUnsupportedAlgorithm); + } + return std::monostate{}; +} + +std::size_t IavPrimulaSignHandler::GetExpectedSignatureSize() const noexcept +{ + return SignatureSizeForAlgorithm(m_algorithm); +} + +Expected IavPrimulaSignHandler::InitializeContext( + const ::score::crypto::daemon::provider::handler::InitializationParams& init_params) +{ + score::mw::log::LogDebug() << "DEBUG: IavPrimulaSignHandler::InitializeContext called with algorithm:" + << m_algorithm; + + // Validate the algorithm and bind the non-owning native key handle required + // for signing. + const auto algorithm_result = ValidateAlgorithm(); + if (!algorithm_result.has_value()) + { + return make_unexpected(algorithm_result.error()); + } + + const auto base_result = ScoreSignHandler::InitializeContext(init_params); + if (!base_result.has_value()) + { + score::mw::log::LogError() << "ERROR: Base signature handler initialization failed in " + "IavPrimulaSignHandler::InitializeContext"; + return make_unexpected(base_result.error()); + } + + if (init_params.bound_key_handler == nullptr) + { + return make_unexpected(DaemonErrorCode::kKeySlotEmpty); + } + + const auto* primula_key = dynamic_cast(init_params.bound_key_handler); + if ((primula_key == nullptr) || (primula_key->GetNativeHandle() == nullptr)) + { + return make_unexpected(DaemonErrorCode::kIncompatibleKeyType); + } + + m_key = primula_key->GetNativeHandle(); + return std::monostate{}; +} + +Expected IavPrimulaSignHandler::Reset() +{ + // Clear the internally owned signature buffer before resetting the base + // handler state. + m_outputBuffer.clear(); + return ScoreSignHandler::Reset(); +} + +Expected IavPrimulaSignHandler::GetSignatureSize() const +{ + // Return the fixed signature size for the configured ML-DSA algorithm. + const auto size = SignatureSizeForAlgorithm(m_algorithm); + if (size == 0U) + { + return make_unexpected(DaemonErrorCode::kUnsupportedAlgorithm); + } + + ResponseParameters response; + response.emplace_back(static_cast(size)); + return response; +} + +Expected IavPrimulaSignHandler::SingleShotSign( + const common::RequestParameter& data, + std::optional output) +{ + const auto algorithm_result = ValidateAlgorithm(); + if (!algorithm_result.has_value()) + { + return make_unexpected(algorithm_result.error()); + } + + const auto* input = std::get_if>(&data); + if (input == nullptr) + { + return make_unexpected(DaemonErrorCode::kInvalidDataType); + } + + if (m_key == nullptr) + { + return make_unexpected(DaemonErrorCode::kKeySlotEmpty); + } + + // Sign the input in a single operation. Use an internally owned buffer + // when no output buffer is provided; otherwise write into the caller's buffer. + const auto expected_signature_length = GetExpectedSignatureSize(); + std::uint8_t* signature_data = nullptr; + const bool allocate_output_buffer = !output.has_value(); + + if (!allocate_output_buffer) + { + auto* output_buffer = std::get_if>(&output.value()); + if (output_buffer == nullptr) + { + return make_unexpected(DaemonErrorCode::kInvalidDataType); + } + if (output_buffer->data() == nullptr || output_buffer->size() < expected_signature_length) + { + return make_unexpected(DaemonErrorCode::kInsufficientBufferSize); + } + signature_data = output_buffer->data(); + } + else + { + m_outputBuffer.clear(); + m_outputBuffer.resize(expected_signature_length); + signature_data = m_outputBuffer.data(); + } + + std::size_t signature_length = expected_signature_length; + const auto status = iav_sign(m_key, input->data(), input->size(), signature_data, &signature_length); + if (status != IavStatusOk || signature_length != expected_signature_length) + { + return make_unexpected(DaemonErrorCode::kAlgorithmExecutionFailed); + } + + // Return owned output for internally allocated storage and a non-owning + // view for caller-provided storage. + ResponseParameters response; + if (allocate_output_buffer) + { + response.emplace_back(common::OwnedBuffer{std::move(m_outputBuffer)}); + } + else + { + response.emplace_back(score::cpp::span{signature_data, signature_length}); + } + return response; +} +} // namespace score::crypto::daemon::provider::score_provider::iav_primula diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.hpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.hpp new file mode 100644 index 000000000..0928e96cd --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.hpp @@ -0,0 +1,79 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file iav_primula_sign_handler.hpp +/// @brief IAV-Primula implementation of the signature handler. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_SIGN_HANDLER_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_SIGN_HANDLER_HPP + +#include "score/crypto/src/daemon/provider/score_provider/operations/sign/score_sign_handler.hpp" +#include "score/iav_primula/include/iav_primula_ffi.h" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +/// @brief IAV-Primula implementation of the provider-neutral signature handler. +/// +/// Supports ML-DSA signing through the IAV-Primula backend. The handler binds +/// a non-owning native key handle and manages the output buffer for +/// single-shot signatures. +class IavPrimulaSignHandler final : public operations::sign::ScoreSignHandler +{ + public: + /// @brief Create an IAV-Primula signature handler. + /// + /// Takes ownership of the operation executor and stores the selected ML-DSA + /// algorithm. + /// + /// @param executor Provider-neutral executor for signature operations. + /// @param algorithm ML-DSA algorithm identifier. + IavPrimulaSignHandler(std::unique_ptr executor, common::AlgorithmId algorithm); + + ~IavPrimulaSignHandler() override = default; + + IavPrimulaSignHandler(const IavPrimulaSignHandler&) = delete; + IavPrimulaSignHandler& operator=(const IavPrimulaSignHandler&) = delete; + IavPrimulaSignHandler(IavPrimulaSignHandler&&) = delete; + IavPrimulaSignHandler& operator=(IavPrimulaSignHandler&&) = delete; + + /// @brief Validate the algorithm and bind the signing key. + /// + /// The bound key must be an IAV-Primula key with a valid native handle. + [[nodiscard]] Expected InitializeContext( + const ::score::crypto::daemon::provider::handler::InitializationParams& init_params) override; + /// @brief Clear the internal output buffer and reset the handler state. + [[nodiscard]] Expected Reset() override; + /// @brief Sign a complete message in a single operation. + /// + /// If output is not provided, the handler allocates an owning response + /// buffer. Otherwise, the caller-provided output buffer is used. + [[nodiscard]] Expected SingleShotSign( + const common::RequestParameter& data, + std::optional output) override; + /// @brief Return the fixed signature size for the configured ML-DSA algorithm. + [[nodiscard]] Expected GetSignatureSize() const override; + + private: + /// @brief Validate that the configured algorithm is a supported ML-DSA algorithm. + [[nodiscard]] Expected ValidateAlgorithm() const; + /// @brief Return the expected signature size for the configured algorithm. + [[nodiscard]] std::size_t GetExpectedSignatureSize() const noexcept; + + iav_primula_key_handle* m_key{nullptr}; ///< Non-owning handle borrowed from the bound key handler. + std::vector m_outputBuffer; ///< Internally owned single-shot signature buffer. +}; + +} // namespace score::crypto::daemon::provider::score_provider::iav_primula + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_SIGN_HANDLER_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.cpp new file mode 100644 index 000000000..95af4aed5 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.cpp @@ -0,0 +1,115 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.hpp" + +#include "score/crypto/src/daemon/common/algorithm_info.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/verify/verify_executor.hpp" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ +namespace +{ +/// @brief Return the expected signature size for a PQC signature algorithm. +/// +/// @return Signature size in bytes, or zero if the algorithm is not a +/// supported signature algorithm. +std::size_t SignatureSize(const common::AlgorithmId& algorithm) +{ + const auto info = common::LookupPqcAlgorithm(algorithm); + return info && info->kind == common::PqcAlgorithmKind::kSignature ? info->signature_or_ciphertext_size : 0U; +} +} // namespace + +IavPrimulaVerifyHandler::IavPrimulaVerifyHandler(std::unique_ptr executor, + common::AlgorithmId algorithm) + : ScoreVerifyHandler{std::move(executor), std::move(algorithm)} +{ +} + +Expected IavPrimulaVerifyHandler::ValidateAlgorithm() const +{ + if (SignatureSize(m_algorithm) == 0U) + { + return make_unexpected(common::DaemonErrorCode::kUnsupportedAlgorithm); + } + return std::monostate{}; +} + +Expected IavPrimulaVerifyHandler::InitializeContext( + const handler::InitializationParams& init_params) +{ + // Validate the algorithm and bind the non-owning native key handle required + // for signature verification. + auto algorithm = ValidateAlgorithm(); + if (!algorithm.has_value()) + { + return make_unexpected(algorithm.error()); + } + auto base = ScoreVerifyHandler::InitializeContext(init_params); + if (!base.has_value()) + { + return make_unexpected(base.error()); + } + if (init_params.bound_key_handler == nullptr) + { + return make_unexpected(common::DaemonErrorCode::kKeySlotEmpty); + } + const auto* key = dynamic_cast(init_params.bound_key_handler); + if (key == nullptr || key->GetNativeHandle() == nullptr) + { + return make_unexpected(common::DaemonErrorCode::kIncompatibleKeyType); + } + m_key = key->GetNativeHandle(); + return std::monostate{}; +} + +Expected IavPrimulaVerifyHandler::SingleShotVerify( + const common::RequestParameter& data, + const common::RequestParameter& signature) +{ + // Verify a complete message and signature in a single operation. + auto algorithm = ValidateAlgorithm(); + if (!algorithm.has_value()) + { + return make_unexpected(algorithm.error()); + } + const auto* message = std::get_if>(&data); + const auto* sig = std::get_if>(&signature); + if (message == nullptr || sig == nullptr) + { + return make_unexpected(common::DaemonErrorCode::kInvalidDataType); + } + if (m_key == nullptr) + { + return make_unexpected(common::DaemonErrorCode::kKeySlotEmpty); + } + if (sig->data() == nullptr || sig->size() != SignatureSize(m_algorithm)) + { + return make_unexpected(common::DaemonErrorCode::kInvalidArgument); + } + const auto status = iav_verify(m_key, message->data(), message->size(), sig->data(), sig->size()); + // Map backend verification status to the handler contract: an invalid + // signature returns false, while backend errors are returned as failures. + if (status == IavStatusVerificationFailed) + { + return false; + } + if (status != IavStatusOk) + { + return make_unexpected(common::DaemonErrorCode::kAlgorithmExecutionFailed); + } + return true; +} +} // namespace score::crypto::daemon::provider::score_provider::iav_primula diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.hpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.hpp new file mode 100644 index 000000000..e5090c002 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.hpp @@ -0,0 +1,76 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file iav_primula_verify_handler.hpp +/// @brief IAV-Primula implementation of the verification handler. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_VERIFY_HANDLER_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_VERIFY_HANDLER_HPP + +#include "score/crypto/src/daemon/provider/score_provider/operations/verify/score_verify_handler.hpp" +#include "score/iav_primula/include/iav_primula_ffi.h" + +namespace score::crypto::daemon::provider::score_provider::iav_primula +{ + +/// @brief IAV-Primula implementation of the provider-neutral verification handler. +/// +/// Supports ML-DSA signature verification through the IAV-Primula backend. +/// The handler binds a non-owning native key handle and performs single-shot +/// verification. +class IavPrimulaVerifyHandler final : public operations::verify::ScoreVerifyHandler +{ + public: + /// @brief Create an IAV-Primula verification handler. + /// + /// Takes ownership of the operation executor and stores the selected ML-DSA + /// algorithm. + /// + /// @param executor Provider-neutral executor for verification operations. + /// @param algorithm ML-DSA algorithm identifier. + IavPrimulaVerifyHandler(std::unique_ptr executor, + common::AlgorithmId algorithm); + ~IavPrimulaVerifyHandler() override = default; + + IavPrimulaVerifyHandler(const IavPrimulaVerifyHandler&) = delete; + IavPrimulaVerifyHandler& operator=(const IavPrimulaVerifyHandler&) = delete; + IavPrimulaVerifyHandler(const IavPrimulaVerifyHandler&&) = delete; + IavPrimulaVerifyHandler& operator=(const IavPrimulaVerifyHandler&&) = delete; + + /// @brief Validate the algorithm and bind the verification key. + /// + /// The bound key must be an IAV-Primula key with a valid native handle. + /// + /// @param init_params Context initialization parameters containing the + /// bound verification key. + [[nodiscard]] Expected InitializeContext( + const handler::InitializationParams& init_params) override; + /// @brief Verify a complete message and signature in a single operation. + /// + /// Returns true for a valid signature, false for an invalid signature, and + /// an error for invalid input, missing keys, or backend failures. + /// + /// @param data Message to verify. + /// @param signature Signature to verify against the message. + [[nodiscard]] Expected SingleShotVerify( + const common::RequestParameter& data, + const common::RequestParameter& signature) override; + + private: + /// @brief Validate that the configured algorithm is a supported ML-DSA algorithm. + [[nodiscard]] Expected ValidateAlgorithm() const; + iav_primula_key_handle* m_key{nullptr}; ///< Non-owning handle borrowed from the bound key handler. +}; +} // namespace score::crypto::daemon::provider::score_provider::iav_primula + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_IAV_PRIMULA_VERIFY_HANDLER_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/BUILD b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/BUILD new file mode 100644 index 000000000..921cf6baa --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/BUILD @@ -0,0 +1,41 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "iav_primula_key_management_test", + srcs = ["iav_primula_key_management_test.cpp"], + deps = [ + "//score/crypto/src/daemon/provider/score_provider:provider_iav_primula_library", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "iav_primula_signature_test", + srcs = ["iav_primula_signature_test.cpp"], + deps = [ + "//score/crypto/src/daemon/provider/score_provider:provider_iav_primula_library", + "//score/crypto/src/daemon/provider/score_provider/operations/sign:score_sign_handler", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "iav_primula_verification_test", + srcs = ["iav_primula_verification_test.cpp"], + deps = [ + "//score/crypto/src/daemon/provider/score_provider:provider_iav_primula_library", + "//score/crypto/src/daemon/provider/score_provider/operations/verify:score_verify_handler", + "@googletest//:gtest_main", + ], +) + +cc_test( + name = "iav_primula_kem_test", + srcs = ["iav_primula_kem_test.cpp"], + deps = [ + "//score/crypto/src/daemon/provider/handler:kem_handler_operations", + "//score/crypto/src/daemon/provider/score_provider:provider_iav_primula_library", + "//score/crypto/src/daemon/provider/score_provider/operations/kem:score_kem_handler", + "@googletest//:gtest_main", + ], +) diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_kem_test.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_kem_test.cpp new file mode 100644 index 000000000..f996ec813 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_kem_test.cpp @@ -0,0 +1,126 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/handler/handler_init_params.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/kem_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/kem/iav_primula_kem_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/kem/kem_executor.hpp" + +#include + +#include + +namespace +{ +using namespace score::crypto::daemon; +using namespace score::crypto::daemon::provider::score_provider::iav_primula; +using Executor = score::crypto::daemon::provider::score_provider::operations::kem::KemExecutor; + +// --------------------------------------------------------------------------- +// Algorithm and input validation +// --------------------------------------------------------------------------- + +TEST(IavPrimulaKemTest, RejectsUnsupportedAlgorithm) +{ + IavPrimulaKemHandler handler{std::make_unique(), "ML-KEM-999"}; + auto result = handler.GenerateKeyPair(); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kUnsupportedAlgorithm); +} + +TEST(IavPrimulaKemTest, PropagatesBackendStatusForKeyGeneration) +{ + IavPrimulaKemHandler handler{std::make_unique(), "ML-KEM-768"}; + auto result = handler.GenerateKeyPair(); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kOperationFailed); +} + +TEST(IavPrimulaKemTest, RejectsInvalidEncapsulationInput) +{ + IavPrimulaKemHandler handler{std::make_unique(), "ML-KEM-768"}; + const std::uint8_t public_key[] = {1U, 2U}; + auto result = handler.Encapsulate(score::cpp::span{public_key, sizeof(public_key)}); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kInvalidArgument); +} + +TEST(IavPrimulaKemTest, RequiresBoundKeyForDecapsulation) +{ + IavPrimulaKemHandler handler{std::make_unique(), "ML-KEM-768"}; + const std::uint8_t ciphertext[] = {1U, 2U}; + auto result = handler.Decapsulate(score::cpp::span{ciphertext, sizeof(ciphertext)}); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kInvalidArgument); +} + +// --------------------------------------------------------------------------- +// Backend status propagation +// --------------------------------------------------------------------------- + +TEST(IavPrimulaKemTest, PropagatesBackendStatusForValidEncapsulation) +{ + IavPrimulaKemHandler handler{std::make_unique(), "ML-KEM-768"}; + // ML-KEM-768 public keys contain 1184 bytes. + const std::vector public_key(1184U, 0U); + auto result = handler.Encapsulate(score::cpp::span{public_key.data(), public_key.size()}); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kOperationFailed); +} + +TEST(IavPrimulaKemTest, PropagatesBackendStatusForValidDecapsulation) +{ + IavPrimulaKemHandler handler{std::make_unique(), "ML-KEM-768"}; + // Use a non-null sentinel to simulate a bound native key handle. The + // backend operation is expected to fail before dereferencing this handle. + IavPrimulaKeyHandler key{reinterpret_cast(0x1), {}, {}}; + ::score::crypto::daemon::provider::handler::InitializationParams params{}; + params.bound_key_handler = &key; + ASSERT_TRUE(handler.InitializeContext(params).has_value()); + // ML-KEM-768 ciphertexts contain 1088 bytes. + const std::vector ciphertext(1088U, 0U); + auto result = handler.Decapsulate(score::cpp::span{ciphertext.data(), ciphertext.size()}); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kOperationFailed); +} + +// --------------------------------------------------------------------------- +// Handler dispatch and context management +// --------------------------------------------------------------------------- + +TEST(IavPrimulaKemTest, DispatchesOperationsAndReset) +{ + // Verify that the handler dispatches both a KEM operation and RESET. + using namespace score::crypto::daemon::provider::handler::kem_handler_operations; + IavPrimulaKemHandler handler{std::make_unique(), "ML-KEM-768"}; + common::OperationIdentifier keygen{0U, KEM_KEYGEN}; + common::RequestParameters no_parameters{}; + auto keygen_result = handler.Execute(keygen, no_parameters); + ASSERT_FALSE(keygen_result.has_value()); + EXPECT_EQ(keygen_result.error(), common::DaemonErrorCode::kOperationFailed); + common::OperationIdentifier reset{0U, KEM_RESET}; + auto reset_result = handler.Execute(reset, no_parameters); + ASSERT_TRUE(reset_result.has_value()); + EXPECT_TRUE(reset_result->empty()); +} + +TEST(IavPrimulaKemTest, AllowsContextWithoutBoundKey) +{ + IavPrimulaKemHandler handler{std::make_unique(), "ML-KEM-768"}; + ::score::crypto::daemon::provider::handler::InitializationParams params{}; + // Context initialization is allowed without a bound key; decapsulation + // validates that a key is available when the operation is executed. + EXPECT_TRUE(handler.InitializeContext(params).has_value()); +} +} // namespace diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_key_management_test.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_key_management_test.cpp new file mode 100644 index 000000000..61b4c9bdd --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_key_management_test.cpp @@ -0,0 +1,96 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_factory.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" + +#include + +namespace +{ +using namespace score::crypto::daemon; +using namespace score::crypto::daemon::provider::score_provider::iav_primula; + +// --------------------------------------------------------------------------- +// Import validation +// --------------------------------------------------------------------------- + +TEST(IavPrimulaKeyManagementTest, RejectsInvalidAlgorithmsAndKeys) +{ + IavPrimulaKeyFactory factory{common::ProviderId{7U}}; + key_management::KeyImportRequest request{}; + + // KEM public-key import validates the same public-key size as ML-DSA. + request.algorithm = "ML-KEM-768"; + std::vector kem_key(1184U); + request.key_data = kem_key.data(); + request.key_data_size = kem_key.size() - 1U; + auto unsupported_result = factory.ImportKey(request); + ASSERT_FALSE(unsupported_result.has_value()); + EXPECT_EQ(unsupported_result.error(), common::DaemonErrorCode::kInvalidArgument); + + // ML-DSA-44 public keys contain 1312 bytes; import one byte less + // deliberately to verify key-size validation. + request.algorithm = "ML-DSA-44"; + std::vector key(1312U); + request.key_data = key.data(); + request.key_data_size = key.size() - 1U; + auto invalid_result = factory.ImportKey(request); + ASSERT_FALSE(invalid_result.has_value()); + EXPECT_EQ(invalid_result.error(), common::DaemonErrorCode::kInvalidArgument); +} + +// --------------------------------------------------------------------------- +// Public-key ownership and lifecycle +// --------------------------------------------------------------------------- + +TEST(IavPrimulaKeyManagementTest, CopiesExportsAndReleasesPublicKey) +{ + key_management::ProviderKeyHandle handle{}; + handle.permissions = score::crypto::KeyOperationPermission::kExport; + // Use synthetic public-key material to verify that the handler copies it. + std::vector public_key(8U, 0xA5U); + IavPrimulaKeyHandler key{nullptr, public_key, handle}; + public_key[0] = 0U; + + // Export must return the copied key data, not the modified source vector. + auto exported = key.Export(); + ASSERT_TRUE(exported.has_value()); + EXPECT_EQ(exported->bytes, std::vector(8U, 0xA5U)); + + // Releasing the key prevents subsequent export operations. + EXPECT_TRUE(key.Release().has_value()); + auto released_export = key.Export(); + ASSERT_FALSE(released_export.has_value()); + EXPECT_EQ(released_export.error(), common::DaemonErrorCode::kKeyOperationNotPermitted); +} + +// --------------------------------------------------------------------------- +// Key-generation dispatch +// --------------------------------------------------------------------------- + +TEST(IavPrimulaKeyManagementTest, RoutesKemKeyGenerationToKemBackendPath) +{ + IavPrimulaKeyFactory factory{common::ProviderId{7U}}; + key_management::KeyGenerationRequest request{}; + request.algorithm = "ML-KEM-768"; + + // The current Rust FFI is a deliberate placeholder and therefore returns + // an operation failure. The call still exercises the KEM-specific branch + // in the factory; once the FFI is implemented this test should be changed + // to assert the generated key metadata and public-key size. + auto result = factory.GenerateKey(request); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kOperationFailed); +} +} // namespace diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_signature_test.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_signature_test.cpp new file mode 100644 index 000000000..a56a91486 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_signature_test.cpp @@ -0,0 +1,75 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/handler/handler_init_params.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/sign/sign_executor.hpp" + +#include + +namespace +{ +using namespace score::crypto::daemon; +using namespace score::crypto::daemon::provider::score_provider::iav_primula; +using Executor = score::crypto::daemon::provider::score_provider::operations::sign::SignExecutor; + +// --------------------------------------------------------------------------- +// Signature algorithm sizes +// --------------------------------------------------------------------------- + +TEST(IavPrimulaSignatureTest, ReportsSupportedSizesAndRejectsKem) +{ + // Expected signature sizes for the supported ML-DSA variants. + const std::pair cases[] = { + // ML-DSA-44: 2420 bytes, ML-DSA-65: 3309 bytes, ML-DSA-87: 4627 bytes. + {"ML-DSA-44", 2420U}, + {"ML-DSA-65", 3309U}, + {"ML-DSA-87", 4627U}}; + for (const auto& test_case : cases) + { + IavPrimulaSignHandler handler{std::make_unique(), test_case.first}; + auto result = handler.GetSignatureSize(); + ASSERT_TRUE(result.has_value()); + EXPECT_EQ(std::get(result->front()), test_case.second); + } + + // A KEM algorithm is not a supported signature algorithm. + IavPrimulaSignHandler unsupported_handler{std::make_unique(), "ML-KEM-768"}; + auto result = unsupported_handler.GetSignatureSize(); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kUnsupportedAlgorithm); +} + +// --------------------------------------------------------------------------- +// Output buffer validation +// --------------------------------------------------------------------------- + +TEST(IavPrimulaSignatureTest, ValidatesOutputBuffer) +{ + IavPrimulaSignHandler handler{std::make_unique(), "ML-DSA-44"}; + // Use a non-null sentinel to simulate a bound native key handle without + // creating a real backend key. + IavPrimulaKeyHandler key{reinterpret_cast(0x1), {}, {}}; + ::score::crypto::daemon::provider::handler::InitializationParams params{}; + params.bound_key_handler = &key; + ASSERT_TRUE(handler.InitializeContext(params).has_value()); + + const std::uint8_t message[] = {1U, 2U}; + // ML-DSA-44 signatures require 2420 bytes; ten bytes are intentionally too small. + std::vector output(10U); + common::RequestParameter input = score::cpp::span{message, 2U}; + auto result = handler.SingleShotSign(input, score::cpp::span{output.data(), output.size()}); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kInsufficientBufferSize); +} +} // namespace diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_verification_test.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_verification_test.cpp new file mode 100644 index 000000000..40f091727 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/iav_primula_verification_test.cpp @@ -0,0 +1,96 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/handler/handler_init_params.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/verify_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/verify/verify_executor.hpp" + +#include + +namespace +{ +using namespace score::crypto::daemon; +using namespace score::crypto::daemon::provider::score_provider::iav_primula; +using Executor = score::crypto::daemon::provider::score_provider::operations::verify::VerifyExecutor; + +// --------------------------------------------------------------------------- +// Algorithm and key validation +// --------------------------------------------------------------------------- + +TEST(IavPrimulaVerificationTest, RejectsUnsupportedAndMissingKeys) +{ + // ML-KEM-768 is not a supported signature-verification algorithm. + IavPrimulaVerifyHandler unsupported_handler{std::make_unique(), "ML-KEM-768"}; + auto unsupported_result = unsupported_handler.InitializeContext({}); + ASSERT_FALSE(unsupported_result.has_value()); + EXPECT_EQ(unsupported_result.error(), common::DaemonErrorCode::kUnsupportedAlgorithm); + + // ML-DSA-44 requires a bound verification key during initialization. + IavPrimulaVerifyHandler handler{std::make_unique(), "ML-DSA-44"}; + auto result = handler.InitializeContext({}); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kKeySlotEmpty); +} + +// --------------------------------------------------------------------------- +// Signature input validation +// --------------------------------------------------------------------------- + +TEST(IavPrimulaVerificationTest, ValidatesKeyTypeAndSignatureSize) +{ + IavPrimulaVerifyHandler handler{std::make_unique(), "ML-DSA-44"}; + // A public-only key has no native handle and cannot be used for this + // verification operation. + IavPrimulaKeyHandler public_key{nullptr, std::vector(1312U), {}}; + ::score::crypto::daemon::provider::handler::InitializationParams params{}; + params.bound_key_handler = &public_key; + auto result = handler.InitializeContext(params); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kIncompatibleKeyType); + + // Use a non-null sentinel to simulate a bound native key handle. + IavPrimulaKeyHandler key{reinterpret_cast(0x1), {}, {}}; + params.bound_key_handler = &key; + ASSERT_TRUE(handler.InitializeContext(params).has_value()); + const std::uint8_t message[] = {1U, 2U}; + // ML-DSA-44 signatures are 2420 bytes; ten bytes are intentionally invalid. + std::vector signature(10U); + common::RequestParameter input = score::cpp::span{message, 2U}; + auto verify = + handler.SingleShotVerify(input, score::cpp::span{signature.data(), signature.size()}); + EXPECT_EQ(verify.error(), common::DaemonErrorCode::kInvalidArgument); +} + +TEST(IavPrimulaVerificationTest, RejectsSingleShotRequestWithoutSignature) +{ + IavPrimulaVerifyHandler handler{std::make_unique(), "ML-DSA-44"}; + IavPrimulaKeyHandler key{reinterpret_cast(0x1), {}, {}}; + ::score::crypto::daemon::provider::handler::InitializationParams params{}; + params.bound_key_handler = &key; + ASSERT_TRUE(handler.InitializeContext(params).has_value()); + + const std::uint8_t message[] = {1U, 2U}; + common::RequestParameters request{ + score::cpp::span{message, 2U}, + }; + common::OperationIdentifier operation{}; + operation.operationAction = + ::score::crypto::daemon::provider::handler::verify_handler_operations::VERIFY_SS; + + auto result = handler.Execute(operation, request); + ASSERT_FALSE(result.has_value()); + EXPECT_EQ(result.error(), common::DaemonErrorCode::kInsufficientParameters); +} +} // namespace diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/integration_tests/BUILD b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/integration_tests/BUILD new file mode 100644 index 000000000..fd4b8782a --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/integration_tests/BUILD @@ -0,0 +1,14 @@ +load("@rules_cc//cc:defs.bzl", "cc_test") + +cc_test( + name = "iav_primula_application_integration_test", + srcs = ["iav_primula_application_integration_test.cpp"], + deps = [ + "//score/crypto/src/daemon/provider/handler:handler_headers", + "//score/crypto/src/daemon/provider/handler:sign_handler_operations", + "//score/crypto/src/daemon/provider/handler:verify_handler_operations", + "//score/crypto/src/daemon/provider/score_provider:provider_iav_primula_library", + "//score/crypto/src/daemon/provider/score_provider/operations/factory:score_handler_factory", + "@googletest//:gtest_main", + ], +) diff --git a/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/integration_tests/iav_primula_application_integration_test.cpp b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/integration_tests/iav_primula_application_integration_test.cpp new file mode 100644 index 000000000..008b6f7b7 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/iav_primula/tests/integration_tests/iav_primula_application_integration_test.cpp @@ -0,0 +1,103 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/handler/handler_init_params.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/sign_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/verify_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/iav_primula_provider.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/key_management/iav_primula_key_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/sign/iav_primula_sign_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/iav_primula/operations/verify/iav_primula_verify_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/factory/score_handler_factory.hpp" + +#include + +namespace +{ +using namespace score::crypto::daemon; +using namespace score::crypto::daemon::provider; +using namespace score::crypto::daemon::provider::score_provider; +using namespace score::crypto::daemon::provider::score_provider::iav_primula; + +// --------------------------------------------------------------------------- +// SIGN handler creation and initialization +// --------------------------------------------------------------------------- + +TEST(IavPrimulaApplicationIntegrationTest, CreatesSignHandlerFromProviderToAlgorithm) +{ + // Initialize the provider with the test provider ID and provider name. + IavPrimulaProvider provider; + ASSERT_TRUE(provider.Initialize(ProviderInitContext{common::ProviderId{7U}, "iavPrimula"})); + + // Create the provider-neutral factory and request an ML-DSA SIGN handler. + auto factory = provider.GetCryptoHandlerFactory(); + ASSERT_NE(factory, nullptr); + auto created = factory->CreateHandler("SIGN", "ML-DSA-44"); + ASSERT_TRUE(created.has_value()); + auto handler = created.value(); + ASSERT_NE(handler, nullptr); + auto* primula_handler = dynamic_cast(handler.get()); + ASSERT_NE(primula_handler, nullptr); + EXPECT_EQ(primula_handler->GetAlgorithm(), "ML-DSA-44"); + + // Use a non-null sentinel to simulate a bound native key handle without + // creating a real backend key. + IavPrimulaKeyHandler key{reinterpret_cast(0x1), {}, {}}; + handler::InitializationParams params{}; + params.bound_key_handler = &key; + ASSERT_TRUE(handler->InitializeContext(params).has_value()); + EXPECT_EQ(primula_handler->GetOperationState(), common::StreamOperationState::IDLE); + + // Initialization without a bound key must be rejected. + auto missing_key_initialization = handler->InitializeContext({}); + ASSERT_FALSE(missing_key_initialization.has_value()); + EXPECT_EQ(missing_key_initialization.error(), common::DaemonErrorCode::kKeySlotEmpty); + provider.Shutdown(); +} + +// --------------------------------------------------------------------------- +// VERIFY handler creation and initialization +// --------------------------------------------------------------------------- + +TEST(IavPrimulaApplicationIntegrationTest, CreatesVerifyHandlerAndReachesAlgorithmValidation) +{ + // Initialize the provider before obtaining its handler factory. + IavPrimulaProvider provider; + ASSERT_TRUE(provider.Initialize(ProviderInitContext{common::ProviderId{7U}, "iavPrimula"})); + + // Create a VERIFY handler for the ML-DSA-44 algorithm. + auto created = provider.GetCryptoHandlerFactory()->CreateHandler("VERIFY", "ML-DSA-44"); + ASSERT_TRUE(created.has_value()); + auto handler = created.value(); + ASSERT_NE(handler, nullptr); + auto* primula_handler = dynamic_cast(handler.get()); + ASSERT_NE(primula_handler, nullptr); + EXPECT_EQ(primula_handler->GetAlgorithm(), "ML-DSA-44"); + + // A non-null sentinel represents a bound native key handle for initialization. + IavPrimulaKeyHandler key{reinterpret_cast(0x1), {}, {}}; + handler::InitializationParams params{}; + params.bound_key_handler = &key; + ASSERT_TRUE(handler->InitializeContext(params).has_value()); + EXPECT_EQ(primula_handler->GetOperationState(), common::StreamOperationState::IDLE); + + // Public-only key material has no native handle and is incompatible with + // the native-key-based verification implementation. + IavPrimulaKeyHandler public_only_key{nullptr, std::vector(1312U), {}}; + params.bound_key_handler = &public_only_key; + auto incompatible_key_initialization = handler->InitializeContext(params); + ASSERT_FALSE(incompatible_key_initialization.has_value()); + EXPECT_EQ(incompatible_key_initialization.error(), common::DaemonErrorCode::kIncompatibleKeyType); + provider.Shutdown(); +} +} // namespace diff --git a/score/crypto/src/daemon/provider/score_provider/operations/factory/score_handler_factory.hpp b/score/crypto/src/daemon/provider/score_provider/operations/factory/score_handler_factory.hpp index 9a71c64d8..a34123b4a 100644 --- a/score/crypto/src/daemon/provider/score_provider/operations/factory/score_handler_factory.hpp +++ b/score/crypto/src/daemon/provider/score_provider/operations/factory/score_handler_factory.hpp @@ -44,22 +44,47 @@ class ScoreHandlerFactory : public handler::ICryptoHandlerFactory ~ScoreHandlerFactory() override = default; - /// Routes to CreateHashHandler, CreateMacHandler, or CreateKeyManagementHandler. + /// @brief Create a handler for the requested operation family. + /// + /// Dispatches to the provider-specific factory method selected by + /// handlerId. Unsupported handler identifiers result in an + /// unsupported-operation error. + /// + /// @param handlerId Operation family to create. + /// @param algorithm Algorithm identifier used by algorithm-specific handlers. ::score::Result CreateHandler(const common::HandlerId& handlerId, const common::AlgorithmId& algorithm) override; protected: - /// Override in concrete provider to create a hash handler. Default returns unsupported. + /// Override in a concrete provider to create a hash handler. + /// The default implementation returns kUnsupportedOperation. [[nodiscard]] virtual ::score::Result CreateHashHandler( const common::AlgorithmId& algorithm); - /// Override in concrete provider to create a MAC handler. Default returns unsupported. + /// Override in a concrete provider to create a MAC handler. + /// The default implementation returns kUnsupportedOperation. [[nodiscard]] virtual ::score::Result CreateMacHandler( const common::AlgorithmId& algorithm); - /// Override in concrete provider to create a key management handler. Default returns unsupported. + /// Override in a concrete provider to create a key management handler. + /// The default implementation returns kUnsupportedOperation. [[nodiscard]] virtual ::score::Result CreateKeyManagementHandler(); + /// Override in a concrete provider to create a signature handler. + /// The default implementation returns kUnsupportedOperation. + [[nodiscard]] virtual ::score::Result CreateSignHandler( + const common::AlgorithmId& algorithm); + + /// Override in a concrete provider to create a signature verification handler. + /// The default implementation returns kUnsupportedOperation. + [[nodiscard]] virtual ::score::Result CreateVerifyHandler( + const common::AlgorithmId& algorithm); + + /// Override in a concrete provider to create a key encapsulation handler. + /// The default implementation returns kUnsupportedOperation. + [[nodiscard]] virtual ::score::Result CreateKemHandler( + const common::AlgorithmId& algorithm); + std::shared_ptr m_key_factory; std::shared_ptr m_slot_handler; key_management::KeyManagementService::Sptr m_km_service; @@ -68,6 +93,9 @@ class ScoreHandlerFactory : public handler::ICryptoHandlerFactory static constexpr const char* HASH = "HASH"; static constexpr const char* MAC = "MAC"; static constexpr const char* KEY_MANAGEMENT = "KEY_MANAGEMENT"; + static constexpr const char* SIGN = "SIGN"; + static constexpr const char* VERIFY = "VERIFY"; + static constexpr const char* KEM = "KEM"; }; } // namespace score::crypto::daemon::provider::score_provider::operations::factory diff --git a/score/crypto/src/daemon/provider/score_provider/operations/factory/src/score_handler_factory.cpp b/score/crypto/src/daemon/provider/score_provider/operations/factory/src/score_handler_factory.cpp index d9c05aca7..ac593bda7 100644 --- a/score/crypto/src/daemon/provider/score_provider/operations/factory/src/score_handler_factory.cpp +++ b/score/crypto/src/daemon/provider/score_provider/operations/factory/src/score_handler_factory.cpp @@ -42,6 +42,18 @@ ::score::Result ScoreHandlerFactory::CreateHandler(const { return CreateKeyManagementHandler(); } + if (handlerId == SIGN) + { + return CreateSignHandler(algorithm); + } + if (handlerId == VERIFY) + { + return CreateVerifyHandler(algorithm); + } + if (handlerId == KEM) + { + return CreateKemHandler(algorithm); + } ::score::result::Error error( static_cast<::score::result::ErrorCode>(::score::crypto::CryptoErrorCode::kUnsupportedOperation), @@ -51,7 +63,7 @@ ::score::Result ScoreHandlerFactory::CreateHandler(const } // --------------------------------------------------------------------------- -// Default implementations — return unsupported +// Default provider hooks — return kUnsupportedOperation when not overridden. // --------------------------------------------------------------------------- ::score::Result ScoreHandlerFactory::CreateHashHandler(const common::AlgorithmId& /*algorithm*/) @@ -81,4 +93,32 @@ ::score::Result ScoreHandlerFactory::CreateKeyManagement return ::score::Result(::score::unexpect, error); } +::score::Result ScoreHandlerFactory::CreateSignHandler(const common::AlgorithmId& /*algorithm*/) +{ + ::score::result::Error error( + static_cast<::score::result::ErrorCode>(::score::crypto::CryptoErrorCode::kUnsupportedOperation), + ::score::crypto::kCryptoErrorDomain, + "Signature handler not supported by this score provider"); + return ::score::Result(::score::unexpect, error); +} + +::score::Result ScoreHandlerFactory::CreateVerifyHandler( + const common::AlgorithmId& /*algorithm*/) +{ + ::score::result::Error error( + static_cast<::score::result::ErrorCode>(::score::crypto::CryptoErrorCode::kUnsupportedOperation), + ::score::crypto::kCryptoErrorDomain, + "Signature verification handler not supported by this score provider"); + return ::score::Result(::score::unexpect, error); +} + +::score::Result ScoreHandlerFactory::CreateKemHandler(const common::AlgorithmId& /*algorithm*/) +{ + ::score::result::Error error( + static_cast<::score::result::ErrorCode>(::score::crypto::CryptoErrorCode::kUnsupportedOperation), + ::score::crypto::kCryptoErrorDomain, + "Key encapsulation handler not supported by this score provider"); + return ::score::Result(::score::unexpect, error); +} + } // namespace score::crypto::daemon::provider::score_provider::operations::factory diff --git a/score/crypto/src/daemon/provider/score_provider/operations/kem/BUILD b/score/crypto/src/daemon/provider/score_provider/operations/kem/BUILD new file mode 100644 index 000000000..735e706c5 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/kem/BUILD @@ -0,0 +1,12 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "score_kem_handler", + srcs = ["src/score_kem_handler.cpp", "src/kem_executor.cpp"], + hdrs = ["score_kem_handler.hpp", "kem_executor.hpp"], + visibility = ["//:__subpackages__"], + deps = [ + "//score/crypto/src/daemon/common", + "//score/crypto/src/daemon/provider/handler:handler_headers", + "//score/crypto/src/daemon/provider/handler:kem_handler_operations", + ], +) diff --git a/score/crypto/src/daemon/provider/score_provider/operations/kem/kem_executor.hpp b/score/crypto/src/daemon/provider/score_provider/operations/kem/kem_executor.hpp new file mode 100644 index 000000000..902d9f6e8 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/kem/kem_executor.hpp @@ -0,0 +1,53 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file kem_executor.hpp +/// @brief Operation dispatcher for provider-neutral KEM handlers. + +#ifndef SCORE_CRYPTO_DAEMON_SCORE_PROVIDER_KEM_EXECUTOR_HPP +#define SCORE_CRYPTO_DAEMON_SCORE_PROVIDER_KEM_EXECUTOR_HPP + +#include "score/crypto/src/common/types.hpp" +#include "score/crypto/src/daemon/common/daemon_error.hpp" +#include "score/crypto/src/daemon/common/types.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::kem +{ + +class ScoreKemHandler; + +/// @brief Stateless dispatcher for one-shot KEM operations. +/// +/// The executor validates operation parameter counts and dispatches key +/// generation, encapsulation, decapsulation, and reset operations to +/// ScoreKemHandler. +class KemExecutor final +{ + public: + /// @brief Execute one KEM operation. + /// + /// Dispatches the operation identified by the operation action to the + /// corresponding ScoreKemHandler method. + /// + /// @param handler KEM handler receiving the operation. + /// @param operation Operation identifier containing the KEM action. + /// @param request Operation parameters. + /// @return Operation response, or a daemon error if the parameters or + /// operation are invalid. + Expected Execute(ScoreKemHandler& handler, + const common::OperationIdentifier& operation, + common::RequestParameters& request); +}; +} // namespace score::crypto::daemon::provider::score_provider::operations::kem + +#endif // SCORE_CRYPTO_DAEMON_SCORE_PROVIDER_KEM_EXECUTOR_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/operations/kem/score_kem_handler.hpp b/score/crypto/src/daemon/provider/score_provider/operations/kem/score_kem_handler.hpp new file mode 100644 index 000000000..4266ae599 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/kem/score_kem_handler.hpp @@ -0,0 +1,86 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file score_kem_handler.hpp +/// @brief Provider-neutral base handler for KEM operations. + +#ifndef SCORE_CRYPTO_DAEMON_SCORE_PROVIDER_KEM_HANDLER_HPP +#define SCORE_CRYPTO_DAEMON_SCORE_PROVIDER_KEM_HANDLER_HPP + +#include "score/crypto/src/daemon/common/daemon_error.hpp" +#include "score/crypto/src/daemon/common/types.hpp" +#include "score/crypto/src/daemon/provider/handler/i_handler.hpp" +#include +#include + +namespace score::crypto::daemon::provider::score_provider::operations::kem +{ + +class KemExecutor; + +/// @brief Provider-neutral base handler for one-shot KEM operations. +/// +/// Delegates operation dispatch to the injected KemExecutor. Concrete +/// provider handlers override the typed KEM methods to implement key +/// generation, encapsulation, and decapsulation. +class ScoreKemHandler : public handler::Handler +{ + public: + /// @brief Create a provider-neutral KEM handler. + /// + /// @param executor Executor used to dispatch KEM operations. + /// @param algorithm Algorithm identifier handled by this instance. + ScoreKemHandler(std::unique_ptr, common::AlgorithmId); + ~ScoreKemHandler() override; + + ScoreKemHandler(const ScoreKemHandler&) = delete; + ScoreKemHandler& operator=(const ScoreKemHandler&) = delete; + ScoreKemHandler(ScoreKemHandler&&) = delete; + ScoreKemHandler& operator=(ScoreKemHandler&&) = delete; + + /// @brief Delegate one KEM operation to the injected executor. + Expected Execute(const common::OperationIdentifier&, + common::RequestParameters&) override; + + /// @brief Initialize the KEM operation context. + Expected InitializeContext(const handler::InitializationParams&) override; + + /// @brief Reset the KEM handler state. + Expected Reset() override; + + /// @brief Generate a KEM key pair. + /// + /// Concrete providers override this method to return the generated public + /// key or provider-specific key-generation output. + virtual Expected GenerateKeyPair(); + + /// @brief Encapsulate a shared secret using a public key. + /// + /// Concrete providers override this method to return the ciphertext and + /// shared secret. + virtual Expected Encapsulate(const common::RequestParameter&); + + /// @brief Decapsulate a ciphertext using the bound private key. + /// + /// Concrete providers override this method to return the shared secret. + virtual Expected Decapsulate(const common::RequestParameter&); + + protected: + common::AlgorithmId m_algorithm; ///< Algorithm handled by this instance. + + private: + std::unique_ptr m_executor; ///< Owns the operation dispatcher. +}; +} // namespace score::crypto::daemon::provider::score_provider::operations::kem + +#endif // SCORE_CRYPTO_DAEMON_SCORE_PROVIDER_KEM_HANDLER_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/operations/kem/src/kem_executor.cpp b/score/crypto/src/daemon/provider/score_provider/operations/kem/src/kem_executor.cpp new file mode 100644 index 000000000..5ed90980c --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/kem/src/kem_executor.cpp @@ -0,0 +1,72 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/operations/kem/kem_executor.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/kem_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/kem/score_kem_handler.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::kem +{ + +namespace ops = ::score::crypto::daemon::provider::handler::kem_handler_operations; + +Expected KemExecutor::Execute( + ScoreKemHandler& handler, + const common::OperationIdentifier& operation, + common::RequestParameters& request) +{ + // Dispatch the KEM operation and enforce the parameter requirements for + // each operation type. + switch (operation.operationAction) + { + case ops::KEM_KEYGEN: + { + if (!request.empty()) + { + return make_unexpected(common::DaemonErrorCode::kInvalidArgument); + } + return handler.GenerateKeyPair(); + } + case ops::KEM_ENCAPSULATE: + { + if (request.empty()) + { + return make_unexpected(common::DaemonErrorCode::kInsufficientParameters); + } + return handler.Encapsulate(request[0]); + } + case ops::KEM_DECAPSULATE: + { + if (request.empty()) + { + return make_unexpected(common::DaemonErrorCode::kInsufficientParameters); + } + return handler.Decapsulate(request[0]); + } + case ops::KEM_RESET: + { + auto reset_result = handler.Reset(); + // Reset does not produce response data; propagate any handler error. + if (!reset_result.has_value()) + { + return make_unexpected(reset_result.error()); + } + return {}; + } + default: + { // Reject operation actions that are not part of the KEM operation namespace. + return make_unexpected(common::DaemonErrorCode::kInvalidOperation); + } + } +} +} // namespace score::crypto::daemon::provider::score_provider::operations::kem diff --git a/score/crypto/src/daemon/provider/score_provider/operations/kem/src/score_kem_handler.cpp b/score/crypto/src/daemon/provider/score_provider/operations/kem/src/score_kem_handler.cpp new file mode 100644 index 000000000..a95761945 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/kem/src/score_kem_handler.cpp @@ -0,0 +1,66 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/operations/kem/score_kem_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/kem/kem_executor.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::kem +{ + +ScoreKemHandler::ScoreKemHandler(std::unique_ptr executor, common::AlgorithmId algorithm) + : m_algorithm{std::move(algorithm)}, m_executor{std::move(executor)} +{ +} + +ScoreKemHandler::~ScoreKemHandler() = default; + +Expected ScoreKemHandler::Execute( + const common::OperationIdentifier& operation, + common::RequestParameters& request) +{ + // Delegate operation dispatch to the injected KEM executor. + return m_executor->Execute(*this, operation, request); +} + +Expected ScoreKemHandler::InitializeContext( + const handler::InitializationParams&) +{ + // The provider-neutral base handler has no context-specific state to initialize. + return {}; +} + +Expected ScoreKemHandler::Reset() +{ + // The provider-neutral base handler has no state to reset. + return {}; +} + +// --------------------------------------------------------------------------- +// Default typed operations — return unsupported unless overridden +// --------------------------------------------------------------------------- + +Expected ScoreKemHandler::GenerateKeyPair() +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} +Expected ScoreKemHandler::Encapsulate( + const common::RequestParameter&) +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} +Expected ScoreKemHandler::Decapsulate( + const common::RequestParameter&) +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} +} // namespace score::crypto::daemon::provider::score_provider::operations::kem diff --git a/score/crypto/src/daemon/provider/score_provider/operations/sign/BUILD b/score/crypto/src/daemon/provider/score_provider/operations/sign/BUILD new file mode 100644 index 000000000..0944bfcb5 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/sign/BUILD @@ -0,0 +1,13 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "score_sign_handler", + srcs = ["src/score_sign_handler.cpp", "src/sign_executor.cpp"], + hdrs = ["score_sign_handler.hpp", "sign_executor.hpp"], + visibility = ["//:__subpackages__"], + deps = [ + "//score/crypto/src/daemon/common", + "//score/crypto/src/daemon/provider/handler:handler_headers", + "//score/crypto/src/daemon/provider/handler:handler_utils_impl", + "//score/crypto/src/daemon/provider/handler:sign_handler_operations", + ], +) diff --git a/score/crypto/src/daemon/provider/score_provider/operations/sign/score_sign_handler.hpp b/score/crypto/src/daemon/provider/score_provider/operations/sign/score_sign_handler.hpp new file mode 100644 index 000000000..61adbabc6 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/sign/score_sign_handler.hpp @@ -0,0 +1,143 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file score_sign_handler.hpp +/// @brief Provider-neutral base handler for signature operations. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_SIGN_SCORE_SIGN_HANDLER_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_SIGN_SCORE_SIGN_HANDLER_HPP + +#include "score/crypto/src/daemon/common/daemon_error.hpp" +#include "score/crypto/src/daemon/common/types.hpp" +#include "score/crypto/src/daemon/provider/handler/i_handler.hpp" +#include +#include +#include + +namespace score::crypto::daemon::provider::score_provider::operations::sign +{ + +/// @brief Forward declaration of the signature-operation executor (m_executor). +class SignExecutor; + +/// @brief Abstract base handler for signature operations under the score interface family. +/// +/// Implements the daemon's Handler interface by delegating Execute() to the +/// injected SignExecutor. Concrete score-interface providers (e.g. OpenSSL, IAV-Primula) +/// inherit from this class and override the typed signature methods. +/// +/// Typed methods default to kUnsupportedOperation so that a partially-implemented +/// provider compiles and returns a clear error at runtime. +/// +/// State management (algorithm, stream operation state) is centralised here. +class ScoreSignHandler : public handler::Handler +{ + public: + using Sptr = std::shared_ptr; + + ScoreSignHandler() = delete; + + /// @brief Create a provider-neutral signature handler. + /// + /// Takes ownership of the operation executor and stores the selected + /// signature algorithm. + /// + /// @param executor Executor used to dispatch signature operations. + /// @param algorithm Algorithm identifier handled by this instance. + ScoreSignHandler(std::unique_ptr executor, const common::AlgorithmId algorithm); + ~ScoreSignHandler() override; + + /// @brief Delegates to the injected executor. + /// + /// @param operation Operation identifier containing the signature action. + /// @param request Operation parameters. + [[nodiscard]] Expected Execute( + const common::OperationIdentifier& operation, + common::RequestParameters& request) override; + + /// @brief Initialize the handler context and reset the stream state to IDLE. + /// + /// @param init_params Context initialization parameters. + [[nodiscard]] Expected InitializeContext( + const handler::InitializationParams& init_params) override; + + /// @brief Reset the intermediate stream state back to IDLE. + [[nodiscard]] Expected Reset() override; + + // ----------------------------------------------------------------------- + // Stream state management + // ----------------------------------------------------------------------- + + /// @brief Return the current signature stream state. + [[nodiscard]] common::StreamOperationState GetOperationState() const noexcept + { + return m_state; + } + + /// @brief Set the current signature stream state. + void SetOperationState(common::StreamOperationState state) noexcept + { + m_state = state; + } + + /// @brief Return the configured signature algorithm. + [[nodiscard]] const common::AlgorithmId& GetAlgorithm() const noexcept + { + return m_algorithm; + } + + // ----------------------------------------------------------------------- + // Typed signature operations — override in concrete provider handlers + // ----------------------------------------------------------------------- + + /// @brief Initialize a signature operation on an existing context. + /// + /// @param initial_data Optional data to include during initialization. + [[nodiscard]] virtual Expected InitSign( + std::optional initial_data); + + /// @brief Add data to the active signature stream. + /// + /// @param data Message data to add to the signature. + [[nodiscard]] virtual Expected UpdateSign( + const common::RequestParameter& data); + + /// @brief Finalize the signature and produce the output. + /// + /// @param final_data Optional final data to add before signing. + /// @param output Optional caller-provided output buffer. + [[nodiscard]] virtual Expected FinalizeSign( + std::optional final_data, + std::optional output); + + /// @brief Perform a single-shot signature without streaming. + /// + /// @param data Message data to sign. + /// @param output Optional caller-provided output buffer. + [[nodiscard]] virtual Expected SingleShotSign( + const common::RequestParameter& data, + std::optional output); + + /// @brief Get the signature size for the current algorithm. + [[nodiscard]] virtual Expected GetSignatureSize() const; + + protected: + common::AlgorithmId m_algorithm; ///< Algorithm handled by this instance. + common::StreamOperationState m_state{common::StreamOperationState::IDLE}; ///< Current streaming state. + + private: + std::unique_ptr m_executor; ///< Owns the operation dispatcher. +}; +} // namespace score::crypto::daemon::provider::score_provider::operations::sign + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_SIGN_SCORE_SIGN_HANDLER_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/operations/sign/sign_executor.hpp b/score/crypto/src/daemon/provider/score_provider/operations/sign/sign_executor.hpp new file mode 100644 index 000000000..4b595187a --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/sign/sign_executor.hpp @@ -0,0 +1,120 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file sign_executor.hpp +/// @brief Operation dispatcher for provider-neutral signature handlers. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_SIGN_SIGN_EXECUTOR_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_SIGN_SIGN_EXECUTOR_HPP + +#include "score/crypto/src/common/types.hpp" +#include "score/crypto/src/daemon/common/daemon_error.hpp" +#include "score/crypto/src/daemon/common/types.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::sign +{ +/// @brief Forward declaration of the provider-neutral signature handler. +class ScoreSignHandler; + +/// @brief Stateless dispatcher for provider-neutral signature operations. +/// +/// Validates operation requests and streaming state transitions, then delegates +/// each operation to the corresponding ScoreSignHandler method. The executor +/// does not store operation state; the state is maintained by the handler. +class SignExecutor final +{ + public: + /// @brief Execute one SIGN operation. + /// + /// Dispatches the operation identified by the operation action to the + /// corresponding ScoreSignHandler method. + /// + /// @param handler Signature handler receiving the operation. + /// @param operation Operation identifier containing the signature action. + /// @param request Operation parameters. + /// @return Operation response, or a daemon error if the parameters, + /// operation, or stream state are invalid. + [[nodiscard]] Expected Execute( + ScoreSignHandler& handler, + const common::OperationIdentifier& operation, + common::RequestParameters& request); + + private: + /// @brief Execute the initialization step of a streaming signature. + /// + /// @param handler Signature handler receiving the operation. + /// @param request Optional initialization data. + /// @return Success, or a daemon error reported by the handler. + [[nodiscard]] Expected ExecuteInit(ScoreSignHandler& handler, + common::RequestParameters& request); + + /// @brief Add data to an active streaming signature. + /// + /// @param handler Signature handler receiving the operation. + /// @param request Request containing the data to be added. + /// @return Success, or a daemon error if the request is invalid or the + /// handler rejects the operation. + [[nodiscard]] Expected ExecuteUpdate(ScoreSignHandler& handler, + common::RequestParameters& request); + + /// @brief Finalize a streaming signature. + /// + /// @param handler Signature handler receiving the operation. + /// @param request Optional output buffer and final data. + /// @return Signature response, or a daemon error reported by the handler. + [[nodiscard]] Expected ExecuteFinalize( + ScoreSignHandler& handler, + common::RequestParameters& request); + + /// @brief Execute a single-shot signature operation. + /// + /// @param handler Signature handler receiving the operation. + /// @param request Input data and an optional output buffer. + /// @return Signature response, or a daemon error if the request is invalid + /// or the handler rejects the operation. + [[nodiscard]] Expected ExecuteSingleShot( + ScoreSignHandler& handler, + common::RequestParameters& request); + + /// @brief Reset the signature handler to its initial stream state. + /// + /// @param handler Signature handler receiving the reset operation. + /// @param request Reset operation parameters, which are unused. + /// @return Success, or a daemon error reported by the handler. + [[nodiscard]] Expected ExecuteReset(ScoreSignHandler& handler, + common::RequestParameters& request); + + /// @brief Query the signature size for the configured algorithm. + /// + /// @param handler Signature handler providing the algorithm-specific size. + /// @param request Operation parameters, which are unused. + /// @return Signature size response, or a daemon error reported by the handler. + [[nodiscard]] Expected GetSignatureSize( + const ScoreSignHandler& handler, + common::RequestParameters& request); + + /// @brief Validate a streaming operation and determine its next state. + /// + /// @param action Streaming operation to validate. + /// @param currentState Current signature stream state. + /// @param nextState Receives the state resulting from the operation. + /// @return Success with the next state, or a daemon error for an invalid + /// operation or state transition. + [[nodiscard]] static Expected ValidateStreamTransition( + common::OperationAction action, + common::StreamOperationState currentState, + common::StreamOperationState& nextState); +}; +} // namespace score::crypto::daemon::provider::score_provider::operations::sign + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_SIGN_SIGN_EXECUTOR_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/operations/sign/src/score_sign_handler.cpp b/score/crypto/src/daemon/provider/score_provider/operations/sign/src/score_sign_handler.cpp new file mode 100644 index 000000000..532414004 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/sign/src/score_sign_handler.cpp @@ -0,0 +1,85 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/operations/sign/score_sign_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/sign/sign_executor.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::sign +{ + +// The handler owns the executor and stores the algorithm used by the provider. +ScoreSignHandler::ScoreSignHandler(std::unique_ptr executor, const common::AlgorithmId algorithm) + : m_algorithm{std::move(algorithm)}, m_executor{std::move(executor)} +{ +} + +ScoreSignHandler::~ScoreSignHandler() = default; + +Expected ScoreSignHandler::Execute( + const common::OperationIdentifier& operation, + common::RequestParameters& request) +{ + // Delegate operation dispatch to the injected signature executor. + return m_executor->Execute(*this, operation, request); +} + +Expected ScoreSignHandler::InitializeContext( + const handler::InitializationParams& /*init_params*/) +{ + // Reset the streaming state when a new handler context is initialized. + m_state = common::StreamOperationState::IDLE; + return std::monostate{}; +} + +Expected ScoreSignHandler::Reset() +{ + // Reset the streaming state; provider-specific resources are reset by + // concrete handlers when they override this method. + m_state = common::StreamOperationState::IDLE; + return std::monostate{}; +} + +// --------------------------------------------------------------------------- +// Default typed operations — return unsupported unless overridden +// --------------------------------------------------------------------------- + +Expected ScoreSignHandler::InitSign( + std::optional /*initial_data*/) +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} + +Expected ScoreSignHandler::UpdateSign(const common::RequestParameter& /*data*/) +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} + +Expected ScoreSignHandler::FinalizeSign( + std::optional /*final_data*/, + std::optional /*output*/) +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} + +Expected ScoreSignHandler::SingleShotSign( + const common::RequestParameter& /*data*/, + std::optional /*output*/) +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} + +Expected ScoreSignHandler::GetSignatureSize() const +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} +} // namespace score::crypto::daemon::provider::score_provider::operations::sign diff --git a/score/crypto/src/daemon/provider/score_provider/operations/sign/src/sign_executor.cpp b/score/crypto/src/daemon/provider/score_provider/operations/sign/src/sign_executor.cpp new file mode 100644 index 000000000..d77919192 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/sign/src/sign_executor.cpp @@ -0,0 +1,247 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/operations/sign/sign_executor.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/sign_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/handler/src/handler_utils.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/sign/score_sign_handler.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::sign +{ +namespace handler = ::score::crypto::daemon::provider::handler; +using common::DaemonErrorCode; +using common::RequestParameters; +using common::ResponseParameters; +using common::StreamOperationState; + +Expected SignExecutor::Execute(ScoreSignHandler& handler, + const common::OperationIdentifier& operationId, + RequestParameters& request) +{ + // Handle operations that do not participate in the streaming state machine. + if (operationId.operationAction == handler::sign_handler_operations::SIGN_GET_SIGNATURE_SIZE) + { + return GetSignatureSize(handler, request); + } + + if (operationId.operationAction == handler::sign_handler_operations::SIGN_RESET) + { + auto result = ExecuteReset(handler, request); + if (!result.has_value()) + { + return make_unexpected(result.error()); + } + return ResponseParameters{}; + } + + // A single-shot signature is only valid while no streaming operation is active. + if (operationId.operationAction == handler::sign_handler_operations::SIGN_SS) + { + StreamOperationState state = handler.GetOperationState(); + if (state != StreamOperationState::IDLE) + { + return make_unexpected(DaemonErrorCode::kOperationInProgress); + } + if (request.empty()) + { + return make_unexpected(DaemonErrorCode::kInsufficientParameters); + } + + return ExecuteSingleShot(handler, request); + } + + // Streaming operations must follow the valid state-machine transition before + // the corresponding handler method is called. + StreamOperationState currentState = handler.GetOperationState(); + StreamOperationState nextState = StreamOperationState::IDLE; + const auto sequenceValidation = ValidateStreamTransition(operationId.operationAction, currentState, nextState); + if (!sequenceValidation.has_value()) + { + return make_unexpected(sequenceValidation.error()); + } + + // Finalization returns a response and updates the state only after success. + if (operationId.operationAction == handler::sign_handler_operations::SIGN_FINALIZE) + { + auto result = ExecuteFinalize(handler, request); + if (result.has_value()) + { + handler.SetOperationState(nextState); + } + return result; + } + + // Initialization and update return no response parameters. The stream state + // is advanced only when the handler accepts the operation. + if (operationId.operationAction == handler::sign_handler_operations::SIGN_INIT) + { + const auto result = ExecuteInit(handler, request); + if (result.has_value()) + { + handler.SetOperationState(nextState); + } + else + { + return make_unexpected(result.error()); + } + } + + if (operationId.operationAction == handler::sign_handler_operations::SIGN_UPDATE) + { + const auto result = ExecuteUpdate(handler, request); + if (result.has_value()) + { + handler.SetOperationState(nextState); + } + else + { + return make_unexpected(result.error()); + } + } + + return ResponseParameters{}; +} + +Expected SignExecutor::ExecuteInit(ScoreSignHandler& handler, + RequestParameters& request) +{ + // Initialization data is optional; a missing or incompatible first + // parameter is treated as no initial data and handled by the provider. + std::optional> initialData; + if (!request.empty()) + { + if (auto* buf = std::get_if>(&request[0])) + { + initialData.emplace(*buf); + } + } + return handler.InitSign(initialData); +} + +Expected SignExecutor::ExecuteUpdate(ScoreSignHandler& handler, + RequestParameters& request) +{ + // UPDATE requires one input buffer containing data for the active stream. + if (request.empty()) + { + return make_unexpected(DaemonErrorCode::kInsufficientParameters); + } + + auto* buf = std::get_if>(&request[0]); + if (buf == nullptr) + { + return make_unexpected(DaemonErrorCode::kInvalidDataType); + } + + return handler.UpdateSign(*buf); +} + +Expected SignExecutor::ExecuteFinalize(ScoreSignHandler& handler, + RequestParameters& request) +{ + // FINALIZE accepts an optional output buffer followed by optional final data. + std::optional> output; + if (!request.empty()) + { + if (auto* buf = std::get_if>(&request[0])) + { + output.emplace(*buf); + } + } + + std::optional> finalData; + if (request.size() > 1) + { + if (auto* buf = std::get_if>(&request[1])) + { + finalData.emplace(*buf); + } + } + + return handler.FinalizeSign(output, finalData); +} + +Expected SignExecutor::ExecuteSingleShot(ScoreSignHandler& handler, + RequestParameters& request) +{ + // SINGLE-SHOT requires input data and may optionally receive an output buffer. + if (request.empty()) + { + return make_unexpected(DaemonErrorCode::kInsufficientParameters); + } + + auto* data = std::get_if>(&request[0]); + if (data == nullptr) + { + return make_unexpected(DaemonErrorCode::kInvalidDataType); + } + + std::optional> output; + if (request.size() > 1) + { + if (auto* buf = std::get_if>(&request[1])) + { + output.emplace(*buf); + } + } + + return handler.SingleShotSign(*data, output); +} + +Expected SignExecutor::ExecuteReset(ScoreSignHandler& handler, + RequestParameters& /*request*/) +{ + // RESET does not consume request parameters; the handler owns the reset logic. + return handler.Reset(); +} + +Expected SignExecutor::GetSignatureSize(const ScoreSignHandler& handler, + RequestParameters& /*request*/) +{ + // The signature size depends only on the configured algorithm. + return handler.GetSignatureSize(); +} + +Expected SignExecutor::ValidateStreamTransition( + const common::OperationAction action, + const StreamOperationState currentState, + StreamOperationState& nextState) +{ + // Map the SIGN action to the generic stream operation used by the + // centralized state-transition validator. + handler::handler_utils::StreamOperation op{}; + if (action == handler::sign_handler_operations::SIGN_INIT) + { + op = handler::handler_utils::StreamOperation::kInit; + } + else if (action == handler::sign_handler_operations::SIGN_UPDATE) + { + op = handler::handler_utils::StreamOperation::kUpdate; + } + else if (action == handler::sign_handler_operations::SIGN_FINALIZE) + { + op = handler::handler_utils::StreamOperation::kFinalize; + } + else + { + return make_unexpected(DaemonErrorCode::kInvalidOperation); + } + const auto result = handler::handler_utils::ValidateStreamOperationSequence(currentState, op); + if (!result.has_value()) + { + return make_unexpected(result.error()); + } + nextState = result.value(); + return std::monostate{}; +} +} // namespace score::crypto::daemon::provider::score_provider::operations::sign diff --git a/score/crypto/src/daemon/provider/score_provider/operations/verify/BUILD b/score/crypto/src/daemon/provider/score_provider/operations/verify/BUILD new file mode 100644 index 000000000..442f0ae31 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/verify/BUILD @@ -0,0 +1,13 @@ +load("@rules_cc//cc:defs.bzl", "cc_library") +cc_library( + name = "score_verify_handler", + srcs = ["src/score_verify_handler.cpp", "src/verify_executor.cpp"], + hdrs = ["score_verify_handler.hpp", "verify_executor.hpp"], + visibility = ["//:__subpackages__"], + deps = [ + "//score/crypto/src/daemon/common", + "//score/crypto/src/daemon/provider/handler:handler_headers", + "//score/crypto/src/daemon/provider/handler:handler_utils_impl", + "//score/crypto/src/daemon/provider/handler:verify_handler_operations", + ], +) diff --git a/score/crypto/src/daemon/provider/score_provider/operations/verify/score_verify_handler.hpp b/score/crypto/src/daemon/provider/score_provider/operations/verify/score_verify_handler.hpp new file mode 100644 index 000000000..dd822cb7a --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/verify/score_verify_handler.hpp @@ -0,0 +1,144 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file score_verify_handler.hpp +/// @brief Provider-neutral base handler for verification operations. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_VERIFY_SCORE_VERIFY_HANDLER_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_VERIFY_SCORE_VERIFY_HANDLER_HPP + +#include "score/crypto/src/daemon/common/daemon_error.hpp" +#include "score/crypto/src/daemon/common/types.hpp" +#include "score/crypto/src/daemon/provider/handler/i_handler.hpp" +#include +#include +#include + +namespace score::crypto::daemon::provider::score_provider::operations::verify +{ +/// @brief Forward declaration of the verification-operation executor. +class VerifyExecutor; + +/// @brief Abstract base handler for verification operations under the score interface family. +/// +/// Implements the daemon's Handler interface by delegating Execute() to the +/// injected VerifyExecutor. Concrete score-interface providers (e.g. OpenSSL, IAV_Primula) +/// inherit from this class and override the typed verification methods. +/// +/// Typed methods default to kUnsupportedOperation so that a partially-implemented +/// provider compiles and returns a clear error at runtime. +/// +/// The handler owns the operation executor and stores the configured algorithm +/// and streaming state. +class ScoreVerifyHandler : public handler::Handler +{ + public: + using Sptr = std::shared_ptr; + + ScoreVerifyHandler() = delete; + + /// @brief Create a provider-neutral verification handler. + /// + /// Takes ownership of the operation executor and stores the selected + /// verification algorithm. + /// + /// @param executor Executor used to dispatch verification operations. + /// @param algorithm Algorithm identifier handled by this instance. + ScoreVerifyHandler(std::unique_ptr executor, const common::AlgorithmId algorithm); + ~ScoreVerifyHandler() override; + + /// @brief Delegates to the injected executor. + /// + /// @param operation Operation identifier containing the verification action. + /// @param request Operation parameters. + [[nodiscard]] Expected Execute( + const common::OperationIdentifier& operation, + common::RequestParameters& request) override; + + /// @brief Initialize the handler context and reset the stream state to IDLE. + /// + /// @param init_params Context initialization parameters. + [[nodiscard]] Expected InitializeContext( + const handler::InitializationParams& init_params) override; + + /// @brief Reset the intermediate verification stream state back to IDLE. + [[nodiscard]] Expected Reset() override; + + // ----------------------------------------------------------------------- + // Stream state management + // ----------------------------------------------------------------------- + + /// @brief Return the current verification stream state. + [[nodiscard]] common::StreamOperationState GetOperationState() const noexcept + { + return m_state; + } + + /// @brief Set the current verification stream state. + void SetOperationState(common::StreamOperationState state) noexcept + { + m_state = state; + } + + /// @brief Return the configured verification algorithm. + [[nodiscard]] const common::AlgorithmId& GetAlgorithm() const noexcept + { + return m_algorithm; + } + + // ----------------------------------------------------------------------- + // Typed verification operations — override in concrete provider handlers + // ----------------------------------------------------------------------- + + /// @brief Initialize a verification operation on an existing context. + /// + /// @param initial_data Optional data to include during initialization. + [[nodiscard]] virtual Expected InitVerify( + std::optional initial_data); + + /// @brief Add data to the active verification stream. + /// + /// @param data Message data to add to the verification. + [[nodiscard]] virtual Expected UpdateVerify( + const common::RequestParameter& data); + + /// @brief Finalize the verification and return the signature result. + /// + /// @param final_data Optional final data to add before verification. + /// @param output Optional signature or output buffer. + /// @return `true` if the signature is valid, `false` if it is invalid, or + /// a daemon error if the verification cannot be performed. + [[nodiscard]] virtual Expected FinalizeVerify( + std::optional final_data, + std::optional output); + + /// @brief Perform single-shot verification without streaming. + /// + /// @param data Message data to verify. + /// @param signature Signature to verify against the message data. + /// @return `true` if the signature is valid, `false` if it is invalid, or + /// a daemon error if the verification cannot be performed. + [[nodiscard]] virtual Expected SingleShotVerify( + const common::RequestParameter& data, + const common::RequestParameter& signature); + + protected: + common::AlgorithmId m_algorithm; ///< Algorithm handled by this instance. + common::StreamOperationState m_state{common::StreamOperationState::IDLE}; ///< Current streaming state. + + private: + std::unique_ptr m_executor; ///< Owns the operation dispatcher. +}; +} // namespace score::crypto::daemon::provider::score_provider::operations::verify + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_VERIFY_SCORE_VERIFY_HANDLER_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/operations/verify/src/score_verify_handler.cpp b/score/crypto/src/daemon/provider/score_provider/operations/verify/src/score_verify_handler.cpp new file mode 100644 index 000000000..66b1ce391 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/verify/src/score_verify_handler.cpp @@ -0,0 +1,91 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/operations/verify/score_verify_handler.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/verify/verify_executor.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::verify +{ + +// The handler owns the executor and stores the algorithm used by the provider. +ScoreVerifyHandler::ScoreVerifyHandler(std::unique_ptr executor, const common::AlgorithmId algorithm) + : m_algorithm{std::move(algorithm)}, m_executor{std::move(executor)} +{ +} + +ScoreVerifyHandler::~ScoreVerifyHandler() = default; + +Expected ScoreVerifyHandler::Execute( + const common::OperationIdentifier& operation, + common::RequestParameters& request) +{ + if (m_executor == nullptr) + { + // A missing executor indicates an invalid handler configuration. + return make_unexpected(common::DaemonErrorCode::kInternalError); + } + + // Delegate operation dispatch to the injected verification executor. + return m_executor->Execute(*this, operation, request); +} + +Expected ScoreVerifyHandler::InitializeContext( + const handler::InitializationParams& /*init_params*/) +{ + // Reset the streaming state when a new handler context is initialized. + m_state = common::StreamOperationState::IDLE; + return std::monostate{}; +} + +Expected ScoreVerifyHandler::Reset() +{ + // Reset the streaming state; provider-specific resources are reset by + // concrete handlers when they override this method. + m_state = common::StreamOperationState::IDLE; + return std::monostate{}; +} + +// --------------------------------------------------------------------------- +// Default typed operations — return unsupported unless overridden +// --------------------------------------------------------------------------- + +Expected ScoreVerifyHandler::InitVerify( + std::optional /*initial_data*/) +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} + +Expected ScoreVerifyHandler::UpdateVerify( + const common::RequestParameter& /*data*/) +{ + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} + +Expected ScoreVerifyHandler::FinalizeVerify( + std::optional /*final_data*/, + std::optional /*output*/) +{ + // The base implementation reports an unsupported operation rather than + // false, which is reserved for an invalid signature after verification. + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} + +Expected ScoreVerifyHandler::SingleShotVerify( + const common::RequestParameter& /*data*/, + const common::RequestParameter& /*signature*/) +{ + // The base implementation reports an unsupported operation rather than + // false, which is reserved for an invalid signature after verification. + return make_unexpected(common::DaemonErrorCode::kUnsupportedOperation); +} +} // namespace score::crypto::daemon::provider::score_provider::operations::verify diff --git a/score/crypto/src/daemon/provider/score_provider/operations/verify/src/verify_executor.cpp b/score/crypto/src/daemon/provider/score_provider/operations/verify/src/verify_executor.cpp new file mode 100644 index 000000000..08873f037 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/verify/src/verify_executor.cpp @@ -0,0 +1,244 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +#include "score/crypto/src/daemon/provider/score_provider/operations/verify/verify_executor.hpp" +#include "score/crypto/src/daemon/provider/handler/operations/verify_handler_operations.hpp" +#include "score/crypto/src/daemon/provider/handler/src/handler_utils.hpp" +#include "score/crypto/src/daemon/provider/score_provider/operations/verify/score_verify_handler.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::verify +{ +namespace handler = ::score::crypto::daemon::provider::handler; +using common::DaemonErrorCode; +using common::RequestParameters; +using common::ResponseParameters; +using common::StreamOperationState; + +Expected VerifyExecutor::Execute(ScoreVerifyHandler& handler, + const common::OperationIdentifier& operationId, + RequestParameters& request) +{ + // RESET does not participate in the streaming state machine. + if (operationId.operationAction == handler::verify_handler_operations::VERIFY_RESET) + { + auto result = ExecuteReset(handler, request); + if (!result.has_value()) + { + return make_unexpected(result.error()); + } + return ResponseParameters{}; + } + + // A single-shot verification is only valid while no streaming operation is active. + if (operationId.operationAction == handler::verify_handler_operations::VERIFY_SS) + { + StreamOperationState state = handler.GetOperationState(); + if (state != StreamOperationState::IDLE) + { + return make_unexpected(DaemonErrorCode::kOperationInProgress); + } + return ExecuteSingleShot(handler, request); + } + + // Streaming operations must follow the valid state-machine transition before + // the corresponding handler method is called. + StreamOperationState currentState = handler.GetOperationState(); + StreamOperationState nextState = StreamOperationState::IDLE; + const auto sequenceValidation = ValidateStreamTransition(operationId.operationAction, currentState, nextState); + if (!sequenceValidation.has_value()) + { + return make_unexpected(sequenceValidation.error()); + } + + // Finalization returns the verification result and updates the state only + // after successful handler execution. + if (operationId.operationAction == handler::verify_handler_operations::VERIFY_FINALIZE) + { + auto result = ExecuteFinalize(handler, request); + if (result.has_value()) + { + handler.SetOperationState(nextState); + } + return result; + } + + // Initialization and update return no response parameters. The stream state + // is advanced only when the handler accepts the operation. + + if (operationId.operationAction == handler::verify_handler_operations::VERIFY_FINALIZE) + { + auto result = ExecuteInit(handler, request); + if (result.has_value()) + { + handler.SetOperationState(nextState); + } + else + { + return make_unexpected(result.error()); + } + } + + if (operationId.operationAction == handler::verify_handler_operations::VERIFY_UPDATE) + { + auto result = ExecuteUpdate(handler, request); + if (result.has_value()) + { + handler.SetOperationState(nextState); + } + else + { + return make_unexpected(result.error()); + } + } + + const auto result = [&]() -> Expected { + if (operationId.operationAction == handler::verify_handler_operations::VERIFY_INIT) + { + return ExecuteInit(handler, request); + } + if (operationId.operationAction == handler::verify_handler_operations::VERIFY_UPDATE) + { + return ExecuteUpdate(handler, request); + } + return make_unexpected(DaemonErrorCode::kInvalidOperation); + }(); + + if (result.has_value()) + { + handler.SetOperationState(nextState); + } + else + { + return make_unexpected(result.error()); + } + + return ResponseParameters{}; +} + +Expected VerifyExecutor::ExecuteInit(ScoreVerifyHandler& handler, + RequestParameters& request) +{ + // Initialization data is optional and is forwarded when it has the + // expected byte-span type. + std::optional> initialData; + if (!request.empty()) + { + if (auto* buf = std::get_if>(&request[0])) + { + initialData.emplace(*buf); + } + } + return handler.InitVerify(initialData); +} + +Expected VerifyExecutor::ExecuteUpdate(ScoreVerifyHandler& handler, + RequestParameters& request) +{ + // UPDATE requires one input buffer containing data for the active stream. + if (request.empty()) + { + return make_unexpected(DaemonErrorCode::kInsufficientParameters); + } + + auto* buf = std::get_if>(&request[0]); + if (buf == nullptr) + { + return make_unexpected(DaemonErrorCode::kInvalidDataType); + } + + return handler.UpdateVerify(*buf); +} + +Expected VerifyExecutor::ExecuteFinalize(ScoreVerifyHandler& handler, + RequestParameters& request) +{ + // FINALIZE accepts the signature data and optional final streaming data. + std::optional> output; + if (!request.empty()) + { + if (auto* buf = std::get_if>(&request[0])) + { + output.emplace(*buf); + } + } + + std::optional> finalData; + if (request.size() > 1) + { + if (auto* buf = std::get_if>(&request[1])) + { + finalData.emplace(*buf); + } + } + + return handler.FinalizeVerify(output, finalData); +} + +Expected VerifyExecutor::ExecuteSingleShot(ScoreVerifyHandler& handler, + RequestParameters& request) +{ + // SINGLE-SHOT forwards the verification data and signature to the handler. + if (request.size() < 2U) + { + return make_unexpected(DaemonErrorCode::kInsufficientParameters); + } + + auto* data = std::get_if>(&request[0]); + if (data == nullptr) + { + return make_unexpected(DaemonErrorCode::kInvalidDataType); + } + + return handler.SingleShotVerify(*data, request[1]); +} + +Expected VerifyExecutor::ExecuteReset(ScoreVerifyHandler& handler, + RequestParameters& /*request*/) +{ + // RESET does not consume request parameters; the handler owns the reset logic. + return handler.Reset(); +} + +Expected VerifyExecutor::ValidateStreamTransition( + const common::OperationAction action, + const StreamOperationState currentState, + StreamOperationState& nextState) +{ + // Map the VERIFY action to the generic stream operation used by the + // centralized state-transition validator. + handler::handler_utils::StreamOperation op{}; + if (action == handler::verify_handler_operations::VERIFY_INIT) + { + op = handler::handler_utils::StreamOperation::kInit; + } + else if (action == handler::verify_handler_operations::VERIFY_UPDATE) + { + op = handler::handler_utils::StreamOperation::kUpdate; + } + else if (action == handler::verify_handler_operations::VERIFY_FINALIZE) + { + op = handler::handler_utils::StreamOperation::kFinalize; + } + else + { + return make_unexpected(DaemonErrorCode::kInvalidOperation); + } + const auto result = handler::handler_utils::ValidateStreamOperationSequence(currentState, op); + if (!result.has_value()) + { + return make_unexpected(result.error()); + } + nextState = result.value(); + return std::monostate{}; +} +} // namespace score::crypto::daemon::provider::score_provider::operations::verify diff --git a/score/crypto/src/daemon/provider/score_provider/operations/verify/verify_executor.hpp b/score/crypto/src/daemon/provider/score_provider/operations/verify/verify_executor.hpp new file mode 100644 index 000000000..0630508d1 --- /dev/null +++ b/score/crypto/src/daemon/provider/score_provider/operations/verify/verify_executor.hpp @@ -0,0 +1,111 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file verify_executor.hpp +/// @brief Operation dispatcher for provider-neutral verification handlers. + +#ifndef SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_VERIFY_VERIFY_EXECUTOR_HPP +#define SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_VERIFY_VERIFY_EXECUTOR_HPP + +#include "score/crypto/src/common/types.hpp" +#include "score/crypto/src/daemon/common/daemon_error.hpp" +#include "score/crypto/src/daemon/common/types.hpp" + +namespace score::crypto::daemon::provider::score_provider::operations::verify +{ +/// @brief Forward declaration of the provider-neutral verification handler. +class ScoreVerifyHandler; + +/// @brief Stateless dispatcher for provider-neutral verification operations. +/// +/// Validates operation requests and streaming state transitions, then delegates +/// each operation to the corresponding ScoreVerifyHandler method. The executor +/// does not store operation state; the state is maintained by the handler. +class VerifyExecutor final +{ + public: + /// @brief Execute one VERIFY operation. + /// + /// Dispatches the operation identified by the operation action to the + /// corresponding ScoreVerifyHandler method. + /// + /// @param handler Verification handler receiving the operation. + /// @param operation Operation identifier containing the verification action. + /// @param request Operation parameters. + /// @return Verification response, or a daemon error if the parameters, + /// operation, or stream state are invalid. + [[nodiscard]] Expected Execute( + ScoreVerifyHandler& handler, + const common::OperationIdentifier& operation, + common::RequestParameters& request); + + private: + /// @brief Execute the initialization step of a streaming verification. + /// + /// @param handler Verification handler receiving the operation. + /// @param request Optional initialization data. + /// @return Success, or a daemon error reported by the handler. + [[nodiscard]] Expected ExecuteInit(ScoreVerifyHandler& handler, + common::RequestParameters& request); + + /// @brief Add data to an active streaming verification. + /// + /// @param handler Verification handler receiving the operation. + /// @param request Request containing the data to be verified. + /// @return Success, or a daemon error if the request is invalid or the + /// handler rejects the operation. + [[nodiscard]] Expected ExecuteUpdate(ScoreVerifyHandler& handler, + common::RequestParameters& request); + + /// @brief Finalize a streaming verification. + /// + /// @param handler Verification handler receiving the operation. + /// @param request Signature and optional final data or output parameters. + /// @return Verification result, or a daemon error reported by the handler. + [[nodiscard]] Expected ExecuteFinalize( + ScoreVerifyHandler& handler, + common::RequestParameters& request); + + /// @brief Execute a single-shot verification operation. + /// + /// @param handler Verification handler receiving the operation. + /// @param request Verification data and signature parameters. + /// @return Verification result, or a daemon error if the request is invalid + /// or the handler rejects the operation. + [[nodiscard]] Expected ExecuteSingleShot( + ScoreVerifyHandler& handler, + common::RequestParameters& request); + + /// @brief Reset the verification handler to its initial stream state. + /// + /// @param handler Verification handler receiving the reset operation. + /// @param request Reset operation parameters, which are unused. + /// @return Success, or a daemon error reported by the handler. + [[nodiscard]] Expected ExecuteReset(ScoreVerifyHandler& handler, + common::RequestParameters& request); + + /// @brief Validate a streaming operation and determine its next state. + /// + /// @param action Streaming operation to validate. + /// @param currentState Current verification stream state. + /// @param nextState Receives the state resulting from the operation. + /// @return Success with the next state, or a daemon error for an invalid + /// operation or state transition. + [[nodiscard]] static Expected ValidateStreamTransition( + common::OperationAction action, + common::StreamOperationState currentState, + common::StreamOperationState& nextState); +}; +} // namespace score::crypto::daemon::provider::score_provider::operations::verify + +#endif // SCORE_CRYPTO_DAEMON_PROVIDER_SCORE_PROVIDER_OPERATIONS_VERIFY_VERIFY_EXECUTOR_HPP diff --git a/score/crypto/src/daemon/provider/score_provider/score_provider_config.hpp b/score/crypto/src/daemon/provider/score_provider/score_provider_config.hpp index 8b7478918..1f042df0d 100644 --- a/score/crypto/src/daemon/provider/score_provider/score_provider_config.hpp +++ b/score/crypto/src/daemon/provider/score_provider/score_provider_config.hpp @@ -39,7 +39,7 @@ struct ScoreProviderEntry { /// Provider name used to register and look up this provider in ProviderManager. std::string providerName{}; - /// Implementation tag that selects the concrete factory, e.g. "openssl". + /// Implementation tag that selects the concrete factory, e.g. "openssl" or "primula". std::string providerImpl{}; /// Provider type (SOFTWARE, HARDWARE, etc.) /// Note: Uses string to avoid including common/types.hpp in config header diff --git a/score/iav_primula/BUILD b/score/iav_primula/BUILD index c7c2aad84..69bf8a6e0 100644 --- a/score/iav_primula/BUILD +++ b/score/iav_primula/BUILD @@ -11,12 +11,32 @@ # SPDX-License-Identifier: Apache-2.0 # ******************************************************************************* -load("@rules_rust//rust:defs.bzl", "rust_library") +load("@rules_rust//rust:defs.bzl", "rust_library", "rust_static_library") rust_library( name = "iav_primula", - srcs = ["src/main.rs"], + srcs = [ + "src/ffi.rs", + ], crate_name = "iav_primula", + crate_root = "src/ffi.rs", edition = "2021", visibility = ["//visibility:public"], ) + +rust_static_library( + name = "iav_primula_staticlib", + srcs = [ + "src/ffi.rs", + ], + crate_name = "iav_primula", + crate_root = "src/ffi.rs", + edition = "2021", + visibility = ["//visibility:public"], +) + +cc_library( + name = "iav_primula_ffi_headers", + hdrs = ["include/iav_primula_ffi.h"], + visibility = ["//visibility:public"], +) diff --git a/score/iav_primula/include/iav_primula_ffi.h b/score/iav_primula/include/iav_primula_ffi.h new file mode 100644 index 000000000..f84f3e4ab --- /dev/null +++ b/score/iav_primula/include/iav_primula_ffi.h @@ -0,0 +1,188 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +/// @file iav_primula_ffi.h +/// @brief Stable C ABI between the C++ daemon and the Rust PQC backend. + +#ifndef SCORE_IAV_PRIMULA_FFI_H +#define SCORE_IAV_PRIMULA_FFI_H + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/// @brief Status codes returned by the IAV-Primula C ABI. +/// +/// The numeric values are part of the stable ABI and must not be changed. +typedef enum iav_status +{ + IavStatusOk = 0, ///< The operation completed successfully. + IavStatusInvalidArgument = 1, ///< One or more arguments are invalid. + IavStatusBufferTooSmall = 2, ///< An output buffer cannot hold the result. + IavStatusUnsupportedAlgorithm = 3, ///< The requested algorithm is not supported. + IavStatusVerificationFailed = 4, ///< Verification completed and the signature is invalid. + IavStatusCryptoFailure = 5 ///< The backend failed to perform the cryptographic operation. +} iav_status; + +/// @brief Algorithms understood by the IAV-Primula C ABI. +/// +/// The numeric values are part of the stable ABI and must not be changed. +typedef enum iav_algorithm +{ + IavAlgorithmMlDsa44 = 1, ///< ML-DSA-44 signature algorithm. + IavAlgorithmMlDsa65 = 2, ///< ML-DSA-65 signature algorithm. + IavAlgorithmMlDsa87 = 3, ///< ML-DSA-87 signature algorithm. + IavAlgorithmMlKem512 = 10, ///< ML-KEM-512 key-encapsulation algorithm. + IavAlgorithmMlKem768 = 11, ///< ML-KEM-768 key-encapsulation algorithm. + IavAlgorithmMlKem1024 = 12 ///< ML-KEM-1024 key-encapsulation algorithm. +} iav_algorithm; + +/// @brief Opaque backend-owned key handle. +/// +/// The handle contents are private to the backend and must not be inspected +/// or modified by callers. Handles returned by generation functions must be +/// released with iav_key_destroy(). +typedef struct iav_primula_key_handle iav_primula_key_handle; + +// --------------------------------------------------------------------------- +// Signature key management +// --------------------------------------------------------------------------- + +/// @brief Generate a signature key pair. +/// +/// @param algorithm Signature algorithm to use. +/// @param key Output pointer receiving the generated key handle. +/// @return IavStatusOk on success, or an error status if generation fails. +iav_status iav_keypair_generate(iav_algorithm algorithm, iav_primula_key_handle** key); + +/// @brief Export the public key associated with a signature key handle. +/// +/// @param key Signature key handle owned by the caller. +/// @param output Caller-provided output buffer for the public key. +/// @param output_len Input buffer capacity and output length written. +/// @return IavStatusOk on success, IavStatusBufferTooSmall when the +/// output buffer is insufficient, or another error status. +iav_status iav_public_key_export(const iav_primula_key_handle* key, uint8_t* output, size_t* output_len); + +// --------------------------------------------------------------------------- +// Signature operations +// --------------------------------------------------------------------------- + +/// @brief Sign a message with a generated signature key. +/// +/// @param key Signature key handle owned by the caller. +/// @param message Message data to sign. +/// @param message_len Number of bytes in message. +/// @param signature Caller-provided output buffer for the signature. +/// @param signature_len Input buffer capacity and output signature length. +/// @return IavStatusOk on success, IavStatusBufferTooSmall when the +/// signature buffer is insufficient, or another error status. +iav_status iav_sign(const iav_primula_key_handle* key, + const uint8_t* message, + size_t message_len, + uint8_t* signature, + size_t* signature_len); + +/// @brief Verify a message signature. +/// +/// @param key Signature key handle owned by the caller. +/// @param message Message data to verify. +/// @param message_len Number of bytes in message. +/// @param signature Signature to verify. +/// @param signature_len Number of bytes in signature. +/// @return IavStatusOk when the signature is valid, +/// IavStatusVerificationFailed when it is invalid, or another +/// error status when verification cannot be performed. +iav_status iav_verify(const iav_primula_key_handle* key, + const uint8_t* message, + size_t message_len, + const uint8_t* signature, + size_t signature_len); + +// --------------------------------------------------------------------------- +// KEM key management +// --------------------------------------------------------------------------- + +/// @brief Generate a KEM key pair. +/// +/// @param algorithm KEM algorithm to use. +/// @param key Output pointer receiving the generated key handle. +/// @return IavStatusOk on success, or an error status if generation fails. +iav_status iav_kem_keypair_generate(iav_algorithm algorithm, iav_primula_key_handle** key); + +/// @brief Export the public key associated with a KEM key handle. +/// +/// @param key KEM key handle owned by the caller. +/// @param output Caller-provided output buffer for the public key. +/// @param output_len Input buffer capacity and output length written. +/// @return IavStatusOk on success, IavStatusBufferTooSmall when the +/// output buffer is insufficient, or another error status. +iav_status iav_kem_public_key_export(const iav_primula_key_handle* key, uint8_t* output, size_t* output_len); + +// --------------------------------------------------------------------------- +// KEM operations +// --------------------------------------------------------------------------- + +/// @brief Encapsulate a shared secret using a public KEM key. +/// +/// @param algorithm KEM algorithm to use. +/// @param public_key Public key used for encapsulation. +/// @param public_key_len Number of bytes in public_key. +/// @param ciphertext Caller-provided output buffer for the ciphertext. +/// @param ciphertext_len Input capacity and output ciphertext length. +/// @param shared_secret Caller-provided output buffer for the shared secret. +/// @param shared_secret_len Input capacity and output shared-secret length. +/// @return IavStatusOk on success, IavStatusBufferTooSmall when an +/// output buffer is insufficient, or another error status. +iav_status iav_kem_encapsulate(iav_algorithm algorithm, + const uint8_t* public_key, + size_t public_key_len, + uint8_t* ciphertext, + size_t* ciphertext_len, + uint8_t* shared_secret, + size_t* shared_secret_len); + +/// @brief Decapsulate a shared secret using a private KEM key. +/// +/// @param key KEM key handle owned by the caller. +/// @param ciphertext Ciphertext to decapsulate. +/// @param ciphertext_len Number of bytes in ciphertext. +/// @param shared_secret Caller-provided output buffer for the shared secret. +/// @param shared_secret_len Input capacity and output shared-secret length. +/// @return IavStatusOk on success, IavStatusBufferTooSmall when the +/// output buffer is insufficient, or another error status. +iav_status iav_kem_decapsulate(const iav_primula_key_handle* key, + const uint8_t* ciphertext, + size_t ciphertext_len, + uint8_t* shared_secret, + size_t* shared_secret_len); + +// --------------------------------------------------------------------------- +// Resource cleanup +// --------------------------------------------------------------------------- + +/// @brief Destroy a key handle and release its backend resources. +/// +/// The handle must not be used after this function returns. +/// +/// @param key Key handle previously returned by a key-generation function. +void iav_key_destroy(iav_primula_key_handle* key); + +#ifdef __cplusplus +} +#endif + +#endif // SCORE_IAV_PRIMULA_FFI_H diff --git a/score/iav_primula/src/ffi.rs b/score/iav_primula/src/ffi.rs new file mode 100644 index 000000000..ea22490e6 --- /dev/null +++ b/score/iav_primula/src/ffi.rs @@ -0,0 +1,206 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +//! Rust side of the stable C ABI for the IAV-Primula PQC backend. +//! +//! Cryptographic implementation types are intentionally not exposed to C++. +//! This module owns the opaque Rust key handle and translates failures to +//! stable `iav_status` values. The cryptographic entry points currently use +//! placeholder implementations and return `IavStatusUnsupportedAlgorithm`. + +use core::ffi::c_void; + +#[repr(C)] +/// Opaque key handle exposed through the C ABI. +/// +/// The handle contents are private to Rust and must not be accessed by C++ +/// callers. +pub struct iav_primula_key_handle { + _private: [u8; 0], +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// Status codes exchanged through the C ABI. +/// +/// The numeric values must remain synchronized with `iav_primula_ffi.h`. +pub enum iav_status { + IavStatusOk = 0, // Operation completed successfully. + IavStatusInvalidArgument = 1, // One or more arguments are invalid. + IavStatusBufferTooSmall = 2, // An output buffer cannot hold the result. + IavStatusUnsupportedAlgorithm = 3, // The requested algorithm is not supported. + IavStatusVerificationFailed = 4, // Verification completed and the signature is invalid. + IavStatusCryptoFailure = 5, // The backend failed during a cryptographic operation. +} + +#[repr(C)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +/// Algorithms exchanged through the C ABI. +/// +/// The numeric values must remain synchronized with `iav_primula_ffi.h`. +pub enum iav_algorithm { + IavAlgorithmMlDsa44 = 1, // ML-DSA-44 signature algorithm. + IavAlgorithmMlDsa65 = 2, // ML-DSA-65 signature algorithm. + IavAlgorithmMlDsa87 = 3, // ML-DSA-87 signature algorithm. + IavAlgorithmMlKem512 = 10, // ML-KEM-512 key-encapsulation algorithm. + IavAlgorithmMlKem768 = 11, // ML-KEM-768 key-encapsulation algorithm. + IavAlgorithmMlKem1024 = 12, // ML-KEM-1024 key-encapsulation algorithm. +} + +/// Return the status used by the currently unimplemented backend operations. +fn unsupported() -> iav_status { + iav_status::IavStatusUnsupportedAlgorithm +} + +// The `extern "C"` ABI and `no_mangle` attribute preserve the C-compatible +// calling convention and exported symbol names declared in iav_primula_ffi.h. + +// --------------------------------------------------------------------------- +// Signature key management +// --------------------------------------------------------------------------- + +/// Generate a signature key pair. +/// +/// This entry point is currently a placeholder and returns +/// `IavStatusUnsupportedAlgorithm`. +#[no_mangle] +pub extern "C" fn iav_keypair_generate( + _algorithm: iav_algorithm, + _key: *mut *mut iav_primula_key_handle, +) -> iav_status { + unsupported() +} + +/// Export a signature public key. +/// +/// This entry point is currently a placeholder and returns +/// `IavStatusUnsupportedAlgorithm`. +#[no_mangle] +pub extern "C" fn iav_public_key_export( + _key: *const iav_primula_key_handle, + _output: *mut u8, + _output_len: *mut usize, +) -> iav_status { + unsupported() +} + +// --------------------------------------------------------------------------- +// KEM key management +// --------------------------------------------------------------------------- + +/// Generate a KEM key pair. +/// +/// This entry point is currently a placeholder and returns +/// `IavStatusUnsupportedAlgorithm`. +#[no_mangle] +pub extern "C" fn iav_kem_keypair_generate( + _algorithm: iav_algorithm, + _key: *mut *mut iav_primula_key_handle, +) -> iav_status { + unsupported() +} + +/// Export a KEM public key. +/// +/// This entry point is currently a placeholder and returns +/// `IavStatusUnsupportedAlgorithm`. +#[no_mangle] +pub extern "C" fn iav_kem_public_key_export( + _key: *const iav_primula_key_handle, + _output: *mut u8, + _output_len: *mut usize, +) -> iav_status { + unsupported() +} + +// --------------------------------------------------------------------------- +// Signature operations +// --------------------------------------------------------------------------- + +/// Sign a message with a signature key. +/// +/// This entry point is currently a placeholder and returns +/// `IavStatusUnsupportedAlgorithm`. +#[no_mangle] +pub extern "C" fn iav_sign( + _key: *const iav_primula_key_handle, + _message: *const u8, + _message_len: usize, + _signature: *mut u8, + _signature_len: *mut usize, +) -> iav_status { + unsupported() +} + +/// Verify a message signature. +/// +/// This entry point is currently a placeholder and returns +/// `IavStatusUnsupportedAlgorithm`. +#[no_mangle] +pub extern "C" fn iav_verify( + _key: *const iav_primula_key_handle, + _message: *const u8, + _message_len: usize, + _signature: *const u8, + _signature_len: usize, +) -> iav_status { + unsupported() +} + +// --------------------------------------------------------------------------- +// KEM operations +// --------------------------------------------------------------------------- + +/// Encapsulate a shared secret using a public KEM key. +/// +/// This entry point is currently a placeholder and returns +/// `IavStatusUnsupportedAlgorithm`. +#[no_mangle] +pub extern "C" fn iav_kem_encapsulate( + _algorithm: iav_algorithm, + _public_key: *const u8, + _public_key_len: usize, + _ciphertext: *mut u8, + _ciphertext_len: *mut usize, + _shared_secret: *mut u8, + _shared_secret_len: *mut usize, +) -> iav_status { + unsupported() +} + +/// Decapsulate a shared secret using a private KEM key. +/// +/// This entry point is currently a placeholder and returns +/// `IavStatusUnsupportedAlgorithm`. +#[no_mangle] +pub extern "C" fn iav_kem_decapsulate( + _key: *const iav_primula_key_handle, + _ciphertext: *const u8, + _ciphertext_len: usize, + _shared_secret: *mut u8, + _shared_secret_len: *mut usize, +) -> iav_status { + unsupported() +} + +// --------------------------------------------------------------------------- +// Resource cleanup +// --------------------------------------------------------------------------- + +/// Destroy a key handle and release its backend resources. +#[no_mangle] +pub extern "C" fn iav_key_destroy(_key: *mut iav_primula_key_handle) {} + +/// Keep the opaque pointer type referenced without exposing its representation. +#[allow(dead_code)] +fn _opaque_pointer_marker(_: *const c_void) {} diff --git a/score/iav_primula/src/main.rs b/score/iav_primula/src/main.rs deleted file mode 100644 index e0f149a92..000000000 --- a/score/iav_primula/src/main.rs +++ /dev/null @@ -1,16 +0,0 @@ -// ******************************************************************************* -// Copyright (c) 2026 Contributors to the Eclipse Foundation -// -// See the NOTICE file(s) distributed with this work for additional -// information regarding copyright ownership. -// -// This program and the accompanying materials are made available under the -// terms of the Apache License Version 2.0 which is available at -// -// -// SPDX-License-Identifier: Apache-2.0 -// ******************************************************************************* - -pub fn get_hello_message() -> &'static str { - "Hello World from iav_primula" -} diff --git a/score/iav_primula/tests/BUILD b/score/iav_primula/tests/BUILD index 1bf65a3d9..836673b15 100644 --- a/score/iav_primula/tests/BUILD +++ b/score/iav_primula/tests/BUILD @@ -14,8 +14,8 @@ load("@rules_rust//rust:defs.bzl", "rust_test") rust_test( - name = "hello_test", - srcs = ["hello_test.rs"], + name = "ffi_test", + srcs = ["ffi_test.rs"], edition = "2021", deps = ["//score/iav_primula"], ) diff --git a/score/iav_primula/tests/ffi_test.rs b/score/iav_primula/tests/ffi_test.rs new file mode 100644 index 000000000..d03aaf678 --- /dev/null +++ b/score/iav_primula/tests/ffi_test.rs @@ -0,0 +1,101 @@ +/******************************************************************************** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Apache License Version 2.0 which is available at + * https://www.apache.org/licenses/LICENSE-2.0 + * + * SPDX-License-Identifier: Apache-2.0 + ********************************************************************************/ + +use iav_primula::{ + iav_algorithm, iav_key_destroy, iav_keypair_generate, iav_kem_decapsulate, + iav_kem_encapsulate, iav_kem_keypair_generate, iav_kem_public_key_export, + iav_public_key_export, iav_sign, iav_status, iav_verify, + iav_primula_key_handle, +}; + +#[test] +fn exported_operations_report_unsupported_until_backend_is_connected() { + // These are minimal placeholder values for the current stub operations: + // key remains null, length is a dummy size parameter, and byte provides a + // minimal message or data reference. + let mut key: *mut iav_primula_key_handle = core::ptr::null_mut(); + let mut length = 0usize; + let byte = 0u8; + + // Signature key management must return the stable unsupported status while + // the concrete implementation is not integrated. + assert_eq!( + iav_keypair_generate(iav_algorithm::IavAlgorithmMlDsa44, &mut key), + iav_status::IavStatusUnsupportedAlgorithm + ); + + assert_eq!( + iav_public_key_export(key, core::ptr::null_mut(), &mut length), + iav_status::IavStatusUnsupportedAlgorithm + ); + + // KEM key management follows the same stub contract. + assert_eq!( + iav_kem_keypair_generate(iav_algorithm::IavAlgorithmMlKem512, &mut key), + iav_status::IavStatusUnsupportedAlgorithm + ); + assert_eq!( + iav_kem_public_key_export(key, core::ptr::null_mut(), &mut length), + iav_status::IavStatusUnsupportedAlgorithm + ); + + // Signature operations use the same unsupported status until implemented. + assert_eq!( + iav_sign(key, &byte, 0, core::ptr::null_mut(), &mut length), + iav_status::IavStatusUnsupportedAlgorithm + ); + assert_eq!( + iav_verify(key, &byte, 0, &byte, 0), + iav_status::IavStatusUnsupportedAlgorithm + ); + + // KEM operations also remain unsupported in the current backend stub. + assert_eq!( + iav_kem_encapsulate( + iav_algorithm::IavAlgorithmMlKem512, + &byte, + 0, + core::ptr::null_mut(), + &mut length, + core::ptr::null_mut(), + &mut length, + ), + iav_status::IavStatusUnsupportedAlgorithm + ); + assert_eq!( + iav_kem_decapsulate(key, &byte, 0, core::ptr::null_mut(), &mut length), + iav_status::IavStatusUnsupportedAlgorithm + ); + + // Resource cleanup is intentionally a no-op for the current null/stub handle. + iav_key_destroy(key); +} + +#[test] +fn ffi_status_and_algorithm_values_are_stable() { + // Status values are part of the stable C ABI. + assert_eq!(iav_status::IavStatusOk as u32, 0); + assert_eq!(iav_status::IavStatusInvalidArgument as u32, 1); + assert_eq!(iav_status::IavStatusBufferTooSmall as u32, 2); + assert_eq!(iav_status::IavStatusUnsupportedAlgorithm as u32, 3); + assert_eq!(iav_status::IavStatusVerificationFailed as u32, 4); + assert_eq!(iav_status::IavStatusCryptoFailure as u32, 5); + + // Algorithm values must match the #[repr(C)] declarations in the C header. + assert_eq!(iav_algorithm::IavAlgorithmMlDsa44 as u32, 1); + assert_eq!(iav_algorithm::IavAlgorithmMlDsa65 as u32, 2); + assert_eq!(iav_algorithm::IavAlgorithmMlDsa87 as u32, 3); + assert_eq!(iav_algorithm::IavAlgorithmMlKem512 as u32, 10); + assert_eq!(iav_algorithm::IavAlgorithmMlKem768 as u32, 11); + assert_eq!(iav_algorithm::IavAlgorithmMlKem1024 as u32, 12); +} diff --git a/score/iav_primula/tests/hello_test.rs b/score/iav_primula/tests/hello_test.rs deleted file mode 100644 index b211f69cd..000000000 --- a/score/iav_primula/tests/hello_test.rs +++ /dev/null @@ -1,19 +0,0 @@ -// ******************************************************************************* -// Copyright (c) 2026 Contributors to the Eclipse Foundation -// -// See the NOTICE file(s) distributed with this work for additional -// information regarding copyright ownership. -// -// This program and the accompanying materials are made available under the -// terms of the Apache License Version 2.0 which is available at -// -// -// SPDX-License-Identifier: Apache-2.0 -// ******************************************************************************* - -use iav_primula::get_hello_message; - -#[test] -fn returns_expected_message() { - assert_eq!(get_hello_message(), "Hello World from iav_primula"); -} diff --git a/tests/iav_primula/BUILD b/tests/iav_primula/BUILD new file mode 100644 index 000000000..173878c63 --- /dev/null +++ b/tests/iav_primula/BUILD @@ -0,0 +1,17 @@ +# ******************************************************************************* +# Copyright (c) 2026 Contributors to the Eclipse Foundation +# +# See the NOTICE file(s) distributed with this work for additional +# information regarding copyright ownership. +# +# This program and the accompanying materials are made available under the +# terms of the Apache License Version 2.0 which is available at +# https://www.apache.org/licenses/LICENSE-2.0 +# +# SPDX-License-Identifier: Apache-2.0 +# ******************************************************************************* + +test_suite( + name = "all", + tests = ["//score/iav_primula/tests:ffi_test"], +)