nectar_primitives/chunk/encryption/
key.rs1use std::mem::size_of;
4
5use alloy_primitives::B256;
6use subtle::ConstantTimeEq;
7use zeroize::{Zeroize, ZeroizeOnDrop};
8
9use super::error::EncryptionError;
10
11#[derive(Clone, Zeroize, ZeroizeOnDrop)]
17pub struct EncryptionKey([u8; size_of::<B256>()]);
18
19impl ConstantTimeEq for EncryptionKey {
20 fn ct_eq(&self, other: &Self) -> subtle::Choice {
21 self.0.ct_eq(&other.0)
22 }
23}
24
25impl PartialEq for EncryptionKey {
26 fn eq(&self, other: &Self) -> bool {
27 self.ct_eq(other).into()
28 }
29}
30
31impl Eq for EncryptionKey {}
32
33impl EncryptionKey {
34 pub const SIZE: usize = size_of::<B256>();
36
37 pub const fn as_bytes(&self) -> &[u8; Self::SIZE] {
39 &self.0
40 }
41
42 #[cfg(feature = "encryption")]
44 pub fn generate() -> Self {
45 use rand::RngExt;
46 Self(rand::rng().random())
47 }
48}
49
50impl From<[u8; Self::SIZE]> for EncryptionKey {
51 fn from(bytes: [u8; Self::SIZE]) -> Self {
52 Self(bytes)
53 }
54}
55
56impl From<B256> for EncryptionKey {
57 fn from(b: B256) -> Self {
58 Self(b.0)
59 }
60}
61
62impl AsRef<[u8; Self::SIZE]> for EncryptionKey {
63 fn as_ref(&self) -> &[u8; Self::SIZE] {
64 &self.0
65 }
66}
67
68impl AsRef<[u8]> for EncryptionKey {
69 fn as_ref(&self) -> &[u8] {
70 &self.0
71 }
72}
73
74impl TryFrom<&[u8]> for EncryptionKey {
75 type Error = EncryptionError;
76
77 fn try_from(slice: &[u8]) -> Result<Self, Self::Error> {
78 if slice.len() != Self::SIZE {
79 return Err(EncryptionError::InvalidKeyLength { len: slice.len() });
80 }
81 let mut bytes = [0u8; Self::SIZE];
82 bytes.copy_from_slice(slice);
83 Ok(Self(bytes))
84 }
85}
86
87impl std::fmt::Debug for EncryptionKey {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 write!(
91 f,
92 "EncryptionKey({:02x}{:02x}{:02x}{:02x}..)",
93 self.0[0], self.0[1], self.0[2], self.0[3]
94 )
95 }
96}
97
98#[cfg(test)]
99mod tests {
100 use super::*;
101
102 #[test]
103 fn from_bytes_roundtrip() {
104 let bytes = [42u8; EncryptionKey::SIZE];
105 let key = EncryptionKey::from(bytes);
106 assert_eq!(key.as_bytes(), &bytes);
107 }
108
109 #[test]
110 fn from_b256() {
111 let b = B256::repeat_byte(0xab);
112 let key = EncryptionKey::from(b);
113 assert_eq!(
114 <EncryptionKey as AsRef<[u8; EncryptionKey::SIZE]>>::as_ref(&key),
115 &[0xab; EncryptionKey::SIZE]
116 );
117 }
118
119 #[test]
120 fn try_from_slice_valid() {
121 let slice = [7u8; EncryptionKey::SIZE];
122 let key = EncryptionKey::try_from(slice.as_slice()).unwrap();
123 assert_eq!(
124 <EncryptionKey as AsRef<[u8; EncryptionKey::SIZE]>>::as_ref(&key),
125 &slice
126 );
127 }
128
129 #[test]
130 fn try_from_slice_invalid() {
131 let short = [0u8; 16];
132 let err = EncryptionKey::try_from(short.as_slice()).unwrap_err();
133 assert!(matches!(err, EncryptionError::InvalidKeyLength { len: 16 }));
134 }
135
136 #[test]
137 fn debug_shows_hex_prefix() {
138 let key = EncryptionKey::from([
139 0xab, 0xcd, 0xef, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
140 0, 0, 0, 0, 0, 0, 0,
141 ]);
142 let dbg = format!("{:?}", key);
143 assert!(dbg.contains("abcdef01"));
144 }
145
146 #[cfg(feature = "encryption")]
147 #[test]
148 fn generate_produces_key() {
149 let k1 = EncryptionKey::generate();
150 let k2 = EncryptionKey::generate();
151 assert_ne!(k1, k2);
153 }
154
155 #[test]
156 fn constant_time_equality() {
157 let k1 = EncryptionKey::from([0x42; EncryptionKey::SIZE]);
158 let k2 = EncryptionKey::from([0x42; EncryptionKey::SIZE]);
159 let k3 = EncryptionKey::from([0x43; EncryptionKey::SIZE]);
160 assert_eq!(k1, k2);
161 assert_ne!(k1, k3);
162 }
163}