Skip to main content

co_storage/crypto/
block.rs

1// SPDX-License-Identifier: AGPL-3.0-only
2// Copyright (C) 2026 1io BRANDGUARDIAN GmbH
3
4use super::secret::Secret;
5use aead::{generic_array::typenum::Unsigned, KeySizeUser, Payload};
6use chacha20poly1305::{
7	aead::{Aead, AeadCore, KeyInit, OsRng},
8	Key, XChaCha20Poly1305,
9};
10use cid::Cid;
11use co_primitives::{from_cbor, to_cbor, Block, KnownMultiCodec, MultiCodec, MultiCodecError};
12use derive_more::From;
13use multihash_codetable::{Code, MultihashDigest};
14use serde::{Deserialize, Serialize};
15use serde_repr::{Deserialize_repr, Serialize_repr};
16use std::{cmp::min, collections::BTreeMap, fmt::Debug, mem::take};
17
18/// blake3 KDF context for derive block keys from versioned co encryption key
19///
20/// [application] [commit timestamp] [purpose]", e.g., "example.com 2019-12-25 16:18:03 session tokens v1
21pub const BLOCK_KEY_DERIVATION: &str = "co 2023-10-24T10:25:23Z block key derivation v1";
22pub const BLOCK_DERIVATION: &str = "co 2023-10-26T14:31:38Z block derivation v1";
23pub const BLOCK_MULTICODEC: u64 = KnownMultiCodec::CoEncryptedBlock as u64;
24
25/// Nonce.
26pub type Nonce = Vec<u8>;
27/// Salt.
28pub type Salt = Vec<u8>;
29/// Cipher octet (encrypted).
30pub type CipherU8 = u8;
31
32#[derive(Debug, thiserror::Error)]
33pub enum AlgorithmError {
34	#[error("Generic Cipher Error")]
35	Cipher,
36
37	#[error("Invalid arguments specified")]
38	InvalidArguments(#[source] anyhow::Error),
39
40	#[error("Generic decoding error")]
41	Decoding,
42
43	#[error("Generic encoding error")]
44	Encoding,
45
46	#[error("Size is to large")]
47	Size,
48}
49impl From<aead::Error> for AlgorithmError {
50	fn from(_: aead::Error) -> Self {
51		AlgorithmError::Cipher
52	}
53}
54impl From<MultiCodecError> for AlgorithmError {
55	fn from(value: MultiCodecError) -> Self {
56		AlgorithmError::InvalidArguments(value.into())
57	}
58}
59
60#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq)]
61#[repr(u8)]
62#[derive(Default)]
63pub enum Algorithm {
64	#[default]
65	XChaCha20Poly1305 = 1,
66}
67impl Algorithm {
68	/// Cipher algorithm key size in bytes.
69	pub fn key_size(&self) -> usize {
70		match self {
71			Algorithm::XChaCha20Poly1305 => XChaCha20Poly1305::key_size(),
72		}
73	}
74
75	/// Cipher algorithm nonce size in bytes.
76	pub fn nonce_size(&self) -> usize {
77		match self {
78			Algorithm::XChaCha20Poly1305 => <XChaCha20Poly1305 as AeadCore>::NonceSize::USIZE,
79		}
80	}
81
82	/// Cipher algorithm tag size in bytes.
83	pub fn tag_size(&self) -> usize {
84		match self {
85			Algorithm::XChaCha20Poly1305 => <XChaCha20Poly1305 as AeadCore>::TagSize::USIZE,
86		}
87	}
88
89	/// Generate a random secret key suitable for the cipher algorithm.
90	pub fn generate_serect(&self) -> Secret {
91		match self {
92			Algorithm::XChaCha20Poly1305 => Secret::new(XChaCha20Poly1305::generate_key(&mut OsRng).to_vec()),
93		}
94	}
95
96	/// Generate a random nonce suitable for the cipher algorithm.
97	pub fn generate_nonce(&self) -> Nonce {
98		match self {
99			Algorithm::XChaCha20Poly1305 => XChaCha20Poly1305::generate_nonce(&mut OsRng).to_vec(),
100		}
101	}
102
103	/// Encrypt single buffer of data.
104	pub fn encrypt(
105		&self,
106		secret: &Secret,
107		nonce: &Nonce,
108		plaintext: &[u8],
109		aad: &[u8],
110	) -> Result<Vec<u8>, AlgorithmError> {
111		// validate
112		if self.nonce_size() != nonce.len() {
113			return Err(AlgorithmError::InvalidArguments(anyhow::anyhow!("nonce size")));
114		}
115		if self.key_size() != secret.divulge().len() {
116			return Err(AlgorithmError::InvalidArguments(anyhow::anyhow!("key size")));
117		}
118
119		// encrypt
120		match self {
121			Algorithm::XChaCha20Poly1305 => {
122				let cipher = XChaCha20Poly1305::new(Key::from_slice(secret.divulge()));
123				let payload = Payload { msg: plaintext, aad };
124				cipher
125					.encrypt(aead::Nonce::<XChaCha20Poly1305>::from_slice(nonce.as_slice()), payload)
126					.map_err(|e| e.into())
127			},
128		}
129	}
130
131	/// Decrypt single buffer of data.
132	pub fn decrypt(
133		&self,
134		secret: &Secret,
135		nonce: &Nonce,
136		ciphertext: &[CipherU8],
137		aad: &[u8],
138	) -> Result<Vec<u8>, AlgorithmError> {
139		// validate
140		if self.nonce_size() != nonce.len() {
141			return Err(AlgorithmError::InvalidArguments(anyhow::anyhow!("nonce size")));
142		}
143		if self.key_size() != secret.divulge().len() {
144			return Err(AlgorithmError::InvalidArguments(anyhow::anyhow!("key size")));
145		}
146
147		// decrypt
148		match self {
149			Algorithm::XChaCha20Poly1305 => {
150				let cipher = XChaCha20Poly1305::new(Key::from_slice(secret.divulge()));
151				let payload = Payload { msg: ciphertext, aad };
152				cipher
153					.decrypt(aead::Nonce::<XChaCha20Poly1305>::from_slice(nonce.as_slice()), payload)
154					.map_err(|e| e.into())
155			},
156		}
157	}
158}
159
160#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq)]
161#[repr(u8)]
162pub enum EncryptionVersion {
163	V1 = 1,
164}
165
166/// Encrypted Block.
167#[derive(Debug, Clone, Serialize, Deserialize)]
168pub struct EncryptedBlock {
169	/// Encryption header for payload.
170	#[serde(rename = "h")]
171	pub header: Header,
172
173	/// Encrypted [`BlockPayload`].
174	#[serde(rename = "d")]
175	pub payload: EncryptedData,
176}
177impl EncryptedBlock {
178	/// Encrypt block (automatically generate block secret).
179	pub fn encrypt(
180		algorithm: Algorithm,
181		secret: &Secret,
182		block: impl Into<BlockPayload>,
183	) -> Result<EncryptedBlock, AlgorithmError> {
184		let block_secret = algorithm.generate_serect();
185		Self::encrypt_with_block_secret(algorithm, secret, &block_secret, block)
186	}
187
188	/// Encrypt block with custom block secret.
189	pub fn encrypt_with_block_secret(
190		algorithm: Algorithm,
191		secret: &Secret,
192		block_secret: &Secret,
193		block: impl Into<BlockPayload>,
194	) -> Result<EncryptedBlock, AlgorithmError> {
195		let block: BlockPayload = block.into();
196
197		// derive data key
198		let data_secret = block_secret.derive_serect(BLOCK_DERIVATION);
199
200		// header
201		let key_slot = KeySlot::new(algorithm, secret, block_secret)?;
202		let header = Header::new(algorithm, vec![key_slot]);
203
204		// result
205		let aad = header.aad();
206
207		// data
208		let data = block.to_bytes().map_err(|_e| AlgorithmError::Encoding)?;
209
210		// encrypt
211		Ok(Self {
212			payload: header
213				.algorithm
214				.encrypt(&data_secret, &header.nonce, data.as_slice(), aad.as_slice())?
215				.into(),
216			header,
217		})
218	}
219
220	/// Get decrypted block.
221	pub fn block(&self, secret: &Secret) -> Result<BlockPayload, AlgorithmError> {
222		let block_secret = self
223			.header
224			.block_secret(secret)
225			.ok_or(AlgorithmError::InvalidArguments(anyhow::anyhow!("key")))?;
226		let aad = self.header.aad();
227		let data = self
228			.payload
229			.inline()
230			.ok_or(AlgorithmError::InvalidArguments(anyhow::anyhow!("Expected inline data")))?;
231		let data_plain = self.decrypt_data(&block_secret, data, &aad)?;
232		from_cbor(&data_plain).map_err(|err| AlgorithmError::InvalidArguments(err.into()))
233	}
234
235	fn decrypt_data(&self, block_secret: &Secret, data: &[u8], aad: &[u8]) -> Result<Vec<u8>, AlgorithmError> {
236		let data_secret = block_secret.derive_serect(BLOCK_DERIVATION);
237		let data = self.header.algorithm.decrypt(&data_secret, &self.header.nonce, data, aad)?;
238		Ok(data)
239	}
240
241	/// Test is encrypted block is valid.
242	pub fn is_valid(&self) -> bool {
243		self.header.is_valid()
244	}
245}
246impl TryInto<Block> for EncryptedBlock {
247	type Error = AlgorithmError;
248
249	/// Convert to encrypted Block.
250	fn try_into(self) -> Result<Block, Self::Error> {
251		let encrypted_data = to_cbor(&self).map_err(|_| AlgorithmError::Encoding)?;
252		let mh = Code::Blake3_256.digest(&encrypted_data);
253		let cid = Cid::new_v1(KnownMultiCodec::CoEncryptedBlock.into(), mh);
254		Ok(Block::new_unchecked(cid, encrypted_data))
255	}
256}
257impl TryFrom<Block> for EncryptedBlock {
258	type Error = AlgorithmError;
259
260	/// Convert from encrypted Block.
261	fn try_from(value: Block) -> Result<Self, Self::Error> {
262		// validate
263		MultiCodec::with_codec(KnownMultiCodec::CoEncryptedBlock, value.cid())?;
264
265		// decode
266		let block: EncryptedBlock = from_cbor(value.data()).map_err(|_| AlgorithmError::Decoding)?;
267
268		// validate
269		if !block.is_valid() {
270			return Err(AlgorithmError::Decoding);
271		}
272
273		// result
274		Ok(block)
275	}
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize, From)]
279#[serde(untagged)]
280pub enum EncryptedData {
281	/// Inline cipher text.
282	#[from]
283	#[serde(with = "serde_bytes")]
284	Inline(Vec<CipherU8>),
285
286	/// Referenced cipher text.
287	/// Expected to be [`KnownMultiCodec::Raw`].
288	#[from]
289	Block(Vec<Cid>),
290}
291impl EncryptedData {
292	pub fn inline(&self) -> Option<&[u8]> {
293		match self {
294			Self::Inline(data) => Some(data),
295			_ => None,
296		}
297	}
298
299	pub fn blocks(&self) -> Option<&[Cid]> {
300		match self {
301			Self::Block(data) => Some(data),
302			_ => None,
303		}
304	}
305
306	/// Fit [`EncryptedData`] into blocks.
307	///
308	/// If the [`EncryptedData`] doesn't fit into one block it will we splitted accordingly.
309	/// This method should return at max 3 blocks.
310	///
311	/// # Returns
312	/// The extra blocks or an empty Vec if it fits inline.
313	pub fn fit_into_blocks(&mut self, max_block_size: usize, inline_offset: Option<usize>) -> Vec<Block> {
314		let mut data = match self {
315			Self::Inline(data) => {
316				if max_block_size >= data.len() + inline_offset.unwrap_or(0) {
317					return vec![];
318				} else {
319					take(data)
320				}
321			},
322			Self::Block(_) => {
323				return vec![];
324			},
325		};
326		let mut extra_blocks = Vec::new();
327		while !data.is_empty() {
328			let rest = data.split_off(min(data.len(), max_block_size));
329			extra_blocks.push(Block::new_data(KnownMultiCodec::Raw, data));
330			data = rest;
331		}
332		*self = Self::Block(extra_blocks.iter().map(|block| *block.cid()).collect());
333		extra_blocks
334	}
335
336	/// Try to inline using blocks. Returns Err if not possible because blocks are missing.
337	pub fn try_inline_blocks(&mut self, blocks: impl IntoIterator<Item = (Cid, Vec<u8>)>) -> Result<(), ()> {
338		match self {
339			Self::Inline(_) => Ok(()),
340			Self::Block(cids) => {
341				let mut blocks: BTreeMap<Cid, Vec<u8>> = blocks.into_iter().collect();
342				if !cids.iter().all(|cid| blocks.contains_key(cid)) {
343					return Err(());
344				}
345				let mut inline = Vec::new();
346				for cid in cids {
347					if let Some(mut block) = blocks.remove(cid) {
348						inline.append(&mut block);
349					} else {
350						return Err(());
351					}
352				}
353				*self = Self::Inline(inline);
354				Ok(())
355			},
356		}
357	}
358}
359
360/// Combines reference mappings and data into one structure.
361/// The max size of this `MAX_BLOCK_SIZE * 3`.
362#[derive(Debug, Clone, Serialize, Deserialize)]
363pub struct BlockPayload {
364	/// The block [`Cid`].
365	#[serde(rename = "c")]
366	pub cid: Cid,
367
368	/// Optionally maps block references from unencrypted Cid (key) to encrypted Cid (value).
369	/// When a mapping exists it is assumed to contain all links that are still resolvable.
370	#[serde(rename = "r", default, skip_serializing_if = "BTreeMap::is_empty")]
371	pub references: BTreeMap<Cid, Cid>,
372
373	/// The block data.
374	#[serde(with = "serde_bytes", rename = "d")]
375	pub data: Vec<u8>,
376}
377impl BlockPayload {
378	/// Returns the cid.
379	pub fn cid(&self) -> &Cid {
380		&self.cid
381	}
382
383	// /// Returns the payload.
384	// pub fn data(&self) -> &[u8] {
385	// 	&self.data
386	// }
387
388	/// Create plain bytes which contains the [`BlockPayload`] as DAG-CBOR.
389	pub fn to_bytes(&self) -> Result<Vec<u8>, anyhow::Error> {
390		Ok(to_cbor(self)?)
391	}
392}
393impl From<Block> for BlockPayload {
394	fn from(value: Block) -> Self {
395		let (cid, data) = value.into_inner();
396		Self { cid, data, references: Default::default() }
397	}
398}
399impl From<BlockPayload> for Block {
400	fn from(value: BlockPayload) -> Self {
401		Block::new_unchecked(value.cid, value.data)
402	}
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
406pub struct Header {
407	/// Version.
408	#[serde(rename = "v")]
409	pub version: EncryptionVersion,
410
411	/// Encryption algorithm for payload.
412	#[serde(rename = "a")]
413	pub algorithm: Algorithm,
414
415	/// Keyslots for payload.
416	#[serde(rename = "k")]
417	pub key_slots: Vec<KeySlot>,
418
419	/// Encryption nonce (iv) used for payload.
420	#[serde(rename = "n", with = "serde_bytes")]
421	pub nonce: Nonce,
422}
423impl Header {
424	pub fn new(algorithm: Algorithm, key_slots: Vec<KeySlot>) -> Self {
425		Self { version: EncryptionVersion::V1, algorithm, nonce: algorithm.generate_nonce(), key_slots }
426	}
427
428	/// Test if header is valid.
429	pub fn is_valid(&self) -> bool {
430		self.version == EncryptionVersion::V1
431			&& self.nonce.len() == self.algorithm.nonce_size()
432			&& self.key_slots.iter().all(KeySlot::is_valid)
433	}
434
435	/// Get AAD bytes for this header.
436	pub fn aad(&self) -> Vec<u8> {
437		let mut result = Vec::with_capacity(1 + 1 + self.nonce.len());
438		result.extend([self.version as u8, self.algorithm as u8].iter());
439		result.extend(self.nonce.iter());
440		// let i = ipld!({
441		// 	"v": self.version as usize,
442		// 	"a": self.algorithm as usize,
443		// 	"n": self.nonce.clone(),
444		// });
445		// DagCborCodec.encode(&i).unwrap().to_vec()
446		result
447	}
448
449	/// Get block secret fot given secret.
450	pub fn block_secret(&self, secret: &Secret) -> Option<Secret> {
451		self.key_slots
452			.iter()
453			.map(|key_slot| key_slot.block_secret(secret))
454			.filter_map(|r| r.ok())
455			.next()
456	}
457
458	/// Calulate CBOR header encoded size with single key slot.
459	///
460	/// XChaCha20Poly1305: 153
461	pub fn encoded_size(algorithm: Algorithm) -> usize {
462		let field_size = 1;
463		let cbor_size = 1;
464		cbor_size
465			// version
466			+ 1 + field_size + cbor_size
467			// algorithm
468			+ 1 + field_size + cbor_size
469			// key_slots
470			+ KeySlot::encoded_size(algorithm) + field_size + cbor_size + cbor_size
471			// nonce
472			+ algorithm.nonce_size() + field_size + cbor_size + cbor_size + cbor_size
473	}
474}
475// impl Into<Vec<u8>> for Header {
476// 	fn into(self) -> Vec<u8> {
477// 		let mut result = Vec::with_capacity(1 + 1 + (1 + 1 + 32 + 24 + 24) + 24);
478// 		result.extend([self.version as u8, self.algorithm as u8].iter());
479// 		result.extend([self.key_slots.len() as u8].iter());
480// 		result.extend(self.key_slots.into_iter().map(|k| Into::<Vec<u8>>::into(k)).flatten());
481// 		result.extend([self.nonce.len() as u8].iter());
482// 		result.extend(self.nonce.into_iter());
483// 		result
484// 	}
485// }
486
487#[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq)]
488#[repr(u8)]
489pub enum KeySlotVersion {
490	/// Key slot version 1.
491	///
492	/// Key Derivation: blake3
493	V1 = 1,
494}
495
496#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
497pub struct KeySlot {
498	/// Key slot version.
499	#[serde(rename = "v")]
500	pub version: KeySlotVersion,
501
502	/// The algorithm used to encrypt the key.
503	#[serde(rename = "a")]
504	pub algorithm: Algorithm,
505
506	/// Encrypted master key.
507	/// The key encryption key is derived from the master key.
508	/// Key Derivation Hash: blake3
509	#[serde(rename = "k", with = "serde_bytes")]
510	pub key: Vec<CipherU8>,
511
512	/// Key derivation salt.
513	#[serde(rename = "s", with = "serde_bytes")]
514	pub salt: Salt,
515
516	/// Key enctyion nonce.
517	#[serde(rename = "n", with = "serde_bytes")]
518	pub nonce: Nonce,
519}
520impl KeySlot {
521	/// Calulate CBOR encoded size.
522	///
523	/// XChaCha20Poly1305: 116
524	pub fn encoded_size(algorithm: Algorithm) -> usize {
525		let tag_size = algorithm.tag_size();
526		let field_size = 1;
527		let cbor_size = 1;
528		cbor_size
529			// version
530			+ 1 + field_size + cbor_size
531			// algorithm
532			+ 1 + field_size + cbor_size
533			// key
534			+ algorithm.key_size() + field_size + tag_size + cbor_size + cbor_size + cbor_size
535			// salt
536			+ algorithm.nonce_size() + field_size + cbor_size + cbor_size + cbor_size
537			// nonce
538			+ algorithm.nonce_size() + field_size + cbor_size + cbor_size + cbor_size
539	}
540
541	/// Create new key slot using the CO Key (serect) and a generated block secret (may reused).
542	pub fn new(algorithm: Algorithm, secret: &Secret, block_secret: &Secret) -> Result<Self, AlgorithmError> {
543		let salt = algorithm.generate_nonce(); // TODO: needs specific size?
544		let secret_derived = secret.derive_serect_with_salt(BLOCK_KEY_DERIVATION, &salt);
545		let nonce = algorithm.generate_nonce();
546		let block_secret_encrypted = algorithm.encrypt(&secret_derived, &nonce, block_secret.divulge(), b"")?;
547		Ok(Self { version: KeySlotVersion::V1, algorithm, key: block_secret_encrypted, nonce, salt })
548	}
549
550	/// Test if is keyslot is valid.
551	pub fn is_valid(&self) -> bool {
552		self.version == KeySlotVersion::V1
553			&& self.key.len() == self.algorithm.key_size() + self.algorithm.tag_size()
554			&& self.nonce.len() == self.algorithm.nonce_size()
555	}
556
557	/// Get block secret from key slot.
558	pub fn block_secret(&self, secret: &Secret) -> Result<Secret, AlgorithmError> {
559		let secret_derived = secret.derive_serect_with_salt(BLOCK_KEY_DERIVATION, &self.salt);
560		let block_secret = self.algorithm.decrypt(&secret_derived, &self.nonce, self.key.as_slice(), b"")?;
561		Ok(Secret::new(block_secret))
562	}
563}
564// impl Into<Vec<u8>> for KeySlot {
565// 	fn into(self) -> Vec<u8> {
566// 		let mut result = Vec::with_capacity(1 + 1 + (1 + 32) + (1 + 24) + (1 + 24));
567// 		result.extend([self.version as u8, self.algorithm as u8].iter());
568// 		result.extend([self.key.len() as u8].iter());
569// 		result.extend(self.key.into_iter());
570// 		result.extend([self.salt.len() as u8].iter());
571// 		result.extend(self.salt.into_iter());
572// 		result.extend([self.nonce.len() as u8].iter());
573// 		result.extend(self.nonce.into_iter());
574// 		result
575// 	}
576// }
577
578#[cfg(test)]
579mod tests {
580	use super::{Algorithm, EncryptedBlock, Header, KeySlot};
581	use crate::crypto::{block::EncryptedData, secret::Secret};
582	use cid::Cid;
583	use co_primitives::{from_cbor, to_cbor, Block, BlockSerializer, DefaultParams, KnownMultiCodec, StoreParams};
584	use std::iter::repeat_n;
585
586	#[test]
587	fn algorithm_key_size() {
588		assert_eq!(Algorithm::XChaCha20Poly1305.key_size(), 32);
589	}
590
591	#[test]
592	fn algorithm_nonce_size() {
593		assert_eq!(Algorithm::XChaCha20Poly1305.nonce_size(), 24);
594	}
595
596	#[test]
597	fn is_valid() {
598		let secret = Secret::new(repeat_n(0u8, Algorithm::default().key_size()).collect());
599		let block_secret = Secret::new(repeat_n(1u8, Algorithm::default().key_size()).collect());
600		let key_slot = KeySlot::new(Algorithm::default(), &secret, &block_secret).unwrap();
601		let header = Header::new(Algorithm::default(), vec![key_slot]);
602		assert!(header.is_valid());
603	}
604
605	#[test]
606	fn serialize_header() {
607		let secret = Secret::new(repeat_n(0u8, Algorithm::default().key_size()).collect());
608		let block_secret = Secret::new(repeat_n(1u8, Algorithm::default().key_size()).collect());
609		let key_slot = KeySlot::new(Algorithm::default(), &secret, &block_secret).unwrap();
610		let header = Header::new(Algorithm::default(), vec![key_slot]);
611
612		// serialize header
613		let bytes = to_cbor(&header).unwrap();
614		// println!("{:?}", header);
615		// let raw_bytes = Into::<Vec<u8>>::into(header.clone());
616		// println!("raw_bytes: {}", raw_bytes.len()); // 129
617		// println!("bytes: {}", bytes.len()); // 153 (153 - 129 = 24)
618		// hexdump::hexdump(Into::<Vec<u8>>::into(header.clone()).as_slice());
619		// println!("key");
620		// hexdump::hexdump(header.key_slots[0].key.as_slice());
621		// println!("key salt");
622		// hexdump::hexdump(header.key_slots[0].salt.as_slice());
623		// println!("key nonce");
624		// hexdump::hexdump(header.key_slots[0].nonce.as_slice());
625		// println!("nonce");
626		// hexdump::hexdump(header.nonce.as_slice());
627		// println!("bytes");
628		// hexdump::hexdump(bytes.as_slice());
629		assert_eq!(bytes.len(), 153);
630
631		// deserialize
632		let header_deserialized: Header = from_cbor(bytes.as_slice()).unwrap();
633		assert_eq!(header_deserialized, header);
634		assert!(header.is_valid());
635	}
636
637	#[test]
638	fn key_slot_encoded_size() {
639		let secret = Secret::new(repeat_n(0u8, Algorithm::default().key_size()).collect());
640		let block_secret = Secret::new(repeat_n(1u8, Algorithm::default().key_size()).collect());
641		let key_slot = KeySlot::new(Algorithm::default(), &secret, &block_secret).unwrap();
642
643		// serialize header
644		let bytes = to_cbor(&key_slot).unwrap();
645		//hexdump::hexdump(bytes.as_slice());
646		assert_eq!(bytes.len(), KeySlot::encoded_size(Algorithm::default()));
647	}
648
649	#[test]
650	fn header_encoded_size() {
651		let secret = Secret::new(repeat_n(0u8, Algorithm::default().key_size()).collect());
652		let block_secret = Secret::new(repeat_n(1u8, Algorithm::default().key_size()).collect());
653		let key_slot = KeySlot::new(Algorithm::default(), &secret, &block_secret).unwrap();
654		let header = Header::new(Algorithm::default(), vec![key_slot]);
655
656		// serialize header
657		let bytes = to_cbor(&header).unwrap();
658		//hexdump::hexdump(bytes.as_slice());
659		assert_eq!(bytes.len(), Header::encoded_size(Algorithm::default()));
660	}
661
662	#[test]
663	fn encrypt_block_roundtrip() {
664		let secret = Secret::new(repeat_n(0u8, Algorithm::default().key_size()).collect());
665		let block = BlockSerializer::default().serialize(&"Hello World!").unwrap();
666
667		//println!("cid: ({}): {}", block.cid().to_bytes().len(), block.cid()); // 36
668		//println!("data: ({}): {:?}", block.data().len(), block.data()); // 13
669
670		// encrypt
671		let encrypted_block = EncryptedBlock::encrypt(Algorithm::default(), &secret, block.clone()).unwrap();
672		assert_ne!(encrypted_block.payload.inline().unwrap(), block.data());
673		//println!("cid: ({}): {:?}", encrypted_block.cid.len(), encrypted_block.cid); // 52 = 36 + 16
674		// println!("data: ({}): {:?}", encrypted_block.payload.inline().unwrap().len(), encrypted_block.payload); // 76
675
676		// serialize
677		let encrypted_block_bytes = to_cbor(&encrypted_block).unwrap();
678		// cbor (7), header (153), payload+tag (76)
679		assert_eq!(encrypted_block_bytes.len(), 236);
680		//println!("length: {}", encrypted_block_bytes.len());
681		//hexdump::hexdump(&encrypted_block_bytes);
682
683		// deserialize
684		let encrypted_block_deserialized: EncryptedBlock = from_cbor(&encrypted_block_bytes).unwrap();
685
686		// decrypt
687		let decrypted_block = encrypted_block_deserialized.block(&secret).unwrap();
688		assert_eq!(decrypted_block.cid(), block.cid());
689		assert_eq!(&decrypted_block.data, block.data());
690	}
691
692	#[test]
693	fn test_fit_to_blocks() {
694		let secret = Secret::new(repeat_n(0u8, Algorithm::default().key_size()).collect());
695		let data: Vec<u8> = repeat_n(0u8, DefaultParams::MAX_BLOCK_SIZE).collect();
696		let block = Block::new_data(KnownMultiCodec::Raw, data);
697
698		//println!("cid: ({}): {}", block.cid().to_bytes().len(), block.cid()); // 36
699		//println!("data: ({}): {:?}", block.data().len(), block.data()); // 13
700
701		// encrypt
702		let mut encrypted_block = EncryptedBlock::encrypt(Algorithm::default(), &secret, block.clone()).unwrap();
703
704		// split
705		let encrypted_extra_blocks = encrypted_block
706			.payload
707			.fit_into_blocks(DefaultParams::MAX_BLOCK_SIZE, Some(Header::encoded_size(Algorithm::default())));
708		assert!(match &encrypted_block.payload {
709			EncryptedData::Block(blocks) =>
710				blocks == &encrypted_extra_blocks.iter().map(|b| *b.cid()).collect::<Vec<Cid>>(),
711			_ => false,
712		});
713
714		// inline
715		encrypted_block
716			.payload
717			.try_inline_blocks(encrypted_extra_blocks.into_iter().map(|v| v.into_inner()))
718			.unwrap();
719
720		// decrypt
721		let decrypted_block = encrypted_block.block(&secret).unwrap();
722		assert_eq!(decrypted_block.cid(), block.cid());
723		assert_eq!(&decrypted_block.data, block.data());
724	}
725}