1use snafu::ensure;
13
14use crate::Result;
15use crate::error;
16
17#[derive(Debug, Clone, PartialEq, Eq)]
25pub struct Share {
26 scheme: String,
27 bytes: Vec<u8>,
28}
29
30impl Drop for Share {
31 fn drop(&mut self) {
32 use zeroize::Zeroize;
33 self.bytes.zeroize();
34 }
35}
36
37impl Share {
38 pub fn new(scheme: impl Into<String>, bytes: impl Into<Vec<u8>>) -> Self {
39 Share {
40 scheme: scheme.into(),
41 bytes: bytes.into(),
42 }
43 }
44
45 pub fn scheme(&self) -> &str {
46 &self.scheme
47 }
48
49 pub fn bytes(&self) -> &[u8] {
50 &self.bytes
51 }
52
53 pub fn into_bytes(self) -> Vec<u8> {
54 self.bytes.clone()
55 }
56
57 pub fn len(&self) -> usize {
58 self.bytes.len()
59 }
60
61 pub fn is_empty(&self) -> bool {
62 self.bytes.is_empty()
63 }
64
65 pub fn assert_scheme(&self, expected: &str) -> Result<()> {
69 ensure!(
70 self.scheme == expected,
71 error::ShareSchemeMismatchSnafu {
72 expected,
73 actual: self.scheme.as_str(),
74 }
75 );
76 Ok(())
77 }
78
79 pub fn to_bytes(&self) -> Vec<u8> {
85 let scheme_bytes = self.scheme.as_bytes();
86 let mut out = Vec::with_capacity(4 + scheme_bytes.len() + self.bytes.len());
87 out.extend_from_slice(&(scheme_bytes.len() as u32).to_be_bytes());
88 out.extend_from_slice(scheme_bytes);
89 out.extend_from_slice(&self.bytes);
90 out
91 }
92
93 pub fn from_bytes(data: &[u8]) -> Result<Self> {
95 if data.len() < 4 {
96 return Err(error::ShareTruncatedSnafu {
97 needed: 4usize,
98 have: data.len(),
99 }
100 .build());
101 }
102 let scheme_len = u32::from_be_bytes([data[0], data[1], data[2], data[3]]) as usize;
103 let scheme_end = 4usize.checked_add(scheme_len).ok_or_else(|| {
104 error::ShareTruncatedSnafu {
105 needed: 4 + scheme_len,
106 have: data.len(),
107 }
108 .build()
109 })?;
110 if data.len() < scheme_end {
111 return Err(error::ShareTruncatedSnafu {
112 needed: scheme_end,
113 have: data.len(),
114 }
115 .build());
116 }
117 let scheme = std::str::from_utf8(&data[4..scheme_end])
118 .map_err(|_| error::ShareInvalidSchemeSnafu {}.build())?
119 .to_string();
120 let bytes = data[scheme_end..].to_vec();
121 Ok(Share { scheme, bytes })
122 }
123}
124
125#[cfg(test)]
126mod tests {
127 use super::*;
128
129 #[test]
130 fn share_round_trip() {
131 let original = Share::new("FROST-ed25519", vec![0xDE, 0xAD, 0xBE, 0xEF]);
132 let encoded = original.to_bytes();
133 let decoded = Share::from_bytes(&encoded).expect("decode succeeds");
134 assert_eq!(original, decoded);
135 }
136
137 #[test]
138 fn share_round_trip_preserves_scheme_name() {
139 let original = Share::new("GG18-ECDSA-P256", vec![1, 2, 3, 4, 5]);
140 let encoded = original.to_bytes();
141 let decoded = Share::from_bytes(&encoded).expect("decode succeeds");
142 assert_eq!(decoded.scheme(), "GG18-ECDSA-P256");
143 assert_eq!(decoded.bytes(), &[1, 2, 3, 4, 5]);
144 }
145
146 #[test]
147 fn share_round_trip_empty_bytes() {
148 let original = Share::new("test-scheme", Vec::new());
149 let encoded = original.to_bytes();
150 let decoded = Share::from_bytes(&encoded).expect("decode succeeds");
151 assert_eq!(original, decoded);
152 assert!(decoded.is_empty());
153 }
154
155 #[test]
156 fn share_from_bytes_rejects_truncated_header() {
157 let err = Share::from_bytes(&[1, 2]).unwrap_err();
158 assert!(matches!(err, error::Error::ShareTruncated { .. }));
159 }
160
161 #[test]
162 fn share_from_bytes_rejects_truncated_scheme() {
163 let data = [0, 0, 0, 10, b'a', b'b'];
165 let err = Share::from_bytes(&data).unwrap_err();
166 assert!(matches!(err, error::Error::ShareTruncated { .. }));
167 }
168
169 #[test]
170 fn share_assert_scheme_accepts_match() {
171 let share = Share::new("FROST-ed25519", vec![1]);
172 share.assert_scheme("FROST-ed25519").expect("match is ok");
173 }
174
175 #[test]
176 fn share_assert_scheme_rejects_mismatch() {
177 let share = Share::new("FROST-ed25519", vec![1]);
178 let err = share.assert_scheme("GG18-ECDSA-P256").unwrap_err();
179 assert!(matches!(err, error::Error::ShareSchemeMismatch { .. }));
180 }
181
182 #[test]
183 fn share_into_bytes_consumes_payload() {
184 let share = Share::new("s", vec![9, 9, 9]);
185 let bytes = share.into_bytes();
186 assert_eq!(bytes, vec![9, 9, 9]);
187 }
188}