Repository: Cosmian/kms (language: python)

Issue:
Support for `SetAttributes` KMIP operation
Supporting the `SetAttribute` operation will greatly help
 - linking objects together in the KMS (links are essential in the PKI and PKCS#12 parts of the server)
 - adding tags to Objects

Much likely easier to handle after fixing #88 

After #101 , `GetAttributes` will be available (this includes tags as well)


Discussion / hints:

Fix patch:
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index 1ba59d7d9..3bdb5abf4 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -137,7 +137,7 @@ repos:
         args: [--skip-string-normalization]
 
   - repo: https://github.com/Cosmian/git-hooks.git
-    rev: v1.0.27
+    rev: v1.0.29
     hooks:
       - id: cargo-format
       # - id: dprint-toml-fix
@@ -149,12 +149,11 @@ repos:
       - id: docker-compose-up
       - id: cargo-test
         args: [--, --skip, test_wrap_auth, --skip, google_cse]
-      - id: clippy-autofix-all
       - id: clippy-autofix-unreachable-pub
-      - id: clippy-autofix-pedantic
-      - id: clippy-autofix-nursery
-      - id: clippy-autofix-others
+      - id: clippy-autofix-all-targets-all-features
+      - id: clippy-autofix-all-targets
       - id: clippy-all-targets-all-features
+      - id: clippy-all-targets
       - id: cargo-format # in last du to clippy fixes
       - id: docker-compose-down
 
diff --git a/Cargo.lock b/Cargo.lock
index 19f7269d4..c8a83052a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1302,6 +1302,7 @@ dependencies = [
  "cosmian_kms_server",
  "cosmian_logger",
  "der",
+ "hex",
  "jwt-simple",
  "kms_test_server",
  "oauth2",
@@ -1401,6 +1402,7 @@ dependencies = [
  "serde",
  "serde_json",
  "sqlx",
+ "strum",
  "tempfile",
  "thiserror",
  "time",
diff --git a/README.md b/README.md
index 4e570325b..587a1f2a3 100644
--- a/README.md
+++ b/README.md
@@ -34,6 +34,7 @@ Keys can be wrapped and unwrapped using RSA, ECIES or RFC5649/AES KWP.
 <!-- toc -->
 
 - [Quick start](#quick-start)
+  * [Example](#example)
 - [Repository content](#repository-content)
 - [Building the KMS](#building-the-kms)
   * [Linux](#linux)
@@ -65,20 +66,21 @@ docker run -p 9998:9998 --name kms ghcr.io/cosmian/kms:4.18.0
 ```
 
 Then, use the CLI to issue commands to the KMS.
-The CLI, called `ckms`, can be either downloaded from [Cosmian packages](https://package.cosmian.com/kms/) or built and launched from this GitHub project by running 
+The CLI, called `ckms`, can be either downloaded from [Cosmian packages](https://package.cosmian.com/kms/) or built and launched from this GitHub project by running
+
 ```sh
 cargo run --bin ckms -- --help
 ```
 
-**Example**
+### Example
 
-1. Create a 256-bit symmetric key 
+1. Create a 256-bit symmetric key
 
 ```sh
 ➜ cargo run --bin ckms -- sym keys create --number-of-bits 256 --algorithm aes --tag project1
 ...
 The symmetric key was successfully generated.
-	  Unique identifier: 87e9e2a8-4538-4701-aa8c-e3af94e44a9e
+   Unique identifier: 87e9e2a8-4538-4701-aa8c-e3af94e44a9e
 
   Tags:
     - project1
@@ -93,13 +95,13 @@ The encrypted file is available at "image.enc"
 ```
 
 3. Decrypt the `image.enc` file using the key
+
 ```sh
 ➜ cargo run --bin ckms -- sym decrypt --tag project1 --output-file image2.png image.enc
 ...
 The decrypted file is available at "image2.png"
 ```
 
-
 See the [documentation](https://docs.cosmian.com/cosmian_key_management_system/) for more.
 
 ## Repository content
diff --git a/crate/cli/Cargo.toml b/crate/cli/Cargo.toml
index f917ce183..9b97f35c9 100644
--- a/crate/cli/Cargo.toml
+++ b/crate/cli/Cargo.toml
@@ -40,6 +40,7 @@ cloudproof = { workspace = true }
 cosmian_kms_client = { path = "../client" }
 cosmian_logger = { path = "../logger" }
 der = { version = "0.7", features = ["pem"] }
+hex = { workspace = true }
 jwt-simple = { version = "0.12", default-features = false, features = [
   "pure-rust",
 ] }
diff --git a/crate/cli/src/actions/certificates/certify.rs b/crate/cli/src/actions/certificates/certify.rs
index f09de392b..86deb8910 100644
--- a/crate/cli/src/actions/certificates/certify.rs
+++ b/crate/cli/src/actions/certificates/certify.rs
@@ -3,7 +3,7 @@ use std::{
     path::PathBuf,
 };
 
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use cosmian_kms_client::{
     cosmian_kmip::kmip::{
         kmip_objects::ObjectType,
@@ -25,7 +25,7 @@ use crate::{
 };
 
 /// The algorithm to use for the keypair generation
-#[derive(clap::ValueEnum, Debug, Clone, Copy)]
+#[derive(ValueEnum, Debug, Clone, Copy)]
 pub(crate) enum Algorithm {
     #[cfg(not(feature = "fips"))]
     NistP192,
@@ -52,21 +52,21 @@ impl Display for Algorithm {
     fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
         match self {
             #[cfg(not(feature = "fips"))]
-            Algorithm::NistP192 => write!(f, "nist-p192"),
+            Self::NistP192 => write!(f, "nist-p192"),
             Self::NistP224 => write!(f, "nist-p224"),
             Self::NistP256 => write!(f, "nist-p256"),
             Self::NistP384 => write!(f, "nist-p384"),
             Self::NistP521 => write!(f, "nist-p521"),
             #[cfg(not(feature = "fips"))]
-            Algorithm::X25519 => write!(f, "x25519"),
+            Self::X25519 => write!(f, "x25519"),
             #[cfg(not(feature = "fips"))]
-            Algorithm::Ed25519 => write!(f, "ed25519"),
+            Self::Ed25519 => write!(f, "ed25519"),
             #[cfg(not(feature = "fips"))]
-            Algorithm::X448 => write!(f, "x448"),
+            Self::X448 => write!(f, "x448"),
             #[cfg(not(feature = "fips"))]
-            Algorithm::Ed448 => write!(f, "ed448"),
+            Self::Ed448 => write!(f, "ed448"),
             #[cfg(not(feature = "fips"))]
-            Algorithm::RSA1024 => write!(f, "rsa1024"),
+            Self::RSA1024 => write!(f, "rsa1024"),
             Self::RSA2048 => write!(f, "rsa2048"),
             Self::RSA3072 => write!(f, "rsa3072"),
             Self::RSA4096 => write!(f, "rsa4096"),
@@ -283,7 +283,7 @@ impl CertifyAction {
                 Some(Box::new(CertificateAttributes::parse_subject_line(
                     self.subject_name.as_ref().ok_or_else(|| {
                         CliError::Default(
-                            "subject name is required when certifying a public key".to_string(),
+                            "subject name is required when certifying a public key".to_owned(),
                         )
                     })?,
                 )?));
@@ -299,7 +299,7 @@ impl CertifyAction {
                 Some(Box::new(CertificateAttributes::parse_subject_line(
                     self.subject_name.as_ref().ok_or_else(|| {
                         CliError::Default(
-                            "subject name is required when generating a keypair".to_string(),
+                            "subject name is required when generating a keypair".to_owned(),
                         )
                     })?,
                 )?));
diff --git a/crate/cli/src/actions/certificates/export_certificate.rs b/crate/cli/src/actions/certificates/export_certificate.rs
index 61ecf3879..19cfc75dd 100644
--- a/crate/cli/src/actions/certificates/export_certificate.rs
+++ b/crate/cli/src/actions/certificates/export_certificate.rs
@@ -1,6 +1,6 @@
 use std::path::PathBuf;
 
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use cosmian_kms_client::{
     export_object,
     kmip::{kmip_objects::Object, kmip_types::KeyFormatType, ttlv::serializer::to_ttlv},
@@ -11,7 +11,7 @@ use tracing::log::trace;
 
 use crate::{actions::console, cli_bail, error::result::CliResult};
 
-#[derive(clap::ValueEnum, Debug, Clone, PartialEq, Eq)]
+#[derive(ValueEnum, Debug, Clone, PartialEq, Eq)]
 pub enum CertificateExportFormat {
     JsonTtlv,
     Pem,
diff --git a/crate/cli/src/actions/certificates/import_certificate.rs b/crate/cli/src/actions/certificates/import_certificate.rs
index ba53d6ffb..813f843cd 100644
--- a/crate/cli/src/actions/certificates/import_certificate.rs
+++ b/crate/cli/src/actions/certificates/import_certificate.rs
@@ -1,6 +1,6 @@
 use std::path::PathBuf;
 
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use cloudproof::reexport::crypto_core::reexport::x509_cert;
 use cosmian_kms_client::{
     cosmian_kmip::kmip::{
@@ -30,7 +30,7 @@ use crate::{
 const MOZILLA_CCADB: &str =
     "https://ccadb.my.salesforce-sites.com/mozilla/IncludedRootsPEMTxt?TrustBitsInclude=Websites";
 
-#[derive(clap::ValueEnum, Debug, Clone)]
+#[derive(ValueEnum, Debug, Clone)]
 pub enum CertificateInputFormat {
     JsonTtlv,
     Pem,
@@ -163,7 +163,7 @@ impl ImportCertificateAction {
                     )
                     .await?;
                 (
-                    "The certificate in the JSON TTLV was successfully imported!".to_string(),
+                    "The certificate in the JSON TTLV was successfully imported!".to_owned(),
                     Some(certificate_id),
                 )
             }
@@ -187,7 +187,7 @@ impl ImportCertificateAction {
                     )
                     .await?;
                 (
-                    "The certificate in the PEM file was successfully imported!".to_string(),
+                    "The certificate in the PEM file was successfully imported!".to_owned(),
                     Some(certificate_id),
                 )
             }
@@ -211,7 +211,7 @@ impl ImportCertificateAction {
                     )
                     .await?;
                 (
-                    "The certificate in the DER file was successfully imported!".to_string(),
+                    "The certificate in the DER file was successfully imported!".to_owned(),
                     Some(certificate_id),
                 )
             }
@@ -239,7 +239,7 @@ impl ImportCertificateAction {
                     )
                     .await?;
                 (
-                    "The certificate chain in the PEM file was successfully imported!".to_string(),
+                    "The certificate chain in the PEM file was successfully imported!".to_owned(),
                     Some(leaf_certificate_id),
                 )
             }
@@ -263,7 +263,7 @@ impl ImportCertificateAction {
                 self.import_chain(kms_rest_client, objects, self.replace_existing, None)
                     .await?;
 
-                ("The list of Mozilla CCADB certificates".to_string(), None)
+                ("The list of Mozilla CCADB certificates".to_owned(), None)
             }
         };
         let mut stdout = console::Stdout::new(&stdout_message);
diff --git a/crate/cli/src/actions/console.rs b/crate/cli/src/actions/console.rs
index 885d1cee0..35e172fd1 100644
--- a/crate/cli/src/actions/console.rs
+++ b/crate/cli/src/actions/console.rs
@@ -2,7 +2,7 @@ use std::collections::HashMap;
 
 use cosmian_kms_client::{
     access::{AccessRightsObtainedResponse, ObjectOwnedResponse, UserAccessResponse},
-    kmip::kmip_types::UniqueIdentifier,
+    kmip::kmip_types::{Attribute, UniqueIdentifier},
 };
 use serde::Serialize;
 use serde_json::Value;
@@ -27,6 +27,8 @@ pub struct Stdout {
     #[serde(skip_serializing_if = "Option::is_none")]
     tags: Option<Vec<String>>,
     #[serde(skip_serializing_if = "Option::is_none")]
+    attribute: Option<Attribute>,
+    #[serde(skip_serializing_if = "Option::is_none")]
     attributes: Option<HashMap<String, Value>>,
     #[serde(skip_serializing_if = "Option::is_none")]
     accesses: Option<Vec<UserAccessResponse>>,
@@ -71,6 +73,10 @@ impl Stdout {
         self.public_key_unique_identifier = Some(public_key_unique_identifier.into());
     }
 
+    pub fn set_attribute(&mut self, attribute: Attribute) {
+        self.attribute = Some(attribute);
+    }
+
     pub fn set_attributes(&mut self, attributes: HashMap<String, Value>) {
         self.attributes = Some(attributes);
     }
@@ -95,6 +101,7 @@ impl Stdout {
     /// # Errors
     ///
     /// Returns an error if there is an issue with writing to the console.
+    #[allow(clippy::print_stdout)]
     pub fn write(&self) -> CliResult<()> {
         // Check if the output format should be JSON
         let json_format_from_env = std::env::var(KMS_CLI_FORMAT)
@@ -134,7 +141,13 @@ impl Stdout {
                 println!("\t  Private key unique identifier: {id}");
             }
 
-            // Print the attributes if present
+            // Print the attribute if present: attribute is a single element
+            if let Some(attribute) = &self.attribute {
+                let json = serde_json::to_string_pretty(attribute)?;
+                println!("{json}");
+            }
+
+            // Print the attributes if present: attributes are a hashmap of key-value pairs
             if let Some(attributes) = &self.attributes {
                 let json = serde_json::to_string_pretty(attributes)?;
                 println!("{json}");
diff --git a/crate/cli/src/actions/cover_crypt/keys/rekey.rs b/crate/cli/src/actions/cover_crypt/keys/rekey.rs
index 07f2d60e5..32affbdbf 100644
--- a/crate/cli/src/actions/cover_crypt/keys/rekey.rs
+++ b/crate/cli/src/actions/cover_crypt/keys/rekey.rs
@@ -51,7 +51,7 @@ impl RekeyAction {
         // Create the kmip query
         let query = build_rekey_keypair_request(
             &id,
-            RekeyEditAction::RekeyAccessPolicy(self.access_policy.clone()),
+            &RekeyEditAction::RekeyAccessPolicy(self.access_policy.clone()),
         )?;
 
         // Query the KMS with your kmip data
@@ -118,7 +118,7 @@ impl PruneAction {
         // Create the kmip query
         let query = build_rekey_keypair_request(
             &id,
-            RekeyEditAction::PruneAccessPolicy(self.access_policy.clone()),
+            &RekeyEditAction::PruneAccessPolicy(self.access_policy.clone()),
         )?;
 
         // Query the KMS with your kmip data
diff --git a/crate/cli/src/actions/cover_crypt/policy.rs b/crate/cli/src/actions/cover_crypt/policy.rs
index f41e19397..912c2075c 100644
--- a/crate/cli/src/actions/cover_crypt/policy.rs
+++ b/crate/cli/src/actions/cover_crypt/policy.rs
@@ -132,7 +132,7 @@ impl CreateAction {
 
         // write the binary file
         write_json_object_to_file(&policy, &self.policy_binary_file)
-            .with_context(|| "failed writing the policy binary file".to_string())?;
+            .with_context(|| "failed writing the policy binary file".to_owned())?;
 
         let stdout = format!(
             "The binary policy file was generated in {:?}.",
@@ -341,7 +341,7 @@ impl AddAttributeAction {
         // Create the kmip query
         let rekey_query = build_rekey_keypair_request(
             &id,
-            RekeyEditAction::AddAttribute(vec![(attr, enc_hint)]),
+            &RekeyEditAction::AddAttribute(vec![(attr, enc_hint)]),
         )?;
 
         // Query the KMS with your kmip data
@@ -402,7 +402,7 @@ impl RenameAttributeAction {
         // Create the kmip query
         let rekey_query = build_rekey_keypair_request(
             &id,
-            RekeyEditAction::RenameAttribute(vec![(attr, self.new_name.clone())]),
+            &RekeyEditAction::RenameAttribute(vec![(attr, self.new_name.clone())]),
         )?;
 
         // Query the KMS with your kmip data
@@ -455,7 +455,7 @@ impl DisableAttributeAction {
 
         // Create the kmip query
         let rekey_query =
-            build_rekey_keypair_request(&id, RekeyEditAction::DisableAttribute(vec![attr]))?;
+            build_rekey_keypair_request(&id, &RekeyEditAction::DisableAttribute(vec![attr]))?;
 
         // Query the KMS with your kmip data
         let rekey_response = kms_rest_client
@@ -510,7 +510,7 @@ impl RemoveAttributeAction {
 
         // Create the kmip query
         let rekey_query =
-            build_rekey_keypair_request(&id, RekeyEditAction::RemoveAttribute(vec![attr]))?;
+            build_rekey_keypair_request(&id, &RekeyEditAction::RemoveAttribute(vec![attr]))?;
 
         // Query the KMS with your kmip data
         let rekey_response = kms_rest_client
diff --git a/crate/cli/src/actions/elliptic_curves/keys/create_key_pair.rs b/crate/cli/src/actions/elliptic_curves/keys/create_key_pair.rs
index 5ba656d99..7ce3b78c4 100644
--- a/crate/cli/src/actions/elliptic_curves/keys/create_key_pair.rs
+++ b/crate/cli/src/actions/elliptic_curves/keys/create_key_pair.rs
@@ -1,4 +1,4 @@
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use cosmian_kms_client::{
     cosmian_kmip::{
         crypto::elliptic_curves::kmip_requests::create_ec_key_pair_request,
@@ -12,7 +12,7 @@ use crate::{
     error::result::{CliResult, CliResultHelper},
 };
 
-#[derive(clap::ValueEnum, Debug, Clone, Copy)]
+#[derive(ValueEnum, Debug, Clone, Copy)]
 pub enum Curve {
     #[cfg(not(feature = "fips"))]
     NistP192,
@@ -34,19 +34,19 @@ impl From<Curve> for RecommendedCurve {
     fn from(curve: Curve) -> Self {
         match curve {
             #[cfg(not(feature = "fips"))]
-            Curve::NistP192 => RecommendedCurve::P192,
+            Curve::NistP192 => Self::P192,
             Curve::NistP224 => Self::P224,
             Curve::NistP256 => Self::P256,
             Curve::NistP384 => Self::P384,
             Curve::NistP521 => Self::P521,
             #[cfg(not(feature = "fips"))]
-            Curve::X25519 => RecommendedCurve::CURVE25519,
+            Curve::X25519 => Self::CURVE25519,
             #[cfg(not(feature = "fips"))]
-            Curve::Ed25519 => RecommendedCurve::CURVEED25519,
+            Curve::Ed25519 => Self::CURVEED25519,
             #[cfg(not(feature = "fips"))]
-            Curve::X448 => RecommendedCurve::CURVE448,
+            Curve::X448 => Self::CURVE448,
             #[cfg(not(feature = "fips"))]
-            Curve::Ed448 => RecommendedCurve::CURVEED448,
+            Curve::Ed448 => Self::CURVEED448,
         }
     }
 }
diff --git a/crate/cli/src/actions/google/gmail_client/client_builder.rs b/crate/cli/src/actions/google/gmail_client/client_builder.rs
index d5b1eeb67..fa08f65db 100644
--- a/crate/cli/src/actions/google/gmail_client/client_builder.rs
+++ b/crate/cli/src/actions/google/gmail_client/client_builder.rs
@@ -41,7 +41,7 @@ impl GmailClientBuilder {
             client: Client::new(),
             token,
             base_url: [
-                "https://gmail.googleapis.com/gmail/v1/users/".to_string(),
+                "https://gmail.googleapis.com/gmail/v1/users/".to_owned(),
                 self.user_id,
             ]
             .concat(),
@@ -63,6 +63,7 @@ impl GmailClient {
             .await
     }
 
+    #[allow(clippy::print_stdout)]
     pub(crate) async fn handle_response(response: Response) -> CliResult<()> {
         if response.status().is_success() {
             let stdout = response.text().await.map_err(GoogleApiError::Reqwest)?;
diff --git a/crate/cli/src/actions/google/keypairs/create.rs b/crate/cli/src/actions/google/keypairs/create.rs
index 80aff58a0..32d8d36e3 100644
--- a/crate/cli/src/actions/google/keypairs/create.rs
+++ b/crate/cli/src/actions/google/keypairs/create.rs
@@ -108,6 +108,7 @@ impl CreateKeyPairsAction {
         GmailClient::handle_response(response).await
     }
 
+    #[allow(clippy::print_stdout)]
     pub async fn run(
         &self,
         conf_path: &PathBuf,
diff --git a/crate/cli/src/actions/login.rs b/crate/cli/src/actions/login.rs
index 48c4f27e6..a8a14597a 100644
--- a/crate/cli/src/actions/login.rs
+++ b/crate/cli/src/actions/login.rs
@@ -71,6 +71,7 @@ impl LoginAction {
     /// * The token exchange request fails.
     /// * The token exchange response cannot be parsed.
     /// * The client configuration cannot be updated or saved.
+    #[allow(clippy::print_stdout)]
     pub async fn process(&self, conf_path: &PathBuf) -> CliResult<()> {
         let mut conf = ClientConf::load(conf_path)?;
         let oauth2_conf = conf.oauth2_conf.as_ref().ok_or_else(|| {
@@ -233,17 +234,17 @@ impl LoginState {
         // parameter returned by the server matches `csrf_state`.
         let received_state = auth_parameters
             .get("state")
-            .ok_or_else(|| CliError::Default("state not received on authentication".to_string()))?;
+            .ok_or_else(|| CliError::Default("state not received on authentication".to_owned()))?;
         if received_state != self.csrf_token.secret() {
             return Err(CliError::Default(
-                "state received on authentication does not match".to_string(),
+                "state received on authentication does not match".to_owned(),
             ))
         }
 
         // extract the authorization code
         let authorization_code = auth_parameters
             .get("code")
-            .ok_or_else(|| CliError::Default("code not received on authentication".to_string()))?;
+            .ok_or_else(|| CliError::Default("code not received on authentication".to_owned()))?;
 
         // Now you can trade it for an access token.
 
diff --git a/crate/cli/src/actions/logout.rs b/crate/cli/src/actions/logout.rs
index 3e7b8368f..14408e9e6 100644
--- a/crate/cli/src/actions/logout.rs
+++ b/crate/cli/src/actions/logout.rs
@@ -23,6 +23,7 @@ impl LogoutAction {
     ///
     /// Returns an error if there is an issue loading or saving the configuration file.
     ///
+    #[allow(clippy::print_stdout)]
     pub fn process(&self, conf_path: &PathBuf) -> CliResult<()> {
         let mut conf = ClientConf::load(conf_path)?;
         conf.kms_access_token = None;
diff --git a/crate/cli/src/actions/markdown.rs b/crate/cli/src/actions/markdown.rs
index 52ca3252a..919bb1154 100644
--- a/crate/cli/src/actions/markdown.rs
+++ b/crate/cli/src/actions/markdown.rs
@@ -18,6 +18,7 @@ impl MarkdownAction {
     /// # Errors
     ///
     /// Returns an error if there is an issue creating or writing to the markdown file.
+    #[allow(clippy::print_stdout)]
     pub fn process(&self, cmd: &Command) -> CliResult<()> {
         let mut output = String::new();
         writeln!(
@@ -39,7 +40,7 @@ fn write_command(out: &mut dyn Write, index: &str, parent: &str, cmd: &Command)
         writeln!(out)?;
     }
     let full_command = if parent.is_empty() {
-        "ckms".to_string()
+        "ckms".to_owned()
     } else {
         format!("{} {}", parent, cmd.get_name())
     };
diff --git a/crate/cli/src/actions/new_database.rs b/crate/cli/src/actions/new_database.rs
index af47033fa..26ec0e703 100644
--- a/crate/cli/src/actions/new_database.rs
+++ b/crate/cli/src/actions/new_database.rs
@@ -29,6 +29,7 @@ impl NewDatabaseAction {
     ///
     /// Returns an error if the query execution on the KMS server fails.
     ///
+    #[allow(clippy::print_stdout)]
     pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
         // Query the KMS to get a new database
         let token = kms_rest_client
diff --git a/crate/cli/src/actions/rsa/mod.rs b/crate/cli/src/actions/rsa/mod.rs
index 943926838..623be6ada 100644
--- a/crate/cli/src/actions/rsa/mod.rs
+++ b/crate/cli/src/actions/rsa/mod.rs
@@ -1,6 +1,6 @@
 use std::fmt::Display;
 
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use cosmian_kms_client::{
     cosmian_kmip::kmip::kmip_types::{CryptographicAlgorithm, HashingAlgorithm},
     kmip::kmip_types::{CryptographicParameters, PaddingMethod},
@@ -43,7 +43,7 @@ impl RsaCommands {
     }
 }
 
-#[derive(clap::ValueEnum, Debug, Clone, Copy)]
+#[derive(ValueEnum, Debug, Clone, Copy)]
 pub enum EncryptionAlgorithm {
     #[cfg(not(feature = "fips"))]
     // a.k.a PKCS#1 v1.5 RSA
@@ -60,12 +60,12 @@ impl Display for EncryptionAlgorithm {
             Self::CkmRsaPkcsOaep => write!(f, "ckm-rsa-pkcs-oaep"),
             Self::CkmRsaAesKeyWrap => write!(f, "ckm-rsa-aes-key-wrap"),
             #[cfg(not(feature = "fips"))]
-            EncryptionAlgorithm::CkmRsaPkcs => write!(f, "ckm-rsa-pkcs"),
+            Self::CkmRsaPkcs => write!(f, "ckm-rsa-pkcs"),
         }
     }
 }
 
-#[derive(clap::ValueEnum, Debug, Clone, Copy)]
+#[derive(ValueEnum, Debug, Clone, Copy)]
 pub enum HashFn {
     Sha1,
     Sha224,
diff --git a/crate/cli/src/actions/shared/delete_attributes.rs b/crate/cli/src/actions/shared/delete_attributes.rs
new file mode 100644
index 000000000..0e3160426
--- /dev/null
+++ b/crate/cli/src/actions/shared/delete_attributes.rs
@@ -0,0 +1,89 @@
+use clap::Parser;
+use cosmian_kms_client::{
+    cosmian_kmip::kmip::kmip_types::UniqueIdentifier,
+    kmip::{
+        kmip_operations::{DeleteAttribute, DeleteAttributeResponse},
+        kmip_types::{Attribute, AttributeReference, Tag},
+    },
+    KmsClient,
+};
+use tracing::trace;
+
+use super::set_attributes::SetOrDeleteAttributes;
+use crate::{actions::console, cli_bail, error::result::CliResult};
+
+/// Delete the KMIP object attributes (one or multiple attributes).
+#[derive(Parser, Debug)]
+#[clap(verbatim_doc_comment)]
+pub struct DeleteAttributesAction {
+    #[clap(flatten)]
+    requested_attributes: SetOrDeleteAttributes,
+
+    /// The attributes or tags to retrieve.
+    /// To specify multiple attributes, use the option multiple times.
+    #[clap(long = "attribute", value_name = "ATTRIBUTE", verbatim_doc_comment)]
+    attribute_tags: Option<Vec<Tag>>,
+}
+
+impl DeleteAttributesAction {
+    async fn delete_attribute(
+        &self,
+        kms_rest_client: &KmsClient,
+        id: &str,
+        current_attribute: Option<Attribute>,
+        attribute_references: Option<Vec<AttributeReference>>,
+    ) -> CliResult<()> {
+        let DeleteAttributeResponse { unique_identifier } = kms_rest_client
+            .delete_attribute(DeleteAttribute {
+                unique_identifier: Some(UniqueIdentifier::TextString(id.to_owned())),
+                current_attribute: current_attribute.clone(),
+                attribute_references,
+            })
+            .await?;
+        trace!("delete_attribute response for {unique_identifier}: {current_attribute:?}",);
+        let mut stdout = console::Stdout::new("Attribute deleted successfully");
+        stdout.set_tags(self.requested_attributes.tags.as_ref());
+        stdout.set_unique_identifier(id);
+        if let Some(current_attribute) = current_attribute {
+            stdout.set_attribute(current_attribute);
+        }
+        stdout.write()?;
+
+        Ok(())
+    }
+
+    /// Processes the `DeleteAttributes` action.
+    ///
+    /// # Errors
+    ///
+    /// This function can return a `CliError` if one of the following conditions occur:
+    ///
+    /// - Either `--id` or one or more `--tag` must be specified.
+    ///
+    pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
+        trace!("DeleteAttributeAction: {:?}", self);
+        let id = if let Some(key_id) = &self.requested_attributes.id {
+            key_id.clone()
+        } else if let Some(tags) = &self.requested_attributes.tags {
+            serde_json::to_string(&tags)?
+        } else {
+            cli_bail!("Either --id or one or more --tag must be specified")
+        };
+
+        for attribute in self.requested_attributes.get_attributes_from_args()? {
+            self.delete_attribute(kms_rest_client, &id, Some(attribute), None)
+                .await?;
+        }
+
+        if let Some(tags) = &self.attribute_tags {
+            let mut references: Vec<AttributeReference> = Vec::with_capacity(tags.len());
+            for tag in tags {
+                references.push(AttributeReference::Standard(tag.to_owned()));
+            }
+            self.delete_attribute(kms_rest_client, &id, None, Some(references))
+                .await?;
+        }
+
+        Ok(())
+    }
+}
diff --git a/crate/cli/src/actions/shared/get_attributes.rs b/crate/cli/src/actions/shared/get_attributes.rs
index 3401687fd..db7aecab8 100644
--- a/crate/cli/src/actions/shared/get_attributes.rs
+++ b/crate/cli/src/actions/shared/get_attributes.rs
@@ -9,41 +9,11 @@ use cosmian_kms_client::{
     write_bytes_to_file, KmsClient,
 };
 use serde_json::Value;
+use strum::IntoEnumIterator;
 use tracing::{debug, trace};
 
 use crate::{actions::console, cli_bail, error::result::CliResult};
 
-#[derive(clap::ValueEnum, Debug, Clone, PartialEq, Eq, Hash)]
-pub enum AttributeTag {
-    ActivationDate,
-    CryptographicAlgorithm,
-    CryptographicLength,
-    CryptographicParameters,
-    CryptographicDomainParameters,
-    CryptographicUsageMask,
-    KeyFormatType,
-    LinkedPrivateKeyId,
-    LinkedPublicKeyId,
-    LinkedIssuerCertificateId,
-    LinkedCertificateId,
-    Tags,
-}
-
-const ALL_ATTRIBUTE_TAGS: [AttributeTag; 12] = [
-    AttributeTag::ActivationDate,
-    AttributeTag::CryptographicAlgorithm,
-    AttributeTag::CryptographicLength,
-    AttributeTag::CryptographicParameters,
-    AttributeTag::CryptographicDomainParameters,
-    AttributeTag::CryptographicUsageMask,
-    AttributeTag::KeyFormatType,
-    AttributeTag::LinkedPrivateKeyId,
-    AttributeTag::LinkedPublicKeyId,
-    AttributeTag::LinkedIssuerCertificateId,
-    AttributeTag::LinkedCertificateId,
-    AttributeTag::Tags,
-];
-
 /// Get the KMIP object attributes and tags.
 ///
 /// When using tags to retrieve the object, rather than the object id,
@@ -61,15 +31,27 @@ pub struct GetAttributesAction {
     #[clap(long = "tag", short = 't', value_name = "TAG", group = "id-tags")]
     tags: Option<Vec<String>>,
 
-    /// The attributes or tags to retrieve.
+    /// The attributes or `KMIP-tags` to retrieve.
     /// To specify multiple attributes, use the option multiple times.
+    /// If not specified, all possible attributes are returned.
     #[clap(
         long = "attribute",
         short = 'a',
         value_name = "ATTRIBUTE",
         verbatim_doc_comment
     )]
-    attribute_tags: Vec<AttributeTag>,
+    attribute_tags: Vec<Tag>,
+
+    /// Filter on retrieved links. Only if KMIP tag `LinkType` is used in `attribute` parameter.
+    /// To specify multiple attributes, use the option multiple times.
+    /// If not specified, all possible link types are returned.
+    #[clap(
+        long = "link-type",
+        short = 'l',
+        value_name = "LINK_TYPE",
+        verbatim_doc_comment
+    )]
+    attribute_link_types: Vec<LinkType>,
 
     /// An optional file where to export the attributes.
     /// The attributes will be in JSON TTLV format.
@@ -77,6 +59,15 @@ pub struct GetAttributesAction {
     output_file: Option<PathBuf>,
 }
 
+fn add_if_not_empty(tag: Tag, new_value: &str, results: &mut HashMap<String, Value>) {
+    if !new_value.is_empty() {
+        results.insert(
+            tag.to_string(),
+            serde_json::to_value(new_value).unwrap_or_default(),
+        );
+    }
+}
+
 impl GetAttributesAction {
     /// Get the KMIP object attributes and tags.
     ///
@@ -105,41 +96,7 @@ impl GetAttributesAction {
 
         let mut references: Vec<AttributeReference> = Vec::with_capacity(self.attribute_tags.len());
         for tag in &self.attribute_tags {
-            match tag {
-                AttributeTag::ActivationDate => {
-                    references.push(AttributeReference::Standard(Tag::ActivationDate));
-                }
-                AttributeTag::CryptographicAlgorithm => {
-                    references.push(AttributeReference::Standard(Tag::CryptographicAlgorithm));
-                }
-                AttributeTag::CryptographicLength => {
-                    references.push(AttributeReference::Standard(Tag::CryptographicLength));
-                }
-                AttributeTag::CryptographicParameters => {
-                    references.push(AttributeReference::Standard(Tag::CryptographicParameters));
-                }
-                AttributeTag::CryptographicDomainParameters => references.push(
-                    AttributeReference::Standard(Tag::CryptographicDomainParameters),
-                ),
-                AttributeTag::CryptographicUsageMask => {
-                    references.push(AttributeReference::Standard(Tag::CryptographicUsageMask));
-                }
-                AttributeTag::KeyFormatType => {
-                    references.push(AttributeReference::Standard(Tag::KeyFormatType));
-                }
-                AttributeTag::LinkedPrivateKeyId => {
-                    references.push(AttributeReference::Standard(Tag::PrivateKey));
-                }
-                AttributeTag::LinkedPublicKeyId => {
-                    references.push(AttributeReference::Standard(Tag::PublicKey));
-                }
-                AttributeTag::LinkedIssuerCertificateId | AttributeTag::LinkedCertificateId => {
-                    references.push(AttributeReference::Standard(Tag::Certificate));
-                }
-                AttributeTag::Tags => {
-                    references.push(AttributeReference::tags_reference());
-                }
-            }
+            references.push(AttributeReference::Standard(*tag));
         }
 
         // perform the Get Attributes request
@@ -153,115 +110,251 @@ impl GetAttributesAction {
             })
             .await?;
 
-        trace!("GetAttributes response for {unique_identifier}: {attributes:?}",);
+        debug!("GetAttributes response for {unique_identifier}: {attributes:?}",);
 
         // if no tag asked -> return values for all possible tags
         let tags = if self.attribute_tags.is_empty() {
-            ALL_ATTRIBUTE_TAGS.to_vec()
+            trace!("No attribute tag specified, returning all possible tags");
+            let mut all_tags = Vec::new();
+            for tag in Tag::iter() {
+                if tag != Tag::VendorExtension {
+                    // Just to avoid the vendor extension tag by default
+                    all_tags.push(tag);
+                }
+            }
+            all_tags
         } else {
             self.attribute_tags.clone()
         };
+
         let mut results: HashMap<String, Value> = HashMap::new();
         for tag in &tags {
             match tag {
-                AttributeTag::ActivationDate => {
+                Tag::ActivationDate => {
                     if let Some(v) = attributes.activation_date.as_ref() {
-                        results.insert(
-                            "activation-date".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
+                    }
+                }
+                Tag::CertificateLength => {
+                    if let Some(v) = attributes.certificate_length.as_ref() {
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
+                    }
+                }
+                Tag::CertificateType => {
+                    if let Some(v) = attributes.certificate_type.as_ref() {
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
+                    }
+                }
+                Tag::CertificateSubjectC => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_c, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectCN => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_cn, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectDC => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_dc, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectEmail => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_email, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectL => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_l, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectO => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_o, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectOU => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_ou, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectST => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_st, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectDNQualifier => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_dn_qualifier, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectSerialNumber => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_serial_number, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectTitle => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_title, &mut results);
+                    }
+                }
+                Tag::CertificateSubjectUID => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_subject_uid, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerC => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_c, &mut results);
                     }
                 }
-                AttributeTag::CryptographicAlgorithm => {
+                Tag::CertificateIssuerCN => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_cn, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerDC => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_dc, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerEmail => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_email, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerL => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_l, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerO => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_o, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerOU => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_ou, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerST => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_st, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerDNQualifier => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_dn_qualifier, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerUID => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_uid, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerSerialNumber => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_serial_number, &mut results);
+                    }
+                }
+                Tag::CertificateIssuerTitle => {
+                    if let Some(v) = attributes.certificate_attributes.as_ref() {
+                        add_if_not_empty(*tag, &v.certificate_issuer_title, &mut results);
+                    }
+                }
+                Tag::CryptographicAlgorithm => {
                     if let Some(v) = attributes.cryptographic_algorithm.as_ref() {
-                        results.insert(
-                            "cryptographic-algorithm".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
                     }
                 }
-                AttributeTag::CryptographicLength => {
+                Tag::CryptographicLength => {
                     if let Some(v) = attributes.cryptographic_length.as_ref() {
-                        results.insert(
-                            "cryptographic-length".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
                     }
                 }
-                AttributeTag::CryptographicParameters => {
+                Tag::CryptographicParameters => {
                     if let Some(v) = attributes.cryptographic_parameters.as_ref() {
-                        results.insert(
-                            "cryptographic-parameters".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
                     }
                 }
-                AttributeTag::CryptographicDomainParameters => {
+                Tag::CryptographicDomainParameters => {
                     if let Some(v) = attributes.cryptographic_domain_parameters.as_ref() {
-                        results.insert(
-                            "cryptographic-domain-parameters".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
                     }
                 }
-                AttributeTag::CryptographicUsageMask => {
+                Tag::CryptographicUsageMask => {
                     if let Some(v) = attributes.cryptographic_usage_mask.as_ref() {
-                        results.insert(
-                            "cryptographic-usage-mask".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
                     }
                 }
-                AttributeTag::KeyFormatType => {
+                Tag::KeyFormatType => {
                     if let Some(v) = attributes.key_format_type.as_ref() {
-                        results.insert(
-                            "key-format-type".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
                     }
                 }
-                AttributeTag::LinkedPrivateKeyId => {
-                    if let Some(v) = attributes.get_link(LinkType::PrivateKeyLink) {
-                        results.insert(
-                            "linked-private-key-id".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
+                Tag::ObjectType => {
+                    if let Some(v) = attributes.object_type.as_ref() {
+                        results
+                            .insert(tag.to_string(), serde_json::to_value(v).unwrap_or_default());
                     }
                 }
-                AttributeTag::LinkedPublicKeyId => {
-                    if let Some(v) = attributes.get_link(LinkType::PublicKeyLink) {
-                        results.insert(
-                            "linked-public-key-id".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
-                    }
-                }
-                AttributeTag::LinkedIssuerCertificateId => {
-                    if let Some(v) = attributes.get_link(LinkType::CertificateLink) {
-                        results.insert(
-                            "linked-issuer-certificate-id".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
-                        );
-                    }
+                Tag::Tag => {
+                    let tags = attributes.get_tags();
+                    results.insert(
+                        tag.to_string(),
+                        serde_json::to_value(tags).unwrap_or_default(),
+                    );
                 }
-                AttributeTag::LinkedCertificateId => {
-                    if let Some(v) = attributes.get_link(LinkType::PKCS12CertificateLink) {
+                Tag::VendorExtension => {
+                    if let Some(vendor_attributes) = attributes.vendor_attributes.as_ref() {
                         results.insert(
-                            "linked-certificate-id".to_string(),
-                            serde_json::to_value(v).unwrap_or_default(),
+                            tag.to_string(),
+                            serde_json::to_value(vendor_attributes).unwrap_or_default(),
                         );
                     }
                 }
-                AttributeTag::Tags => {
-                    let tags = attributes.get_tags();
-                    results.insert(
-                        "tags".to_string(),
-                        serde_json::to_value(tags).unwrap_or_default(),
-                    );
+                _x => {
+                    // trace!("Tag {x} not supported");
                 }
             }
         }
 
+        // if no link asked -> return values for all possible links
+        let link_types = if self.attribute_link_types.is_empty() {
+            trace!("No link type specified, returning all possible link types");
+            let mut all_links = Vec::new();
+            for link in LinkType::iter() {
+                all_links.push(link);
+            }
+            all_links
+        } else {
+            self.attribute_link_types.clone()
+        };
+
+        trace!("Attributes at this point: {attributes:?}",);
+        for link_type in &link_types {
+            trace!("Processing link type: {link_type:?}",);
+            if let Some(v) = attributes.get_link(*link_type).as_ref() {
+                trace!("Link type {link_type} found: {v:?}",);
+                results.insert(
+                    link_type.to_string(),
+                    serde_json::to_value(v).unwrap_or_default(),
+                );
+            }
+        }
+
         if let Some(output_file) = &self.output_file {
             let json = serde_json::to_string_pretty(&results)?;
             debug!("GetAttributes response for {unique_identifier}: {}", json);
diff --git a/crate/cli/src/actions/shared/import_key.rs b/crate/cli/src/actions/shared/import_key.rs
index 956d53dec..6c353f06a 100644
--- a/crate/cli/src/actions/shared/import_key.rs
+++ b/crate/cli/src/actions/shared/import_key.rs
@@ -1,6 +1,6 @@
 use std::path::PathBuf;
 
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use cosmian_kms_client::{
     cosmian_kmip::kmip::{
         kmip_data_structures::{KeyBlock, KeyMaterial, KeyValue},
@@ -20,7 +20,7 @@ use crate::{
     error::{result::CliResult, CliError},
 };
 
-#[derive(clap::ValueEnum, Debug, Clone)]
+#[derive(ValueEnum, Debug, Clone)]
 pub(crate) enum ImportKeyFormat {
     JsonTtlv,
     Pem,
@@ -231,6 +231,7 @@ impl ImportKeyAction {
 }
 
 /// Read a key from a PEM file
+#[allow(clippy::print_stdout)]
 fn read_key_from_pem(bytes: &[u8]) -> CliResult<Object> {
     let mut objects = objects_from_pem(bytes)?;
     let object = objects
diff --git a/crate/cli/src/actions/shared/mod.rs b/crate/cli/src/actions/shared/mod.rs
index 6970c28f8..2e5d03639 100644
--- a/crate/cli/src/actions/shared/mod.rs
+++ b/crate/cli/src/actions/shared/mod.rs
@@ -1,7 +1,9 @@
+mod delete_attributes;
 mod export_key;
 mod get_attributes;
 pub(crate) mod import_key;
 mod locate;
+mod set_attributes;
 pub mod utils;
 
 #[cfg(feature = "openssl")]
@@ -10,10 +12,12 @@ mod wrap_key;
 #[cfg(feature = "openssl")]
 mod unwrap_key;
 
+pub use delete_attributes::DeleteAttributesAction;
 pub use export_key::{ExportKeyAction, ExportKeyFormat};
-pub use get_attributes::{AttributeTag, GetAttributesAction};
+pub use get_attributes::GetAttributesAction;
 pub use import_key::ImportKeyAction;
 pub use locate::LocateObjectsAction;
+pub use set_attributes::{SetAttributesAction, SetOrDeleteAttributes, VendorAttributeCli};
 #[cfg(feature = "openssl")]
 pub use unwrap_key::UnwrapKeyAction;
 #[cfg(feature = "openssl")]
diff --git a/crate/cli/src/actions/shared/set_attributes.rs b/crate/cli/src/actions/shared/set_attributes.rs
new file mode 100644
index 000000000..be1bd49d0
--- /dev/null
+++ b/crate/cli/src/actions/shared/set_attributes.rs
@@ -0,0 +1,305 @@
+use std::convert::TryFrom;
+
+use clap::Parser;
+use cosmian_kms_client::{
+    cosmian_kmip::kmip::kmip_types::UniqueIdentifier,
+    kmip::{
+        kmip_operations::{SetAttribute, SetAttributeResponse},
+        kmip_types::{
+            self, Attribute, CryptographicAlgorithm, Link, LinkType, LinkedObjectIdentifier,
+            StateEnumeration, VendorAttribute,
+        },
+    },
+    KmsClient,
+};
+use serde::Deserialize;
+use tracing::{info, trace};
+
+use super::utils::KeyUsage;
+use crate::{
+    actions::{console, shared::utils::build_usage_mask_from_key_usage},
+    cli_bail,
+    error::result::CliResult,
+};
+
+#[derive(Parser, Deserialize, Default, Debug, Clone, PartialEq, Eq)]
+pub struct VendorAttributeCli {
+    /// The vendor identification.
+    #[clap(long, short = 'v', requires = "attribute_name")]
+    pub vendor_identification: Option<String>,
+    /// The attribute name.
+    #[clap(long, short = 'n', requires = "vendor_identification")]
+    pub attribute_name: Option<String>,
+    /// The attribute value.
+    #[clap(long, requires = "vendor_identification")]
+    pub attribute_value: Option<String>,
+}
+
+impl TryFrom<&VendorAttributeCli> for Attribute {
+    type Error = crate::error::CliError;
+
+    fn try_from(vendor_attribute: &VendorAttributeCli) -> Result<Self, Self::Error> {
+        let vendor_attribute = kmip_types::VendorAttribute {
+            vendor_identification: vendor_attribute
+                .vendor_identification
+                .clone()
+                .unwrap_or_default(),
+            attribute_name: vendor_attribute.attribute_name.clone().unwrap_or_default(),
+            attribute_value: hex::decode(
+                vendor_attribute.attribute_value.clone().unwrap_or_default(),
+            )?,
+        };
+        Ok(Self::VendorAttributes(vec![vendor_attribute]))
+    }
+}
+
+impl TryFrom<&VendorAttributeCli> for VendorAttribute {
+    type Error = crate::error::CliError;
+
+    fn try_from(vendor_attribute: &VendorAttributeCli) -> Result<Self, Self::Error> {
+        let vendor_attribute = Self {
+            vendor_identification: vendor_attribute
+                .vendor_identification
+                .clone()
+                .unwrap_or_default(),
+            attribute_name: vendor_attribute.attribute_name.clone().unwrap_or_default(),
+            attribute_value: hex::decode(
+                vendor_attribute.attribute_value.clone().unwrap_or_default(),
+            )?,
+        };
+        Ok(vendor_attribute)
+    }
+}
+
+#[derive(Parser, Default, Debug)]
+pub struct SetOrDeleteAttributes {
+    /// The unique identifier of the cryptographic object.
+    /// If not specified, tags should be specified
+    #[clap(long = "id", short = 'i', group = "id-tags")]
+    pub(crate) id: Option<String>,
+
+    /// Tag to use to retrieve the key when no key id is specified.
+    /// To specify multiple tags, use the option multiple times.
+    #[clap(long = "tag", short = 't', value_name = "TAG", group = "id-tags")]
+    pub(crate) tags: Option<Vec<String>>,
+
+    /// Set the activation date of the key.
+    #[clap(long, short = 'd')]
+    pub(crate) activation_date: Option<u64>,
+
+    /// The cryptographic algorithm used by the key.
+    #[clap(long, short = 'a')]
+    pub(crate) cryptographic_algorithm: Option<CryptographicAlgorithm>,
+
+    /// The length of the cryptographic key.
+    #[clap(long)]
+    pub(crate) cryptographic_length: Option<i32>,
+
+    /// The key usage.
+    #[clap(long, short = 'u')]
+    pub(crate) key_usage: Option<Vec<KeyUsage>>,
+
+    /// The link to the corresponding public key id if any.
+    #[clap(long)]
+    pub(crate) public_key_id: Option<String>,
+
+    /// The link to the corresponding private key id if any.
+    #[clap(long)]
+    pub(crate) private_key_id: Option<String>,
+
+    /// The link to the corresponding certificate id if any.
+    #[clap(long)]
+    pub(crate) certificate_id: Option<String>,
+
+    /// The link to the corresponding PKCS12 certificate id if any.
+    #[clap(long = "p12-id")]
+    pub(crate) pkcs12_certificate_id: Option<String>,
+
+    /// The link to the corresponding PKCS12 password certificate if any.
+    #[clap(long = "p12-pwd")]
+    pub(crate) pkcs12_password_certificate: Option<String>,
+
+    /// The link to the corresponding parent id if any.
+    #[clap(long)]
+    pub(crate) parent_id: Option<String>,
+
+    /// The link to the corresponding child id if any.
+    #[clap(long)]
+    pub(crate) child_id: Option<String>,
+
+    /// The state of the object.
+    #[clap(long, short = 's')]
+    pub(crate) state: Option<StateEnumeration>,
+
+    #[clap(flatten)]
+    pub vendor_attributes: Option<VendorAttributeCli>,
+}
+
+impl SetOrDeleteAttributes {
+    pub(crate) fn get_attributes_from_args(&self) -> CliResult<Vec<Attribute>> {
+        let mut result = Vec::new();
+
+        if let Some(activation_date) = &self.activation_date {
+            let attribute = Attribute::ActivationDate(*activation_date);
+            result.push(attribute);
+        }
+
+        if let Some(cryptographic_algorithm) = &self.cryptographic_algorithm {
+            let attribute = Attribute::CryptographicAlgorithm(cryptographic_algorithm.to_owned());
+            result.push(attribute);
+        }
+
+        if let Some(cryptographic_length) = &self.cryptographic_length {
+            let attribute = Attribute::CryptographicLength(*cryptographic_length);
+            result.push(attribute);
+        }
+
+        if let Some(key_usage) = &self.key_usage {
+            let cryptographic_usage_mask =
+                build_usage_mask_from_key_usage(key_usage).ok_or_else(|| {
+                    crate::error::CliError::Conversion(format!(
+                        "Could not convert {key_usage:?} to cryptographic usage mask"
+                    ))
+                })?;
+            let attribute = Attribute::CryptographicUsageMask(cryptographic_usage_mask);
+            result.push(attribute);
+        }
+
+        if let Some(public_key_id) = &self.public_key_id {
+            let attribute = Attribute::Links(vec![Link {
+                link_type: LinkType::PublicKeyLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(public_key_id.clone()),
+            }]);
+            result.push(attribute);
+        }
+
+        if let Some(private_key_id) = &self.private_key_id {
+            let attribute = Attribute::Links(vec![Link {
+                link_type: LinkType::PrivateKeyLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(
+                    private_key_id.clone(),
+                ),
+            }]);
+            result.push(attribute);
+        }
+
+        if let Some(certificate_id) = &self.certificate_id {
+            let attribute = Attribute::Links(vec![Link {
+                link_type: LinkType::CertificateLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(
+                    certificate_id.clone(),
+                ),
+            }]);
+            result.push(attribute);
+        }
+
+        if let Some(pkcs12_certificate_id) = &self.pkcs12_certificate_id {
+            let attribute = Attribute::Links(vec![Link {
+                link_type: LinkType::PKCS12CertificateLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(
+                    pkcs12_certificate_id.clone(),
+                ),
+            }]);
+            result.push(attribute);
+        }
+
+        if let Some(pkcs12_password_certificate) = &self.pkcs12_password_certificate {
+            let attribute = Attribute::Links(vec![Link {
+                link_type: LinkType::PKCS12PasswordLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(
+                    pkcs12_password_certificate.clone(),
+                ),
+            }]);
+            result.push(attribute);
+        }
+
+        if let Some(parent_id) = &self.parent_id {
+            let attribute = Attribute::Links(vec![Link {
+                link_type: LinkType::ParentLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(parent_id.clone()),
+            }]);
+            result.push(attribute);
+        }
+
+        if let Some(child_id) = &self.child_id {
+            let attribute = Attribute::Links(vec![Link {
+                link_type: LinkType::ChildLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(child_id.clone()),
+            }]);
+            result.push(attribute);
+        }
+
+        if let Some(state) = &self.state {
+            let attribute = Attribute::State(state.to_owned());
+            result.push(attribute);
+        }
+
+        if let Some(vendor_attributes) = &self.vendor_attributes {
+            let attribute = Attribute::try_from(vendor_attributes)?;
+            result.push(attribute);
+        }
+
+        Ok(result)
+    }
+}
+
+/// Set the KMIP object attributes (one or multiple attributes).
+#[derive(Parser, Debug)]
+#[clap(verbatim_doc_comment)]
+pub struct SetAttributesAction {
+    #[clap(flatten)]
+    requested_attributes: SetOrDeleteAttributes,
+}
+
+impl SetAttributesAction {
+    async fn set_attribute(
+        &self,
+        kms_rest_client: &KmsClient,
+        id: &str,
+        attribute: Attribute,
+    ) -> CliResult<()> {
+        let SetAttributeResponse { unique_identifier } = kms_rest_client
+            .set_attribute(SetAttribute {
+                unique_identifier: Some(UniqueIdentifier::TextString(id.to_owned())),
+                new_attribute: attribute.clone(),
+            })
+            .await?;
+        info!("SetAttributes response for {unique_identifier}: {attribute:?}");
+        let mut stdout = console::Stdout::new("Attribute set successfully");
+        stdout.set_tags(self.requested_attributes.tags.as_ref());
+        stdout.set_unique_identifier(id);
+        stdout.set_attribute(attribute);
+        stdout.write()?;
+        Ok(())
+    }
+
+    /// Processes the `SetAttributes` action.
+    ///
+    /// # Errors
+    ///
+    /// This function can return a `CliError` if one of the following conditions occur:
+    ///
+    /// - Either `--id` or one or more `--tag` must be specified.
+    ///
+    pub async fn process(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
+        trace!("SetAttributeAction: {:?}", self);
+        let id = if let Some(key_id) = &self.requested_attributes.id {
+            key_id.clone()
+        } else if let Some(tags) = &self.requested_attributes.tags {
+            serde_json::to_string(&tags)?
+        } else {
+            cli_bail!("Either --id or one or more --tag must be specified")
+        };
+
+        let attributes_to_set = self.requested_attributes.get_attributes_from_args()?;
+        if attributes_to_set.is_empty() {
+            cli_bail!("No attribute specified")
+        }
+
+        for attribute in attributes_to_set {
+            self.set_attribute(kms_rest_client, &id, attribute).await?;
+        }
+
+        Ok(())
+    }
+}
diff --git a/crate/cli/src/actions/shared/unwrap_key.rs b/crate/cli/src/actions/shared/unwrap_key.rs
index 4b735f8da..9735b3d2d 100644
--- a/crate/cli/src/actions/shared/unwrap_key.rs
+++ b/crate/cli/src/actions/shared/unwrap_key.rs
@@ -93,7 +93,7 @@ impl UnwrapKeyAction {
             let key_bytes = general_purpose::STANDARD
                 .decode(b64)
                 .with_context(|| "failed decoding the unwrap key")?;
-            create_symmetric_key_kmip_object(&key_bytes, CryptographicAlgorithm::AES)
+            create_symmetric_key_kmip_object(&key_bytes, CryptographicAlgorithm::AES)?
         } else if let Some(key_id) = &self.unwrap_key_id {
             export_object(kms_rest_client, key_id, ExportObjectParams::default())
                 .await?
diff --git a/crate/cli/src/actions/shared/utils/key_usage.rs b/crate/cli/src/actions/shared/utils/key_usage.rs
index 50600271d..38dae1f29 100644
--- a/crate/cli/src/actions/shared/utils/key_usage.rs
+++ b/crate/cli/src/actions/shared/utils/key_usage.rs
@@ -1,6 +1,8 @@
 use cosmian_kms_client::kmip::kmip_types::CryptographicUsageMask;
+use serde::Deserialize;
+use strum::EnumIter;
 
-#[derive(clap::ValueEnum, Debug, Clone)]
+#[derive(clap::ValueEnum, Deserialize, Debug, Clone, EnumIter, PartialEq, Eq)]
 pub enum KeyUsage {
     Sign,
     Verify,
diff --git a/crate/cli/src/actions/shared/wrap_key.rs b/crate/cli/src/actions/shared/wrap_key.rs
index b67bd84e9..6933b92d7 100644
--- a/crate/cli/src/actions/shared/wrap_key.rs
+++ b/crate/cli/src/actions/shared/wrap_key.rs
@@ -78,6 +78,7 @@ impl WrapKeyAction {
     /// - The key block cannot be wrapped with the wrapping key.
     /// - The wrapped key object cannot be written to the output file.
     /// - The console output cannot be written.
+    #[allow(clippy::print_stdout)]
     pub async fn run(&self, kms_rest_client: &KmsClient) -> CliResult<()> {
         // read the key file
         let mut object = read_object_from_json_ttlv_file(&self.key_file_in)?;
@@ -95,7 +96,7 @@ impl WrapKeyAction {
             let key_bytes = general_purpose::STANDARD
                 .decode(b64)
                 .with_context(|| "failed decoding the wrap key")?;
-            create_symmetric_key_kmip_object(&key_bytes, CryptographicAlgorithm::AES)
+            create_symmetric_key_kmip_object(&key_bytes, CryptographicAlgorithm::AES)?
         } else if let Some(password) = &self.wrap_password {
             let key_bytes = derive_key_from_password::<SYMMETRIC_WRAPPING_KEY_SIZE>(
                 &[0_u8; 16],
@@ -103,7 +104,7 @@ impl WrapKeyAction {
             )?;
 
             let symmetric_key_object =
-                create_symmetric_key_kmip_object(key_bytes.as_ref(), CryptographicAlgorithm::AES);
+                create_symmetric_key_kmip_object(key_bytes.as_ref(), CryptographicAlgorithm::AES)?;
 
             // Print the wrapping key for user.
             println!(
diff --git a/crate/cli/src/actions/symmetric/keys/create_key.rs b/crate/cli/src/actions/symmetric/keys/create_key.rs
index 9cfcd23eb..f1bd1c261 100644
--- a/crate/cli/src/actions/symmetric/keys/create_key.rs
+++ b/crate/cli/src/actions/symmetric/keys/create_key.rs
@@ -1,5 +1,5 @@
 use base64::{engine::general_purpose, Engine as _};
-use clap::Parser;
+use clap::{Parser, ValueEnum};
 use cosmian_kms_client::{
     cosmian_kmip::{
         crypto::symmetric::{create_symmetric_key_kmip_object, symmetric_key_create_request},
@@ -14,7 +14,7 @@ use crate::{
     error::result::{CliResult, CliResultHelper},
 };
 
-#[derive(clap::ValueEnum, Debug, Clone, Copy)]
+#[derive(ValueEnum, Debug, Clone, Copy)]
 pub(crate) enum SymmetricAlgorithm {
     #[cfg(not(feature = "fips"))]
     Chacha20,
@@ -96,7 +96,7 @@ impl CreateKeyAction {
         };
 
         let unique_identifier = if let Some(key_bytes) = key_bytes {
-            let object = create_symmetric_key_kmip_object(key_bytes.as_slice(), algorithm);
+            let object = create_symmetric_key_kmip_object(key_bytes.as_slice(), algorithm)?;
             import_object(
                 kms_rest_client,
                 None,
diff --git a/crate/cli/src/error/mod.rs b/crate/cli/src/error/mod.rs
index 01c8402c2..17f5dc74d 100644
--- a/crate/cli/src/error/mod.rs
+++ b/crate/cli/src/error/mod.rs
@@ -9,6 +9,7 @@ use cosmian_kms_client::{
     },
     ClientError,
 };
+use hex::FromHexError;
 use pem::PemError;
 use thiserror::Error;
 
@@ -184,7 +185,13 @@ impl From<KmipError> for CliError {
 
 impl From<base64::DecodeError> for CliError {
     fn from(e: base64::DecodeError) -> Self {
-        Self::Default(e.to_string())
+        Self::Conversion(e.to_string())
+    }
+}
+
+impl From<FromHexError> for CliError {
+    fn from(e: FromHexError) -> Self {
+        Self::Conversion(e.to_string())
     }
 }
 
diff --git a/crate/cli/src/lib.rs b/crate/cli/src/lib.rs
index 656c7fffb..d7127e33a 100644
--- a/crate/cli/src/lib.rs
+++ b/crate/cli/src/lib.rs
@@ -18,6 +18,8 @@
 
     // restriction lints
     clippy::map_err_ignore,
+    clippy::print_stdout,
+    clippy::redundant_clone
 )]
 #![allow(
     clippy::module_name_repetitions,
diff --git a/crate/cli/src/main.rs b/crate/cli/src/main.rs
index 82ae66c65..bd025d466 100644
--- a/crate/cli/src/main.rs
+++ b/crate/cli/src/main.rs
@@ -14,7 +14,9 @@ use cosmian_kms_cli::{
         markdown::MarkdownAction,
         new_database::NewDatabaseAction,
         rsa::RsaCommands,
-        shared::{GetAttributesAction, LocateObjectsAction},
+        shared::{
+            DeleteAttributesAction, GetAttributesAction, LocateObjectsAction, SetAttributesAction,
+        },
         symmetric::SymmetricCommands,
         version::ServerVersionAction,
     },
@@ -66,6 +68,8 @@ enum CliCommands {
     #[command(subcommand)]
     Ec(EllipticCurveCommands),
     GetAttributes(GetAttributesAction),
+    SetAttributes(SetAttributesAction),
+    DeleteAttributes(DeleteAttributesAction),
     Locate(LocateObjectsAction),
     NewDatabase(NewDatabaseAction),
     #[command(subcommand)]
@@ -129,6 +133,8 @@ async fn main_() -> CliResult<()> {
                 CliCommands::NewDatabase(action) => action.process(&kms_rest_client).await?,
                 CliCommands::ServerVersion(action) => action.process(&kms_rest_client).await?,
                 CliCommands::GetAttributes(action) => action.process(&kms_rest_client).await?,
+                CliCommands::SetAttributes(action) => action.process(&kms_rest_client).await?,
+                CliCommands::DeleteAttributes(action) => action.process(&kms_rest_client).await?,
                 CliCommands::Google(action) => action.process(&conf_path, &kms_rest_client).await?,
                 _ => {
                     tracing::error!("unexpected command");
diff --git a/crate/client/src/access.rs b/crate/client/src/access.rs
index 04b83ac76..3e4c0dfd4 100644
--- a/crate/client/src/access.rs
+++ b/crate/client/src/access.rs
@@ -27,7 +27,7 @@ impl fmt::Display for Access {
             self.user_id,
             self.unique_identifier
                 .as_ref()
-                .map_or_else(|| "[N/A]".to_string(), std::string::ToString::to_string),
+                .map_or_else(|| "[N/A]".to_owned(), std::string::ToString::to_string),
             self.operation_types
         )
     }
diff --git a/crate/client/src/certificate_verifier.rs b/crate/client/src/certificate_verifier.rs
index 84e7a427e..066b678bb 100644
--- a/crate/client/src/certificate_verifier.rs
+++ b/crate/client/src/certificate_verifier.rs
@@ -38,7 +38,7 @@ impl ServerCertVerifier for LeafCertificateVerifier {
         // Verify the leaf certificate
         if !end_entity.eq(&self.expected_cert) {
             return Err(RustTLSError::General(
-                "Leaf certificate doesn't match the expected one".to_string(),
+                "Leaf certificate doesn't match the expected one".to_owned(),
             ))
         }
 
diff --git a/crate/client/src/config.rs b/crate/client/src/config.rs
index 56124b600..e054eb30d 100644
--- a/crate/client/src/config.rs
+++ b/crate/client/src/config.rs
@@ -125,7 +125,7 @@ impl Default for ClientConf {
     fn default() -> Self {
         Self {
             accept_invalid_certs: false,
-            kms_server_url: "http://0.0.0.0:9998".to_string(),
+            kms_server_url: "http://0.0.0.0:9998".to_owned(),
             verified_cert: None,
             kms_access_token: None,
             kms_database_secret: None,
diff --git a/crate/client/src/encodings.rs b/crate/client/src/encodings.rs
index 9bc53b5a6..d6631fd45 100644
--- a/crate/client/src/encodings.rs
+++ b/crate/client/src/encodings.rs
@@ -83,17 +83,17 @@ pub fn objects_from_pem(bytes: &[u8]) -> Result<Vec<Object>, ClientError> {
             }),
             "X509 CRL" => {
                 return Err(ClientError::NotSupported(
-                    "X509 CRL not supported on this server".to_string(),
+                    "X509 CRL not supported on this server".to_owned(),
                 ))
             }
             "NEW CERTIFICATE REQUEST" => {
                 return Err(ClientError::NotSupported(
-                    "NEW CERTIFICATE REQUEST not supported on this server".to_string(),
+                    "NEW CERTIFICATE REQUEST not supported on this server".to_owned(),
                 ))
             }
             "CERTIFICATE REQUEST" => {
                 return Err(ClientError::NotSupported(
-                    "CERTIFICATE REQUEST not supported on this server".to_string(),
+                    "CERTIFICATE REQUEST not supported on this server".to_owned(),
                 ))
             }
             x => {
diff --git a/crate/client/src/kms_rest_client.rs b/crate/client/src/kms_rest_client.rs
index f4a82138f..7296102d8 100644
--- a/crate/client/src/kms_rest_client.rs
+++ b/crate/client/src/kms_rest_client.rs
@@ -1,4 +1,5 @@
 use std::{
+    fmt::Debug,
     fs::File,
     io::{BufReader, Read},
     sync::Arc,
@@ -7,7 +8,10 @@ use std::{
 
 use cosmian_kmip::kmip::{
     kmip_messages::{Message, MessageResponse},
-    kmip_operations::{ReKey, ReKeyResponse},
+    kmip_operations::{
+        DeleteAttribute, DeleteAttributeResponse, ReKey, ReKeyResponse, SetAttribute,
+        SetAttributeResponse,
+    },
 };
 // re-export the kmip module as kmip
 use cosmian_kmip::kmip::{
@@ -228,6 +232,24 @@ impl KmsClient {
             .await
     }
 
+    /// This operation requests the server to either add or modify an attribute. The request contains the Unique Identifier of the Managed Object to which the attribute pertains, along with the attribute and value. If the object did not have any instances of the attribute, one is created. If the object had exactly one instance, then it is modified. If it has more than one instance an error is raised. Read-Only attributes SHALL NOT be added or modified using this operation.
+    pub async fn set_attribute(
+        &self,
+        request: SetAttribute,
+    ) -> Result<SetAttributeResponse, ClientError> {
+        self.post_ttlv::<SetAttribute, SetAttributeResponse>(&request)
+            .await
+    }
+
+    /// This operation requests the server to delete an attribute associated with a Managed Object. The request contains the Unique Identifier of the Managed Object whose attribute is to be deleted, the Current Attribute of the attribute. Attributes that are always REQUIRED to have a value SHALL never be deleted by this operation. Attempting to delete a non-existent attribute or specifying an Current Attribute for which there exists no attribute value SHALL result in an error. If no Current Attribute is specified in the request, and an Attribute Reference is specified, then all instances of the specified attribute SHALL be deleted.
+    pub async fn delete_attribute(
+        &self,
+        request: DeleteAttribute,
+    ) -> Result<DeleteAttributeResponse, ClientError> {
+        self.post_ttlv::<DeleteAttribute, DeleteAttributeResponse>(&request)
+            .await
+    }
+
     /// This operation requests the server to Import a Managed Object specified
     /// by its Unique Identifier. The request specifies the object being
     /// imported and all the attributes to be assigned to the object. The
@@ -594,7 +616,7 @@ impl KmsClient {
             Some(d) => {
                 trace!(
                     "==>\n{}",
-                    serde_json::to_string_pretty(&d).unwrap_or_else(|_| "[N/A]".to_string())
+                    serde_json::to_string_pretty(&d).unwrap_or_else(|_| "[N/A]".to_owned())
                 );
                 self.client.post(server_url).json(d).send().await?
             }
@@ -606,7 +628,7 @@ impl KmsClient {
             let response = response.json::<R>().await?;
             trace!(
                 "<==\n{}",
-                serde_json::to_string_pretty(&response).unwrap_or_else(|_| "[N/A]".to_string())
+                serde_json::to_string_pretty(&response).unwrap_or_else(|_| "[N/A]".to_owned())
             );
             return Ok(response)
         }
@@ -618,9 +640,10 @@ impl KmsClient {
 
     pub async fn post_ttlv<O, R>(&self, kmip_request: &O) -> Result<R, ClientError>
     where
-        O: Serialize,
+        O: Serialize + Debug,
         R: serde::de::DeserializeOwned + Sized + 'static,
     {
+        trace!("kmip_request: {:?}", kmip_request);
         let endpoint = "/kmip/2_1";
         let server_url = format!("{}{endpoint}", self.server_url);
         let mut request = self.client.post(&server_url);
@@ -633,7 +656,7 @@ impl KmsClient {
         }
         trace!(
             "==>\n{}",
-            serde_json::to_string_pretty(&ttlv).unwrap_or_else(|_| "[N/A]".to_string())
+            serde_json::to_string_pretty(&ttlv).unwrap_or_else(|_| "[N/A]".to_owned())
         );
         request = request.json(&ttlv);
 
@@ -649,7 +672,7 @@ impl KmsClient {
             }
             trace!(
                 "<==\n{}",
-                serde_json::to_string_pretty(&ttlv).unwrap_or_else(|_| "[N/A]".to_string())
+                serde_json::to_string_pretty(&ttlv).unwrap_or_else(|_| "[N/A]".to_owned())
             );
             return from_ttlv(&ttlv).map_err(|e| ClientError::ResponseFailed(e.to_string()))
         }
@@ -663,6 +686,7 @@ impl KmsClient {
 /// Some errors are returned by the Middleware without going through our own error manager.
 /// In that case, we make the error clearer here for the client.
 async fn handle_error(endpoint: &str, response: Response) -> Result<String, ClientError> {
+    trace!("Error response received on {endpoint}: Response: {response:?}");
     let status = response.status();
     let text = response.text().await?;
 
@@ -671,8 +695,8 @@ async fn handle_error(endpoint: &str, response: Response) -> Result<String, Clie
         endpoint,
         if text.is_empty() {
             match status {
-                StatusCode::NOT_FOUND => "KMS server endpoint does not exist".to_string(),
-                StatusCode::UNAUTHORIZED => "Bad authorization token".to_string(),
+                StatusCode::NOT_FOUND => "KMS server endpoint does not exist".to_owned(),
+                StatusCode::UNAUTHORIZED => "Bad authorization token".to_owned(),
                 _ => format!("{status} {text}"),
             }
         } else {
diff --git a/crate/kmip/src/crypto/cover_crypt/attributes.rs b/crate/kmip/src/crypto/cover_crypt/attributes.rs
index 662d28e6b..9735e7c27 100644
--- a/crate/kmip/src/crypto/cover_crypt/attributes.rs
+++ b/crate/kmip/src/crypto/cover_crypt/attributes.rs
@@ -31,21 +31,26 @@ pub fn policy_as_vendor_attribute(policy: &Policy) -> Result<VendorAttribute, Km
 
 /// Extract an `CoverCrypt` policy from attributes
 pub fn policy_from_attributes(attributes: &Attributes) -> Result<Policy, KmipError> {
-    if let Some(bytes) =
-        attributes.get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_POLICY)
-    {
-        Policy::parse_and_convert(bytes).map_err(|e| {
-            KmipError::InvalidKmipValue(
-                ErrorReason::Invalid_Attribute_Value,
-                format!("failed deserializing the CoverCrypt Policy from the attributes: {e}"),
-            )
-        })
-    } else {
-        Err(KmipError::InvalidKmipValue(
-            ErrorReason::Invalid_Attribute_Value,
-            "the attributes do not contain a CoverCrypt Policy".to_string(),
-        ))
-    }
+    attributes
+        .get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_POLICY)
+        .map_or_else(
+            || {
+                Err(KmipError::InvalidKmipValue(
+                    ErrorReason::Invalid_Attribute_Value,
+                    "the attributes do not contain a CoverCrypt Policy".to_owned(),
+                ))
+            },
+            |bytes| {
+                Policy::parse_and_convert(bytes).map_err(|e| {
+                    KmipError::InvalidKmipValue(
+                        ErrorReason::Invalid_Attribute_Value,
+                        format!(
+                            "failed deserializing the CoverCrypt Policy from the attributes: {e}"
+                        ),
+                    )
+                })
+            },
+        )
 }
 
 /// Add or replace an `CoverCrypt` policy in attributes in place
@@ -72,7 +77,7 @@ pub fn access_policy_as_vendor_attribute(
 
 /// Convert from `CoverCrypt` policy attributes to vendor attributes
 pub fn attributes_as_vendor_attribute(
-    attributes: Vec<abe_policy::Attribute>,
+    attributes: &[abe_policy::Attribute],
 ) -> Result<VendorAttribute, KmipError> {
     Ok(VendorAttribute {
         vendor_identification: VENDOR_ID_COSMIAN.to_owned(),
@@ -116,30 +121,34 @@ pub fn attributes_from_attributes(
     } else {
         Err(KmipError::InvalidKmipValue(
             ErrorReason::Invalid_Attribute_Value,
-            "the attributes do not contain CoverCrypt (vendor) Attributes".to_string(),
+            "the attributes do not contain CoverCrypt (vendor) Attributes".to_owned(),
         ))
     }
 }
 
 /// Extract an `CoverCrypt` Access policy from attributes
 pub fn access_policy_from_attributes(attributes: &Attributes) -> Result<String, KmipError> {
-    if let Some(bytes) = attributes
+    attributes
         .get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_ACCESS_POLICY)
-    {
-        String::from_utf8(bytes.to_vec()).map_err(|e| {
-            KmipError::InvalidKmipValue(
-                ErrorReason::Invalid_Attribute_Value,
-                format!(
-                    "failed to read Access Policy string from the (vendor) attributes bytes: {e}"
-                ),
-            )
-        })
-    } else {
-        Err(KmipError::InvalidKmipValue(
-            ErrorReason::Invalid_Attribute_Value,
-            "the attributes do not contain an Access Policy".to_string(),
-        ))
-    }
+        .map_or_else(
+            || {
+                Err(KmipError::InvalidKmipValue(
+                    ErrorReason::Invalid_Attribute_Value,
+                    "the attributes do not contain an Access Policy".to_owned(),
+                ))
+            },
+            |bytes| {
+                String::from_utf8(bytes.to_vec()).map_err(|e| {
+                    KmipError::InvalidKmipValue(
+                        ErrorReason::Invalid_Attribute_Value,
+                        format!(
+                            "failed to read Access Policy string from the (vendor) attributes \
+                             bytes: {e}"
+                        ),
+                    )
+                })
+            },
+        )
 }
 
 /// Add or replace an access policy in attributes in place
@@ -174,12 +183,12 @@ pub enum RekeyEditAction {
 
 /// Convert an edit action to a vendor attribute
 pub fn rekey_edit_action_as_vendor_attribute(
-    action: RekeyEditAction,
+    action: &RekeyEditAction,
 ) -> Result<VendorAttribute, KmipError> {
     Ok(VendorAttribute {
         vendor_identification: VENDOR_ID_COSMIAN.to_owned(),
         attribute_name: VENDOR_ATTR_COVER_CRYPT_REKEY_ACTION.to_owned(),
-        attribute_value: serde_json::to_vec(&action).map_err(|e| {
+        attribute_value: serde_json::to_vec(action).map_err(|e| {
             KmipError::InvalidKmipValue(
                 ErrorReason::Invalid_Attribute_Value,
                 format!("failed serializing the CoverCrypt action: {e}"),
@@ -195,19 +204,24 @@ pub fn rekey_edit_action_as_vendor_attribute(
 pub fn rekey_edit_action_from_attributes(
     attributes: &Attributes,
 ) -> Result<RekeyEditAction, KmipError> {
-    if let Some(bytes) = attributes
+    attributes
         .get_vendor_attribute_value(VENDOR_ID_COSMIAN, VENDOR_ATTR_COVER_CRYPT_REKEY_ACTION)
-    {
-        serde_json::from_slice::<RekeyEditAction>(bytes).map_err(|e| {
-            KmipError::InvalidKmipValue(
-                ErrorReason::Invalid_Attribute_Value,
-                format!("failed reading the CoverCrypt action from the attribute bytes: {e}"),
-            )
-        })
-    } else {
-        Err(KmipError::InvalidKmipObject(
-            ErrorReason::Missing_Data,
-            "Missing VENDOR_ATTR_COVER_CRYPT_REKEY_ACTION".to_string(),
-        ))
-    }
+        .map_or_else(
+            || {
+                Err(KmipError::InvalidKmipObject(
+                    ErrorReason::Missing_Data,
+                    "Missing VENDOR_ATTR_COVER_CRYPT_REKEY_ACTION".to_owned(),
+                ))
+            },
+            |bytes| {
+                serde_json::from_slice::<RekeyEditAction>(bytes).map_err(|e| {
+                    KmipError::InvalidKmipValue(
+                        ErrorReason::Invalid_Attribute_Value,
+                        format!(
+                            "failed reading the CoverCrypt action from the attribute bytes: {e}"
+                        ),
+                    )
+                })
+            },
+        )
 }
diff --git a/crate/kmip/src/crypto/cover_crypt/decryption.rs b/crate/kmip/src/crypto/cover_crypt/decryption.rs
index c303e3800..d9762f176 100644
--- a/crate/kmip/src/crypto/cover_crypt/decryption.rs
+++ b/crate/kmip/src/crypto/cover_crypt/decryption.rs
@@ -119,10 +119,12 @@ impl CovercryptDecryption {
         let nb_chunks = {
             let len = de.read_leb128_u64()?;
             ser.write_leb128_u64(len)?;
-            usize::try_from(len).map_err(|_| {
+            usize::try_from(len).map_err(|e| {
                 KmipError::KmipError(
                     ErrorReason::Invalid_Message,
-                    format!("size of vector is too big for architecture: {len} bytes"),
+                    format!(
+                        "size of vector is too big for architecture: {len} bytes. Error: {e:?}"
+                    ),
                 )
             })?
         };
@@ -169,7 +171,7 @@ impl CovercryptDecryption {
         let cleartext_header = cleartext_header.ok_or_else(|| {
             KmipError::KmipError(
                 ErrorReason::Internal_Server_Error,
-                "unable to recover any header".to_string(),
+                "unable to recover any header".to_owned(),
             )
         })?;
 
@@ -190,7 +192,7 @@ impl DecryptionSystem for CovercryptDecryption {
         let encrypted_bytes = request.data.as_ref().ok_or_else(|| {
             KmipError::KmipError(
                 ErrorReason::Invalid_Message,
-                "The decryption request should contain encrypted data".to_string(),
+                "The decryption request should contain encrypted data".to_owned(),
             )
         })?;
 
diff --git a/crate/kmip/src/crypto/cover_crypt/encryption.rs b/crate/kmip/src/crypto/cover_crypt/encryption.rs
index e075387b9..6837b8965 100644
--- a/crate/kmip/src/crypto/cover_crypt/encryption.rs
+++ b/crate/kmip/src/crypto/cover_crypt/encryption.rs
@@ -46,7 +46,7 @@ impl CoverCryptEncryption {
         let policy = policy_from_attributes(public_key_attributes.ok_or_else(|| {
             KmipError::KmipError(
                 ErrorReason::Attribute_Not_Found,
-                "the master public key does not have attributes with the Policy".to_string(),
+                "the master public key does not have attributes with the Policy".to_owned(),
             )
         })?)?;
 
@@ -101,10 +101,12 @@ impl CoverCryptEncryption {
         let nb_chunks = {
             let len = de.read_leb128_u64()?;
             ser.write_leb128_u64(len)?;
-            usize::try_from(len).map_err(|_| {
+            usize::try_from(len).map_err(|e| {
                 KmipError::KmipError(
                     ErrorReason::Invalid_Message,
-                    format!("size of vector is too big for architecture: {len} bytes"),
+                    format!(
+                        "size of vector is too big for architecture: {len} bytes. Error: {e:?}"
+                    ),
                 )
             })?
         };
@@ -175,7 +177,7 @@ impl EncryptionSystem for CoverCryptEncryption {
                 .ok_or_else(|| {
                     KmipError::KmipError(
                         ErrorReason::Invalid_Attribute_Value,
-                        "encryption policy missing".to_string(),
+                        "encryption policy missing".to_owned(),
                     )
                 })?;
         let encryption_policy = AccessPolicy::from_boolean_expression(encryption_policy_string)
diff --git a/crate/kmip/src/crypto/cover_crypt/kmip_requests.rs b/crate/kmip/src/crypto/cover_crypt/kmip_requests.rs
index e6885e09e..66b20107f 100644
--- a/crate/kmip/src/crypto/cover_crypt/kmip_requests.rs
+++ b/crate/kmip/src/crypto/cover_crypt/kmip_requests.rs
@@ -114,6 +114,7 @@ pub fn build_import_decryption_private_key_request<T: IntoIterator<Item = impl A
         private_key.to_vec()
     });
 
+    let cryptographic_length = Some(i32::try_from(private_key.len())? * 8);
     Ok(Import {
         unique_identifier: UniqueIdentifier::TextString(unique_identifier.unwrap_or_default()),
         object_type: ObjectType::PrivateKey,
@@ -134,7 +135,7 @@ pub fn build_import_decryption_private_key_request<T: IntoIterator<Item = impl A
                     key_material: KeyMaterial::ByteString(key),
                     attributes: Some(Box::new(attributes)),
                 },
-                cryptographic_length: Some(private_key.len() as i32 * 8),
+                cryptographic_length,
                 key_wrapping_data: if is_wrapped {
                     Some(Box::new(KeyWrappingData {
                         wrapping_method: WrappingMethod::Encrypt,
@@ -190,6 +191,7 @@ pub fn build_import_private_key_request<T: IntoIterator<Item = impl AsRef<str>>>
         private_key.to_vec()
     });
 
+    let cryptographic_length = Some(i32::try_from(private_key.len())? * 8);
     Ok(Import {
         unique_identifier: UniqueIdentifier::TextString(unique_identifier.unwrap_or_default()),
         object_type: ObjectType::PrivateKey,
@@ -209,7 +211,7 @@ pub fn build_import_private_key_request<T: IntoIterator<Item = impl AsRef<str>>>
                     key_material: KeyMaterial::ByteString(key),
                     attributes: Some(Box::new(attributes)),
                 },
-                cryptographic_length: Some(private_key.len() as i32 * 8),
+                cryptographic_length,
                 key_wrapping_data: if is_wrapped {
                     Some(Box::new(KeyWrappingData {
                         wrapping_method: WrappingMethod::Encrypt,
@@ -249,6 +251,7 @@ pub fn build_import_public_key_request<T: IntoIterator<Item = impl AsRef<str>>>(
     };
     attributes.set_tags(tags)?;
 
+    let cryptographic_length = Some(i32::try_from(public_key.len())? * 8);
     Ok(Import {
         unique_identifier: UniqueIdentifier::TextString(unique_identifier.unwrap_or_default()),
         object_type: ObjectType::PublicKey,
@@ -264,7 +267,7 @@ pub fn build_import_public_key_request<T: IntoIterator<Item = impl AsRef<str>>>(
                     key_material: KeyMaterial::ByteString(Zeroizing::from(public_key.to_vec())),
                     attributes: Some(Box::new(attributes)),
                 },
-                cryptographic_length: Some(public_key.len() as i32 * 8),
+                cryptographic_length,
                 key_wrapping_data: None,
             },
         },
@@ -288,7 +291,7 @@ pub fn build_locate_symmetric_key_request(access_policy: &str) -> Result<Locate,
 /// Build a `Revoke` request to locate an `CoverCrypt` User Decryption Key
 pub fn build_destroy_key_request(unique_identifier: &str) -> Result<Destroy, KmipError> {
     Ok(Destroy {
-        unique_identifier: Some(UniqueIdentifier::TextString(unique_identifier.to_string())),
+        unique_identifier: Some(UniqueIdentifier::TextString(unique_identifier.to_owned())),
     })
 }
 
@@ -301,11 +304,11 @@ pub fn build_destroy_key_request(unique_identifier: &str) -> Result<Destroy, Kmi
 /// The routine will then locate and renew all user decryption keys with those `CoverCrypt` attributes
 pub fn build_rekey_keypair_request(
     master_private_key_unique_identifier: &str,
-    action: RekeyEditAction,
+    action: &RekeyEditAction,
 ) -> Result<ReKeyKeyPair, KmipError> {
     Ok(ReKeyKeyPair {
         private_key_unique_identifier: Some(UniqueIdentifier::TextString(
-            master_private_key_unique_identifier.to_string(),
+            master_private_key_unique_identifier.to_owned(),
         )),
         private_key_attributes: Some(Attributes {
             object_type: Some(ObjectType::PrivateKey),
diff --git a/crate/kmip/src/crypto/cover_crypt/master_keys.rs b/crate/kmip/src/crypto/cover_crypt/master_keys.rs
index cc87c948f..64d0747ac 100644
--- a/crate/kmip/src/crypto/cover_crypt/master_keys.rs
+++ b/crate/kmip/src/crypto/cover_crypt/master_keys.rs
@@ -30,9 +30,9 @@ pub fn create_master_keypair(
     cover_crypt: &Covercrypt,
     private_key_uid: &str,
     public_key_uid: &str,
-    common_attributes: Option<Attributes>,
-    private_key_attributes: Option<Attributes>,
-    public_key_attributes: Option<Attributes>,
+    common_attributes: &Option<Attributes>,
+    private_key_attributes: &Option<Attributes>,
+    public_key_attributes: &Option<Attributes>,
 ) -> Result<KeyPair, KmipError> {
     let any_attributes = common_attributes
         .as_ref()
@@ -109,9 +109,10 @@ fn create_master_private_key_object(
     attributes.link = Some(vec![Link {
         link_type: LinkType::PublicKeyLink,
         linked_object_identifier: LinkedObjectIdentifier::TextString(
-            master_public_key_uid.to_string(),
+            master_public_key_uid.to_owned(),
         ),
     }]);
+    let cryptographic_length = Some(i32::try_from(key.len())? * 8);
     Ok(Object::PrivateKey {
         key_block: KeyBlock {
             cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
@@ -121,7 +122,7 @@ fn create_master_private_key_object(
                 key_material: KeyMaterial::ByteString(Zeroizing::from(key.to_vec())),
                 attributes: Some(Box::new(attributes)),
             },
-            cryptographic_length: Some(key.len() as i32 * 8),
+            cryptographic_length,
             key_wrapping_data: None,
         },
     })
@@ -148,9 +149,10 @@ fn create_master_public_key_object(
     attributes.link = Some(vec![Link {
         link_type: LinkType::PrivateKeyLink,
         linked_object_identifier: LinkedObjectIdentifier::TextString(
-            master_private_key_uid.to_string(),
+            master_private_key_uid.to_owned(),
         ),
     }]);
+    let cryptographic_length = Some(i32::try_from(key.len())? * 8);
     Ok(Object::PublicKey {
         key_block: KeyBlock {
             cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
@@ -160,7 +162,7 @@ fn create_master_public_key_object(
                 key_material: KeyMaterial::ByteString(Zeroizing::from(key.to_vec())),
                 attributes: Some(Box::new(attributes)),
             },
-            cryptographic_length: Some(key.len() as i32 * 8),
+            cryptographic_length,
             key_wrapping_data: None,
         },
     })
diff --git a/crate/kmip/src/crypto/cover_crypt/secret_key.rs b/crate/kmip/src/crypto/cover_crypt/secret_key.rs
index 1a7484b7d..c33518a5c 100644
--- a/crate/kmip/src/crypto/cover_crypt/secret_key.rs
+++ b/crate/kmip/src/crypto/cover_crypt/secret_key.rs
@@ -50,7 +50,7 @@ pub fn wrapped_secret_key(
         ..Attributes::default()
     };
 
-    let cryptographic_length = sk.encrypted_symmetric_key.len() as i32 * 8;
+    let cryptographic_length = Some(i32::try_from(sk.encrypted_symmetric_key.len())? * 8);
     let key_value = KeyValue {
         key_material: KeyMaterial::ByteString(Zeroizing::from(sk.encrypted_symmetric_key)),
         attributes: Some(Box::new(wrapped_key_attributes)),
@@ -67,7 +67,7 @@ pub fn wrapped_secret_key(
             key_format_type: KeyFormatType::TransparentSymmetricKey,
             key_compression_type: None,
             key_value,
-            cryptographic_length: Some(cryptographic_length),
+            cryptographic_length,
             key_wrapping_data: Some(Box::new(key_wrapping_data)),
         },
     })
@@ -103,7 +103,7 @@ fn prepare_symmetric_key(
     let policy = policy_from_attributes(public_key_attributes.ok_or_else(|| {
         KmipError::InvalidKmipObject(
             ErrorReason::Attribute_Not_Found,
-            "the master public key does not have attributes with the Policy".to_string(),
+            "the master public key does not have attributes with the Policy".to_owned(),
         )
     })?)?;
 
@@ -138,14 +138,14 @@ impl TryFrom<&KeyBlock> for CoverCryptSymmetricKey {
         {
             return Err(KmipError::InvalidKmipObject(
                 ErrorReason::Invalid_Data_Type,
-                "this Secret Key does not contain an CoverCrypt key".to_string(),
+                "this Secret Key does not contain an CoverCrypt key".to_owned(),
             ))
         }
 
         if sk.key_wrapping_data.is_some() {
             return Err(KmipError::KmipNotSupported(
                 ErrorReason::Key_Wrap_Type_Not_Supported,
-                "unwrapping an CoverCrypt Secret Key is not yet supported".to_string(),
+                "unwrapping an CoverCrypt Secret Key is not yet supported".to_owned(),
             ))
         }
         serde_json::from_slice::<Self>(match &sk.key_value.key_material {
diff --git a/crate/kmip/src/crypto/cover_crypt/user_key.rs b/crate/kmip/src/crypto/cover_crypt/user_key.rs
index 5e8abccc2..8a2a2abfe 100644
--- a/crate/kmip/src/crypto/cover_crypt/user_key.rs
+++ b/crate/kmip/src/crypto/cover_crypt/user_key.rs
@@ -134,9 +134,10 @@ impl UserDecryptionKeysHandler {
         attributes.link = Some(vec![Link {
             link_type: LinkType::ParentLink,
             linked_object_identifier: LinkedObjectIdentifier::TextString(
-                master_private_key_id.to_string(),
+                master_private_key_id.to_owned(),
             ),
         }]);
+        let cryptographic_length = Some(i32::try_from(user_decryption_key_len)? * 8);
         Ok(Object::PrivateKey {
             key_block: KeyBlock {
                 cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
@@ -146,7 +147,7 @@ impl UserDecryptionKeysHandler {
                     key_material: KeyMaterial::ByteString(user_decryption_key_bytes),
                     attributes: Some(Box::new(attributes)),
                 },
-                cryptographic_length: Some(user_decryption_key_len as i32 * 8),
+                cryptographic_length,
                 key_wrapping_data: None,
             },
         })
@@ -184,8 +185,7 @@ impl UserDecryptionKeysHandler {
                 format!("cover crypt: failed serializing the user key: {e}"),
             )
         })?;
-        let user_decryption_key_len = user_decryption_key_bytes.len() as i32 * 8;
-
+        let cryptographic_length = Some(i32::try_from(user_decryption_key_bytes.len())? * 8);
         Ok(Object::PrivateKey {
             key_block: KeyBlock {
                 cryptographic_algorithm: Some(CryptographicAlgorithm::CoverCrypt),
@@ -195,7 +195,7 @@ impl UserDecryptionKeysHandler {
                     key_material: KeyMaterial::ByteString(user_decryption_key_bytes),
                     attributes: Some(Box::new(usk_attributes)),
                 },
-                cryptographic_length: Some(user_decryption_key_len),
+                cryptographic_length,
                 key_wrapping_data: None,
             },
         })
diff --git a/crate/kmip/src/crypto/dh_shared_keys.rs b/crate/kmip/src/crypto/dh_shared_keys.rs
index 159463677..f0fe8ee9c 100644
--- a/crate/kmip/src/crypto/dh_shared_keys.rs
+++ b/crate/kmip/src/crypto/dh_shared_keys.rs
@@ -27,10 +27,10 @@ impl TryFrom<&EnclaveSharedKeyCreateRequest> for VendorAttribute {
         Ok(Self {
             vendor_identification: VENDOR_ID_COSMIAN.to_owned(),
             attribute_name: "enclave_shared_key_create_request".to_owned(),
-            attribute_value: serde_json::to_vec(&request).map_err(|_| {
+            attribute_value: serde_json::to_vec(&request).map_err(|e| {
                 KmipError::InvalidKmipObject(
                     ErrorReason::Invalid_Attribute_Value,
-                    "failed serializing the shared key setup value".to_string(),
+                    format!("failed serializing the shared key setup value. Error: {e:?}"),
                 )
             })?,
         })
@@ -46,13 +46,13 @@ impl TryFrom<&VendorAttribute> for EnclaveSharedKeyCreateRequest {
         {
             return Err(KmipError::InvalidKmipObject(
                 ErrorReason::Invalid_Attribute_Value,
-                "the attributes in not a shared key create request".to_string(),
+                "the attributes in not a shared key create request".to_owned(),
             ))
         }
-        serde_json::from_slice::<Self>(&attribute.attribute_value).map_err(|_| {
+        serde_json::from_slice::<Self>(&attribute.attribute_value).map_err(|e| {
             KmipError::InvalidKmipObject(
                 ErrorReason::Invalid_Attribute_Value,
-                "failed deserializing the Shared Key Create Request".to_string(),
+                format!("failed deserializing the Shared Key Create Request. Error: {e:?}"),
             )
         })
     }
@@ -67,7 +67,7 @@ impl TryFrom<&Attributes> for EnclaveSharedKeyCreateRequest {
                 ErrorReason::Invalid_Attribute_Value,
                 "the attributes do not contain any vendor attribute, hence no shared key setup \
                  data"
-                    .to_string(),
+                    .to_owned(),
             )
         })?;
 
@@ -82,7 +82,7 @@ impl TryFrom<&Attributes> for EnclaveSharedKeyCreateRequest {
                     ErrorReason::Invalid_Attribute_Value,
                     "the attributes do not contain any vendor attribute, hence no shared key \
                      setup data"
-                        .to_string(),
+                        .to_owned(),
                 )
             })?;
         Self::try_from(va)
diff --git a/crate/kmip/src/crypto/elliptic_curves/ecies/mod.rs b/crate/kmip/src/crypto/elliptic_curves/ecies/mod.rs
index 908f2e10e..dd815c4db 100644
--- a/crate/kmip/src/crypto/elliptic_curves/ecies/mod.rs
+++ b/crate/kmip/src/crypto/elliptic_curves/ecies/mod.rs
@@ -9,8 +9,8 @@ mod standard_curves;
 /// Encrypt `plaintext` data using `pubkey` public key following ECIES.
 ///
 /// When using Curve25519 (X25519 or Ed25519 which is converted to X25519),
-/// SalsaSealBox is used. The implementation is compatible with that of libsodium:
-/// the hashing algorithm is set to Blake2b and the AEAD to Salsa20Poly1305.
+/// `SalsaSealBox` is used. The implementation is compatible with that of libsodium:
+/// the hashing algorithm is set to Blake2b and the AEAD to `Salsa20Poly1305`.
 ///
 /// When using standard curves, the hashing algorithm is SHAKE128, the
 /// AEAD is AES 128 GCM and the following ECIES algorithm is used:
@@ -42,8 +42,8 @@ pub fn ecies_encrypt(public_key: &PKey<Public>, plaintext: &[u8]) -> Result<Vec<
 /// Decrypt `ciphertext` data using `privkey` private key following ECIES.
 ///
 /// When using Curve25519 (X25519 or Ed25519 which is converted to X25519),
-/// SalsaSealBox is used. The implementation is compatible with that of libsodium:
-/// the hashing algorithm is set to Blake2b and the AEAD to Salsa20Poly1305.
+/// `SalsaSealBox` is used. The implementation is compatible with that of libsodium:
+/// the hashing algorithm is set to Blake2b and the AEAD to `Salsa20Poly1305`.
 ///
 /// When using standard curves, the hashing algorithm is SHAKE128, the
 /// AEAD is AES 128 GCM and the following ECIES algorithm is used:
diff --git a/crate/kmip/src/crypto/elliptic_curves/ecies/salsa_sealbox.rs b/crate/kmip/src/crypto/elliptic_curves/ecies/salsa_sealbox.rs
index 284f78bb8..ab888042a 100644
--- a/crate/kmip/src/crypto/elliptic_curves/ecies/salsa_sealbox.rs
+++ b/crate/kmip/src/crypto/elliptic_curves/ecies/salsa_sealbox.rs
@@ -17,8 +17,11 @@ use crate::{
 
 #[allow(non_snake_case)]
 /// Encrypt `plaintext` data using `pubkey`, a Curve 25519 public key, following ECIES
-/// in a way that is compatible with libsodium SalsaSealBox.
-pub fn sealbox_encrypt(public_key: &PKey<Public>, plaintext: &[u8]) -> Result<Vec<u8>, KmipError> {
+/// in a way that is compatible with libsodium `SalsaSealBox`.
+pub(crate) fn sealbox_encrypt(
+    public_key: &PKey<Public>,
+    plaintext: &[u8],
+) -> Result<Vec<u8>, KmipError> {
     let ciphertext = match public_key.id() {
         Id::ED25519 => {
             trace!("encrypt: Ed25519");
@@ -51,8 +54,8 @@ pub fn sealbox_encrypt(public_key: &PKey<Public>, plaintext: &[u8]) -> Result<Ve
 }
 
 /// Decrypt `ciphertext` data using `privkey`, a curve 25519 private key following ECIES
-/// in a way compatible with Salsa SealBox provided by libsodium.
-pub fn sealbox_decrypt(
+/// in a way compatible with Salsa `SealBox` provided by libsodium.
+pub(crate) fn sealbox_decrypt(
     private_key: &PKey<Private>,
     ciphertext: &[u8],
 ) -> Result<Zeroizing<Vec<u8>>, KmipError> {
diff --git a/crate/kmip/src/crypto/elliptic_curves/ecies/standard_curves.rs b/crate/kmip/src/crypto/elliptic_curves/ecies/standard_curves.rs
index e833c9492..6c830c6ec 100644
--- a/crate/kmip/src/crypto/elliptic_curves/ecies/standard_curves.rs
+++ b/crate/kmip/src/crypto/elliptic_curves/ecies/standard_curves.rs
@@ -72,7 +72,7 @@ fn ecies_get_key(
 /// Notice we don't send the IV since it is derived by hashing the public key as
 /// well as the ephemeral public key.
 #[allow(non_snake_case)]
-pub fn ecies_encrypt(pubkey: &PKey<Public>, plaintext: &[u8]) -> Result<Vec<u8>, KmipError> {
+pub(crate) fn ecies_encrypt(pubkey: &PKey<Public>, plaintext: &[u8]) -> Result<Vec<u8>, KmipError> {
     let mut ctx = BigNumContext::new_secure()?;
     let Q = pubkey.ec_key()?;
     let curve = Q.group();
@@ -110,7 +110,7 @@ pub fn ecies_encrypt(pubkey: &PKey<Public>, plaintext: &[u8]) -> Result<Vec<u8>,
 /// The IV for decryption is computed by taking the hash of the recipient public
 /// key and the ephemeral public key.
 #[allow(non_snake_case)]
-pub fn ecies_decrypt(
+pub(crate) fn ecies_decrypt(
     private_key: &PKey<Private>,
     ciphertext: &[u8],
 ) -> Result<Zeroizing<Vec<u8>>, KmipError> {
@@ -121,7 +121,7 @@ pub fn ecies_decrypt(
 
     // OpenSSL stored compressed coordinates with one extra byte for some
     // reason hence the + 1 at the end.
-    let pubkey_vec_size = idiv_ceil(curve.order_bits() as usize, 8) + 1;
+    let pubkey_vec_size = idiv_ceil(usize::try_from(curve.order_bits())?, 8) + 1;
     if ciphertext.len() <= pubkey_vec_size + aead.tag_size() {
         kmip_bail!("ECIES: Decryption error: invalid ciphertext")
     }
@@ -161,9 +161,9 @@ fn aead_and_digest(curve: &EcGroupRef) -> Result<(AeadCipher, MessageDigest), Km
     Ok((aead, md))
 }
 
+#[allow(clippy::unwrap_used)]
 #[cfg(test)]
 mod tests {
-    use std::ops::Deref;
 
     use openssl::{
         ec::{EcGroup, EcKey},
@@ -181,12 +181,12 @@ mod tests {
         let pubkey = PKey::from_ec_key(ec_pubkey).unwrap();
         let privkey = PKey::from_ec_key(ec_privkey).unwrap();
 
-        let plaintext = "i love pancakes".as_bytes();
+        let plaintext = b"i love pancakes";
 
         let ct = ecies_encrypt(&pubkey, plaintext).unwrap();
         let pt = ecies_decrypt(&privkey, &ct).unwrap();
 
-        assert_eq!(plaintext, pt.deref());
+        assert_eq!(plaintext.to_vec(), *pt);
     }
 
     #[test]
diff --git a/crate/kmip/src/crypto/elliptic_curves/kmip_requests.rs b/crate/kmip/src/crypto/elliptic_curves/kmip_requests.rs
index 530ac9f16..fe774cb7c 100644
--- a/crate/kmip/src/crypto/elliptic_curves/kmip_requests.rs
+++ b/crate/kmip/src/crypto/elliptic_curves/kmip_requests.rs
@@ -65,7 +65,8 @@ fn build_mask_from_curve(
 /// - NIST.SP.800-186 - Section 3.1.2 table 2.
 /// - NIST.FIPS.186-5
 #[cfg(not(feature = "fips"))]
-fn build_mask_from_curve(
+#[allow(clippy::unnecessary_wraps, clippy::missing_const_for_fn)]
+const fn build_mask_from_curve(
     _curve: RecommendedCurve,
     _is_private_mask: bool,
 ) -> Result<CryptographicUsageMask, KmipError> {
@@ -149,7 +150,7 @@ pub fn create_ec_key_pair_request<T: IntoIterator<Item = impl AsRef<str>>>(
 #[must_use]
 pub fn get_private_key_request(uid: &str) -> Get {
     Get {
-        unique_identifier: Some(UniqueIdentifier::TextString(uid.to_string())),
+        unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
         key_format_type: Some(KeyFormatType::TransparentECPrivateKey),
         ..Get::default()
     }
@@ -158,7 +159,7 @@ pub fn get_private_key_request(uid: &str) -> Get {
 #[must_use]
 pub fn get_public_key_request(uid: &str) -> Get {
     Get {
-        unique_identifier: Some(UniqueIdentifier::TextString(uid.to_string())),
+        unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
         key_format_type: Some(KeyFormatType::TransparentECPublicKey),
         ..Get::default()
     }
diff --git a/crate/kmip/src/crypto/elliptic_curves/mod.rs b/crate/kmip/src/crypto/elliptic_curves/mod.rs
index 7432dedb0..3bb261e16 100644
--- a/crate/kmip/src/crypto/elliptic_curves/mod.rs
+++ b/crate/kmip/src/crypto/elliptic_curves/mod.rs
@@ -7,7 +7,7 @@ pub mod ecies;
 pub mod kmip_requests;
 pub mod operation;
 
-// Montgomerry curves key length.
+// Montgomery curves key length.
 pub const X25519_PRIVATE_KEY_LENGTH: usize = 0x20;
 pub const X25519_PUBLIC_KEY_LENGTH: usize = 0x20;
 pub const X448_PRIVATE_KEY_LENGTH: usize = 0x38;
diff --git a/crate/kmip/src/crypto/elliptic_curves/operation.rs b/crate/kmip/src/crypto/elliptic_curves/operation.rs
index d7ab4210a..2550a260e 100644
--- a/crate/kmip/src/crypto/elliptic_curves/operation.rs
+++ b/crate/kmip/src/crypto/elliptic_curves/operation.rs
@@ -16,6 +16,7 @@ use crate::crypto::elliptic_curves::{
 };
 use crate::{
     crypto::secret::SafeBigUint,
+    error::result::KmipResult,
     kmip::{
         kmip_data_structures::{KeyBlock, KeyMaterial, KeyValue},
         kmip_objects::{Object, ObjectType},
@@ -126,15 +127,16 @@ pub fn to_ec_public_key(
     curve: RecommendedCurve,
     algorithm: Option<CryptographicAlgorithm>,
     public_key_mask: Option<CryptographicUsageMask>,
-) -> Object {
-    let cryptographic_length_in_bits = bytes.len() as i32 * 8;
+) -> KmipResult<Object> {
+    let cryptographic_length = Some(i32::try_from(bytes.len())? * 8);
     trace!(
-        "to_ec_public_key: bytes len: {}, bits: {}",
-        cryptographic_length_in_bits,
+        "to_ec_public_key: bytes len: {:?}, bits: {}",
+        cryptographic_length,
         pkey_bits_number
     );
 
-    Object::PublicKey {
+    let q_length = Some(i32::try_from(pkey_bits_number)?);
+    Ok(Object::PublicKey {
         key_block: KeyBlock {
             cryptographic_algorithm: algorithm,
             key_format_type: KeyFormatType::TransparentECPublicKey,
@@ -147,7 +149,7 @@ pub fn to_ec_public_key(
                 attributes: Some(Box::new(Attributes {
                     object_type: Some(ObjectType::PublicKey),
                     cryptographic_algorithm: algorithm,
-                    cryptographic_length: Some(cryptographic_length_in_bits),
+                    cryptographic_length,
                     cryptographic_usage_mask: public_key_mask,
                     vendor_attributes: None,
                     key_format_type: Some(KeyFormatType::TransparentECPublicKey),
@@ -156,22 +158,22 @@ pub fn to_ec_public_key(
                         ..CryptographicParameters::default()
                     })),
                     cryptographic_domain_parameters: Some(CryptographicDomainParameters {
-                        q_length: Some(pkey_bits_number as i32),
+                        q_length,
                         recommended_curve: Some(curve),
                     }),
                     link: Some(vec![Link {
                         link_type: LinkType::PrivateKeyLink,
                         linked_object_identifier: LinkedObjectIdentifier::TextString(
-                            private_key_uid.to_string(),
+                            private_key_uid.to_owned(),
                         ),
                     }]),
                     ..Attributes::default()
                 })),
             },
-            cryptographic_length: Some(cryptographic_length_in_bits),
+            cryptographic_length,
             key_wrapping_data: None,
         },
-    }
+    })
 }
 
 /// Convert to an Elliptic Curve KMIP Private Key.
@@ -190,16 +192,17 @@ pub fn to_ec_private_key(
     curve: RecommendedCurve,
     algorithm: Option<CryptographicAlgorithm>,
     private_key_mask: Option<CryptographicUsageMask>,
-) -> Object {
-    let cryptographic_length_in_bits = bytes.len() as i32 * 8;
+) -> KmipResult<Object> {
+    let cryptographic_length = Some(i32::try_from(bytes.len())? * 8);
 
     trace!(
-        "to_ec_private_key: bytes len: {}, bits: {}",
-        cryptographic_length_in_bits,
+        "to_ec_private_key: bytes len: {:?}, bits: {}",
+        cryptographic_length,
         pkey_bits_number
     );
 
-    Object::PrivateKey {
+    let q_length = Some(i32::try_from(pkey_bits_number)?);
+    Ok(Object::PrivateKey {
         key_block: KeyBlock {
             cryptographic_algorithm: algorithm,
             key_format_type: KeyFormatType::TransparentECPrivateKey,
@@ -212,7 +215,7 @@ pub fn to_ec_private_key(
                 attributes: Some(Box::new(Attributes {
                     object_type: Some(ObjectType::PrivateKey),
                     cryptographic_algorithm: algorithm,
-                    cryptographic_length: Some(cryptographic_length_in_bits),
+                    cryptographic_length,
                     cryptographic_usage_mask: private_key_mask,
                     vendor_attributes: None,
                     key_format_type: Some(KeyFormatType::TransparentECPrivateKey),
@@ -221,22 +224,22 @@ pub fn to_ec_private_key(
                         ..CryptographicParameters::default()
                     })),
                     cryptographic_domain_parameters: Some(CryptographicDomainParameters {
-                        q_length: Some(pkey_bits_number as i32),
+                        q_length,
                         recommended_curve: Some(curve),
                     }),
                     link: Some(vec![Link {
                         link_type: LinkType::PublicKeyLink,
                         linked_object_identifier: LinkedObjectIdentifier::TextString(
-                            public_key_uid.to_string(),
+                            public_key_uid.to_owned(),
                         ),
                     }]),
                     ..Attributes::default()
                 })),
             },
-            cryptographic_length: Some(cryptographic_length_in_bits),
+            cryptographic_length,
             key_wrapping_data: None,
         },
-    }
+    })
 }
 
 /// Generate an X25519 Key Pair. Not FIPS 140-3 compliant.
@@ -257,7 +260,7 @@ pub fn create_x25519_key_pair(
         RecommendedCurve::CURVE25519,
         algorithm,
         public_key_mask,
-    );
+    )?;
 
     let private_key = to_ec_private_key(
         &Zeroizing::from(private_key.raw_private_key()?),
@@ -266,7 +269,7 @@ pub fn create_x25519_key_pair(
         RecommendedCurve::CURVE25519,
         algorithm,
         private_key_mask,
-    );
+    )?;
 
     Ok(KeyPair::new(private_key, public_key))
 }
@@ -289,7 +292,7 @@ pub fn create_x448_key_pair(
         RecommendedCurve::CURVE448,
         algorithm,
         public_key_mask,
-    );
+    )?;
 
     let private_key = to_ec_private_key(
         &Zeroizing::from(private_key.raw_private_key()?),
@@ -298,7 +301,7 @@ pub fn create_x448_key_pair(
         RecommendedCurve::CURVE448,
         algorithm,
         private_key_mask,
-    );
+    )?;
 
     Ok(KeyPair::new(private_key, public_key))
 }
@@ -336,7 +339,7 @@ pub fn create_ed25519_key_pair(
         RecommendedCurve::CURVEED25519,
         algorithm,
         public_key_mask,
-    );
+    )?;
     trace!("create_ed25519_key_pair: public_key OK");
 
     let private_key = to_ec_private_key(
@@ -346,7 +349,7 @@ pub fn create_ed25519_key_pair(
         RecommendedCurve::CURVEED25519,
         algorithm,
         private_key_mask,
-    );
+    )?;
     trace!("create_ed25519_key_pair: private_key OK");
 
     Ok(KeyPair::new(private_key, public_key))
@@ -385,7 +388,7 @@ pub fn create_ed448_key_pair(
         RecommendedCurve::CURVEED448,
         algorithm,
         public_key_mask,
-    );
+    )?;
     trace!("create_ed448_key_pair: public_key OK");
 
     let private_key = to_ec_private_key(
@@ -395,7 +398,7 @@ pub fn create_ed448_key_pair(
         RecommendedCurve::CURVEED448,
         algorithm,
         private_key_mask,
-    );
+    )?;
     trace!("create_ed448_key_pair: private_key OK");
 
     Ok(KeyPair::new(private_key, public_key))
@@ -438,14 +441,15 @@ pub fn create_approved_ecc_key_pair(
 
     trace!("create_approved_ecc_key_pair: ec key OK");
 
+    let pkey_bits_number = u32::try_from(ec_private_key.private_key().num_bits())?;
     let private_key = to_ec_private_key(
         &Zeroizing::from(ec_private_key.private_key().to_vec()),
-        ec_private_key.private_key().num_bits() as u32,
+        pkey_bits_number,
         public_key_uid,
         curve,
         algorithm,
         private_key_mask,
-    );
+    )?;
     trace!("create_approved_ecc_key_pair: private key converted OK");
 
     let mut ctx = BigNumContext::new()?;
@@ -453,17 +457,18 @@ pub fn create_approved_ecc_key_pair(
         &ec_private_key
             .public_key()
             .to_bytes(&group, PointConversionForm::COMPRESSED, &mut ctx)?,
-        ec_private_key.private_key().num_bits() as u32,
+        pkey_bits_number,
         private_key_uid,
         curve,
         algorithm,
         public_key_mask,
-    );
+    )?;
     trace!("create_approved_ecc_key_pair: public key converted OK");
 
     Ok(KeyPair::new(private_key, public_key))
 }
 
+#[allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
 #[cfg(all(test, feature = "openssl"))]
 mod tests {
     #[cfg(not(feature = "fips"))]
@@ -563,9 +568,12 @@ mod tests {
         // public key
         //
         let original_public_key_value = &wrap_key_pair.public_key().key_block().unwrap().key_value;
-        let original_public_key_bytes = match &original_public_key_value.key_material {
-            KeyMaterial::TransparentECPublicKey { q_string, .. } => q_string,
-            _ => panic!("Not a transparent public key"),
+        let KeyMaterial::TransparentECPublicKey {
+            q_string: original_public_key_bytes,
+            ..
+        } = &original_public_key_value.key_material
+        else {
+            panic!("Not a transparent public key")
         };
         // try to convert to openssl
         let p_key = PKey::public_key_from_raw_bytes(original_public_key_bytes, Id::X25519).unwrap();
@@ -676,9 +684,12 @@ mod tests {
         // public key
         //
         let original_public_key_value = &wrap_key_pair.public_key().key_block().unwrap().key_value;
-        let original_public_key_bytes = match &original_public_key_value.key_material {
-            KeyMaterial::TransparentECPublicKey { q_string, .. } => q_string,
-            _ => panic!("Not a transparent public key"),
+        let KeyMaterial::TransparentECPublicKey {
+            q_string: original_public_key_bytes,
+            ..
+        } = &original_public_key_value.key_material
+        else {
+            panic!("Not a transparent public key")
         };
         // try to convert to openssl
         let p_key = PKey::public_key_from_raw_bytes(original_public_key_bytes, Id::X448).unwrap();
@@ -725,7 +736,7 @@ mod tests {
 
         let res = check_ecc_mask_against_flags(Some(mask), flags);
 
-        assert!(res.is_ok());
+        res.unwrap();
     }
 
     #[test]
@@ -744,7 +755,7 @@ mod tests {
 
         let res = check_ecc_mask_against_flags(Some(mask), flags);
 
-        assert!(res.is_ok());
+        res.unwrap();
     }
 
     #[test]
@@ -784,7 +795,7 @@ mod tests {
 
         let res = check_ecc_mask_against_flags(Some(mask), flags);
 
-        assert!(res.is_ok());
+        res.unwrap();
     }
 
     #[test]
@@ -796,12 +807,12 @@ mod tests {
         let mask = CryptographicUsageMask::Sign;
         let res = check_ecc_mask_against_flags(Some(mask), FIPS_PRIVATE_ECC_MASK_SIGN);
 
-        assert!(res.is_ok());
+        res.unwrap();
 
         let mask = CryptographicUsageMask::Verify;
         let res = check_ecc_mask_against_flags(Some(mask), FIPS_PUBLIC_ECC_MASK_SIGN);
 
-        assert!(res.is_ok());
+        res.unwrap();
     }
 
     #[test]
@@ -813,24 +824,24 @@ mod tests {
         let mask = CryptographicUsageMask::KeyAgreement;
         let res = check_ecc_mask_against_flags(Some(mask), FIPS_PRIVATE_ECC_MASK_ECDH);
 
-        assert!(res.is_ok());
+        res.unwrap();
 
         let mask = CryptographicUsageMask::KeyAgreement;
         let res = check_ecc_mask_against_flags(Some(mask), FIPS_PUBLIC_ECC_MASK_ECDH);
 
-        assert!(res.is_ok());
+        res.unwrap();
 
         let mask = CryptographicUsageMask::CRLSign
             | CryptographicUsageMask::CertificateSign
             | CryptographicUsageMask::KeyAgreement;
         let res = check_ecc_mask_against_flags(Some(mask), FIPS_PRIVATE_ECC_MASK_SIGN_ECDH);
 
-        assert!(res.is_ok());
+        res.unwrap();
 
         let mask = CryptographicUsageMask::Verify | CryptographicUsageMask::KeyAgreement;
         let res = check_ecc_mask_against_flags(Some(mask), FIPS_PUBLIC_ECC_MASK_SIGN_ECDH);
 
-        assert!(res.is_ok());
+        res.unwrap();
     }
 
     #[test]
@@ -959,7 +970,7 @@ mod tests {
             allowed,
         );
 
-        assert!(res.is_ok());
+        res.unwrap();
     }
 
     #[test]
diff --git a/crate/kmip/src/crypto/generic/data_to_encrypt.rs b/crate/kmip/src/crypto/generic/data_to_encrypt.rs
index 660249109..99dda431a 100644
--- a/crate/kmip/src/crypto/generic/data_to_encrypt.rs
+++ b/crate/kmip/src/crypto/generic/data_to_encrypt.rs
@@ -92,6 +92,7 @@ impl DataToEncrypt {
     }
 }
 
+#[allow(clippy::unwrap_used)]
 #[cfg(test)]
 mod tests {
     use super::DataToEncrypt;
diff --git a/crate/kmip/src/crypto/generic/kmip_requests.rs b/crate/kmip/src/crypto/generic/kmip_requests.rs
index 1ed7380e3..370d6bf9b 100644
--- a/crate/kmip/src/crypto/generic/kmip_requests.rs
+++ b/crate/kmip/src/crypto/generic/kmip_requests.rs
@@ -21,7 +21,7 @@ pub fn build_revoke_key_request(
     revocation_reason: RevocationReason,
 ) -> Result<Revoke, KmipError> {
     Ok(Revoke {
-        unique_identifier: Some(UniqueIdentifier::TextString(unique_identifier.to_string())),
+        unique_identifier: Some(UniqueIdentifier::TextString(unique_identifier.to_owned())),
         revocation_reason,
         compromise_occurrence_date: None,
     })
@@ -143,7 +143,7 @@ pub fn build_import_object_request(
     attributes: Attributes,
     unique_identifier: &str,
     replace_existing: Option<bool>,
-    _key_wrapping_specification: Option<KeyWrappingSpecification>,
+    _key_wrapping_specification: &Option<KeyWrappingSpecification>,
 ) -> Import {
     let key_wrap_type = if object.key_wrapping_data().is_some() {
         Some(KeyWrapType::AsRegistered)
diff --git a/crate/kmip/src/crypto/password_derivation.rs b/crate/kmip/src/crypto/password_derivation.rs
index c29069351..3b05e44f4 100644
--- a/crate/kmip/src/crypto/password_derivation.rs
+++ b/crate/kmip/src/crypto/password_derivation.rs
@@ -69,6 +69,7 @@ pub fn derive_key_from_password<const LENGTH: usize>(
 }
 
 #[test]
+#[allow(clippy::unwrap_used)]
 fn test_password_derivation() {
     #[cfg(feature = "fips")]
     // Load FIPS provider module from OpenSSL.
@@ -82,8 +83,10 @@ fn test_password_derivation() {
 }
 
 #[test]
+#[allow(clippy::unwrap_used)]
 #[cfg(feature = "fips")]
 fn test_password_derivation_bad_size() {
+    const BIG_KEY_LENGTH: usize = (((1 << 32) - 1) * 512) / 8 + 1;
     // Load FIPS provider module from OpenSSL.
     openssl::provider::Provider::load(None, "fips").unwrap();
 
@@ -91,10 +94,9 @@ fn test_password_derivation_bad_size() {
     let my_weak_password = b"123princ3ss".to_vec();
     let secure_mk_res = derive_key_from_password::<13>(salt, &my_weak_password);
 
-    assert!(secure_mk_res.is_err());
+    secure_mk_res.unwrap_err();
 
-    const BIG_KEY_LENGTH: usize = (((1 << 32) - 1) * 512) / 8 + 1;
     let secure_mk_res = derive_key_from_password::<BIG_KEY_LENGTH>(salt, &my_weak_password);
 
-    assert!(secure_mk_res.is_err());
+    secure_mk_res.unwrap_err();
 }
diff --git a/crate/kmip/src/crypto/rsa/ckm_rsa_aes_key_wrap.rs b/crate/kmip/src/crypto/rsa/ckm_rsa_aes_key_wrap.rs
index fbce13589..979f86d13 100644
--- a/crate/kmip/src/crypto/rsa/ckm_rsa_aes_key_wrap.rs
+++ b/crate/kmip/src/crypto/rsa/ckm_rsa_aes_key_wrap.rs
@@ -40,7 +40,7 @@ pub fn ckm_rsa_aes_key_wrap(
     plaintext: &[u8],
 ) -> Result<Vec<u8>, KmipError> {
     // Generate temporary AES key.
-    let mut kek = Zeroizing::from(vec![0u8; AES_KWP_KEY_LENGTH]);
+    let mut kek = Zeroizing::from(vec![0_u8; AES_KWP_KEY_LENGTH]);
     rand_bytes(&mut kek)?;
 
     // Encapsulate it using RSA-OAEP.
@@ -84,7 +84,7 @@ pub fn ckm_rsa_aes_key_unwrap(
         )
     }
 
-    let encapsulation_bytes_len = rsa_privkey.size() as usize;
+    let encapsulation_bytes_len = usize::try_from(rsa_privkey.size())?;
     if ciphertext.len() <= encapsulation_bytes_len {
         kmip_bail!(
             "CKM_RSA_OAEP decryption error: encrypted data of insufficient length: got {}",
@@ -105,6 +105,7 @@ pub fn ckm_rsa_aes_key_unwrap(
     Ok(plaintext)
 }
 
+#[allow(clippy::panic_in_result_fn, clippy::unwrap_used)]
 #[cfg(test)]
 mod tests {
     use openssl::pkey::PKey;
diff --git a/crate/kmip/src/crypto/rsa/ckm_rsa_pkcs.rs b/crate/kmip/src/crypto/rsa/ckm_rsa_pkcs.rs
index 21ae54033..c964f97c4 100644
--- a/crate/kmip/src/crypto/rsa/ckm_rsa_pkcs.rs
+++ b/crate/kmip/src/crypto/rsa/ckm_rsa_pkcs.rs
@@ -1,6 +1,6 @@
 //! Implements the RSA Key Encryption Mechanism `CKM_RSA_PKCS`
 //! a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40 available at
-//! https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html#_Toc408226893
+//! <https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html>#_Toc408226893
 //!
 //! This scheme is no longer FIPS approved for wrap/unwrap encrypt/decrypt operations.
 use openssl::{
@@ -17,7 +17,7 @@ use crate::kmip_bail;
 
 /// Key Wrap using `CKM_RSA_PKCS`
 /// a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40 available at
-/// https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html#_Toc408226893
+/// <https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html>#_Toc408226893
 ///
 /// The maximum dek length is k-11 where k is the length in octets of the RSA modulus
 /// The output length is the same as the modulus length.
@@ -33,7 +33,7 @@ pub fn ckm_rsa_pkcs_key_wrap(pub_key: &PKey<Public>, dek: &[u8]) -> Result<Vec<u
 
 /// Encryption using `CKM_RSA_PKCS`
 /// a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40 available at
-/// https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html#_Toc408226893
+/// <https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html>#_Toc408226893
 ///
 /// The maximum plaintext length is  k-11 where k is the length in octets of the RSA modulus
 /// The output length is the same as the modulus length.
@@ -56,7 +56,7 @@ fn init_ckm_rsa_pkcs_encryption_context(
     let rsa_pub_key = pub_key.rsa()?;
 
     // The ciphertext has the same length as the modulus.
-    let encapsulation_bytes_len = rsa_pub_key.size() as usize;
+    let encapsulation_bytes_len = usize::try_from(rsa_pub_key.size())?;
     let ciphertext = Vec::with_capacity(encapsulation_bytes_len);
 
     // Perform OAEP encryption.
@@ -68,7 +68,7 @@ fn init_ckm_rsa_pkcs_encryption_context(
 
 /// Key Unwrap using `CKM_RSA_PKCS`
 /// a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40 available at
-/// https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html#_Toc408226893
+/// <https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html>#_Toc408226893
 ///
 /// The wrapped data encryption key (dek) should be of size k where k is the length in octets of the RSA modulus.
 ///
@@ -88,7 +88,7 @@ pub fn ckm_rsa_pkcs_key_unwrap(
 
 /// Decrypt using `CKM_RSA_PKCS`
 /// a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40 available at
-/// https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html#_Toc408226893
+/// <https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html>#_Toc408226893
 ///
 /// The ciphertext should be of size k where k is the length in octets of the RSA modulus.
 ///
@@ -113,7 +113,7 @@ fn init_ckm_rsa_pkcs_decryption_context(
     let rsa_priv_key = priv_key.rsa()?;
 
     // The plaintext has length equal to the modulus length - 11 bytes.
-    let plaintext_bytes_len = rsa_priv_key.size() as usize - 11;
+    let plaintext_bytes_len = usize::try_from(rsa_priv_key.size())? - 11;
     let plaintext = Zeroizing::from(Vec::with_capacity(plaintext_bytes_len));
 
     // Perform OAEP encryption.
@@ -123,6 +123,7 @@ fn init_ckm_rsa_pkcs_decryption_context(
     Ok((ctx, plaintext))
 }
 
+#[allow(clippy::panic_in_result_fn)]
 #[cfg(test)]
 mod tests {
     use openssl::pkey::PKey;
diff --git a/crate/kmip/src/crypto/rsa/ckm_rsa_pkcs_oaep.rs b/crate/kmip/src/crypto/rsa/ckm_rsa_pkcs_oaep.rs
index f2b2eee78..b68670834 100644
--- a/crate/kmip/src/crypto/rsa/ckm_rsa_pkcs_oaep.rs
+++ b/crate/kmip/src/crypto/rsa/ckm_rsa_pkcs_oaep.rs
@@ -2,7 +2,8 @@
 //! a.k.a PKCS #1 RSA OAEP as specified in PKCS#11 v2.40 available at
 //! <http://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html>#_Toc408226895
 //!
-//! This scheme is part of the NIST 800-56B rev. 2 recommendation available at section 7.2.2
+//! This scheme is part of the NIST 800-56B rev. 2
+//!  recommendation available at section 7.2.2
 //! <https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Br2.pdf>
 //!
 //! As part of the NIST specification, NIST approved hash functions which can be used for the OAEP scheme are listed in
@@ -87,7 +88,7 @@ fn init_ckm_rsa_pkcs_oaep_encryption_context(
     }
 
     // The ciphertext has the same length as the modulus.
-    let encapsulation_bytes_len = rsa_pub_key.size() as usize;
+    let encapsulation_bytes_len = usize::try_from(rsa_pub_key.size())?;
     let ciphertext = Vec::with_capacity(encapsulation_bytes_len);
 
     // Perform OAEP encryption.
@@ -161,7 +162,7 @@ fn init_ckm_rsa_pkcs_oaep_decryption_context(
     let hash_fn: &MdRef = hash_fn.try_into()?;
 
     // The ciphertext has the same length as the modulus.
-    let plaintext_bytes_len = rsa_priv_key.size() as usize - 2 - 2 * hash_fn.size();
+    let plaintext_bytes_len = usize::try_from(rsa_priv_key.size())? - 2 - 2 * hash_fn.size();
     let plaintext = Zeroizing::from(Vec::with_capacity(plaintext_bytes_len));
 
     // Perform OAEP encryption.
@@ -172,6 +173,7 @@ fn init_ckm_rsa_pkcs_oaep_decryption_context(
     Ok((ctx, plaintext))
 }
 
+#[allow(clippy::panic_in_result_fn, clippy::unwrap_used)]
 #[cfg(test)]
 mod tests {
     use openssl::pkey::PKey;
diff --git a/crate/kmip/src/crypto/rsa/kmip_requests.rs b/crate/kmip/src/crypto/rsa/kmip_requests.rs
index 03f9f7391..72c4f3cf8 100644
--- a/crate/kmip/src/crypto/rsa/kmip_requests.rs
+++ b/crate/kmip/src/crypto/rsa/kmip_requests.rs
@@ -27,10 +27,10 @@ pub fn create_rsa_key_pair_request<T: IntoIterator<Item = impl AsRef<str>>>(
     let public_key_mask = CryptographicUsageMask::Unrestricted;
 
     let algorithm = CryptographicAlgorithm::RSA;
-
+    let cryptographic_length = Some(i32::try_from(cryptographic_length)?);
     let mut common_attributes = Attributes {
         cryptographic_algorithm: Some(algorithm),
-        cryptographic_length: Some(cryptographic_length as i32),
+        cryptographic_length,
         cryptographic_domain_parameters: None,
         cryptographic_parameters: None,
         cryptographic_usage_mask: Some(private_key_mask | public_key_mask),
@@ -46,7 +46,7 @@ pub fn create_rsa_key_pair_request<T: IntoIterator<Item = impl AsRef<str>>>(
     // public key and private key usage masks on key creation.
     let private_key_attributes = Attributes {
         cryptographic_algorithm: Some(algorithm),
-        cryptographic_length: Some(cryptographic_length as i32),
+        cryptographic_length,
         cryptographic_domain_parameters: None,
         cryptographic_parameters: None,
         cryptographic_usage_mask: Some(private_key_mask),
@@ -57,7 +57,7 @@ pub fn create_rsa_key_pair_request<T: IntoIterator<Item = impl AsRef<str>>>(
 
     let public_key_attributes = Attributes {
         cryptographic_algorithm: Some(algorithm),
-        cryptographic_length: Some(cryptographic_length as i32),
+        cryptographic_length,
         cryptographic_domain_parameters: None,
         cryptographic_parameters: None,
         cryptographic_usage_mask: Some(public_key_mask),
@@ -77,7 +77,7 @@ pub fn create_rsa_key_pair_request<T: IntoIterator<Item = impl AsRef<str>>>(
 #[must_use]
 pub fn get_private_key_request(uid: &str) -> Get {
     Get {
-        unique_identifier: Some(UniqueIdentifier::TextString(uid.to_string())),
+        unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
         key_format_type: Some(KeyFormatType::TransparentRSAPrivateKey),
         ..Get::default()
     }
@@ -86,7 +86,7 @@ pub fn get_private_key_request(uid: &str) -> Get {
 #[must_use]
 pub fn get_public_key_request(uid: &str) -> Get {
     Get {
-        unique_identifier: Some(UniqueIdentifier::TextString(uid.to_string())),
+        unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
         key_format_type: Some(KeyFormatType::TransparentRSAPublicKey),
         ..Get::default()
     }
diff --git a/crate/kmip/src/crypto/rsa/mod.rs b/crate/kmip/src/crypto/rsa/mod.rs
index 8a0e0ee0b..d03152e66 100644
--- a/crate/kmip/src/crypto/rsa/mod.rs
+++ b/crate/kmip/src/crypto/rsa/mod.rs
@@ -39,21 +39,21 @@ pub const FIPS_PUBLIC_RSA_MASK: CryptographicUsageMask = CryptographicUsageMask:
 pub fn default_cryptographic_parameters(
     cryptographic_parameters: Option<&CryptographicParameters>,
 ) -> (CryptographicAlgorithm, PaddingMethod, HashingAlgorithm) {
-    cryptographic_parameters
-        .map(|cp| {
+    cryptographic_parameters.map_or_else(
+        || {
+            (
+                CryptographicAlgorithm::RSA,
+                PaddingMethod::OAEP,
+                HashingAlgorithm::SHA256,
+            )
+        },
+        |cp| {
             (
                 cp.cryptographic_algorithm
                     .unwrap_or(CryptographicAlgorithm::RSA),
                 cp.padding_method.unwrap_or(PaddingMethod::OAEP),
                 cp.hashing_algorithm.unwrap_or(HashingAlgorithm::SHA256),
             )
-        })
-        .unwrap_or_else(|| {
-            (
-                // default to CKM_RSA_PKCS_OAEP_KEY_WRAP
-                CryptographicAlgorithm::RSA,
-                PaddingMethod::OAEP,
-                HashingAlgorithm::SHA256,
-            )
-        })
+        },
+    )
 }
diff --git a/crate/kmip/src/crypto/rsa/operation.rs b/crate/kmip/src/crypto/rsa/operation.rs
index 3b63698e9..16359c943 100644
--- a/crate/kmip/src/crypto/rsa/operation.rs
+++ b/crate/kmip/src/crypto/rsa/operation.rs
@@ -105,7 +105,7 @@ pub fn to_rsa_public_key(
                     link: Some(vec![Link {
                         link_type: LinkType::PrivateKeyLink,
                         linked_object_identifier: LinkedObjectIdentifier::TextString(
-                            private_key_uid.to_string(),
+                            private_key_uid.to_owned(),
                         ),
                     }]),
                     ..Attributes::default()
@@ -179,7 +179,7 @@ pub fn to_rsa_private_key(
                     link: Some(vec![Link {
                         link_type: LinkType::PublicKeyLink,
                         linked_object_identifier: LinkedObjectIdentifier::TextString(
-                            public_key_uid.to_string(),
+                            public_key_uid.to_owned(),
                         ),
                     }]),
                     ..Attributes::default()
@@ -231,6 +231,7 @@ pub fn create_rsa_key_pair(
     Ok(KeyPair::new(private_key, public_key))
 }
 
+#[allow(clippy::unwrap_used)]
 #[test]
 #[cfg(feature = "fips")]
 fn test_create_rsa_incorrect_mask() {
@@ -266,6 +267,7 @@ fn test_create_rsa_incorrect_mask() {
     assert!(res.is_err());
 }
 
+#[allow(clippy::unwrap_used)]
 #[test]
 #[cfg(feature = "fips")]
 fn test_create_rsa_incorrect_mask_unrestricted() {
@@ -301,6 +303,7 @@ fn test_create_rsa_incorrect_mask_unrestricted() {
     assert!(res.is_err());
 }
 
+#[allow(clippy::unwrap_used)]
 #[test]
 #[cfg(feature = "fips")]
 fn test_create_rsa_fips_mask() {
@@ -318,9 +321,10 @@ fn test_create_rsa_fips_mask() {
         Some(FIPS_PUBLIC_RSA_MASK),
     );
 
-    assert!(res.is_ok());
+    res.unwrap();
 }
 
+#[allow(clippy::unwrap_used)]
 #[test]
 #[cfg(feature = "fips")]
 fn test_create_rsa_incorrect_algorithm() {
diff --git a/crate/kmip/src/crypto/secret.rs b/crate/kmip/src/crypto/secret.rs
index 078c8875e..71ea1e193 100644
--- a/crate/kmip/src/crypto/secret.rs
+++ b/crate/kmip/src/crypto/secret.rs
@@ -57,9 +57,10 @@ impl<const LENGTH: usize> Secret<LENGTH> {
     ///
     /// All bytes are initially set to 0.
     #[must_use]
+    #[allow(unsafe_code)]
     pub fn new() -> Self {
         // heap-allocate and turn into `Box` but looses `LENGTH`-constraint in type
-        let data = vec![0u8; LENGTH].into_boxed_slice();
+        let data = vec![0_u8; LENGTH].into_boxed_slice();
         // cast the raw pointer back to our fixed-length type
         // it is considered safe because `data` is initialized in the previous line
         let data = unsafe { Box::from_raw(Box::into_raw(data).cast::<[u8; LENGTH]>()) };
diff --git a/crate/kmip/src/crypto/symmetric/aead.rs b/crate/kmip/src/crypto/symmetric/aead.rs
index 00d95da57..8111b9f38 100644
--- a/crate/kmip/src/crypto/symmetric/aead.rs
+++ b/crate/kmip/src/crypto/symmetric/aead.rs
@@ -40,7 +40,7 @@ impl AeadCipher {
             Self::Aes128Gcm => Cipher::aes_128_gcm(),
             Self::Aes256Gcm => Cipher::aes_256_gcm(),
             #[cfg(not(feature = "fips"))]
-            AeadCipher::Chacha20Poly1305 => Cipher::chacha20_poly1305(),
+            Self::Chacha20Poly1305 => Cipher::chacha20_poly1305(),
         }
     }
 
@@ -51,7 +51,7 @@ impl AeadCipher {
             Self::Aes128Gcm => AES_128_GCM_MAC_LENGTH,
             Self::Aes256Gcm => AES_256_GCM_MAC_LENGTH,
             #[cfg(not(feature = "fips"))]
-            AeadCipher::Chacha20Poly1305 => CHACHA20_POLY1305_MAC_LENGTH,
+            Self::Chacha20Poly1305 => CHACHA20_POLY1305_MAC_LENGTH,
         }
     }
 
@@ -62,7 +62,7 @@ impl AeadCipher {
             Self::Aes128Gcm => AES_128_GCM_IV_LENGTH,
             Self::Aes256Gcm => AES_256_GCM_IV_LENGTH,
             #[cfg(not(feature = "fips"))]
-            AeadCipher::Chacha20Poly1305 => CHACHA20_POLY1305_IV_LENGTH,
+            Self::Chacha20Poly1305 => CHACHA20_POLY1305_IV_LENGTH,
         }
     }
 
@@ -73,7 +73,7 @@ impl AeadCipher {
             Self::Aes128Gcm => AES_128_GCM_KEY_LENGTH,
             Self::Aes256Gcm => AES_256_GCM_KEY_LENGTH,
             #[cfg(not(feature = "fips"))]
-            AeadCipher::Chacha20Poly1305 => CHACHA20_POLY1305_KEY_LENGTH,
+            Self::Chacha20Poly1305 => CHACHA20_POLY1305_KEY_LENGTH,
         }
     }
 
@@ -109,7 +109,7 @@ impl AeadCipher {
                     ));
                 }
                 match key_size {
-                    32 => Ok(AeadCipher::Chacha20Poly1305),
+                    32 => Ok(Self::Chacha20Poly1305),
                     _ => kmip_bail!(KmipError::NotSupported(
                         "ChaCha20 key must be 32 bytes long".to_owned()
                     )),
@@ -182,6 +182,7 @@ pub fn aead_decrypt(
     Ok(plaintext)
 }
 
+#[allow(clippy::unwrap_used)]
 #[cfg(test)]
 mod tests {
     #[cfg(feature = "fips")]
diff --git a/crate/kmip/src/crypto/symmetric/aes_256_gcm.rs b/crate/kmip/src/crypto/symmetric/aes_256_gcm.rs
index 068776a74..93035ef74 100644
--- a/crate/kmip/src/crypto/symmetric/aes_256_gcm.rs
+++ b/crate/kmip/src/crypto/symmetric/aes_256_gcm.rs
@@ -86,7 +86,7 @@ impl EncryptionSystem for AesGcmSystem {
         // see `Block` in crypto_base.
         if let Some(cp) = &request.cryptographic_parameters {
             if let Some(block_number) = cp.initial_counter_value {
-                aad.extend((block_number as usize).to_le_bytes());
+                aad.extend(usize::try_from(block_number)?.to_le_bytes());
             }
         }
 
@@ -150,7 +150,7 @@ impl DecryptionSystem for AesGcmSystem {
 
         // Recover nonce.
         let request_nonce_bytes = request.iv_counter_nonce.as_ref().ok_or_else(|| {
-            KmipError::NotSupported("The nonce is mandatory for AES GCM.".to_string())
+            KmipError::NotSupported("The nonce is mandatory for AES GCM.".to_owned())
         })?;
         let nonce: [u8; AES_256_GCM_IV_LENGTH] = request_nonce_bytes.as_slice().try_into()?;
 
@@ -160,7 +160,7 @@ impl DecryptionSystem for AesGcmSystem {
         // see `Block` in crypto_base.
         if let Some(cp) = &request.cryptographic_parameters {
             if let Some(block_number) = cp.initial_counter_value {
-                aad.extend((block_number as usize).to_le_bytes());
+                aad.extend(usize::try_from(block_number)?.to_le_bytes());
             }
         }
 
diff --git a/crate/kmip/src/crypto/symmetric/mod.rs b/crate/kmip/src/crypto/symmetric/mod.rs
index 35638e128..f8fbdd322 100644
--- a/crate/kmip/src/crypto/symmetric/mod.rs
+++ b/crate/kmip/src/crypto/symmetric/mod.rs
@@ -31,4 +31,5 @@ pub mod rfc5649;
 
 #[cfg(feature = "openssl")]
 #[cfg(test)]
+#[allow(clippy::unwrap_used, clippy::panic_in_result_fn)]
 mod tests;
diff --git a/crate/kmip/src/crypto/symmetric/rfc5649.rs b/crate/kmip/src/crypto/symmetric/rfc5649.rs
index 102cf9eeb..aaa7be872 100644
--- a/crate/kmip/src/crypto/symmetric/rfc5649.rs
+++ b/crate/kmip/src/crypto/symmetric/rfc5649.rs
@@ -15,7 +15,7 @@
 use openssl::symm::{encrypt, Cipher, Crypter, Mode};
 use zeroize::Zeroizing;
 
-use crate::error::KmipError;
+use crate::error::{result::KmipResult, KmipError};
 
 const DEFAULT_RFC5649_CONST: u32 = 0xA659_59A6_u32;
 const DEFAULT_IV: u64 = 0xA6A6_A6A6_A6A6_A6A6;
@@ -25,24 +25,25 @@ const AES_BLOCK_SIZE: usize = 0x10;
 /// Build the iv according to the RFC 5649.
 ///
 /// `wrapped_key_size` is the size of the key to wrap.
-fn build_iv(wrapped_key_size: usize) -> u64 {
-    u64::from(DEFAULT_RFC5649_CONST) << 32 | u64::from((wrapped_key_size as u32).to_le())
+fn build_iv(wrapped_key_size: usize) -> KmipResult<u64> {
+    Ok(u64::from(DEFAULT_RFC5649_CONST) << 32 | u64::try_from(wrapped_key_size)?.to_le())
 }
 
 /// Check if the `iv` value obtained after decryption is appropriate according
 /// to the RFC 5649.
-fn check_iv(iv: u64, data: &[u8]) -> bool {
+#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
+fn check_iv(iv: u64, data: &[u8]) -> KmipResult<bool> {
     let data_size: usize = data.len();
-    if (iv >> 32) as u32 != DEFAULT_RFC5649_CONST {
-        return false
+    if u32::try_from(iv >> 32)? != DEFAULT_RFC5649_CONST {
+        return Ok(false)
     }
 
     let real_data_size = u32::to_le(iv as u32) as usize;
     if real_data_size > data_size || real_data_size <= (data_size - 8) {
-        return false
+        return Ok(false)
     }
 
-    data[real_data_size..].iter().all(|&x| x == 0)
+    Ok(data[real_data_size..].iter().all(|&x| x == 0))
 }
 
 /// Wrap a plain text of variable length following RFC 5649.
@@ -57,7 +58,7 @@ pub fn rfc5649_wrap(plain: &[u8], kek: &[u8]) -> Result<Vec<u8>, KmipError> {
     // Pad the plaintext with null bytes.
     let padded_plain = if n_bytes_key != 0 {
         let missing_bytes = 8 - n_bytes_key;
-        [plain.to_vec(), vec![0u8; missing_bytes]].concat()
+        [plain.to_vec(), vec![0_u8; missing_bytes]].concat()
     } else {
         plain.to_vec()
     };
@@ -65,7 +66,7 @@ pub fn rfc5649_wrap(plain: &[u8], kek: &[u8]) -> Result<Vec<u8>, KmipError> {
     if n <= 8 {
         // (C[0] | C[1]) = ENC(K, A | P[1]).
         let iv_and_key = [
-            &build_iv(n).to_be_bytes(),
+            &build_iv(n)?.to_be_bytes(),
             &padded_plain[0..AES_WRAP_PAD_BLOCK_SIZE],
         ]
         .concat();
@@ -80,14 +81,14 @@ pub fn rfc5649_wrap(plain: &[u8], kek: &[u8]) -> Result<Vec<u8>, KmipError> {
             32 => encrypt(Cipher::aes_256_ecb(), kek, None, &iv_and_key)?,
             _ => {
                 return Err(KmipError::InvalidSize(
-                    "The kek size should be 16, 24 or 32".to_string(),
+                    "The kek size should be 16, 24 or 32".to_owned(),
                 ))
             }
         };
 
         Ok(ciphertext[..AES_BLOCK_SIZE].to_vec())
     } else {
-        _wrap_64(&padded_plain, kek, Some(build_iv(n)))
+        _wrap_64(&padded_plain, kek, Some(build_iv(n)?))
     }
 }
 
@@ -95,12 +96,13 @@ pub fn rfc5649_wrap(plain: &[u8], kek: &[u8]) -> Result<Vec<u8>, KmipError> {
 ///
 /// The function name matches the one used in the RFC and has no link to the
 /// unwrap function in Rust.
+#[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
 pub fn rfc5649_unwrap(ciphertext: &[u8], kek: &[u8]) -> Result<Zeroizing<Vec<u8>>, KmipError> {
     let n = ciphertext.len();
 
     if n % AES_WRAP_PAD_BLOCK_SIZE != 0 || n < AES_BLOCK_SIZE {
         return Err(KmipError::InvalidSize(
-            "The ciphertext size should be >= 16 and a multiple of 16.".to_string(),
+            "The ciphertext size should be >= 16 and a multiple of 16.".to_owned(),
         ))
     }
 
@@ -108,9 +110,9 @@ pub fn rfc5649_unwrap(ciphertext: &[u8], kek: &[u8]) -> Result<Zeroizing<Vec<u8>
         let (iv, padded_plain) = _unwrap_64(ciphertext, kek)?;
 
         // Verify integrity check register as described in RFC 5649.
-        if !check_iv(iv, &padded_plain) {
+        if !check_iv(iv, &padded_plain)? {
             return Err(KmipError::InvalidSize(
-                "The ciphertext is invalid. Decrypted IV is not appropriate".to_string(),
+                "The ciphertext is invalid. Decrypted IV is not appropriate".to_owned(),
             ))
         }
 
@@ -129,7 +131,7 @@ pub fn rfc5649_unwrap(ciphertext: &[u8], kek: &[u8]) -> Result<Zeroizing<Vec<u8>
             32 => Crypter::new(Cipher::aes_256_ecb(), Mode::Decrypt, kek, None)?,
             _ => {
                 return Err(KmipError::InvalidSize(
-                    "The kek size should be 16, 24 or 32 bytes".to_string(),
+                    "The kek size should be 16, 24 or 32 bytes".to_owned(),
                 ))
             }
         };
@@ -145,13 +147,13 @@ pub fn rfc5649_unwrap(ciphertext: &[u8], kek: &[u8]) -> Result<Zeroizing<Vec<u8>
         if !check_iv(
             u64::from_be_bytes(plaintext[0..AES_WRAP_PAD_BLOCK_SIZE].try_into()?),
             &plaintext[AES_WRAP_PAD_BLOCK_SIZE..16],
-        ) {
+        )? {
             return Err(KmipError::InvalidSize(
-                "The ciphertext is invalid. Decrypted IV is not appropriate".to_string(),
+                "The ciphertext is invalid. Decrypted IV is not appropriate".to_owned(),
             ))
         }
 
-        let unpadded_size = u32::from_be_bytes(plaintext[4..8].try_into()?) as usize;
+        let unpadded_size = usize::try_from(u32::from_be_bytes(plaintext[4..8].try_into()?))?;
 
         Ok(Zeroizing::from(
             plaintext[AES_WRAP_PAD_BLOCK_SIZE..(AES_WRAP_PAD_BLOCK_SIZE + unpadded_size)].to_vec(),
@@ -168,7 +170,7 @@ fn _wrap_64(plain: &[u8], kek: &[u8], iv: Option<u64>) -> Result<Vec<u8>, KmipEr
 
     if n % AES_WRAP_PAD_BLOCK_SIZE != 0 {
         return Err(KmipError::InvalidSize(
-            "The plaintext size should be a multiple of 8".to_string(),
+            "The plaintext size should be a multiple of 8".to_owned(),
         ))
     }
 
@@ -188,7 +190,7 @@ fn _wrap_64(plain: &[u8], kek: &[u8], iv: Option<u64>) -> Result<Vec<u8>, KmipEr
         32 => Cipher::aes_256_ecb(),
         _ => {
             return Err(KmipError::InvalidSize(
-                "The kek size should be 16, 24 or 32".to_string(),
+                "The kek size should be 16, 24 or 32".to_owned(),
             ))
         }
     };
@@ -205,7 +207,7 @@ fn _wrap_64(plain: &[u8], kek: &[u8], iv: Option<u64>) -> Result<Vec<u8>, KmipEr
             let ciphertext = encrypt(cipher, kek, None, &plaintext_block)?;
 
             // A = MSB(64, B) ^ t where t = (n*j)+i
-            let t = ((n * j) + (i + 1)) as u64;
+            let t = u64::try_from((n * j) + (i + 1))?;
 
             icr = u64::from_be_bytes(ciphertext[0..8].try_into()?) ^ t;
             *block = u64::from_be_bytes(ciphertext[8..16].try_into()?);
@@ -226,7 +228,7 @@ fn _unwrap_64(ciphertext: &[u8], kek: &[u8]) -> Result<(u64, Zeroizing<Vec<u8>>)
 
     if n % AES_WRAP_PAD_BLOCK_SIZE != 0 || n < AES_BLOCK_SIZE {
         return Err(KmipError::InvalidSize(
-            "The ciphertext size should be >= 16 and a multiple of 8".to_string(),
+            "The ciphertext size should be >= 16 and a multiple of 8".to_owned(),
         ))
     }
 
@@ -253,7 +255,7 @@ fn _unwrap_64(ciphertext: &[u8], kek: &[u8]) -> Result<(u64, Zeroizing<Vec<u8>>)
         32 => Crypter::new(Cipher::aes_256_ecb(), Mode::Decrypt, kek, None)?,
         _ => {
             return Err(KmipError::InvalidSize(
-                "The kek size should be 16, 24 or 32".to_string(),
+                "The kek size should be 16, 24 or 32".to_owned(),
             ))
         }
     };
@@ -261,7 +263,7 @@ fn _unwrap_64(ciphertext: &[u8], kek: &[u8]) -> Result<(u64, Zeroizing<Vec<u8>>)
 
     for j in (0..6).rev() {
         for (i, block) in blocks[1..].iter_mut().rev().enumerate().take(n) {
-            let t = ((n * j) + (n - i)) as u64;
+            let t = u64::try_from((n * j) + (n - i))?;
 
             // B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i
             let big_i = (u128::from(icr ^ t) << 64 | u128::from(*block)).to_be_bytes();
@@ -290,6 +292,7 @@ fn _unwrap_64(ciphertext: &[u8], kek: &[u8]) -> Result<(u64, Zeroizing<Vec<u8>>)
     Ok((icr, unwrapped_key))
 }
 
+#[allow(clippy::unwrap_used, clippy::expect_used)]
 #[cfg(test)]
 mod tests {
     use zeroize::Zeroizing;
@@ -298,6 +301,7 @@ mod tests {
 
     #[test]
     pub(crate) fn test_wrap1() {
+        const TEST_SIZE_LIMIT: usize = 100;
         #[cfg(feature = "fips")]
         // Load FIPS provider module from OpenSSL.
         openssl::provider::Provider::load(None, "fips").unwrap();
@@ -320,9 +324,8 @@ mod tests {
             Zeroizing::from(key_to_wrap.to_vec())
         );
 
-        const TEST_SIZE_LIMIT: usize = 100;
         for size in 1..=TEST_SIZE_LIMIT {
-            let key_to_wrap = &[0u8; TEST_SIZE_LIMIT][..size];
+            let key_to_wrap = &[0_u8; TEST_SIZE_LIMIT][..size];
             let ciphertext = rfc5649_wrap(key_to_wrap, kek).expect("Fail to wrap");
             assert_eq!(
                 rfc5649_unwrap(&ciphertext, kek).expect("Fail to unwrap"),
@@ -393,8 +396,8 @@ mod tests {
             0x4f,
         ];
 
-        assert!(rfc5649_wrap(key_to_wrap, kek).is_err());
-        assert!(rfc5649_unwrap(&wrapped_key, kek).is_err());
+        rfc5649_wrap(key_to_wrap, kek).unwrap_err();
+        rfc5649_unwrap(&wrapped_key, kek).unwrap_err();
 
         // Large input
         let kek = b"\x00";
@@ -406,8 +409,8 @@ mod tests {
             0xfa, 0x54, 0x3b, 0x6a,
         ];
 
-        assert!(rfc5649_wrap(key_to_wrap, kek).is_err());
-        assert!(rfc5649_unwrap(&wrapped_key, kek).is_err());
+        rfc5649_wrap(key_to_wrap, kek).unwrap_err();
+        rfc5649_unwrap(&wrapped_key, kek).unwrap_err();
     }
 
     #[test]
@@ -421,7 +424,7 @@ mod tests {
             0xaf, 0xbe, 0xb0, 0xf0, 0x7d, 0xfb, 0xf5, 0x41, 0x92, 0x0, 0xf2, 0xcc, 0xb5, 0xb, 0xb2,
         ];
 
-        assert!(rfc5649_unwrap(&wrapped_key, kek).is_err());
+        rfc5649_unwrap(&wrapped_key, kek).unwrap_err();
     }
 
     #[test]
@@ -436,7 +439,7 @@ mod tests {
             0x4a,
         ];
 
-        assert!(rfc5649_unwrap(&wrapped_key, kek).is_err());
+        rfc5649_unwrap(&wrapped_key, kek).unwrap_err();
 
         let wrapped_key = [
             0x13, 0x8b, 0xde, 0xaa, 0x9b, 0x8f, 0xa7, 0xfc, 0x61, 0xf9, 0x77, 0x42, 0xe7, 0x22,
@@ -444,6 +447,6 @@ mod tests {
             0xfa, 0x54, 0x3b, 0x6b,
         ];
 
-        assert!(rfc5649_unwrap(&wrapped_key, kek).is_err());
+        rfc5649_unwrap(&wrapped_key, kek).unwrap_err();
     }
 }
diff --git a/crate/kmip/src/crypto/symmetric/symmetric_key.rs b/crate/kmip/src/crypto/symmetric/symmetric_key.rs
index 7d2ed4c52..499aec3cb 100644
--- a/crate/kmip/src/crypto/symmetric/symmetric_key.rs
+++ b/crate/kmip/src/crypto/symmetric/symmetric_key.rs
@@ -1,7 +1,7 @@
 use zeroize::Zeroizing;
 
 use crate::{
-    error::KmipError,
+    error::{result::KmipResult, KmipError},
     kmip::{
         kmip_data_structures::{KeyBlock, KeyMaterial, KeyValue},
         kmip_objects::{Object, ObjectType},
@@ -11,18 +11,17 @@ use crate::{
 };
 
 /// Create a symmetric key for the given algorithm
-#[must_use]
 pub fn create_symmetric_key_kmip_object(
     key_bytes: &[u8],
     cryptographic_algorithm: CryptographicAlgorithm,
-) -> Object {
+) -> KmipResult<Object> {
     // this length is in bits
-    let symmetric_key_len = key_bytes.len() as i32 * 8;
+    let cryptographic_length = Some(i32::try_from(key_bytes.len())? * 8);
 
     let attributes = Attributes {
         object_type: Some(ObjectType::SymmetricKey),
         cryptographic_algorithm: Some(cryptographic_algorithm),
-        cryptographic_length: Some(symmetric_key_len),
+        cryptographic_length,
         cryptographic_usage_mask: Some(
             CryptographicUsageMask::Encrypt
                 | CryptographicUsageMask::Decrypt
@@ -39,7 +38,7 @@ pub fn create_symmetric_key_kmip_object(
     //  see https://docs.oasis-open.org/kmip/kmip-spec/v2.1/os/kmip-spec-v2.1-os.html#_Toc57115585
     // The key created here has a format of TransparentSymmetricKey
     // This is no a problem since when it is exported, it is by default converted to a Raw key
-    Object::SymmetricKey {
+    Ok(Object::SymmetricKey {
         key_block: KeyBlock {
             cryptographic_algorithm: Some(cryptographic_algorithm),
             key_format_type: KeyFormatType::TransparentSymmetricKey,
@@ -50,10 +49,10 @@ pub fn create_symmetric_key_kmip_object(
                 },
                 attributes: Some(Box::new(attributes)),
             },
-            cryptographic_length: Some(symmetric_key_len),
+            cryptographic_length,
             key_wrapping_data: None,
         },
-    }
+    })
 }
 
 /// Build a `CreateKeyPairRequest` for a symmetric key
@@ -62,9 +61,10 @@ pub fn symmetric_key_create_request<T: IntoIterator<Item = impl AsRef<str>>>(
     cryptographic_algorithm: CryptographicAlgorithm,
     tags: T,
 ) -> Result<Create, KmipError> {
+    let cryptographic_length = Some(i32::try_from(key_len_in_bits)?);
     let mut attributes = Attributes {
         cryptographic_algorithm: Some(cryptographic_algorithm),
-        cryptographic_length: Some(key_len_in_bits as i32),
+        cryptographic_length,
         cryptographic_parameters: None,
         cryptographic_usage_mask: Some(
             CryptographicUsageMask::Encrypt
diff --git a/crate/kmip/src/crypto/wrap/common.rs b/crate/kmip/src/crypto/wrap/common.rs
index f95a932b2..ebe64e8ec 100644
--- a/crate/kmip/src/crypto/wrap/common.rs
+++ b/crate/kmip/src/crypto/wrap/common.rs
@@ -10,20 +10,21 @@ pub(crate) fn rsa_parameters(
         .encryption_key_information
         .as_ref()
         .and_then(|eki| eki.cryptographic_parameters.as_ref())
-        .map(|cp| {
-            (
-                cp.cryptographic_algorithm
-                    .unwrap_or(CryptographicAlgorithm::AES),
-                cp.padding_method.unwrap_or(PaddingMethod::OAEP),
-                cp.hashing_algorithm.unwrap_or(HashingAlgorithm::SHA256),
-            )
-        })
-        .unwrap_or_else(|| {
-            (
-                // default to CKM_RSA_AES_KEY_WRAP
-                CryptographicAlgorithm::AES,
-                PaddingMethod::OAEP,
-                HashingAlgorithm::SHA256,
-            )
-        })
+        .map_or_else(
+            || {
+                (
+                    CryptographicAlgorithm::AES,
+                    PaddingMethod::OAEP,
+                    HashingAlgorithm::SHA256,
+                )
+            },
+            |cp| {
+                (
+                    cp.cryptographic_algorithm
+                        .unwrap_or(CryptographicAlgorithm::AES),
+                    cp.padding_method.unwrap_or(PaddingMethod::OAEP),
+                    cp.hashing_algorithm.unwrap_or(HashingAlgorithm::SHA256),
+                )
+            },
+        )
 }
diff --git a/crate/kmip/src/crypto/wrap/mod.rs b/crate/kmip/src/crypto/wrap/mod.rs
index 2b0d7ccfd..067c02966 100644
--- a/crate/kmip/src/crypto/wrap/mod.rs
+++ b/crate/kmip/src/crypto/wrap/mod.rs
@@ -1,4 +1,5 @@
 mod common;
+#[allow(clippy::unwrap_used, clippy::panic_in_result_fn)]
 #[cfg(test)]
 mod tests;
 #[cfg(feature = "openssl")]
diff --git a/crate/kmip/src/crypto/wrap/unwrap_key.rs b/crate/kmip/src/crypto/wrap/unwrap_key.rs
index 91733bf37..137b7b424 100644
--- a/crate/kmip/src/crypto/wrap/unwrap_key.rs
+++ b/crate/kmip/src/crypto/wrap/unwrap_key.rs
@@ -143,30 +143,27 @@ pub(crate) fn unwrap(
                 .and_then(|parameters| parameters.block_cipher_mode);
             let unwrap_secret = unwrapping_key_block.key_bytes()?;
 
-            match block_cipher_mode {
-                Some(BlockCipherMode::GCM) => {
-                    // unwrap using aes Gcm
-                    let len = ciphertext.len();
-                    let aead = AeadCipher::Aes256Gcm;
-                    let nonce = &ciphertext[..NONCE_LENGTH];
-                    let wrapped_key_bytes = &ciphertext[NONCE_LENGTH..len - TAG_LENGTH];
-                    let tag = &ciphertext[len - TAG_LENGTH..];
-                    let authenticated_data = aad.unwrap_or_default();
-                    let plaintext = aead_decrypt(
-                        aead,
-                        &unwrap_secret,
-                        nonce,
-                        authenticated_data,
-                        wrapped_key_bytes,
-                        tag,
-                    )?;
-                    Ok(plaintext)
-                }
-                _ => {
-                    // unwrap using rfc_5649
-                    let plaintext = rfc5649_unwrap(ciphertext, &unwrap_secret)?;
-                    Ok(plaintext)
-                }
+            if block_cipher_mode == Some(BlockCipherMode::GCM) {
+                // unwrap using aes Gcm
+                let len = ciphertext.len();
+                let aead = AeadCipher::Aes256Gcm;
+                let nonce = &ciphertext[..NONCE_LENGTH];
+                let wrapped_key_bytes = &ciphertext[NONCE_LENGTH..len - TAG_LENGTH];
+                let tag = &ciphertext[len - TAG_LENGTH..];
+                let authenticated_data = aad.unwrap_or_default();
+                let plaintext = aead_decrypt(
+                    aead,
+                    &unwrap_secret,
+                    nonce,
+                    authenticated_data,
+                    wrapped_key_bytes,
+                    tag,
+                )?;
+                Ok(plaintext)
+            } else {
+                // unwrap using rfc_5649
+                let plaintext = rfc5649_unwrap(ciphertext, &unwrap_secret)?;
+                Ok(plaintext)
             }
         }
         KeyFormatType::TransparentECPrivateKey | KeyFormatType::TransparentRSAPrivateKey => {
diff --git a/crate/kmip/src/crypto/wrap/wrap_key.rs b/crate/kmip/src/crypto/wrap/wrap_key.rs
index 2e6094df8..4b896ba4e 100644
--- a/crate/kmip/src/crypto/wrap/wrap_key.rs
+++ b/crate/kmip/src/crypto/wrap/wrap_key.rs
@@ -56,24 +56,21 @@ fn check_block_cipher_mode_in_encryption_key_information(
     wrapping_key: &Object,
     key_wrapping_specification: &KeyWrappingSpecification,
 ) -> KmipResult<()> {
-    match wrapping_key {
-        Object::SymmetricKey { .. } => Ok(()),
-        _ => {
-            if let Some(encryption_key_information) = key_wrapping_specification
-                .encryption_key_information
-                .as_ref()
-            {
-                if let Some(cryptographic_parameters) =
-                    encryption_key_information.cryptographic_parameters.as_ref()
-                {
-                    if cryptographic_parameters.block_cipher_mode.is_some() {
-                        kmip_bail!("BlockCipherMode is only used for a SymmetricKey object")
-                    }
-                }
+    if let Object::SymmetricKey { .. } = wrapping_key {
+        // Do nothing
+    } else if let Some(encryption_key_information) = key_wrapping_specification
+        .encryption_key_information
+        .as_ref()
+    {
+        if let Some(cryptographic_parameters) =
+            encryption_key_information.cryptographic_parameters.as_ref()
+        {
+            if cryptographic_parameters.block_cipher_mode.is_some() {
+                kmip_bail!("BlockCipherMode is only used for a SymmetricKey object")
             }
-            Ok(())
         }
     }
+    Ok(())
 }
 
 /// Wrap a key block with a wrapping key
@@ -236,40 +233,37 @@ pub(crate) fn wrap(
                     debug!("wrap: block_cipher_mode: {:?}", block_cipher_mode);
                     let key_bytes = key_block.key_bytes()?;
                     let aad = additional_data_encryption.unwrap_or_default();
-                    match block_cipher_mode {
-                        Some(BlockCipherMode::GCM) => {
-                            // wrap using aes GCM
-                            let aead = AeadCipher::from_algorithm_and_key_size(
-                                cryptographic_algorithm,
-                                block_cipher_mode,
-                                key_bytes.len(),
-                            )?;
+                    if block_cipher_mode == Some(BlockCipherMode::GCM) {
+                        // wrap using aes GCM
+                        let aead = AeadCipher::from_algorithm_and_key_size(
+                            cryptographic_algorithm,
+                            block_cipher_mode,
+                            key_bytes.len(),
+                        )?;
 
-                            let nonce = random_nonce(aead)?;
+                        let nonce = random_nonce(aead)?;
 
-                            let (ct, authenticated_encryption_tag) =
-                                aead_encrypt(aead, &key_bytes, &nonce, aad, key_to_wrap)?;
-                            let mut ciphertext = Vec::with_capacity(
-                                nonce.len() + ct.len() + authenticated_encryption_tag.len(),
-                            );
-                            ciphertext.extend_from_slice(&nonce);
-                            ciphertext.extend_from_slice(&ct);
-                            ciphertext.extend_from_slice(&authenticated_encryption_tag);
+                        let (ct, authenticated_encryption_tag) =
+                            aead_encrypt(aead, &key_bytes, &nonce, aad, key_to_wrap)?;
+                        let mut ciphertext = Vec::with_capacity(
+                            nonce.len() + ct.len() + authenticated_encryption_tag.len(),
+                        );
+                        ciphertext.extend_from_slice(&nonce);
+                        ciphertext.extend_from_slice(&ct);
+                        ciphertext.extend_from_slice(&authenticated_encryption_tag);
 
-                            debug!(
-                                "wrap: nonce: {}, aad: {}, tag: {}",
-                                general_purpose::STANDARD.encode(&nonce),
-                                general_purpose::STANDARD.encode(aad),
-                                general_purpose::STANDARD.encode(&authenticated_encryption_tag),
-                            );
+                        debug!(
+                            "wrap: nonce: {}, aad: {}, tag: {}",
+                            general_purpose::STANDARD.encode(&nonce),
+                            general_purpose::STANDARD.encode(aad),
+                            general_purpose::STANDARD.encode(&authenticated_encryption_tag),
+                        );
 
-                            Ok(ciphertext)
-                        }
-                        _ => {
-                            // wrap using rfc_5649
-                            let ciphertext = rfc5649_wrap(key_to_wrap, &key_bytes)?;
-                            Ok(ciphertext)
-                        }
+                        Ok(ciphertext)
+                    } else {
+                        // wrap using rfc_5649
+                        let ciphertext = rfc5649_wrap(key_to_wrap, &key_bytes)?;
+                        Ok(ciphertext)
                     }
                 }
                 #[cfg(feature = "openssl")]
diff --git a/crate/kmip/src/error/mod.rs b/crate/kmip/src/error/mod.rs
index 7c6b7dcca..2773fef22 100644
--- a/crate/kmip/src/error/mod.rs
+++ b/crate/kmip/src/error/mod.rs
@@ -1,3 +1,5 @@
+use std::num::TryFromIntError;
+
 use cloudproof::reexport::crypto_core::{reexport::pkcs8, CryptoCoreError};
 use thiserror::Error;
 
@@ -122,6 +124,12 @@ impl From<pkcs8::Error> for KmipError {
     }
 }
 
+impl From<TryFromIntError> for KmipError {
+    fn from(e: TryFromIntError) -> Self {
+        Self::ConversionError(e.to_string())
+    }
+}
+
 /// Return early with an error if a condition is not satisfied.
 ///
 /// This macro is equivalent to `if !$cond { return Err(From::from($err)); }`.
@@ -172,6 +180,7 @@ macro_rules! kmip_bail {
     };
 }
 
+#[allow(clippy::unwrap_used)]
 #[cfg(test)]
 mod tests {
     use super::KmipError;
diff --git a/crate/kmip/src/error/result.rs b/crate/kmip/src/error/result.rs
index cea5d2c17..47c233bf8 100644
--- a/crate/kmip/src/error/result.rs
+++ b/crate/kmip/src/error/result.rs
@@ -36,7 +36,7 @@ where
 
 impl<T> KmipResultHelper<T> for Option<T> {
     fn context(self, context: &str) -> KmipResult<T> {
-        self.ok_or_else(|| KmipError::Default(context.to_string()))
+        self.ok_or_else(|| KmipError::Default(context.to_owned()))
     }
 
     fn with_context<D, O>(self, op: O) -> KmipResult<T>
diff --git a/crate/kmip/src/kmip/extra/tagging.rs b/crate/kmip/src/kmip/extra/tagging.rs
index 4f9d742a3..6c5492223 100644
--- a/crate/kmip/src/kmip/extra/tagging.rs
+++ b/crate/kmip/src/kmip/extra/tagging.rs
@@ -25,9 +25,12 @@ impl Attributes {
         tags: T,
     ) -> Result<(), KmipError> {
         let va = self.get_vendor_attribute_mut(VENDOR_ID_COSMIAN, VENDOR_ATTR_TAG);
-        va.attribute_value = serde_json::to_vec::<HashSet<String>>(&HashSet::from_iter(
-            tags.into_iter().map(|t| t.as_ref().to_owned()),
-        ))?;
+        va.attribute_value = serde_json::to_vec::<HashSet<String>>(
+            &tags
+                .into_iter()
+                .map(|t| t.as_ref().to_owned())
+                .collect::<HashSet<_>>(),
+        )?;
         Ok(())
     }
 
diff --git a/crate/kmip/src/kmip/extra/x509_extensions.rs b/crate/kmip/src/kmip/extra/x509_extensions.rs
index ee0b1572b..32eeb90dc 100644
--- a/crate/kmip/src/kmip/extra/x509_extensions.rs
+++ b/crate/kmip/src/kmip/extra/x509_extensions.rs
@@ -66,7 +66,7 @@ pub fn parse_v3_ca(
 ) -> Result<Vec<X509Extension>, KmipError> {
     let v3_ca = conf.section(Some("v3_ca")).ok_or_else(|| {
         KmipError::NotSupported(
-            "unable to find `v3_ca` parag from X.509 extension content".to_string(),
+            "unable to find `v3_ca` parag from X.509 extension content".to_owned(),
         )
     })?;
 
@@ -291,6 +291,7 @@ fn colon_split<'a>(value: &'a str, property_name: &str) -> Result<&'a str, KmipE
     Ok(val)
 }
 
+#[allow(clippy::unwrap_used)]
 #[cfg(test)]
 mod tests {
     use cosmian_logger::log_utils::log_init;
@@ -312,7 +313,7 @@ mod tests {
     fn test_split() {
         let split = colon_split("email:dummy@gmail.com", "email").unwrap();
         assert_eq!(split, "dummy@gmail.com");
-        assert!(colon_split("email:dummy@gmail.com", "emails").is_err());
+        colon_split("email:dummy@gmail.com", "emails").unwrap_err();
     }
 
     #[test]
@@ -365,7 +366,7 @@ crlDistributionPoints=URI:http://cse.example.com/crl.pem
         let cert_as_txt = x509.as_ref().to_text().unwrap();
         let cert = String::from_utf8_lossy(&cert_as_txt);
 
-        let _cert = r"            X509v3 Basic Constraints: 
+        let cert_ = r"            X509v3 Basic Constraints: 
                 CA:TRUE, pathlen:0
             X509v3 Key Usage: 
                 Digital Signature, Certificate Sign
@@ -378,7 +379,7 @@ crlDistributionPoints=URI:http://cse.example.com/crl.pem
     Signature Value:
 
 ";
-        assert_eq!(cert.split_once("X509v3 extensions:\n").unwrap().1, _cert);
+        assert_eq!(cert.split_once("X509v3 extensions:\n").unwrap().1, cert_);
 
         for ext in &exts_with_x509_parser {
             info!("\n\next: {:?}", ext);
diff --git a/crate/kmip/src/kmip/kmip_data_structures.rs b/crate/kmip/src/kmip/kmip_data_structures.rs
index 15e1b6d49..5565735c3 100644
--- a/crate/kmip/src/kmip/kmip_data_structures.rs
+++ b/crate/kmip/src/kmip/kmip_data_structures.rs
@@ -92,7 +92,7 @@ impl KeyBlock {
                 ErrorReason::Invalid_Data_Type,
                 "Key bytes can only be recovered from ByteString or TransparentSymmetricKey key \
                  material."
-                    .to_string(),
+                    .to_owned(),
             )),
         }
     }
@@ -167,9 +167,10 @@ impl KeyBlock {
         }
 
         // Find the link of the requested type in the list of links, if it exists
-        match links.iter().find(|&link| link.link_type == link_type) {
-            None => Ok(None),
-            Some(link) => match &link.linked_object_identifier {
+        links
+            .iter()
+            .find(|&link| link.link_type == link_type)
+            .map_or(Ok(None), |link| match &link.linked_object_identifier {
                 // If the linked object identifier is a text string, return it
                 LinkedObjectIdentifier::TextString(s) => Ok(Some(s.clone())),
                 // Enumeration and index identifiers are not yet supported
@@ -179,8 +180,7 @@ impl KeyBlock {
                 LinkedObjectIdentifier::Index(_) => Err(KmipError::NotSupported(
                     "Link Index not yet supported".to_owned(),
                 )),
-            },
-        }
+            })
     }
 
     /// Recover the cryptographic algorithm.
@@ -221,10 +221,7 @@ pub struct KeyValue {
 
 // Attributes is default is a fix for https://github.com/Cosmian/kms/issues/92
 fn attributes_is_default_or_none<T: Default + PartialEq + Serialize>(val: &Option<T>) -> bool {
-    match val {
-        Some(v) => *v == T::default(),
-        None => true,
-    }
+    val.as_ref().map_or(true, |v| *v == T::default())
 }
 
 impl KeyValue {
@@ -232,7 +229,7 @@ impl KeyValue {
         self.attributes.as_deref().ok_or_else(|| {
             KmipError::InvalidKmipValue(
                 ErrorReason::Invalid_Attribute_Value,
-                "key is missing its attributes".to_string(),
+                "key is missing its attributes".to_owned(),
             )
         })
     }
@@ -241,7 +238,7 @@ impl KeyValue {
         self.attributes.as_deref_mut().ok_or_else(|| {
             KmipError::InvalidKmipValue(
                 ErrorReason::Invalid_Attribute_Value,
-                "key is missing its mutable attributes".to_string(),
+                "key is missing its mutable attributes".to_owned(),
             )
         })
     }
@@ -325,6 +322,8 @@ impl Default for KeyWrappingData {
 
 /// This is a separate structure that is defined for operations that provide the option to return wrapped keys. The Key Wrapping Specification SHALL be included inside the operation request if clients request the server to return a wrapped key. If Cryptographic Parameters are specified in the Encryption Key Information and/or the MAC/Signature Key Information of the Key Wrapping Specification, then the server SHALL verify that they match one of the instances of the Cryptographic Parameters attribute of the corresponding key.. If the corresponding key does not have any Cryptographic Parameters attribute, or if no match is found, then an error is returned.
 ///
+///
+///
 /// This structure contains:
 ///
 /// ·         A Wrapping Method that indicates the method used to wrap the Key Value.
@@ -423,6 +422,7 @@ pub enum KeyMaterial {
     },
 }
 
+#[allow(clippy::upper_case_acronyms)]
 #[derive(Serialize, Deserialize, Clone, Copy)]
 enum KeyTypeSer {
     DH,
@@ -603,6 +603,7 @@ impl<'de> Deserialize<'de> for KeyMaterial {
                 formatter.write_str("struct KeyMaterialVisitor")
             }
 
+            #[allow(clippy::many_single_char_names)]
             fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
             where
                 V: MapAccess<'de>,
diff --git a/crate/kmip/src/kmip/kmip_messages.rs b/crate/kmip/src/kmip/kmip_messages.rs
index 0c77a7634..1689422d7 100644
--- a/crate/kmip/src/kmip/kmip_messages.rs
+++ b/crate/kmip/src/kmip/kmip_messages.rs
@@ -46,8 +46,10 @@ impl Serialize for Message {
     where
         S: serde::Serializer,
     {
+        let header_batch_count = usize::try_from(self.header.batch_count)
+            .map_err(|err| ser::Error::custom(format!("failed to convert batch count: {err:?}")))?;
         // check batch item count
-        if self.header.batch_count as usize != self.items.len() {
+        if header_batch_count != self.items.len() {
             return Err(ser::Error::custom(format!(
                 "mismatch number of batch items between header (`{}`) and items list (`{}`)",
                 self.header.batch_count,
@@ -363,8 +365,11 @@ impl Serialize for MessageResponse {
     where
         S: serde::Serializer,
     {
+        let header_batch_count = usize::try_from(self.header.batch_count)
+            .map_err(|err| ser::Error::custom(format!("failed to convert batch count: {err:?}")))?;
+
         // check batch item count
-        if self.header.batch_count as usize != self.items.len() {
+        if header_batch_count != self.items.len() {
             return Err(ser::Error::custom(format!(
                 "mismatch number of batch items between header (`{}`) and items list (`{}`)",
                 self.header.batch_count,
@@ -422,6 +427,7 @@ pub struct MessageResponseHeader {
 ///
 /// The timestamp is automatically set to now (UTC time)
 impl Default for MessageResponseHeader {
+    #[allow(clippy::cast_sign_loss, clippy::as_conversions)]
     fn default() -> Self {
         Self {
             timestamp: Utc::now().timestamp() as u64,
diff --git a/crate/kmip/src/kmip/kmip_objects.rs b/crate/kmip/src/kmip/kmip_objects.rs
index 174602715..35a1ecc28 100644
--- a/crate/kmip/src/kmip/kmip_objects.rs
+++ b/crate/kmip/src/kmip/kmip_objects.rs
@@ -1,5 +1,6 @@
 use std::convert::TryFrom;
 
+use clap::ValueEnum;
 use num_bigint_dig::BigUint;
 use serde::{Deserialize, Serialize};
 use strum::EnumIter;
@@ -157,7 +158,7 @@ impl Object {
             | Self::SplitKey { key_block, .. } => Ok(key_block),
             _ => Err(KmipError::InvalidKmipObject(
                 ErrorReason::Invalid_Object_Type,
-                "This object does not have a key block".to_string(),
+                "This object does not have a key block".to_owned(),
             )),
         }
     }
@@ -193,7 +194,7 @@ impl Object {
             | Self::SplitKey { key_block, .. } => Ok(key_block),
             _ => Err(KmipError::InvalidKmipObject(
                 ErrorReason::Invalid_Object_Type,
-                "This object does not have a key block (function `key_block_mut`)".to_string(),
+                "This object does not have a key block (function `key_block_mut`)".to_owned(),
             )),
         }
     }
@@ -235,7 +236,7 @@ impl TryFrom<&[u8]> for Object {
         serde_json::from_slice(object_bytes).map_err(|_e| {
             Self::Error::InvalidKmipValue(
                 ErrorReason::Invalid_Attribute_Value,
-                "failed deserializing to an Object".to_string(),
+                "failed deserializing to an Object".to_owned(),
             )
         })
     }
@@ -248,7 +249,7 @@ impl TryInto<Vec<u8>> for Object {
         serde_json::to_vec(&self).map_err(|_e| {
             Self::Error::InvalidKmipObject(
                 ErrorReason::Invalid_Attribute_Value,
-                "failed serializing Object to bytes".to_string(),
+                "failed serializing Object to bytes".to_owned(),
             )
         })
     }
@@ -257,17 +258,26 @@ impl TryInto<Vec<u8>> for Object {
 /// The type of a KMIP Objects
 #[allow(non_camel_case_types)]
 #[allow(clippy::enum_clike_unportable_variant)]
-#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, EnumIter)]
+#[derive(ValueEnum, Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, EnumIter)]
 #[serde(rename_all = "PascalCase")]
 pub enum ObjectType {
+    #[value(name = "Certificate")]
     Certificate = 0x0000_0001,
+    #[value(name = "SymmetricKey")]
     SymmetricKey = 0x0000_0002,
+    #[value(name = "PublicKey")]
     PublicKey = 0x0000_0003,
+    #[value(name = "PrivateKey")]
     PrivateKey = 0x0000_0004,
+    #[value(name = "SplitKey")]
     SplitKey = 0x0000_0005,
+    #[value(name = "SecretData")]
     SecretData = 0x0000_0007,
+    #[value(name = "OpaqueObject")]
     OpaqueObject = 0x0000_0008,
+    #[value(name = "PGPKey")]
     PGPKey = 0x0000_0009,
+    #[value(name = "CertificateRequest")]
     CertificateRequest = 0x0000_000A,
 }
 
@@ -296,15 +306,15 @@ impl TryFrom<&str> for ObjectType {
 impl From<ObjectType> for String {
     fn from(object_type: ObjectType) -> Self {
         match object_type {
-            ObjectType::Certificate => "Certificate".to_string(),
-            ObjectType::SymmetricKey => "SymmetricKey".to_string(),
-            ObjectType::PublicKey => "PublicKey".to_string(),
-            ObjectType::PrivateKey => "PrivateKey".to_string(),
-            ObjectType::SplitKey => "SplitKey".to_string(),
-            ObjectType::SecretData => "SecretData".to_string(),
-            ObjectType::OpaqueObject => "OpaqueObject".to_string(),
-            ObjectType::PGPKey => "PGPKey".to_string(),
-            ObjectType::CertificateRequest => "CertificateRequest".to_string(),
+            ObjectType::Certificate => "Certificate".to_owned(),
+            ObjectType::SymmetricKey => "SymmetricKey".to_owned(),
+            ObjectType::PublicKey => "PublicKey".to_owned(),
+            ObjectType::PrivateKey => "PrivateKey".to_owned(),
+            ObjectType::SplitKey => "SplitKey".to_owned(),
+            ObjectType::SecretData => "SecretData".to_owned(),
+            ObjectType::OpaqueObject => "OpaqueObject".to_owned(),
+            ObjectType::PGPKey => "PGPKey".to_owned(),
+            ObjectType::CertificateRequest => "CertificateRequest".to_owned(),
         }
     }
 }
diff --git a/crate/kmip/src/kmip/kmip_operations.rs b/crate/kmip/src/kmip/kmip_operations.rs
index 4337be8b5..5f366e520 100644
--- a/crate/kmip/src/kmip/kmip_operations.rs
+++ b/crate/kmip/src/kmip/kmip_operations.rs
@@ -12,7 +12,7 @@ use super::{
     kmip_data_structures::KeyWrappingSpecification,
     kmip_objects::{Object, ObjectType},
     kmip_types::{
-        AttributeReference, Attributes, CertificateRequestType, CryptographicParameters,
+        Attribute, AttributeReference, Attributes, CertificateRequestType, CryptographicParameters,
         KeyCompressionType, KeyFormatType, KeyWrapType, ObjectGroupMember, OperationEnumeration,
         ProtectionStorageMasks, ProtocolVersion, RevocationReason, StorageStatusMask,
         UniqueIdentifier, ValidityIndicator,
@@ -121,6 +121,10 @@ pub enum Operation {
     GetResponse(GetResponse),
     GetAttributes(GetAttributes),
     GetAttributesResponse(GetAttributesResponse),
+    SetAttribute(SetAttribute),
+    SetAttributeResponse(SetAttributeResponse),
+    DeleteAttribute(DeleteAttribute),
+    DeleteAttributeResponse(DeleteAttributeResponse),
     Encrypt(Encrypt),
     EncryptResponse(EncryptResponse),
     Decrypt(Decrypt),
@@ -150,6 +154,8 @@ impl Operation {
             | Self::Export(_)
             | Self::Get(_)
             | Self::GetAttributes(_)
+            | Self::SetAttribute(_)
+            | Self::DeleteAttribute(_)
             | Self::Encrypt(_)
             | Self::Decrypt(_)
             | Self::Locate(_)
@@ -166,6 +172,8 @@ impl Operation {
             | Self::ExportResponse(_)
             | Self::GetResponse(_)
             | Self::GetAttributesResponse(_)
+            | Self::SetAttributeResponse(_)
+            | Self::DeleteAttributeResponse(_)
             | Self::EncryptResponse(_)
             | Self::DecryptResponse(_)
             | Self::LocateResponse(_)
@@ -191,6 +199,12 @@ impl Operation {
             Self::GetAttributes(_) | Self::GetAttributesResponse(_) => {
                 OperationEnumeration::GetAttributes
             }
+            Self::SetAttribute(_) | Self::SetAttributeResponse(_) => {
+                OperationEnumeration::SetAttribute
+            }
+            Self::DeleteAttribute(_) | Self::DeleteAttributeResponse(_) => {
+                OperationEnumeration::DeleteAttribute
+            }
             Self::Encrypt(_) | Self::EncryptResponse(_) => OperationEnumeration::Encrypt,
             Self::Decrypt(_) | Self::DecryptResponse(_) => OperationEnumeration::Decrypt,
             Self::Locate(_) | Self::LocateResponse(_) => OperationEnumeration::Locate,
@@ -656,7 +670,7 @@ impl From<&String> for Export {
 impl From<&str> for Export {
     // Create a ExportRequest for an object to be returned "as registered"
     fn from(uid: &str) -> Self {
-        Self::from(uid.to_string())
+        Self::from(uid.to_owned())
     }
 }
 
@@ -748,7 +762,7 @@ impl Get {
     /// use cosmian_kmip::kmip::kmip_operations::Get;
     /// use cosmian_kmip::kmip::kmip_types::UniqueIdentifier;
     ///
-    /// let get_request = Get::new(UniqueIdentifier::TextString("1234".to_string()), false, None, None);
+    /// let get_request = Get::new(UniqueIdentifier::TextString("1234".to_owned()), false, None, None);
     /// ```
     #[must_use]
     pub const fn new(
@@ -804,7 +818,7 @@ impl From<&str> for Get {
     // Create a GetRequest for an object to be returned "as registered"
     fn from(uid: &str) -> Self {
         Self::new(
-            UniqueIdentifier::TextString(uid.to_string()),
+            UniqueIdentifier::TextString(uid.to_owned()),
             false,
             None,
             None,
@@ -841,7 +855,10 @@ pub struct GetAttributes {
     pub unique_identifier: Option<UniqueIdentifier>,
     /// Specifies an attribute associated with
     /// the object.
-    #[serde(skip_serializing_if = "Option::is_none", rename = "AttributeReference")]
+    #[serde(
+        skip_serializing_if = "Option::is_none",
+        rename = "AttributeReferences"
+    )]
     pub attribute_references: Option<Vec<AttributeReference>>,
 }
 impl From<String> for GetAttributes {
@@ -872,6 +889,47 @@ pub struct GetAttributesResponse {
     pub attributes: Attributes,
 }
 
+#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "PascalCase")]
+pub struct SetAttribute {
+    /// The Unique Identifier of the object. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub unique_identifier: Option<UniqueIdentifier>,
+    /// Specifies the new value for the attribute associated with the object.
+    pub new_attribute: Attribute,
+}
+
+#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "PascalCase")]
+pub struct SetAttributeResponse {
+    /// The Unique Identifier of the object
+    pub unique_identifier: UniqueIdentifier,
+}
+
+#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "PascalCase")]
+pub struct DeleteAttribute {
+    /// Determines the object whose attributes are being deleted. If omitted, then the ID Placeholder value is used by the server as the Unique Identifier.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub unique_identifier: Option<UniqueIdentifier>,
+    /// Specifies the attribute associated with the object to be deleted.
+    #[serde(skip_serializing_if = "Option::is_none")]
+    pub current_attribute: Option<Attribute>,
+    /// Specifies the reference for the attribute associated with the object to be deleted.
+    #[serde(
+        skip_serializing_if = "Option::is_none",
+        rename = "AttributeReferences"
+    )]
+    pub attribute_references: Option<Vec<AttributeReference>>,
+}
+
+#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
+#[serde(rename_all = "PascalCase")]
+pub struct DeleteAttributeResponse {
+    /// The Unique Identifier of the object
+    pub unique_identifier: UniqueIdentifier,
+}
+
 #[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq)]
 #[serde(rename_all = "PascalCase")]
 pub struct Encrypt {
@@ -1245,6 +1303,8 @@ pub struct RevokeResponse {
 
 /// This request is used to generate a replacement key for an existing symmetric key. It is analogous to the Create operation, except that attributes of the replacement key are copied from the existing key, with the exception of the attributes listed in Re-key Attribute Requirements.
 ///
+///
+///
 /// As the replacement key takes over the name attribute of the existing key, Re-key SHOULD only be performed once on a given key.
 ///
 /// The server SHALL copy the Unique Identifier of the replacement key returned by this operation into the ID Placeholder variable.
diff --git a/crate/kmip/src/kmip/kmip_types.rs b/crate/kmip/src/kmip/kmip_types.rs
index a28413878..e4a15ef26 100644
--- a/crate/kmip/src/kmip/kmip_types.rs
+++ b/crate/kmip/src/kmip/kmip_types.rs
@@ -3,7 +3,6 @@
 
 // see CryptographicUsageMask
 #![allow(non_upper_case_globals)]
-
 use std::{
     fmt,
     fmt::{Display, Formatter},
@@ -21,6 +20,7 @@ use serde::{
     Deserialize, Serialize,
 };
 use strum::{Display, EnumIter, EnumString};
+use tracing::trace;
 use uuid::Uuid;
 
 use super::kmip_objects::ObjectType;
@@ -108,116 +108,199 @@ pub enum SplitKeyMethod {
 ///  - Raw for opaque objects and Secret Data
 ///
 #[allow(clippy::enum_clike_unportable_variant)]
-#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Display, EnumIter)]
+#[derive(
+    ValueEnum, Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, EnumIter, Display,
+)]
 pub enum KeyFormatType {
+    #[value(name = "Raw")]
     Raw = 0x01,
+    #[value(name = "Opaque")]
     Opaque = 0x02,
+    #[value(name = "PKCS1")]
     PKCS1 = 0x03,
+    #[value(name = "PKCS8")]
     PKCS8 = 0x04,
+    #[value(name = "X509")]
     X509 = 0x05,
+    #[value(name = "ECPrivateKey")]
     ECPrivateKey = 0x06,
+    #[value(name = "TransparentSymmetricKey")]
     TransparentSymmetricKey = 0x07,
+    #[value(name = "TransparentDSAPrivateKey")]
     TransparentDSAPrivateKey = 0x08,
+    #[value(name = "TransparentDSAPublicKey")]
     TransparentDSAPublicKey = 0x09,
+    #[value(name = "TransparentRSAPrivateKey")]
     TransparentRSAPrivateKey = 0x0A,
+    #[value(name = "TransparentRSAPublicKey")]
     TransparentRSAPublicKey = 0x0B,
+    #[value(name = "TransparentDHPrivateKey")]
     TransparentDHPrivateKey = 0x0C,
+    #[value(name = "TransparentDHPublicKey")]
     TransparentDHPublicKey = 0x0D,
+    #[value(name = "TransparentECPrivateKey")]
     TransparentECPrivateKey = 0x14,
+    #[value(name = "TransparentECPublicKey")]
     TransparentECPublicKey = 0x15,
+    #[value(name = "PKCS12")]
     PKCS12 = 0x16,
+    #[value(name = "PKCS10")]
     PKCS10 = 0x17,
     #[cfg(not(feature = "fips"))]
     /// This mode is to support legacy, but common, PKCS#12 formats that use
-    /// PBE_WITHSHA1AND40BITRC2_CBC for the encryption algorithm of certificate,
-    /// PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC for the encryption algorithm of the key
+    /// `PBE_WITHSHA1AND40BITRC2_CBC` for the encryption algorithm of certificate,
+    /// `PBE_WITHSHA1AND3_KEY_TRIPLEDES_CBC` for the encryption algorithm of the key
     /// and SHA-1 for the MAC.
     /// This is not a standard PKCS#12 format but is used by some software
-    /// such as Java KeyStores, Mac OS X Keychains, and some versions of OpenSSL (1x).
+    /// such as Java `KeyStores`, Mac OS X Keychains, and some versions of OpenSSL (1x).
     /// Use PKCS12 instead for standard (newer) PKCS#12 format.
     Pkcs12Legacy = 0x8880_0001,
     PKCS7 = 0x8880_0002,
     // Available slot 0x8880_0003,
     // Available slot 0x8880_0004,
+    #[value(name = "EnclaveECKeyPair")]
     EnclaveECKeyPair = 0x8880_0005,
+    #[value(name = "EnclaveECSharedKey")]
     EnclaveECSharedKey = 0x8880_0006,
     // Available slot 0x8880_0007,
     // Available slot 0x8880_0008,
     // Available slot 0x8880_0009,
     // Available slot 0x8880_000A,
     // Available slot 0x8880_000B,
+    #[value(name = "CoverCryptSecretKey")]
     CoverCryptSecretKey = 0x8880_000C,
+    #[value(name = "CoverCryptPublicKey")]
     CoverCryptPublicKey = 0x8880_000D,
 }
 
 #[allow(non_camel_case_types)]
 #[allow(clippy::enum_clike_unportable_variant)]
-#[derive(Serialize, Deserialize, Copy, Clone, Debug, Display, Eq, PartialEq, EnumIter)]
+#[derive(
+    ValueEnum, Serialize, Deserialize, Copy, Clone, Debug, Display, Eq, PartialEq, EnumIter,
+)]
 pub enum CryptographicAlgorithm {
+    #[value(name = "DES")]
     DES = 0x0000_0001,
+    #[value(name = "THREE_DES")]
     THREE_DES = 0x0000_0002,
+    #[value(name = "AES")]
     AES = 0x0000_0003,
     /// This is `CKM_RSA_PKCS_OAEP` from PKCS#11
     /// see <https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html>#_Toc408226895
     /// To use  `CKM_RSA_AES_KEY_WRAP` from PKCS#11, use and RSA key with AES as the algorithm
     /// See <https://docs.oasis-open.org/pkcs11/pkcs11-curr/v2.40/cos01/pkcs11-curr-v2.40-cos01.html>#_Toc408226908
+    #[value(name = "RSA")]
     RSA = 0x0000_0004,
+    #[value(name = "DSA")]
     DSA = 0x0000_0005,
+    #[value(name = "ECDSA")]
     ECDSA = 0x0000_0006,
+    #[value(name = "HMACSHA1")]
     HMACSHA1 = 0x0000_0007,
+    #[value(name = "HMACSHA224")]
     HMACSHA224 = 0x0000_0008,
+    #[value(name = "HMACSHA256")]
     HMACSHA256 = 0x0000_0009,
+    #[value(name = "HMACSHA384")]
     HMACSHA384 = 0x0000_000A,
+    #[value(name = "HMACSHA512")]
     HMACSHA512 = 0x0000_000B,
+    #[value(name = "HMACMD5")]
     HMACMD5 = 0x0000_000C,
+    #[value(name = "DH")]
     DH = 0x0000_000D,
+    #[value(name = "ECDH")]
     ECDH = 0x0000_000E,
+    #[value(name = "ECMQV")]
     ECMQV = 0x0000_000F,
+    #[value(name = "Blowfish")]
     Blowfish = 0x0000_0010,
+    #[value(name = "Camellia")]
     Camellia = 0x0000_0011,
+    #[value(name = "CAST5")]
     CAST5 = 0x0000_0012,
+    #[value(name = "IDEA")]
     IDEA = 0x0000_0013,
+    #[value(name = "MARS")]
     MARS = 0x0000_0014,
+    #[value(name = "RC2")]
     RC2 = 0x0000_0015,
+    #[value(name = "RC4")]
     RC4 = 0x0000_0016,
+    #[value(name = "RC5")]
     RC5 = 0x0000_0017,
+    #[value(name = "SKIPJACK")]
     SKIPJACK = 0x0000_0018,
+    #[value(name = "Twofish")]
     Twofish = 0x0000_0019,
+    #[value(name = "EC")]
     EC = 0x0000_001A,
+    #[value(name = "OneTimePad")]
     OneTimePad = 0x0000_001B,
+    #[value(name = "ChaCha20")]
     ChaCha20 = 0x0000_001C,
+    #[value(name = "Poly1305")]
     Poly1305 = 0x0000_001D,
+    #[value(name = "ChaCha20Poly1305")]
     ChaCha20Poly1305 = 0x0000_001E,
+    #[value(name = "SHA3224")]
     SHA3224 = 0x0000_001F,
+    #[value(name = "SHA3256")]
     SHA3256 = 0x0000_0020,
+    #[value(name = "SHA3384")]
     SHA3384 = 0x0000_0021,
+    #[value(name = "SHA3512")]
     SHA3512 = 0x0000_0022,
+    #[value(name = "HMACSHA3224")]
     HMACSHA3224 = 0x0000_0023,
+    #[value(name = "HMACSHA3256")]
     HMACSHA3256 = 0x0000_0024,
+    #[value(name = "HMACSHA3384")]
     HMACSHA3384 = 0x0000_0025,
+    #[value(name = "HMACSHA3512")]
     HMACSHA3512 = 0x0000_0026,
+    #[value(name = "SHAKE128")]
     SHAKE128 = 0x0000_0027,
+    #[value(name = "SHAKE256")]
     SHAKE256 = 0x0000_0028,
+    #[value(name = "ARIA")]
     ARIA = 0x0000_0029,
+    #[value(name = "SEED")]
     SEED = 0x0000_002A,
+    #[value(name = "SM2")]
     SM2 = 0x0000_002B,
+    #[value(name = "SM3")]
     SM3 = 0x0000_002C,
+    #[value(name = "SM4")]
     SM4 = 0x0000_002D,
+    #[value(name = "GOSTR34102012")]
     GOSTR34102012 = 0x0000_002E,
+    #[value(name = "GOSTR34112012")]
     GOSTR34112012 = 0x0000_002F,
+    #[value(name = "GOSTR34132015")]
     GOSTR34132015 = 0x0000_0030,
+    #[value(name = "GOST2814789")]
     GOST2814789 = 0x0000_0031,
+    #[value(name = "XMSS")]
     XMSS = 0x0000_0032,
+    #[value(name = "SPHINCS_256")]
     SPHINCS_256 = 0x0000_0033,
+    #[value(name = "Page166Of230McEliece")]
     Page166Of230McEliece = 0x0000_0034,
+    #[value(name = "McEliece6960119")]
     McEliece6960119 = 0x0000_0035,
+    #[value(name = "McEliece8192128")]
     McEliece8192128 = 0x0000_0036,
+    #[value(name = "Ed25519")]
     Ed25519 = 0x0000_0037,
+    #[value(name = "Ed448")]
     Ed448 = 0x0000_0038,
     // Available slot 0x8880_0001,
     // Available slot 0x8880_0002,
     // Available slot 0x8880_0003,
+    #[value(name = "CoverCrypt")]
     CoverCrypt = 0x8880_0004,
+    #[value(name = "CoverCryptBulk")]
     CoverCryptBulk = 0x8880_0005,
 }
 
@@ -407,7 +490,7 @@ impl Serialize for CryptographicUsageMask {
     where
         S: serde::Serializer,
     {
-        serializer.serialize_i32(self.bits() as i32)
+        serializer.serialize_i32(i32::try_from(self.bits()).map_err(serde::ser::Error::custom)?)
     }
 }
 impl<'de> Deserialize<'de> for CryptographicUsageMask {
@@ -436,14 +519,18 @@ impl<'de> Deserialize<'de> for CryptographicUsageMask {
             where
                 E: de::Error,
             {
-                Ok(CryptographicUsageMask(v as u32))
+                Ok(CryptographicUsageMask(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
 
             fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
             where
                 E: de::Error,
             {
-                Ok(CryptographicUsageMask(v as u32))
+                Ok(CryptographicUsageMask(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
 
             // used by the direct JSON representation
@@ -451,7 +538,9 @@ impl<'de> Deserialize<'de> for CryptographicUsageMask {
             where
                 E: de::Error,
             {
-                Ok(CryptographicUsageMask(v as u32))
+                Ok(CryptographicUsageMask(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
         }
         deserializer.deserialize_any(CryptographicUsageMaskVisitor)
@@ -486,7 +575,7 @@ impl Serialize for ProtectionStorageMasks {
     where
         S: serde::Serializer,
     {
-        serializer.serialize_i32(self.bits() as i32)
+        serializer.serialize_i32(i32::try_from(self.bits()).map_err(serde::ser::Error::custom)?)
     }
 }
 impl<'de> Deserialize<'de> for ProtectionStorageMasks {
@@ -515,14 +604,18 @@ impl<'de> Deserialize<'de> for ProtectionStorageMasks {
             where
                 E: de::Error,
             {
-                Ok(ProtectionStorageMasks(v as u32))
+                Ok(ProtectionStorageMasks(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
 
             fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
             where
                 E: de::Error,
             {
-                Ok(ProtectionStorageMasks(v as u32))
+                Ok(ProtectionStorageMasks(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
 
             // used by the direct JSON representation
@@ -530,7 +623,9 @@ impl<'de> Deserialize<'de> for ProtectionStorageMasks {
             where
                 E: de::Error,
             {
-                Ok(ProtectionStorageMasks(v as u32))
+                Ok(ProtectionStorageMasks(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
         }
         deserializer.deserialize_any(ProtectionStorageMasksVisitor)
@@ -563,7 +658,7 @@ impl Serialize for StorageStatusMask {
     where
         S: serde::Serializer,
     {
-        serializer.serialize_i32(self.bits() as i32)
+        serializer.serialize_i32(i32::try_from(self.bits()).map_err(serde::ser::Error::custom)?)
     }
 }
 impl<'de> Deserialize<'de> for StorageStatusMask {
@@ -592,14 +687,18 @@ impl<'de> Deserialize<'de> for StorageStatusMask {
             where
                 E: de::Error,
             {
-                Ok(StorageStatusMask(v as u32))
+                Ok(StorageStatusMask(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
 
             fn visit_i64<E>(self, v: i64) -> Result<Self::Value, E>
             where
                 E: de::Error,
             {
-                Ok(StorageStatusMask(v as u32))
+                Ok(StorageStatusMask(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
 
             // used by the direct JSON representation
@@ -607,7 +706,9 @@ impl<'de> Deserialize<'de> for StorageStatusMask {
             where
                 E: de::Error,
             {
-                Ok(StorageStatusMask(v as u32))
+                Ok(StorageStatusMask(
+                    u32::try_from(v).map_err(de::Error::custom)?,
+                ))
             }
         }
         deserializer.deserialize_any(StorageStatusMaskVisitor)
@@ -615,49 +716,65 @@ impl<'de> Deserialize<'de> for StorageStatusMask {
 }
 
 #[allow(non_camel_case_types)]
-#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Display)]
+#[derive(
+    ValueEnum, Serialize, Deserialize, Copy, Clone, Debug, Eq, Display, PartialEq, EnumIter,
+)]
 pub enum LinkType {
     /// For Certificate objects: the parent certificate for a certificate in a
     /// certificate chain. For Public Key objects: the corresponding
     /// certificate(s), containing the same public key.
+    #[value(name = "CertificateLink")]
     CertificateLink = 0x0000_0101,
     /// For a Private Key object: the public key corresponding to the private
     /// key. For a Certificate object: the public key contained in the
     /// certificate.
+    #[value(name = "PublicKeyLink")]
     PublicKeyLink = 0x0000_0102,
     /// For a Public Key object: the private key corresponding to the public
     /// key.
+    #[value(name = "PrivateKeyLink")]
     PrivateKeyLink = 0x0000_0103,
     /// For a derived Symmetric Key or Secret Data object: the object(s) from
     /// which the current symmetric key was derived.
+    #[value(name = "DerivationBaseObjectLink")]
     DerivationBaseObjectLink = 0x0000_0104,
     /// The symmetric key(s) or Secret Data object(s) that were derived from
     /// the current object.
+    #[value(name = "DerivedKeyLink")]
     DerivedKeyLink = 0x0000_0105,
     /// For a Symmetric Key, an Asymmetric Private Key, or an Asymmetric
     /// Public Key object: the key that resulted from the re-key of the current
     /// key. For a Certificate object: the certificate that resulted from the
     /// re- certify. Note that there SHALL be only one such replacement
     /// object per Managed Object.
+    #[value(name = "ReplacementObjectLink")]
     ReplacementObjectLink = 0x0000_0106,
     /// For a Symmetric Key, an Asymmetric Private Key, or an Asymmetric
     /// Public Key object: the key that was re-keyed to obtain the current key.
     /// For a Certificate object: the certificate that was re-certified to
     /// obtain the current certificate.
+    #[value(name = "ReplacedObjectLink")]
     ReplacedObjectLink = 0x0000_0107,
     /// For all object types: the container or other parent object corresponding
     /// to the object.
+    #[value(name = "ParentLink")]
     ParentLink = 0x0000_0108,
     /// For all object types: the subordinate, derived or other child object
     /// corresponding to the object.
+    #[value(name = "ChildLink")]
     ChildLink = 0x0000_0109,
     /// For all object types: the previous object to this object.
+    #[value(name = "PreviousLink")]
     PreviousLink = 0x0000_010A,
     /// For all object types: the next object to this object.
+    #[value(name = "NextLink")]
     NextLink = 0x0000_010B,
+    #[value(name = "PKCS12CertificateLink")]
     PKCS12CertificateLink = 0x0000_010C,
+    #[value(name = "PKCS12PasswordLink")]
     PKCS12PasswordLink = 0x0000_010D,
     /// For wrapped objects: the object that was used to wrap this object.
+    #[value(name = "WrappingKeyLink")]
     WrappingKeyLink = 0x0000_010E,
     //Extensions 8XXXXXXX
 }
@@ -810,6 +927,19 @@ pub struct VendorAttribute {
     pub attribute_value: Vec<u8>,
 }
 
+impl Display for VendorAttribute {
+    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "VendorAttribute {{ vendor_identification: {}, attribute_name: {}, attribute_value: \
+             {} }}",
+            self.vendor_identification,
+            self.attribute_name,
+            hex::encode(&self.attribute_value)
+        )
+    }
+}
+
 /// The following subsections describe the attributes that are associated with
 /// Managed Objects. Attributes that an object MAY have multiple instances of
 /// are referred to as multi-instance attributes. All instances of an attribute
@@ -1081,14 +1211,12 @@ impl Attributes {
     /// Get the link to the object.
     #[must_use]
     pub fn get_link(&self, link_type: LinkType) -> Option<LinkedObjectIdentifier> {
-        if let Some(links) = &self.link {
+        self.link.as_ref().and_then(|links| {
             links
                 .iter()
                 .find(|&l| l.link_type == link_type)
                 .map(|l| l.linked_object_identifier.clone())
-        } else {
-            None
-        }
+        })
     }
 
     /// Remove the link from the attributes
@@ -1135,11 +1263,9 @@ impl Attributes {
 
     /// Set the bits in `mask` to the attributes's `CryptographicUsageMask` bits.
     pub fn set_cryptographic_usage_mask_bits(&mut self, mask: CryptographicUsageMask) {
-        let mask = if let Some(attr_mask) = self.cryptographic_usage_mask {
-            attr_mask | mask
-        } else {
-            mask
-        };
+        let mask = self
+            .cryptographic_usage_mask
+            .map_or(mask, |attr_mask| attr_mask | mask);
 
         self.cryptographic_usage_mask = Some(mask);
     }
@@ -1154,7 +1280,7 @@ impl Attributes {
         let usage_mask = self.cryptographic_usage_mask.ok_or_else(|| {
             KmipError::InvalidKmipValue(
                 ErrorReason::Incompatible_Cryptographic_Usage_Mask,
-                "CryptographicUsageMask is None".to_string(),
+                "CryptographicUsageMask is None".to_owned(),
             )
         })?;
 
@@ -1189,6 +1315,224 @@ impl Attributes {
     }
 }
 
+/// Structure used in various operations to provide the New Attribute value in the request.
+#[derive(Clone, Debug, PartialEq, Eq)]
+#[allow(clippy::large_enum_variant)]
+pub enum Attribute {
+    ActivationDate(u64),
+    CryptographicAlgorithm(CryptographicAlgorithm),
+    CryptographicLength(i32),
+    CryptographicParameters(CryptographicParameters),
+    CryptographicDomainParameters(CryptographicDomainParameters),
+    CryptographicUsageMask(CryptographicUsageMask),
+    Links(Vec<Link>),
+    State(StateEnumeration),
+    VendorAttributes(Vec<VendorAttribute>),
+}
+
+impl Serialize for Attribute {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: serde::Serializer,
+    {
+        match self {
+            Self::ActivationDate(activation_date) => {
+                let mut st = serializer.serialize_struct("Attribute", 1)?;
+                st.serialize_field("ActivationDate", activation_date)?;
+                st.end()
+            }
+            Self::CryptographicAlgorithm(crypto_algorithm) => {
+                let mut st = serializer.serialize_struct("Attribute", 1)?;
+                st.serialize_field("CryptographicAlgorithm", crypto_algorithm)?;
+                st.end()
+            }
+            Self::CryptographicLength(crypto_length) => {
+                let mut st = serializer.serialize_struct("Attribute", 1)?;
+                st.serialize_field("CryptographicLength", crypto_length)?;
+                st.end()
+            }
+            Self::CryptographicParameters(crypto_parameters) => {
+                let mut st = serializer.serialize_struct("Attribute", 1)?;
+                st.serialize_field("CryptographicParameters", crypto_parameters)?;
+                st.end()
+            }
+            Self::CryptographicDomainParameters(crypto_domain_parameters) => {
+                let mut st = serializer.serialize_struct("Attribute", 1)?;
+                st.serialize_field("CryptographicDomainParameters", crypto_domain_parameters)?;
+                st.end()
+            }
+            Self::CryptographicUsageMask(crypto_usage_mask) => {
+                let mut st = serializer.serialize_struct("Attribute", 1)?;
+                st.serialize_field("CryptographicUsageMask", crypto_usage_mask)?;
+                st.end()
+            }
+            Self::Links(links) => {
+                let mut st = serializer.serialize_struct("Attribute", links.len())?;
+                for link in links {
+                    st.serialize_field("Link", link)?;
+                }
+                st.end()
+            }
+            Self::State(state) => {
+                let mut st = serializer.serialize_struct("Attribute", 1)?;
+                st.serialize_field("State", state)?;
+                st.end()
+            }
+            Self::VendorAttributes(vendor_attributes) => {
+                let mut st = serializer.serialize_struct("Attribute", vendor_attributes.len())?;
+                for vendor_attribute in vendor_attributes {
+                    st.serialize_field("VendorAttribute", vendor_attribute)?;
+                }
+                st.end()
+            }
+        }
+    }
+}
+
+impl<'de> Deserialize<'de> for Attribute {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        #[derive(Deserialize, Debug)]
+        #[serde(field_identifier)]
+        enum Field {
+            ActivationDate,
+            CryptographicAlgorithm,
+            CryptographicLength,
+            CryptographicParameters,
+            CryptographicDomainParameters,
+            CryptographicUsageMask,
+            Link,
+            State,
+            VendorAttribute,
+        }
+
+        struct AttributeVisitor;
+
+        impl<'de> Visitor<'de> for AttributeVisitor {
+            type Value = Attribute;
+
+            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
+                formatter.write_str("struct AttributeVisitor")
+            }
+
+            fn visit_map<V>(self, mut map: V) -> Result<Self::Value, V::Error>
+            where
+                V: MapAccess<'de>,
+            {
+                let mut activation_date: Option<u64> = None;
+                let mut cryptographic_algorithm: Option<CryptographicAlgorithm> = None;
+                let mut cryptographic_length: Option<i32> = None;
+                let mut cryptographic_parameters: Option<CryptographicParameters> = None;
+                let mut cryptographic_domain_parameters: Option<CryptographicDomainParameters> =
+                    None;
+                let mut cryptographic_usage_mask: Option<CryptographicUsageMask> = None;
+                let mut links: Vec<Link> = Vec::new();
+                let mut state: Option<StateEnumeration> = None;
+                let mut vendor_attributes: Vec<VendorAttribute> = Vec::new();
+
+                while let Some(key) = map.next_key()? {
+                    trace!("visit_map: Key: {key:?}");
+                    match key {
+                        Field::ActivationDate => {
+                            if activation_date.is_some() {
+                                return Err(de::Error::duplicate_field("activation_date"))
+                            }
+                            activation_date = Some(map.next_value()?);
+                        }
+                        Field::CryptographicAlgorithm => {
+                            if cryptographic_algorithm.is_some() {
+                                return Err(de::Error::duplicate_field("cryptographic_algorithm"))
+                            }
+                            cryptographic_algorithm = Some(map.next_value()?);
+                        }
+                        Field::CryptographicLength => {
+                            if cryptographic_length.is_some() {
+                                return Err(de::Error::duplicate_field("cryptographic_length"))
+                            }
+                            cryptographic_length = Some(map.next_value()?);
+                        }
+                        Field::CryptographicParameters => {
+                            if cryptographic_parameters.is_some() {
+                                return Err(de::Error::duplicate_field("cryptographic_parameters"))
+                            }
+                            cryptographic_parameters = Some(map.next_value()?);
+                        }
+                        Field::CryptographicDomainParameters => {
+                            if cryptographic_domain_parameters.is_some() {
+                                return Err(de::Error::duplicate_field(
+                                    "cryptographic_domain_parameters",
+                                ))
+                            }
+                            cryptographic_domain_parameters = Some(map.next_value()?);
+                        }
+                        Field::CryptographicUsageMask => {
+                            if cryptographic_usage_mask.is_some() {
+                                return Err(de::Error::duplicate_field("cryptographic_usage_mask"))
+                            }
+                            cryptographic_usage_mask = Some(map.next_value()?);
+                        }
+                        Field::Link => {
+                            links.push(map.next_value()?);
+                        }
+                        Field::State => {
+                            if state.is_some() {
+                                return Err(de::Error::duplicate_field("state"))
+                            }
+                            state = Some(map.next_value()?);
+                        }
+                        Field::VendorAttribute => {
+                            vendor_attributes.push(map.next_value()?);
+                        }
+                    }
+                }
+
+                trace!("Attribute::deserialize: Link: {:?}", links);
+                if let Some(activation_date) = activation_date {
+                    return Ok(Attribute::ActivationDate(activation_date))
+                } else if let Some(cryptographic_algorithm) = cryptographic_algorithm {
+                    return Ok(Attribute::CryptographicAlgorithm(cryptographic_algorithm))
+                } else if let Some(cryptographic_length) = cryptographic_length {
+                    return Ok(Attribute::CryptographicLength(cryptographic_length))
+                } else if let Some(cryptographic_parameters) = cryptographic_parameters {
+                    return Ok(Attribute::CryptographicParameters(cryptographic_parameters))
+                } else if let Some(cryptographic_domain_parameters) =
+                    cryptographic_domain_parameters
+                {
+                    return Ok(Attribute::CryptographicDomainParameters(
+                        cryptographic_domain_parameters,
+                    ))
+                } else if let Some(cryptographic_usage_mask) = cryptographic_usage_mask {
+                    return Ok(Attribute::CryptographicUsageMask(cryptographic_usage_mask))
+                } else if !links.is_empty() {
+                    return Ok(Attribute::Links(links))
+                } else if let Some(state) = state {
+                    return Ok(Attribute::State(state))
+                } else if !vendor_attributes.is_empty() {
+                    return Ok(Attribute::VendorAttributes(vendor_attributes))
+                }
+
+                Ok(Attribute::ActivationDate(0))
+            }
+        }
+
+        const FIELDS: &[&str] = &[
+            "activation_date",
+            "cryptographic_algorithm",
+            "cryptographic_length",
+            "cryptographic_parameters",
+            "cryptographic_domain_parameters",
+            "cryptographic_usage_mask",
+            "link",
+            "public_key_link",
+            "state",
+            "vendor_attributes",
+        ];
+        deserializer.deserialize_struct("Attribute", FIELDS, AttributeVisitor)
+    }
+}
+
 /// The Certificate Attributes are the various items included in a certificate. The following list is based on RFC2253.
 #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq, Default)]
 #[serde(rename_all = "PascalCase")]
@@ -1253,7 +1597,7 @@ impl CertificateAttributes {
                 .next()
                 .ok_or_else(|| {
                     KmipError::Default(
-                        "Missing x509 certificate `subject name` identifier".to_string(),
+                        "Missing x509 certificate `subject name` identifier".to_owned(),
                     )
                 })?
                 .trim();
@@ -1314,353 +1658,704 @@ impl AttributeReference {
     #[must_use]
     pub fn tags_reference() -> Self {
         Self::Vendor(VendorAttributeReference {
-            vendor_identification: VENDOR_ID_COSMIAN.to_string(),
-            attribute_name: VENDOR_ATTR_TAG.to_string(),
+            vendor_identification: VENDOR_ID_COSMIAN.to_owned(),
+            attribute_name: VENDOR_ATTR_TAG.to_owned(),
         })
     }
 }
 
 #[allow(non_camel_case_types)]
 #[allow(clippy::enum_variant_names)]
-#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, EnumString, Display)]
+#[derive(
+    Serialize,
+    Deserialize,
+    Copy,
+    Clone,
+    Debug,
+    Eq,
+    PartialEq,
+    Display,
+    EnumString,
+    EnumIter,
+    Hash,
+    ValueEnum,
+)]
 pub enum Tag {
+    #[value(name = "ActivationDate")]
     ActivationDate = 0x42_0001,
+    #[value(name = "ApplicationData")]
     ApplicationData = 0x42_0002,
+    #[value(name = "ApplicationNamespace")]
     ApplicationNamespace = 0x42_0003,
+    #[value(name = "ApplicationSpecific_Information")]
     ApplicationSpecific_Information = 0x42_0004,
+    #[value(name = "ArchiveDate")]
     ArchiveDate = 0x42_0005,
+    #[value(name = "AsynchronousCorrelation_Value")]
     AsynchronousCorrelation_Value = 0x42_0006,
+    #[value(name = "AsynchronousIndicator")]
     AsynchronousIndicator = 0x42_0007,
+    #[value(name = "Attribute")]
     Attribute = 0x42_0008,
+    #[value(name = "AttributeName")]
     AttributeName = 0x42_000A,
+    #[value(name = "AttributeValue")]
     AttributeValue = 0x42_000B,
+    #[value(name = "Authentication")]
     Authentication = 0x42_000C,
+    #[value(name = "BatchCount")]
     BatchCount = 0x42_000D,
+    #[value(name = "BatchErrorContinuationOption")]
     BatchErrorContinuationOption = 0x42_000E,
+    #[value(name = "BatchItem")]
     BatchItem = 0x42_000F,
+    #[value(name = "BatchOrderOption")]
     BatchOrderOption = 0x42_0010,
+    #[value(name = "BlockCipherMode")]
     BlockCipherMode = 0x42_0011,
+    #[value(name = "CancellationResult")]
     CancellationResult = 0x42_0012,
+    #[value(name = "Certificate")]
     Certificate = 0x42_0013,
+    #[value(name = "CertificateRequest")]
     CertificateRequest = 0x42_0018,
+    #[value(name = "CertificateRequestType")]
     CertificateRequestType = 0x42_0019,
+    #[value(name = "CertificateType")]
     CertificateType = 0x42_001D,
+    #[value(name = "CertificateValue")]
     CertificateValue = 0x42_001E,
+    #[value(name = "CompromiseDate")]
     CompromiseDate = 0x42_0020,
+    #[value(name = "CompromiseOccurrenceDate")]
     CompromiseOccurrenceDate = 0x42_0021,
+    #[value(name = "ContactInformation")]
     ContactInformation = 0x42_0022,
+    #[value(name = "Credential")]
     Credential = 0x42_0023,
+    #[value(name = "CredentialType")]
     CredentialType = 0x42_0024,
+    #[value(name = "CredentialValue")]
     CredentialValue = 0x42_0025,
+    #[value(name = "CriticalityIndicator")]
     CriticalityIndicator = 0x42_0026,
+    #[value(name = "CRTCoefficient")]
     CRTCoefficient = 0x42_0027,
+    #[value(name = "CryptographicAlgorithm")]
     CryptographicAlgorithm = 0x42_0028,
+    #[value(name = "CryptographicDomainParameters")]
     CryptographicDomainParameters = 0x42_0029,
+    #[value(name = "CryptographicLength")]
     CryptographicLength = 0x42_002A,
+    #[value(name = "CryptographicParameters")]
     CryptographicParameters = 0x42_002B,
+    #[value(name = "CryptographicUsageMask")]
     CryptographicUsageMask = 0x42_002C,
+    #[value(name = "D")]
     D = 0x42_002E,
+    #[value(name = "DeactivationDate")]
     DeactivationDate = 0x42_002F,
+    #[value(name = "DerivationData")]
     DerivationData = 0x42_0030,
+    #[value(name = "DerivationMethod")]
     DerivationMethod = 0x42_0031,
+    #[value(name = "DerivationParameters")]
     DerivationParameters = 0x42_0032,
+    #[value(name = "DestroyDate")]
     DestroyDate = 0x42_0033,
+    #[value(name = "Digest")]
     Digest = 0x42_0034,
+    #[value(name = "DigestValue")]
     DigestValue = 0x42_0035,
+    #[value(name = "EncryptionKeyInformation")]
     EncryptionKeyInformation = 0x42_0036,
+    #[value(name = "G")]
     G = 0x42_0037,
+    #[value(name = "HashingAlgorithm")]
     HashingAlgorithm = 0x42_0038,
+    #[value(name = "InitialDate")]
     InitialDate = 0x42_0039,
+    #[value(name = "InitializationVector")]
     InitializationVector = 0x42_003A,
+    #[value(name = "IterationCount")]
     IterationCount = 0x42_003C,
+    #[value(name = "IVCounterNonce")]
     IVCounterNonce = 0x42_003D,
+    #[value(name = "J")]
     J = 0x42_003E,
+    #[value(name = "Key")]
     Key = 0x42_003F,
+    #[value(name = "KeyBlock")]
     KeyBlock = 0x42_0040,
+    #[value(name = "KeyCompressionType")]
     KeyCompressionType = 0x42_0041,
+    #[value(name = "KeyFormatType")]
     KeyFormatType = 0x42_0042,
+    #[value(name = "KeyMaterial")]
     KeyMaterial = 0x42_0043,
+    #[value(name = "KeyPartIdentifier")]
     KeyPartIdentifier = 0x42_0044,
+    #[value(name = "KeyValue")]
     KeyValue = 0x42_0045,
+    #[value(name = "KeyWrappingData")]
     KeyWrappingData = 0x42_0046,
+    #[value(name = "KeyWrappingSpecification")]
     KeyWrappingSpecification = 0x42_0047,
+    #[value(name = "LastChangeDate")]
     LastChangeDate = 0x42_0048,
+    #[value(name = "LeaseTime")]
     LeaseTime = 0x42_0049,
+    #[value(name = "Link")]
     Link = 0x42_004A,
+    #[value(name = "LinkType")]
     LinkType = 0x42_004B,
+    #[value(name = "LinkedObjectIdentifier")]
     LinkedObjectIdentifier = 0x42_004C,
+    #[value(name = "MACSignature")]
     MACSignature = 0x42_004D,
+    #[value(name = "MACSignatureKey_Information")]
     MACSignatureKey_Information = 0x42_004E,
+    #[value(name = "MaximumItems")]
     MaximumItems = 0x42_004F,
+    #[value(name = "MaximumResponseSize")]
     MaximumResponseSize = 0x42_0050,
+    #[value(name = "MessageExtension")]
     MessageExtension = 0x42_0051,
+    #[value(name = "Modulus")]
     Modulus = 0x42_0052,
+    #[value(name = "Name")]
     Name = 0x42_0053,
+    #[value(name = "NameType")]
     NameType = 0x42_0054,
+    #[value(name = "NameValue")]
     NameValue = 0x42_0055,
+    #[value(name = "ObjectGroup")]
     ObjectGroup = 0x42_0056,
+    #[value(name = "ObjectType")]
     ObjectType = 0x42_0057,
+    #[value(name = "Offset")]
     Offset = 0x42_0058,
+    #[value(name = "OpaqueDataType")]
     OpaqueDataType = 0x42_0059,
+    #[value(name = "OpaqueDataValue")]
     OpaqueDataValue = 0x42_005A,
+    #[value(name = "OpaqueObject")]
     OpaqueObject = 0x42_005B,
+    #[value(name = "Operation")]
     Operation = 0x42_005C,
+    #[value(name = "P")]
     P = 0x42_005E,
+    #[value(name = "PaddingMethod")]
     PaddingMethod = 0x42_005F,
+    #[value(name = "PrimeExponentP")]
     PrimeExponentP = 0x42_0060,
+    #[value(name = "PrimeExponentQ")]
     PrimeExponentQ = 0x42_0061,
+    #[value(name = "PrimeFieldSize")]
     PrimeFieldSize = 0x42_0062,
+    #[value(name = "PrivateExponent")]
     PrivateExponent = 0x42_0063,
+    #[value(name = "PrivateKey")]
     PrivateKey = 0x42_0064,
+    #[value(name = "PrivateKeyUniqueIdentifier")]
     PrivateKeyUniqueIdentifier = 0x42_0066,
+    #[value(name = "ProcessStartDate")]
     ProcessStartDate = 0x42_0067,
+    #[value(name = "ProtectStopDate")]
     ProtectStopDate = 0x42_0068,
+    #[value(name = "ProtocolVersion")]
     ProtocolVersion = 0x42_0069,
+    #[value(name = "ProtocolVersionMajor")]
     ProtocolVersionMajor = 0x42_006A,
+    #[value(name = "ProtocolVersionMinor")]
     ProtocolVersionMinor = 0x42_006B,
+    #[value(name = "PublicExponent")]
     PublicExponent = 0x42_006C,
+    #[value(name = "PublicKey")]
     PublicKey = 0x42_006D,
+    #[value(name = "PublicKeyUniqueIdentifier")]
     PublicKeyUniqueIdentifier = 0x42_006F,
+    #[value(name = "PutFunction")]
     PutFunction = 0x42_0070,
+    #[value(name = "Q")]
     Q = 0x42_0071,
+    #[value(name = "QString")]
     QString = 0x42_0072,
+    #[value(name = "Qlength")]
     Qlength = 0x42_0073,
+    #[value(name = "QueryFunction")]
     QueryFunction = 0x42_0074,
+    #[value(name = "RecommendedCurve")]
     RecommendedCurve = 0x42_0075,
+    #[value(name = "ReplacedUniqueIdentifier")]
     ReplacedUniqueIdentifier = 0x42_0076,
+    #[value(name = "RequestHeader")]
     RequestHeader = 0x42_0077,
+    #[value(name = "RequestMessage")]
     RequestMessage = 0x42_0078,
+    #[value(name = "RequestPayload")]
     RequestPayload = 0x42_0079,
+    #[value(name = "ResponseHeader")]
     ResponseHeader = 0x42_007A,
+    #[value(name = "ResponseMessage")]
     ResponseMessage = 0x42_007B,
+    #[value(name = "ResponsePayload")]
     ResponsePayload = 0x42_007C,
+    #[value(name = "ResultMessage")]
     ResultMessage = 0x42_007D,
+    #[value(name = "ResultReason")]
     ResultReason = 0x42_007E,
+    #[value(name = "ResultStatus")]
     ResultStatus = 0x42_007F,
+    #[value(name = "RevocationMessage")]
     RevocationMessage = 0x42_0080,
+    #[value(name = "RevocationReason")]
     RevocationReason = 0x42_0081,
+    #[value(name = "RevocationReasonCode")]
     RevocationReasonCode = 0x42_0082,
+    #[value(name = "KeyRoleType")]
     KeyRoleType = 0x42_0083,
+    #[value(name = "Salt")]
     Salt = 0x42_0084,
+    #[value(name = "SecretData")]
     SecretData = 0x42_0085,
+    #[value(name = "SecretDataType")]
     SecretDataType = 0x42_0086,
+    #[value(name = "ServerInformation")]
     ServerInformation = 0x42_0088,
+    #[value(name = "SplitKey")]
     SplitKey = 0x42_0089,
+    #[value(name = "SplitKeyMethod")]
     SplitKeyMethod = 0x42_008A,
+    #[value(name = "SplitKeyParts")]
     SplitKeyParts = 0x42_008B,
+    #[value(name = "SplitKeyThreshold")]
     SplitKeyThreshold = 0x42_008C,
+    #[value(name = "State")]
     State = 0x42_008D,
+    #[value(name = "StorageStatusMask")]
     StorageStatusMask = 0x42_008E,
+    #[value(name = "SymmetricKey")]
     SymmetricKey = 0x42_008F,
+    #[value(name = "TimeStamp")]
     TimeStamp = 0x42_0092,
+    #[value(name = "UniqueBatchItemID")]
     UniqueBatchItemID = 0x42_0093,
+    #[value(name = "UniqueIdentifier")]
     UniqueIdentifier = 0x42_0094,
+    #[value(name = "UsageLimits")]
     UsageLimits = 0x42_0095,
+    #[value(name = "UsageLimitsCount")]
     UsageLimitsCount = 0x42_0096,
+    #[value(name = "UsageLimitsTotal")]
     UsageLimitsTotal = 0x42_0097,
+    #[value(name = "UsageLimitsUnit")]
     UsageLimitsUnit = 0x42_0098,
+    #[value(name = "Username")]
     Username = 0x42_0099,
+    #[value(name = "ValidityDate")]
     ValidityDate = 0x42_009A,
+    #[value(name = "ValidityIndicator")]
     ValidityIndicator = 0x42_009B,
+    #[value(name = "VendorExtension")]
     VendorExtension = 0x42_009C,
+    #[value(name = "VendorIdentification")]
     VendorIdentification = 0x42_009D,
+    #[value(name = "WrappingMethod")]
     WrappingMethod = 0x42_009E,
+    #[value(name = "X")]
     X = 0x42_009F,
+    #[value(name = "Y")]
     Y = 0x42_00A0,
+    #[value(name = "Password")]
     Password = 0x42_00A1,
+    #[value(name = "DeviceIdentifier")]
     DeviceIdentifier = 0x42_00A2,
+    #[value(name = "EncodingOption")]
     EncodingOption = 0x42_00A3,
+    #[value(name = "ExtensionInformation")]
     ExtensionInformation = 0x42_00A4,
+    #[value(name = "ExtensionName")]
     ExtensionName = 0x42_00A5,
+    #[value(name = "ExtensionTag")]
     ExtensionTag = 0x42_00A6,
+    #[value(name = "ExtensionType")]
     ExtensionType = 0x42_00A7,
+    #[value(name = "Fresh")]
     Fresh = 0x42_00A8,
+    #[value(name = "MachineIdentifier")]
     MachineIdentifier = 0x42_00A9,
+    #[value(name = "MediaIdentifier")]
     MediaIdentifier = 0x42_00AA,
+    #[value(name = "NetworkIdentifier")]
     NetworkIdentifier = 0x42_00AB,
+    #[value(name = "ObjectGroupMember")]
     ObjectGroupMember = 0x42_00AC,
+    #[value(name = "CertificateLength")]
     CertificateLength = 0x42_00AD,
+    #[value(name = "DigitalSignatureAlgorithm")]
     DigitalSignatureAlgorithm = 0x42_00AE,
+    #[value(name = "CertificateSerialNumber")]
     CertificateSerialNumber = 0x42_00AF,
+    #[value(name = "DeviceSerialNumber")]
     DeviceSerialNumber = 0x42_00B0,
+    #[value(name = "IssuerAlternativeName")]
     IssuerAlternativeName = 0x42_00B1,
+    #[value(name = "IssuerDistinguishedName")]
     IssuerDistinguishedName = 0x42_00B2,
+    #[value(name = "SubjectAlternativeName")]
     SubjectAlternativeName = 0x42_00B3,
+    #[value(name = "SubjectDistinguishedName")]
     SubjectDistinguishedName = 0x42_00B4,
+    #[value(name = "X509CertificateIdentifier")]
     X509CertificateIdentifier = 0x42_00B5,
+    #[value(name = "X509CertificateIssuer")]
     X509CertificateIssuer = 0x42_00B6,
+    #[value(name = "X509CertificateSubject")]
     X509CertificateSubject = 0x42_00B7,
+    #[value(name = "KeyValueLocation")]
     KeyValueLocation = 0x42_00B8,
+    #[value(name = "KeyValueLocationValue")]
     KeyValueLocationValue = 0x42_00B9,
+    #[value(name = "KeyValueLocationType")]
     KeyValueLocationType = 0x42_00BA,
+    #[value(name = "KeyValuePresent")]
     KeyValuePresent = 0x42_00BB,
+    #[value(name = "OriginalCreationDate")]
     OriginalCreationDate = 0x42_00BC,
+    #[value(name = "PGPKey")]
     PGPKey = 0x42_00BD,
+    #[value(name = "PGPKeyVersion")]
     PGPKeyVersion = 0x42_00BE,
+    #[value(name = "AlternativeName")]
     AlternativeName = 0x42_00BF,
+    #[value(name = "AlternativeNameValue")]
     AlternativeNameValue = 0x42_00C0,
+    #[value(name = "AlternativeNameType")]
     AlternativeNameType = 0x42_00C1,
+    #[value(name = "Data")]
     Data = 0x42_00C2,
+    #[value(name = "SignatureData")]
     SignatureData = 0x42_00C3,
+    #[value(name = "DataLength")]
     DataLength = 0x42_00C4,
+    #[value(name = "RandomIV")]
     RandomIV = 0x42_00C5,
+    #[value(name = "MACData")]
     MACData = 0x42_00C6,
+    #[value(name = "AttestationType")]
     AttestationType = 0x42_00C7,
+    #[value(name = "Nonce")]
     Nonce = 0x42_00C8,
+    #[value(name = "NonceID")]
     NonceID = 0x42_00C9,
+    #[value(name = "NonceValue")]
     NonceValue = 0x42_00CA,
+    #[value(name = "AttestationMeasurement")]
     AttestationMeasurement = 0x42_00CB,
+    #[value(name = "AttestationAssertion")]
     AttestationAssertion = 0x42_00CC,
+    #[value(name = "IVLength")]
     IVLength = 0x42_00CD,
+    #[value(name = "TagLength")]
     TagLength = 0x42_00CE,
+    #[value(name = "FixedFieldLength")]
     FixedFieldLength = 0x42_00CF,
+    #[value(name = "CounterLength")]
     CounterLength = 0x42_00D0,
+    #[value(name = "InitialCounterValue")]
     InitialCounterValue = 0x42_00D1,
+    #[value(name = "InvocationFieldLength")]
     InvocationFieldLength = 0x42_00D2,
+    #[value(name = "AttestationCapableIndicator")]
     AttestationCapableIndicator = 0x42_00D3,
+    #[value(name = "OffsetItems")]
     OffsetItems = 0x42_00D4,
+    #[value(name = "LocatedItems")]
     LocatedItems = 0x42_00D5,
+    #[value(name = "CorrelationValue")]
     CorrelationValue = 0x42_00D6,
+    #[value(name = "InitIndicator")]
     InitIndicator = 0x42_00D7,
+    #[value(name = "FinalIndicator")]
     FinalIndicator = 0x42_00D8,
+    #[value(name = "RNGParameters")]
     RNGParameters = 0x42_00D9,
+    #[value(name = "RNGAlgorithm")]
     RNGAlgorithm = 0x42_00DA,
+    #[value(name = "DRBGAlgorithm")]
     DRBGAlgorithm = 0x42_00DB,
+    #[value(name = "FIPS186Variation")]
     FIPS186Variation = 0x42_00DC,
+    #[value(name = "PredictionResistance")]
     PredictionResistance = 0x42_00DD,
+    #[value(name = "RandomNumberGenerator")]
     RandomNumberGenerator = 0x42_00DE,
+    #[value(name = "ValidationInformation")]
     ValidationInformation = 0x42_00DF,
+    #[value(name = "ValidationAuthorityType")]
     ValidationAuthorityType = 0x42_00E0,
+    #[value(name = "ValidationAuthorityCountry")]
     ValidationAuthorityCountry = 0x42_00E1,
+    #[value(name = "ValidationAuthorityURI")]
     ValidationAuthorityURI = 0x42_00E2,
+    #[value(name = "ValidationVersionMajor")]
     ValidationVersionMajor = 0x42_00E3,
+    #[value(name = "ValidationVersionMinor")]
     ValidationVersionMinor = 0x42_00E4,
+    #[value(name = "ValidationType")]
     ValidationType = 0x42_00E5,
+    #[value(name = "ValidationLevel")]
     ValidationLevel = 0x42_00E6,
+    #[value(name = "ValidationCertificateIdentifier")]
     ValidationCertificateIdentifier = 0x42_00E7,
+    #[value(name = "ValidationCertificateURI")]
     ValidationCertificateURI = 0x42_00E8,
+    #[value(name = "ValidationVendorURI")]
     ValidationVendorURI = 0x42_00E9,
+    #[value(name = "ValidationProfile")]
     ValidationProfile = 0x42_00EA,
+    #[value(name = "ProfileInformation")]
     ProfileInformation = 0x42_00EB,
+    #[value(name = "ProfileName")]
     ProfileName = 0x42_00EC,
+    #[value(name = "ServerURI")]
     ServerURI = 0x42_00ED,
+    #[value(name = "ServerPort")]
     ServerPort = 0x42_00EE,
+    #[value(name = "StreamingCapability")]
     StreamingCapability = 0x42_00EF,
+    #[value(name = "AsynchronousCapability")]
     AsynchronousCapability = 0x42_00F0,
+    #[value(name = "AttestationCapability")]
     AttestationCapability = 0x42_00F1,
+    #[value(name = "UnwrapMode")]
     UnwrapMode = 0x42_00F2,
+    #[value(name = "DestroyAction")]
     DestroyAction = 0x42_00F3,
+    #[value(name = "ShreddingAlgorithm")]
     ShreddingAlgorithm = 0x42_00F4,
+    #[value(name = "RNGMode")]
     RNGMode = 0x42_00F5,
+    #[value(name = "ClientRegistrationMethod")]
     ClientRegistrationMethod = 0x42_00F6,
+    #[value(name = "CapabilityInformation")]
     CapabilityInformation = 0x42_00F7,
+    #[value(name = "KeyWrapType")]
     KeyWrapType = 0x42_00F8,
+    #[value(name = "BatchUndoCapability")]
     BatchUndoCapability = 0x42_00F9,
+    #[value(name = "BatchContinueCapability")]
     BatchContinueCapability = 0x42_00FA,
+    #[value(name = "PKCS12FriendlyName")]
     PKCS12FriendlyName = 0x42_00FB,
+    #[value(name = "Description")]
     Description = 0x42_00FC,
+    #[value(name = "Comment")]
     Comment = 0x42_00FD,
+    #[value(name = "AuthenticatedEncryptionAdditionalData")]
     AuthenticatedEncryptionAdditionalData = 0x42_00FE,
+    #[value(name = "AuthenticatedEncryptionTag")]
     AuthenticatedEncryptionTag = 0x42_00FF,
+    #[value(name = "SaltLength")]
     SaltLength = 0x42_0100,
+    #[value(name = "MaskGenerator")]
     MaskGenerator = 0x42_0101,
+    #[value(name = "MaskGeneratorHashingAlgorithm")]
     MaskGeneratorHashingAlgorithm = 0x42_0102,
+    #[value(name = "PSource")]
     PSource = 0x42_0103,
+    #[value(name = "TrailerField")]
     TrailerField = 0x42_0104,
+    #[value(name = "ClientCorrelationValue")]
     ClientCorrelationValue = 0x42_0105,
+    #[value(name = "ServerCorrelationValue")]
     ServerCorrelationValue = 0x42_0106,
+    #[value(name = "DigestedData")]
     DigestedData = 0x42_0107,
+    #[value(name = "CertificateSubjectCN")]
     CertificateSubjectCN = 0x42_0108,
+    #[value(name = "CertificateSubjectO")]
     CertificateSubjectO = 0x42_0109,
+    #[value(name = "CertificateSubjectOU")]
     CertificateSubjectOU = 0x42_010A,
+    #[value(name = "CertificateSubjectEmail")]
     CertificateSubjectEmail = 0x42_010B,
+    #[value(name = "CertificateSubjectC")]
     CertificateSubjectC = 0x42_010C,
+    #[value(name = "CertificateSubjectST")]
     CertificateSubjectST = 0x42_010D,
+    #[value(name = "CertificateSubjectL")]
     CertificateSubjectL = 0x42_010E,
+    #[value(name = "CertificateSubjectUID")]
     CertificateSubjectUID = 0x42_010F,
+    #[value(name = "CertificateSubjectSerialNumber")]
     CertificateSubjectSerialNumber = 0x42_0110,
+    #[value(name = "CertificateSubjectTitle")]
     CertificateSubjectTitle = 0x42_0111,
+    #[value(name = "CertificateSubjectDC")]
     CertificateSubjectDC = 0x42_0112,
+    #[value(name = "CertificateSubjectDNQualifier")]
     CertificateSubjectDNQualifier = 0x42_0113,
+    #[value(name = "CertificateIssuerCN")]
     CertificateIssuerCN = 0x42_0114,
+    #[value(name = "CertificateIssuerO")]
     CertificateIssuerO = 0x42_0115,
+    #[value(name = "CertificateIssuerOU")]
     CertificateIssuerOU = 0x42_0116,
+    #[value(name = "CertificateIssuerEmail")]
     CertificateIssuerEmail = 0x42_0117,
+    #[value(name = "CertificateIssuerC")]
     CertificateIssuerC = 0x42_0118,
+    #[value(name = "CertificateIssuerST")]
     CertificateIssuerST = 0x42_0119,
+    #[value(name = "CertificateIssuerL")]
     CertificateIssuerL = 0x42_011A,
+    #[value(name = "CertificateIssuerUID")]
     CertificateIssuerUID = 0x42_011B,
+    #[value(name = "CertificateIssuerSerialNumber")]
     CertificateIssuerSerialNumber = 0x42_011C,
+    #[value(name = "CertificateIssuerTitle")]
     CertificateIssuerTitle = 0x42_011D,
+    #[value(name = "CertificateIssuerDC")]
     CertificateIssuerDC = 0x42_011E,
+    #[value(name = "CertificateIssuerDNQualifier")]
     CertificateIssuerDNQualifier = 0x42_011F,
+    #[value(name = "Sensitive")]
     Sensitive = 0x42_0120,
+    #[value(name = "AlwaysSensitive")]
     AlwaysSensitive = 0x42_0121,
+    #[value(name = "Extractable")]
     Extractable = 0x42_0122,
+    #[value(name = "NeverExtractable")]
     NeverExtractable = 0x42_0123,
+    #[value(name = "ReplaceExisting")]
     ReplaceExisting = 0x42_0124,
+    #[value(name = "Attributes")]
     Attributes = 0x42_0125,
+    #[value(name = "CommonAttributes")]
     CommonAttributes = 0x42_0126,
+    #[value(name = "PrivateKeyAttributes")]
     PrivateKeyAttributes = 0x42_0127,
+    #[value(name = "PublicKeyAttributes")]
     PublicKeyAttributes = 0x42_0128,
+    #[value(name = "ExtensionEnumeration")]
     ExtensionEnumeration = 0x42_0129,
+    #[value(name = "ExtensionAttribute")]
     ExtensionAttribute = 0x42_012A,
+    #[value(name = "ExtensionParentStructureTag")]
     ExtensionParentStructureTag = 0x42_012B,
+    #[value(name = "ExtensionDescription")]
     ExtensionDescription = 0x42_012C,
+    #[value(name = "ServerName")]
     ServerName = 0x42_012D,
+    #[value(name = "ServerSerialNumber")]
     ServerSerialNumber = 0x42_012E,
+    #[value(name = "ServerVersion")]
     ServerVersion = 0x42_012F,
+    #[value(name = "ServerLoad")]
     ServerLoad = 0x42_0130,
+    #[value(name = "ProductName")]
     ProductName = 0x42_0131,
+    #[value(name = "BuildLevel")]
     BuildLevel = 0x42_0132,
+    #[value(name = "BuildDate")]
     BuildDate = 0x42_0133,
+    #[value(name = "ClusterInfo")]
     ClusterInfo = 0x42_0134,
+    #[value(name = "AlternateFailoverEndpoints")]
     AlternateFailoverEndpoints = 0x42_0135,
+    #[value(name = "ShortUniqueIdentifier")]
     ShortUniqueIdentifier = 0x42_0136,
+    #[value(name = "Reserved")]
     Reserved = 0x42_0137,
+    #[value(name = "Tag")]
     Tag = 0x42_0138,
+    #[value(name = "CertificateRequestUniqueIdentifier")]
     CertificateRequestUniqueIdentifier = 0x42_0139,
+    #[value(name = "NISTKeyType")]
     NISTKeyType = 0x42_013A,
+    #[value(name = "AttributeReference")]
     AttributeReference = 0x42_013B,
+    #[value(name = "CurrentAttribute")]
     CurrentAttribute = 0x42_013C,
+    #[value(name = "NewAttribute")]
     NewAttribute = 0x42_013D,
+    #[value(name = "CertificateRequestValue")]
     CertificateRequestValue = 0x42_0140,
+    #[value(name = "LogMessage")]
     LogMessage = 0x42_0141,
+    #[value(name = "ProfileVersion")]
     ProfileVersion = 0x42_0142,
+    #[value(name = "ProfileVersionMajor")]
     ProfileVersionMajor = 0x42_0143,
+    #[value(name = "ProfileVersionMinor")]
     ProfileVersionMinor = 0x42_0144,
+    #[value(name = "ProtectionLevel")]
     ProtectionLevel = 0x42_0145,
+    #[value(name = "ProtectionPeriod")]
     ProtectionPeriod = 0x42_0146,
+    #[value(name = "QuantumSafe")]
     QuantumSafe = 0x42_0147,
+    #[value(name = "QuantumSafeCapability")]
     QuantumSafeCapability = 0x42_0148,
+    #[value(name = "Ticket")]
     Ticket = 0x42_0149,
+    #[value(name = "TicketType")]
     TicketType = 0x42_014A,
+    #[value(name = "TicketValue")]
     TicketValue = 0x42_014B,
+    #[value(name = "RequestCount")]
     RequestCount = 0x42_014C,
+    #[value(name = "Rights")]
     Rights = 0x42_014D,
+    #[value(name = "Objects")]
     Objects = 0x42_014E,
+    #[value(name = "Operations")]
     Operations = 0x42_014F,
+    #[value(name = "Right")]
     Right = 0x42_0150,
+    #[value(name = "EndpointRole")]
     EndpointRole = 0x42_0151,
+    #[value(name = "DefaultsInformation")]
     DefaultsInformation = 0x42_0152,
+    #[value(name = "ObjectDefaults")]
     ObjectDefaults = 0x42_0153,
+    #[value(name = "Ephemeral")]
     Ephemeral = 0x42_0154,
+    #[value(name = "ServerHashedPassword")]
     ServerHashedPassword = 0x42_0155,
+    #[value(name = "OneTimePassword")]
     OneTimePassword = 0x42_0156,
+    #[value(name = "HashedPassword")]
     HashedPassword = 0x42_0157,
+    #[value(name = "AdjustmentType")]
     AdjustmentType = 0x42_0158,
+    #[value(name = "PKCS11Interface")]
     PKCS11Interface = 0x42_0159,
+    #[value(name = "PKCS11Function")]
     PKCS11Function = 0x42_015A,
+    #[value(name = "PKCS11InputParameters")]
     PKCS11InputParameters = 0x42_015B,
+    #[value(name = "PKCS11OutputParameters")]
     PKCS11OutputParameters = 0x42_015C,
+    #[value(name = "PKCS11ReturnCode")]
     PKCS11ReturnCode = 0x42_015D,
+    #[value(name = "ProtectionStorageMask")]
     ProtectionStorageMask = 0x42_015E,
+    #[value(name = "ProtectionStorageMasks")]
     ProtectionStorageMasks = 0x42_015F,
+    #[value(name = "InteropFunction")]
     InteropFunction = 0x42_0160,
+    #[value(name = "InteropIdentifier")]
     InteropIdentifier = 0x42_0161,
+    #[value(name = "AdjustmentValue")]
     AdjustmentValue = 0x42_0162,
+    #[value(name = "CommonProtectionStorageMasks")]
     CommonProtectionStorageMasks = 0x42_0163,
+    #[value(name = "PrivateProtectionStorageMasks")]
     PrivateProtectionStorageMasks = 0x42_0164,
+    #[value(name = "PublicProtectionStorageMasks")]
     PublicProtectionStorageMasks = 0x42_0165,
     // Extensions 540000 – 54FFFF
 }
@@ -1965,7 +2660,7 @@ pub struct MacSignatureKeyInformation {
 #[allow(non_camel_case_types)]
 #[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq)]
 pub enum EncodingOption {
-    /// the wrapped un-encoded value of the Byte String Key Material field in
+    /// the wrapped-encoded value of the Byte String Key Material field in
     /// the Key Value structure
     NoEncoding = 0x0000_0001,
     /// the wrapped TTLV-encoded Key Value structure
@@ -1987,9 +2682,12 @@ pub enum KeyWrapType {
 ///
 /// Note: The states correspond to those described in [SP800-57-1].
 #[allow(non_camel_case_types)]
-#[derive(Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Display)]
+#[derive(
+    ValueEnum, Serialize, Deserialize, Copy, Clone, Debug, Eq, PartialEq, Display, EnumIter,
+)]
 pub enum StateEnumeration {
     /// Pre-Active: The object exists and SHALL NOT be used for any cryptographic purpose.
+    #[value(name = "PreActive")]
     PreActive = 0x0000_0001,
     /// Active: The object SHALL be transitioned to the Active state prior to being used for any
     /// cryptographic purpose. The object SHALL only be used for all cryptographic purposes that
@@ -1997,6 +2695,7 @@ pub enum StateEnumeration {
     /// set, then the object SHALL NOT be used for cryptographic purposes prior to the Process
     /// Start Date. If a Protect Stop attribute is set, then the object SHALL NOT be used for
     /// cryptographic purposes after the Process Stop Date.
+    #[value(name = "Active")]
     Active = 0x0000_0002,
     /// Deactivated: The object SHALL NOT be used for applying cryptographic protection (e.g.,
     /// encryption, signing, wrapping, `MACing`, deriving) . The object SHALL only be used for
@@ -2004,6 +2703,7 @@ pub enum StateEnumeration {
     /// SHOULD only be used to process cryptographically-protected information (e.g., decryption,
     /// signature verification, unwrapping, MAC verification under extraordinary circumstances and
     /// when special permission is granted.
+    #[value(name = "Deactivated")]
     Deactivated = 0x0000_0003,
     /// Compromised: The object SHALL NOT be used for applying cryptographic protection (e.g.,
     /// encryption, signing, wrapping, `MACing`, deriving). The object SHOULD only be used to process
@@ -2011,11 +2711,14 @@ pub enum StateEnumeration {
     /// unwrapping, MAC verification in a client that is trusted to use managed objects that have
     /// been compromised. The object SHALL only be used for cryptographic purposes permitted by the
     /// Cryptographic Usage Mask attribute.
+    #[value(name = "Compromised")]
     Compromised = 0x0000_0004,
     /// Destroyed: The object SHALL NOT be used for any cryptographic purpose.
+    #[value(name = "Destroyed")]
     Destroyed = 0x0000_0005,
     /// Destroyed Compromised: The object SHALL NOT be used for any cryptographic purpose; however
     /// its compromised status SHOULD be retained for audit or security purposes.
+    #[value(name = "Destroyed_Compromised")]
     Destroyed_Compromised = 0x0000_0006,
 }
 
@@ -2074,6 +2777,11 @@ impl UniqueIdentifier {
 }
 
 impl From<LinkedObjectIdentifier> for UniqueIdentifier {
+    #[allow(
+        clippy::cast_sign_loss,
+        clippy::cast_possible_truncation,
+        clippy::as_conversions
+    )]
     fn from(value: LinkedObjectIdentifier) -> Self {
         match value {
             LinkedObjectIdentifier::TextString(s) => Self::TextString(s),
diff --git a/crate/kmip/src/kmip/ttlv/deserializer.rs b/crate/kmip/src/kmip/ttlv/deserializer.rs
index dfe001a29..145c0aa46 100644
--- a/crate/kmip/src/kmip/ttlv/deserializer.rs
+++ b/crate/kmip/src/kmip/ttlv/deserializer.rs
@@ -91,9 +91,6 @@ pub fn from_ttlv<'a, T>(s: &'a TTLV) -> Result<T>
 where
     T: Deserialize<'a>,
 {
-    let mut deserializer = TtlvDeserializer::from_ttlv(s);
-    let value = T::deserialize(&mut deserializer)?;
-
     // postfix the TTLV if it is a root object
     trait PostFix
     where
@@ -112,6 +109,10 @@ where
             Ok(Self::post_fix(object_type, self))
         }
     }
+
+    let mut deserializer = TtlvDeserializer::from_ttlv(s);
+    let value = T::deserialize(&mut deserializer)?;
+
     value.post_fix(s.tag.as_str())
 }
 
@@ -178,7 +179,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
             }),
             TTLValue::BigInteger(e) => visitor.visit_seq(TtlvDeserializer {
                 deserializing: Deserializing::BigInt,
-                inputs: Inputs::BigInt(to_u32_digits(e)),
+                inputs: Inputs::BigInt(to_u32_digits(e)?),
                 // start at 0 because the Visit Map is going to increment first
                 index: 0,
             }),
@@ -217,7 +218,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         V: Visitor<'de>,
     {
         Err(TtlvError::custom(
-            "Unable to deserialize an i8 value. Not supported in KMIP:".to_string(),
+            "Unable to deserialize an i8 value. Not supported in KMIP:".to_owned(),
         ))
     }
 
@@ -227,7 +228,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         V: Visitor<'de>,
     {
         Err(TtlvError::custom(
-            "Unable to deserialize an i16 value. Not supported in KMIP:".to_string(),
+            "Unable to deserialize an i16 value. Not supported in KMIP:".to_owned(),
         ))
     }
 
@@ -289,7 +290,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         V: Visitor<'de>,
     {
         Err(TtlvError::custom(
-            "Unable to deserialize a u16 value. Not supported in KMIP:".to_string(),
+            "Unable to deserialize a u16 value. Not supported in KMIP:".to_owned(),
         ))
     }
 
@@ -340,7 +341,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         V: Visitor<'de>,
     {
         Err(TtlvError::custom(
-            "Unable to deserialize a f32 value. Not supported in KMIP:".to_string(),
+            "Unable to deserialize a f32 value. Not supported in KMIP:".to_owned(),
         ))
     }
 
@@ -363,7 +364,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         V: Visitor<'de>,
     {
         Err(TtlvError::custom(
-            "Unable to deserialize a char value. Not supported in KMIP:".to_string(),
+            "Unable to deserialize a char value. Not supported in KMIP:".to_owned(),
         ))
     }
 
@@ -415,7 +416,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
     {
         Err(TtlvError::custom(
             "Unable to deserialize a byte array (not in a ByteString). Not supported in KMIP:"
-                .to_string(),
+                .to_owned(),
         ))
     }
 
@@ -426,7 +427,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
     {
         Err(TtlvError::custom(
             "Unable to deserialize a byte array (not in a ByteString). Not supported in KMIP:"
-                .to_string(),
+                .to_owned(),
         ))
     }
 
@@ -446,7 +447,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         V: Visitor<'de>,
     {
         Err(TtlvError::custom(
-            "Unable to deserialize unit. Not supported in KMIP:".to_string(),
+            "Unable to deserialize unit. Not supported in KMIP:".to_owned(),
         ))
     }
 
@@ -457,7 +458,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         V: Visitor<'de>,
     {
         Err(TtlvError::custom(
-            "Unable to deserialize unit struct. Not supported in KMIP:".to_string(),
+            "Unable to deserialize unit struct. Not supported in KMIP:".to_owned(),
         ))
     }
 
@@ -483,7 +484,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         match self.deserializing {
             Deserializing::StructureTag => Err(TtlvError::custom(
                 "deserialize_seq. A seq should not be deserialized when deserializing a tag"
-                    .to_string(),
+                    .to_owned(),
             )),
             Deserializing::StructureValue => {
                 let value = &self.get_structure()?[self.index - 1].value;
@@ -503,7 +504,7 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
                     {
                         visitor.visit_seq(TtlvDeserializer {
                             deserializing: Deserializing::BigInt,
-                            inputs: Inputs::BigInt(to_u32_digits(big_int)),
+                            inputs: Inputs::BigInt(to_u32_digits(big_int)?),
                             index: 0,
                         })
                     }
@@ -606,8 +607,8 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
     // #[instrument(skip(self, visitor))]
     fn deserialize_enum<V>(
         self,
-        _name: &'static str,
-        _variants: &'static [&'static str],
+        name: &'static str,
+        variants: &'static [&'static str],
         visitor: V,
     ) -> Result<V::Value>
     where
@@ -616,14 +617,14 @@ impl<'de, 'a> de::Deserializer<'de> for &'a mut TtlvDeserializer<'de> {
         match &self.deserializing {
             Deserializing::StructureTag => Err(TtlvError::custom(
                 "deserialize_enum. An enum should not be deserialized when deserializing a tag"
-                    .to_string(),
+                    .to_owned(),
             )),
             Deserializing::StructureValue => {
                 let value = &self.get_structure()?[self.index - 1].value;
                 trace!(
                     "\ndeserialize_enum {}: {:?}, \n[{}]: {:#?}\n",
-                    _name,
-                    _variants,
+                    name,
+                    variants,
                     &self.index - 1,
                     &value
                 );
@@ -709,7 +710,7 @@ impl<'de> MapAccess<'de> for TtlvDeserializer<'de> {
             Deserializing::StructureTag => Err(TtlvError::custom(
                 "next_key_seed. An next key seed should not be deserialized when deserializing a \
                  Tag"
-                .to_string(),
+                .to_owned(),
             )),
             Deserializing::StructureValue => {
                 // Check if there are no more elements.
@@ -757,7 +758,7 @@ impl<'de> MapAccess<'de> for TtlvDeserializer<'de> {
             Deserializing::StructureValue => Err(TtlvError::custom(
                 "next_value_seed. A next value seed should not be deserialized when already \
                  deserializing a Value"
-                    .to_string(),
+                    .to_owned(),
             )),
             x => Err(TtlvError::custom(format!(
                 "next_value_seed. A next value seed should not be deserialized when deserializing \
@@ -823,7 +824,7 @@ impl<'de> SeqAccess<'de> for TtlvDeserializer<'de> {
                 // method on the injected Deserializer (`self` in this case)
                 seed.deserialize(self).map(Some)
             }
-            x => Err(TtlvError::custom(format!(
+            x @ Deserializing::StructureTag => Err(TtlvError::custom(format!(
                 "next_element_seed. A next element seed should not be deserialized when \
                  deserializing a {x:?}"
             ))),
diff --git a/crate/kmip/src/kmip/ttlv/mod.rs b/crate/kmip/src/kmip/ttlv/mod.rs
index 362d0d149..4188462e8 100644
--- a/crate/kmip/src/kmip/ttlv/mod.rs
+++ b/crate/kmip/src/kmip/ttlv/mod.rs
@@ -2,6 +2,13 @@ pub mod deserializer;
 pub mod error;
 pub mod serializer;
 
+#[allow(
+    clippy::cast_possible_truncation,
+    clippy::cast_possible_wrap,
+    clippy::as_conversions,
+    clippy::unwrap_used,
+    clippy::panic
+)]
 #[cfg(test)]
 mod tests;
 
@@ -15,6 +22,8 @@ use serde::{
 };
 use time::{format_description::well_known::Rfc3339, OffsetDateTime};
 
+use crate::error::result::KmipResult;
+
 pub enum ItemTypeEnumeration {
     Structure = 0x01,
     Integer = 0x02,
@@ -117,7 +126,6 @@ impl PartialEq for TTLValue {
         match (self, other) {
             (Self::Structure(l0), Self::Structure(r0)) => l0 == r0,
             (Self::Integer(l0), Self::Integer(r0)) => l0 == r0,
-            (Self::BitMask(l0), Self::BitMask(r0)) => l0 == r0,
             (Self::LongInteger(l0), Self::LongInteger(r0)) => l0 == r0,
             (Self::BigInteger(l0), Self::BigInteger(r0)) => l0 == r0,
             (Self::Enumeration(l0), Self::Enumeration(r0)) => l0 == r0,
@@ -125,7 +133,9 @@ impl PartialEq for TTLValue {
             (Self::TextString(l0), Self::TextString(r0)) => l0 == r0,
             (Self::ByteString(l0), Self::ByteString(r0)) => l0 == r0,
             (Self::DateTime(l0), Self::DateTime(r0)) => l0.unix_timestamp() == r0.unix_timestamp(),
-            (Self::Interval(l0), Self::Interval(r0)) => l0 == r0,
+            (Self::BitMask(l0), Self::BitMask(r0)) | (Self::Interval(l0), Self::Interval(r0)) => {
+                l0 == r0
+            }
             (Self::DateTimeExtended(l0), Self::DateTimeExtended(r0)) => {
                 l0.unix_timestamp_nanos() / 1000 == r0.unix_timestamp_nanos() / 1000
             }
@@ -169,13 +179,13 @@ impl Serialize for TTLV {
                 serializer,
                 &self.tag,
                 "Integer",
-                &("0x".to_string() + &hex::encode_upper(v.to_be_bytes())),
+                &("0x".to_owned() + &hex::encode_upper(v.to_be_bytes())),
             ),
             TTLValue::LongInteger(v) => _serialize(
                 serializer,
                 &self.tag,
                 "LongInteger",
-                &("0x".to_string() + &hex::encode_upper(v.to_be_bytes())),
+                &("0x".to_owned() + &hex::encode_upper(v.to_be_bytes())),
             ),
             TTLValue::BigInteger(v) => {
                 //TODO Note that Big Integers must be sign extended to
@@ -185,7 +195,7 @@ impl Serialize for TTLV {
                     serializer,
                     &self.tag,
                     "BigInteger",
-                    &("0x".to_string() + &hex::encode_upper(v.to_bytes_be())),
+                    &("0x".to_owned() + &hex::encode_upper(v.to_bytes_be())),
                 )
             }
             TTLValue::Enumeration(v) => _serialize(serializer, &self.tag, "Enumeration", v),
@@ -207,7 +217,7 @@ impl Serialize for TTLV {
                 serializer,
                 &self.tag,
                 "DateTimeExtended",
-                &("0x".to_string()
+                &("0x".to_owned()
                     + &hex::encode_upper((v.unix_timestamp_nanos() / 1000).to_be_bytes())),
             ),
         }
@@ -252,19 +262,22 @@ impl<'de> Deserialize<'de> for IntegerOrMask {
                 })?))
             }
 
-            fn visit_str<E>(self, hex: &str) -> Result<Self::Value, E>
+            // v is in hexadecimal format
+            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
             where
                 E: de::Error,
             {
-                if &hex[0..2] != "0x" {
-                    return Err(de::Error::custom(format!("Invalid value for Mask: {hex}")))
+                if &v[0..2] != "0x" {
+                    return Err(de::Error::custom(format!("Invalid value for Mask: {v}")))
                 }
-                let bytes = hex::decode(&hex[2..])
-                    .map_err(|_e| de::Error::custom(format!("Invalid value for Mask: {hex}")))?;
-                let m: u32 =
-                    u32::from_be_bytes(bytes.as_slice().try_into().map_err(|_e| {
-                        de::Error::custom(format!("Invalid value for Mask: {hex}"))
-                    })?);
+                let bytes = hex::decode(&v[2..])
+                    .map_err(|_e| de::Error::custom(format!("Invalid value for Mask: {v}")))?;
+                let m: u32 = u32::from_be_bytes(
+                    bytes
+                        .as_slice()
+                        .try_into()
+                        .map_err(|_e| de::Error::custom(format!("Invalid value for Mask: {v}")))?,
+                );
                 Ok(IntegerOrMask::Mask(m))
             }
         }
@@ -322,7 +335,7 @@ impl<'de> Deserialize<'de> for TTLV {
                             if value.is_some() {
                                 return Err(de::Error::duplicate_field("value"))
                             }
-                            let typ = typ.clone().unwrap_or_else(|| "Structure".to_string());
+                            let typ = typ.clone().unwrap_or_else(|| "Structure".to_owned());
                             value = Some(match typ.as_str() {
                                 "Structure" => TTLValue::Structure(map.next_value()?),
                                 "Integer" => {
@@ -340,16 +353,17 @@ impl<'de> Deserialize<'de> for TTLV {
                                              start with 0x)"
                                         )))
                                     }
-                                    let bytes = hex::decode(&hex[2..]).map_err(|_| {
+                                    let bytes = hex::decode(&hex[2..]).map_err(|e| {
                                         de::Error::custom(format!(
                                             "Invalid value for i64 hex String: {hex} (not a hex \
-                                             string)"
+                                             string). Error: {e}",
                                         ))
                                     })?;
                                     let v: i64 = i64::from_be_bytes(
-                                        bytes.as_slice().try_into().map_err(|_| {
+                                        bytes.as_slice().try_into().map_err(|e| {
                                             de::Error::custom(format!(
-                                                "Invalid value for i64 hex String: {hex}"
+                                                "Invalid value for i64 hex String: {hex}. Error: \
+                                                 {e}",
                                             ))
                                         })?,
                                     );
@@ -366,9 +380,15 @@ impl<'de> Deserialize<'de> for TTLV {
                                         de::Error::custom(format!("Invalid value for Mask: {hex}"))
                                     })?;
                                     // build the `BigUint` using the `Vec<u32>` representation.
-                                    let v = BigUint::from_slice(&to_u32_digits(
-                                        &BigUint::from_bytes_be(bytes.as_slice()),
-                                    ));
+                                    let v = BigUint::from_slice(
+                                        &to_u32_digits(&BigUint::from_bytes_be(bytes.as_slice()))
+                                            .map_err(|e| {
+                                            de::Error::custom(format!(
+                                                "Invalid value for BigInteger: {}. Error: {}",
+                                                &hex, e
+                                            ))
+                                        })?,
+                                    );
                                     TTLValue::BigInteger(v)
                                 }
                                 "Enumeration" => {
@@ -411,23 +431,25 @@ impl<'de> Deserialize<'de> for TTLV {
                                              start with 0x)",
                                         )))
                                     }
-                                    let bytes = hex::decode(&hex[2..]).map_err(|_| {
+                                    let bytes = hex::decode(&hex[2..]).map_err(|e| {
                                         de::Error::custom(format!(
                                             "Invalid value for i64 hex String: {hex} (not an hex \
-                                             string)"
+                                             string). Error: {e}",
                                         ))
                                     })?;
                                     let v = i128::from_be_bytes(
-                                        bytes.as_slice().try_into().map_err(|_| {
+                                        bytes.as_slice().try_into().map_err(|e| {
                                             de::Error::custom(format!(
-                                                "Invalid value for i64 hex String: {hex}"
+                                                "Invalid value for i64 hex String: {hex}. Error: \
+                                                 {e}",
                                             ))
                                         })?,
                                     );
                                     let dt = OffsetDateTime::from_unix_timestamp_nanos(v * 1000)
-                                        .map_err(|_| {
+                                        .map_err(|e| {
                                             de::Error::custom(format!(
-                                                "Invalid value for unix timestamp: {hex}",
+                                                "Invalid value for unix timestamp: {hex}. Error: \
+                                                 {e}",
                                             ))
                                         })?;
                                     TTLValue::DateTimeExtended(dt)
@@ -456,8 +478,7 @@ impl<'de> Deserialize<'de> for TTLV {
 ///
 /// This conversion is done manually, as `num-bigint-dig`
 /// doesn't provide such conversion.
-#[must_use]
-pub fn to_u32_digits(big_int: &BigUint) -> Vec<u32> {
+pub fn to_u32_digits(big_int: &BigUint) -> KmipResult<Vec<u32>> {
     // Since the KMS works with big-endian representation of byte arrays, casting
     // a group of 4 bytes in big-endian u32 representation needs revert iter so
     // that if you have a chunk [0, 12, 143, 239] you will do
@@ -471,14 +492,13 @@ pub fn to_u32_digits(big_int: &BigUint) -> Vec<u32> {
     let mut bytes_be = big_int.to_bytes_be();
     bytes_be.reverse();
 
-    bytes_be
-        .chunks(4)
-        .map(|group_of_4_bytes| {
-            let mut acc = 0;
-            for (k, elt) in group_of_4_bytes.iter().enumerate() {
-                acc += u32::from(*elt) * 2_u32.pow(k as u32 * 8);
-            }
-            acc
-        })
-        .collect::<Vec<_>>()
+    let mut result = Vec::new();
+    for group_of_4_bytes in bytes_be.chunks(4) {
+        let mut acc = 0;
+        for (k, elt) in group_of_4_bytes.iter().enumerate() {
+            acc += u32::from(*elt) * 2_u32.pow(u32::try_from(k)? * 8);
+        }
+        result.push(acc);
+    }
+    Ok(result)
 }
diff --git a/crate/kmip/src/kmip/ttlv/serializer.rs b/crate/kmip/src/kmip/ttlv/serializer.rs
index 7f261457a..d7ec10b0c 100644
--- a/crate/kmip/src/kmip/ttlv/serializer.rs
+++ b/crate/kmip/src/kmip/ttlv/serializer.rs
@@ -28,13 +28,6 @@ pub fn to_ttlv<T>(value: &T) -> Result<TTLV>
 where
     T: Serialize,
 {
-    let mut serializer = TTLVSerializer {
-        parents: vec![],
-        current: TTLV::default(),
-    };
-    value.serialize(&mut serializer)?;
-    let mut ttlv = serializer.current;
-
     // postfix the TTLV if it is a root object
     trait Detect {
         fn detect(&self) -> Option<ObjectType>;
@@ -49,6 +42,14 @@ where
             Some(self.object_type())
         }
     }
+
+    let mut serializer = TTLVSerializer {
+        parents: vec![],
+        current: TTLV::default(),
+    };
+    value.serialize(&mut serializer)?;
+    let mut ttlv = serializer.current;
+
     if let Some(object_type) = value.detect() {
         ttlv.tag = object_type.to_string();
     };
@@ -138,12 +139,14 @@ impl<'a> ser::Serializer for &'a mut TTLVSerializer {
 
     // assume this is an integer
     //#[instrument(skip(self))]
+    #[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
     fn serialize_f32(self, v: f32) -> Result<Self::Ok> {
         self.serialize_i32(v.round() as i32)
     }
 
     // assume this is a Long integer
     //#[instrument(skip(self))]
+    #[allow(clippy::as_conversions, clippy::cast_possible_truncation)]
     fn serialize_f64(self, v: f64) -> Result<Self::Ok> {
         self.serialize_i64(v.round() as i64)
     }
@@ -153,7 +156,7 @@ impl<'a> ser::Serializer for &'a mut TTLVSerializer {
     //#[instrument(skip(self))]
     fn serialize_char(self, _v: char) -> Result<Self::Ok> {
         Err(TtlvError::custom(
-            "'char' type is unsupported in TTLV".to_string(),
+            "'char' type is unsupported in TTLV".to_owned(),
         ))
     }
 
@@ -209,7 +212,7 @@ impl<'a> ser::Serializer for &'a mut TTLVSerializer {
     //#[instrument(skip(self))]
     fn serialize_none(self) -> Result<Self::Ok> {
         Err(TtlvError::custom(
-            "'Option.None' is unsupported in TTLV".to_string(),
+            "'Option.None' is unsupported in TTLV".to_owned(),
         ))
     }
 
@@ -272,14 +275,14 @@ impl<'a> ser::Serializer for &'a mut TTLVSerializer {
     //#[instrument(skip(self))]
     fn serialize_unit(self) -> Result<Self::Ok> {
         Err(TtlvError::custom(
-            "'value ()' is unsupported in TTLV".to_string(),
+            "'value ()' is unsupported in TTLV".to_owned(),
         ))
     }
 
     //#[instrument(skip(self))]
     fn serialize_unit_struct(self, _name: &'static str) -> Result<Self::Ok> {
         Err(TtlvError::custom(
-            "'unit struct' is unsupported in TTLV".to_string(),
+            "'unit struct' is unsupported in TTLV".to_owned(),
         ))
     }
 
@@ -287,11 +290,11 @@ impl<'a> ser::Serializer for &'a mut TTLVSerializer {
     //#[instrument(skip(self))]
     fn serialize_unit_variant(
         self,
-        _name: &'static str,
+        name: &'static str,
         _variant_index: u32,
         variant: &'static str,
     ) -> Result<Self::Ok> {
-        trace!("serialize_unit_variant, name: {_name}::{variant}");
+        trace!("serialize_unit_variant, name: {name}::{variant}");
         self.current.value = TTLValue::Enumeration(TTLVEnumeration::Name(variant.to_owned()));
         Ok(())
     }
@@ -333,15 +336,15 @@ impl<'a> ser::Serializer for &'a mut TTLVSerializer {
     //#[instrument(skip(self, value))]
     fn serialize_newtype_variant<T>(
         self,
-        _name: &'static str,
+        name: &'static str,
         _variant_index: u32,
-        _variant: &'static str,
+        variant: &'static str,
         value: &T,
     ) -> Result<Self::Ok>
     where
         T: ?Sized + Serialize,
     {
-        trace!("serialize_newtype_variant, name: {_name}::{_variant}");
+        trace!("serialize_newtype_variant, name: {name}::{variant}");
         value.serialize(self)
     }
 
@@ -388,7 +391,7 @@ impl<'a> ser::Serializer for &'a mut TTLVSerializer {
         _len: usize,
     ) -> Result<Self::SerializeTupleVariant> {
         Err(TtlvError::custom(
-            "'tuple variant' is unsupported in TTLV".to_string(),
+            "'tuple variant' is unsupported in TTLV".to_owned(),
         ))
     }
 
@@ -399,9 +402,7 @@ impl<'a> ser::Serializer for &'a mut TTLVSerializer {
     // data.
     //#[instrument(skip(self))]
     fn serialize_map(self, _len: Option<usize>) -> Result<Self::SerializeMap> {
-        Err(TtlvError::custom(
-            "'map' is unsupported in TTLV".to_string(),
-        ))
+        Err(TtlvError::custom("'map' is unsupported in TTLV".to_owned()))
     }
 
     // A statically sized heterogeneous key-value pairing
@@ -471,7 +472,7 @@ impl<'a> ser::SerializeSeq for &'a mut TTLVSerializer {
         let parent: &mut TTLV = self
             .parents
             .last_mut()
-            .ok_or_else(|| TtlvError::custom("'no parent for the element !".to_string()))?;
+            .ok_or_else(|| TtlvError::custom("'no parent for the element !".to_owned()))?;
         // give the same tag as tag of the parent
         self.current.tag.clone_from(&parent.tag);
 
@@ -503,7 +504,7 @@ impl<'a> ser::SerializeSeq for &'a mut TTLVSerializer {
             Some(p) => p,
             None => {
                 return Err(TtlvError::custom(
-                    "'unexpected end of seq: no parent ".to_string(),
+                    "'unexpected end of seq: no parent ".to_owned(),
                 ))
             }
         };
@@ -563,7 +564,7 @@ impl<'a> ser::SerializeTupleVariant for &'a mut TTLVSerializer {
         T: ?Sized + Serialize,
     {
         Err(TtlvError::custom(
-            "'tuple variant' fields are unsupported in TTLV".to_string(),
+            "'tuple variant' fields are unsupported in TTLV".to_owned(),
         ))
         //value.serialize(&mut **self)
     }
@@ -571,7 +572,7 @@ impl<'a> ser::SerializeTupleVariant for &'a mut TTLVSerializer {
     // //#[instrument(skip(self))]
     fn end(self) -> Result<Self::Ok> {
         Err(TtlvError::custom(
-            "'tuple variant' is unsupported in TTLV".to_string(),
+            "'tuple variant' is unsupported in TTLV".to_owned(),
         ))
     }
 }
@@ -591,7 +592,7 @@ impl<'a> ser::SerializeMap for &'a mut TTLVSerializer {
         T: ?Sized + Serialize,
     {
         Err(TtlvError::custom(
-            "'map' keys are unsupported in TTLV".to_string(),
+            "'map' keys are unsupported in TTLV".to_owned(),
         ))
     }
 
@@ -604,7 +605,7 @@ impl<'a> ser::SerializeMap for &'a mut TTLVSerializer {
         T: ?Sized + Serialize,
     {
         Err(TtlvError::custom(
-            "'map' values are unsupported in TTLV".to_string(),
+            "'map' values are unsupported in TTLV".to_owned(),
         ))
     }
 
@@ -612,7 +613,7 @@ impl<'a> ser::SerializeMap for &'a mut TTLVSerializer {
     fn end(self) -> Result<Self::Ok> {
         Ok(())
         // Err(TtlvError::custom(
-        //     "'map' is unsupported in TTLV".to_string(),
+        //     "'map' is unsupported in TTLV".to_owned(),
         // ))
     }
 }
@@ -682,7 +683,7 @@ impl<'a> ser::SerializeStruct for &'a mut TTLVSerializer {
 
         let parent: &mut TTLV = match self.parents.last_mut() {
             Some(p) => p,
-            None => return Err(TtlvError::custom("'no parent for the field !".to_string())),
+            None => return Err(TtlvError::custom("'no parent for the field !".to_owned())),
         };
 
         match &mut parent.value {
@@ -711,7 +712,7 @@ impl<'a> ser::SerializeStruct for &'a mut TTLVSerializer {
             Some(p) => p,
             None => {
                 return Err(TtlvError::custom(
-                    "'unexpected end of struct fields: no parent ".to_string(),
+                    "'unexpected end of struct fields: no parent ".to_owned(),
                 ))
             }
         };
diff --git a/crate/kmip/src/lib.rs b/crate/kmip/src/lib.rs
index 3f31aff47..7462ebcb8 100644
--- a/crate/kmip/src/lib.rs
+++ b/crate/kmip/src/lib.rs
@@ -1,4 +1,54 @@
-#![allow(clippy::upper_case_acronyms)]
+#![deny(
+    nonstandard_style,
+    refining_impl_trait,
+    future_incompatible,
+    keyword_idents,
+    let_underscore,
+    rust_2024_compatibility,
+    unreachable_pub,
+    unused,
+    unsafe_code,
+    clippy::all,
+    clippy::suspicious,
+    clippy::complexity,
+    clippy::perf,
+    clippy::style,
+    clippy::pedantic,
+    clippy::cargo,
+    clippy::nursery,
+
+    // restriction lints
+    clippy::unwrap_used,
+    clippy::get_unwrap,
+    clippy::expect_used,
+    // clippy::indexing_slicing,
+    clippy::unwrap_in_result,
+    clippy::assertions_on_result_states,
+    clippy::panic,
+    clippy::panic_in_result_fn,
+    clippy::renamed_function_params,
+    clippy::verbose_file_reads,
+    clippy::str_to_string,
+    clippy::string_to_string,
+    clippy::unreachable,
+    clippy::as_conversions,
+    clippy::print_stdout,
+    clippy::empty_structs_with_brackets,
+    clippy::unseparated_literal_suffix,
+    clippy::map_err_ignore,
+    clippy::redundant_clone,
+)]
+#![allow(
+    clippy::module_name_repetitions,
+    clippy::similar_names,
+    clippy::too_many_lines,
+    clippy::cargo_common_metadata,
+    clippy::multiple_crate_versions,
+    clippy::missing_errors_doc,
+    clippy::missing_panics_doc,
+    clippy::redundant_pub_crate,
+    clippy::cognitive_complexity
+)]
 // required to detect generic type in Serializer
 #![feature(min_specialization)]
 // To parse a slice
diff --git a/crate/kmip/src/openssl/certificate.rs b/crate/kmip/src/openssl/certificate.rs
index b810faa48..da0ee962f 100644
--- a/crate/kmip/src/openssl/certificate.rs
+++ b/crate/kmip/src/openssl/certificate.rs
@@ -35,7 +35,7 @@ pub fn kmip_certificate_to_openssl(certificate: &Object) -> Result<X509, KmipErr
         }),
         _ => Err(KmipError::InvalidKmipValue(
             crate::kmip::kmip_operations::ErrorReason::Invalid_Attribute_Value,
-            "expected a certificate".to_string(),
+            "expected a certificate".to_owned(),
         )),
     }
 }
@@ -220,6 +220,7 @@ impl CertificateAttributes {
     }
 }
 
+#[allow(clippy::unwrap_used)]
 #[cfg(test)]
 mod tests {
 
@@ -245,55 +246,52 @@ mod tests {
         // ----> Check subject name
         assert_eq!(
             certificate_attributes.certificate_subject_c,
-            "US".to_string()
+            "US".to_owned()
         );
         assert_eq!(
             certificate_attributes.certificate_subject_st,
-            "Denial".to_string()
+            "Denial".to_owned()
         );
         assert_eq!(
             certificate_attributes.certificate_subject_l,
-            "Springfield".to_string()
+            "Springfield".to_owned()
         );
         assert_eq!(
             certificate_attributes.certificate_subject_o,
-            "Dis".to_string()
+            "Dis".to_owned()
         );
         assert_eq!(
             certificate_attributes.certificate_subject_cn,
-            "www.RSA-4096-example.com".to_string()
+            "www.RSA-4096-example.com".to_owned()
         );
 
         // ----> Check issuer name
-        assert_eq!(
-            certificate_attributes.certificate_issuer_c,
-            "US".to_string()
-        );
+        assert_eq!(certificate_attributes.certificate_issuer_c, "US".to_owned());
         assert_eq!(
             certificate_attributes.certificate_issuer_st,
-            "Denial".to_string()
+            "Denial".to_owned()
         );
         assert_eq!(
             certificate_attributes.certificate_issuer_l,
-            "Springfield".to_string()
+            "Springfield".to_owned()
         );
         assert_eq!(
             certificate_attributes.certificate_issuer_o,
-            "Dis".to_string()
+            "Dis".to_owned()
         );
         assert_eq!(
             certificate_attributes.certificate_issuer_cn,
-            "www.RSA-4096-example.com".to_string()
+            "www.RSA-4096-example.com".to_owned()
         );
 
         // ----> Check SPKI
         assert_eq!(
             certificate_attributes.certificate_subject_uid,
-            "33a90ad71894709603a677775d0b902edcd9eaeb".to_string()
+            "33a90ad71894709603a677775d0b902edcd9eaeb".to_owned()
         );
         assert_eq!(
             certificate_attributes.certificate_subject_serial_number,
-            "715437E16BFD2371DB5074169C3EE44E30EEB88C".to_string()
+            "715437E16BFD2371DB5074169C3EE44E30EEB88C".to_owned()
         );
     }
 }
diff --git a/crate/kmip/src/openssl/private_key.rs b/crate/kmip/src/openssl/private_key.rs
index a75804738..d7eb51af3 100644
--- a/crate/kmip/src/openssl/private_key.rs
+++ b/crate/kmip/src/openssl/private_key.rs
@@ -211,6 +211,8 @@ pub fn openssl_private_key_to_kmip(
     key_format_type: KeyFormatType,
     cryptographic_usage_mask: Option<CryptographicUsageMask>,
 ) -> Result<Object, KmipError> {
+    let cryptographic_length = Some(i32::try_from(private_key.bits())?);
+
     #[cfg(not(feature = "fips"))]
     // When not in FIPS mode, None defaults to Unrestricted.
     let cryptographic_usage_mask = if cryptographic_usage_mask.is_none() {
@@ -269,7 +271,7 @@ pub fn openssl_private_key_to_kmip(
                     },
                     attributes: Some(Box::new(Attributes {
                         cryptographic_algorithm: Some(CryptographicAlgorithm::RSA),
-                        cryptographic_length: Some(private_key.bits() as i32),
+                        cryptographic_length,
                         key_format_type: Some(KeyFormatType::TransparentRSAPrivateKey),
                         object_type: Some(ObjectType::PrivateKey),
                         cryptographic_usage_mask,
@@ -277,7 +279,7 @@ pub fn openssl_private_key_to_kmip(
                     })),
                 },
                 cryptographic_algorithm: Some(CryptographicAlgorithm::RSA),
-                cryptographic_length: Some(private_key.bits() as i32),
+                cryptographic_length,
                 key_wrapping_data: None,
                 key_compression_type: None,
             }
@@ -338,6 +340,7 @@ pub fn openssl_private_key_to_kmip(
                 ),
                 x => kmip_bail!("Unsupported curve: {:?} in KMIP format", x),
             };
+            let cryptographic_length = Some(i32::try_from(private_key.bits())?);
             KeyBlock {
                 key_format_type,
                 key_value: KeyValue {
@@ -351,7 +354,7 @@ pub fn openssl_private_key_to_kmip(
                         certificate_type: None,
                         certificate_length: None,
                         cryptographic_algorithm: Some(cryptographic_algorithm),
-                        cryptographic_length: Some(private_key.bits() as i32),
+                        cryptographic_length,
                         key_format_type: Some(KeyFormatType::TransparentECPrivateKey),
                         link: None,
                         object_type: Some(ObjectType::PrivateKey),
@@ -366,7 +369,7 @@ pub fn openssl_private_key_to_kmip(
                     })),
                 },
                 cryptographic_algorithm: Some(cryptographic_algorithm),
-                cryptographic_length: Some(private_key.bits() as i32),
+                cryptographic_length,
                 key_wrapping_data: None,
                 key_compression_type: None,
             }
@@ -388,7 +391,7 @@ pub fn openssl_private_key_to_kmip(
                     )),
                     attributes: Some(Box::new(Attributes {
                         cryptographic_algorithm,
-                        cryptographic_length: Some(private_key.bits() as i32),
+                        cryptographic_length,
                         key_format_type: Some(KeyFormatType::PKCS8),
                         object_type: Some(ObjectType::PrivateKey),
                         cryptographic_usage_mask,
@@ -396,7 +399,7 @@ pub fn openssl_private_key_to_kmip(
                     })),
                 },
                 cryptographic_algorithm,
-                cryptographic_length: Some(private_key.bits() as i32),
+                cryptographic_length,
                 key_wrapping_data: None,
                 key_compression_type: None,
             }
@@ -414,7 +417,7 @@ pub fn openssl_private_key_to_kmip(
                     )),
                     attributes: Some(Box::new(Attributes {
                         cryptographic_algorithm: Some(CryptographicAlgorithm::ECDH),
-                        cryptographic_length: Some(private_key.bits() as i32),
+                        cryptographic_length,
                         key_format_type: Some(KeyFormatType::ECPrivateKey),
                         object_type: Some(ObjectType::PrivateKey),
                         cryptographic_usage_mask,
@@ -422,7 +425,7 @@ pub fn openssl_private_key_to_kmip(
                     })),
                 },
                 cryptographic_algorithm: Some(CryptographicAlgorithm::ECDH),
-                cryptographic_length: Some(private_key.bits() as i32),
+                cryptographic_length,
                 key_wrapping_data: None,
                 key_compression_type: None,
             }
@@ -439,7 +442,7 @@ pub fn openssl_private_key_to_kmip(
                     )),
                     attributes: Some(Box::new(Attributes {
                         cryptographic_algorithm: Some(CryptographicAlgorithm::RSA),
-                        cryptographic_length: Some(private_key.bits() as i32),
+                        cryptographic_length,
                         key_format_type: Some(KeyFormatType::PKCS1),
                         object_type: Some(ObjectType::PrivateKey),
                         cryptographic_usage_mask,
@@ -447,7 +450,7 @@ pub fn openssl_private_key_to_kmip(
                     })),
                 },
                 cryptographic_algorithm: Some(CryptographicAlgorithm::RSA),
-                cryptographic_length: Some(private_key.bits() as i32),
+                cryptographic_length,
                 key_wrapping_data: None,
                 key_compression_type: None,
             }
@@ -460,6 +463,7 @@ pub fn openssl_private_key_to_kmip(
     Ok(Object::PrivateKey { key_block })
 }
 
+#[allow(clippy::unwrap_used, clippy::panic, clippy::as_conversions)]
 #[cfg(test)]
 mod tests {
     use openssl::{
diff --git a/crate/kmip/src/openssl/public_key.rs b/crate/kmip/src/openssl/public_key.rs
index bac38b4c8..bdc5ae952 100644
--- a/crate/kmip/src/openssl/public_key.rs
+++ b/crate/kmip/src/openssl/public_key.rs
@@ -176,11 +176,13 @@ pub fn openssl_public_key_to_kmip(
         cryptographic_usage_mask
     };
 
+    let cryptographic_length = Some(i32::try_from(public_key.bits())?);
     let key_block = match key_format_type {
         KeyFormatType::PKCS1 => {
             let rsa_public_key = public_key
                 .rsa()
                 .context("The public key is not an openssl RSA public key")?;
+            let cryptographic_length = Some(i32::try_from(rsa_public_key.size())? * 8);
             KeyBlock {
                 key_format_type,
                 key_value: KeyValue {
@@ -189,7 +191,7 @@ pub fn openssl_public_key_to_kmip(
                     )),
                     attributes: Some(Box::new(Attributes {
                         cryptographic_algorithm: Some(CryptographicAlgorithm::RSA),
-                        cryptographic_length: Some((rsa_public_key.size() * 8) as i32),
+                        cryptographic_length,
                         key_format_type: Some(KeyFormatType::PKCS1),
                         object_type: Some(ObjectType::PublicKey),
                         cryptographic_usage_mask,
@@ -197,7 +199,7 @@ pub fn openssl_public_key_to_kmip(
                     })),
                 },
                 cryptographic_algorithm: Some(CryptographicAlgorithm::RSA),
-                cryptographic_length: Some((rsa_public_key.size() * 8) as i32),
+                cryptographic_length,
                 key_wrapping_data: None,
                 key_compression_type: None,
             }
@@ -217,7 +219,7 @@ pub fn openssl_public_key_to_kmip(
                     key_material: KeyMaterial::ByteString(spki_der),
                     attributes: Some(Box::new(Attributes {
                         cryptographic_algorithm,
-                        cryptographic_length: Some(public_key.bits() as i32),
+                        cryptographic_length,
                         key_format_type: Some(KeyFormatType::PKCS8),
                         object_type: Some(ObjectType::PublicKey),
                         cryptographic_usage_mask,
@@ -225,7 +227,7 @@ pub fn openssl_public_key_to_kmip(
                     })),
                 },
                 cryptographic_algorithm,
-                cryptographic_length: Some(public_key.bits() as i32),
+                cryptographic_length,
                 key_wrapping_data: None,
                 key_compression_type: None,
             }
@@ -245,7 +247,7 @@ pub fn openssl_public_key_to_kmip(
                     },
                     attributes: Some(Box::new(Attributes {
                         cryptographic_algorithm: Some(CryptographicAlgorithm::RSA),
-                        cryptographic_length: Some(public_key.bits() as i32),
+                        cryptographic_length,
                         key_format_type: Some(KeyFormatType::TransparentRSAPublicKey),
                         object_type: Some(ObjectType::PublicKey),
                         cryptographic_usage_mask,
@@ -253,7 +255,7 @@ pub fn openssl_public_key_to_kmip(
                     })),
                 },
                 cryptographic_algorithm: Some(CryptographicAlgorithm::RSA),
-                cryptographic_length: Some(public_key.bits() as i32),
+                cryptographic_length,
                 key_wrapping_data: None,
                 key_compression_type: None,
             }
@@ -299,7 +301,7 @@ pub fn openssl_public_key_to_kmip(
                             },
                             attributes: Some(Box::new(Attributes {
                                 cryptographic_algorithm: Some(CryptographicAlgorithm::ECDH),
-                                cryptographic_length: Some(public_key.bits() as i32),
+                                cryptographic_length,
                                 key_format_type: Some(KeyFormatType::TransparentECPublicKey),
                                 object_type: Some(ObjectType::PublicKey),
                                 cryptographic_domain_parameters: Some(
@@ -313,7 +315,7 @@ pub fn openssl_public_key_to_kmip(
                             })),
                         },
                         cryptographic_algorithm: Some(CryptographicAlgorithm::ECDH),
-                        cryptographic_length: Some(public_key.bits() as i32),
+                        cryptographic_length,
                         key_wrapping_data: None,
                         key_compression_type: None,
                     }
@@ -329,7 +331,7 @@ pub fn openssl_public_key_to_kmip(
                             },
                             attributes: Some(Box::new(Attributes {
                                 cryptographic_algorithm: Some(CryptographicAlgorithm::ECDH),
-                                cryptographic_length: Some(public_key.bits() as i32),
+                                cryptographic_length,
                                 key_format_type: Some(KeyFormatType::TransparentECPublicKey),
                                 object_type: Some(ObjectType::PublicKey),
                                 cryptographic_domain_parameters: Some(
@@ -343,7 +345,7 @@ pub fn openssl_public_key_to_kmip(
                             })),
                         },
                         cryptographic_algorithm: Some(CryptographicAlgorithm::ECDH),
-                        cryptographic_length: Some(public_key.bits() as i32),
+                        cryptographic_length,
                         key_wrapping_data: None,
                         key_compression_type: None,
                     }
@@ -359,7 +361,7 @@ pub fn openssl_public_key_to_kmip(
                             },
                             attributes: Some(Box::new(Attributes {
                                 cryptographic_algorithm: Some(CryptographicAlgorithm::Ed25519),
-                                cryptographic_length: Some(public_key.bits() as i32),
+                                cryptographic_length,
                                 key_format_type: Some(KeyFormatType::TransparentECPublicKey),
                                 object_type: Some(ObjectType::PublicKey),
                                 cryptographic_domain_parameters: Some(
@@ -373,7 +375,7 @@ pub fn openssl_public_key_to_kmip(
                             })),
                         },
                         cryptographic_algorithm: Some(CryptographicAlgorithm::Ed25519),
-                        cryptographic_length: Some(public_key.bits() as i32),
+                        cryptographic_length,
                         key_wrapping_data: None,
                         key_compression_type: None,
                     }
@@ -389,7 +391,7 @@ pub fn openssl_public_key_to_kmip(
                             },
                             attributes: Some(Box::new(Attributes {
                                 cryptographic_algorithm: Some(CryptographicAlgorithm::ECDH),
-                                cryptographic_length: Some(public_key.bits() as i32),
+                                cryptographic_length,
                                 key_format_type: Some(KeyFormatType::TransparentECPublicKey),
                                 object_type: Some(ObjectType::PublicKey),
                                 cryptographic_domain_parameters: Some(
@@ -403,7 +405,7 @@ pub fn openssl_public_key_to_kmip(
                             })),
                         },
                         cryptographic_algorithm: Some(CryptographicAlgorithm::ECDH),
-                        cryptographic_length: Some(public_key.bits() as i32),
+                        cryptographic_length,
                         key_wrapping_data: None,
                         key_compression_type: None,
                     }
@@ -419,7 +421,7 @@ pub fn openssl_public_key_to_kmip(
                             },
                             attributes: Some(Box::new(Attributes {
                                 cryptographic_algorithm: Some(CryptographicAlgorithm::Ed448),
-                                cryptographic_length: Some(public_key.bits() as i32),
+                                cryptographic_length,
                                 key_format_type: Some(KeyFormatType::TransparentECPublicKey),
                                 object_type: Some(ObjectType::PublicKey),
                                 cryptographic_domain_parameters: Some(
@@ -433,7 +435,7 @@ pub fn openssl_public_key_to_kmip(
                             })),
                         },
                         cryptographic_algorithm: Some(CryptographicAlgorithm::Ed448),
-                        cryptographic_length: Some(public_key.bits() as i32),
+                        cryptographic_length,
                         key_wrapping_data: None,
                         key_compression_type: None,
                     }
@@ -447,6 +449,7 @@ pub fn openssl_public_key_to_kmip(
     Ok(Object::PublicKey { key_block })
 }
 
+#[allow(clippy::unwrap_used, clippy::panic, clippy::as_conversions)]
 #[cfg(test)]
 mod tests {
     use openssl::{
diff --git a/crate/pkcs11/module/src/core/attribute.rs b/crate/pkcs11/module/src/core/attribute.rs
index 4f84b8735..25f1a7fd8 100644
--- a/crate/pkcs11/module/src/core/attribute.rs
+++ b/crate/pkcs11/module/src/core/attribute.rs
@@ -374,7 +374,7 @@ impl Attributes {
     pub fn get_class(&self) -> MResult<CK_OBJECT_CLASS> {
         match self.get(AttributeType::Class) {
             Some(Attribute::Class(class)) => Ok(*class),
-            None => Err(MError::Todo("get_class: no class attribute".to_string())),
+            None => Err(MError::Todo("get_class: no class attribute".to_owned())),
             other => Err(MError::Todo(format!(
                 "get_class: unexpected attribute value: {other:?}, on class attribute type"
             ))),
diff --git a/crate/pkcs11/provider/src/backend.rs b/crate/pkcs11/provider/src/backend.rs
index 96f79d8bc..a397b5b99 100644
--- a/crate/pkcs11/provider/src/backend.rs
+++ b/crate/pkcs11/provider/src/backend.rs
@@ -76,7 +76,7 @@ impl Backend for CkmsBackend {
             .unwrap_or_else(|_| COSMIAN_PKCS11_DISK_ENCRYPTION_TAG.to_string());
         let kms_objects = get_kms_objects(
             &self.kms_client,
-            &[disk_encryption_tag, "_cert".to_string()],
+            &[disk_encryption_tag, "_cert".to_owned()],
             Some(KeyFormatType::X509),
         )?;
         let mut result = Vec::with_capacity(kms_objects.len());
@@ -110,7 +110,7 @@ impl Backend for CkmsBackend {
         let disk_encryption_tag = std::env::var("COSMIAN_PKCS11_DISK_ENCRYPTION_TAG")
             .unwrap_or_else(|_| COSMIAN_PKCS11_DISK_ENCRYPTION_TAG.to_string());
         Ok(
-            locate_kms_objects(&self.kms_client, &[disk_encryption_tag, "_sk".to_string()])?
+            locate_kms_objects(&self.kms_client, &[disk_encryption_tag, "_sk".to_owned()])?
                 .into_iter()
                 .map(|id| {
                     Arc::new(Pkcs11PrivateKey::new(id, RemoteObjectType::PrivateKey))
@@ -139,8 +139,8 @@ impl Backend for CkmsBackend {
             .unwrap_or_else(|_| COSMIAN_PKCS11_DISK_ENCRYPTION_TAG.to_string());
         let kms_objects = get_kms_objects(
             &self.kms_client,
-            &[disk_encryption_tag, "_kk".to_string()],
-            Some(KeyFormatType::Raw), // that is the default format type
+            &[disk_encryption_tag, "_kk".to_owned()],
+            Some(KeyFormatType::Raw),
         )?;
         let mut result = Vec::with_capacity(kms_objects.len());
         for dao in kms_objects {
@@ -188,7 +188,7 @@ impl PrivateKey for EmptyPrivateKeyImpl {
     }
 
     fn label(&self) -> String {
-        "PrivateKeyImpl".to_string()
+        "PrivateKeyImpl".to_owned()
     }
 
     fn sign(
diff --git a/crate/pkcs11/provider/src/kms_object.rs b/crate/pkcs11/provider/src/kms_object.rs
index 5f71e1996..e8e4d26a8 100644
--- a/crate/pkcs11/provider/src/kms_object.rs
+++ b/crate/pkcs11/provider/src/kms_object.rs
@@ -151,6 +151,6 @@ pub(crate) async fn kms_decrypt_async(
     };
     let response = kms_client.decrypt(decryption_request).await?;
     response.data.ok_or_else(|| {
-        Pkcs11Error::ServerError("Decryption response does not contain data".to_string())
+        Pkcs11Error::ServerError("Decryption response does not contain data".to_owned())
     })
 }
diff --git a/crate/pkcs11/provider/src/lib.rs b/crate/pkcs11/provider/src/lib.rs
index a3ade3819..390bc18e3 100644
--- a/crate/pkcs11/provider/src/lib.rs
+++ b/crate/pkcs11/provider/src/lib.rs
@@ -19,7 +19,7 @@ mod pkcs11_private_key;
 #[no_mangle]
 pub unsafe extern "C" fn C_GetFunctionList(pp_function_list: CK_FUNCTION_LIST_PTR_PTR) -> CK_RV {
     let debug_level =
-        std::env::var("COSMIAN_PKCS11_LOGGING_LEVEL").unwrap_or_else(|_| "info".to_string());
+        std::env::var("COSMIAN_PKCS11_LOGGING_LEVEL").unwrap_or_else(|_| "info".to_owned());
     initialize_logging("ckms-pkcs11", Level::from_str(&debug_level).ok(), None);
     // Instantiate a backend with a kms client using the `kms.json` file in the local default directory.
     cosmian_pkcs11_module::traits::register_backend(Box::new(backend::CkmsBackend::instantiate(
diff --git a/crate/pyo3/src/py_kms_client.rs b/crate/pyo3/src/py_kms_client.rs
index 479c250d7..2c600bdf8 100644
--- a/crate/pyo3/src/py_kms_client.rs
+++ b/crate/pyo3/src/py_kms_client.rs
@@ -111,11 +111,11 @@ impl KmsClient {
             Some(cert_bytes) => Some(Certificate(
                 X509::from_pem(cert_bytes.as_bytes())
                     .map_err(|_| {
-                        PyException::new_err("Cannot parse TEE certificate as PEM".to_string())
+                        PyException::new_err("Cannot parse TEE certificate as PEM".to_owned())
                     })?
                     .to_der()
                     .map_err(|_| {
-                        PyException::new_err("Cannot convert TEE certificate to DER".to_string())
+                        PyException::new_err("Cannot convert TEE certificate to DER".to_owned())
                     })?,
             )),
             None => None,
@@ -295,7 +295,7 @@ impl KmsClient {
         rekey_keypair!(
             self,
             master_secret_key_identifier.0,
-            RekeyEditAction::RekeyAccessPolicy(access_policy),
+            &RekeyEditAction::RekeyAccessPolicy(access_policy),
             py
         )
     }
@@ -321,7 +321,7 @@ impl KmsClient {
         rekey_keypair!(
             self,
             master_secret_key_identifier.0,
-            RekeyEditAction::PruneAccessPolicy(access_policy),
+            &RekeyEditAction::PruneAccessPolicy(access_policy),
             py
         )
     }
@@ -349,7 +349,7 @@ impl KmsClient {
         rekey_keypair!(
             self,
             master_secret_key_identifier.0,
-            RekeyEditAction::RemoveAttribute(vec![attr]),
+            &RekeyEditAction::RemoveAttribute(vec![attr]),
             py
         )
     }
@@ -374,7 +374,7 @@ impl KmsClient {
         rekey_keypair!(
             self,
             master_secret_key_identifier.0,
-            RekeyEditAction::DisableAttribute(vec![attr]),
+            &RekeyEditAction::DisableAttribute(vec![attr]),
             py
         )
     }
@@ -400,7 +400,7 @@ impl KmsClient {
         rekey_keypair!(
             self,
             master_secret_key_identifier.0,
-            RekeyEditAction::AddAttribute(vec![(attr, EncryptionHint::new(is_hybridized))]),
+            &RekeyEditAction::AddAttribute(vec![(attr, EncryptionHint::new(is_hybridized))]),
             py
         )
     }
@@ -426,7 +426,7 @@ impl KmsClient {
         rekey_keypair!(
             self,
             master_secret_key_identifier.0,
-            RekeyEditAction::RenameAttribute(vec![(attr, new_name.to_string())]),
+            &RekeyEditAction::RenameAttribute(vec![(attr, new_name.to_string())]),
             py
         )
     }
diff --git a/crate/server/Cargo.toml b/crate/server/Cargo.toml
index b07aa1bb3..f20ffce9f 100644
--- a/crate/server/Cargo.toml
+++ b/crate/server/Cargo.toml
@@ -92,6 +92,7 @@ sqlx = { version = "0.8.1", default-features = false, features = [
   "postgres",
   "sqlite",
 ] }
+strum = { workspace = true, features = ["std", "derive", "strum_macros"] }
 thiserror = { workspace = true }
 time = { workspace = true, features = ["local-offset", "formatting"] }
 tokio = { workspace = true, features = ["full"] }
diff --git a/crate/server/src/core/cover_crypt/locate_user_decryption_keys.rs b/crate/server/src/core/cover_crypt/locate_user_decryption_keys.rs
index 6227ac1cc..886fa5eec 100644
--- a/crate/server/src/core/cover_crypt/locate_user_decryption_keys.rs
+++ b/crate/server/src/core/cover_crypt/locate_user_decryption_keys.rs
@@ -28,7 +28,7 @@ pub(crate) async fn locate_user_decryption_keys(
 ) -> KResult<Option<Vec<String>>> {
     // Convert the policy attributes to vendor attributes
     let vendor_attributes = match cover_crypt_policy_attributes_to_revoke {
-        Some(att) => Some(vec![attributes_as_vendor_attribute(att)?]),
+        Some(att) => Some(vec![attributes_as_vendor_attribute(&att)?]),
         None => None,
     };
     // Search the user decryption keys that need to be refreshed
diff --git a/crate/server/src/core/implementation.rs b/crate/server/src/core/implementation.rs
index dc19e31bd..ff4ae8128 100644
--- a/crate/server/src/core/implementation.rs
+++ b/crate/server/src/core/implementation.rs
@@ -127,7 +127,7 @@ impl KMS {
                     let mut symmetric_key = Zeroizing::from(vec![0; key_len]);
                     rand_bytes(&mut symmetric_key)?;
                     let object =
-                        create_symmetric_key_kmip_object(&symmetric_key, *cryptographic_algorithm);
+                        create_symmetric_key_kmip_object(&symmetric_key, *cryptographic_algorithm)?;
 
                     //return the object and the tags
                     Ok((object, tags))
diff --git a/crate/server/src/core/kms.rs b/crate/server/src/core/kms.rs
index 5be1fb364..8eb8b5caa 100644
--- a/crate/server/src/core/kms.rs
+++ b/crate/server/src/core/kms.rs
@@ -14,10 +14,11 @@ use cosmian_kmip::{
         kmip_messages::{Message, MessageResponse},
         kmip_operations::{
             Certify, CertifyResponse, Create, CreateKeyPair, CreateKeyPairResponse, CreateResponse,
-            Decrypt, DecryptResponse, Destroy, DestroyResponse, Encrypt, EncryptResponse, Export,
-            ExportResponse, Get, GetAttributes, GetAttributesResponse, GetResponse, Import,
-            ImportResponse, Locate, LocateResponse, ReKey, ReKeyKeyPair, ReKeyKeyPairResponse,
-            ReKeyResponse, Revoke, RevokeResponse, Validate, ValidateResponse,
+            Decrypt, DecryptResponse, DeleteAttribute, DeleteAttributeResponse, Destroy,
+            DestroyResponse, Encrypt, EncryptResponse, Export, ExportResponse, Get, GetAttributes,
+            GetAttributesResponse, GetResponse, Import, ImportResponse, Locate, LocateResponse,
+            ReKey, ReKeyKeyPair, ReKeyKeyPairResponse, ReKeyResponse, Revoke, RevokeResponse,
+            SetAttribute, SetAttributeResponse, Validate, ValidateResponse,
         },
         kmip_types::{StateEnumeration, UniqueIdentifier},
     },
@@ -336,6 +337,26 @@ impl KMS {
         operations::get_attributes(self, request, user, params).await
     }
 
+    /// This operation requests the server to either add or modify an attribute. The request contains the Unique Identifier of the Managed Object to which the attribute pertains, along with the attribute and value. If the object did not have any instances of the attribute, one is created. If the object had exactly one instance, then it is modified. If it has more than one instance an error is raised. Read-Only attributes SHALL NOT be added or modified using this operation.
+    pub(crate) async fn set_attribute(
+        &self,
+        request: SetAttribute,
+        user: &str,
+        params: Option<&ExtraDatabaseParams>,
+    ) -> KResult<SetAttributeResponse> {
+        operations::set_attribute(self, request, user, params).await
+    }
+
+    /// This operation requests the server to delete an attribute associated with a Managed Object. The request contains the Unique Identifier of the Managed Object whose attribute is to be deleted, the Current Attribute of the attribute. Attributes that are always REQUIRED to have a value SHALL never be deleted by this operation. Attempting to delete a non-existent attribute or specifying an Current Attribute for which there exists no attribute value SHALL result in an error. If no Current Attribute is specified in the request, and an Attribute Reference is specified, then all instances of the specified attribute SHALL be deleted.
+    pub(crate) async fn delete_attribute(
+        &self,
+        request: DeleteAttribute,
+        user: &str,
+        params: Option<&ExtraDatabaseParams>,
+    ) -> KResult<DeleteAttributeResponse> {
+        operations::delete_attribute(self, request, user, params).await
+    }
+
     /// This operation requests that the server search for one or more Managed
     /// Objects, depending on the attributes specified in the request. All
     /// attributes are allowed to be used. The request MAY contain a Maximum
diff --git a/crate/server/src/core/operations/create_key_pair.rs b/crate/server/src/core/operations/create_key_pair.rs
index feb53a318..6b5d1306f 100644
--- a/crate/server/src/core/operations/create_key_pair.rs
+++ b/crate/server/src/core/operations/create_key_pair.rs
@@ -291,9 +291,9 @@ pub(crate) fn generate_key_pair_and_tags(
             &Covercrypt::default(),
             private_key_uid,
             public_key_uid,
-            Some(common_attributes),
-            request.private_key_attributes,
-            request.public_key_attributes,
+            &Some(common_attributes),
+            &request.private_key_attributes,
+            &request.public_key_attributes,
         )
         .map_err(Into::into),
         other => {
diff --git a/crate/server/src/core/operations/delete_attribute.rs b/crate/server/src/core/operations/delete_attribute.rs
new file mode 100644
index 000000000..f07965975
--- /dev/null
+++ b/crate/server/src/core/operations/delete_attribute.rs
@@ -0,0 +1,144 @@
+use cosmian_kmip::kmip::{
+    kmip_operations::{DeleteAttribute, DeleteAttributeResponse},
+    kmip_types::{Attribute, AttributeReference, Tag, UniqueIdentifier},
+};
+use cosmian_kms_client::access::ObjectOperationType;
+use tracing::trace;
+
+use crate::{
+    core::{extra_database_params::ExtraDatabaseParams, KMS},
+    database::retrieve_object_for_operation,
+    error::KmsError,
+    result::{KResult, KResultHelper},
+};
+
+pub(crate) async fn delete_attribute(
+    kms: &KMS,
+    request: DeleteAttribute,
+    user: &str,
+    params: Option<&ExtraDatabaseParams>,
+) -> KResult<DeleteAttributeResponse> {
+    trace!("Delete attribute: {}", serde_json::to_string(&request)?);
+
+    // there must be an identifier
+    let uid_or_tags = request
+        .unique_identifier
+        .as_ref()
+        .ok_or(KmsError::UnsupportedPlaceholder)?
+        .as_str()
+        .context("Delete Attribute: the unique identifier must be a string")?;
+
+    let mut owm = retrieve_object_for_operation(
+        uid_or_tags,
+        ObjectOperationType::GetAttributes,
+        kms,
+        user,
+        params,
+    )
+    .await?;
+    trace!(
+        "Delete Attribute: Retrieved object for: {:?}",
+        serde_json::to_string(&owm)
+    );
+
+    let mut attributes = owm.attributes;
+
+    if let Some(attribute) = request.current_attribute {
+        match attribute {
+            Attribute::ActivationDate(activation_date) => {
+                if Some(activation_date) == attributes.activation_date {
+                    attributes.activation_date = None;
+                }
+            }
+            Attribute::CryptographicAlgorithm(algo) => {
+                if Some(algo) == attributes.cryptographic_algorithm {
+                    attributes.cryptographic_algorithm = None;
+                }
+            }
+            Attribute::CryptographicLength(length) => {
+                if Some(length) == attributes.cryptographic_length {
+                    attributes.cryptographic_length = None;
+                }
+            }
+            Attribute::CryptographicParameters(parameters) => {
+                if Some(Box::new(parameters)) == attributes.cryptographic_parameters {
+                    attributes.cryptographic_parameters = None;
+                }
+            }
+            Attribute::CryptographicDomainParameters(domain_parameters) => {
+                if Some(domain_parameters) == attributes.cryptographic_domain_parameters {
+                    attributes.cryptographic_domain_parameters = None;
+                }
+            }
+            Attribute::CryptographicUsageMask(usage_mask) => {
+                if Some(usage_mask) == attributes.cryptographic_usage_mask {
+                    attributes.cryptographic_usage_mask = None;
+                }
+            }
+            Attribute::Links(links) => {
+                if Some(links) == attributes.link {
+                    attributes.link = None;
+                }
+            }
+            Attribute::VendorAttributes(vendor_attributes) => {
+                if Some(vendor_attributes) == attributes.vendor_attributes {
+                    attributes.vendor_attributes = None;
+                }
+            }
+            x @ Attribute::State(_) => {
+                return Err(KmsError::NotSupported(format!(
+                    "Delete Attribute on {x:?} not supported"
+                )));
+            }
+        }
+    };
+
+    if let Some(attribute_references) = request.attribute_references {
+        for attribute_reference in attribute_references {
+            match attribute_reference {
+                AttributeReference::Standard(tag) => match tag {
+                    Tag::ActivationDate => {
+                        attributes.activation_date = None;
+                    }
+                    Tag::CryptographicAlgorithm => {
+                        attributes.cryptographic_algorithm = None;
+                    }
+                    Tag::CryptographicLength => {
+                        attributes.cryptographic_length = None;
+                    }
+                    Tag::CryptographicParameters => {
+                        attributes.cryptographic_parameters = None;
+                    }
+                    Tag::CryptographicDomainParameters => {
+                        attributes.cryptographic_domain_parameters = None;
+                    }
+                    Tag::CryptographicUsageMask => {
+                        attributes.cryptographic_usage_mask = None;
+                    }
+                    Tag::LinkType => {
+                        attributes.link = None;
+                    }
+                    Tag::VendorExtension => {
+                        attributes.vendor_attributes = None;
+                    }
+                    _ => {}
+                },
+                AttributeReference::Vendor(_) => attributes.vendor_attributes = None,
+            }
+        }
+    }
+
+    let tags = kms.db.retrieve_tags(&owm.id, params).await?;
+
+    if let Ok(object_attributes) = owm.object.attributes_mut() {
+        *object_attributes = attributes.clone();
+    }
+
+    kms.db
+        .update_object(&owm.id, &owm.object, &attributes, Some(&tags), params)
+        .await?;
+
+    Ok(DeleteAttributeResponse {
+        unique_identifier: UniqueIdentifier::TextString(owm.id.clone()),
+    })
+}
diff --git a/crate/server/src/core/operations/dispatch.rs b/crate/server/src/core/operations/dispatch.rs
index eeff95d15..a46fec67f 100644
--- a/crate/server/src/core/operations/dispatch.rs
+++ b/crate/server/src/core/operations/dispatch.rs
@@ -1,7 +1,8 @@
 use cosmian_kmip::kmip::{
     kmip_operations::{
-        Certify, Create, CreateKeyPair, Decrypt, Destroy, Encrypt, Export, Get, GetAttributes,
-        Import, Locate, Operation, ReKey, ReKeyKeyPair, Revoke, Validate,
+        Certify, Create, CreateKeyPair, Decrypt, DeleteAttribute, Destroy, Encrypt, Export, Get,
+        GetAttributes, Import, Locate, Operation, ReKey, ReKeyKeyPair, Revoke, SetAttribute,
+        Validate,
     },
     ttlv::{deserializer::from_ttlv, TTLV},
 };
@@ -66,6 +67,16 @@ pub(crate) async fn dispatch(
             let resp = kms.get_attributes(req, user, database_params).await?;
             Operation::GetAttributesResponse(resp)
         }
+        "SetAttribute" => {
+            let req = from_ttlv::<SetAttribute>(ttlv)?;
+            let resp = kms.set_attribute(req, user, database_params).await?;
+            Operation::SetAttributeResponse(resp)
+        }
+        "DeleteAttribute" => {
+            let req = from_ttlv::<DeleteAttribute>(ttlv)?;
+            let resp = kms.delete_attribute(req, user, database_params).await?;
+            Operation::DeleteAttributeResponse(resp)
+        }
         "Import" => {
             let req = from_ttlv::<Import>(ttlv)?;
             let resp = kms.import(req, user, database_params).await?;
diff --git a/crate/server/src/core/operations/export_utils.rs b/crate/server/src/core/operations/export_utils.rs
index c980e7591..05db44e1a 100644
--- a/crate/server/src/core/operations/export_utils.rs
+++ b/crate/server/src/core/operations/export_utils.rs
@@ -319,48 +319,45 @@ async fn process_private_key(
     }
 
     //No wrapping requested: export the private key to the requested format
-    match key_format_type {
-        Some(kft) => {
-            #[cfg(not(feature = "fips"))]
-            let supported_formats = [
-                KeyFormatType::PKCS1,
-                KeyFormatType::PKCS8,
-                KeyFormatType::TransparentECPrivateKey,
-                KeyFormatType::TransparentRSAPrivateKey,
-                KeyFormatType::ECPrivateKey,
-                KeyFormatType::PKCS12,
-                KeyFormatType::Pkcs12Legacy,
-            ];
-            #[cfg(feature = "fips")]
-            let supported_formats = [
-                KeyFormatType::PKCS1,
-                KeyFormatType::PKCS8,
-                KeyFormatType::TransparentECPrivateKey,
-                KeyFormatType::TransparentRSAPrivateKey,
-                KeyFormatType::ECPrivateKey,
-                KeyFormatType::PKCS12,
-            ];
-            if !supported_formats.contains(kft) {
-                kms_bail!(
-                    "export: unsupported Key Format Type: {:?} for a private key",
-                    kft
-                )
-            }
-            let object = openssl_private_key_to_kmip(
-                &openssl_key,
-                *kft,
-                attributes.cryptographic_usage_mask,
-            )?;
-            object_with_metadata.object = object;
-        }
-        None => {
-            // No format type requested: export the private key to the default format
-            let object = openssl_private_key_to_kmip_default_format(
-                &openssl_key,
-                attributes.cryptographic_usage_mask,
-            )?;
-            object_with_metadata.object = object;
+    if let Some(key_format_type) = key_format_type {
+        #[cfg(not(feature = "fips"))]
+        let supported_formats = [
+            KeyFormatType::PKCS1,
+            KeyFormatType::PKCS8,
+            KeyFormatType::TransparentECPrivateKey,
+            KeyFormatType::TransparentRSAPrivateKey,
+            KeyFormatType::ECPrivateKey,
+            KeyFormatType::PKCS12,
+            KeyFormatType::Pkcs12Legacy,
+        ];
+        #[cfg(feature = "fips")]
+        let supported_formats = [
+            KeyFormatType::PKCS1,
+            KeyFormatType::PKCS8,
+            KeyFormatType::TransparentECPrivateKey,
+            KeyFormatType::TransparentRSAPrivateKey,
+            KeyFormatType::ECPrivateKey,
+            KeyFormatType::PKCS12,
+        ];
+        if !supported_formats.contains(key_format_type) {
+            kms_bail!(
+                "export: unsupported Key Format Type: {:?} for a private key",
+                key_format_type
+            )
         }
+        let object = openssl_private_key_to_kmip(
+            &openssl_key,
+            *key_format_type,
+            attributes.cryptographic_usage_mask,
+        )?;
+        object_with_metadata.object = object;
+    } else {
+        // No format type requested: export the private key to the default format
+        let object = openssl_private_key_to_kmip_default_format(
+            &openssl_key,
+            attributes.cryptographic_usage_mask,
+        )?;
+        object_with_metadata.object = object;
     }
     // add the attributes back
     let key_block = object_with_metadata.object.key_block_mut()?;
@@ -452,29 +449,28 @@ async fn process_public_key(
     }
 
     //No wrapping requested: export the private key to the requested format
-    match key_format_type {
-        Some(kft) => match kft {
+    if let Some(key_format_type) = key_format_type {
+        match key_format_type {
             KeyFormatType::PKCS1
             | KeyFormatType::PKCS8
             | KeyFormatType::TransparentECPublicKey
             | KeyFormatType::TransparentRSAPublicKey => {
                 let object = openssl_public_key_to_kmip(
                     &openssl_key,
-                    *kft,
+                    *key_format_type,
                     attributes.cryptographic_usage_mask,
                 )?;
                 object_with_metadata.object = object;
             }
-            _ => kms_bail!("export: unsupported Key Format Type: {:?}", kft),
-        },
-        None => {
-            // No format type requested: export the private key to the default format
-            let object = openssl_public_key_to_kmip_default_format(
-                &openssl_key,
-                attributes.cryptographic_usage_mask,
-            )?;
-            object_with_metadata.object = object;
+            _ => kms_bail!("export: unsupported Key Format Type: {:?}", key_format_type),
         }
+    } else {
+        // No format type requested: export the private key to the default format
+        let object = openssl_public_key_to_kmip_default_format(
+            &openssl_key,
+            attributes.cryptographic_usage_mask,
+        )?;
+        object_with_metadata.object = object;
     }
 
     // add the attributes back
diff --git a/crate/server/src/core/operations/get_attributes.rs b/crate/server/src/core/operations/get_attributes.rs
index 1456c9af8..4ea4b7b57 100644
--- a/crate/server/src/core/operations/get_attributes.rs
+++ b/crate/server/src/core/operations/get_attributes.rs
@@ -11,6 +11,7 @@ use cosmian_kmip::{
     openssl::{kmip_private_key_to_openssl, kmip_public_key_to_openssl},
 };
 use cosmian_kms_client::access::ObjectOperationType;
+use strum::IntoEnumIterator;
 use tracing::{debug, trace};
 
 use crate::{
@@ -26,20 +27,6 @@ use crate::{
     result::{KResult, KResultHelper},
 };
 
-/// All the tags that can be retrieved
-const ALL_TAGS: [Tag; 10] = [
-    Tag::ActivationDate,
-    Tag::CryptographicAlgorithm,
-    Tag::CryptographicLength,
-    Tag::CryptographicParameters,
-    Tag::CryptographicDomainParameters,
-    Tag::CryptographicUsageMask,
-    Tag::KeyFormatType,
-    Tag::Certificate,
-    Tag::PrivateKey,
-    Tag::PublicKey,
-];
-
 pub(crate) async fn get_attributes(
     kms: &KMS,
     request: GetAttributes,
@@ -65,7 +52,7 @@ pub(crate) async fn get_attributes(
     )
     .await?;
     trace!(
-        "Retrieved object for get attributes: {:?}",
+        "Get Attributes: Retrieved object for get attributes: {:?}",
         serde_json::to_string(&owm)
     );
     let object_type = owm.object.object_type();
@@ -75,15 +62,6 @@ pub(crate) async fn get_attributes(
             // KMIP Attributes retrieved from dedicated column `Attributes`
             owm.attributes
         }
-        Object::CertificateRequest { .. }
-        | Object::OpaqueObject { .. }
-        | Object::PGPKey { .. }
-        | Object::SecretData { .. }
-        | Object::SplitKey { .. } => {
-            return Err(KmsError::InvalidRequest(format!(
-                "get: unsupported object type for {uid_or_tags}",
-            )))
-        }
         Object::PrivateKey { key_block } => {
             let mut attributes = key_block.key_value.attributes.clone().unwrap_or_default();
             attributes.object_type = Some(object_type);
@@ -138,9 +116,21 @@ pub(crate) async fn get_attributes(
             attributes.link.clone_from(&owm.attributes.link);
             *attributes
         }
+        Object::CertificateRequest { .. }
+        | Object::OpaqueObject { .. }
+        | Object::PGPKey { .. }
+        | Object::SecretData { .. }
+        | Object::SplitKey { .. } => {
+            return Err(KmsError::InvalidRequest(format!(
+                "get: unsupported object type for {uid_or_tags}",
+            )))
+        }
     };
 
+    trace!("Get Attributes: Attributes: {:?}", attributes);
+
     let mut req_attributes = request.attribute_references.unwrap_or_default();
+    trace!("Get Attributes: Requested attributes: {req_attributes:?}");
 
     // request all attributes
     if req_attributes.is_empty() {
@@ -150,7 +140,12 @@ pub(crate) async fn get_attributes(
             attribute_name: VENDOR_ATTR_TAG.to_owned(),
         }));
         // standard attributes
-        req_attributes.extend(ALL_TAGS.iter().map(|t| AttributeReference::Standard(*t)));
+        let mut all_tags = Vec::new();
+        for tag in Tag::iter() {
+            all_tags.push(tag);
+        }
+
+        req_attributes.extend(all_tags.iter().map(|t| AttributeReference::Standard(*t)));
     };
 
     // request selected attributes
@@ -185,6 +180,12 @@ pub(crate) async fn get_attributes(
                 Tag::ActivationDate => {
                     res.activation_date = attributes.activation_date;
                 }
+                Tag::CertificateLength => {
+                    res.certificate_length = attributes.certificate_length;
+                }
+                Tag::CertificateType => {
+                    res.certificate_type = attributes.certificate_type;
+                }
                 Tag::CryptographicAlgorithm => {
                     res.cryptographic_algorithm = attributes.cryptographic_algorithm;
                 }
@@ -205,25 +206,28 @@ pub(crate) async fn get_attributes(
                 Tag::KeyFormatType => {
                     res.key_format_type = attributes.key_format_type;
                 }
-                Tag::PrivateKey => {
-                    if let Some(link) = attributes.get_link(LinkType::PrivateKeyLink) {
-                        res.set_link(LinkType::PrivateKeyLink, link);
+                Tag::Certificate => {
+                    if let Some(certificate_attributes) = attributes.certificate_attributes.clone()
+                    {
+                        res.certificate_attributes = Some(certificate_attributes);
                     }
                 }
-                Tag::PublicKey => {
-                    if let Some(link) = attributes.get_link(LinkType::PublicKeyLink) {
-                        res.set_link(LinkType::PublicKeyLink, link);
-                    }
+                Tag::ObjectType => {
+                    res.object_type = attributes.object_type;
                 }
-                Tag::Certificate => {
-                    if let Some(link) = attributes.get_link(LinkType::PKCS12CertificateLink) {
-                        res.set_link(LinkType::PKCS12CertificateLink, link);
+                Tag::VendorExtension => {
+                    if let Some(vendor_attributes) = attributes.vendor_attributes.clone() {
+                        res.vendor_attributes = Some(vendor_attributes);
                     }
-                    if let Some(link) = attributes.get_link(LinkType::CertificateLink) {
-                        res.set_link(LinkType::CertificateLink, link);
+                }
+                Tag::LinkType => {
+                    trace!("Get Attributes: LinkType: {:?}", attributes.link);
+                    for link_type in LinkType::iter() {
+                        if let Some(link) = attributes.get_link(link_type).as_ref() {
+                            res.set_link(link_type, link.clone());
+                        }
                     }
                 }
-
                 _ => {}
             },
         }
@@ -234,6 +238,7 @@ pub(crate) async fn get_attributes(
         owm.id,
         res.get_tags()
     );
+    trace!("Get Attributes: Response: {res:?}");
     Ok(GetAttributesResponse {
         unique_identifier: UniqueIdentifier::TextString(owm.id.clone()),
         attributes: res,
diff --git a/crate/server/src/core/operations/mod.rs b/crate/server/src/core/operations/mod.rs
index 12b5d4f07..a8162ffc4 100644
--- a/crate/server/src/core/operations/mod.rs
+++ b/crate/server/src/core/operations/mod.rs
@@ -2,6 +2,7 @@ mod certify;
 mod create;
 mod create_key_pair;
 mod decrypt;
+mod delete_attribute;
 mod destroy;
 mod dispatch;
 mod encrypt;
@@ -15,6 +16,7 @@ mod message;
 mod rekey;
 mod rekey_keypair;
 mod revoke;
+mod set_attribute;
 mod validate;
 mod wrapping;
 
@@ -22,6 +24,7 @@ pub(crate) use certify::certify;
 pub(crate) use create::create;
 pub(crate) use create_key_pair::create_key_pair;
 pub(crate) use decrypt::decrypt;
+pub(crate) use delete_attribute::delete_attribute;
 pub(crate) use destroy::{destroy_operation, recursively_destroy_key};
 pub(crate) use dispatch::dispatch;
 pub(crate) use encrypt::encrypt;
@@ -35,5 +38,6 @@ pub(crate) use message::message;
 pub(crate) use rekey::rekey;
 pub(crate) use rekey_keypair::rekey_keypair;
 pub(crate) use revoke::{recursively_revoke_key, revoke_operation};
+pub(crate) use set_attribute::set_attribute;
 pub(crate) use validate::validate_operation;
 pub(crate) use wrapping::unwrap_key;
diff --git a/crate/server/src/core/operations/set_attribute.rs b/crate/server/src/core/operations/set_attribute.rs
new file mode 100644
index 000000000..820ce4b79
--- /dev/null
+++ b/crate/server/src/core/operations/set_attribute.rs
@@ -0,0 +1,130 @@
+use cosmian_kmip::kmip::{
+    kmip_objects::ObjectType,
+    kmip_operations::{SetAttribute, SetAttributeResponse},
+    kmip_types::{Attribute, UniqueIdentifier},
+};
+use cosmian_kms_client::access::ObjectOperationType;
+use tracing::{debug, trace};
+
+use crate::{
+    core::{extra_database_params::ExtraDatabaseParams, KMS},
+    database::retrieve_object_for_operation,
+    error::KmsError,
+    result::{KResult, KResultHelper},
+};
+
+pub(crate) async fn set_attribute(
+    kms: &KMS,
+    request: SetAttribute,
+    user: &str,
+    params: Option<&ExtraDatabaseParams>,
+) -> KResult<SetAttributeResponse> {
+    trace!("Set attribute: {}", serde_json::to_string(&request)?);
+
+    // there must be an identifier
+    let uid_or_tags = request
+        .unique_identifier
+        .as_ref()
+        .ok_or(KmsError::UnsupportedPlaceholder)?
+        .as_str()
+        .context("Set Attribute: the unique identifier must be a string")?;
+
+    let mut owm: crate::database::object_with_metadata::ObjectWithMetadata =
+        retrieve_object_for_operation(
+            uid_or_tags,
+            ObjectOperationType::GetAttributes,
+            kms,
+            user,
+            params,
+        )
+        .await?;
+    trace!(
+        "Set Attribute: Retrieved object for: {:?}",
+        serde_json::to_string(&owm)
+    );
+
+    let mut attributes = owm.attributes;
+
+    match request.new_attribute {
+        Attribute::ActivationDate(activation_date) => {
+            trace!("Set Attribute: Activation Date: {:?}", activation_date);
+            attributes.activation_date = Some(activation_date);
+        }
+        Attribute::CryptographicAlgorithm(cryptographic_algorithm) => {
+            trace!(
+                "Set Attribute: Cryptographic Algorithm: {:?}",
+                cryptographic_algorithm
+            );
+            attributes.cryptographic_algorithm = Some(cryptographic_algorithm);
+        }
+        Attribute::CryptographicLength(length) => {
+            trace!("Set Attribute: Cryptographic Length: {:?}", length);
+            attributes.cryptographic_length = Some(length);
+        }
+        Attribute::CryptographicParameters(parameters) => {
+            trace!("Set Attribute: Cryptographic Parameters: {:?}", parameters);
+            attributes.cryptographic_parameters = Some(Box::new(parameters));
+        }
+        Attribute::CryptographicDomainParameters(domain_parameters) => {
+            trace!(
+                "Set Attribute: Cryptographic Domain Parameters: {:?}",
+                domain_parameters
+            );
+            attributes.cryptographic_domain_parameters = Some(domain_parameters);
+        }
+        Attribute::CryptographicUsageMask(usage_mask) => {
+            trace!("Set Attribute: Cryptographic Usage Mask: {:?}", usage_mask);
+            attributes.cryptographic_usage_mask = Some(usage_mask);
+        }
+        Attribute::Links(links) => {
+            trace!("Set Attribute: Link: {:?}", links);
+            for link in &links {
+                attributes.set_link(link.link_type, link.linked_object_identifier.clone());
+            }
+        }
+        Attribute::State(state) => {
+            trace!("Set Attribute: State: {:?}", state);
+            owm.state = state;
+        }
+        Attribute::VendorAttributes(vendor_attributes) => {
+            trace!("Set Attribute: Vendor Attributes: {:?}", vendor_attributes);
+            for vendor_attribute in vendor_attributes {
+                attributes.set_vendor_attribute(
+                    &vendor_attribute.vendor_identification,
+                    &vendor_attribute.attribute_name,
+                    vendor_attribute.attribute_value,
+                );
+            }
+        }
+    }
+
+    let tags = kms.db.retrieve_tags(&owm.id, params).await?;
+
+    match owm.object.object_type() {
+        ObjectType::PublicKey
+        | ObjectType::PrivateKey
+        | ObjectType::SplitKey
+        | ObjectType::SecretData
+        | ObjectType::PGPKey
+        | ObjectType::SymmetricKey => {
+            let object_attributes = owm.object.attributes_mut()?;
+            *object_attributes = attributes.clone();
+            debug!("Set Object Attribute: {:?}", object_attributes);
+        }
+        _ => {
+            trace!(
+                "Set Attribute: Object type {:?} does not have attributes (nor key block)",
+                owm.object.object_type()
+            );
+        }
+    }
+
+    debug!("Set Attribute: {:?}", attributes);
+    kms.db
+        .update_object(&owm.id, &owm.object, &attributes, Some(&tags), params)
+        .await?;
+
+    Ok(SetAttributeResponse {
+        unique_identifier: UniqueIdentifier::TextString(owm.id.clone()),
+    })
+}
diff --git a/crate/server/src/database/redis/redis_transaction_async.rs b/crate/server/src/database/redis/redis_transaction_async.rs
index 4ea2758b5..a180e781c 100644
--- a/crate/server/src/database/redis/redis_transaction_async.rs
+++ b/crate/server/src/database/redis/redis_transaction_async.rs
@@ -143,17 +143,17 @@ mod tests {
             mgr.clone(),
             &["key"],
             Context {
-                new_value: "blah".to_string(),
+                new_value: "blah".to_owned(),
             },
             return_blah
         )?;
-        assert_eq!(res, vec!["blah".to_string()]);
+        assert_eq!(res, vec!["blah".to_owned()]);
 
         let res: Vec<String> = transaction_async!(
             mgr.clone(),
             &["key"],
             Context {
-                new_value: "blah".to_string(),
+                new_value: "blah".to_owned(),
             },
             |mut mgr: ConnectionManager, mut pipeline: Pipeline, context: Context| async move {
                 pipeline
@@ -164,7 +164,7 @@ mod tests {
                     .await
             }
         )?;
-        assert_eq!(res, vec!["blah".to_string()]);
+        assert_eq!(res, vec!["blah".to_owned()]);
 
         // now insert a key/value and modify it in a transaction
         mgr.clone().set("key", "value").await?;
@@ -187,7 +187,7 @@ mod tests {
             mgr.clone(),
             &["key"],
             Context {
-                new_value: "new".to_string()
+                new_value: "new".to_owned()
             },
             modify_key
         )?;
diff --git a/crate/server/src/expiry.rs b/crate/server/src/expiry.rs
index 2869268ce..504663083 100644
--- a/crate/server/src/expiry.rs
+++ b/crate/server/src/expiry.rs
@@ -18,8 +18,7 @@ pub(crate) async fn demo_timeout() {
         {
             let now = OffsetDateTime::now_utc();
             let end = OffsetDateTime::parse(
-                &String::from_utf8(DEMO_TIMEOUT.to_vec())
-                    .expect("should be ok to convert back to String"),
+                core::str::from_utf8(DEMO_TIMEOUT).expect("should be ok to convert back to String"),
                 &Rfc2822,
             )
             .expect("should be able to parse rfc2822 datetime");
diff --git a/crate/server/src/lib.rs b/crate/server/src/lib.rs
index 096c8c27e..5d7333a2b 100644
--- a/crate/server/src/lib.rs
+++ b/crate/server/src/lib.rs
@@ -1,7 +1,6 @@
 //! The lib is mostly useful for the CLI tests but
 //! since it is declared, all the modules in other Files
 //! will be resolved against the lib. So everything is exported
-
 #![deny(
     nonstandard_style,
     refining_impl_trait,
@@ -11,6 +10,7 @@
     rust_2024_compatibility,
     unreachable_pub,
     unused,
+    unsafe_code,
     clippy::all,
     clippy::suspicious,
     clippy::complexity,
@@ -39,6 +39,7 @@
     clippy::empty_structs_with_brackets,
     clippy::unseparated_literal_suffix,
     clippy::map_err_ignore,
+    clippy::redundant_clone,
 )]
 #![allow(
     clippy::module_name_repetitions,
@@ -66,6 +67,6 @@ pub mod telemetry;
 
 pub use database::KMSServer;
 
-#[allow(clippy::panic, clippy::unwrap_used, clippy::expect_used)]
+#[allow(clippy::panic, clippy::unwrap_used, clippy::expect_used, unsafe_code)]
 #[cfg(test)]
 mod tests;
diff --git a/crate/server/src/main.rs b/crate/server/src/main.rs
index 7c261e7e5..8b0977b41 100644
--- a/crate/server/src/main.rs
+++ b/crate/server/src/main.rs
@@ -174,42 +174,42 @@ mod tests {
     fn test_toml() {
         let config = ClapConfig {
             db: DBConfig {
-                database_type: Some("[redis-findex, postgresql,...]".to_string()),
-                database_url: Some("[redis urls]".to_string()),
+                database_type: Some("[redis-findex, postgresql,...]".to_owned()),
+                database_url: Some("[redis urls]".to_owned()),
                 sqlite_path: PathBuf::from("[sqlite path]"),
-                redis_master_password: Some("[redis master password]".to_string()),
-                redis_findex_label: Some("[redis findex label]".to_string()),
+                redis_master_password: Some("[redis master password]".to_owned()),
+                redis_findex_label: Some("[redis findex label]".to_owned()),
                 clear_database: false,
             },
             http: HttpConfig {
                 port: 443,
-                hostname: "[hostname]".to_string(),
+                hostname: "[hostname]".to_owned(),
                 https_p12_file: Some(PathBuf::from("[https p12 file]")),
-                https_p12_password: Some("[https p12 password]".to_string()),
+                https_p12_password: Some("[https p12 password]".to_owned()),
                 authority_cert_file: Some(PathBuf::from("[authority cert file]")),
                 api_token_id: None,
             },
             auth: JwtAuthConfig {
                 jwt_issuer_uri: Some(vec![
-                    "[jwt issuer uri 1]".to_string(),
-                    "[jwt issuer uri 2]".to_string(),
+                    "[jwt issuer uri 1]".to_owned(),
+                    "[jwt issuer uri 2]".to_owned(),
                 ]),
-                jwks_uri: Some(vec!["[jwks uri 1]".to_string(), "[jwks uri 2]".to_string()]),
+                jwks_uri: Some(vec!["[jwks uri 1]".to_owned(), "[jwks uri 2]".to_owned()]),
                 jwt_audience: Some(vec![
-                    "[jwt audience 1]".to_string(),
-                    "[jwt audience 2]".to_string(),
+                    "[jwt audience 1]".to_owned(),
+                    "[jwt audience 2]".to_owned(),
                 ]),
             },
             workspace: WorkspaceConfig {
                 root_data_path: PathBuf::from("[root data path]"),
                 tmp_path: PathBuf::from("[tmp path]"),
             },
-            default_username: "[default username]".to_string(),
+            default_username: "[default username]".to_owned(),
             force_default_username: false,
-            google_cse_kacls_url: Some("[google cse kacls url]".to_string()),
-            ms_dke_service_url: Some("[ms dke service url]".to_string()),
+            google_cse_kacls_url: Some("[google cse kacls url]".to_owned()),
+            ms_dke_service_url: Some("[ms dke service url]".to_owned()),
             telemetry: TelemetryConfig {
-                otlp: Some("http://localhost:4317".to_string()),
+                otlp: Some("http://localhost:4317".to_owned()),
                 quiet: false,
             },
             info: false,
diff --git a/crate/server/src/middlewares/api_token_auth.rs b/crate/server/src/middlewares/api_token_auth.rs
index 0c3cfb022..c9587409c 100644
--- a/crate/server/src/middlewares/api_token_auth.rs
+++ b/crate/server/src/middlewares/api_token_auth.rs
@@ -82,52 +82,47 @@ async fn get_api_token(kms_server: &Arc<KMS>, api_token_id: &str) -> KResult<Str
 }
 
 async fn manage_api_token(kms_server: Arc<KMS>, req: &ServiceRequest) -> KResult<()> {
-    match &kms_server.params.api_token_id {
-        Some(api_token_id) => {
-            trace!("Token authentication using this API token ID: {api_token_id}");
-            let api_token = get_api_token(&kms_server, api_token_id.as_str()).await?;
+    if let Some(api_token_id) = &kms_server.params.api_token_id {
+        trace!("Token authentication using this API token ID: {api_token_id}");
+        let api_token = get_api_token(&kms_server, api_token_id.as_str()).await?;
 
-            let client_token = req
-                .headers()
-                .get(header::AUTHORIZATION)
-                .ok_or_else(|| KmsError::InvalidRequest("Missing Authorization header".to_owned()))?
-                .to_str()
-                .map_err(|e| {
-                    KmsError::InvalidRequest(format!(
-                        "Error converting header value to string: {e:?}"
-                    ))
-                })?
-                .split("Bearer")
-                .collect::<Vec<&str>>();
+        let client_token = req
+            .headers()
+            .get(header::AUTHORIZATION)
+            .ok_or_else(|| KmsError::InvalidRequest("Missing Authorization header".to_owned()))?
+            .to_str()
+            .map_err(|e| {
+                KmsError::InvalidRequest(format!("Error converting header value to string: {e:?}"))
+            })?
+            .split("Bearer")
+            .collect::<Vec<&str>>();
 
-            if client_token.len() != 2 {
-                return Err(KmsError::InvalidRequest(format!(
-                    "Invalid Authorization header format: expected: \"Bearer <API_TOKEN>\", got \
-                     {client_token:?}"
-                )));
-            }
-            let client_token = client_token[1].trim_start().to_lowercase();
+        if client_token.len() != 2 {
+            return Err(KmsError::InvalidRequest(format!(
+                "Invalid Authorization header format: expected: \"Bearer <API_TOKEN>\", got \
+                 {client_token:?}"
+            )));
+        }
+        let client_token = client_token[1].trim_start().to_lowercase();
 
-            trace!("API Token: {api_token}");
-            trace!("Client API Token: {client_token}");
+        trace!("API Token: {api_token}");
+        trace!("Client API Token: {client_token}");
 
-            if client_token == api_token.as_str() {
-                debug!("Token authentication successful");
-                Ok(())
-            } else {
-                error!(
-                    "{:?} {} 401 unauthorized: Client and server authentication tokens mismatch",
-                    req.method(),
-                    req.path(),
-                );
-                Err(KmsError::Unauthorized(
-                    "Client and server authentication tokens mismatch".to_owned(),
-                ))
-            }
-        }
-        None => {
-            trace!("No API Token provided");
+        if client_token == api_token.as_str() {
+            debug!("Token authentication successful");
             Ok(())
+        } else {
+            error!(
+                "{:?} {} 401 unauthorized: Client and server authentication tokens mismatch",
+                req.method(),
+                req.path(),
+            );
+            Err(KmsError::Unauthorized(
+                "Client and server authentication tokens mismatch".to_owned(),
+            ))
         }
+    } else {
+        trace!("No API Token provided");
+        Ok(())
     }
 }
diff --git a/crate/server/src/routes/google_cse/jwt.rs b/crate/server/src/routes/google_cse/jwt.rs
index 74673e2f5..24c2c20bf 100644
--- a/crate/server/src/routes/google_cse/jwt.rs
+++ b/crate/server/src/routes/google_cse/jwt.rs
@@ -316,7 +316,7 @@ pub(crate) async fn validate_tokens(
 
 #[cfg(test)]
 mod tests {
-    #![allow(clippy::unwrap_used)]
+    #![allow(clippy::unwrap_used, unsafe_code)]
     use std::sync::Arc;
 
     use cosmian_logger::log_utils::log_init;
diff --git a/documentation/docs/cli/main_commands.md b/documentation/docs/cli/main_commands.md
index af8481b9f..3cf2894fe 100644
--- a/documentation/docs/cli/main_commands.md
+++ b/documentation/docs/cli/main_commands.md
@@ -32,23 +32,27 @@ Possible values:  `"true", "false"` [default: `"false"`]
 
 **`get-attributes`** [[5]](#5-ckms-get-attributes)  Get the KMIP object attributes and tags.
 
-**`locate`** [[6]](#6-ckms-locate)  Locate cryptographic objects inside the KMS
+**`set-attributes`** [[6]](#6-ckms-set-attributes)  Set the KMIP object attributes (one or multiple attributes).
 
-**`new-database`** [[7]](#7-ckms-new-database)  Initialize a new user encrypted database and return the secret (`SQLCipher` only).
+**`delete-attributes`** [[7]](#7-ckms-delete-attributes)  Delete the KMIP object attributes (one or multiple attributes).
 
-**`rsa`** [[8]](#8-ckms-rsa)  Manage RSA keys. Encrypt and decrypt data using RSA keys
+**`locate`** [[8]](#8-ckms-locate)  Locate cryptographic objects inside the KMS
 
-**`server-version`** [[9]](#9-ckms-server-version)  Print the version of the server
+**`new-database`** [[9]](#9-ckms-new-database)  Initialize a new user encrypted database and return the secret (`SQLCipher` only).
 
-**`sym`** [[10]](#10-ckms-sym)  Manage symmetric keys. Encrypt and decrypt data
+**`rsa`** [[10]](#10-ckms-rsa)  Manage RSA keys. Encrypt and decrypt data using RSA keys
 
-**`login`** [[11]](#11-ckms-login)  Login to the Identity Provider of the KMS server using the `OAuth2` authorization code flow.
+**`server-version`** [[11]](#11-ckms-server-version)  Print the version of the server
 
-**`logout`** [[12]](#12-ckms-logout)  Logout from the Identity Provider.
+**`sym`** [[12]](#12-ckms-sym)  Manage symmetric keys. Encrypt and decrypt data
 
-**`markdown`** [[13]](#13-ckms-markdown)  Action to auto-generate doc in Markdown format Run `cargo run --bin ckms -- markdown documentation/docs/cli/main_commands.md`
+**`login`** [[13]](#13-ckms-login)  Login to the Identity Provider of the KMS server using the `OAuth2` authorization code flow.
 
-**`google`** [[14]](#14-ckms-google)  Manage google elements. Handle key pairs and identities from Gmail API
+**`logout`** [[14]](#14-ckms-logout)  Logout from the Identity Provider.
+
+**`markdown`** [[15]](#15-ckms-markdown)  Action to auto-generate doc in Markdown format Run `cargo run --bin ckms -- markdown documentation/docs/cli/main_commands.md`
+
+**`google`** [[16]](#16-ckms-google)  Manage google elements. Handle key pairs and identities from Gmail API
 
 ---
 
@@ -1108,10 +1112,17 @@ Get the KMIP object attributes and tags.
 
 `--tag [-t] <TAG>` Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times
 
-`--attribute [-a] <ATTRIBUTE>` The attributes or tags to retrieve.
+`--attribute [-a] <ATTRIBUTE>` The attributes or `KMIP-tags` to retrieve.
+To specify multiple attributes, use the option multiple times.
+If not specified, all possible attributes are returned.
+
+Possible values:  `"ActivationDate", "ApplicationData", "ApplicationNamespace", "ApplicationSpecific_Information", "ArchiveDate", "AsynchronousCorrelation_Value", "AsynchronousIndicator", "Attribute", "AttributeName", "AttributeValue", "Authentication", "BatchCount", "BatchErrorContinuationOption", "BatchItem", "BatchOrderOption", "BlockCipherMode", "CancellationResult", "Certificate", "CertificateRequest", "CertificateRequestType", "CertificateType", "CertificateValue", "CompromiseDate", "CompromiseOccurrenceDate", "ContactInformation", "Credential", "CredentialType", "CredentialValue", "CriticalityIndicator", "CRTCoefficient", "CryptographicAlgorithm", "CryptographicDomainParameters", "CryptographicLength", "CryptographicParameters", "CryptographicUsageMask", "D", "DeactivationDate", "DerivationData", "DerivationMethod", "DerivationParameters", "DestroyDate", "Digest", "DigestValue", "EncryptionKeyInformation", "G", "HashingAlgorithm", "InitialDate", "InitializationVector", "IterationCount", "IVCounterNonce", "J", "Key", "KeyBlock", "KeyCompressionType", "KeyFormatType", "KeyMaterial", "KeyPartIdentifier", "KeyValue", "KeyWrappingData", "KeyWrappingSpecification", "LastChangeDate", "LeaseTime", "Link", "LinkType", "LinkedObjectIdentifier", "MACSignature", "MACSignatureKey_Information", "MaximumItems", "MaximumResponseSize", "MessageExtension", "Modulus", "Name", "NameType", "NameValue", "ObjectGroup", "ObjectType", "Offset", "OpaqueDataType", "OpaqueDataValue", "OpaqueObject", "Operation", "P", "PaddingMethod", "PrimeExponentP", "PrimeExponentQ", "PrimeFieldSize", "PrivateExponent", "PrivateKey", "PrivateKeyUniqueIdentifier", "ProcessStartDate", "ProtectStopDate", "ProtocolVersion", "ProtocolVersionMajor", "ProtocolVersionMinor", "PublicExponent", "PublicKey", "PublicKeyUniqueIdentifier", "PutFunction", "Q", "QString", "Qlength", "QueryFunction", "RecommendedCurve", "ReplacedUniqueIdentifier", "RequestHeader", "RequestMessage", "RequestPayload", "ResponseHeader", "ResponseMessage", "ResponsePayload", "ResultMessage", "ResultReason", "ResultStatus", "RevocationMessage", "RevocationReason", "RevocationReasonCode", "KeyRoleType", "Salt", "SecretData", "SecretDataType", "ServerInformation", "SplitKey", "SplitKeyMethod", "SplitKeyParts", "SplitKeyThreshold", "State", "StorageStatusMask", "SymmetricKey", "TimeStamp", "UniqueBatchItemID", "UniqueIdentifier", "UsageLimits", "UsageLimitsCount", "UsageLimitsTotal", "UsageLimitsUnit", "Username", "ValidityDate", "ValidityIndicator", "VendorExtension", "VendorIdentification", "WrappingMethod", "X", "Y", "Password", "DeviceIdentifier", "EncodingOption", "ExtensionInformation", "ExtensionName", "ExtensionTag", "ExtensionType", "Fresh", "MachineIdentifier", "MediaIdentifier", "NetworkIdentifier", "ObjectGroupMember", "CertificateLength", "DigitalSignatureAlgorithm", "CertificateSerialNumber", "DeviceSerialNumber", "IssuerAlternativeName", "IssuerDistinguishedName", "SubjectAlternativeName", "SubjectDistinguishedName", "X509CertificateIdentifier", "X509CertificateIssuer", "X509CertificateSubject", "KeyValueLocation", "KeyValueLocationValue", "KeyValueLocationType", "KeyValuePresent", "OriginalCreationDate", "PGPKey", "PGPKeyVersion", "AlternativeName", "AlternativeNameValue", "AlternativeNameType", "Data", "SignatureData", "DataLength", "RandomIV", "MACData", "AttestationType", "Nonce", "NonceID", "NonceValue", "AttestationMeasurement", "AttestationAssertion", "IVLength", "TagLength", "FixedFieldLength", "CounterLength", "InitialCounterValue", "InvocationFieldLength", "AttestationCapableIndicator", "OffsetItems", "LocatedItems", "CorrelationValue", "InitIndicator", "FinalIndicator", "RNGParameters", "RNGAlgorithm", "DRBGAlgorithm", "FIPS186Variation", "PredictionResistance", "RandomNumberGenerator", "ValidationInformation", "ValidationAuthorityType", "ValidationAuthorityCountry", "ValidationAuthorityURI", "ValidationVersionMajor", "ValidationVersionMinor", "ValidationType", "ValidationLevel", "ValidationCertificateIdentifier", "ValidationCertificateURI", "ValidationVendorURI", "ValidationProfile", "ProfileInformation", "ProfileName", "ServerURI", "ServerPort", "StreamingCapability", "AsynchronousCapability", "AttestationCapability", "UnwrapMode", "DestroyAction", "ShreddingAlgorithm", "RNGMode", "ClientRegistrationMethod", "CapabilityInformation", "KeyWrapType", "BatchUndoCapability", "BatchContinueCapability", "PKCS12FriendlyName", "Description", "Comment", "AuthenticatedEncryptionAdditionalData", "AuthenticatedEncryptionTag", "SaltLength", "MaskGenerator", "MaskGeneratorHashingAlgorithm", "PSource", "TrailerField", "ClientCorrelationValue", "ServerCorrelationValue", "DigestedData", "CertificateSubjectCN", "CertificateSubjectO", "CertificateSubjectOU", "CertificateSubjectEmail", "CertificateSubjectC", "CertificateSubjectST", "CertificateSubjectL", "CertificateSubjectUID", "CertificateSubjectSerialNumber", "CertificateSubjectTitle", "CertificateSubjectDC", "CertificateSubjectDNQualifier", "CertificateIssuerCN", "CertificateIssuerO", "CertificateIssuerOU", "CertificateIssuerEmail", "CertificateIssuerC", "CertificateIssuerST", "CertificateIssuerL", "CertificateIssuerUID", "CertificateIssuerSerialNumber", "CertificateIssuerTitle", "CertificateIssuerDC", "CertificateIssuerDNQualifier", "Sensitive", "AlwaysSensitive", "Extractable", "NeverExtractable", "ReplaceExisting", "Attributes", "CommonAttributes", "PrivateKeyAttributes", "PublicKeyAttributes", "ExtensionEnumeration", "ExtensionAttribute", "ExtensionParentStructureTag", "ExtensionDescription", "ServerName", "ServerSerialNumber", "ServerVersion", "ServerLoad", "ProductName", "BuildLevel", "BuildDate", "ClusterInfo", "AlternateFailoverEndpoints", "ShortUniqueIdentifier", "Reserved", "Tag", "CertificateRequestUniqueIdentifier", "NISTKeyType", "AttributeReference", "CurrentAttribute", "NewAttribute", "CertificateRequestValue", "LogMessage", "ProfileVersion", "ProfileVersionMajor", "ProfileVersionMinor", "ProtectionLevel", "ProtectionPeriod", "QuantumSafe", "QuantumSafeCapability", "Ticket", "TicketType", "TicketValue", "RequestCount", "Rights", "Objects", "Operations", "Right", "EndpointRole", "DefaultsInformation", "ObjectDefaults", "Ephemeral", "ServerHashedPassword", "OneTimePassword", "HashedPassword", "AdjustmentType", "PKCS11Interface", "PKCS11Function", "PKCS11InputParameters", "PKCS11OutputParameters", "PKCS11ReturnCode", "ProtectionStorageMask", "ProtectionStorageMasks", "InteropFunction", "InteropIdentifier", "AdjustmentValue", "CommonProtectionStorageMasks", "PrivateProtectionStorageMasks", "PublicProtectionStorageMasks"`
+
+`--link-type [-l] <LINK_TYPE>` Filter on retrieved links. Only if KMIP tag `LinkType` is used in `attribute` parameter.
 To specify multiple attributes, use the option multiple times.
+If not specified, all possible link types are returned.
 
-Possible values:  `"activation-date", "cryptographic-algorithm", "cryptographic-length", "cryptographic-parameters", "cryptographic-domain-parameters", "cryptographic-usage-mask", "key-format-type", "linked-private-key-id", "linked-public-key-id", "linked-issuer-certificate-id", "linked-certificate-id", "tags"`
+Possible values:  `"CertificateLink", "PublicKeyLink", "PrivateKeyLink", "DerivationBaseObjectLink", "DerivedKeyLink", "ReplacementObjectLink", "ReplacedObjectLink", "ParentLink", "ChildLink", "PreviousLink", "NextLink", "PKCS12CertificateLink", "PKCS12PasswordLink", "WrappingKeyLink"`
 
 `--output-file [-o] <OUTPUT_FILE>` An optional file where to export the attributes.
 The attributes will be in JSON TTLV format.
@@ -1120,7 +1131,114 @@ The attributes will be in JSON TTLV format.
 
 ---
 
-## 6 ckms locate
+## 6 ckms set-attributes
+
+Set the KMIP object attributes (one or multiple attributes).
+
+### Usage
+`ckms set-attributes [options]`
+### Arguments
+`--id [-i] <ID>` The unique identifier of the cryptographic object. If not specified, tags should be specified
+
+`--tag [-t] <TAG>` Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times
+
+`--activation-date [-d] <ACTIVATION_DATE>` Set the activation date of the key
+
+`--cryptographic-algorithm [-a] <CRYPTOGRAPHIC_ALGORITHM>` The cryptographic algorithm used by the key
+
+Possible values:  `"DES", "THREE_DES", "AES", "RSA", "DSA", "ECDSA", "HMACSHA1", "HMACSHA224", "HMACSHA256", "HMACSHA384", "HMACSHA512", "HMACMD5", "DH", "ECDH", "ECMQV", "Blowfish", "Camellia", "CAST5", "IDEA", "MARS", "RC2", "RC4", "RC5", "SKIPJACK", "Twofish", "EC", "OneTimePad", "ChaCha20", "Poly1305", "ChaCha20Poly1305", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "HMACSHA3224", "HMACSHA3256", "HMACSHA3384", "HMACSHA3512", "SHAKE128", "SHAKE256", "ARIA", "SEED", "SM2", "SM3", "SM4", "GOSTR34102012", "GOSTR34112012", "GOSTR34132015", "GOST2814789", "XMSS", "SPHINCS_256", "Page166Of230McEliece", "McEliece6960119", "McEliece8192128", "Ed25519", "Ed448", "CoverCrypt", "CoverCryptBulk"`
+
+`--cryptographic-length <CRYPTOGRAPHIC_LENGTH>` The length of the cryptographic key
+
+`--key-usage [-u] <KEY_USAGE>` The key usage
+
+Possible values:  `"sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"`
+
+`--public-key-id <PUBLIC_KEY_ID>` The link to the corresponding public key id if any
+
+`--private-key-id <PRIVATE_KEY_ID>` The link to the corresponding private key id if any
+
+`--certificate-id <CERTIFICATE_ID>` The link to the corresponding certificate id if any
+
+`--p12-id <PKCS12_CERTIFICATE_ID>` The link to the corresponding PKCS12 certificate id if any
+
+`--p12-pwd <PKCS12_PASSWORD_CERTIFICATE>` The link to the corresponding PKCS12 password certificate if any
+
+`--parent-id <PARENT_ID>` The link to the corresponding parent id if any
+
+`--child-id <CHILD_ID>` The link to the corresponding child id if any
+
+`--state [-s] <STATE>` The state of the object
+
+Possible values:  `"PreActive", "Active", "Deactivated", "Compromised", "Destroyed", "Destroyed_Compromised"`
+
+`--vendor-identification [-v] <VENDOR_IDENTIFICATION>` The vendor identification
+
+`--attribute-name [-n] <ATTRIBUTE_NAME>` The attribute name
+
+`--attribute-value <ATTRIBUTE_VALUE>` The attribute value
+
+
+
+---
+
+## 7 ckms delete-attributes
+
+Delete the KMIP object attributes (one or multiple attributes).
+
+### Usage
+`ckms delete-attributes [options]`
+### Arguments
+`--id [-i] <ID>` The unique identifier of the cryptographic object. If not specified, tags should be specified
+
+`--tag [-t] <TAG>` Tag to use to retrieve the key when no key id is specified. To specify multiple tags, use the option multiple times
+
+`--activation-date [-d] <ACTIVATION_DATE>` Set the activation date of the key
+
+`--cryptographic-algorithm [-a] <CRYPTOGRAPHIC_ALGORITHM>` The cryptographic algorithm used by the key
+
+Possible values:  `"DES", "THREE_DES", "AES", "RSA", "DSA", "ECDSA", "HMACSHA1", "HMACSHA224", "HMACSHA256", "HMACSHA384", "HMACSHA512", "HMACMD5", "DH", "ECDH", "ECMQV", "Blowfish", "Camellia", "CAST5", "IDEA", "MARS", "RC2", "RC4", "RC5", "SKIPJACK", "Twofish", "EC", "OneTimePad", "ChaCha20", "Poly1305", "ChaCha20Poly1305", "SHA3224", "SHA3256", "SHA3384", "SHA3512", "HMACSHA3224", "HMACSHA3256", "HMACSHA3384", "HMACSHA3512", "SHAKE128", "SHAKE256", "ARIA", "SEED", "SM2", "SM3", "SM4", "GOSTR34102012", "GOSTR34112012", "GOSTR34132015", "GOST2814789", "XMSS", "SPHINCS_256", "Page166Of230McEliece", "McEliece6960119", "McEliece8192128", "Ed25519", "Ed448", "CoverCrypt", "CoverCryptBulk"`
+
+`--cryptographic-length <CRYPTOGRAPHIC_LENGTH>` The length of the cryptographic key
+
+`--key-usage [-u] <KEY_USAGE>` The key usage
+
+Possible values:  `"sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-key", "mac-generate", "mac-verify", "derive-key", "key-agreement", "certificate-sign", "crl-sign", "authenticate", "unrestricted"`
+
+`--public-key-id <PUBLIC_KEY_ID>` The link to the corresponding public key id if any
+
+`--private-key-id <PRIVATE_KEY_ID>` The link to the corresponding private key id if any
+
+`--certificate-id <CERTIFICATE_ID>` The link to the corresponding certificate id if any
+
+`--p12-id <PKCS12_CERTIFICATE_ID>` The link to the corresponding PKCS12 certificate id if any
+
+`--p12-pwd <PKCS12_PASSWORD_CERTIFICATE>` The link to the corresponding PKCS12 password certificate if any
+
+`--parent-id <PARENT_ID>` The link to the corresponding parent id if any
+
+`--child-id <CHILD_ID>` The link to the corresponding child id if any
+
+`--state [-s] <STATE>` The state of the object
+
+Possible values:  `"PreActive", "Active", "Deactivated", "Compromised", "Destroyed", "Destroyed_Compromised"`
+
+`--vendor-identification [-v] <VENDOR_IDENTIFICATION>` The vendor identification
+
+`--attribute-name [-n] <ATTRIBUTE_NAME>` The attribute name
+
+`--attribute-value <ATTRIBUTE_VALUE>` The attribute value
+
+`--attribute <ATTRIBUTE>` The attributes or tags to retrieve.
+To specify multiple attributes, use the option multiple times.
+
+Possible values:  `"ActivationDate", "ApplicationData", "ApplicationNamespace", "ApplicationSpecific_Information", "ArchiveDate", "AsynchronousCorrelation_Value", "AsynchronousIndicator", "Attribute", "AttributeName", "AttributeValue", "Authentication", "BatchCount", "BatchErrorContinuationOption", "BatchItem", "BatchOrderOption", "BlockCipherMode", "CancellationResult", "Certificate", "CertificateRequest", "CertificateRequestType", "CertificateType", "CertificateValue", "CompromiseDate", "CompromiseOccurrenceDate", "ContactInformation", "Credential", "CredentialType", "CredentialValue", "CriticalityIndicator", "CRTCoefficient", "CryptographicAlgorithm", "CryptographicDomainParameters", "CryptographicLength", "CryptographicParameters", "CryptographicUsageMask", "D", "DeactivationDate", "DerivationData", "DerivationMethod", "DerivationParameters", "DestroyDate", "Digest", "DigestValue", "EncryptionKeyInformation", "G", "HashingAlgorithm", "InitialDate", "InitializationVector", "IterationCount", "IVCounterNonce", "J", "Key", "KeyBlock", "KeyCompressionType", "KeyFormatType", "KeyMaterial", "KeyPartIdentifier", "KeyValue", "KeyWrappingData", "KeyWrappingSpecification", "LastChangeDate", "LeaseTime", "Link", "LinkType", "LinkedObjectIdentifier", "MACSignature", "MACSignatureKey_Information", "MaximumItems", "MaximumResponseSize", "MessageExtension", "Modulus", "Name", "NameType", "NameValue", "ObjectGroup", "ObjectType", "Offset", "OpaqueDataType", "OpaqueDataValue", "OpaqueObject", "Operation", "P", "PaddingMethod", "PrimeExponentP", "PrimeExponentQ", "PrimeFieldSize", "PrivateExponent", "PrivateKey", "PrivateKeyUniqueIdentifier", "ProcessStartDate", "ProtectStopDate", "ProtocolVersion", "ProtocolVersionMajor", "ProtocolVersionMinor", "PublicExponent", "PublicKey", "PublicKeyUniqueIdentifier", "PutFunction", "Q", "QString", "Qlength", "QueryFunction", "RecommendedCurve", "ReplacedUniqueIdentifier", "RequestHeader", "RequestMessage", "RequestPayload", "ResponseHeader", "ResponseMessage", "ResponsePayload", "ResultMessage", "ResultReason", "ResultStatus", "RevocationMessage", "RevocationReason", "RevocationReasonCode", "KeyRoleType", "Salt", "SecretData", "SecretDataType", "ServerInformation", "SplitKey", "SplitKeyMethod", "SplitKeyParts", "SplitKeyThreshold", "State", "StorageStatusMask", "SymmetricKey", "TimeStamp", "UniqueBatchItemID", "UniqueIdentifier", "UsageLimits", "UsageLimitsCount", "UsageLimitsTotal", "UsageLimitsUnit", "Username", "ValidityDate", "ValidityIndicator", "VendorExtension", "VendorIdentification", "WrappingMethod", "X", "Y", "Password", "DeviceIdentifier", "EncodingOption", "ExtensionInformation", "ExtensionName", "ExtensionTag", "ExtensionType", "Fresh", "MachineIdentifier", "MediaIdentifier", "NetworkIdentifier", "ObjectGroupMember", "CertificateLength", "DigitalSignatureAlgorithm", "CertificateSerialNumber", "DeviceSerialNumber", "IssuerAlternativeName", "IssuerDistinguishedName", "SubjectAlternativeName", "SubjectDistinguishedName", "X509CertificateIdentifier", "X509CertificateIssuer", "X509CertificateSubject", "KeyValueLocation", "KeyValueLocationValue", "KeyValueLocationType", "KeyValuePresent", "OriginalCreationDate", "PGPKey", "PGPKeyVersion", "AlternativeName", "AlternativeNameValue", "AlternativeNameType", "Data", "SignatureData", "DataLength", "RandomIV", "MACData", "AttestationType", "Nonce", "NonceID", "NonceValue", "AttestationMeasurement", "AttestationAssertion", "IVLength", "TagLength", "FixedFieldLength", "CounterLength", "InitialCounterValue", "InvocationFieldLength", "AttestationCapableIndicator", "OffsetItems", "LocatedItems", "CorrelationValue", "InitIndicator", "FinalIndicator", "RNGParameters", "RNGAlgorithm", "DRBGAlgorithm", "FIPS186Variation", "PredictionResistance", "RandomNumberGenerator", "ValidationInformation", "ValidationAuthorityType", "ValidationAuthorityCountry", "ValidationAuthorityURI", "ValidationVersionMajor", "ValidationVersionMinor", "ValidationType", "ValidationLevel", "ValidationCertificateIdentifier", "ValidationCertificateURI", "ValidationVendorURI", "ValidationProfile", "ProfileInformation", "ProfileName", "ServerURI", "ServerPort", "StreamingCapability", "AsynchronousCapability", "AttestationCapability", "UnwrapMode", "DestroyAction", "ShreddingAlgorithm", "RNGMode", "ClientRegistrationMethod", "CapabilityInformation", "KeyWrapType", "BatchUndoCapability", "BatchContinueCapability", "PKCS12FriendlyName", "Description", "Comment", "AuthenticatedEncryptionAdditionalData", "AuthenticatedEncryptionTag", "SaltLength", "MaskGenerator", "MaskGeneratorHashingAlgorithm", "PSource", "TrailerField", "ClientCorrelationValue", "ServerCorrelationValue", "DigestedData", "CertificateSubjectCN", "CertificateSubjectO", "CertificateSubjectOU", "CertificateSubjectEmail", "CertificateSubjectC", "CertificateSubjectST", "CertificateSubjectL", "CertificateSubjectUID", "CertificateSubjectSerialNumber", "CertificateSubjectTitle", "CertificateSubjectDC", "CertificateSubjectDNQualifier", "CertificateIssuerCN", "CertificateIssuerO", "CertificateIssuerOU", "CertificateIssuerEmail", "CertificateIssuerC", "CertificateIssuerST", "CertificateIssuerL", "CertificateIssuerUID", "CertificateIssuerSerialNumber", "CertificateIssuerTitle", "CertificateIssuerDC", "CertificateIssuerDNQualifier", "Sensitive", "AlwaysSensitive", "Extractable", "NeverExtractable", "ReplaceExisting", "Attributes", "CommonAttributes", "PrivateKeyAttributes", "PublicKeyAttributes", "ExtensionEnumeration", "ExtensionAttribute", "ExtensionParentStructureTag", "ExtensionDescription", "ServerName", "ServerSerialNumber", "ServerVersion", "ServerLoad", "ProductName", "BuildLevel", "BuildDate", "ClusterInfo", "AlternateFailoverEndpoints", "ShortUniqueIdentifier", "Reserved", "Tag", "CertificateRequestUniqueIdentifier", "NISTKeyType", "AttributeReference", "CurrentAttribute", "NewAttribute", "CertificateRequestValue", "LogMessage", "ProfileVersion", "ProfileVersionMajor", "ProfileVersionMinor", "ProtectionLevel", "ProtectionPeriod", "QuantumSafe", "QuantumSafeCapability", "Ticket", "TicketType", "TicketValue", "RequestCount", "Rights", "Objects", "Operations", "Right", "EndpointRole", "DefaultsInformation", "ObjectDefaults", "Ephemeral", "ServerHashedPassword", "OneTimePassword", "HashedPassword", "AdjustmentType", "PKCS11Interface", "PKCS11Function", "PKCS11InputParameters", "PKCS11OutputParameters", "PKCS11ReturnCode", "ProtectionStorageMask", "ProtectionStorageMasks", "InteropFunction", "InteropIdentifier", "AdjustmentValue", "CommonProtectionStorageMasks", "PrivateProtectionStorageMasks", "PublicProtectionStorageMasks"`
+
+
+
+---
+
+## 8 ckms locate
 
 Locate cryptographic objects inside the KMS
 
@@ -1148,7 +1266,7 @@ To specify multiple tags, use the option multiple times.
 
 ---
 
-## 7 ckms new-database
+## 9 ckms new-database
 
 Initialize a new user encrypted database and return the secret (`SQLCipher` only).
 
@@ -1158,7 +1276,7 @@ Initialize a new user encrypted database and return the secret (`SQLCipher` only
 
 ---
 
-## 8 ckms rsa
+## 10 ckms rsa
 
 Manage RSA keys. Encrypt and decrypt data using RSA keys
 
@@ -1167,15 +1285,15 @@ Manage RSA keys. Encrypt and decrypt data using RSA keys
 
 ### Subcommands
 
-**`keys`** [[8.1]](#81-ckms-rsa-keys)  Create, destroy, import, and export RSA key pairs
+**`keys`** [[10.1]](#101-ckms-rsa-keys)  Create, destroy, import, and export RSA key pairs
 
-**`encrypt`** [[8.2]](#82-ckms-rsa-encrypt)  Encrypt a file with the given public key using either
+**`encrypt`** [[10.2]](#102-ckms-rsa-encrypt)  Encrypt a file with the given public key using either
 
  - `CKM_RSA_PKCS` a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40
  - `CKM_RSA_PKCS_OAEP` a.k.a PKCS #1 RSA OAEP as specified in PKCS#11 v2.40
  - `CKM_RSA_AES_KEY_WRAP` as specified in PKCS#11 v2.40
 
-**`decrypt`** [[8.3]](#83-ckms-rsa-decrypt)  Decrypt a file with the given public key using either
+**`decrypt`** [[10.3]](#103-ckms-rsa-decrypt)  Decrypt a file with the given public key using either
 
  - `CKM_RSA_PKCS` a.k.a PKCS #1 RSA V1.5 as specified in PKCS#11 v2.40
  - `CKM_RSA_PKCS_OAEP` a.k.a PKCS #1 RSA OAEP as specified in PKCS#11 v2.40
@@ -1183,7 +1301,7 @@ Manage RSA keys. Encrypt and decrypt data using RSA keys
 
 ---
 
-## 8.1 ckms rsa keys
+## 10.1 ckms rsa keys
 
 Create, destroy, import, and export RSA key pairs
 
@@ -1192,19 +1310,19 @@ Create, destroy, import, and export RSA key pairs
 
 ### Subcommands
 
-**`create`** [[8.1.1]](#811-ckms-rsa-keys-create)  Create a new RSA key pair
+**`create`** [[10.1.1]](#1011-ckms-rsa-keys-create)  Create a new RSA key pair
 
-**`export`** [[8.1.2]](#812-ckms-rsa-keys-export)  Export a key from the KMS
+**`export`** [[10.1.2]](#1012-ckms-rsa-keys-export)  Export a key from the KMS
 
-**`import`** [[8.1.3]](#813-ckms-rsa-keys-import)  Import a private or public key in the KMS.
+**`import`** [[10.1.3]](#1013-ckms-rsa-keys-import)  Import a private or public key in the KMS.
 
-**`revoke`** [[8.1.4]](#814-ckms-rsa-keys-revoke)  Revoke a public or private key
+**`revoke`** [[10.1.4]](#1014-ckms-rsa-keys-revoke)  Revoke a public or private key
 
-**`destroy`** [[8.1.5]](#815-ckms-rsa-keys-destroy)  Destroy a public or private key
+**`destroy`** [[10.1.5]](#1015-ckms-rsa-keys-destroy)  Destroy a public or private key
 
 ---
 
-## 8.1.1 ckms rsa keys create
+## 10.1.1 ckms rsa keys create
 
 Create a new RSA key pair
 
@@ -1219,7 +1337,7 @@ Create a new RSA key pair
 
 ---
 
-## 8.1.2 ckms rsa keys export
+## 10.1.2 ckms rsa keys export
 
 Export a key from the KMS
 
@@ -1269,7 +1387,7 @@ Possible values:  `"CBC", "ECB", "PCBC", "CFB", "OFB", "CTR", "CMAC", "CCM", "GC
 
 ---
 
-## 8.1.3 ckms rsa keys import
+## 10.1.3 ckms rsa keys import
 
 Import a private or public key in the KMS.
 
@@ -1312,7 +1430,7 @@ Possible values:  `"sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-k
 
 ---
 
-## 8.1.4 ckms rsa keys revoke
+## 10.1.4 ckms rsa keys revoke
 
 Revoke a public or private key
 
@@ -1330,7 +1448,7 @@ Revoke a public or private key
 
 ---
 
-## 8.1.5 ckms rsa keys destroy
+## 10.1.5 ckms rsa keys destroy
 
 Destroy a public or private key
 
@@ -1346,7 +1464,7 @@ Destroy a public or private key
 
 ---
 
-## 8.2 ckms rsa encrypt
+## 10.2 ckms rsa encrypt
 
 Encrypt a file with the given public key using either
 
@@ -1378,7 +1496,7 @@ Possible values:  `"sha1", "sha224", "sha256", "sha384", "sha512", "sha3-224", "
 
 ---
 
-## 8.3 ckms rsa decrypt
+## 10.3 ckms rsa decrypt
 
 Decrypt a file with the given public key using either
 
@@ -1411,7 +1529,7 @@ Possible values:  `"sha1", "sha224", "sha256", "sha384", "sha512", "sha3-224", "
 
 ---
 
-## 9 ckms server-version
+## 11 ckms server-version
 
 Print the version of the server
 
@@ -1421,7 +1539,7 @@ Print the version of the server
 
 ---
 
-## 10 ckms sym
+## 12 ckms sym
 
 Manage symmetric keys. Encrypt and decrypt data
 
@@ -1430,15 +1548,15 @@ Manage symmetric keys. Encrypt and decrypt data
 
 ### Subcommands
 
-**`keys`** [[10.1]](#101-ckms-sym-keys)  Create, destroy, import, and export symmetric keys
+**`keys`** [[12.1]](#121-ckms-sym-keys)  Create, destroy, import, and export symmetric keys
 
-**`encrypt`** [[10.2]](#102-ckms-sym-encrypt)  Encrypt a file using AES GCM
+**`encrypt`** [[12.2]](#122-ckms-sym-encrypt)  Encrypt a file using AES GCM
 
-**`decrypt`** [[10.3]](#103-ckms-sym-decrypt)  Decrypts a file using AES GCM
+**`decrypt`** [[12.3]](#123-ckms-sym-decrypt)  Decrypts a file using AES GCM
 
 ---
 
-## 10.1 ckms sym keys
+## 12.1 ckms sym keys
 
 Create, destroy, import, and export symmetric keys
 
@@ -1447,21 +1565,21 @@ Create, destroy, import, and export symmetric keys
 
 ### Subcommands
 
-**`create`** [[10.1.1]](#1011-ckms-sym-keys-create)  Create a new symmetric key
+**`create`** [[12.1.1]](#1211-ckms-sym-keys-create)  Create a new symmetric key
 
-**`re-key`** [[10.1.2]](#1012-ckms-sym-keys-re-key)  Refresh an existing symmetric key
+**`re-key`** [[12.1.2]](#1212-ckms-sym-keys-re-key)  Refresh an existing symmetric key
 
-**`export`** [[10.1.3]](#1013-ckms-sym-keys-export)  Export a key from the KMS
+**`export`** [[12.1.3]](#1213-ckms-sym-keys-export)  Export a key from the KMS
 
-**`import`** [[10.1.4]](#1014-ckms-sym-keys-import)  Import a private or public key in the KMS.
+**`import`** [[12.1.4]](#1214-ckms-sym-keys-import)  Import a private or public key in the KMS.
 
-**`revoke`** [[10.1.5]](#1015-ckms-sym-keys-revoke)  Revoke a symmetric key
+**`revoke`** [[12.1.5]](#1215-ckms-sym-keys-revoke)  Revoke a symmetric key
 
-**`destroy`** [[10.1.6]](#1016-ckms-sym-keys-destroy)  Destroy a symmetric key
+**`destroy`** [[12.1.6]](#1216-ckms-sym-keys-destroy)  Destroy a symmetric key
 
 ---
 
-## 10.1.1 ckms sym keys create
+## 12.1.1 ckms sym keys create
 
 Create a new symmetric key
 
@@ -1482,7 +1600,7 @@ Possible values:  `"chacha20", "aes", "sha3", "shake"` [default: `"aes"`]
 
 ---
 
-## 10.1.2 ckms sym keys re-key
+## 12.1.2 ckms sym keys re-key
 
 Refresh an existing symmetric key
 
@@ -1495,7 +1613,7 @@ Refresh an existing symmetric key
 
 ---
 
-## 10.1.3 ckms sym keys export
+## 12.1.3 ckms sym keys export
 
 Export a key from the KMS
 
@@ -1545,7 +1663,7 @@ Possible values:  `"CBC", "ECB", "PCBC", "CFB", "OFB", "CTR", "CMAC", "CCM", "GC
 
 ---
 
-## 10.1.4 ckms sym keys import
+## 12.1.4 ckms sym keys import
 
 Import a private or public key in the KMS.
 
@@ -1588,7 +1706,7 @@ Possible values:  `"sign", "verify", "encrypt", "decrypt", "wrap-key", "unwrap-k
 
 ---
 
-## 10.1.5 ckms sym keys revoke
+## 12.1.5 ckms sym keys revoke
 
 Revoke a symmetric key
 
@@ -1606,7 +1724,7 @@ Revoke a symmetric key
 
 ---
 
-## 10.1.6 ckms sym keys destroy
+## 12.1.6 ckms sym keys destroy
 
 Destroy a symmetric key
 
@@ -1622,7 +1740,7 @@ Destroy a symmetric key
 
 ---
 
-## 10.2 ckms sym encrypt
+## 12.2 ckms sym encrypt
 
 Encrypt a file using AES GCM
 
@@ -1644,7 +1762,7 @@ Encrypt a file using AES GCM
 
 ---
 
-## 10.3 ckms sym decrypt
+## 12.3 ckms sym decrypt
 
 Decrypts a file using AES GCM
 
@@ -1667,7 +1785,7 @@ Decrypts a file using AES GCM
 
 ---
 
-## 11 ckms login
+## 13 ckms login
 
 Login to the Identity Provider of the KMS server using the `OAuth2` authorization code flow.
 
@@ -1677,7 +1795,7 @@ Login to the Identity Provider of the KMS server using the `OAuth2` authorizatio
 
 ---
 
-## 12 ckms logout
+## 14 ckms logout
 
 Logout from the Identity Provider.
 
@@ -1687,7 +1805,7 @@ Logout from the Identity Provider.
 
 ---
 
-## 13 ckms markdown
+## 15 ckms markdown
 
 Action to auto-generate doc in Markdown format Run `cargo run --bin ckms -- markdown documentation/docs/cli/main_commands.md`
 
@@ -1701,7 +1819,7 @@ Action to auto-generate doc in Markdown format Run `cargo run --bin ckms -- mark
 
 ---
 
-## 14 ckms google
+## 16 ckms google
 
 Manage google elements. Handle key pairs and identities from Gmail API
 
@@ -1710,13 +1828,13 @@ Manage google elements. Handle key pairs and identities from Gmail API
 
 ### Subcommands
 
-**`key-pairs`** [[14.1]](#141-ckms-google-key-pairs)  Insert, get, list, enable, disabled and obliterate key pairs to Gmail API
+**`key-pairs`** [[16.1]](#161-ckms-google-key-pairs)  Insert, get, list, enable, disabled and obliterate key pairs to Gmail API
 
-**`identities`** [[14.2]](#142-ckms-google-identities)  Insert, get, list, patch and delete identities from Gmail API
+**`identities`** [[16.2]](#162-ckms-google-identities)  Insert, get, list, patch and delete identities from Gmail API
 
 ---
 
-## 14.1 ckms google key-pairs
+## 16.1 ckms google key-pairs
 
 Insert, get, list, enable, disabled and obliterate key pairs to Gmail API
 
@@ -1725,30 +1843,30 @@ Insert, get, list, enable, disabled and obliterate key pairs to Gmail API
 
 ### Subcommands
 
-**`get`** [[14.1.1]](#1411-ckms-google-key-pairs-get)  Retrieves an existing client-side encryption key pair.
+**`get`** [[16.1.1]](#1611-ckms-google-key-pairs-get)  Retrieves an existing client-side encryption key pair.
 
-**`list`** [[14.1.2]](#1412-ckms-google-key-pairs-list)  Lists client-side encryption key pairs for a user.
+**`list`** [[16.1.2]](#1612-ckms-google-key-pairs-list)  Lists client-side encryption key pairs for a user.
 
-**`enable`** [[14.1.3]](#1413-ckms-google-key-pairs-enable)  Turns on a client-side encryption key pair that was turned off. The key pair becomes active
+**`enable`** [[16.1.3]](#1613-ckms-google-key-pairs-enable)  Turns on a client-side encryption key pair that was turned off. The key pair becomes active
 again for any associated client-side encryption identities.
 
-**`disable`** [[14.1.4]](#1414-ckms-google-key-pairs-disable)  Turns off a client-side encryption key pair. The authenticated user can no longer use the key
+**`disable`** [[16.1.4]](#1614-ckms-google-key-pairs-disable)  Turns off a client-side encryption key pair. The authenticated user can no longer use the key
 pair to decrypt incoming CSE message texts or sign outgoing CSE mail. To regain access, use the
 key pairs.enable to turn on the key pair. After 30 days, you can permanently delete the key pair
 by using the key pairs.obliterate method.
 
-**`obliterate`** [[14.1.5]](#1415-ckms-google-key-pairs-obliterate)  Deletes a client-side encryption key pair permanently and immediately. You can only permanently
+**`obliterate`** [[16.1.5]](#1615-ckms-google-key-pairs-obliterate)  Deletes a client-side encryption key pair permanently and immediately. You can only permanently
 delete key pairs that have been turned off for more than 30 days. To turn off a key pair, use
 the key pairs disable method. Gmail can't restore or decrypt any messages that were encrypted by
 an obliterated key. Authenticated users and Google Workspace administrators lose access to
 reading the encrypted messages.
 
-**`create`** [[14.1.6]](#1416-ckms-google-key-pairs-create)  Creates and uploads a client-side encryption S/MIME public key certificate chain and private key
+**`create`** [[16.1.6]](#1616-ckms-google-key-pairs-create)  Creates and uploads a client-side encryption S/MIME public key certificate chain and private key
 metadata for a user.
 
 ---
 
-## 14.1.1 ckms google key-pairs get
+## 16.1.1 ckms google key-pairs get
 
 Retrieves an existing client-side encryption key pair.
 
@@ -1764,7 +1882,7 @@ Retrieves an existing client-side encryption key pair.
 
 ---
 
-## 14.1.2 ckms google key-pairs list
+## 16.1.2 ckms google key-pairs list
 
 Lists client-side encryption key pairs for a user.
 
@@ -1778,7 +1896,7 @@ Lists client-side encryption key pairs for a user.
 
 ---
 
-## 14.1.3 ckms google key-pairs enable
+## 16.1.3 ckms google key-pairs enable
 
 Turns on a client-side encryption key pair that was turned off. The key pair becomes active
 again for any associated client-side encryption identities.
@@ -1795,7 +1913,7 @@ again for any associated client-side encryption identities.
 
 ---
 
-## 14.1.4 ckms google key-pairs disable
+## 16.1.4 ckms google key-pairs disable
 
 Turns off a client-side encryption key pair. The authenticated user can no longer use the key
 pair to decrypt incoming CSE message texts or sign outgoing CSE mail. To regain access, use the
@@ -1814,7 +1932,7 @@ by using the key pairs.obliterate method.
 
 ---
 
-## 14.1.5 ckms google key-pairs obliterate
+## 16.1.5 ckms google key-pairs obliterate
 
 Deletes a client-side encryption key pair permanently and immediately. You can only permanently
 delete key pairs that have been turned off for more than 30 days. To turn off a key pair, use
@@ -1834,7 +1952,7 @@ reading the encrypted messages.
 
 ---
 
-## 14.1.6 ckms google key-pairs create
+## 16.1.6 ckms google key-pairs create
 
 Creates and uploads a client-side encryption S/MIME public key certificate chain and private key
 metadata for a user.
@@ -1863,7 +1981,7 @@ Possible values:  `"true", "false"` [default: `"false"`]
 
 ---
 
-## 14.2 ckms google identities
+## 16.2 ckms google identities
 
 Insert, get, list, patch and delete identities from Gmail API
 
@@ -1872,24 +1990,24 @@ Insert, get, list, patch and delete identities from Gmail API
 
 ### Subcommands
 
-**`get`** [[14.2.1]](#1421-ckms-google-identities-get)  Retrieves a client-side encryption identity configuration.
+**`get`** [[16.2.1]](#1621-ckms-google-identities-get)  Retrieves a client-side encryption identity configuration.
 
-**`list`** [[14.2.2]](#1422-ckms-google-identities-list)  Lists the client-side encrypted identities for an authenticated user.
+**`list`** [[16.2.2]](#1622-ckms-google-identities-list)  Lists the client-side encrypted identities for an authenticated user.
 
-**`insert`** [[14.2.3]](#1423-ckms-google-identities-insert)  Creates and configures a client-side encryption identity that's authorized to send mail from the
+**`insert`** [[16.2.3]](#1623-ckms-google-identities-insert)  Creates and configures a client-side encryption identity that's authorized to send mail from the
 user account. Google publishes the S/MIME certificate to a shared domain-wide directory so that
 people within a Google Workspace organization can encrypt and send mail to the identity.
 
-**`delete`** [[14.2.4]](#1424-ckms-google-identities-delete)  Deletes a client-side encryption identity. The authenticated user can no longer use the identity
+**`delete`** [[16.2.4]](#1624-ckms-google-identities-delete)  Deletes a client-side encryption identity. The authenticated user can no longer use the identity
 to send encrypted messages. You cannot restore the identity after you delete it. Instead, use
 the identities.create method to create another identity with the same configuration.
 
-**`patch`** [[14.2.5]](#1425-ckms-google-identities-patch)  Associates a different key pair with an existing client-side encryption identity. The updated
+**`patch`** [[16.2.5]](#1625-ckms-google-identities-patch)  Associates a different key pair with an existing client-side encryption identity. The updated
 key pair must validate against Google's S/MIME certificate profiles.
 
 ---
 
-## 14.2.1 ckms google identities get
+## 16.2.1 ckms google identities get
 
 Retrieves a client-side encryption identity configuration.
 
@@ -1903,7 +2021,7 @@ Retrieves a client-side encryption identity configuration.
 
 ---
 
-## 14.2.2 ckms google identities list
+## 16.2.2 ckms google identities list
 
 Lists the client-side encrypted identities for an authenticated user.
 
@@ -1917,7 +2035,7 @@ Lists the client-side encrypted identities for an authenticated user.
 
 ---
 
-## 14.2.3 ckms google identities insert
+## 16.2.3 ckms google identities insert
 
 Creates and configures a client-side encryption identity that's authorized to send mail from the
 user account. Google publishes the S/MIME certificate to a shared domain-wide directory so that
@@ -1935,7 +2053,7 @@ people within a Google Workspace organization can encrypt and send mail to the i
 
 ---
 
-## 14.2.4 ckms google identities delete
+## 16.2.4 ckms google identities delete
 
 Deletes a client-side encryption identity. The authenticated user can no longer use the identity
 to send encrypted messages. You cannot restore the identity after you delete it. Instead, use
@@ -1951,7 +2069,7 @@ the identities.create method to create another identity with the same configurat
 
 ---
 
-## 14.2.5 ckms google identities patch
+## 16.2.5 ckms google identities patch
 
 Associates a different key pair with an existing client-side encryption identity. The updated
 key pair must validate against Google's S/MIME certificate profiles.
diff --git a/documentation/docs/kmip_2_1/_delete_attributes.md b/documentation/docs/kmip_2_1/_delete_attributes.md
new file mode 100644
index 000000000..64a2f07f2
--- /dev/null
+++ b/documentation/docs/kmip_2_1/_delete_attributes.md
@@ -0,0 +1,179 @@
+### Specification
+
+This operation requests the server to delete an attribute associated with a Managed Object. The request contains the Unique Identifier of the Managed Object whose attribute is to be deleted, the Current Attribute of the attribute. Attributes that are always REQUIRED to have a value SHALL never be deleted by this operation. Attempting to delete a non-existent attribute or specifying an Current Attribute for which there exists no attribute value SHALL result in an error. If no Current Attribute is specified in the request, and an Attribute Reference is specified, then all instances of the specified attribute SHALL be deleted.
+
+### Implementation
+
+This operation can be applied to all [supported objects](./objects.md). One or more attributes can be set at once.
+
+### Example - Delete links on Certificate
+
+Delete the links on a certificate object.
+
+Corresponding `ckms` CLI command:
+
+```bash
+  ckms delete-attributes -i 03948573-9348-aaaa-aaaa-93857383 --public-key-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz --private-key-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz --certificate-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz
+```
+
+The request deletes the KMIP links of the public key and the private key of the underlying certificate.
+The `DeleteAttribute` operation being unitary, 3 requests are sent to the server, one for each link.
+
+Output is:
+
+```json
+Attribute deleted successfully
+          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
+[
+  {
+    "LinkType": "PublicKeyLink",
+    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+  }
+]
+Attribute deleted successfully
+          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
+[
+  {
+    "LinkType": "PrivateKeyLink",
+    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+  }
+]
+Attribute deleted successfully
+          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
+[
+  {
+    "LinkType": "CertificateLink",
+    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+  }
+]
+```
+
+=== "First Request"
+
+    ```json
+        {
+          "tag": "DeleteAttribute",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "10a875bd-9cc5-45a3-99ef-b2cdedd848bf"
+            },
+            {
+              "tag": "CurrentAttribute",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "Link",
+                  "type": "Structure",
+                  "value": [
+                    {
+                      "tag": "LinkType",
+                      "type": "Enumeration",
+                      "value": "PublicKeyLink"
+                    },
+                    {
+                      "tag": "LinkedObjectIdentifier",
+                      "type": "TextString",
+                      "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        }
+    ```
+
+=== "Second Request"
+
+    ```json
+        {
+          "tag": "DeleteAttribute",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "10a875bd-9cc5-45a3-99ef-b2cdedd848bf"
+            },
+            {
+              "tag": "CurrentAttribute",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "Link",
+                  "type": "Structure",
+                  "value": [
+                    {
+                      "tag": "LinkType",
+                      "type": "Enumeration",
+                      "value": "PrivateKeyLink"
+                    },
+                    {
+                      "tag": "LinkedObjectIdentifier",
+                      "type": "TextString",
+                      "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        }
+    ```
+
+=== "Third Request"
+
+    ```json
+        {
+          "tag": "DeleteAttribute",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "10a875bd-9cc5-45a3-99ef-b2cdedd848bf"
+            },
+            {
+              "tag": "CurrentAttribute",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "Link",
+                  "type": "Structure",
+                  "value": [
+                    {
+                      "tag": "LinkType",
+                      "type": "Enumeration",
+                      "value": "CertificateLink"
+                    },
+                    {
+                      "tag": "LinkedObjectIdentifier",
+                      "type": "TextString",
+                      "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        }
+    ```
+
+=== "Response"
+
+    ```json
+        {
+          "tag": "DeleteAttributeResponse",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1"
+            }
+          ]
+        }
+    ```
diff --git a/documentation/docs/kmip_2_1/_set_attributes.md b/documentation/docs/kmip_2_1/_set_attributes.md
new file mode 100644
index 000000000..2905fa513
--- /dev/null
+++ b/documentation/docs/kmip_2_1/_set_attributes.md
@@ -0,0 +1,208 @@
+### Specification
+
+This operation requests the server to either add or modify an attribute. The request contains the Unique Identifier of the Managed Object to which the attribute pertains, along with the attribute and value. If the object did not have any instances of the attribute, one is created. If the object had exactly one instance, then it is modified. If it has more than one instance an error is raised. Read-Only attributes SHALL NOT be added or modified using this operation.
+
+### Implementation
+
+This operation can be applied to all [supported objects](./objects.md). One or more attributes can be set at once. The operation is idempotent, meaning that if the attribute is already set, the operation will not fail.
+
+### Example - A symmetric key
+
+Set an attribute of a symmetric key by its unique identifier `027cced1-ff2b-4bd3-a200-db1041583bdc`.
+
+Corresponding `ckms` CLI command:
+
+```bash
+  ckms set-attributes -i 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1 --activation-date 1726211157791
+```
+
+The request sets the activation date of a symmetric key.
+
+The response contains all the system and user tags associated with the key. This is the hex encoded value of a JSON
+array with value
+
+```json
+Attribute set successfully
+          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
+```
+
+=== "Request"
+
+    ```json
+        {
+          "tag": "SetAttribute",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "10a875bd-9cc5-45a3-99ef-b2cdedd848bf"
+            },
+            {
+              "tag": "NewAttribute",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "Link",
+                  "type": "Structure",
+                  "value": [
+                    {
+                      "tag": "LinkType",
+                      "type": "Enumeration",
+                      "value": "CertificateLink"
+                    },
+                    {
+                      "tag": "LinkedObjectIdentifier",
+                      "type": "TextString",
+                      "value": "certificate_id"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        }
+    ```
+
+=== "Response"
+
+    ```json
+        {
+          "tag": "SetAttributeResponse",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1"
+            }
+          ]
+        }
+```
+
+### Example - Set links on Certificate
+
+Set the links on a certificate object. It will set 2 KMIP attributes.
+
+Corresponding `ckms` CLI command:
+
+```bash
+  ckms set-attributes -i 03948573-9348-aaaa-aaaa-93857383 --public-key-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz --private-key-id xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz
+```
+
+The request set the KMIP links of the public key and the private key of the underlying certificate.
+The `SetAttribute` operation being unitary, 2 requests are sent to the server, one for each link.
+
+Output is:
+
+```json
+Attribute set successfully
+          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
+[
+  {
+    "LinkType": "PublicKeyLink",
+    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+  }
+]
+Attribute set successfully
+          Unique identifier: 6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1
+[
+  {
+    "LinkType": "PrivateKeyLink",
+    "LinkedObjectIdentifier": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+  }
+]
+```
+
+=== "First Request"
+
+    ```json
+        {
+          "tag": "SetAttribute",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "10a875bd-9cc5-45a3-99ef-b2cdedd848bf"
+            },
+            {
+              "tag": "NewAttribute",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "Link",
+                  "type": "Structure",
+                  "value": [
+                    {
+                      "tag": "LinkType",
+                      "type": "Enumeration",
+                      "value": "PublicKeyLink"
+                    },
+                    {
+                      "tag": "LinkedObjectIdentifier",
+                      "type": "TextString",
+                      "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        }
+    ```
+
+=== "Second Request"
+
+    ```json
+        {
+          "tag": "SetAttribute",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "10a875bd-9cc5-45a3-99ef-b2cdedd848bf"
+            },
+            {
+              "tag": "NewAttribute",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "Link",
+                  "type": "Structure",
+                  "value": [
+                    {
+                      "tag": "LinkType",
+                      "type": "Enumeration",
+                      "value": "PrivateKeyLink"
+                    },
+                    {
+                      "tag": "LinkedObjectIdentifier",
+                      "type": "TextString",
+                      "value": "xxxxxxxx-yyyy-yyyy-yyyy-zzzzzzzzzzzz"
+                    }
+                  ]
+                }
+              ]
+            }
+          ]
+        }
+    ```
+
+
+=== "Response"
+
+    ```json
+        {
+          "tag": "SetAttributeResponse",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "UniqueIdentifier",
+              "type": "TextString",
+              "value": "6209aa2a-900f-4a1c-b2ca-9b4af1bbd1d1"
+            }
+          ]
+        }
+```
diff --git a/documentation/mkdocs.yml b/documentation/mkdocs.yml
index a03c73e96..6966156f4 100644
--- a/documentation/mkdocs.yml
+++ b/documentation/mkdocs.yml
@@ -80,6 +80,8 @@ nav:
       - Export: kmip_2_1/_export.md
       - Get: kmip_2_1/_get.md
       - Get Attributes: kmip_2_1/_get_attributes.md
+      - Set Attributes: kmip_2_1/_set_attributes.md
+      - Delete Attributes: kmip_2_1/_delete_attributes.md
       - Import: kmip_2_1/_import.md
       - Locate: kmip_2_1/_locate.md
       - Re-Key: kmip_2_1/_re-key.md


Test patch:
diff --git a/crate/cli/src/tests/access.rs b/crate/cli/src/tests/access.rs
index ce510e462..ba9cf71bf 100644
--- a/crate/cli/src/tests/access.rs
+++ b/crate/cli/src/tests/access.rs
@@ -3,6 +3,7 @@ use std::process::Command;
 use assert_cmd::prelude::*;
 use cosmian_kms_client::KMS_CLI_CONF_ENV;
 use kms_test_server::start_default_test_kms_server_with_cert_auth;
+use tracing::trace;
 
 use super::{symmetric::create_key::create_symmetric_key, utils::recover_cmd_logs};
 use crate::{
@@ -387,7 +388,7 @@ pub(crate) async fn test_list_access_rights() -> CliResult<()> {
 
     // the owner can list access rights granted
     let owner_list = list_access(&ctx.owner_client_conf_path, &key_id)?;
-    print!("owner list {owner_list}");
+    trace!("owner list {owner_list}");
     assert!(owner_list.contains("user.client@acme.com: {get}"));
 
     // The user is not the owner and thus should not be able to list accesses on this object
@@ -458,7 +459,7 @@ pub(crate) async fn test_access_right_obtained() -> CliResult<()> {
 
     // the user should have the "get" access granted
     let list = list_accesses_rights_obtained(&ctx.user_client_conf_path)?;
-    println!("user list {list}");
+    trace!("user list {list}");
     assert!(list.contains(&key_id));
     assert!(list.contains("get"));
 
@@ -593,7 +594,7 @@ pub(crate) async fn test_access_right_obtained_using_wildcard() -> CliResult<()>
 
     // the user should have the "get" access granted
     let list = list_accesses_rights_obtained(&ctx.user_client_conf_path)?;
-    println!("user list {list}");
+    trace!("user list {list}");
     assert!(list.contains(&key_id));
     assert!(list.contains("get"));
     assert!(list.contains("encrypt"));
diff --git a/crate/cli/src/tests/auth_tests.rs b/crate/cli/src/tests/auth_tests.rs
index 1e2d8db3c..097c461d5 100644
--- a/crate/cli/src/tests/auth_tests.rs
+++ b/crate/cli/src/tests/auth_tests.rs
@@ -62,7 +62,7 @@ pub(crate) async fn test_all_authentications() -> CliResult<()> {
     // plaintext no auth
     let ctx = start_test_server_with_options(
         DBConfig {
-            database_type: Some("sqlite".to_string()),
+            database_type: Some("sqlite".to_owned()),
             sqlite_path: PathBuf::from("./sqlite-data-auth-tests"),
             clear_database: true,
             ..DBConfig::default()
@@ -83,7 +83,7 @@ pub(crate) async fn test_all_authentications() -> CliResult<()> {
     ctx.stop_server().await?;
 
     let default_db_config = DBConfig {
-        database_type: Some("sqlite".to_string()),
+        database_type: Some("sqlite".to_owned()),
         sqlite_path: PathBuf::from("./sqlite-data-auth-tests"),
         clear_database: false,
         ..DBConfig::default()
@@ -145,8 +145,8 @@ pub(crate) async fn test_all_authentications() -> CliResult<()> {
             use_jwt_token: true,
             use_https: true,
             use_client_cert: false,
-            api_token_id: Some("my_bad_token_id".to_string()),
-            api_token: Some("my_bad_token".to_string()),
+            api_token_id: Some("my_bad_token_id".to_owned()),
+            api_token: Some("my_bad_token".to_owned()),
         },
     )
     .await?;
@@ -177,8 +177,8 @@ pub(crate) async fn test_all_authentications() -> CliResult<()> {
             use_jwt_token: false,
             use_https: true,
             use_client_cert: true,
-            api_token_id: Some("my_bad_token_id".to_string()),
-            api_token: Some("my_bad_token".to_string()),
+            api_token_id: Some("my_bad_token_id".to_owned()),
+            api_token: Some("my_bad_token".to_owned()),
         },
     )
     .await?;
@@ -193,8 +193,8 @@ pub(crate) async fn test_all_authentications() -> CliResult<()> {
             use_jwt_token: true,
             use_https: true,
             use_client_cert: true,
-            api_token_id: Some("my_bad_token_id".to_string()),
-            api_token: Some("my_bad_token".to_string()),
+            api_token_id: Some("my_bad_token_id".to_owned()),
+            api_token: Some("my_bad_token".to_owned()),
         },
     )
     .await?;
diff --git a/crate/cli/src/tests/certificates/certify.rs b/crate/cli/src/tests/certificates/certify.rs
index 72ad3b124..f2d4486cf 100644
--- a/crate/cli/src/tests/certificates/certify.rs
+++ b/crate/cli/src/tests/certificates/certify.rs
@@ -476,7 +476,7 @@ async fn test_certify_a_public_key_test_without_extensions() -> CliResult<()> {
             public_key_id_to_certify: Some(public_key_id),
             issuer_private_key_id: Some(issuer_private_key_id.clone()),
             subject_name: Some(
-                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_string(),
+                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_owned(),
             ),
             ..Default::default()
         },
@@ -519,7 +519,7 @@ async fn test_certify_a_public_key_test_with_extensions() -> CliResult<()> {
             public_key_id_to_certify: Some(public_key_id),
             issuer_private_key_id: Some(issuer_private_key_id.clone()),
             subject_name: Some(
-                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_string(),
+                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_owned(),
             ),
             certificate_extensions: Some(PathBuf::from("test_data/certificates/openssl/ext.cnf")),
             ..Default::default()
@@ -618,7 +618,7 @@ async fn test_certify_issue_with_subject_name() -> CliResult<()> {
             generate_keypair: true,
             algorithm: Some(Algorithm::NistP256),
             subject_name: Some(
-                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_string(),
+                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_owned(),
             ),
             issuer_private_key_id: Some(issuer_private_key_id.clone()),
             tags: Some(vec!["certify_a_csr_test".to_owned()]),
@@ -661,7 +661,7 @@ async fn test_certify_a_public_key_test_self_signed() -> CliResult<()> {
         CertifyOp {
             public_key_id_to_certify: Some(public_key_id),
             subject_name: Some(
-                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_string(),
+                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_owned(),
             ),
             ..Default::default()
         },
@@ -694,7 +694,7 @@ pub(crate) async fn create_self_signed_cert(ctx: &TestsContext) -> CliResult<Str
         CertifyOp {
             public_key_id_to_certify: Some(public_key_id),
             subject_name: Some(
-                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_string(),
+                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_owned(),
             ),
             ..Default::default()
         },
@@ -740,7 +740,7 @@ async fn test_certify_issue_with_subject_name_self_signed_with_extensions() -> C
             generate_keypair: true,
             algorithm: Some(Algorithm::NistP256),
             subject_name: Some(
-                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_string(),
+                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_owned(),
             ),
             tags: Some(vec!["certify_self_signed".to_owned()]),
             certificate_extensions: Some(PathBuf::from(
diff --git a/crate/cli/src/tests/certificates/encrypt.rs b/crate/cli/src/tests/certificates/encrypt.rs
index 8581ad43f..8cf39c258 100644
--- a/crate/cli/src/tests/certificates/encrypt.rs
+++ b/crate/cli/src/tests/certificates/encrypt.rs
@@ -4,7 +4,7 @@ use assert_cmd::prelude::*;
 use cosmian_kms_client::{read_bytes_from_file, KMS_CLI_CONF_ENV};
 use kms_test_server::start_default_test_kms_server;
 use tempfile::TempDir;
-use tracing::debug;
+use tracing::{debug, trace};
 use uuid::Uuid;
 
 use super::SUB_COMMAND;
@@ -259,7 +259,7 @@ async fn import_encrypt_decrypt(filename: &str) -> CliResult<()> {
         ..Default::default()
     })?;
 
-    println!("import private key with unwrap");
+    trace!("import private key with unwrap");
     debug!("\n\nImport a wrapped Private key but unwrap it into server");
     import_key(ImportKeyParams {
         cli_conf_path: ctx.owner_client_conf_path.clone(),
@@ -273,7 +273,7 @@ async fn import_encrypt_decrypt(filename: &str) -> CliResult<()> {
         replace_existing: true,
         authenticated_additional_data: None,
     })?;
-    println!("import private key with unwrap OK");
+    trace!("import private key with unwrap OK");
 
     debug!("\n\nImport a wrapped Private key but let is save it `as registered` into server");
     let wrapped_private_key_uid = import_key(ImportKeyParams {
diff --git a/crate/cli/src/tests/certificates/get_attributes.rs b/crate/cli/src/tests/certificates/get_attributes.rs
index d6734abd1..1282d4251 100644
--- a/crate/cli/src/tests/certificates/get_attributes.rs
+++ b/crate/cli/src/tests/certificates/get_attributes.rs
@@ -1,7 +1,10 @@
+use cosmian_kms_client::kmip::kmip_types::{LinkType, Tag};
 use kms_test_server::start_default_test_kms_server;
+use tracing::debug;
 
 use crate::{
-    actions::{certificates::CertificateInputFormat, shared::AttributeTag},
+    actions::certificates::CertificateInputFormat,
+    error::result::CliResult,
     tests::{
         certificates::import::{import_certificate, ImportCertificateInput},
         shared::get_attributes,
@@ -9,7 +12,7 @@ use crate::{
 };
 
 #[tokio::test]
-async fn test_get_attributes_p12() {
+async fn test_get_attributes_p12() -> CliResult<()> {
     // Create a test server
     let ctx = start_default_test_kms_server().await;
 
@@ -24,51 +27,54 @@ async fn test_get_attributes_p12() {
         tags: Some(&["import_pkcs12"]),
         replace_existing: true,
         ..Default::default()
-    })
-    .unwrap();
+    })?;
 
     //get the attributes of the private key and check that they are correct
-    let attributes = get_attributes(
+    let pkcs12_attributes = get_attributes(
         &ctx.owner_client_conf_path,
         &imported_p12_sk_uid,
-        &[
-            AttributeTag::KeyFormatType,
-            AttributeTag::LinkedPublicKeyId,
-            AttributeTag::LinkedCertificateId,
-        ],
-    )
-    .unwrap();
+        &[Tag::KeyFormatType, Tag::LinkType],
+        &[],
+    )?;
 
-    assert!(!attributes.contains_key(&AttributeTag::LinkedPublicKeyId));
+    debug!("test_get_attributes_p12: pkcs12_attributes: {pkcs12_attributes:?}");
+    assert!(!pkcs12_attributes.contains_key(&LinkType::PublicKeyLink.to_string()));
     assert_eq!(
-        attributes.get(&AttributeTag::KeyFormatType).unwrap(),
+        pkcs12_attributes
+            .get(&Tag::KeyFormatType.to_string())
+            .unwrap(),
         &serde_json::json!("PKCS1")
     );
-    let intermediate_certificate_id = attributes
-        .get(&AttributeTag::LinkedCertificateId)
-        .unwrap()
-        .as_str()
-        .unwrap();
+    let intermediate_certificate_id: String = serde_json::from_value(
+        pkcs12_attributes
+            .get(&LinkType::PKCS12CertificateLink.to_string())
+            .unwrap()
+            .clone(),
+    )?;
 
     //get the attributes of the certificate and check that they are correct
-    let attributes = get_attributes(
+    let intermediate_attributes = get_attributes(
         &ctx.owner_client_conf_path,
-        intermediate_certificate_id,
-        &[
-            AttributeTag::KeyFormatType,
-            AttributeTag::LinkedPrivateKeyId,
-            AttributeTag::LinkedIssuerCertificateId,
-        ],
-    )
-    .unwrap();
+        &intermediate_certificate_id,
+        &[Tag::KeyFormatType, Tag::LinkType],
+        &[],
+    )?;
+
+    debug!("test_get_attributes_p12: intermediate_attributes: {intermediate_attributes:?}");
 
     assert_eq!(
-        attributes.get(&AttributeTag::KeyFormatType).unwrap(),
+        intermediate_attributes
+            .get(&Tag::KeyFormatType.to_string())
+            .unwrap(),
         &serde_json::json!("X509")
     );
     assert_eq!(
-        attributes.get(&AttributeTag::LinkedPrivateKeyId).unwrap(),
+        intermediate_attributes
+            .get(&LinkType::PrivateKeyLink.to_string())
+            .unwrap(),
         &serde_json::json!(imported_p12_sk_uid)
     );
-    assert!(!attributes.contains_key(&AttributeTag::LinkedIssuerCertificateId));
+    assert!(!intermediate_attributes.contains_key(&LinkType::CertificateLink.to_string()));
+
+    Ok(())
 }
diff --git a/crate/cli/src/tests/certificates/mod.rs b/crate/cli/src/tests/certificates/mod.rs
index 8bf6dcac2..f585657c8 100644
--- a/crate/cli/src/tests/certificates/mod.rs
+++ b/crate/cli/src/tests/certificates/mod.rs
@@ -1,4 +1,4 @@
-mod certify;
+pub(crate) mod certify;
 pub(crate) mod encrypt;
 mod export;
 mod get_attributes;
diff --git a/crate/cli/src/tests/certificates/validate.rs b/crate/cli/src/tests/certificates/validate.rs
index 10d2391c2..8ea0de08a 100644
--- a/crate/cli/src/tests/certificates/validate.rs
+++ b/crate/cli/src/tests/certificates/validate.rs
@@ -169,7 +169,7 @@ async fn test_validate_cli() -> CliResult<()> {
     let test2_res = validate_certificate(
         &ctx.owner_client_conf_path,
         "certificates",
-        vec!["test_data/certificates/chain/leaf2.cert.der".to_string()],
+        vec!["test_data/certificates/chain/leaf2.cert.der".to_owned()],
         vec![
             intermediate_certificate_id.clone(),
             root_certificate_id.clone(),
@@ -185,10 +185,10 @@ async fn test_validate_cli() -> CliResult<()> {
     let test3_res = validate_certificate(
         &ctx.owner_client_conf_path,
         "certificates",
-        vec!["test_data/certificates/chain/leaf2.cert.der".to_string()],
+        vec!["test_data/certificates/chain/leaf2.cert.der".to_owned()],
         vec![intermediate_certificate_id, root_certificate_id.clone()],
         // Date: 15/04/2048
-        Some("4804152030Z".to_string()),
+        Some("4804152030Z".to_owned()),
     );
     info!(
         "validate chain with leaf2: result supposed to be invalid, as date is posthumous to \
diff --git a/crate/cli/src/tests/google_cmd/identities.rs b/crate/cli/src/tests/google_cmd/identities.rs
index ec8886a18..c81703d5a 100644
--- a/crate/cli/src/tests/google_cmd/identities.rs
+++ b/crate/cli/src/tests/google_cmd/identities.rs
@@ -5,6 +5,7 @@ use cosmian_kms_client::{write_json_object_to_file, GmailApiConf, KMS_CLI_CONF_E
 use kms_test_server::{start_default_test_kms_server, TestsContext};
 use serde::Deserialize;
 use tempfile::TempDir;
+use tracing::trace;
 
 use crate::{
     error::CliError,
@@ -161,7 +162,7 @@ async fn test_google_identities() -> Result<(), CliError> {
     let owner_client_conf_path = create_gmail_api_conf(ctx)?;
     // Read the content of the owner client conf file
     let conf_content = std::fs::read_to_string(&owner_client_conf_path)?;
-    println!("{conf_content}");
+    trace!("{conf_content}");
 
     // Fetch and list identities and compare them
     let listed_identities = list_identities(&owner_client_conf_path, user_id)?;
diff --git a/crate/cli/src/tests/new_database.rs b/crate/cli/src/tests/new_database.rs
index 3d7fecdd3..7f6d2ae5a 100644
--- a/crate/cli/src/tests/new_database.rs
+++ b/crate/cli/src/tests/new_database.rs
@@ -122,7 +122,7 @@ async fn test_multiple_databases() -> CliResult<()> {
     // since we are going to rewrite the conf, use a different port
     let ctx = start_test_server_with_options(
         DBConfig {
-            database_type: Some("sqlite-enc".to_string()),
+            database_type: Some("sqlite-enc".to_owned()),
             sqlite_path: PathBuf::from(DEFAULT_SQLITE_PATH),
             clear_database: true,
             ..DBConfig::default()
diff --git a/crate/cli/src/tests/rsa/encrypt_decrypt.rs b/crate/cli/src/tests/rsa/encrypt_decrypt.rs
index f60a2bad4..88e94b707 100644
--- a/crate/cli/src/tests/rsa/encrypt_decrypt.rs
+++ b/crate/cli/src/tests/rsa/encrypt_decrypt.rs
@@ -5,6 +5,7 @@ use cosmian_kms_client::{read_bytes_from_file, KMS_CLI_CONF_ENV};
 use kms_test_server::start_default_test_kms_server;
 use predicates::prelude::*;
 use tempfile::TempDir;
+use tracing::trace;
 
 use super::SUB_COMMAND;
 use crate::{
@@ -104,6 +105,8 @@ async fn test_rsa_encrypt_decrypt_using_ckm_rsa_pkcs() -> CliResult<()> {
     //     "cosmian_kms_cli=trace,cosmian_kms_server=info,cosmian_kms_server::core::operations=trace,\
     //      cosmian_kms_utils=trace,cosmian_kmip=info",
     // );
+
+    use tracing::trace;
     let ctx = start_default_test_kms_server().await;
 
     // create a temp dir
@@ -120,8 +123,8 @@ async fn test_rsa_encrypt_decrypt_using_ckm_rsa_pkcs() -> CliResult<()> {
     let (private_key_id, public_key_id) =
         create_rsa_4096_bits_key_pair(&ctx.owner_client_conf_path, &[])?;
 
-    println!("private_key_id: {private_key_id}");
-    println!("public_key_id: {public_key_id}");
+    trace!("private_key_id: {private_key_id}");
+    trace!("public_key_id: {public_key_id}");
     encrypt(
         &ctx.owner_client_conf_path,
         &[input_file.to_str().unwrap()],
@@ -192,8 +195,8 @@ async fn test_rsa_encrypt_decrypt_using_ckm_rsa_pkcs_oaep() -> CliResult<()> {
     let (private_key_id, public_key_id) =
         create_rsa_4096_bits_key_pair(&ctx.owner_client_conf_path, &[])?;
 
-    println!("private_key_id: {private_key_id}");
-    println!("public_key_id: {public_key_id}");
+    trace!("private_key_id: {private_key_id}");
+    trace!("public_key_id: {public_key_id}");
     encrypt(
         &ctx.owner_client_conf_path,
         &[input_file.to_str().unwrap()],
@@ -276,8 +279,8 @@ async fn test_rsa_encrypt_decrypt_using_rsa_aes_key_wrap() -> CliResult<()> {
     let (private_key_id, public_key_id) =
         create_rsa_4096_bits_key_pair(&ctx.owner_client_conf_path, &[])?;
 
-    println!("private_key_id: {private_key_id}");
-    println!("public_key_id: {public_key_id}");
+    trace!("private_key_id: {private_key_id}");
+    trace!("public_key_id: {public_key_id}");
     encrypt(
         &ctx.owner_client_conf_path,
         &[input_file.to_str().unwrap()],
diff --git a/crate/cli/src/tests/shared/delete_attribute.rs b/crate/cli/src/tests/shared/delete_attribute.rs
new file mode 100644
index 000000000..37b5e4fde
--- /dev/null
+++ b/crate/cli/src/tests/shared/delete_attribute.rs
@@ -0,0 +1,42 @@
+use std::process::Command;
+
+use assert_cmd::cargo::CommandCargoExt;
+use cosmian_kms_client::{kmip::kmip_types::Tag, KMS_CLI_CONF_ENV};
+
+use super::set_attribute::prepare_attributes;
+use crate::{
+    actions::shared::CommonAttributes,
+    error::{result::CliResult, CliError},
+    tests::{utils::recover_cmd_logs, PROG_NAME},
+};
+
+pub(crate) fn delete_attributes(
+    cli_conf_path: &str,
+    common_attributes: &Option<CommonAttributes>,
+    attribute_references: Option<Vec<Tag>>,
+) -> CliResult<String> {
+    let mut args = common_attributes
+        .as_ref()
+        .map_or_else(Vec::<String>::new, |common_attributes| {
+            prepare_attributes(common_attributes)
+        });
+
+    if let Some(references) = attribute_references {
+        for reference in references {
+            args.push("--attribute".to_owned());
+            args.push(reference.to_string());
+        }
+    }
+    let mut cmd = Command::cargo_bin(PROG_NAME)?;
+    cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
+
+    cmd.arg("delete-attributes").args(args);
+    let output = recover_cmd_logs(&mut cmd);
+    if output.status.success() {
+        let set_attribute_output = std::str::from_utf8(&output.stdout)?;
+        return Ok(set_attribute_output.to_string())
+    }
+    Err(CliError::Default(
+        std::str::from_utf8(&output.stderr)?.to_owned(),
+    ))
+}
diff --git a/crate/cli/src/tests/shared/delete_attributes.rs b/crate/cli/src/tests/shared/delete_attributes.rs
new file mode 100644
index 000000000..810f98f28
--- /dev/null
+++ b/crate/cli/src/tests/shared/delete_attributes.rs
@@ -0,0 +1,42 @@
+use std::process::Command;
+
+use assert_cmd::cargo::CommandCargoExt;
+use cosmian_kms_client::{kmip::kmip_types::Tag, KMS_CLI_CONF_ENV};
+
+use super::set_attributes::prepare_attributes;
+use crate::{
+    actions::shared::SetOrDeleteAttributes,
+    error::{result::CliResult, CliError},
+    tests::{utils::recover_cmd_logs, PROG_NAME},
+};
+
+pub(crate) fn delete_attributes(
+    cli_conf_path: &str,
+    requested_attributes: &Option<SetOrDeleteAttributes>,
+    attribute_references: Option<Vec<Tag>>,
+) -> CliResult<String> {
+    let mut args = requested_attributes
+        .as_ref()
+        .map_or_else(Vec::<String>::new, |requested_attributes| {
+            prepare_attributes(requested_attributes)
+        });
+
+    if let Some(references) = attribute_references {
+        for reference in references {
+            args.push("--attribute".to_owned());
+            args.push(reference.to_string());
+        }
+    }
+    let mut cmd = Command::cargo_bin(PROG_NAME)?;
+    cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
+
+    cmd.arg("delete-attributes").args(args);
+    let output = recover_cmd_logs(&mut cmd);
+    if output.status.success() {
+        let set_attribute_output = std::str::from_utf8(&output.stdout)?;
+        return Ok(set_attribute_output.to_string())
+    }
+    Err(CliError::Default(
+        std::str::from_utf8(&output.stderr)?.to_owned(),
+    ))
+}
diff --git a/crate/cli/src/tests/shared/destroy.rs b/crate/cli/src/tests/shared/destroy.rs
index 6586804c0..f57bd07b1 100644
--- a/crate/cli/src/tests/shared/destroy.rs
+++ b/crate/cli/src/tests/shared/destroy.rs
@@ -4,6 +4,7 @@ use assert_cmd::prelude::CommandCargoExt;
 use cosmian_kms_client::{read_object_from_json_ttlv_file, KMS_CLI_CONF_ENV};
 use kms_test_server::start_default_test_kms_server;
 use tempfile::TempDir;
+use tracing::trace;
 
 #[cfg(not(feature = "fips"))]
 use crate::tests::cover_crypt::{
@@ -142,7 +143,7 @@ async fn test_destroy_ec_key() -> CliResult<()> {
         // destroy should not work when not revoked
         assert!(destroy(&ctx.owner_client_conf_path, "ec", &public_key_id).is_err());
 
-        println!("OK. revoking");
+        trace!("OK. revoking");
 
         // revoke then destroy
         revoke(
@@ -152,7 +153,7 @@ async fn test_destroy_ec_key() -> CliResult<()> {
             "revocation test",
         )?;
 
-        println!("OK. destroying");
+        trace!("OK. destroying");
 
         // destroy via the private key
         destroy(&ctx.owner_client_conf_path, "ec", &public_key_id)?;
@@ -302,7 +303,7 @@ async fn test_destroy_cover_crypt() -> CliResult<()> {
             export_key(ExportKeyParams {
                 cli_conf_path: ctx.owner_client_conf_path.clone(),
                 sub_command: "cc".to_owned(),
-                key_id: master_private_key_id.clone(),
+                key_id: master_private_key_id,
                 key_file: tmp_path.join("output.export").to_str().unwrap().to_owned(),
                 ..Default::default()
             })
@@ -312,7 +313,7 @@ async fn test_destroy_cover_crypt() -> CliResult<()> {
             export_key(ExportKeyParams {
                 cli_conf_path: ctx.owner_client_conf_path.clone(),
                 sub_command: "cc".to_owned(),
-                key_id: master_public_key_id.clone(),
+                key_id: master_public_key_id,
                 key_file: tmp_path.join("output.export").to_str().unwrap().to_owned(),
                 ..Default::default()
             })
@@ -322,7 +323,7 @@ async fn test_destroy_cover_crypt() -> CliResult<()> {
             export_key(ExportKeyParams {
                 cli_conf_path: ctx.owner_client_conf_path.clone(),
                 sub_command: "cc".to_owned(),
-                key_id: user_key_id_2.clone(),
+                key_id: user_key_id_2,
                 key_file: tmp_path.join("output.export").to_str().unwrap().to_owned(),
                 ..Default::default()
             })
diff --git a/crate/cli/src/tests/shared/export.rs b/crate/cli/src/tests/shared/export.rs
index 0ce2abf27..4046a6ae8 100644
--- a/crate/cli/src/tests/shared/export.rs
+++ b/crate/cli/src/tests/shared/export.rs
@@ -433,7 +433,7 @@ pub(crate) async fn test_export_error_cover_crypt() -> CliResult<()> {
     export_key(ExportKeyParams {
         cli_conf_path: ctx.owner_client_conf_path.clone(),
         sub_command: "cc".to_owned(),
-        key_id: master_private_key_id.clone(),
+        key_id: master_private_key_id,
         key_file: "/does_not_exist/output.export".to_owned(),
         ..Default::default()
     })
@@ -447,6 +447,8 @@ pub(crate) async fn test_export_error_cover_crypt() -> CliResult<()> {
 #[tokio::test]
 pub(crate) async fn test_export_x25519() -> CliResult<()> {
     // create a temp dir
+
+    use tracing::trace;
     let tmp_dir = TempDir::new()?;
     let tmp_path = tmp_dir.path();
     // init the test server
@@ -490,14 +492,14 @@ pub(crate) async fn test_export_x25519() -> CliResult<()> {
     let mut d_vec = d.to_bytes_be();
     // 32 is privkey size on x25519.
     pad_be_bytes(&mut d_vec, 32);
-    println!("d_vec size is {:?}", d_vec.len());
+    trace!("d_vec size is {:?}", d_vec.len());
     let pkey_1 = PKey::private_key_from_raw_bytes(&d_vec, Id::X25519).unwrap();
 
     // Export the bytes only
     export_key(ExportKeyParams {
         cli_conf_path: ctx.owner_client_conf_path.clone(),
         sub_command: "ec".to_owned(),
-        key_id: private_key_id.clone(),
+        key_id: private_key_id,
         key_file: tmp_path
             .join("output.export.bytes")
             .to_str()
@@ -551,7 +553,7 @@ pub(crate) async fn test_export_x25519() -> CliResult<()> {
     export_key(ExportKeyParams {
         cli_conf_path: ctx.owner_client_conf_path.clone(),
         sub_command: "ec".to_owned(),
-        key_id: public_key_id.clone(),
+        key_id: public_key_id,
         key_file: tmp_path
             .join("output.export.bytes")
             .to_str()
diff --git a/crate/cli/src/tests/shared/get_attributes.rs b/crate/cli/src/tests/shared/get_attributes.rs
index 26e0364ea..1bc8f3387 100644
--- a/crate/cli/src/tests/shared/get_attributes.rs
+++ b/crate/cli/src/tests/shared/get_attributes.rs
@@ -1,11 +1,13 @@
 use std::{collections::HashMap, process::Command};
 
 use assert_cmd::cargo::CommandCargoExt;
-use cosmian_kms_client::KMS_CLI_CONF_ENV;
+use cosmian_kms_client::{
+    kmip::kmip_types::{LinkType, Tag},
+    KMS_CLI_CONF_ENV,
+};
 use serde_json::Value;
 
 use crate::{
-    actions::shared::AttributeTag,
     error::{
         result::{CliResult, CliResultHelper},
         CliError,
@@ -16,8 +18,9 @@ use crate::{
 pub(crate) fn get_attributes(
     cli_conf_path: &str,
     uid: &str,
-    attribute_tags: &[AttributeTag],
-) -> CliResult<HashMap<AttributeTag, Value>> {
+    attribute_tags: &[Tag],
+    attribute_link_types: &[LinkType],
+) -> CliResult<HashMap<String, Value>> {
     let temp_file = tempfile::NamedTempFile::new()?;
     let mut args: Vec<String> = [
         "--id",
@@ -34,21 +37,12 @@ pub(crate) fn get_attributes(
 
     for tag in attribute_tags {
         args.push("--attribute".to_owned());
-        let arg_value = match tag {
-            AttributeTag::ActivationDate => "activation-date",
-            AttributeTag::CryptographicAlgorithm => "cryptographic-algorithm",
-            AttributeTag::CryptographicLength => "cryptographic-length",
-            AttributeTag::CryptographicParameters => "cryptographic-parameters",
-            AttributeTag::CryptographicUsageMask => "cryptographic-usage-mask",
-            AttributeTag::KeyFormatType => "key-format-type",
-            AttributeTag::LinkedPrivateKeyId => "linked-private-key-id",
-            AttributeTag::LinkedPublicKeyId => "linked-public-key-id",
-            AttributeTag::LinkedIssuerCertificateId => "linked-issuer-certificate-id",
-            AttributeTag::LinkedCertificateId => "linked-certificate-id",
-            AttributeTag::CryptographicDomainParameters => "cryptographic-domain-parameters",
-            AttributeTag::Tags => "tags",
-        };
-        args.push(arg_value.to_owned());
+        args.push(tag.to_string());
+    }
+
+    for link_type in attribute_link_types {
+        args.push("--link-type".to_owned());
+        args.push(link_type.to_string());
     }
 
     let mut cmd = Command::cargo_bin(PROG_NAME)?;
@@ -59,26 +53,7 @@ pub(crate) fn get_attributes(
     if output.status.success() {
         let output = std::fs::read_to_string(temp_file.path())?;
         let output: HashMap<String, Value> = serde_json::from_str(&output)?;
-        let mut result = HashMap::with_capacity(output.len());
-        for (k, v) in output {
-            let tag = match k.as_str() {
-                "activation-date" => AttributeTag::ActivationDate,
-                "cryptographic-algorithm" => AttributeTag::CryptographicAlgorithm,
-                "cryptographic-length" => AttributeTag::CryptographicLength,
-                "cryptographic-parameters" => AttributeTag::CryptographicParameters,
-                "cryptographic-usage-mask" => AttributeTag::CryptographicUsageMask,
-                "key-format-type" => AttributeTag::KeyFormatType,
-                "linked-private-key-id" => AttributeTag::LinkedPrivateKeyId,
-                "linked-public-key-id" => AttributeTag::LinkedPublicKeyId,
-                "linked-issuer-certificate-id" => AttributeTag::LinkedIssuerCertificateId,
-                "linked-certificate-id" => AttributeTag::LinkedCertificateId,
-                "cryptographic-domain-parameters" => AttributeTag::CryptographicDomainParameters,
-                "tags" => AttributeTag::Tags,
-                _ => return Err(CliError::Default(format!("unknown attribute tag: {k}"))),
-            };
-            result.insert(tag, v);
-        }
-        return Ok(result)
+        return Ok(output)
     }
     Err(CliError::Default(
         std::str::from_utf8(&output.stderr)?.to_owned(),
diff --git a/crate/cli/src/tests/shared/import_export_wrapping.rs b/crate/cli/src/tests/shared/import_export_wrapping.rs
index 2866071e0..6aa7b898d 100644
--- a/crate/cli/src/tests/shared/import_export_wrapping.rs
+++ b/crate/cli/src/tests/shared/import_export_wrapping.rs
@@ -20,7 +20,7 @@ use cosmian_kms_client::{
 };
 use kms_test_server::start_default_test_kms_server;
 use tempfile::TempDir;
-use tracing::debug;
+use tracing::{debug, trace};
 
 use super::ExportKeyParams;
 use crate::{
@@ -46,11 +46,11 @@ pub(crate) async fn test_import_export_wrap_rfc_5649() -> CliResult<()> {
     let mut rng = CsRng::from_entropy();
     let mut wrap_key_bytes = vec![0; 32];
     rng.fill_bytes(&mut wrap_key_bytes);
-    let wrap_key = create_symmetric_key_kmip_object(&wrap_key_bytes, CryptographicAlgorithm::AES);
+    let wrap_key = create_symmetric_key_kmip_object(&wrap_key_bytes, CryptographicAlgorithm::AES)?;
     write_kmip_object_to_file(&wrap_key, &wrap_key_path)?;
 
     // import the wrapping key
-    println!("importing wrapping key");
+    trace!("importing wrapping key");
     let wrap_key_uid = import_key(ImportKeyParams {
         cli_conf_path: ctx.owner_client_conf_path.clone(),
         sub_command: "sym".to_string(),
@@ -276,7 +276,7 @@ fn test_import_export_wrap_private_key(
         export_key(ExportKeyParams {
             cli_conf_path: cli_conf_path.to_string(),
             sub_command: sub_command.to_string(),
-            key_id: unwrapped_key_id.to_string(),
+            key_id: unwrapped_key_id,
             key_file: re_exported_key_file.to_str().unwrap().to_string(),
             ..Default::default()
         })?;
@@ -314,7 +314,7 @@ fn test_import_export_wrap_private_key(
         export_key(ExportKeyParams {
             cli_conf_path: cli_conf_path.to_string(),
             sub_command: sub_command.to_string(),
-            key_id: wrapped_key_id.to_string(),
+            key_id: wrapped_key_id,
             key_file: exported_unwrapped_key_file.to_str().unwrap().to_string(),
             unwrap: true,
             ..Default::default()
diff --git a/crate/cli/src/tests/shared/locate.rs b/crate/cli/src/tests/shared/locate.rs
index 960ecc4b9..ebb40e05e 100644
--- a/crate/cli/src/tests/shared/locate.rs
+++ b/crate/cli/src/tests/shared/locate.rs
@@ -67,6 +67,8 @@ pub(crate) fn locate(
 #[tokio::test]
 pub(crate) async fn test_locate_cover_crypt() -> CliResult<()> {
     // init the test server
+
+    use tracing::trace;
     let ctx = start_default_test_kms_server_with_cert_auth().await;
 
     // generate a new master key pair
@@ -77,8 +79,8 @@ pub(crate) async fn test_locate_cover_crypt() -> CliResult<()> {
         &["test_cc"],
     )?;
 
-    println!("master_private_key_id: {master_private_key_id}");
-    println!("master_public_key_id: {master_public_key_id}");
+    trace!("master_private_key_id: {master_private_key_id}");
+    trace!("master_public_key_id: {master_public_key_id}");
 
     // Locate with Tags
     let ids = locate(
@@ -88,7 +90,7 @@ pub(crate) async fn test_locate_cover_crypt() -> CliResult<()> {
         None,
         None,
     )?;
-    println!("ids: {ids:?}");
+    trace!("ids: {ids:?}");
     assert_eq!(ids.len(), 2);
     assert!(ids.contains(&master_private_key_id));
     assert!(ids.contains(&master_public_key_id));
diff --git a/crate/cli/src/tests/shared/mod.rs b/crate/cli/src/tests/shared/mod.rs
index ad2be82e6..9f6efe97b 100644
--- a/crate/cli/src/tests/shared/mod.rs
+++ b/crate/cli/src/tests/shared/mod.rs
@@ -1,9 +1,12 @@
+pub(crate) use delete_attributes::delete_attributes;
 pub(crate) use destroy::destroy;
 pub(crate) use export::{export_key, ExportKeyParams};
 pub(crate) use get_attributes::get_attributes;
 pub(crate) use import::{import_key, ImportKeyParams};
 pub(crate) use revoke::revoke;
+pub(crate) use set_attributes::set_attributes;
 
+mod delete_attributes;
 mod destroy;
 mod export;
 mod export_import;
@@ -14,5 +17,6 @@ mod import_export_encodings;
 mod import_export_wrapping;
 mod locate;
 mod revoke;
+mod set_attributes;
 #[cfg(all(not(feature = "fips"), feature = "openssl"))]
 mod wrap_unwrap;
diff --git a/crate/cli/src/tests/shared/revoke.rs b/crate/cli/src/tests/shared/revoke.rs
index 9845a752b..95e963adb 100644
--- a/crate/cli/src/tests/shared/revoke.rs
+++ b/crate/cli/src/tests/shared/revoke.rs
@@ -263,7 +263,7 @@ async fn test_revoke_cover_crypt() -> CliResult<()> {
             export_key(ExportKeyParams {
                 cli_conf_path: ctx.owner_client_conf_path.to_string(),
                 sub_command: "cc".to_owned(),
-                key_id: master_private_key_id.to_string(),
+                key_id: master_private_key_id,
                 key_file: tmp_path.join("output.export").to_str().unwrap().to_string(),
                 ..Default::default()
             })
@@ -273,7 +273,7 @@ async fn test_revoke_cover_crypt() -> CliResult<()> {
             export_key(ExportKeyParams {
                 cli_conf_path: ctx.owner_client_conf_path.to_string(),
                 sub_command: "cc".to_owned(),
-                key_id: master_public_key_id.to_string(),
+                key_id: master_public_key_id,
                 key_file: tmp_path.join("output.export").to_str().unwrap().to_string(),
                 ..Default::default()
             })
@@ -283,7 +283,7 @@ async fn test_revoke_cover_crypt() -> CliResult<()> {
             export_key(ExportKeyParams {
                 cli_conf_path: ctx.owner_client_conf_path.to_string(),
                 sub_command: "cc".to_owned(),
-                key_id: user_key_id_2.to_string(),
+                key_id: user_key_id_2,
                 key_file: tmp_path.join("output.export").to_str().unwrap().to_string(),
                 ..Default::default()
             })
diff --git a/crate/cli/src/tests/shared/set_attribute.rs b/crate/cli/src/tests/shared/set_attribute.rs
new file mode 100644
index 000000000..d5c0e2948
--- /dev/null
+++ b/crate/cli/src/tests/shared/set_attribute.rs
@@ -0,0 +1,123 @@
+use std::process::Command;
+
+use assert_cmd::cargo::CommandCargoExt;
+use cosmian_kms_client::KMS_CLI_CONF_ENV;
+
+use crate::{
+    actions::shared::CommonAttributes,
+    error::{result::CliResult, CliError},
+    tests::{utils::recover_cmd_logs, PROG_NAME},
+};
+
+pub(crate) fn prepare_attributes(common_attributes: &CommonAttributes) -> Vec<std::string::String> {
+    let mut args = Vec::<String>::new();
+
+    if let Some(id) = &common_attributes.id {
+        args.push("--id".to_owned());
+        args.push(id.clone());
+    }
+    if let Some(date) = common_attributes.activation_date {
+        args.push("--activation-date".to_owned());
+        args.push(date.to_string());
+    }
+
+    if let Some(cryptographic_algorithm) = common_attributes.cryptographic_algorithm {
+        args.push("--cryptographic-algorithm".to_owned());
+        args.push(cryptographic_algorithm.to_string());
+    }
+    if let Some(cryptographic_length) = common_attributes.cryptographic_length {
+        args.push("--cryptographic-length".to_owned());
+        args.push(cryptographic_length.to_string());
+    }
+
+    if let Some(key_usage_vec) = &common_attributes.key_usage {
+        for usage in key_usage_vec {
+            args.push("--key-usage".to_owned());
+            args.push(usage.clone().into());
+        }
+    }
+
+    if let Some(public_key_id) = &common_attributes.public_key_id {
+        args.push("--public-key-id".to_owned());
+        args.push(public_key_id.clone());
+    }
+
+    if let Some(private_key_id) = &common_attributes.private_key_id {
+        args.push("--private-key-id".to_owned());
+        args.push(private_key_id.clone());
+    }
+
+    if let Some(certificate_id) = &common_attributes.certificate_id {
+        args.push("--certificate-id".to_owned());
+        args.push(certificate_id.clone());
+    }
+
+    if let Some(pkcs12_certificate_id) = &common_attributes.pkcs12_certificate_id {
+        args.push("--pkcs12-certificate-id".to_owned());
+        args.push(pkcs12_certificate_id.clone());
+    }
+
+    if let Some(pkcs12_password_certificate) = &common_attributes.pkcs12_password_certificate {
+        args.push("--pkcs12-password-certificate".to_owned());
+        args.push(pkcs12_password_certificate.clone());
+    }
+
+    if let Some(parent_id) = &common_attributes.parent_id {
+        args.push("--parent-id".to_owned());
+        args.push(parent_id.clone());
+    }
+
+    if let Some(child_id) = &common_attributes.child_id {
+        args.push("--child-id".to_owned());
+        args.push(child_id.clone());
+    }
+
+    if let Some(state) = common_attributes.state {
+        args.push("--state".to_owned());
+        args.push(state.to_string());
+    }
+
+    if let Some(tags) = &common_attributes.tags {
+        for tag in tags {
+            args.push("--tag".to_owned());
+            args.push(tag.clone());
+        }
+    }
+
+    if let Some(vendor_attributes) = &common_attributes.vendor_attributes {
+        if let Some(vendor_identification) = &vendor_attributes.vendor_identification {
+            args.push("--vendor-identification".to_owned());
+            args.push(vendor_identification.clone());
+        }
+        if let Some(attribute_name) = &vendor_attributes.attribute_name {
+            args.push("--attribute-name".to_owned());
+            args.push(attribute_name.clone());
+        }
+        if let Some(attribute_value) = &vendor_attributes.attribute_value {
+            args.push("--attribute-value".to_owned());
+            args.push(attribute_value.clone());
+        }
+    }
+
+    args
+}
+
+pub(crate) fn set_attributes(
+    cli_conf_path: &str,
+    common_attributes: &CommonAttributes,
+) -> CliResult<String> {
+    let args = prepare_attributes(common_attributes);
+
+    let mut cmd = Command::cargo_bin(PROG_NAME)?;
+    cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
+
+    cmd.arg("set-attributes").args(args);
+    let output = recover_cmd_logs(&mut cmd);
+    if output.status.success() {
+        let set_attribute_output = std::str::from_utf8(&output.stdout)?;
+        return Ok(set_attribute_output.to_string())
+    }
+    Err(CliError::Default(
+        std::str::from_utf8(&output.stderr)?.to_owned(),
+    ))
+}
diff --git a/crate/cli/src/tests/shared/set_attributes.rs b/crate/cli/src/tests/shared/set_attributes.rs
new file mode 100644
index 000000000..319d9a898
--- /dev/null
+++ b/crate/cli/src/tests/shared/set_attributes.rs
@@ -0,0 +1,125 @@
+use std::process::Command;
+
+use assert_cmd::cargo::CommandCargoExt;
+use cosmian_kms_client::KMS_CLI_CONF_ENV;
+
+use crate::{
+    actions::shared::SetOrDeleteAttributes,
+    error::{result::CliResult, CliError},
+    tests::{utils::recover_cmd_logs, PROG_NAME},
+};
+
+pub(crate) fn prepare_attributes(
+    requested_attributes: &SetOrDeleteAttributes,
+) -> Vec<std::string::String> {
+    let mut args = Vec::<String>::new();
+
+    if let Some(id) = &requested_attributes.id {
+        args.push("--id".to_owned());
+        args.push(id.clone());
+    }
+    if let Some(date) = requested_attributes.activation_date {
+        args.push("--activation-date".to_owned());
+        args.push(date.to_string());
+    }
+
+    if let Some(cryptographic_algorithm) = requested_attributes.cryptographic_algorithm {
+        args.push("--cryptographic-algorithm".to_owned());
+        args.push(cryptographic_algorithm.to_string());
+    }
+    if let Some(cryptographic_length) = requested_attributes.cryptographic_length {
+        args.push("--cryptographic-length".to_owned());
+        args.push(cryptographic_length.to_string());
+    }
+
+    if let Some(key_usage_vec) = &requested_attributes.key_usage {
+        for usage in key_usage_vec {
+            args.push("--key-usage".to_owned());
+            args.push(usage.clone().into());
+        }
+    }
+
+    if let Some(public_key_id) = &requested_attributes.public_key_id {
+        args.push("--public-key-id".to_owned());
+        args.push(public_key_id.clone());
+    }
+
+    if let Some(private_key_id) = &requested_attributes.private_key_id {
+        args.push("--private-key-id".to_owned());
+        args.push(private_key_id.clone());
+    }
+
+    if let Some(certificate_id) = &requested_attributes.certificate_id {
+        args.push("--certificate-id".to_owned());
+        args.push(certificate_id.clone());
+    }
+
+    if let Some(pkcs12_certificate_id) = &requested_attributes.pkcs12_certificate_id {
+        args.push("--p12-id".to_owned());
+        args.push(pkcs12_certificate_id.clone());
+    }
+
+    if let Some(pkcs12_password_certificate) = &requested_attributes.pkcs12_password_certificate {
+        args.push("--p12-pwd".to_owned());
+        args.push(pkcs12_password_certificate.clone());
+    }
+
+    if let Some(parent_id) = &requested_attributes.parent_id {
+        args.push("--parent-id".to_owned());
+        args.push(parent_id.clone());
+    }
+
+    if let Some(child_id) = &requested_attributes.child_id {
+        args.push("--child-id".to_owned());
+        args.push(child_id.clone());
+    }
+
+    if let Some(state) = requested_attributes.state {
+        args.push("--state".to_owned());
+        args.push(state.to_string());
+    }
+
+    if let Some(tags) = &requested_attributes.tags {
+        for tag in tags {
+            args.push("--tag".to_owned());
+            args.push(tag.clone());
+        }
+    }
+
+    if let Some(vendor_attributes) = &requested_attributes.vendor_attributes {
+        if let Some(vendor_identification) = &vendor_attributes.vendor_identification {
+            args.push("--vendor-identification".to_owned());
+            args.push(vendor_identification.clone());
+        }
+        if let Some(attribute_name) = &vendor_attributes.attribute_name {
+            args.push("--attribute-name".to_owned());
+            args.push(attribute_name.clone());
+        }
+        if let Some(attribute_value) = &vendor_attributes.attribute_value {
+            args.push("--attribute-value".to_owned());
+            args.push(attribute_value.clone());
+        }
+    }
+
+    args
+}
+
+pub(crate) fn set_attributes(
+    cli_conf_path: &str,
+    requested_attributes: &SetOrDeleteAttributes,
+) -> CliResult<String> {
+    let args = prepare_attributes(requested_attributes);
+
+    let mut cmd = Command::cargo_bin(PROG_NAME)?;
+    cmd.env(KMS_CLI_CONF_ENV, cli_conf_path);
+
+    cmd.arg("set-attributes").args(args);
+    let output = recover_cmd_logs(&mut cmd);
+    if output.status.success() {
+        let set_attribute_output = std::str::from_utf8(&output.stdout)?;
+        return Ok(set_attribute_output.to_string())
+    }
+    Err(CliError::Default(
+        std::str::from_utf8(&output.stderr)?.to_owned(),
+    ))
+}
diff --git a/crate/cli/src/tests/symmetric/mod.rs b/crate/cli/src/tests/symmetric/mod.rs
index 25fa964bf..2382aa720 100644
--- a/crate/cli/src/tests/symmetric/mod.rs
+++ b/crate/cli/src/tests/symmetric/mod.rs
@@ -1,5 +1,6 @@
 pub(crate) mod create_key;
 pub(crate) mod encrypt_decrypt;
 pub(crate) mod rekey;
+mod test_set_attribute;
 
 pub(crate) const SUB_COMMAND: &str = "sym";
diff --git a/crate/cli/src/tests/symmetric/test_set_attribute.rs b/crate/cli/src/tests/symmetric/test_set_attribute.rs
new file mode 100644
index 000000000..72279237d
--- /dev/null
+++ b/crate/cli/src/tests/symmetric/test_set_attribute.rs
@@ -0,0 +1,313 @@
+use cosmian_kms_client::kmip::{
+    extra::VENDOR_ID_COSMIAN,
+    kmip_types::{
+        CryptographicAlgorithm, CryptographicUsageMask, LinkType, StateEnumeration, Tag,
+        VendorAttribute,
+    },
+};
+use kms_test_server::{start_default_test_kms_server, TestsContext};
+use strum::IntoEnumIterator;
+use tracing::trace;
+
+use crate::{
+    actions::{
+        certificates::Algorithm,
+        shared::{
+            utils::{build_usage_mask_from_key_usage, KeyUsage},
+            SetOrDeleteAttributes, VendorAttributeCli,
+        },
+    },
+    error::result::CliResult,
+    tests::{
+        certificates::certify::{certify, CertifyOp},
+        shared::{delete_attributes, get_attributes, set_attributes},
+        symmetric::create_key::create_symmetric_key,
+    },
+};
+
+fn get_all_attribute_tags() -> Vec<Tag> {
+    let mut tags = Vec::new();
+    for tag in Tag::iter() {
+        tags.push(tag);
+    }
+    tags
+}
+
+fn get_all_link_types() -> Vec<LinkType> {
+    let mut links = Vec::new();
+    for link_type in LinkType::iter() {
+        links.push(link_type);
+    }
+    links
+}
+
+#[allow(clippy::cognitive_complexity)]
+fn get_and_check_attributes(
+    ctx: &TestsContext,
+    uid: &str,
+    requested_attributes: &SetOrDeleteAttributes,
+) -> CliResult<()> {
+    let get_attributes = get_attributes(
+        &ctx.owner_client_conf_path,
+        uid,
+        &get_all_attribute_tags(),
+        &get_all_link_types(),
+    )?;
+    trace!("get_and_check_attributes: {get_attributes:?}");
+
+    if let Some(activation_date) = requested_attributes.activation_date {
+        let date: u64 = serde_json::from_value(
+            get_attributes
+                .get(&Tag::ActivationDate.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+
+        assert_eq!(date, activation_date);
+    }
+    if let Some(cryptographic_length) = requested_attributes.cryptographic_length {
+        let length: i32 = serde_json::from_value(
+            get_attributes
+                .get(&Tag::CryptographicLength.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+        assert_eq!(length, cryptographic_length);
+    }
+    if let Some(cryptographic_algorithm) = requested_attributes.cryptographic_algorithm {
+        let algo: CryptographicAlgorithm = serde_json::from_value(
+            get_attributes
+                .get(&Tag::CryptographicAlgorithm.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+        assert_eq!(algo, cryptographic_algorithm);
+    }
+    if let Some(key_usage) = &requested_attributes.key_usage {
+        let get_key_usage: CryptographicUsageMask = serde_json::from_value(
+            get_attributes
+                .get(&Tag::CryptographicUsageMask.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+        assert_eq!(
+            get_key_usage,
+            build_usage_mask_from_key_usage(key_usage).unwrap()
+        );
+    }
+    if let Some(public_key_id) = &requested_attributes.public_key_id {
+        let id: String = serde_json::from_value(
+            get_attributes
+                .get(&LinkType::PublicKeyLink.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+        assert_eq!(&id, public_key_id);
+    }
+    if let Some(private_key_id) = &requested_attributes.private_key_id {
+        let id: String = serde_json::from_value(
+            get_attributes
+                .get(&LinkType::PrivateKeyLink.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+        assert_eq!(&id, private_key_id);
+    }
+    if let Some(certificate_id) = &requested_attributes.certificate_id {
+        let certificate_link_id: String = serde_json::from_value(
+            get_attributes
+                .get(&LinkType::CertificateLink.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+        assert_eq!(certificate_id, &certificate_link_id);
+    }
+    if let Some(pkcs12_certificate_id) = &requested_attributes.pkcs12_certificate_id {
+        let pkcs12_id: String = serde_json::from_value(
+            get_attributes
+                .get(&LinkType::PKCS12CertificateLink.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+
+        assert_eq!(&pkcs12_id, pkcs12_certificate_id);
+    }
+    if let Some(pkcs12_password_certificate) = &requested_attributes.pkcs12_password_certificate {
+        let pkcs12_password_link: String = serde_json::from_value(
+            get_attributes
+                .get(&LinkType::PKCS12PasswordLink.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+        assert_eq!(&pkcs12_password_link, pkcs12_password_certificate);
+    }
+    if let Some(vendor_attributes) = &requested_attributes.vendor_attributes {
+        let vendor_attributes_: Vec<VendorAttribute> = serde_json::from_value(
+            get_attributes
+                .get(&Tag::VendorExtension.to_string())
+                .unwrap()
+                .clone(),
+        )?;
+        let input_vendor_attributes = vec![VendorAttribute::try_from(vendor_attributes)?];
+        assert_eq!(vendor_attributes_, input_vendor_attributes);
+    }
+
+    Ok(())
+}
+
+fn check_set_delete_attributes(uid: &str, ctx: &TestsContext) -> CliResult<()> {
+    let key_usage = Some(vec![KeyUsage::Encrypt, KeyUsage::Decrypt]);
+    for activation_date in [None, Some(5)] {
+        for cryptographic_length in [None, Some(256)] {
+            let requested_attributes = SetOrDeleteAttributes {
+                id: Some(uid.to_owned()),
+                activation_date,
+                cryptographic_length,
+                key_usage: key_usage.clone(),
+                public_key_id: Some("public_key_id".to_string()),
+                private_key_id: Some("private_key_id".to_string()),
+                certificate_id: Some("certificate_id".to_string()),
+                pkcs12_certificate_id: Some("pkcs12_certificate_id".to_string()),
+                pkcs12_password_certificate: Some("toto".to_string()),
+                parent_id: Some("parent_id".to_string()),
+                child_id: Some("child_id".to_string()),
+                vendor_attributes: Some(VendorAttributeCli {
+                    vendor_identification: Some(VENDOR_ID_COSMIAN.to_string()),
+                    attribute_name: Some("my_new_attribute".to_string()),
+                    attribute_value: Some("AABBCCDDEEFF".to_string()),
+                }),
+                ..SetOrDeleteAttributes::default()
+            };
+            set_attributes(&ctx.owner_client_conf_path, &requested_attributes)?;
+            get_and_check_attributes(ctx, uid, &requested_attributes)?;
+            delete_attributes(
+                &ctx.owner_client_conf_path,
+                &Some(requested_attributes),
+                None,
+            )?;
+        }
+    }
+
+    // Test cryptographic algorithm one by one
+    for cryptographic_algorithm in CryptographicAlgorithm::iter() {
+        let requested_attributes = SetOrDeleteAttributes {
+            id: Some(uid.to_owned()),
+            cryptographic_algorithm: Some(cryptographic_algorithm),
+            ..SetOrDeleteAttributes::default()
+        };
+
+        set_attributes(&ctx.owner_client_conf_path, &requested_attributes)?;
+        get_and_check_attributes(ctx, uid, &requested_attributes)?;
+        delete_attributes(
+            &ctx.owner_client_conf_path,
+            &Some(requested_attributes),
+            None,
+        )?;
+    }
+
+    // Test key usage one by one
+    for key_usage in KeyUsage::iter() {
+        let requested_attributes = SetOrDeleteAttributes {
+            id: Some(uid.to_owned()),
+            key_usage: Some(vec![key_usage.clone()]),
+            ..SetOrDeleteAttributes::default()
+        };
+        set_attributes(&ctx.owner_client_conf_path, &requested_attributes)?;
+        get_and_check_attributes(ctx, uid, &requested_attributes)?;
+        delete_attributes(
+            &ctx.owner_client_conf_path,
+            &Some(requested_attributes),
+            None,
+        )?;
+    }
+
+    // Test state one by one
+    for state in StateEnumeration::iter() {
+        let requested_attributes = SetOrDeleteAttributes {
+            id: Some(uid.to_owned()),
+            state: Some(state),
+            ..SetOrDeleteAttributes::default()
+        };
+
+        set_attributes(&ctx.owner_client_conf_path, &requested_attributes)?;
+        get_and_check_attributes(ctx, uid, &requested_attributes)?;
+        delete_attributes(
+            &ctx.owner_client_conf_path,
+            &Some(requested_attributes),
+            None,
+        )
+        .unwrap_err();
+    }
+
+    trace!("Test delete all attributes by references");
+    let requested_attributes = Some(SetOrDeleteAttributes {
+        id: Some(uid.to_owned()),
+        ..SetOrDeleteAttributes::default()
+    });
+    for tag in Tag::iter() {
+        delete_attributes(
+            &ctx.owner_client_conf_path,
+            &requested_attributes,
+            Some(vec![tag]),
+        )?;
+    }
+
+    // Accumulate all values of AttributeTag in a Vec
+    let mut attribute_tags = Vec::new();
+    for tag in Tag::iter() {
+        attribute_tags.push(tag);
+    }
+    delete_attributes(
+        &ctx.owner_client_conf_path,
+        &requested_attributes,
+        Some(attribute_tags),
+    )?;
+    Ok(())
+}
+
+/// This asynchronous test function performs a series of operations to validate the setting,
+/// getting, and deleting of attributes in a Key Management System (KMS) server. It follows
+/// these steps:
+///
+/// 1. Starts a default test KMS server.
+/// 2. Creates an AES 256-bit symmetric key and verifies its attributes.
+/// 3. Certifies a Certificate Signing Request (CSR) without an issuer (self-signed) and verifies its attributes.
+///
+/// The function uses various helper functions to set, get, and delete attributes, and checks
+/// the correctness of these operations by comparing the expected and actual values.
+///
+/// # Returns
+///
+/// This function returns a `CliResult<()>`, which is an alias for `Result<(), CliError>`.
+///
+/// # Errors
+///
+/// This function will return an error if any of the attribute operations (set, get, delete)
+/// fail or if the test KMS server fails to start.
+/// ```
+#[tokio::test]
+async fn test_set_attribute_ckms() -> CliResult<()> {
+    // Create a test server
+    let ctx = start_default_test_kms_server().await;
+
+    // AES 256 bit key
+    let uid = create_symmetric_key(&ctx.owner_client_conf_path, None, None, None, &[])?;
+    check_set_delete_attributes(&uid, ctx)?;
+
+    // Certify the CSR without issuer i.e. self signed
+    let uid = certify(
+        &ctx.owner_client_conf_path,
+        CertifyOp {
+            generate_keypair: true,
+            algorithm: Some(Algorithm::NistP256),
+            subject_name: Some(
+                "C = FR, ST = IdF, L = Paris, O = AcmeTest, CN = Test Leaf".to_string(),
+            ),
+            tags: Some(vec!["certify_self_signed".to_owned()]),
+            ..Default::default()
+        },
+    )?;
+    check_set_delete_attributes(&uid, ctx)?;
+
+    Ok(())
+}
diff --git a/crate/kmip/src/crypto/symmetric/tests.rs b/crate/kmip/src/crypto/symmetric/tests.rs
index 1572b253a..87a6e54e1 100644
--- a/crate/kmip/src/crypto/symmetric/tests.rs
+++ b/crate/kmip/src/crypto/symmetric/tests.rs
@@ -8,6 +8,7 @@ use crate::{
         },
         DecryptionSystem, EncryptionSystem,
     },
+    error::result::KmipResult,
     kmip::{
         kmip_operations::{Decrypt, Encrypt},
         kmip_types::{CryptographicAlgorithm, CryptographicParameters, UniqueIdentifier},
@@ -15,21 +16,21 @@ use crate::{
 };
 
 #[test]
-pub(crate) fn test_aes() {
+pub(crate) fn test_aes() -> KmipResult<()> {
     #[cfg(feature = "fips")]
     // Load FIPS provider module from OpenSSL.
     openssl::provider::Provider::load(None, "fips").unwrap();
 
     let mut symmetric_key = vec![0; AES_256_GCM_KEY_LENGTH];
     rand_bytes(&mut symmetric_key).unwrap();
-    let key = create_symmetric_key_kmip_object(&symmetric_key, CryptographicAlgorithm::AES);
+    let key = create_symmetric_key_kmip_object(&symmetric_key, CryptographicAlgorithm::AES)?;
     let aes = AesGcmSystem::instantiate("blah", &key).unwrap();
     let mut data = zeroize::Zeroizing::from(vec![0_u8; 42]);
     rand_bytes(&mut data).unwrap();
     let mut uid = vec![0_u8; 32];
     rand_bytes(&mut uid).unwrap();
 
-    let mut nonce = vec![0u8; AES_256_GCM_IV_LENGTH];
+    let mut nonce = vec![0_u8; AES_256_GCM_IV_LENGTH];
     rand_bytes(&mut nonce).unwrap();
 
     // encrypt
@@ -69,4 +70,5 @@ pub(crate) fn test_aes() {
         .unwrap();
 
     assert_eq!(&data.clone(), &dec_res.data.unwrap());
+    Ok(())
 }
diff --git a/crate/kmip/src/crypto/wrap/tests.rs b/crate/kmip/src/crypto/wrap/tests.rs
index 15cd521e5..630d3da9c 100644
--- a/crate/kmip/src/crypto/wrap/tests.rs
+++ b/crate/kmip/src/crypto/wrap/tests.rs
@@ -25,6 +25,7 @@ use crate::{
         symmetric::create_symmetric_key_kmip_object,
         wrap::{unwrap_key::unwrap, wrap_key::wrap},
     },
+    error::result::KmipResult,
     kmip::{
         kmip_data_structures::KeyWrappingData,
         kmip_types::{CryptographicAlgorithm, CryptographicUsageMask, KeyFormatType},
@@ -42,7 +43,7 @@ fn test_wrap_unwrap() -> Result<(), KmipError> {
     let sym_wrapping_key = create_symmetric_key_kmip_object(
         sym_wrapping_key_bytes.as_slice(),
         CryptographicAlgorithm::AES,
-    );
+    )?;
 
     // the key to wrap
     let mut sym_key_to_wrap_bytes = vec![0; 32];
@@ -50,7 +51,7 @@ fn test_wrap_unwrap() -> Result<(), KmipError> {
     let mut sym_key_to_wrap = create_symmetric_key_kmip_object(
         sym_key_to_wrap_bytes.as_slice(),
         CryptographicAlgorithm::AES,
-    );
+    )?;
 
     let algorithm = Some(CryptographicAlgorithm::EC);
     let private_key_mask_wp = Some(CryptographicUsageMask::UnwrapKey);
@@ -119,7 +120,7 @@ fn wrap_test(
         assert_ne!(key_to_wrap.key_block()?.key_bytes()?, key_to_wrap_bytes);
         assert_eq!(
             key_to_wrap.key_block()?.key_wrapping_data,
-            Some(Default::default())
+            Some(Box::default())
         );
         // unwrap
         unwrap_key_block(key_to_wrap.key_block_mut()?, unwrapping_key)?;
@@ -156,7 +157,7 @@ fn wrap_test(
 }
 
 #[test]
-fn test_encrypt_decrypt_rfc_5649() {
+fn test_encrypt_decrypt_rfc_5649() -> KmipResult<()> {
     #[cfg(feature = "fips")]
     // Load FIPS provider module from OpenSSL.
     openssl::provider::Provider::load(None, "fips").unwrap();
@@ -164,13 +165,14 @@ fn test_encrypt_decrypt_rfc_5649() {
     let mut symmetric_key = vec![0; 32];
     rand_bytes(&mut symmetric_key).unwrap();
     let wrap_key =
-        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES)?;
 
     let plaintext = b"plaintext";
     let ciphertext = wrap(&wrap_key, &KeyWrappingData::default(), plaintext, None).unwrap();
     let decrypted_plaintext =
         unwrap(&wrap_key, &KeyWrappingData::default(), &ciphertext, None).unwrap();
     assert_eq!(plaintext, &decrypted_plaintext[..]);
+    Ok(())
 }
 #[test]
 #[cfg(not(feature = "fips"))]
@@ -292,7 +294,7 @@ fn test_encrypt_decrypt_no_rsa_1024_in_fips() {
         plaintext,
         None,
     );
-    assert!(encryption_res.is_err());
+    encryption_res.unwrap_err();
 }
 
 #[test]
diff --git a/crate/kmip/src/kmip/ttlv/tests/mod.rs b/crate/kmip/src/kmip/ttlv/tests/mod.rs
index 1638d2d43..f290c67b3 100644
--- a/crate/kmip/src/kmip/ttlv/tests/mod.rs
+++ b/crate/kmip/src/kmip/ttlv/tests/mod.rs
@@ -1,11 +1,13 @@
+use cosmian_logger::log_utils::log_init;
 use num_bigint_dig::BigUint;
 use serde::{de::DeserializeOwned, Deserialize, Serialize};
 use time::OffsetDateTime;
+use tracing::trace;
 use zeroize::Zeroizing;
 
 use crate::{
     crypto::secret::SafeBigUint,
-    error::KmipError,
+    error::{result::KmipResult, KmipError},
     kmip::{
         kmip_data_structures::{KeyBlock, KeyMaterial, KeyValue},
         kmip_messages::{
@@ -15,25 +17,26 @@ use crate::{
         kmip_objects::{Object, ObjectType},
         kmip_operations::{
             Create, DecryptResponse, Encrypt, ErrorReason, Import, ImportResponse, Locate,
-            LocateResponse, Operation,
+            LocateResponse, Operation, SetAttribute,
         },
         kmip_types::{
-            AsynchronousIndicator, AttestationType, Attributes, BatchErrorContinuationOption,
-            Credential, CryptographicAlgorithm, CryptographicUsageMask, KeyFormatType, Link,
-            LinkedObjectIdentifier, MessageExtension, Nonce, OperationEnumeration, ProtocolVersion,
+            AsynchronousIndicator, AttestationType, Attribute, Attributes,
+            BatchErrorContinuationOption, Credential, CryptographicAlgorithm,
+            CryptographicUsageMask, KeyFormatType, Link, LinkType, LinkedObjectIdentifier,
+            MessageExtension, Nonce, OperationEnumeration, ProtocolVersion,
             ResultStatusEnumeration, UniqueIdentifier,
         },
         ttlv::{deserializer::from_ttlv, serializer::to_ttlv, TTLVEnumeration, TTLValue, TTLV},
     },
 };
 
-pub(crate) fn aes_key_material(key_value: &[u8]) -> KeyMaterial {
+fn aes_key_material(key_value: &[u8]) -> KeyMaterial {
     KeyMaterial::TransparentSymmetricKey {
         key: Zeroizing::from(key_value.to_vec()),
     }
 }
 
-pub(crate) fn aes_key_value(key_value: &[u8]) -> KeyValue {
+fn aes_key_value(key_value: &[u8]) -> KeyValue {
     KeyValue {
         key_material: aes_key_material(key_value),
         attributes: Some(Box::new(Attributes {
@@ -47,7 +50,7 @@ pub(crate) fn aes_key_value(key_value: &[u8]) -> KeyValue {
     }
 }
 
-pub(crate) fn aes_key_block(key_value: &[u8]) -> KeyBlock {
+fn aes_key_block(key_value: &[u8]) -> KeyBlock {
     KeyBlock {
         key_format_type: KeyFormatType::TransparentSymmetricKey,
         key_compression_type: None,
@@ -58,52 +61,52 @@ pub(crate) fn aes_key_block(key_value: &[u8]) -> KeyBlock {
     }
 }
 
-pub(crate) fn aes_key(key_value: &[u8]) -> Object {
+fn aes_key(key_value: &[u8]) -> Object {
     Object::SymmetricKey {
         key_block: aes_key_block(key_value),
     }
 }
 
-pub(crate) fn aes_key_material_ttlv(key_value: &[u8]) -> TTLV {
+fn aes_key_material_ttlv(key_value: &[u8]) -> TTLV {
     TTLV {
-        tag: "KeyMaterial".to_string(),
+        tag: "KeyMaterial".to_owned(),
         value: TTLValue::Structure(vec![TTLV {
-            tag: "Key".to_string(),
+            tag: "Key".to_owned(),
             value: TTLValue::ByteString(key_value.to_vec()),
         }]),
     }
 }
 
-pub(crate) fn aes_key_value_ttlv(key_value: &[u8]) -> TTLV {
+fn aes_key_value_ttlv(key_value: &[u8]) -> TTLV {
     TTLV {
-        tag: "KeyValue".to_string(),
+        tag: "KeyValue".to_owned(),
         value: TTLValue::Structure(vec![
             aes_key_material_ttlv(key_value),
             TTLV {
-                tag: "Attributes".to_string(),
+                tag: "Attributes".to_owned(),
                 value: TTLValue::Structure(vec![
                     TTLV {
-                        tag: "CryptographicAlgorithm".to_string(),
-                        value: TTLValue::Enumeration(TTLVEnumeration::Name("AES".to_string())),
+                        tag: "CryptographicAlgorithm".to_owned(),
+                        value: TTLValue::Enumeration(TTLVEnumeration::Name("AES".to_owned())),
                     },
                     TTLV {
-                        tag: "CryptographicLength".to_string(),
+                        tag: "CryptographicLength".to_owned(),
                         value: TTLValue::Integer(key_value.len() as i32 * 8),
                     },
                     TTLV {
-                        tag: "CryptographicUsageMask".to_string(),
+                        tag: "CryptographicUsageMask".to_owned(),
                         value: TTLValue::Integer(4),
                     },
                     TTLV {
-                        tag: "KeyFormatType".to_string(),
+                        tag: "KeyFormatType".to_owned(),
                         value: TTLValue::Enumeration(TTLVEnumeration::Name(
-                            "TransparentSymmetricKey".to_string(),
+                            "TransparentSymmetricKey".to_owned(),
                         )),
                     },
                     TTLV {
-                        tag: "ObjectType".to_string(),
+                        tag: "ObjectType".to_owned(),
                         value: TTLValue::Enumeration(TTLVEnumeration::Name(
-                            "SymmetricKey".to_string(),
+                            "SymmetricKey".to_owned(),
                         )),
                     },
                 ]),
@@ -112,39 +115,39 @@ pub(crate) fn aes_key_value_ttlv(key_value: &[u8]) -> TTLV {
     }
 }
 
-pub(crate) fn aes_key_block_ttlv(key_value: &[u8]) -> TTLV {
+fn aes_key_block_ttlv(key_value: &[u8]) -> TTLV {
     TTLV {
-        tag: "KeyBlock".to_string(),
+        tag: "KeyBlock".to_owned(),
         value: TTLValue::Structure(vec![
             TTLV {
-                tag: "KeyFormatType".to_string(),
+                tag: "KeyFormatType".to_owned(),
                 value: TTLValue::Enumeration(TTLVEnumeration::Name(
-                    "TransparentSymmetricKey".to_string(),
+                    "TransparentSymmetricKey".to_owned(),
                 )),
             },
             aes_key_value_ttlv(key_value),
             TTLV {
-                tag: "CryptographicAlgorithm".to_string(),
-                value: TTLValue::Enumeration(TTLVEnumeration::Name("AES".to_string())),
+                tag: "CryptographicAlgorithm".to_owned(),
+                value: TTLValue::Enumeration(TTLVEnumeration::Name("AES".to_owned())),
             },
             TTLV {
-                tag: "CryptographicLength".to_string(),
+                tag: "CryptographicLength".to_owned(),
                 value: TTLValue::Integer(key_value.len() as i32 * 8),
             },
         ]),
     }
 }
 
-pub(crate) fn aes_key_ttlv(key_value: &[u8]) -> TTLV {
+fn aes_key_ttlv(key_value: &[u8]) -> TTLV {
     TTLV {
-        tag: "SymmetricKey".to_string(),
+        tag: "SymmetricKey".to_owned(),
         value: TTLValue::Structure(vec![aes_key_block_ttlv(key_value)]),
     }
 }
 
 #[test]
 fn test_enumeration() {
-    let es = TTLVEnumeration::Name("blah".to_string());
+    let es = TTLVEnumeration::Name("blah".to_owned());
     let s = serde_json::to_string_pretty(&es).unwrap();
     assert_eq!(es, serde_json::from_str(&s).unwrap());
 
@@ -161,54 +164,54 @@ fn test_enumeration() {
 fn test_serialization_deserialization() {
     let now = OffsetDateTime::now_utc();
     let ttlv = TTLV {
-        tag: "Test".to_string(),
+        tag: "Test".to_owned(),
         value: TTLValue::Structure(vec![
             TTLV {
-                tag: "AnInt".to_string(),
+                tag: "AnInt".to_owned(),
                 value: TTLValue::Integer(42),
             },
             TTLV {
-                tag: "ABitMask".to_string(),
+                tag: "ABitMask".to_owned(),
                 value: TTLValue::BitMask(42),
             },
             TTLV {
-                tag: "ALongInt".to_string(),
+                tag: "ALongInt".to_owned(),
                 value: TTLValue::LongInteger(-42_i64),
             },
             TTLV {
-                tag: "ABigInteger".to_string(),
+                tag: "ABigInteger".to_owned(),
                 value: TTLValue::BigInteger(BigUint::from(2_487_678_887_987_987_798_676_u128)),
             },
             TTLV {
-                tag: "AnEnumeration_1".to_string(),
+                tag: "AnEnumeration_1".to_owned(),
                 value: TTLValue::Enumeration(TTLVEnumeration::Integer(54)),
             },
             TTLV {
-                tag: "AnEnumeration_2".to_string(),
-                value: TTLValue::Enumeration(TTLVEnumeration::Name("blah".to_string())),
+                tag: "AnEnumeration_2".to_owned(),
+                value: TTLValue::Enumeration(TTLVEnumeration::Name("blah".to_owned())),
             },
             TTLV {
-                tag: "ABoolean".to_string(),
+                tag: "ABoolean".to_owned(),
                 value: TTLValue::Boolean(true),
             },
             TTLV {
-                tag: "ATextString".to_string(),
-                value: TTLValue::TextString("blah".to_string()),
+                tag: "ATextString".to_owned(),
+                value: TTLValue::TextString("blah".to_owned()),
             },
             TTLV {
-                tag: "AnByteString".to_string(),
+                tag: "AnByteString".to_owned(),
                 value: TTLValue::ByteString(b"hello".to_vec()),
             },
             TTLV {
-                tag: "ADateTime".to_string(),
+                tag: "ADateTime".to_owned(),
                 value: TTLValue::DateTime(now),
             },
             TTLV {
-                tag: "AnInterval".to_string(),
+                tag: "AnInterval".to_owned(),
                 value: TTLValue::Interval(27),
             },
             TTLV {
-                tag: "ADateTimeExtended".to_string(),
+                tag: "ADateTimeExtended".to_owned(),
                 value: TTLValue::DateTimeExtended(now),
             },
         ]),
@@ -226,12 +229,12 @@ fn test_serialization_deserialization() {
 
 #[test]
 fn test_ser_int() {
-    cosmian_logger::log_utils::log_init(Some("info,hyper=info,reqwest=info"));
     #[derive(Serialize)]
     #[serde(rename_all = "PascalCase")]
     struct Test {
         an_int: u32,
     }
+    log_init(Some("info,hyper=info,reqwest=info"));
 
     let test = Test {
         an_int: 1,
@@ -246,12 +249,12 @@ fn test_ser_int() {
 
 #[test]
 fn test_ser_array() {
-    cosmian_logger::log_utils::log_init(Some("info,hyper=info,reqwest=info"));
     #[derive(Serialize)]
     #[serde(rename_all = "PascalCase")]
     struct Test {
         seq: Vec<&'static str>,
     }
+    log_init(Some("info,hyper=info,reqwest=info"));
 
     let test = Test {
         seq: vec!["a", "b"],
@@ -264,12 +267,12 @@ fn test_ser_array() {
 
 #[test]
 fn test_ser_big_int() {
-    cosmian_logger::log_utils::log_init(Some("info,hyper=info,reqwest=info"));
     #[derive(Serialize)]
     #[serde(rename_all = "PascalCase")]
     struct Test {
         big_int: BigUint,
     }
+    log_init(Some("info,hyper=info,reqwest=info"));
 
     let test = Test {
         big_int: BigUint::from(0x1111_1111_1222_2222_u128),
@@ -298,7 +301,7 @@ fn test_ser_big_int() {
 
 #[test]
 fn test_ser_aes_key() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let key_bytes: &[u8] = b"this_is_a_test";
     let aes_key = aes_key(key_bytes);
     let ttlv = to_ttlv(&aes_key).unwrap();
@@ -307,24 +310,23 @@ fn test_ser_aes_key() {
 
 #[test]
 fn test_des_int() {
-    cosmian_logger::log_utils::log_init(None);
-
     #[derive(Serialize, Deserialize, PartialEq, Debug)]
     #[serde(rename_all = "PascalCase")]
     struct Test {
         an_int: i32,
         another_int: u32,
     }
+    log_init(None);
 
     let ttlv = TTLV {
-        tag: "Test".to_string(),
+        tag: "Test".to_owned(),
         value: TTLValue::Structure(vec![
             TTLV {
-                tag: "AnInt".to_string(),
+                tag: "AnInt".to_owned(),
                 value: TTLValue::Integer(2),
             },
             TTLV {
-                tag: "AnotherInt".to_string(),
+                tag: "AnotherInt".to_owned(),
                 value: TTLValue::BitMask(4),
             },
         ]),
@@ -342,25 +344,24 @@ fn test_des_int() {
 
 #[test]
 fn test_des_array() {
-    cosmian_logger::log_utils::log_init(None);
-
     #[derive(Serialize, Deserialize, PartialEq, Debug)]
     #[serde(rename_all = "PascalCase")]
     struct Test {
         ints: Vec<i32>,
     }
 
+    log_init(None);
     let ttlv = TTLV {
-        tag: "Test".to_string(),
+        tag: "Test".to_owned(),
         value: TTLValue::Structure(vec![TTLV {
-            tag: "Ints".to_string(),
+            tag: "Ints".to_owned(),
             value: TTLValue::Structure(vec![
                 TTLV {
-                    tag: "Ints".to_string(),
+                    tag: "Ints".to_owned(),
                     value: TTLValue::Integer(2),
                 },
                 TTLV {
-                    tag: "Ints".to_string(),
+                    tag: "Ints".to_owned(),
                     value: TTLValue::Integer(4),
                 },
             ]),
@@ -373,19 +374,18 @@ fn test_des_array() {
 
 #[test]
 fn test_des_enum() {
-    cosmian_logger::log_utils::log_init(None);
-
     #[derive(Serialize, Deserialize, PartialEq, Debug)]
     #[serde(rename_all = "PascalCase")]
     struct Test {
         crypto_algo: CryptographicAlgorithm,
     }
+    log_init(None);
 
     let ttlv = TTLV {
-        tag: "Test".to_string(),
+        tag: "Test".to_owned(),
         value: TTLValue::Structure(vec![TTLV {
-            tag: "CryptoAlgo".to_string(),
-            value: TTLValue::Enumeration(TTLVEnumeration::Name("AES".to_string())),
+            tag: "CryptoAlgo".to_owned(),
+            value: TTLValue::Enumeration(TTLVEnumeration::Name("AES".to_owned())),
         }]),
     };
 
@@ -400,14 +400,14 @@ fn test_des_enum() {
 
 #[test]
 fn test_key_material_vec_deserialization() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let bytes = Zeroizing::from(vec![
         116, 104, 105, 115, 95, 105, 115, 95, 97, 95, 116, 101, 115, 116,
     ]);
     let ttlv = TTLV {
-        tag: "KeyMaterial".to_string(),
+        tag: "KeyMaterial".to_owned(),
         value: TTLValue::Structure(vec![TTLV {
-            tag: "Key".to_string(),
+            tag: "Key".to_owned(),
             value: TTLValue::ByteString(bytes.to_vec()),
         }]),
     };
@@ -418,28 +418,28 @@ fn test_key_material_vec_deserialization() {
 
 #[test]
 fn test_key_material_big_int_deserialization() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let ttlv = TTLV {
-        tag: "KeyMaterial".to_string(),
+        tag: "KeyMaterial".to_owned(),
         value: TTLValue::Structure(vec![
             TTLV {
-                tag: "KeyTypeSer".to_string(),
-                value: TTLValue::Enumeration(TTLVEnumeration::Name("DH".to_string())),
+                tag: "KeyTypeSer".to_owned(),
+                value: TTLValue::Enumeration(TTLVEnumeration::Name("DH".to_owned())),
             },
             TTLV {
-                tag: "P".to_string(),
+                tag: "P".to_owned(),
                 value: TTLValue::BigInteger(BigUint::from(u32::MAX)),
             },
             TTLV {
-                tag: "Q".to_string(),
+                tag: "Q".to_owned(),
                 value: TTLValue::BigInteger(BigUint::from(1_u32)),
             },
             TTLV {
-                tag: "G".to_string(),
+                tag: "G".to_owned(),
                 value: TTLValue::BigInteger(BigUint::from(2_u32)),
             },
             TTLV {
-                tag: "X".to_string(),
+                tag: "X".to_owned(),
                 value: TTLValue::BigInteger(BigUint::from(u128::MAX)),
             },
         ]),
@@ -473,7 +473,7 @@ fn test_big_int_deserialization() {
 
 #[test]
 fn test_des_aes_key() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let key_bytes: &[u8] = b"this_is_a_test";
 
     let json = serde_json::to_value(aes_key(key_bytes)).unwrap();
@@ -492,7 +492,7 @@ fn test_des_aes_key() {
 
 #[test]
 fn test_aes_key_block() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let key_bytes: &[u8] = b"this_is_a_test";
     //
     let json = serde_json::to_value(aes_key_block(key_bytes)).unwrap();
@@ -506,7 +506,7 @@ fn test_aes_key_block() {
 
 #[test]
 fn test_aes_key_value() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let key_bytes: &[u8] = b"this_is_a_test";
     //
     let json = serde_json::to_value(aes_key_value(key_bytes)).unwrap();
@@ -520,7 +520,7 @@ fn test_aes_key_value() {
 
 #[test]
 fn test_aes_key_material() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let key_bytes: &[u8] = b"this_is_a_test";
     let ttlv = aes_key_material_ttlv(key_bytes);
     let rec: KeyMaterial = from_ttlv(&ttlv).unwrap();
@@ -529,7 +529,6 @@ fn test_aes_key_material() {
 
 #[test]
 fn test_some_attributes() {
-    cosmian_logger::log_utils::log_init(None);
     #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
     #[serde(untagged)]
     enum Wrapper {
@@ -541,6 +540,8 @@ fn test_some_attributes() {
             whatever: i32,
         },
     }
+    log_init(None);
+
     let value = Wrapper::Attr {
         attributes: Some(Box::new(Attributes {
             object_type: Some(ObjectType::SymmetricKey),
@@ -557,7 +558,7 @@ fn test_some_attributes() {
 
 #[test]
 fn test_java_import_request() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let ir_java = r#"
 {
   "tag" : "Import",
@@ -624,9 +625,9 @@ fn test_java_import_request() {
 
 #[test]
 fn test_java_import_response() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let ir = ImportResponse {
-        unique_identifier: UniqueIdentifier::TextString("blah".to_string()),
+        unique_identifier: UniqueIdentifier::TextString("blah".to_owned()),
     };
     let json = serde_json::to_string(&to_ttlv(&ir).unwrap()).unwrap();
     let ir_ = from_ttlv(&serde_json::from_str::<TTLV>(&json).unwrap()).unwrap();
@@ -635,7 +636,7 @@ fn test_java_import_response() {
 
 #[test]
 fn test_byte_string_key_material() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let key_bytes: &[u8] = b"this_is_a_test";
     let key_value = KeyValue {
         key_material: KeyMaterial::ByteString(Zeroizing::from(key_bytes.to_vec())),
@@ -651,7 +652,7 @@ fn test_byte_string_key_material() {
 
 #[test]
 fn test_aes_key_full() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let key_bytes: &[u8] = b"this_is_a_test";
     let aes_key = aes_key(key_bytes);
     let ttlv = to_ttlv(&aes_key).unwrap();
@@ -664,7 +665,7 @@ fn test_aes_key_full() {
 
 #[test]
 pub(crate) fn test_attributes_with_links() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
     let json = include_str!("./attributes_with_links.json");
     let ttlv: TTLV = serde_json::from_str(json).unwrap();
     let _attributes: Attributes = from_ttlv(&ttlv).unwrap();
@@ -672,10 +673,10 @@ pub(crate) fn test_attributes_with_links() {
 
 #[test]
 pub(crate) fn test_import_correct_object() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
 
     // This file was migrated from GPSW without touching the keys (just changing the `CryptographicAlgorithm` and `KeyFormatType`)
-    // It cannot be used to do crypto stuff, it's just for testing the serialization/deserialisation of TTLV.
+    // It cannot be used to do crypto stuff, it's just for testing the serialization/deserialization of TTLV.
     let json = include_str!("./import.json");
     let ttlv: TTLV = serde_json::from_str(json).unwrap();
     let import: Import = from_ttlv(&ttlv).unwrap();
@@ -700,7 +701,7 @@ pub(crate) fn test_create() {
         cryptographic_algorithm: Some(CryptographicAlgorithm::AES),
         link: Some(vec![Link {
             link_type: crate::kmip::kmip_types::LinkType::ParentLink,
-            linked_object_identifier: LinkedObjectIdentifier::TextString("SK".to_string()),
+            linked_object_identifier: LinkedObjectIdentifier::TextString("SK".to_owned()),
         }]),
         ..Attributes::default()
     };
@@ -717,7 +718,7 @@ pub(crate) fn test_create() {
         create_.attributes.cryptographic_algorithm.unwrap()
     );
     assert_eq!(
-        LinkedObjectIdentifier::TextString("SK".to_string()),
+        LinkedObjectIdentifier::TextString("SK".to_owned()),
         create_.attributes.link.as_ref().unwrap()[0].linked_object_identifier
     );
 }
@@ -726,18 +727,18 @@ pub(crate) fn test_create() {
 // is actually fixed
 #[test]
 fn test_issue_deserialize_object_with_empty_attributes() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
 
     // this works
-    let _: KeyBlock = serialize_deserialize(get_key_block()).unwrap();
-    println!("KeyBlock serialize/deserialize OK");
+    serialize_deserialize(&get_key_block()).unwrap();
+    trace!("KeyBlock serialize/deserialize OK");
 
     // this should work too but does not deserialize
     // because of the empty Attributes in the KeyValue
     let object = Object::SymmetricKey {
         key_block: get_key_block(),
     };
-    let object_: Object = serialize_deserialize(object).unwrap();
+    let object_: Object = serialize_deserialize(&object).unwrap();
     match object_ {
         Object::SymmetricKey { key_block } => {
             assert_eq!(
@@ -749,9 +750,9 @@ fn test_issue_deserialize_object_with_empty_attributes() {
     }
 }
 
-fn serialize_deserialize<T: DeserializeOwned + Serialize>(object: T) -> Result<T, KmipError> {
+fn serialize_deserialize<T: DeserializeOwned + Serialize>(object: &T) -> Result<T, KmipError> {
     // serialize
-    let object_ttlv = to_ttlv(&object)?;
+    let object_ttlv = to_ttlv(object)?;
     let json = serde_json::to_string_pretty(&object_ttlv)?;
     // deserialize
     let ttlv: TTLV = serde_json::from_str(&json)?;
@@ -783,7 +784,7 @@ fn get_key_block() -> KeyBlock {
 
 #[test]
 pub(crate) fn test_message_request() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
 
     let req = Message {
         header: MessageHeader {
@@ -793,8 +794,8 @@ pub(crate) fn test_message_request() {
             },
             maximum_response_size: Some(9999),
             batch_count: 1,
-            client_correlation_value: Some("client_123".to_string()),
-            server_correlation_value: Some("server_234".to_string()),
+            client_correlation_value: Some("client_123".to_owned()),
+            server_correlation_value: Some("server_234".to_owned()),
             asynchronous_indicator: Some(AsynchronousIndicator::Optional),
             attestation_capable_indicator: Some(true),
             attestation_type: Some(vec![AttestationType::TPM_Quote]),
@@ -820,7 +821,7 @@ pub(crate) fn test_message_request() {
                 ..Default::default()
             }),
             message_extension: Some(vec![MessageExtension {
-                vendor_identification: "CosmianVendor".to_string(),
+                vendor_identification: "CosmianVendor".to_owned(),
                 criticality_indicator: false,
                 vendor_extension: vec![42_u8],
             }]),
@@ -841,7 +842,7 @@ pub(crate) fn test_message_request() {
 
 #[test]
 pub(crate) fn test_message_response() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
 
     let res = MessageResponse {
         header: MessageResponseHeader {
@@ -850,15 +851,15 @@ pub(crate) fn test_message_response() {
                 protocol_version_minor: 0,
             },
             batch_count: 2,
-            client_correlation_value: Some("client_123".to_string()),
-            server_correlation_value: Some("server_234".to_string()),
+            client_correlation_value: Some("client_123".to_owned()),
+            server_correlation_value: Some("server_234".to_owned()),
             attestation_type: Some(vec![AttestationType::TPM_Quote]),
             timestamp: 1_697_201_574,
             nonce: Some(Nonce {
                 nonce_id: vec![5, 6, 7],
                 nonce_value: vec![8, 9, 0],
             }),
-            server_hashed_password: Some("5e8953ab".to_string()),
+            server_hashed_password: Some("5e8953ab".to_owned()),
         },
         items: vec![
             MessageResponseBatchItem {
@@ -867,11 +868,11 @@ pub(crate) fn test_message_response() {
                 response_payload: Some(Operation::LocateResponse(LocateResponse {
                     located_items: Some(134),
                     unique_identifiers: Some(vec![UniqueIdentifier::TextString(
-                        "some_id".to_string(),
+                        "some_id".to_owned(),
                     )]),
                 })),
                 message_extension: Some(MessageExtension {
-                    vendor_identification: "CosmianVendor".to_string(),
+                    vendor_identification: "CosmianVendor".to_owned(),
                     criticality_indicator: false,
                     vendor_extension: vec![42_u8],
                 }),
@@ -884,18 +885,18 @@ pub(crate) fn test_message_response() {
                 operation: Some(OperationEnumeration::Decrypt),
                 unique_batch_item_id: Some(1235),
                 response_payload: Some(Operation::DecryptResponse(DecryptResponse {
-                    unique_identifier: UniqueIdentifier::TextString("id_12345".to_string()),
+                    unique_identifier: UniqueIdentifier::TextString("id_12345".to_owned()),
                     data: Some(Zeroizing::from(b"decrypted_data".to_vec())),
                     correlation_value: Some(vec![9_u8, 13]),
                 })),
                 message_extension: Some(MessageExtension {
-                    vendor_identification: "CosmianVendor".to_string(),
+                    vendor_identification: "CosmianVendor".to_owned(),
                     criticality_indicator: true,
                     vendor_extension: vec![42_u8],
                 }),
                 result_status: ResultStatusEnumeration::OperationUndone,
                 result_reason: Some(ErrorReason::Response_Too_Large),
-                result_message: Some("oversized data".to_string()),
+                result_message: Some("oversized data".to_owned()),
                 asynchronous_correlation_value: Some(vec![43_u8, 6]),
             },
         ],
@@ -923,14 +924,14 @@ pub(crate) fn test_message_response() {
     );
     assert_eq!(
         decrypt.unique_identifier,
-        UniqueIdentifier::TextString("id_12345".to_string())
+        UniqueIdentifier::TextString("id_12345".to_owned())
     );
     assert_eq!(res, res_);
 }
 
 #[test]
 pub(crate) fn test_message_enforce_enum() {
-    cosmian_logger::log_utils::log_init(None);
+    log_init(None);
 
     // check Message request serializer reinforcement
     let req = Message {
@@ -954,7 +955,7 @@ pub(crate) fn test_message_enforce_enum() {
     };
     assert_eq!(
         to_ttlv(&req).unwrap_err().to_string(),
-        "operation enum (`Create`) doesn't correspond to request payload (`Locate`)".to_string()
+        "operation enum (`Create`) doesn't correspond to request payload (`Locate`)".to_owned()
     );
 
     let req = Message {
@@ -972,7 +973,7 @@ pub(crate) fn test_message_enforce_enum() {
     };
     assert_eq!(
         to_ttlv(&req).unwrap_err().to_string(),
-        "mismatch number of batch items between header (`15`) and items list (`1`)".to_string()
+        "mismatch number of batch items between header (`15`) and items list (`1`)".to_owned()
     );
 
     let req = Message {
@@ -989,7 +990,7 @@ pub(crate) fn test_message_enforce_enum() {
     assert_eq!(
         to_ttlv(&req).unwrap_err().to_string(),
         "item's protocol version is greater (`3.0`) than header's protocol version (`2.1`)"
-            .to_string()
+            .to_owned()
     );
 
     let req = Message {
@@ -1007,7 +1008,7 @@ pub(crate) fn test_message_enforce_enum() {
             unique_batch_item_id: None,
             // mismatch operation regarding the enum
             request_payload: Operation::DecryptResponse(DecryptResponse {
-                unique_identifier: UniqueIdentifier::TextString("id_12345".to_string()),
+                unique_identifier: UniqueIdentifier::TextString("id_12345".to_owned()),
                 data: Some(Zeroizing::from(b"decrypted_data".to_vec())),
                 correlation_value: None,
             }),
@@ -1016,7 +1017,7 @@ pub(crate) fn test_message_enforce_enum() {
     };
     assert_eq!(
         to_ttlv(&req).unwrap_err().to_string(),
-        "request payload operation is not a request type operation (`Response`)".to_string()
+        "request payload operation is not a request type operation (`Response`)".to_owned()
     );
 
     // check Message response serializer reinforcement
@@ -1046,7 +1047,7 @@ pub(crate) fn test_message_enforce_enum() {
         to_ttlv(&res).unwrap_err().to_string(),
         "missing `AsynchronousCorrelationValue` with pending status (`ResultStatus` is set to \
          `OperationPending`)"
-            .to_string()
+            .to_owned()
     );
 
     let res = MessageResponse {
@@ -1073,7 +1074,7 @@ pub(crate) fn test_message_enforce_enum() {
     };
     assert_eq!(
         to_ttlv(&res).unwrap_err().to_string(),
-        "operation enum (`Decrypt`) doesn't correspond to response payload (`Locate`)".to_string()
+        "operation enum (`Decrypt`) doesn't correspond to response payload (`Locate`)".to_owned()
     );
 
     let res = MessageResponse {
@@ -1093,7 +1094,7 @@ pub(crate) fn test_message_enforce_enum() {
     };
     assert_eq!(
         to_ttlv(&res).unwrap_err().to_string(),
-        "mismatch number of batch items between header (`22`) and items list (`1`)".to_string()
+        "mismatch number of batch items between header (`22`) and items list (`1`)".to_owned()
     );
 
     let res = MessageResponse {
@@ -1114,7 +1115,7 @@ pub(crate) fn test_message_enforce_enum() {
     assert_eq!(
         to_ttlv(&res).unwrap_err().to_string(),
         "item's protocol version is greater (`128.128`) than header's protocol version (`2.1`)"
-            .to_string()
+            .to_owned()
     );
 
     let res = MessageResponse {
@@ -1124,15 +1125,15 @@ pub(crate) fn test_message_enforce_enum() {
                 protocol_version_minor: 0,
             },
             batch_count: 1,
-            client_correlation_value: Some("client_123".to_string()),
-            server_correlation_value: Some("server_234".to_string()),
+            client_correlation_value: Some("client_123".to_owned()),
+            server_correlation_value: Some("server_234".to_owned()),
             attestation_type: Some(vec![AttestationType::TPM_Quote]),
             timestamp: 1_697_201_574,
             nonce: Some(Nonce {
                 nonce_id: vec![5, 6, 7],
                 nonce_value: vec![8, 9, 0],
             }),
-            server_hashed_password: Some("5e8953ab".to_string()),
+            server_hashed_password: Some("5e8953ab".to_owned()),
         },
         items: vec![MessageResponseBatchItem {
             operation: Some(OperationEnumeration::Locate),
@@ -1141,7 +1142,7 @@ pub(crate) fn test_message_enforce_enum() {
             // we could only have an `Operation::LocateResponse`
             response_payload: Some(Operation::Locate(Locate::default())),
             message_extension: Some(MessageExtension {
-                vendor_identification: "CosmianVendor".to_string(),
+                vendor_identification: "CosmianVendor".to_owned(),
                 criticality_indicator: false,
                 vendor_extension: vec![42_u8],
             }),
@@ -1153,6 +1154,169 @@ pub(crate) fn test_message_enforce_enum() {
     };
     assert_eq!(
         to_ttlv(&res).unwrap_err().to_string(),
-        "response payload operation is not a response type operation (`Request`)".to_string()
+        "response payload operation is not a response type operation (`Request`)".to_owned()
     );
 }
+
+#[test]
+fn test_deserialization_set_attribute() -> KmipResult<()> {
+    log_init(None);
+    let set_attribute_request = r#"
+    {
+      "tag": "SetAttribute",
+      "type": "Structure",
+      "value": [
+        {
+          "tag": "UniqueIdentifier",
+          "type": "TextString",
+          "value": "173cb39b-c95a-4e98-ae0d-3e8079e145e6"
+        },
+        {
+          "tag": "NewAttribute",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "Link",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "LinkType",
+                  "type": "Enumeration",
+                  "value": "PublicKeyLink"
+                },
+                {
+                  "tag": "LinkedObjectIdentifier",
+                  "type": "TextString",
+                  "value": "public_key_id"
+                }
+              ]
+            }
+          ]
+        }
+      ]
+    }
+    "#;
+    let ttlv: TTLV = serde_json::from_str(set_attribute_request)?;
+    let _set_attribute_request: SetAttribute = from_ttlv(&ttlv)?;
+    trace!("ttlv: {:?}", ttlv);
+
+    Ok(())
+}
+
+#[test]
+fn test_deserialization_attribute() -> KmipResult<()> {
+    log_init(None);
+    let attribute_str = r#"
+    {
+          "tag": "NewAttribute",
+          "type": "Structure",
+          "value": [
+            {
+              "tag": "Link",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "LinkType",
+                  "type": "Enumeration",
+                  "value": "PublicKeyLink"
+                },
+                {
+                  "tag": "LinkedObjectIdentifier",
+                  "type": "TextString",
+                  "value": "public_key_id"
+                }
+              ]
+            }
+          ]
+    }
+    "#;
+    let ttlv: TTLV = serde_json::from_str(attribute_str)?;
+    trace!("ttlv: {:?}", ttlv);
+
+    let attribute: Attribute = from_ttlv(&ttlv)?;
+    trace!("attribute: {:?}", attribute);
+
+    Ok(())
+}
+
+#[test]
+fn test_deserialization_link() -> KmipResult<()> {
+    log_init(None);
+    let link_str = r#"
+    {
+              "tag": "Link",
+              "type": "Structure",
+              "value": [
+                {
+                  "tag": "LinkType",
+                  "type": "Enumeration",
+                  "value": "PublicKeyLink"
+                },
+                {
+                  "tag": "LinkedObjectIdentifier",
+                  "type": "TextString",
+                  "value": "public_key_id"
+                }
+              ]
+    }
+    "#;
+    let ttlv: TTLV = serde_json::from_str(link_str)?;
+    trace!("ttlv: {:?}", ttlv);
+
+    let link: Link = from_ttlv(&ttlv)?;
+    trace!("attribute: {:?}", link);
+
+    Ok(())
+}
+
+#[test]
+fn test_serialization_set_attribute() -> KmipResult<()> {
+    log_init(None);
+    let set_attribute_request = SetAttribute {
+        unique_identifier: Some(UniqueIdentifier::TextString(
+            "173cb39b-c95a-4e98-ae0d-3e8079e145e6".to_owned(),
+        )),
+        new_attribute: Attribute::Links(vec![
+            Link {
+                link_type: LinkType::PublicKeyLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(
+                    "public_key_id".to_owned(),
+                ),
+            },
+            Link {
+                link_type: LinkType::PrivateKeyLink,
+                linked_object_identifier: LinkedObjectIdentifier::TextString(
+                    "private_key_id".to_owned(),
+                ),
+            },
+        ]),
+    };
+
+    let set_attribute = to_ttlv(&set_attribute_request)?;
+    trace!("set_attribute: {:#?}", set_attribute);
+
+    let set_attribute_deserialized: SetAttribute = from_ttlv(&set_attribute)?;
+    trace!(
+        "set_attribute_deserialized: {:?}",
+        set_attribute_deserialized
+    );
+
+    Ok(())
+}
+
+#[test]
+fn test_serialization_link() -> KmipResult<()> {
+    log_init(None);
+    let link_request = vec![Link {
+        link_type: LinkType::PublicKeyLink,
+        linked_object_identifier: LinkedObjectIdentifier::TextString("public_key_id".to_owned()),
+    }];
+
+    let link = to_ttlv(&link_request)?;
+    trace!("link: {:#?}", link);
+
+    let link_deserialized: Vec<Link> = from_ttlv(&link)?;
+    trace!("set_attribute_deserialized: {:?}", link_deserialized);
+
+    Ok(())
+}
diff --git a/crate/pkcs11/provider/src/tests.rs b/crate/pkcs11/provider/src/tests.rs
index e35a95d21..896dab68d 100644
--- a/crate/pkcs11/provider/src/tests.rs
+++ b/crate/pkcs11/provider/src/tests.rs
@@ -23,8 +23,8 @@ async fn test_kms_client() -> Result<(), Pkcs11Error> {
 
     let keys = get_kms_objects_async(
         &kms_client,
-        &["disk-encryption".to_string(), "_kk".to_string()],
-        None, //default key format type is Raw
+        &["disk-encryption".to_owned()],
+        Some(KeyFormatType::Raw),
     )
     .await?;
     assert_eq!(keys.len(), 2);
@@ -33,7 +33,7 @@ async fn test_kms_client() -> Result<(), Pkcs11Error> {
         .flat_map(|k| k.other_tags.clone())
         .collect::<Vec<String>>();
     labels.sort();
-    assert_eq!(labels, vec!["vol1".to_string(), "vol2".to_string()]);
+    assert_eq!(labels, vec!["vol1".to_owned(), "vol2".to_owned()]);
     Ok(())
 }
 
@@ -58,10 +58,10 @@ fn initialize_backend() -> Result<CkmsBackend, Pkcs11Error> {
 }
 
 async fn create_keys(kms_client: &KmsClient) -> Result<(), Pkcs11Error> {
-    let vol1 = create_symmetric_key_kmip_object(&[1, 2, 3, 4], CryptographicAlgorithm::AES);
+    let vol1 = create_symmetric_key_kmip_object(&[1, 2, 3, 4], CryptographicAlgorithm::AES)?;
     let _vol1_id = import_object(
         kms_client,
-        Some("vol1".to_string()),
+        Some("vol1".to_owned()),
         vol1,
         None,
         false,
@@ -70,10 +70,10 @@ async fn create_keys(kms_client: &KmsClient) -> Result<(), Pkcs11Error> {
     )
     .await?;
 
-    let vol2 = create_symmetric_key_kmip_object(&[4, 5, 6, 7], CryptographicAlgorithm::AES);
+    let vol2 = create_symmetric_key_kmip_object(&[4, 5, 6, 7], CryptographicAlgorithm::AES)?;
     let _vol2_id = import_object(
         kms_client,
-        Some("vol2".to_string()),
+        Some("vol2".to_owned()),
         vol2,
         None,
         false,
@@ -113,7 +113,7 @@ async fn load_p12() -> Result<String, Pkcs11Error> {
 
     let p12_id = import_object(
         &kms_client,
-        Some("test.p12".to_string()),
+        Some("test.p12".to_owned()),
         p12_sk,
         None,
         false,
@@ -136,7 +136,7 @@ fn test_backend() -> Result<(), Pkcs11Error> {
         .map(|dao| dao.label())
         .collect::<Vec<String>>();
     labels.sort();
-    assert_eq!(labels, vec!["vol1".to_string(), "vol2".to_string()]);
+    assert_eq!(labels, vec!["vol1".to_owned(), "vol2".to_owned()]);
 
     // RSA certificate
     let certificates = backend.find_all_certificates()?;
diff --git a/crate/server/src/database/tests/additional_redis_findex_tests.rs b/crate/server/src/database/tests/additional_redis_findex_tests.rs
index 08aa4c7ee..b9cb14955 100644
--- a/crate/server/src/database/tests/additional_redis_findex_tests.rs
+++ b/crate/server/src/database/tests/additional_redis_findex_tests.rs
@@ -61,7 +61,7 @@ pub(crate) async fn test_objects_db() -> KResult<()> {
 
     let mut symmetric_key = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key);
-    let object = create_symmetric_key_kmip_object(&symmetric_key, CryptographicAlgorithm::AES);
+    let object = create_symmetric_key_kmip_object(&symmetric_key, CryptographicAlgorithm::AES)?;
 
     // clean up
     o_db.clear_all().await?;
diff --git a/crate/server/src/database/tests/database_tests.rs b/crate/server/src/database/tests/database_tests.rs
index 288d732c2..272b727a7 100644
--- a/crate/server/src/database/tests/database_tests.rs
+++ b/crate/server/src/database/tests/database_tests.rs
@@ -38,14 +38,14 @@ pub(crate) async fn tx_and_list<DB: Database>(
     let mut symmetric_key = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key);
     let symmetric_key_1 =
-        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES)?;
 
     let uid_1 = Uuid::new_v4().to_string();
 
     let mut symmetric_key = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key);
     let symmetric_key_2 =
-        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES)?;
 
     let uid_2 = Uuid::new_v4().to_string();
 
@@ -125,14 +125,14 @@ pub(crate) async fn atomic<DB: Database>(
     let mut symmetric_key = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key);
     let symmetric_key_1 =
-        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES)?;
 
     let uid_1 = Uuid::new_v4().to_string();
 
     let mut symmetric_key = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key);
     let symmetric_key_2 =
-        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES)?;
 
     let uid_2 = Uuid::new_v4().to_string();
 
@@ -246,7 +246,7 @@ pub(crate) async fn upsert<DB: Database>(
     let mut symmetric_key = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key);
     let mut symmetric_key =
-        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES)?;
 
     let uid = Uuid::new_v4().to_string();
 
@@ -356,7 +356,7 @@ pub(crate) async fn crud<DB: Database>(
     let mut symmetric_key = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key);
     let mut symmetric_key =
-        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES)?;
 
     let uid = Uuid::new_v4().to_string();
 
diff --git a/crate/server/src/database/tests/find_attributes_test.rs b/crate/server/src/database/tests/find_attributes_test.rs
index c271a9a0b..024adcd4d 100644
--- a/crate/server/src/database/tests/find_attributes_test.rs
+++ b/crate/server/src/database/tests/find_attributes_test.rs
@@ -39,7 +39,7 @@ pub(crate) async fn find_attributes<DB: Database>(
     let mut symmetric_key_bytes = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key_bytes);
     let mut symmetric_key =
-        create_symmetric_key_kmip_object(&symmetric_key_bytes, CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(&symmetric_key_bytes, CryptographicAlgorithm::AES)?;
 
     let uid = Uuid::new_v4().to_string();
 
diff --git a/crate/server/src/database/tests/json_access_test.rs b/crate/server/src/database/tests/json_access_test.rs
index 0dba10704..587659a08 100644
--- a/crate/server/src/database/tests/json_access_test.rs
+++ b/crate/server/src/database/tests/json_access_test.rs
@@ -38,7 +38,7 @@ pub(crate) async fn json_access<DB: Database>(
     let mut symmetric_key_bytes = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key_bytes);
     let symmetric_key =
-        create_symmetric_key_kmip_object(&symmetric_key_bytes, CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(&symmetric_key_bytes, CryptographicAlgorithm::AES)?;
 
     let uid = Uuid::new_v4().to_string();
 
diff --git a/crate/server/src/database/tests/owner_test.rs b/crate/server/src/database/tests/owner_test.rs
index 762802e60..cc21044df 100644
--- a/crate/server/src/database/tests/owner_test.rs
+++ b/crate/server/src/database/tests/owner_test.rs
@@ -33,7 +33,7 @@ pub(crate) async fn owner<DB: Database>(
     let mut symmetric_key_bytes = vec![0; 32];
     rng.fill_bytes(&mut symmetric_key_bytes);
     let symmetric_key =
-        create_symmetric_key_kmip_object(&symmetric_key_bytes, CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(&symmetric_key_bytes, CryptographicAlgorithm::AES)?;
     let uid = Uuid::new_v4().to_string();
 
     db.upsert(
diff --git a/crate/server/src/database/tests/tagging_tests.rs b/crate/server/src/database/tests/tagging_tests.rs
index 4fa68608b..8886849b5 100644
--- a/crate/server/src/database/tests/tagging_tests.rs
+++ b/crate/server/src/database/tests/tagging_tests.rs
@@ -40,7 +40,7 @@ pub(crate) async fn tags<DB: Database>(
     rng.fill_bytes(&mut symmetric_key_bytes);
     // create symmetric key
     let symmetric_key =
-        create_symmetric_key_kmip_object(&symmetric_key_bytes, CryptographicAlgorithm::AES);
+        create_symmetric_key_kmip_object(&symmetric_key_bytes, CryptographicAlgorithm::AES)?;
 
     // insert into DB
 
diff --git a/crate/server/src/tests/cover_crypt_tests/integration_tests.rs b/crate/server/src/tests/cover_crypt_tests/integration_tests.rs
index 1916e3a03..46e0ff6ad 100644
--- a/crate/server/src/tests/cover_crypt_tests/integration_tests.rs
+++ b/crate/server/src/tests/cover_crypt_tests/integration_tests.rs
@@ -237,7 +237,7 @@ async fn integration_tests_use_ids_no_tags() -> KResult<()> {
     let ap_to_edit = "Department::MKG".to_owned();
     let request = build_rekey_keypair_request(
         private_key_unique_identifier,
-        RekeyEditAction::RekeyAccessPolicy(ap_to_edit.clone()),
+        &RekeyEditAction::RekeyAccessPolicy(ap_to_edit.clone()),
     )?;
     let rekey_keypair_response: ReKeyKeyPairResponse = test_utils::post(&app, &request).await?;
     assert_eq!(
@@ -312,7 +312,7 @@ async fn integration_tests_use_ids_no_tags() -> KResult<()> {
     // Prune old keys associated to the access policy
     let request = build_rekey_keypair_request(
         private_key_unique_identifier,
-        RekeyEditAction::PruneAccessPolicy(ap_to_edit),
+        &RekeyEditAction::PruneAccessPolicy(ap_to_edit),
     )?;
     let rekey_keypair_response: KResult<ReKeyKeyPairResponse> =
         test_utils::post(&app, &request).await;
@@ -344,7 +344,7 @@ async fn integration_tests_use_ids_no_tags() -> KResult<()> {
     ];
     let request = build_rekey_keypair_request(
         private_key_unique_identifier,
-        RekeyEditAction::AddAttribute(new_policy_attributes),
+        &RekeyEditAction::AddAttribute(new_policy_attributes),
     )?;
     let rekey_keypair_response: KResult<ReKeyKeyPairResponse> =
         test_utils::post(&app, &request).await;
@@ -376,7 +376,7 @@ async fn integration_tests_use_ids_no_tags() -> KResult<()> {
     )];
     let request = build_rekey_keypair_request(
         private_key_unique_identifier,
-        RekeyEditAction::RenameAttribute(rename_policy_attributes_pair),
+        &RekeyEditAction::RenameAttribute(rename_policy_attributes_pair),
     )?;
     let rekey_keypair_response: KResult<ReKeyKeyPairResponse> =
         test_utils::post(&app, &request).await;
@@ -405,7 +405,7 @@ async fn integration_tests_use_ids_no_tags() -> KResult<()> {
     let disable_policy_attributes = vec![Attribute::from(("Department", "MKG"))];
     let request = build_rekey_keypair_request(
         private_key_unique_identifier,
-        RekeyEditAction::DisableAttribute(disable_policy_attributes),
+        &RekeyEditAction::DisableAttribute(disable_policy_attributes),
     )?;
     let rekey_keypair_response: KResult<ReKeyKeyPairResponse> =
         test_utils::post(&app, &request).await;
@@ -435,7 +435,7 @@ async fn integration_tests_use_ids_no_tags() -> KResult<()> {
     let remove_policy_attributes = vec![Attribute::from(("Department", "HumanResources"))];
     let request = build_rekey_keypair_request(
         private_key_unique_identifier,
-        RekeyEditAction::RemoveAttribute(remove_policy_attributes),
+        &RekeyEditAction::RemoveAttribute(remove_policy_attributes),
     )?;
     let rekey_keypair_response: KResult<ReKeyKeyPairResponse> =
         test_utils::post(&app, &request).await;
diff --git a/crate/server/src/tests/cover_crypt_tests/integration_tests_tags.rs b/crate/server/src/tests/cover_crypt_tests/integration_tests_tags.rs
index ee21cc87a..fabfdfd9f 100644
--- a/crate/server/src/tests/cover_crypt_tests/integration_tests_tags.rs
+++ b/crate/server/src/tests/cover_crypt_tests/integration_tests_tags.rs
@@ -47,7 +47,7 @@ async fn test_re_key_with_tags() -> KResult<()> {
     // Re_key all key pairs with matching access policy
     let request = build_rekey_keypair_request(
         &mkp_json_tag,
-        RekeyEditAction::RekeyAccessPolicy("Department::MKG".to_owned()),
+        &RekeyEditAction::RekeyAccessPolicy("Department::MKG".to_owned()),
     )?;
     let rekey_keypair_response: ReKeyKeyPairResponse = test_utils::post(&app, &request).await?;
     assert_eq!(
@@ -264,7 +264,7 @@ async fn integration_tests_with_tags() -> KResult<()> {
     // Rekey all key pairs with matching access policy
     let request = build_rekey_keypair_request(
         &mkp_json_tag,
-        RekeyEditAction::RekeyAccessPolicy("Department::MKG".to_owned()),
+        &RekeyEditAction::RekeyAccessPolicy("Department::MKG".to_owned()),
     )?;
     let rekey_keypair_response: ReKeyKeyPairResponse = test_utils::post(&app, &request).await?;
     assert_eq!(
diff --git a/crate/server/src/tests/curve_25519_tests.rs b/crate/server/src/tests/curve_25519_tests.rs
index d1d5ddfdd..129f9e4e4 100644
--- a/crate/server/src/tests/curve_25519_tests.rs
+++ b/crate/server/src/tests/curve_25519_tests.rs
@@ -162,7 +162,7 @@ async fn test_curve_25519_key_pair() -> KResult<()> {
         RecommendedCurve::CURVE25519,
         Some(CryptographicAlgorithm::ECDH),
         Some(CryptographicUsageMask::Unrestricted),
-    );
+    )?;
     let request = Import {
         unique_identifier: UniqueIdentifier::TextString(String::new()),
         object_type: ObjectType::PublicKey,
diff --git a/crate/server/src/tests/kmip_server_tests.rs b/crate/server/src/tests/kmip_server_tests.rs
index 9cd0938da..6c46703fe 100644
--- a/crate/server/src/tests/kmip_server_tests.rs
+++ b/crate/server/src/tests/kmip_server_tests.rs
@@ -165,7 +165,7 @@ async fn test_curve_25519_key_pair() -> KResult<()> {
         RecommendedCurve::CURVE25519,
         Some(CryptographicAlgorithm::ECDH),
         Some(CryptographicUsageMask::Unrestricted),
-    );
+    )?;
     let request = Import {
         unique_identifier: UniqueIdentifier::TextString(String::new()),
         object_type: ObjectType::PublicKey,
diff --git a/crate/server/src/tests/mod.rs b/crate/server/src/tests/mod.rs
index 90714b351..4d24c42a4 100644
--- a/crate/server/src/tests/mod.rs
+++ b/crate/server/src/tests/mod.rs
@@ -3,6 +3,7 @@ mod cover_crypt_tests;
 
 pub(crate) mod google_cse;
 mod ms_dke;
+pub(crate) mod test_set_attribute;
 pub(crate) mod test_utils;
 pub(crate) mod test_validate;
 
diff --git a/crate/server/src/tests/test_set_attribute.rs b/crate/server/src/tests/test_set_attribute.rs
new file mode 100644
index 000000000..c529b2ce9
--- /dev/null
+++ b/crate/server/src/tests/test_set_attribute.rs
@@ -0,0 +1,269 @@
+//! This module contains tests for setting and removing attributes on a KMIP server.
+//!
+//! The tests cover various attributes including activation date, cryptographic algorithm,
+//! cryptographic length, and link attributes. Each test follows a similar pattern:
+//! 1. Verify the attribute is not set initially.
+//! 2. Set the attribute.
+//! 3. Verify the attribute is set correctly.
+//! 4. Remove the attribute.
+//! 5. Verify the attribute is removed.
+//!
+//! The tests use the `KMSServer` to perform operations and the `GetAttributesResponse` to
+//! verify the state of attributes.
+//!
+//! # Constants
+//! - `USER`: A constant string representing the user identifier.
+//!
+//! # Functions
+//! - `get_attributes`: Asynchronously retrieves attributes from the KMIP server.
+//! - `set_attribute`: Asynchronously sets an attribute on the KMIP server.
+//! - `test_set_attribute_server`: The main test function that initializes the server and runs all attribute tests.
+//! - `set_activation_date_and_remove_it`: Tests setting and removing the activation date attribute.
+//! - `set_link_attribute_and_remove_it`: Tests setting and removing link attributes.
+//! - `set_cryptographic_algorithm_and_remove_it`: Tests setting and removing the cryptographic algorithm attribute.
+//! - `set_cryptographic_length_and_remove_it`: Tests setting and removing the cryptographic length attribute.
+
+use std::{collections::HashSet, sync::Arc};
+
+use cloudproof::reexport::crypto_core::{
+    reexport::rand_core::{RngCore, SeedableRng},
+    CsRng,
+};
+use cosmian_kmip::{
+    crypto::symmetric::create_symmetric_key_kmip_object,
+    kmip::{
+        kmip_operations::{DeleteAttribute, GetAttributes, GetAttributesResponse, SetAttribute},
+        kmip_types::{
+            Attribute, AttributeReference, CryptographicAlgorithm, Link, LinkType,
+            LinkedObjectIdentifier, Tag, UniqueIdentifier,
+        },
+    },
+};
+use cosmian_logger::log_utils::log_init;
+use uuid::Uuid;
+
+use crate::{
+    config::ServerParams, result::KResult, tests::test_utils::https_clap_config, KMSServer,
+};
+
+const USER: &str = "eyJhbGciOiJSUzI1Ni";
+
+async fn get_attributes(
+    kms: &Arc<KMSServer>,
+    uid: &str,
+    tag: Tag,
+) -> KResult<GetAttributesResponse> {
+    kms.get_attributes(
+        GetAttributes {
+            unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
+            attribute_references: Some(vec![AttributeReference::Standard(tag)]),
+        },
+        USER,
+        None,
+    )
+    .await
+}
+
+async fn set_attribute(kms: &Arc<KMSServer>, uid: &str, attribute: Attribute) -> KResult<()> {
+    kms.set_attribute(
+        SetAttribute {
+            unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
+            new_attribute: attribute,
+        },
+        USER,
+        None,
+    )
+    .await?;
+    Ok(())
+}
+
+async fn delete_attribute(kms: &Arc<KMSServer>, delete_request: DeleteAttribute) -> KResult<()> {
+    kms.delete_attribute(delete_request, USER, None).await?;
+    Ok(())
+}
+
+#[tokio::test]
+pub(crate) async fn test_set_attribute_server() -> KResult<()> {
+    log_init(None);
+
+    let clap_config = https_clap_config();
+    let kms = Arc::new(KMSServer::instantiate(ServerParams::try_from(clap_config)?).await?);
+
+    let mut rng = CsRng::from_entropy();
+
+    // Create key
+    let mut symmetric_key = vec![0; 32];
+    rng.fill_bytes(&mut symmetric_key);
+    let sym_key_object =
+        create_symmetric_key_kmip_object(symmetric_key.as_slice(), CryptographicAlgorithm::AES)?;
+    let uid = Uuid::new_v4().to_string();
+
+    kms.db
+        .create(
+            Some(uid.clone()),
+            USER,
+            &sym_key_object,
+            sym_key_object.attributes()?,
+            &HashSet::new(),
+            None,
+        )
+        .await?;
+
+    //
+    // Start tests
+    //
+    set_activation_date_and_remove_it(
+        &kms,
+        &uid,
+        DeleteAttribute {
+            unique_identifier: Some(UniqueIdentifier::TextString(uid.clone())),
+            current_attribute: Some(Attribute::ActivationDate(42)),
+            attribute_references: None,
+        },
+    )
+    .await?;
+    set_activation_date_and_remove_it(
+        &kms,
+        &uid,
+        DeleteAttribute {
+            unique_identifier: Some(UniqueIdentifier::TextString(uid.clone())),
+            current_attribute: None,
+            attribute_references: Some(vec![AttributeReference::Standard(Tag::ActivationDate)]),
+        },
+    )
+    .await?;
+    set_cryptographic_algorithm_and_remove_it(&kms, &uid).await?;
+    set_cryptographic_length_and_remove_it(&kms, &uid).await?;
+    set_link_attribute_and_remove_it(&kms, &uid, Tag::LinkType, LinkType::CertificateLink).await?;
+    set_link_attribute_and_remove_it(&kms, &uid, Tag::LinkType, LinkType::PKCS12CertificateLink)
+        .await?;
+    set_link_attribute_and_remove_it(&kms, &uid, Tag::LinkType, LinkType::PrivateKeyLink).await?;
+    set_link_attribute_and_remove_it(&kms, &uid, Tag::LinkType, LinkType::PublicKeyLink).await?;
+
+    Ok(())
+}
+
+async fn set_activation_date_and_remove_it(
+    kms: &Arc<KMSServer>,
+    uid: &str,
+    delete_request: DeleteAttribute,
+) -> KResult<()> {
+    // Check no activation date is set
+    let get_response = get_attributes(kms, uid, Tag::ActivationDate).await?;
+    assert!(get_response.attributes.activation_date.is_none());
+
+    // Set activation date
+    set_attribute(kms, uid, Attribute::ActivationDate(42)).await?;
+    // and check if it is set correctly
+    let get_response = get_attributes(kms, uid, Tag::ActivationDate).await?;
+    assert_eq!(get_response.attributes.activation_date, Some(42));
+    // Remove activation date
+    delete_attribute(kms, delete_request).await?;
+    // and check if it is removed
+    let get_response = get_attributes(kms, uid, Tag::ActivationDate).await?;
+    assert!(get_response.attributes.activation_date.is_none());
+
+    Ok(())
+}
+
+async fn set_link_attribute_and_remove_it(
+    kms: &Arc<KMSServer>,
+    uid: &str,
+    tag: Tag,
+    link_type: LinkType,
+) -> KResult<()> {
+    // Check no link attribute is set
+    let get_response = get_attributes(kms, uid, tag).await?;
+    assert!(get_response.attributes.link.is_none());
+
+    let links = vec![Link {
+        link_type,
+        linked_object_identifier: LinkedObjectIdentifier::TextString("my_link".to_owned()),
+    }];
+    set_attribute(kms, uid, Attribute::Links(links.clone())).await?;
+
+    let get_response = get_attributes(kms, uid, tag).await?;
+    assert_eq!(get_response.attributes.link, Some(links));
+
+    kms.delete_attribute(
+        DeleteAttribute {
+            unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
+            attribute_references: Some(vec![AttributeReference::Standard(tag)]),
+            ..DeleteAttribute::default()
+        },
+        USER,
+        None,
+    )
+    .await?;
+
+    let get_response = get_attributes(kms, uid, tag).await?;
+    assert!(get_response.attributes.link.is_none());
+
+    Ok(())
+}
+
+async fn set_cryptographic_algorithm_and_remove_it(kms: &Arc<KMSServer>, uid: &str) -> KResult<()> {
+    // Check no cryptographic algorithm is set
+    let get_response = get_attributes(kms, uid, Tag::CryptographicAlgorithm).await?;
+
+    assert_eq!(
+        get_response.attributes.cryptographic_algorithm,
+        Some(CryptographicAlgorithm::AES)
+    );
+
+    set_attribute(
+        kms,
+        uid,
+        Attribute::CryptographicAlgorithm(CryptographicAlgorithm::AES),
+    )
+    .await?;
+
+    let get_response = get_attributes(kms, uid, Tag::CryptographicAlgorithm).await?;
+    assert_eq!(
+        get_response.attributes.cryptographic_algorithm,
+        Some(CryptographicAlgorithm::AES)
+    );
+
+    delete_attribute(
+        kms,
+        DeleteAttribute {
+            unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
+            current_attribute: Some(Attribute::CryptographicAlgorithm(
+                CryptographicAlgorithm::AES,
+            )),
+            attribute_references: None,
+        },
+    )
+    .await?;
+
+    let get_response = get_attributes(kms, uid, Tag::CryptographicAlgorithm).await?;
+    assert!(get_response.attributes.cryptographic_algorithm.is_none());
+
+    Ok(())
+}
+
+async fn set_cryptographic_length_and_remove_it(kms: &Arc<KMSServer>, uid: &str) -> KResult<()> {
+    // Check no cryptographic length is set
+    let get_response = get_attributes(kms, uid, Tag::CryptographicLength).await?;
+    assert_eq!(get_response.attributes.cryptographic_length, Some(256));
+
+    set_attribute(kms, uid, Attribute::CryptographicLength(256)).await?;
+
+    let get_response = get_attributes(kms, uid, Tag::CryptographicLength).await?;
+    assert_eq!(get_response.attributes.cryptographic_length, Some(256));
+
+    delete_attribute(
+        kms,
+        DeleteAttribute {
+            unique_identifier: Some(UniqueIdentifier::TextString(uid.to_owned())),
+            current_attribute: Some(Attribute::CryptographicLength(256)),
+            attribute_references: None,
+        },
+    )
+    .await?;
+
+    let get_response = get_attributes(kms, uid, Tag::CryptographicLength).await?;
+    assert!(get_response.attributes.cryptographic_length.is_none());
+
+    Ok(())
+}
diff --git a/crate/test_server/src/test_server.rs b/crate/test_server/src/test_server.rs
index 5742e88e0..7a0852491 100644
--- a/crate/test_server/src/test_server.rs
+++ b/crate/test_server/src/test_server.rs
@@ -36,6 +36,7 @@ fn sqlite_db_config() -> DBConfig {
     trace!("TESTS: using sqlite");
     let tmp_dir = TempDir::new().unwrap();
     let file_path = tmp_dir.path().join("test_sqlite.db");
+    // let file_path = PathBuf::from("test_sqlite.db");
     if file_path.exists() {
         std::fs::remove_file(&file_path).unwrap();
     }
@@ -299,7 +300,7 @@ fn generate_http_config(
             HttpConfig {
                 port,
                 https_p12_file: Some(root_dir.join("certificates/server/kmserver.acme.com.p12")),
-                https_p12_password: Some("password".to_string()),
+                https_p12_password: Some("password".to_owned()),
                 authority_cert_file: Some(root_dir.join("certificates/server/ca.crt")),
                 api_token_id,
                 ..HttpConfig::default()
@@ -308,7 +309,7 @@ fn generate_http_config(
             HttpConfig {
                 port,
                 https_p12_file: Some(root_dir.join("certificates/server/kmserver.acme.com.p12")),
-                https_p12_password: Some("password".to_string()),
+                https_p12_password: Some("password".to_owned()),
                 api_token_id,
                 ..HttpConfig::default()
             }
@@ -388,16 +389,14 @@ fn generate_owner_conf(
             let p = root_dir.join("certificates/owner/owner.client.acme.com.old.format.p12");
             Some(
                 p.to_str()
-                    .ok_or_else(|| {
-                        ClientError::Default("Can't convert path to string".to_string())
-                    })?
+                    .ok_or_else(|| ClientError::Default("Can't convert path to string".to_owned()))?
                     .to_string(),
             )
         } else {
             None
         },
         ssl_client_pkcs12_password: if server_params.authority_cert_file.is_some() {
-            Some("password".to_string())
+            Some("password".to_owned())
         } else {
             None
         },
@@ -426,11 +425,11 @@ fn generate_user_conf(port: u16, owner_client_conf: &ClientConf) -> Result<Strin
         let p = root_dir.join("certificates/user/user.client.acme.com.old.format.p12");
         Some(
             p.to_str()
-                .ok_or_else(|| ClientError::Default("Can't convert path to string".to_string()))?
+                .ok_or_else(|| ClientError::Default("Can't convert path to string".to_owned()))?
                 .to_string(),
         )
     };
-    user_conf.ssl_client_pkcs12_password = Some("password".to_string());
+    user_conf.ssl_client_pkcs12_password = Some("password".to_owned());
 
     // write the user conf
     let user_conf_path = format!("/tmp/user_kms_{port}.json");
@@ -450,7 +449,7 @@ pub fn generate_invalid_conf(correct_conf: &ClientConf) -> String {
 
     let mut invalid_conf = correct_conf.clone();
     // and a temp file
-    let invalid_conf_path = "/tmp/invalid_conf.json".to_string();
+    let invalid_conf_path = "/tmp/invalid_conf.json".to_owned();
     // Generate a wrong token with valid group id
     let secrets = b64
         .decode(