vitaminc_aead/ciphertext/
mod.rs1mod read_monads;
2mod write_monads;
3use bytes::Bytes;
4use read_monads::CipherTextReader;
5use serde::{Deserialize, Serialize};
6pub use write_monads::CipherTextBuilder;
7
8pub const WIRE_VERSION: u8 = 1;
22
23#[derive(Debug, Serialize, Deserialize)]
25#[serde(transparent)]
26pub struct LocalCipherText(Bytes);
27
28impl LocalCipherText {
29 pub fn into_inner(self) -> Bytes {
30 self.0
31 }
32
33 pub fn into_reader(self) -> CipherTextReader {
34 CipherTextReader::new(self.0)
35 }
36
37 pub fn wire_version(&self) -> Option<u8> {
44 self.0.first().copied()
45 }
46}
47
48impl AsRef<[u8]> for LocalCipherText {
49 fn as_ref(&self) -> &[u8] {
50 self.0.as_ref()
51 }
52}
53
54impl From<Vec<u8>> for LocalCipherText {
55 fn from(bytes: Vec<u8>) -> Self {
56 LocalCipherText(Bytes::from(bytes))
57 }
58}
59
60#[cfg(test)]
61mod tests {
62 use super::*;
63 use crate::Nonce;
64 use vitaminc_protected::{Controlled, Protected};
65
66 #[test]
67 fn test_ciphertext_builder_with_plaintext_in_place() -> Result<(), ()> {
68 let nonce = Nonce::new([1u8; 12]);
69 let plaintext = vec![0u8; 10];
70 let ciphertext = CipherTextBuilder::new()
71 .append_nonce(nonce)
72 .append_target_plaintext(plaintext)
73 .accepts_ciphertext_and_tag_ok(|mut ciphertext| {
74 ciphertext.copy_from_slice(&[2u8; 10]);
75 ciphertext.extend([3u8; 16]);
76 Ok(ciphertext)
77 })
78 .build()?;
79
80 assert_eq!(ciphertext.0.len(), 39);
82 assert_eq!(ciphertext.0[0], WIRE_VERSION);
83 assert_eq!(ciphertext.wire_version(), Some(WIRE_VERSION));
84 assert_eq!(&ciphertext.0[1..13], &[1u8; 12]);
85 assert_eq!(&ciphertext.0[13..23], &[2u8; 10]);
86 assert_eq!(&ciphertext.0[23..], &[3u8; 16]);
87
88 Ok(())
89 }
90
91 #[test]
92 fn test_ciphertext_reader() -> Result<(), ()> {
93 let nonce = Nonce::new([1u8; 12]);
94 let plaintext: Protected<Vec<u8>> = Protected::new(vec![0u8; 10]);
95
96 let ciphertext = CipherTextBuilder::new()
97 .append_nonce(nonce)
98 .append_target_plaintext(plaintext)
99 .accepts_ciphertext_and_tag_ok(|mut ciphertext| {
100 ciphertext.copy_from_slice(&[2u8; 10]);
101 ciphertext.extend([3u8; 16]);
102 Ok(ciphertext)
103 })
104 .build()?;
105
106 let (nonce, reader) = ciphertext
107 .into_reader()
108 .read_version()
109 .map_err(|_| ())?
110 .read_nonce::<12>()
111 .map_err(|_| ())?;
112
113 let plaintext = reader
114 .accepts_plaintext_ok(|data| {
115 assert_eq!(data.len(), 26);
116 assert_eq!(&data[..10], [2u8; 10]);
117 assert_eq!(&data[10..], [3u8; 16]);
118 data[..10].copy_from_slice(&[0u8; 10]);
120 Ok(10)
121 })
122 .read()?;
123
124 assert_eq!(nonce.into_inner(), [1u8; 12]);
125 assert_eq!(plaintext.risky_unwrap()[..10], vec![0u8; 10]);
126
127 Ok(())
128 }
129
130 #[test]
131 fn wire_version_reads_the_stored_first_byte() {
132 assert_eq!(
135 LocalCipherText::from(vec![7u8, 0, 0]).wire_version(),
136 Some(7)
137 );
138 assert_eq!(
139 LocalCipherText::from(vec![WIRE_VERSION]).wire_version(),
140 Some(WIRE_VERSION)
141 );
142 assert_eq!(LocalCipherText::from(Vec::new()).wire_version(), None);
143 }
144}