1use crate::constants::{
2 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL,
3 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE,
4 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH,
5 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY,
6};
7use crate::error::{Error, ErrorContext, ValueConstraint};
8
9#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
22#[non_exhaustive]
23#[repr(u8)]
24pub enum Tag {
25 #[default]
27 Message = 0,
28 Push = 1,
31 Rekey = 2,
33 Final = 3,
36}
37
38const _: () = {
39 assert!(Tag::Message as u8 == CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE);
40 assert!(Tag::Push as u8 == CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH);
41 assert!(Tag::Rekey as u8 == CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY);
42 assert!(Tag::Final as u8 == CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL);
43};
44
45impl Tag {
46 #[inline]
50 #[must_use]
51 pub const fn bits(self) -> u8 {
52 self as u8
53 }
54}
55
56impl TryFrom<u8> for Tag {
57 type Error = Error;
58
59 fn try_from(bits: u8) -> Result<Self, Self::Error> {
66 match bits {
67 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_MESSAGE => Ok(Self::Message),
68 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_PUSH => Ok(Self::Push),
69 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_REKEY => Ok(Self::Rekey),
70 CRYPTO_SECRETSTREAM_XCHACHA20POLY1305_TAG_FINAL => Ok(Self::Final),
71 _ => Err(Error::InvalidValue {
72 context: ErrorContext::Tag,
73 actual: u64::from(bits),
74 constraint: ValueConstraint::AllowedBits {
75 mask: u64::from(Self::Final.bits()),
76 },
77 }),
78 }
79 }
80}
81
82#[cfg(test)]
83mod tests {
84 use super::Tag;
85
86 #[test]
87 fn tag_bytes_are_libsodiums_values() {
88 let cases = [
89 (Tag::Message, 0u8),
90 (Tag::Push, 1),
91 (Tag::Rekey, 2),
92 (Tag::Final, 3),
93 ];
94 for (tag, bits) in cases {
95 assert_eq!(tag.bits(), bits);
96 assert_eq!(Tag::try_from(bits).expect("known tag byte"), tag);
97 }
98 }
99
100 #[test]
101 fn tag_from_u8_rejects_every_unknown_byte() {
102 for bits in 4..=u8::MAX {
103 let error = Tag::try_from(bits).expect_err("unknown tag byte must be rejected");
104 assert!(
105 matches!(
106 error,
107 crate::Error::InvalidValue {
108 context: crate::ErrorContext::Tag,
109 actual,
110 constraint: crate::ValueConstraint::AllowedBits { mask: 0x3 },
111 } if actual == u64::from(bits)
112 ),
113 "tag byte {bits:#04x}: {error:?}"
114 );
115 }
116 }
117}