1 //! Utility methods to support structured append (multiple QR codes)
2
3 use crate::bits::{Bits, ExtendedMode};
4 use crate::structured::StructuredQrError::*;
5 use crate::types::QrError::Structured;
6 use crate::{bits, EcLevel, QrCode, QrResult, Version};
7 use std::convert::TryFrom;
8 use std::fmt::{Display, Formatter};
9
10 /// Error variants regarding structured append
11 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
12 pub enum StructuredQrError {
13 /// Structured append mode should contain at least two qrcodes
14 AtLeast2Pieces,
15 /// Encoded number of pieces does not match real number of pieces
16 TotalMismatch(usize),
17 /// Not all the QR codes parts are present
18 MissingParts,
19 /// Computed parity byte does not match
20 Parity,
21 /// QR code data shorter than 5 bytes
22 TooShort,
23 /// QR code mode is not structured append
24 StructuredWrongMode,
25 /// QR code encoding is not the one supported in structured append
26 StructuredWrongEnc,
27 /// QR code sequence number is greater than total
28 SeqGreaterThanTotal(u8, u8),
29 /// QR code length mismatch
30 LengthMismatch(usize, usize),
31 /// Unsupported version
32 UnsupportedVersion(i16),
33 /// Maximum supported pieces in structured append is 16
34 SplitMax16(usize),
35 }
36
37 impl Display for StructuredQrError {
fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result38 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39 match self {
40 AtLeast2Pieces => write!(f, "Need at least 2 different pieces to merge structured QR"),
41 TotalMismatch(i) => write!(f, "Total pieces in input {} does not match the encoded total, or different encoded totals", i),
42 MissingParts => write!(f, "Not all the part are present"),
43 Parity => write!(f, "Invalid parities while merging"),
44 TooShort => write!(f, "QR data shorter than 5 bytes"),
45 StructuredWrongMode => write!(f, "Structured append QR must have mode 3"),
46 StructuredWrongEnc => write!(f, "Structured append QR must have encoding 4"),
47 SeqGreaterThanTotal(s, t) => write!(f, "QR sequence {} greater than total {}", s, t),
48 LengthMismatch(calc, exp) => write!(f, "calculated end {} greater than effective length {}", calc, exp),
49 UnsupportedVersion(ver) => write!(f, "Unsupported version {}", ver),
50 SplitMax16(req) => write!(f, "Could split into max 16 qr, requested {}", req),
51 }
52 }
53 }
54
55 /// Merge multiple structured QR codes content into the original content
merge_qrs(mut bytes: Vec<Vec<u8>>) -> QrResult<Vec<u8>>56 pub fn merge_qrs(mut bytes: Vec<Vec<u8>>) -> QrResult<Vec<u8>> {
57 use std::collections::HashSet;
58 use std::convert::TryInto;
59
60 let mut vec_structured = vec![];
61
62 bytes.sort();
63 bytes.dedup();
64
65 if bytes.len() < 2 {
66 return Err(Structured(AtLeast2Pieces));
67 }
68
69 for vec in bytes {
70 let current: StructuredQr = vec.try_into()?;
71 vec_structured.push(current);
72 }
73
74 let total = (vec_structured.len() - 1) as u8;
75 let totals_same = vec_structured.iter().map(|q| q.total).all(|t| t == total);
76 if !totals_same {
77 return Err(Structured(TotalMismatch(vec_structured.len())));
78 }
79
80 let sequences: HashSet<u8> = vec_structured.iter().map(|q| q.seq).collect();
81 let all_sequence = sequences.len() == vec_structured.len();
82 if !all_sequence {
83 return Err(Structured(MissingParts));
84 }
85
86 vec_structured.sort_by(|a, b| a.seq.cmp(&b.seq)); // allows to merge out of order by reordering here
87 let result: Vec<u8> = vec_structured
88 .iter()
89 .flat_map(|q| q.content.clone())
90 .collect();
91
92 let final_parity = result.iter().fold(0u8, |acc, &x| acc ^ x);
93 if vec_structured
94 .iter()
95 .map(|q| q.parity)
96 .all(|p| p == final_parity)
97 {
98 Ok(result)
99 } else {
100 Err(Structured(Parity))
101 }
102 }
103
104 struct StructuredQr {
105 pub seq: u8, // u4
106 pub total: u8, // u4
107 pub parity: u8,
108 pub content: Vec<u8>,
109 }
110
111 impl TryFrom<Vec<u8>> for StructuredQr {
112 type Error = crate::types::QrError;
113
try_from(value: Vec<u8>) -> QrResult<Self>114 fn try_from(value: Vec<u8>) -> QrResult<Self> {
115 if value.len() < 5 {
116 return Err(Structured(TooShort));
117 }
118 let qr_mode = value[0] >> 4;
119 if qr_mode != 3 {
120 return Err(Structured(StructuredWrongMode));
121 }
122 let seq = value[0] & 0x0f;
123 let total = value[1] >> 4;
124 if seq > total {
125 return Err(Structured(SeqGreaterThanTotal(seq, total)));
126 }
127 let parity = ((value[1] & 0x0f) << 4) + (value[2] >> 4);
128 let enc_mode = value[2] & 0x0f;
129 if enc_mode != 4 {
130 return Err(Structured(StructuredWrongEnc));
131 }
132
133 let (length, from) = if value.len() < u8::max_value() as usize + 4 {
134 // 4 is header size, TODO recheck boundary
135 (value[3] as u16, 4usize)
136 } else {
137 (((value[3] as u16) << 8) + (value[4] as u16), 5usize)
138 };
139 let end = from + length as usize;
140 if value.len() < end {
141 return Err(Structured(LengthMismatch(end, value.len())));
142 }
143 let content = value[from..end].to_vec();
144 //TODO check padding
145
146 Ok(StructuredQr {
147 seq,
148 total,
149 parity,
150 content,
151 })
152 }
153 }
154
155 /// Represent a QR code that could be splitted in `total_qr` QR codes
156 pub struct SplittedQr {
157 /// QR code version
158 pub version: i16,
159 /// QR code parity (will be the same in any QR code of the sequence)
160 pub parity: u8,
161 /// Total QR codes necessary to encode `bytes` with `version` QR codes
162 pub total_qr: usize,
163 /// QR code content
164 pub bytes: Vec<u8>,
165 }
166
167 impl SplittedQr {
168 /// Creates a new `SplittedQr` if possible given `bytes` len and max QR code `version` to use
new(bytes: Vec<u8>, version: i16) -> QrResult<Self>169 pub fn new(bytes: Vec<u8>, version: i16) -> QrResult<Self> {
170 let parity = bytes.iter().fold(0u8, |acc, &x| acc ^ x);
171 if version == 0 {
172 return Err(Structured(UnsupportedVersion(version)));
173 }
174 let max_bytes = *MAX_BYTES
175 .get(version as usize)
176 .ok_or(Structured(UnsupportedVersion(version)))?;
177 let extra = if bytes.len() % max_bytes == 0 { 0 } else { 1 };
178 let total_qr = bytes.len() / max_bytes + extra;
179 if total_qr > 16 {
180 return Err(Structured(SplitMax16(total_qr)));
181 }
182
183 Ok(SplittedQr {
184 bytes,
185 version,
186 parity,
187 total_qr,
188 })
189 }
190
split_to_bits(&self) -> QrResult<Vec<Bits>>191 fn split_to_bits(&self) -> QrResult<Vec<Bits>> {
192 let max_bytes = MAX_BYTES[self.version as usize];
193 if self.total_qr == 1 {
194 let bits = bits::encode_auto(&self.bytes, LEVEL)?;
195 Ok(vec![bits])
196 } else {
197 let mut result = vec![];
198 for (i, chunk) in self.bytes.chunks(max_bytes).enumerate() {
199 let bits = self.make_chunk(i, chunk)?;
200 result.push(bits);
201 }
202 Ok(result)
203 }
204 }
205
206 /// Creates the sequence of QR codes that merged back would produce `self.bytes`
split(&self) -> QrResult<Vec<QrCode>>207 pub fn split(&self) -> QrResult<Vec<QrCode>> {
208 self.split_to_bits()?
209 .into_iter()
210 .map(|bits| QrCode::with_bits(bits, LEVEL))
211 .collect()
212 }
213
make_chunk(&self, i: usize, chunk: &[u8]) -> QrResult<Bits>214 fn make_chunk(&self, i: usize, chunk: &[u8]) -> QrResult<Bits> {
215 //println!("chunk len : {} version: {}", chunk.len(), self.version);
216 //println!("chunk : {}", hex::encode(chunk) );
217 let mut bits = Bits::new(Version::Normal(self.version));
218 bits.push_mode_indicator(ExtendedMode::StructuredAppend)?;
219 bits.push_number_checked(4, i)?;
220 bits.push_number_checked(4, self.total_qr - 1)?;
221 bits.push_number_checked(8, self.parity as usize)?;
222 bits.push_byte_data(chunk)?;
223 bits.push_terminator(LEVEL)?;
224
225 //println!("bits: {}\n", hex::encode(bits.clone().into_bytes()));
226
227 Ok(bits)
228 }
229 }
230
231 const LEVEL: crate::types::EcLevel = EcLevel::L;
232
233 /// Max bytes encodable in a structured append qr code, given Qr code version as array index
234 const MAX_BYTES: [usize; 33] = [
235 0, 15, 30, 51, 76, 104, 132, 152, 190, 228, 269, 319, 365, 423, 456, 518, 584, 642, 716, 790,
236 856, 927, 1001, 1089, 1169, 1271, 1365, 1463, 1526, 1626, 1730, 1838, 1950,
237 ];
238
239 #[cfg(test)]
240 mod tests {
241 use crate::bits::{Bits, ExtendedMode};
242 use crate::structured::StructuredQrError::*;
243 use crate::structured::{merge_qrs, SplittedQr, StructuredQr, LEVEL};
244 use crate::{QrCode, Version};
245 use rand::Rng;
246 use std::convert::TryInto;
247
248 // from example https://segno.readthedocs.io/en/stable/structured-append.html#structured-append
249 /*
250 I read the news today oh boy
251 4 1c 49207265616420746865206e6577 7320746f646179206f6820626f79 000ec11ec
252
253 I read the new
254 3 0 1 39 4 0e 49207265616420746865206e6577 00
255
256 s today oh boy
257 3 1 1 39 4 0e 7320746f646179206f6820626f79 00
258
259 MODE SEQ TOTAL PARITY MODE LENGTH
260 */
261
262 const _FULL: &str = "41c49207265616420746865206e65777320746f646179206f6820626f79000ec11ec";
263 const FULL_CONTENT: &str = "49207265616420746865206e65777320746f646179206f6820626f79";
264 const FIRST: &str = "3013940e49207265616420746865206e657700";
265 const FIRST_CONTENT: &str = "49207265616420746865206e6577";
266 const SECOND: &str = "3113940e7320746f646179206f6820626f7900";
267 const SECOND_CONTENT: &str = "7320746f646179206f6820626f79";
268
269 #[test]
test_try_into_structured()270 fn test_try_into_structured() {
271 let bytes = hex::decode(FIRST).unwrap();
272 let content = hex::decode(FIRST_CONTENT).unwrap();
273 let structured: StructuredQr = bytes.try_into().unwrap();
274 assert_eq!(structured.seq, 0);
275 assert_eq!(structured.total, 1);
276 assert_eq!(structured.parity, 57);
277 assert_eq!(structured.content, content);
278
279 let bytes = hex::decode(SECOND).unwrap();
280 let content = hex::decode(SECOND_CONTENT).unwrap();
281 let structured_2: StructuredQr = bytes.try_into().unwrap();
282 assert_eq!(structured_2.seq, 1);
283 assert_eq!(structured_2.total, 1);
284 assert_eq!(structured_2.parity, 57);
285 assert_eq!(structured_2.content, content);
286 }
287
288 #[test]
test_merge()289 fn test_merge() {
290 let first = hex::decode(FIRST).unwrap();
291 let second = hex::decode(SECOND).unwrap();
292 let full_content = hex::decode(FULL_CONTENT).unwrap();
293 let vec = vec![first.clone(), second.clone()];
294 let result = merge_qrs(vec).unwrap();
295 assert_eq!(hex::encode(result), FULL_CONTENT);
296
297 let vec = vec![second.clone(), first.clone()];
298 let result = merge_qrs(vec).unwrap(); //merge out of order
299 assert_eq!(hex::encode(result), FULL_CONTENT);
300
301 let vec = vec![second.clone(), first.clone(), second.clone()];
302 let result = merge_qrs(vec).unwrap(); //merge duplicates
303 assert_eq!(hex::encode(result), FULL_CONTENT);
304
305 let vec = vec![first.clone(), first.clone()];
306 let result = merge_qrs(vec);
307 assert_eq!(result.unwrap_err().to_string(), AtLeast2Pieces.to_string());
308
309 let vec = vec![first.clone(), full_content];
310 let result = merge_qrs(vec);
311 assert_eq!(
312 result.unwrap_err().to_string(),
313 StructuredWrongMode.to_string()
314 );
315
316 let mut first_mut = first.clone();
317 first_mut[15] = 14u8;
318 let vec = vec![first.clone(), first_mut.clone()];
319 let result = merge_qrs(vec);
320 assert_eq!(result.unwrap_err().to_string(), MissingParts.to_string());
321
322 let vec = vec![first, first_mut.clone(), second];
323 let result = merge_qrs(vec);
324 assert_eq!(
325 result.unwrap_err().to_string(),
326 TotalMismatch(3).to_string(),
327 );
328 }
329
330 #[test]
test_structured_append()331 fn test_structured_append() {
332 let data = "I read the news today oh boy".as_bytes();
333 let data_half = "I read the new".as_bytes();
334 let parity = data.iter().fold(0u8, |acc, &x| acc ^ x);
335 let mut bits = Bits::new(Version::Normal(1));
336 bits.push_mode_indicator(ExtendedMode::StructuredAppend)
337 .unwrap();
338 bits.push_number_checked(4, 0).unwrap(); // first element of the sequence
339 bits.push_number_checked(4, 1).unwrap(); // total length of the sequence (means 2)
340 bits.push_number_checked(8, parity as usize).unwrap(); //parity of the complete data
341 bits.push_byte_data(data_half).unwrap();
342 bits.push_terminator(LEVEL).unwrap();
343 assert_eq!(
344 hex::encode(bits.into_bytes()),
345 "3013940e49207265616420746865206e657700"
346 ); // raw bytes of the first qr code of the example
347 }
348
349 #[test]
test_split_merge_qr()350 fn test_split_merge_qr() {
351 // consider using https://rust-fuzz.github.io/book/introduction.html
352 let mut rng = rand::thread_rng();
353 let random_bytes: Vec<u8> = (0..4000).map(|_| rand::random::<u8>()).collect();
354 for _ in 0..1_000 {
355 let len = rng.gen_range(100, 4000);
356 let ver = rng.gen_range(10, 20);
357 let data = random_bytes[0..len].to_vec();
358 let split_qr = SplittedQr::new(data.clone(), ver).unwrap();
359 let bits = split_qr.split_to_bits().unwrap();
360 if bits.len() > 1 {
361 let bytes: Vec<Vec<u8>> = bits.into_iter().map(|b| b.into_bytes()).collect();
362 let result = merge_qrs(bytes).unwrap();
363 assert_eq!(result, data);
364 }
365 }
366 }
367
368 #[test]
test_print_qr()369 fn test_print_qr() {
370 let qr = QrCode::new(b"01234567").unwrap();
371 let printed = qr.to_string(false, 3);
372 assert_eq!(hex::encode(printed.as_bytes()),"2020202020202020202020202020202020202020202020202020200a2020202020202020202020202020202020202020202020202020200a202020e29688e29680e29680e29680e29680e29680e296882020e29688e29684e29688e2968820e29688e29680e29680e29680e29680e29680e296882020200a202020e2968820e29688e29688e2968820e2968820e29688e2968420202020e2968820e29688e29688e2968820e296882020200a202020e2968820e29680e29680e2968020e2968820e2968820e29680e29680e2968820e2968820e29680e29680e2968020e296882020200a202020e29680e29680e29680e29680e29680e29680e2968020e2968820e29680e29684e2968820e29680e29680e29680e29680e29680e29680e296802020200a202020e2968020e29680e29688e29680e29688e29680e29684e29684e29680e2968420e2968820e29680e29688e29680e29688e2968820202020200a2020202020e2968020e2968420e29680e2968020e2968820e2968020e2968020e29684e29688e29688e29688e29680e296802020200a202020202020e29680e29680e29680e29680e29680e2968820e29684e29688e29684e29688e2968420e29680e29684e2968420202020200a202020e29688e29680e29680e29680e29680e29680e2968820e29684e29680e29688e29684e29688e29684e29688e296802020e2968420e296842020200a202020e2968820e29688e29688e2968820e2968820e29688e296842020e296882020e2968820e29680e2968020202020200a202020e2968820e29680e29680e2968020e2968820e2968020e29680e2968020e2968020e29684e2968820e29688e29684202020200a202020e29680e29680e29680e29680e29680e29680e2968020e29680e29680e29680e2968020e296802020e2968020e2968020202020200a2020202020202020202020202020202020202020202020202020200a0a");
373 }
374 }
375