1use core::borrow::Borrow;
4
5use spideroak_crypto::hash::{Digest, Hash};
6use zerocopy::{Immutable, IntoBytes, KnownLayout, Unaligned};
7
8use crate::{
9 aranya::{Signature, SigningKeyId},
10 ciphersuite::{CipherSuite, CipherSuiteExt},
11 id::custom_id,
12};
13
14custom_id! {
15 #[derive(Immutable, IntoBytes, KnownLayout, Unaligned)]
17 pub struct GroupId;
18}
19
20custom_id! {
21 #[derive(Immutable, IntoBytes, KnownLayout, Unaligned)]
23 pub struct PolicyId;
24}
25
26custom_id! {
27 #[derive(Immutable, IntoBytes, KnownLayout, Unaligned)]
29 pub struct CmdId;
30}
31
32pub(crate) fn cmd_id<CS: CipherSuite>(
34 cmd: &Digest<<CS::Hash as Hash>::DigestSize>,
35 sig: &Signature<CS>,
36) -> CmdId {
37 CS::tuple_hash(
43 b"PolicyCommandId-v1",
44 [cmd.as_bytes(), sig.raw_sig().borrow()],
45 )
46 .into_array()
47 .into()
48}
49
50pub fn merge_cmd_id<CS: CipherSuite>(left: CmdId, right: CmdId) -> CmdId {
52 CS::tuple_hash(b"MergeCommandId-v1", [left.as_bytes(), right.as_bytes()])
58 .into_array()
59 .into()
60}
61
62#[derive(Copy, Clone, Debug)]
64pub struct Cmd<'a> {
65 pub data: &'a [u8],
67 pub name: &'a str,
71 pub parent_id: &'a CmdId,
73}
74
75impl Cmd<'_> {
76 pub(crate) fn digest<CS: CipherSuite>(
79 &self,
80 author: SigningKeyId,
81 ) -> Digest<<CS::Hash as Hash>::DigestSize> {
82 CS::tuple_hash(
93 b"SignPolicyCommand-v1",
94 [
95 author.as_bytes(),
97 self.name.as_bytes(),
99 self.parent_id.as_bytes(),
101 self.data,
103 ],
104 )
105 }
106}
107
108custom_id! {
109 #[derive(Immutable, IntoBytes, KnownLayout, Unaligned)]
111 pub struct RoleId;
112}
113
114pub fn role_id<CS: CipherSuite>(cmd_id: CmdId, name: &str, policy_id: PolicyId) -> RoleId {
119 CS::tuple_hash(
126 b"RoleId-v1",
127 [cmd_id.as_bytes(), name.as_bytes(), policy_id.as_bytes()],
128 )
129 .into_array()
130 .into()
131}
132
133custom_id! {
134 #[derive(Immutable, IntoBytes, KnownLayout, Unaligned)]
136 pub struct LabelId;
137}
138
139pub fn label_id<CS: CipherSuite>(cmd_id: CmdId, name: &str, policy_id: PolicyId) -> LabelId {
144 CS::tuple_hash(
151 b"LabelId-v1",
152 [cmd_id.as_bytes(), name.as_bytes(), policy_id.as_bytes()],
153 )
154 .into_array()
155 .into()
156}
157
158#[cfg(test)]
159mod tests {
160 use spideroak_crypto::{ed25519::Ed25519, rust};
161
162 use super::*;
163 use crate::{Id, default::DhKemP256HkdfSha256, test_util::TestCs};
164
165 type CS = TestCs<
166 rust::Aes256Gcm,
167 rust::Sha256,
168 rust::HkdfSha512,
169 DhKemP256HkdfSha256,
170 rust::HmacSha512,
171 Ed25519,
172 >;
173
174 #[test]
176 fn test_label_id() {
177 let tests = [
178 (
179 CmdId::default(),
180 "foo",
181 PolicyId::default(),
182 "EwfLLZLFzYdgqbZfjXcMfc3hNiHpkgrJsvRHEzyDzpVQ",
183 ),
184 (
185 CmdId::default(),
186 "bar",
187 PolicyId::default(),
188 "7AEhZudswFSSYRXzigAgjeTjt4cCFC8QMxiE7Xnj3GKK",
189 ),
190 (
191 CmdId::from(Id::from_bytes([b'A'; 32])),
192 "bar",
193 PolicyId::default(),
194 "24di1Kgod1CXqJ1Pgi77ptpT6t2Nk3YGifqSTjSttn8z",
195 ),
196 (
197 CmdId::from([b'A'; 32]),
198 "baz",
199 PolicyId::from([b'B'; 32]),
200 "J2miD6wUPVVfxakYHBTA8n2wGFHyAty2P2zLedHz2Nov",
201 ),
202 ];
203 for (i, (cmd_id, name, policy_id, want)) in tests.iter().enumerate() {
204 let got = label_id::<CS>(*cmd_id, name, *policy_id);
205 let want = LabelId::decode(*want).unwrap();
206 assert_eq!(got, want, "#{i}");
207 }
208 }
209
210 #[test]
212 fn test_role_id() {
213 let tests = [
214 (
215 CmdId::default(),
216 "foo",
217 PolicyId::default(),
218 "2UKzdrSR8nXYwZB6MKoAHpDkNuGbj4CKhDuJpy1CWAKd",
219 ),
220 (
221 CmdId::default(),
222 "bar",
223 PolicyId::default(),
224 "BXi83x4nTF7gXD21AZAaGTg83yFvJMhezHSDQ7yYT1xx",
225 ),
226 (
227 CmdId::from(Id::from_bytes([b'A'; 32])),
228 "bar",
229 PolicyId::default(),
230 "8syerzb6aY9rcVwDf1mq3m7Qh547tE6wUAFrw8fAJNZq",
231 ),
232 (
233 CmdId::from([b'A'; 32]),
234 "baz",
235 PolicyId::from([b'B'; 32]),
236 "9Nkp9bxw2a7FvhkjexhBM2o4Su59AacWdEGYVmPHMKYr",
237 ),
238 ];
239 for (i, (cmd_id, name, policy_id, want)) in tests.iter().enumerate() {
240 let got = role_id::<CS>(*cmd_id, name, *policy_id);
241 let want = RoleId::decode(*want).unwrap();
242 assert_eq!(got, want, "#{i}");
243 }
244 }
245}