px_native/cipher/
splice.rs1use px_errors::AppError;
22
23pub fn v_q(salt: &[u8], payload: &[u8], offsets: &[i64]) -> Result<Vec<u8>, AppError> {
28 if offsets.len() < salt.len() {
29 return Err(AppError::InternalError(format!(
30 "vQ: need {} offsets for salt, got {}",
31 salt.len(),
32 offsets.len()
33 )));
34 }
35 let mut bd: Vec<u8> = Vec::with_capacity(payload.len() + salt.len());
36 let mut be: i64 = 0;
37 let plen = payload.len();
38 for (bg, salt_byte) in salt.iter().enumerate() {
39 let cut = offsets[bg] - (bg as i64) - 1;
40 bd.extend_from_slice(substring(payload, be, cut, plen));
41 bd.push(*salt_byte);
42 be = cut;
43 }
44 let plen_i = plen as i64;
45 bd.extend_from_slice(substring(payload, be, plen_i, plen));
46 Ok(bd)
47}
48
49fn substring(buf: &[u8], start: i64, end: i64, len: usize) -> &[u8] {
51 let len_i = len as i64;
52 let mut s = start.clamp(0, len_i);
53 let mut e = end.clamp(0, len_i);
54 if s > e {
55 std::mem::swap(&mut s, &mut e);
56 }
57 &buf[s as usize..e as usize]
58}
59
60#[cfg(test)]
61#[allow(clippy::expect_used)]
62mod tests {
63 use super::*;
64 use crate::cipher::offsets::v_n;
65
66 #[test]
67 fn round_trip_against_offsets_prng() {
68 let salt = b"tag";
69 let payload = b"AAAAAAAAAA";
70 let offsets = v_n(payload.len(), payload.len(), b"seed");
71 let out = v_q(salt, payload, &offsets).expect("splice");
72 assert_eq!(out.len(), payload.len() + salt.len());
73 }
74
75 #[test]
76 fn empty_salt_returns_payload() {
77 let out = v_q(b"", b"abcdef", &[]).expect("splice");
78 assert_eq!(out, b"abcdef");
79 }
80}