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 = 2;
32
33#[derive(Debug, Serialize, Deserialize)]
35#[serde(transparent)]
36pub struct LocalCipherText(Bytes);
37
38impl LocalCipherText {
39 pub fn into_inner(self) -> Bytes {
40 self.0
41 }
42
43 pub fn into_reader(self) -> CipherTextReader {
44 CipherTextReader::new(self.0)
45 }
46
47 pub fn wire_version(&self) -> Option<u8> {
54 self.0.first().copied()
55 }
56}
57
58impl AsRef<[u8]> for LocalCipherText {
59 fn as_ref(&self) -> &[u8] {
60 self.0.as_ref()
61 }
62}
63
64impl From<Vec<u8>> for LocalCipherText {
65 fn from(bytes: Vec<u8>) -> Self {
66 LocalCipherText(Bytes::from(bytes))
67 }
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use crate::Nonce;
74 use vitaminc_protected::{Controlled, Protected};
75
76 #[test]
77 fn test_ciphertext_builder_with_plaintext_in_place() -> Result<(), ()> {
78 let nonce = Nonce::new([1u8; 12]);
79 let plaintext = vec![0u8; 10];
80 let ciphertext = CipherTextBuilder::new()
81 .append_nonce(nonce)
82 .append_target_plaintext(plaintext)
83 .accepts_ciphertext_and_tag_ok(|mut ciphertext| {
84 ciphertext.copy_from_slice(&[2u8; 10]);
85 ciphertext.extend([3u8; 16]);
86 Ok(ciphertext)
87 })
88 .build()?;
89
90 assert_eq!(ciphertext.0.len(), 39);
92 assert_eq!(ciphertext.0[0], WIRE_VERSION);
93 assert_eq!(ciphertext.wire_version(), Some(WIRE_VERSION));
94 assert_eq!(&ciphertext.0[1..13], &[1u8; 12]);
95 assert_eq!(&ciphertext.0[13..23], &[2u8; 10]);
96 assert_eq!(&ciphertext.0[23..], &[3u8; 16]);
97
98 Ok(())
99 }
100
101 #[test]
102 fn test_ciphertext_reader() -> Result<(), ()> {
103 let nonce = Nonce::new([1u8; 12]);
104 let plaintext: Protected<Vec<u8>> = Protected::new(vec![0u8; 10]);
105
106 let ciphertext = CipherTextBuilder::new()
107 .append_nonce(nonce)
108 .append_target_plaintext(plaintext)
109 .accepts_ciphertext_and_tag_ok(|mut ciphertext| {
110 ciphertext.copy_from_slice(&[2u8; 10]);
111 ciphertext.extend([3u8; 16]);
112 Ok(ciphertext)
113 })
114 .build()?;
115
116 let (nonce, reader) = ciphertext
117 .into_reader()
118 .read_version()
119 .map_err(|_| ())?
120 .read_nonce::<12>()
121 .map_err(|_| ())?;
122
123 let plaintext = reader
124 .accepts_plaintext_ok(|data| {
125 assert_eq!(data.len(), 26);
126 assert_eq!(&data[..10], [2u8; 10]);
127 assert_eq!(&data[10..], [3u8; 16]);
128 data[..10].copy_from_slice(&[0u8; 10]);
130 Ok(10)
131 })
132 .read()?;
133
134 assert_eq!(nonce.into_inner(), [1u8; 12]);
135 assert_eq!(plaintext.risky_unwrap()[..10], vec![0u8; 10]);
136
137 Ok(())
138 }
139
140 #[test]
141 fn wire_version_reads_the_stored_first_byte() {
142 assert_eq!(
145 LocalCipherText::from(vec![7u8, 0, 0]).wire_version(),
146 Some(7)
147 );
148 assert_eq!(
149 LocalCipherText::from(vec![WIRE_VERSION]).wire_version(),
150 Some(WIRE_VERSION)
151 );
152 assert_eq!(LocalCipherText::from(Vec::new()).wire_version(), None);
153 }
154}