Skip to main content

nectar_primitives/chunk/encryption/
reference.rs

1//! Chunk reference types for encrypted chunks.
2
3use std::mem::size_of;
4
5use crate::chunk::ChunkAddress;
6
7use super::error::EncryptionError;
8use super::key::EncryptionKey;
9
10/// An encrypted chunk reference: 32-byte address + 32-byte decryption key.
11///
12/// This type statically guarantees the reference is encrypted,
13/// eliminating runtime variant checks.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub struct EncryptedChunkRef {
16    address: ChunkAddress,
17    key: EncryptionKey,
18}
19
20impl EncryptedChunkRef {
21    /// Serialized size: address + decryption key.
22    pub const SIZE: usize = size_of::<ChunkAddress>() + EncryptionKey::SIZE;
23
24    /// Create a new encrypted chunk reference.
25    pub const fn new(address: ChunkAddress, key: EncryptionKey) -> Self {
26        Self { address, key }
27    }
28
29    /// Chunk address (BMT hash of ciphertext).
30    pub const fn address(&self) -> &ChunkAddress {
31        &self.address
32    }
33
34    /// Decryption key.
35    pub const fn key(&self) -> &EncryptionKey {
36        &self.key
37    }
38
39    /// Consume and return (address, key).
40    pub fn into_parts(self) -> (ChunkAddress, EncryptionKey) {
41        (self.address, self.key)
42    }
43
44    /// Write the reference into `buf`. Panics if `buf` is too small.
45    pub fn write_to(&self, buf: &mut [u8]) {
46        buf[..size_of::<ChunkAddress>()].copy_from_slice(self.address.as_bytes());
47        buf[size_of::<ChunkAddress>()..Self::SIZE].copy_from_slice(self.key.as_bytes());
48    }
49}
50
51impl From<&EncryptedChunkRef> for [u8; EncryptedChunkRef::SIZE] {
52    fn from(r: &EncryptedChunkRef) -> Self {
53        let mut buf = [0u8; EncryptedChunkRef::SIZE];
54        r.write_to(&mut buf);
55        buf
56    }
57}
58
59impl From<EncryptedChunkRef> for [u8; EncryptedChunkRef::SIZE] {
60    fn from(r: EncryptedChunkRef) -> Self {
61        (&r).into()
62    }
63}
64
65impl From<&EncryptedChunkRef> for Vec<u8> {
66    fn from(r: &EncryptedChunkRef) -> Self {
67        let mut v = Self::with_capacity(EncryptedChunkRef::SIZE);
68        v.extend_from_slice(r.address.as_bytes());
69        v.extend_from_slice(r.key.as_bytes());
70        v
71    }
72}
73
74impl TryFrom<&[u8]> for EncryptedChunkRef {
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::InvalidReferenceLength { len: slice.len() });
80        }
81        let addr = ChunkAddress::from_slice(&slice[..size_of::<ChunkAddress>()])
82            .map_err(|_| EncryptionError::InvalidReferenceLength { len: slice.len() })?;
83        let key = EncryptionKey::try_from(&slice[size_of::<ChunkAddress>()..])?;
84        Ok(Self { address: addr, key })
85    }
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use alloy_primitives::B256;
92
93    #[test]
94    fn encrypted_roundtrip() {
95        let addr = ChunkAddress::from(B256::repeat_byte(0xcd));
96        let key = EncryptionKey::from([0xef; 32]);
97        let enc_ref = EncryptedChunkRef::new(addr, key.clone());
98
99        assert_eq!(enc_ref.address(), &addr);
100        assert_eq!(enc_ref.key(), &key);
101
102        let bytes: [u8; 64] = (&enc_ref).into();
103        assert_eq!(bytes.len(), 64);
104
105        let recovered = EncryptedChunkRef::try_from(bytes.as_slice()).unwrap();
106        assert_eq!(recovered, enc_ref);
107    }
108
109    #[test]
110    fn invalid_length() {
111        let bad = [0u8; 48];
112        let err = EncryptedChunkRef::try_from(bad.as_slice()).unwrap_err();
113        assert!(matches!(
114            err,
115            EncryptionError::InvalidReferenceLength { len: 48 }
116        ));
117    }
118
119    #[test]
120    fn write_to_buffer() {
121        let addr = ChunkAddress::from(B256::repeat_byte(0x22));
122        let key = EncryptionKey::from([0x33; 32]);
123        let enc_ref = EncryptedChunkRef::new(addr, key);
124
125        let mut buf = [0u8; 64];
126        enc_ref.write_to(&mut buf);
127        assert_eq!(&buf[..32], &[0x22; 32]);
128        assert_eq!(&buf[32..], &[0x33; 32]);
129    }
130}