Skip to main content

encryption/
key.rs

1// Copyright (c) 2026 erik <erik@erik.xyz> — https://erik.xyz
2
3//! 定长密钥材质:Drop 时清零,Debug 不打印内容。
4
5use std::fmt;
6
7use zeroize::Zeroize;
8
9use crate::error::{Error, Result};
10
11/// 定长密钥(如 `Key<32>`、`Key<16>`)。构造后不可变。
12pub struct Key<const N: usize>([u8; N]);
13
14impl<const N: usize> Key<N> {
15    pub fn new(bytes: [u8; N]) -> Self {
16        Self(bytes)
17    }
18
19    /// 从切片构造;长度不符返回 [`Error::InvalidKeyLength`]。
20    pub fn from_slice(bytes: &[u8], label: &'static str) -> Result<Self> {
21        let array: [u8; N] = bytes.try_into().map_err(|_| Error::InvalidKeyLength {
22            label,
23            expected: N,
24            got: bytes.len(),
25        })?;
26        Ok(Self(array))
27    }
28
29    pub fn as_bytes(&self) -> &[u8; N] {
30        &self.0
31    }
32}
33
34impl<const N: usize> Drop for Key<N> {
35    fn drop(&mut self) {
36        self.0.zeroize();
37    }
38}
39
40impl<const N: usize> fmt::Debug for Key<N> {
41    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
42        write!(f, "Key<{N}>(***)")
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use super::*;
49
50    #[test]
51    fn from_slice_checks_length() {
52        let key = Key::<32>::from_slice(&[7u8; 32], "AES-256-GCM").unwrap();
53        assert_eq!(key.as_bytes()[0], 7);
54
55        let error = Key::<32>::from_slice(&[0u8; 16], "AES-256-GCM").unwrap_err();
56        assert_eq!(
57            error,
58            Error::InvalidKeyLength {
59                label: "AES-256-GCM",
60                expected: 32,
61                got: 16
62            }
63        );
64    }
65
66    #[test]
67    fn debug_is_redacted() {
68        let key = Key::<16>::new([0u8; 16]);
69        assert_eq!(format!("{key:?}"), "Key<16>(***)");
70    }
71}