dkls23/proto/
signed.rs

1// Copyright (c) Silence Laboratories Pte. Ltd. All Rights Reserved.
2// This software is licensed under the Silence Laboratories License Agreement.
3
4//! Module for handling signed messages in the protocol.
5//!
6//! This module provides functionality for creating and verifying signed messages
7//! with support for additional data and trailers. It uses a pluggable signature
8//! scheme interface to allow for different signature implementations.
9
10use std::marker::PhantomData;
11use std::ops::Range;
12
13use bytemuck::{AnyBitPattern, NoUninit};
14use signature::{SignatureEncoding, Signer, Verifier};
15
16use sl_mpc_mate::message::*;
17
18/// A wrapper for a message of type T with support for in-place signing and verifying.
19///
20/// This struct provides functionality for creating and verifying signed messages
21/// with the following format:
22///
23/// ```text
24/// [ msg-hdr | payload | trailer | signature ]
25/// ```
26///
27/// Where:
28/// - `msg-hdr`: Message header containing ID, TTL, and flags
29/// - `payload`: The message payload of type T
30/// - `trailer`: Optional additional data
31/// - `signature`: The cryptographic signature
32///
33/// # Type Parameters
34/// * `T` - The type of the message payload
35/// * `S` - The type of signature encoding used
36pub struct SignedMessage<T, S: SignatureEncoding> {
37    buffer: Vec<u8>,
38    marker: PhantomData<(T, <S as SignatureEncoding>::Repr)>,
39}
40
41impl<S: SignatureEncoding, T: AnyBitPattern + NoUninit> SignedMessage<T, S> {
42    /// Size of the message header in bytes.
43    pub const HEADER_SIZE: usize = MESSAGE_HEADER_SIZE;
44
45    const T_SIZE: usize = core::mem::size_of::<T>();
46    const S_SIZE: usize = core::mem::size_of::<S::Repr>();
47
48    /// Calculates the total size of a signed message.
49    ///
50    /// # Arguments
51    /// * `trailer` - Size of the trailer data in bytes
52    ///
53    /// # Returns
54    /// The total size in bytes needed for the signed message
55    pub const fn size(trailer: usize) -> usize {
56        MESSAGE_HEADER_SIZE + Self::T_SIZE + trailer + Self::S_SIZE
57    }
58
59    /// Creates a new signed message with the specified parameters.
60    ///
61    /// # Arguments
62    /// * `id` - Message identifier
63    /// * `ttl` - Time-to-live value
64    /// * `flags` - Message flags
65    /// * `trailer` - Size of trailer data in bytes
66    ///
67    /// # Returns
68    /// A new `SignedMessage` instance
69    pub fn new(id: &MsgId, ttl: u32, flags: u16, trailer: usize) -> Self {
70        let buffer = vec![0u8; Self::size(trailer)];
71
72        Self::from_buffer(buffer, id, ttl, flags, trailer)
73    }
74
75    /// Creates a signed message from an existing buffer.
76    ///
77    /// # Arguments
78    /// * `buffer` - Existing buffer to use
79    /// * `id` - Message identifier
80    /// * `ttl` - Time-to-live value
81    /// * `flags` - Message flags
82    /// * `trailer` - Size of trailer data in bytes
83    ///
84    /// # Returns
85    /// A new `SignedMessage` instance using the provided buffer
86    pub fn from_buffer(
87        mut buffer: Vec<u8>,
88        id: &MsgId,
89        ttl: u32,
90        flags: u16,
91        trailer: usize,
92    ) -> Self {
93        buffer.resize(Self::size(trailer), 0);
94
95        if let Some(hdr) = buffer.first_chunk_mut::<MESSAGE_HEADER_SIZE>() {
96            MsgHdr::encode(hdr, id, ttl, flags);
97        }
98
99        Self {
100            buffer,
101            marker: PhantomData,
102        }
103    }
104
105    /// Returns mutable references to the message payload and trailer.
106    ///
107    /// # Returns
108    /// A tuple containing:
109    /// - Mutable reference to the payload object
110    /// - Mutable reference to the trailer bytes
111    pub fn payload(&mut self) -> (&mut T, &mut [u8]) {
112        let end = self.buffer.len() - Self::S_SIZE;
113
114        let body = &mut self.buffer[MESSAGE_HEADER_SIZE..end];
115        let (msg, trailer) = body.split_at_mut(Self::T_SIZE);
116
117        (bytemuck::from_bytes_mut(msg), trailer)
118    }
119
120    /// Signs the message and returns the underlying byte vector.
121    ///
122    /// # Arguments
123    /// * `signing_key` - The key used to sign the message
124    ///
125    /// # Returns
126    /// The signed message as a byte vector
127    pub fn sign<K: Signer<S>>(self, signing_key: &K) -> Vec<u8> {
128        let mut buffer = self.buffer;
129
130        let last = buffer.len() - Self::S_SIZE;
131        let (msg, tail) = buffer.split_at_mut(last);
132
133        let sign = signing_key.sign(msg).to_bytes();
134
135        tail.copy_from_slice(sign.as_ref());
136
137        buffer
138    }
139
140    /// Builds and signs a message using a closure to set the payload.
141    ///
142    /// # Arguments
143    /// * `id` - Message identifier
144    /// * `ttl` - Time-to-live value
145    /// * `trailer` - Size of trailer data in bytes
146    /// * `signing_key` - The key used to sign the message
147    /// * `f` - Closure that sets the payload and trailer content
148    ///
149    /// # Returns
150    /// The signed message as a byte vector
151    pub fn build<F, K: Signer<S>>(
152        id: &MsgId,
153        ttl: u32,
154        trailer: usize,
155        signing_key: &K,
156        f: F,
157    ) -> Vec<u8>
158    where
159        F: FnOnce(&mut T, &mut [u8]),
160    {
161        let mut msg = Self::new(id, ttl, 0, trailer);
162        let (payload, trailer) = msg.payload();
163        f(payload, trailer);
164        msg.sign(signing_key)
165    }
166
167    /// Verifies a signed message and returns references to the payload and trailer.
168    ///
169    /// # Arguments
170    /// * `buffer` - The signed message buffer
171    /// * `trailer` - Size of trailer data in bytes
172    /// * `verify_key` - The key used to verify the signature
173    ///
174    /// # Returns
175    /// A tuple containing references to the payload and trailer,
176    /// or `None` if verification fails
177    pub fn verify_with_trailer<'msg, V: Verifier<S>>(
178        buffer: &'msg [u8],
179        trailer: usize,
180        verify_key: &V,
181    ) -> Option<(&'msg T, &'msg [u8])> {
182        // Make sure that buffer is exactly right size
183        if buffer.len() != Self::size(trailer) {
184            return None;
185        }
186
187        let sign_offset = buffer.len() - Self::S_SIZE;
188        let (msg, sign) = buffer.split_at(sign_offset);
189        let sign = S::try_from(sign).ok()?;
190
191        verify_key.verify(msg, &sign).ok()?;
192
193        let body = &msg[MESSAGE_HEADER_SIZE..];
194        let (payload, trailer) = body.split_at(Self::T_SIZE);
195        Some((bytemuck::from_bytes(payload), trailer))
196    }
197
198    /// Verifies a signed message and returns a reference to the payload.
199    ///
200    /// # Arguments
201    /// * `buffer` - The signed message buffer
202    /// * `verify_key` - The key used to verify the signature
203    ///
204    /// # Returns
205    /// A reference to the payload, or `None` if verification fails
206    pub fn verify<'msg, V: Verifier<S>>(
207        buffer: &'msg [u8],
208        verify_key: &V,
209    ) -> Option<&'msg T> {
210        Self::verify_with_trailer(buffer, 0, verify_key).map(|(m, _)| m)
211    }
212}
213
214impl<S: SignatureEncoding> SignedMessage<(), S> {
215    /// Verifies a message in the buffer and returns the range containing the payload.
216    ///
217    /// # Arguments
218    /// * `buffer` - The signed message buffer
219    /// * `verify_key` - The key used to verify the signature
220    ///
221    /// # Returns
222    /// The range of bytes containing the payload, or `None` if verification fails
223    pub fn verify_buffer<V: Verifier<S>>(
224        buffer: &[u8],
225        verify_key: &V,
226    ) -> Option<Range<usize>> {
227        let overhead = MESSAGE_HEADER_SIZE + Self::S_SIZE;
228
229        if buffer.len() > overhead {
230            let trailer = buffer.len() - overhead;
231
232            Self::verify_with_trailer(buffer, trailer, verify_key)?;
233
234            Some(MESSAGE_HEADER_SIZE..buffer.len() - Self::S_SIZE)
235        } else {
236            None
237        }
238    }
239}