Skip to content

Commit 19ad8bc

Browse files
committed
Added encoding and decoding with padding
1 parent ea05f0b commit 19ad8bc

File tree

3 files changed

+436
-1
lines changed

3 files changed

+436
-1
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,4 +154,4 @@ dist
154154
.idea/
155155

156156
# OS specific
157-
.DS_Store/
157+
.DS_Store

src/lib/high_level/data_types.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use crate::low_level::elgamal::{ElGamal, ELGAMAL_LENGTH};
66
use derive_more::{Deref, From};
77
use rand_core::{CryptoRng, RngCore};
88
use serde::{Deserialize, Deserializer, Serialize, Serializer};
9+
use std::io::{Error, ErrorKind};
910

1011
/// A pseudonym (in the background, this is a [`GroupElement`]) that can be used to identify a user
1112
/// within a specific context, which can be encrypted, rekeyed and reshuffled.
@@ -196,6 +197,115 @@ pub trait Encryptable {
196197
fn as_bytes(&self) -> Option<[u8; 16]> {
197198
self.value().encode_lizard()
198199
}
200+
201+
/// Encodes an arbitrary byte array into one or more Encryptables
202+
/// Uses PKCS#7 style padding where the padding byte value equals the number of padding bytes
203+
fn from_bytes_padded(data: &[u8]) -> Vec<Self>
204+
where
205+
Self: Sized,
206+
{
207+
if data.is_empty() {
208+
return vec![];
209+
}
210+
211+
let mut result = Vec::new();
212+
213+
// Process all full blocks, that do not need padding
214+
// Initialize the last block with the padding value
215+
// Copy remaining data if there is any
216+
for i in 0..(data.len() / 16) {
217+
let start = i * 16;
218+
// This is safe, as we know that the slice is 16 bytes long
219+
result.push(Self::from_bytes(
220+
&data[start..start + 16].try_into().unwrap(),
221+
));
222+
}
223+
224+
let remaining = data.len() % 16;
225+
let padding_byte = (16 - remaining) as u8;
226+
227+
let mut last_block = [padding_byte; 16];
228+
229+
if remaining > 0 {
230+
last_block[..remaining].copy_from_slice(&data[data.len() - remaining..]);
231+
}
232+
233+
result.push(Self::from_bytes(&last_block));
234+
235+
result
236+
}
237+
238+
/// Encodes an arbitrary string into one or more Encrtypb
239+
/// Uses PKCS#7 style padding where the padding byte value equals the number of padding bytes
240+
fn from_string_padded(text: &str) -> Vec<Self>
241+
where
242+
Self: Sized,
243+
{
244+
// Convert string to bytes and pass to the byte encoding function
245+
Self::from_bytes_padded(text.as_bytes())
246+
}
247+
248+
/// Decodes encryptables back to the original string
249+
/// Returns an error if the decoded bytes are not valid UTF-8
250+
fn to_string_padded(encryptables: &[Self]) -> Result<String, Error>
251+
where
252+
Self: Sized,
253+
{
254+
let bytes = Self::to_bytes_padded(encryptables)?;
255+
String::from_utf8(bytes).map_err(|e| Error::new(ErrorKind::InvalidData, e.to_string()))
256+
}
257+
258+
/// Decodes encryptables back to the original byte array
259+
fn to_bytes_padded(encryptables: &[Self]) -> Result<Vec<u8>, Error>
260+
where
261+
Self: Sized,
262+
{
263+
if encryptables.is_empty() {
264+
return Err(Error::new(
265+
ErrorKind::InvalidInput,
266+
"No encryptables provided",
267+
));
268+
}
269+
270+
let mut result = Vec::with_capacity(encryptables.len() * 16);
271+
272+
// Copy over all blocks except the last one
273+
// Validate padding and copy the data part of the last block
274+
// Copy over all blocks except the last one
275+
for data_point in &encryptables[..encryptables.len() - 1] {
276+
let block = data_point.as_bytes().ok_or(Error::new(
277+
ErrorKind::InvalidData,
278+
"Encryptable conversion to bytes failed",
279+
))?;
280+
result.extend_from_slice(&block);
281+
}
282+
283+
// This is safe, we know that there is at least one element in the slice
284+
let last_block = encryptables.last().unwrap().as_bytes().ok_or(Error::new(
285+
ErrorKind::InvalidData,
286+
"Last encryptables conversion to bytes failed",
287+
))?;
288+
289+
let padding_byte = last_block[15];
290+
291+
if padding_byte == 0 || padding_byte > 16 {
292+
return Err(Error::new(ErrorKind::InvalidData, "Invalid padding"));
293+
}
294+
295+
if last_block[16 - padding_byte as usize..]
296+
.iter()
297+
.any(|&b| b != padding_byte)
298+
{
299+
return Err(Error::new(ErrorKind::InvalidData, "Inconsistent padding"));
300+
}
301+
302+
// Add the data part of the last block
303+
let data_bytes = 16 - padding_byte as usize;
304+
result.extend_from_slice(&last_block[..data_bytes]);
305+
306+
Ok(result)
307+
}
308+
199309
/// Create multiple messages from a byte array.
200310
/// TODO: remove this method, as it cannot handle data that is not a multiple of 16 bytes and padding should generally not belong in this library.
201311
#[deprecated]

0 commit comments

Comments
 (0)