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}