enquo_core/datatype/boolean/
v1.rs1use ciborium::{cbor, value::Value};
5use serde::{Deserialize, Serialize};
6use serde_with::skip_serializing_none;
7use std::cmp::Ordering;
8
9use crate::{
10 crypto::{AES256v1, OREv1},
11 field::KeyId,
12 Error, Field,
13};
14
15#[skip_serializing_none]
17#[derive(Debug, Serialize, Deserialize)]
18#[doc(hidden)]
19pub struct V1 {
20 #[serde(rename = "a")]
22 aes_ciphertext: AES256v1,
23 #[serde(rename = "o")]
25 ore_ciphertext: Option<OREv1<1, 2>>,
26 #[serde(rename = "k", with = "serde_bytes")]
28 kid: Vec<u8>,
29}
30
31#[allow(non_upper_case_globals)]
33const BOOLEANv1_ORE_KEY_IDENTIFIER: &[u8] = b"boolean::V1.ore_key";
34
35impl V1 {
36 pub(crate) fn new(b: bool, context: &[u8], field: &Field) -> Result<V1, Error> {
38 Self::encrypt(b, context, field, false)
39 }
40
41 pub(crate) fn new_with_unsafe_parts(
43 b: bool,
44 context: &[u8],
45 field: &Field,
46 ) -> Result<V1, Error> {
47 Self::encrypt(b, context, field, true)
48 }
49
50 fn encrypt(b: bool, context: &[u8], field: &Field, include_left: bool) -> Result<V1, Error> {
52 let v = cbor!(b).map_err(|e| {
53 Error::EncodingError(format!("failed to convert bool to ciborium value: {e}"))
54 })?;
55
56 let mut msg: Vec<u8> = Default::default();
57 ciborium::ser::into_writer(&v, &mut msg)
58 .map_err(|e| Error::EncodingError(format!("failed to encode bool value: {e}")))?;
59
60 let aes = AES256v1::new(&msg, context, field)?;
61
62 let ore = if include_left {
63 OREv1::<1, 2>::new_with_left(b, BOOLEANv1_ORE_KEY_IDENTIFIER, field)?
64 } else {
65 OREv1::<1, 2>::new(b, BOOLEANv1_ORE_KEY_IDENTIFIER, field)?
66 };
67
68 Ok(V1 {
69 aes_ciphertext: aes,
70 ore_ciphertext: Some(ore),
71 kid: field.key_id()?.into(),
72 })
73 }
74
75 pub(crate) fn decrypt(&self, context: &[u8], field: &Field) -> Result<bool, Error> {
77 let pt = self.aes_ciphertext.decrypt(context, field)?;
78
79 let v = ciborium::de::from_reader(&*pt)
80 .map_err(|e| Error::DecodingError(format!("could not decode decrypted value: {e}")))?;
81
82 #[allow(clippy::wildcard_enum_match_arm)] match v {
84 Value::Bool(b) => Ok(b),
85 _ => Err(Error::DecodingError(format!(
86 "Decoded value is not a boolean (got {v:?})"
87 ))),
88 }
89 }
90
91 pub(crate) fn key_id(&self) -> KeyId {
93 let mut key_id: KeyId = Default::default();
94 key_id.copy_from_slice(&self.kid);
95 key_id
96 }
97
98 pub(crate) fn make_unqueryable(&mut self) {
100 self.ore_ciphertext = None;
101 }
102}
103
104impl Ord for V1 {
105 #[allow(clippy::panic, clippy::expect_used)] fn cmp(&self, other: &Self) -> Ordering {
107 assert!(
108 self.kid == other.kid,
109 "Cannot compare ciphertexts from different keys"
110 );
111
112 let lhs = self
113 .ore_ciphertext
114 .as_ref()
115 .expect("Cannot compare without an ORE ciphertext on the left-hand side");
116 let rhs = other
117 .ore_ciphertext
118 .as_ref()
119 .expect("Cannot compare without an ORE ciphertext on the right-hand side");
120
121 lhs.cmp(rhs)
122 }
123}
124
125impl PartialOrd for V1 {
126 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
127 Some(self.cmp(other))
128 }
129}
130
131impl PartialEq for V1 {
132 fn eq(&self, other: &Self) -> bool {
133 self.cmp(other) == Ordering::Equal
134 }
135}
136
137impl Eq for V1 {}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use crate::{key_provider::Static, Root};
143 use std::sync::Arc;
144
145 fn field() -> Field {
146 Root::new(Arc::new(
147 Static::new(b"this is a suuuuper long test key").unwrap(),
148 ))
149 .unwrap()
150 .field(b"foo", b"bar")
151 .unwrap()
152 }
153
154 #[test]
155 fn value_round_trips() {
156 let true_value = V1::new(true, b"context", &field()).unwrap();
157 let false_value = V1::new(false, b"context", &field()).unwrap();
158
159 assert_eq!(true, true_value.decrypt(b"context", &field()).unwrap());
160 assert_eq!(false, false_value.decrypt(b"context", &field()).unwrap());
161 }
162
163 #[test]
164 fn incorrect_context_fails() {
165 let value = V1::new(true, b"somecontext", &field()).unwrap();
166
167 let err = value.decrypt(b"othercontext", &field()).err();
168 assert!(matches!(err, Some(Error::DecryptionError(_))));
169 }
170
171 #[test]
172 fn serialised_ciphertext_size() {
173 let value = V1::new(true, b"somecontext", &field()).unwrap();
174 let serde_value = cbor!(value).unwrap();
175
176 let mut s: Vec<u8> = vec![];
177 ciborium::ser::into_writer(&serde_value, &mut s).unwrap();
178 assert!(s.len() < 81, "s.len() == {}", s.len());
179 }
180
181 #[test]
182 fn default_encryption_is_safe() {
183 let value = V1::new(true, b"somecontext", &field()).unwrap();
184
185 assert!(!value.ore_ciphertext.unwrap().has_left());
186 }
187}