1pub mod crypto;
2pub mod error;
3pub mod format;
4pub mod verify;
5
6pub use error::{Error, Result};
7pub use format::{Artifact, ArtifactState, FLAG_SEALED, MAGIC};
8
9use serde::{Deserialize, Serialize};
12use std::fmt;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub struct ArtifactId(pub [u8; 16]);
17
18impl ArtifactId {
19 pub fn new() -> Self {
20 use rand::RngCore;
21 let mut id = [0u8; 16];
22 rand::thread_rng().fill_bytes(&mut id);
23 Self(id)
24 }
25
26 pub fn from_bytes(bytes: [u8; 16]) -> Self {
27 Self(bytes)
28 }
29
30 pub fn to_hex(&self) -> String {
31 hex::encode(self.0)
32 }
33
34 pub fn from_hex(s: &str) -> Result<Self> {
35 let bytes = hex::decode(s).map_err(|_| Error::InvalidHex)?;
36 if bytes.len() != 16 {
37 return Err(Error::InvalidArtifactId);
38 }
39 let mut arr = [0u8; 16];
40 arr.copy_from_slice(&bytes);
41 Ok(Self(arr))
42 }
43
44 pub fn short_display(&self) -> String {
46 hex::encode(&self.0[..8])
47 }
48}
49
50impl fmt::Display for ArtifactId {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "{}", self.to_hex())
53 }
54}
55
56impl Default for ArtifactId {
57 fn default() -> Self {
58 Self::new()
59 }
60}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
64#[repr(u8)]
65pub enum Intent {
66 Lab = 0,
67 OwnedInfra = 1,
68 AuthRedteam = 2,
69 BlueRemediation = 3,
70 Research = 4,
71}
72
73impl Intent {
74 pub fn from_u8(value: u8) -> Result<Self> {
75 match value {
76 0 => Ok(Intent::Lab),
77 1 => Ok(Intent::OwnedInfra),
78 2 => Ok(Intent::AuthRedteam),
79 3 => Ok(Intent::BlueRemediation),
80 4 => Ok(Intent::Research),
81 _ => Err(Error::InvalidIntent(value)),
82 }
83 }
84
85 pub fn to_u8(self) -> u8 {
86 self as u8
87 }
88
89 pub fn as_str(&self) -> &'static str {
90 match self {
91 Intent::Lab => "LAB",
92 Intent::OwnedInfra => "OWNED-INFRA",
93 Intent::AuthRedteam => "AUTH-REDTEAM",
94 Intent::BlueRemediation => "BLUE-REMEDIATION",
95 Intent::Research => "RESEARCH",
96 }
97 }
98
99 pub fn all() -> &'static [Intent] {
100 &[
101 Intent::Lab,
102 Intent::OwnedInfra,
103 Intent::AuthRedteam,
104 Intent::BlueRemediation,
105 Intent::Research,
106 ]
107 }
108
109 pub fn from_str(s: &str) -> Result<Self> {
110 match s.to_uppercase().as_str() {
111 "LAB" => Ok(Intent::Lab),
112 "OWNED-INFRA" | "OWNED_INFRA" => Ok(Intent::OwnedInfra),
113 "AUTH-REDTEAM" | "AUTH_REDTEAM" | "AUTHREDTEAM" => Ok(Intent::AuthRedteam),
114 "BLUE-REMEDIATION" | "BLUE_REMEDIATION" => Ok(Intent::BlueRemediation),
115 "RESEARCH" => Ok(Intent::Research),
116 _ => Err(Error::InvalidIntentString(s.to_string())),
117 }
118 }
119}
120
121impl fmt::Display for Intent {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 write!(f, "{}", self.as_str())
124 }
125}
126
127#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
129pub struct KeyFingerprint(pub [u8; 32]);
130
131impl KeyFingerprint {
132 pub fn new(public_key_bytes: &[u8; 32]) -> Self {
133 let hash = blake3::hash(public_key_bytes);
134 Self(*hash.as_bytes())
135 }
136
137 pub fn to_hex(&self) -> String {
138 hex::encode(self.0)
139 }
140
141 pub fn short_display(&self) -> String {
142 hex::encode(&self.0[..8])
143 }
144}
145
146impl fmt::Display for KeyFingerprint {
147 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
148 write!(f, "{}", self.short_display())
149 }
150}
151
152#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
154#[serde(rename_all = "PascalCase")]
155pub enum RevocationReason {
156 Error,
157 Compromised,
158 Superseded,
159 AccessRevoked,
160 Other,
161}
162