1use core::{cmp::Ordering, fmt};
2
3use buggy::Bug;
4use byteorder::{ByteOrder as _, LittleEndian};
5pub use spideroak_crypto::hpke::MessageLimitReached;
6use spideroak_crypto::{
7 aead,
8 hpke::{self, HpkeError, OpenCtx, SealCtx},
9 import::ImportError,
10};
11
12use crate::{
13 afc::shared::{RawOpenKey, RawSealKey},
14 ciphersuite::CipherSuite,
15 policy::LabelId,
16};
17
18#[derive(Copy, Clone, Debug, Default, Hash, Eq, PartialEq, Ord, PartialOrd)]
20pub struct Seq(hpke::Seq);
21
22impl Seq {
23 pub const ZERO: Self = Self(hpke::Seq::ZERO);
25
26 pub const fn new(seq: u64) -> Self {
28 Self(hpke::Seq::new(seq))
29 }
30
31 pub const fn to_u64(&self) -> u64 {
33 self.0.to_u64()
34 }
35
36 #[cfg(any(test, feature = "test_util"))]
40 pub(crate) fn max<N: crate::generic_array::ArrayLength>() -> u64 {
41 hpke::Seq::max::<N>()
42 }
43}
44
45impl From<Seq> for u64 {
46 fn from(seq: Seq) -> Self {
47 seq.to_u64()
48 }
49}
50
51impl From<u64> for Seq {
52 fn from(seq: u64) -> Self {
53 Self::new(seq)
54 }
55}
56
57impl PartialEq<u64> for Seq {
58 fn eq(&self, other: &u64) -> bool {
59 PartialEq::eq(&self.to_u64(), other)
60 }
61}
62
63impl PartialOrd<u64> for Seq {
64 fn partial_cmp(&self, other: &u64) -> Option<Ordering> {
65 PartialOrd::partial_cmp(&self.to_u64(), other)
66 }
67}
68
69impl fmt::Display for Seq {
70 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
71 self.0.fmt(f)
72 }
73}
74
75macro_rules! packed {
76 (
77 $(#[$meta:meta])*
78 $vis:vis struct $name:ident $($tokens:tt)*
79 ) => {
80 $(#[$meta])*
81 $vis struct $name $($tokens)*
82 impl $name {
83 $vis const PACKED_SIZE: usize = {
85 #[repr(C, packed)]
86 #[allow(dead_code)]
87 $vis struct $name $($tokens)*
88 ::core::mem::size_of::<$name>()
89 };
90 }
91 };
92}
93
94packed! {
95 pub struct AuthData {
100 pub version: u32,
102 pub label_id: LabelId,
104 }
105}
106
107impl AuthData {
108 fn to_bytes(&self) -> [u8; Self::PACKED_SIZE] {
109 let mut b = [0u8; Self::PACKED_SIZE];
110 LittleEndian::write_u32(&mut b[0..4], self.version);
111 b[4..].copy_from_slice(self.label_id.as_bytes());
112 b
113 }
114}
115
116pub struct SealKey<CS: CipherSuite> {
118 ctx: SealCtx<CS::Aead>,
119}
120
121impl<CS: CipherSuite> SealKey<CS> {
122 pub const OVERHEAD: usize = SealCtx::<CS::Aead>::OVERHEAD;
124
125 pub fn from_raw(key: &RawSealKey<CS>, seq: Seq) -> Result<Self, ImportError> {
127 let RawSealKey { key, base_nonce } = key;
128 let ctx = SealCtx::new(key, base_nonce, seq.0)?;
129 Ok(Self { ctx })
130 }
131
132 pub fn seal(
139 &mut self,
140 dst: &mut [u8],
141 plaintext: &[u8],
142 ad: &AuthData,
143 ) -> Result<Seq, SealError> {
144 let seq = self.ctx.seal(dst, plaintext, &ad.to_bytes())?;
145 Ok(Seq(seq))
146 }
147
148 pub fn seal_in_place(
151 &mut self,
152 data: impl AsMut<[u8]>,
153 tag: &mut [u8],
154 ad: &AuthData,
155 ) -> Result<Seq, SealError> {
156 let seq = self.ctx.seal_in_place(data, tag, &ad.to_bytes())?;
157 Ok(Seq(seq))
158 }
159
160 #[inline]
162 pub fn seq(&self) -> Seq {
163 Seq(self.ctx.seq())
164 }
165}
166
167#[derive(Debug, Eq, PartialEq, thiserror::Error)]
169pub enum SealError {
170 #[error("message limit reached")]
173 MessageLimitReached,
174 #[error(transparent)]
176 Other(HpkeError),
177 #[error(transparent)]
179 Bug(#[from] Bug),
180}
181
182impl From<HpkeError> for SealError {
183 fn from(err: HpkeError) -> Self {
184 match err {
185 HpkeError::MessageLimitReached => Self::MessageLimitReached,
186 err => Self::Other(err),
187 }
188 }
189}
190
191pub struct OpenKey<CS: CipherSuite> {
193 ctx: OpenCtx<CS::Aead>,
194}
195
196impl<CS: CipherSuite> OpenKey<CS> {
197 pub const OVERHEAD: usize = OpenCtx::<CS::Aead>::OVERHEAD;
199
200 pub fn from_raw(key: &RawOpenKey<CS>) -> Result<Self, ImportError> {
202 let RawOpenKey { key, base_nonce } = key;
203 let ctx = OpenCtx::new(key, base_nonce, Seq::ZERO.0)?;
207 Ok(Self { ctx })
208 }
209
210 pub fn open(
217 &self,
218 dst: &mut [u8],
219 ciphertext: &[u8],
220 ad: &AuthData,
221 seq: Seq,
222 ) -> Result<(), OpenError> {
223 self.ctx.open_at(dst, ciphertext, &ad.to_bytes(), seq.0)?;
224 Ok(())
225 }
226
227 pub fn open_in_place(
234 &self,
235 data: impl AsMut<[u8]>,
236 tag: &[u8],
237 ad: &AuthData,
238 seq: Seq,
239 ) -> Result<(), OpenError> {
240 self.ctx
241 .open_in_place_at(data, tag, &ad.to_bytes(), seq.0)?;
242 Ok(())
243 }
244}
245
246#[derive(Debug, Eq, PartialEq, thiserror::Error)]
248pub enum OpenError {
249 #[error("authentication error")]
251 Authentication,
252 #[error("message limit reached")]
258 MessageLimitReached,
259 #[error(transparent)]
261 Other(HpkeError),
262 #[error(transparent)]
264 Bug(#[from] Bug),
265}
266
267impl From<HpkeError> for OpenError {
268 fn from(err: HpkeError) -> Self {
269 match err {
270 HpkeError::Open(aead::OpenError::Authentication) => Self::Authentication,
271 HpkeError::MessageLimitReached => Self::MessageLimitReached,
272 err => Self::Other(err),
273 }
274 }
275}