1use serde::{Deserialize, Serialize};
2
3pub const KEY_RECORD_VERSION: u32 = 1;
4pub const KEY_ID_BYTES: usize = 8;
5pub const VERIFYING_KEY_BYTES: usize = 32;
6
7#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
8#[serde(rename_all = "snake_case")]
9pub enum KeyKind {
10 Agent,
11 Operator,
12}
13
14#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
15pub struct KeyRecord {
16 pub v: u32,
17 pub principal: String,
18 #[serde(with = "crate::encoding::bin_bytes")]
19 pub key_id: Vec<u8>,
20 #[serde(with = "crate::encoding::bin_bytes")]
21 pub verifying_key: Vec<u8>,
22 pub kind: KeyKind,
23 pub valid_from_micros: u64,
24 pub valid_to_micros: Option<u64>,
25 pub revoked: bool,
26}
27
28impl KeyRecord {
29 #[must_use]
30 pub fn new(
31 principal: impl Into<String>,
32 key_id: Vec<u8>,
33 verifying_key: Vec<u8>,
34 kind: KeyKind,
35 ) -> Self {
36 Self {
37 v: KEY_RECORD_VERSION,
38 principal: principal.into(),
39 key_id,
40 verifying_key,
41 kind,
42 valid_from_micros: 0,
43 valid_to_micros: None,
44 revoked: false,
45 }
46 }
47
48 pub fn validate(&self) -> Result<(), &'static str> {
49 if self.v != KEY_RECORD_VERSION {
50 return Err("unsupported key record version");
51 }
52 if self.principal.is_empty() {
53 return Err("key principal must not be empty");
54 }
55 if self.key_id.len() != KEY_ID_BYTES {
56 return Err("Ed25519 key id must be 8 bytes");
57 }
58 if self.verifying_key.len() != VERIFYING_KEY_BYTES {
59 return Err("Ed25519 public key must be 32 bytes");
60 }
61 if self
62 .valid_to_micros
63 .is_some_and(|end| end <= self.valid_from_micros)
64 {
65 return Err("key validity end must be after its start");
66 }
67 Ok(())
68 }
69}
70
71#[cfg(all(test, feature = "cbor"))]
72mod tests {
73 use super::*;
74 use crate::framing::{decode_named, encode_named};
75
76 #[test]
77 fn given_a_key_record_when_round_tripped_then_should_preserve_its_lifecycle() {
78 let record = KeyRecord {
79 v: KEY_RECORD_VERSION,
80 principal: "operator-1".to_owned(),
81 key_id: vec![3; KEY_ID_BYTES],
82 verifying_key: vec![7; VERIFYING_KEY_BYTES],
83 kind: KeyKind::Operator,
84 valid_from_micros: 100,
85 valid_to_micros: Some(200),
86 revoked: true,
87 };
88
89 let encoded = encode_named(&record).expect("key record encodes");
90 let decoded: KeyRecord = decode_named(&encoded).expect("key record decodes");
91
92 assert_eq!(decoded, record);
93 assert!(decoded.validate().is_ok());
94 }
95}