ironshield_types/serde_utils.rs
1//! # Utility Functions for Serialization and Concatenation with Serde.
2
3use base64::Engine;
4use serde::{
5 Deserialize,
6 Deserializer,
7 Serializer,
8 de::Error
9};
10
11/// Custom serialization for 64-byte arrays (Ed25519 signatures)
12///
13/// Serializes a fixed-size 64-byte array into a byte sequence
14/// suitable for various serialization formats, namely, JSON.
15///
16/// # Arguments
17/// * `signature`: A reference to a 64-byte array representing
18/// an Ed25519 signature.
19/// * `serializer`: The serde serializer instance that handles
20/// the serialization format.
21///
22/// # Returns
23/// * `Result<S::Ok, S::Error>`: Success value from the serializer
24/// or a serialization error if the
25/// operation fails.
26///
27/// # Type Parameters
28/// * `S`: The serializer type that implements the `Serializer`
29/// trait.
30///
31/// # Example
32/// ```
33/// use ironshield_types::serialize_signature;
34///
35/// #[derive(serde::Serialize)]
36/// struct SignedMessage {
37/// #[serde(serialize_with = "serialize_signature")]
38/// signature: [u8; 64],
39/// }
40/// ```
41pub fn serialize_signature<S>(signature: &[u8; 64], serializer: S) -> Result<S::Ok, S::Error>
42where
43 S: Serializer,
44{
45 serializer.serialize_bytes(signature)
46}
47
48/// Custom deserialization for 64-byte arrays (Ed25519 signatures)
49///
50/// Deserializes a byte sequence back into a fixed-size 64-byte array
51/// with strict length validation to ensure cryptographic "correctness".
52///
53/// # Arguments
54/// * `deserializer`: The serde deserializer instance that
55/// handles the actual deserialization.
56///
57/// # Returns
58/// * `Result<[u8; 64], D::Error>`: A 64-byte array on success,
59/// or a deserialization error
60/// if the operation fails or
61/// the byte length is incorrect.
62///
63/// # Type Parameters
64/// * `D`: The deserializer type that implements the `Deserializer`
65/// trait.
66///
67/// # Errors
68/// * Returns a custom error if the deserialized byte sequence is
69/// not exactly 64 bytes long. (Requirement in the Ed25519 standard.)
70///
71/// # Example
72/// ```
73/// use ironshield_types::deserialize_signature;
74///
75/// #[derive(serde::Deserialize)]
76/// struct SignedMessage {
77/// #[serde(deserialize_with = "deserialize_signature")]
78/// signature: [u8; 64],
79/// }
80/// ```
81pub fn deserialize_signature<'de, D>(deserializer: D) -> Result<[u8; 64], D::Error>
82where
83 D: Deserializer<'de>,
84{
85 let bytes: Vec<u8> = Vec::deserialize(deserializer)?;
86
87 if bytes.len() != 64 {
88 return Err(Error::custom(format!("Expected 64 bytes, got {}", bytes.len())));
89 }
90
91 let mut array = [0u8; 64];
92 array.copy_from_slice(&bytes);
93 Ok(array)
94}
95
96/// Custom serialization for 32-byte arrays (challenge params, public keys).
97///
98/// Serializes a fixed-size 32-byte array into a byte sequence
99/// suitable for various serialization formats.
100///
101/// # Arguments
102/// * `bytes`: A reference to a 32-byte array representing
103/// cryptographic data such as public keys or
104/// challenge parameters.
105/// * `serializer`: The serde serializer instance that will
106/// handle the actual serialization format.
107///
108/// # Returns
109/// * `Result<S::Ok, S::Error>`: Success value from the serializer
110/// or a serialization error if
111/// the operation fails.
112///
113/// # Type Parameters
114/// * `S`: The serializer type that implements the `Serializer` trait.
115///
116/// # Example
117/// ```
118/// use ironshield_types::serialize_32_bytes;
119///
120/// #[derive(serde::Serialize)]
121/// struct CryptoKey {
122/// #[serde(serialize_with = "serialize_32_bytes")]
123/// public_key: [u8; 32],
124/// }
125/// ```
126pub fn serialize_32_bytes<S>(bytes: &[u8; 32], serializer: S) -> Result<S::Ok, S::Error>
127where
128 S: Serializer,
129{
130 serializer.serialize_bytes(bytes)
131}
132
133/// Custom serialization for 32-byte arrays (challenge params, public keys)
134///
135/// Deserializes a byte sequence back into a fixed-size 32-byte array,
136/// with strict length validation to ensure cryptographic correctness.
137///
138/// # Arguments
139/// * `deserializer`: The serde deserializer instance that will
140/// handle the actual deserialization from the
141/// source format.
142///
143/// # Returns
144/// * `Result<[u8; 32], D::Error>`: A 32-byte array on success,
145/// or a deserialization error
146/// if the operation fails or
147/// the byte length is incorrect.
148///
149/// # Type Parameters
150/// * `D`: The deserializer type that implements the `Deserializer`
151/// trait.
152///
153/// # Errors
154/// * Returns a custom error if the deserialized byte sequence
155/// is not exactly 32 bytes long, (Requirement of the
156/// cryptographic primitive in use.)
157///
158/// # Example
159/// ```
160/// use ironshield_types::deserialize_32_bytes;
161///
162/// #[derive(serde::Deserialize)]
163/// struct CryptoKey {
164/// #[serde(deserialize_with = "deserialize_32_bytes")]
165/// public_key: [u8; 32],
166/// }
167/// ```
168pub fn deserialize_32_bytes<'de, D>(deserializer: D) -> Result<[u8; 32], D::Error>
169where
170 D: Deserializer<'de>,
171{
172 let bytes: Vec<u8> = Vec::deserialize(deserializer)?;
173
174 if bytes.len() != 32 {
175 return Err(Error::custom(format!("Expected 32 bytes, got {}", bytes.len())));
176 }
177
178 let mut array = [0u8; 32];
179 array.copy_from_slice(&bytes);
180 Ok(array)
181}
182
183/// Encodes a concatenated string into a Base64 URL-safe
184/// format without padding.
185///
186/// Intended for use with a concatenated string generated
187/// from the function `concat_struct`.
188/// Encodes using base64url encoding (RFC 4648, Section 5).
189///
190/// # Arguments
191/// * `concat_string`: The string to be encoded, typically
192/// concatenated from the function
193/// `concat_struct`.
194///
195/// # Returns
196/// * `String`: A Base64 URL-safe encoded string without padding.
197pub fn concat_struct_base64url_encode(concat_string: &str) -> String {
198 base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(concat_string.as_bytes())
199}
200
201/// Decodes a Base64 URL-safe encoded string into a
202/// concatenated string.
203///
204/// Intended for use with a Base64 URL-safe encoded string
205/// generated from the function
206/// `concat_struct_base64url_encode`.
207///
208/// # Arguments
209/// * `encoded_string`: The Base64 URL-safe encoded string
210/// to decode.
211///
212/// # Returns
213/// * `Result<String, String>`: A Result containing the decoded
214/// string or an error if decoding fails.
215///
216/// # Errors
217/// * Returns a `base64::DecodeError` if the input string
218/// is not valid Base64 URL-safe encoded.
219pub fn concat_struct_base64url_decode(encoded_string: String) -> Result<String, String> {
220 let decoded_bytes: Vec<u8> = base64::engine::general_purpose::URL_SAFE_NO_PAD
221 .decode(encoded_string)
222 .map_err(|e: base64::DecodeError| format!("Base64 decode error: {}", e))?;
223
224 String::from_utf8(decoded_bytes)
225 .map_err(|e: std::string::FromUtf8Error| format!("UTF-8 conversion error: {}", e))
226}