use core::borrow::Borrow as _;
use spideroak_crypto::hash::{Digest, Hash};
use crate::{
aranya::{Signature, SigningKeyId},
ciphersuite::{CipherSuite, CipherSuiteExt as _},
id::{IdExt as _, custom_id},
};
custom_id! {
pub struct GroupId;
}
custom_id! {
pub struct PolicyId;
}
custom_id! {
pub struct CmdId;
}
pub(crate) fn cmd_id<CS: CipherSuite>(
cmd: &Digest<<CS::Hash as Hash>::DigestSize>,
sig: &Signature<CS>,
) -> CmdId {
CmdId::new::<CS>(
b"PolicyCommandId-v1",
[cmd.as_bytes(), sig.raw_sig().borrow()],
)
}
pub fn merge_cmd_id<CS: CipherSuite>(left: CmdId, right: CmdId) -> CmdId {
CmdId::new::<CS>(b"MergeCommandId-v1", [left.as_bytes(), right.as_bytes()])
}
#[derive(Copy, Clone, Debug)]
pub struct Cmd<'a> {
pub data: &'a [u8],
pub name: &'a str,
pub parent_id: &'a CmdId,
}
impl Cmd<'_> {
pub(crate) fn digest<CS: CipherSuite>(
&self,
author: SigningKeyId,
) -> Digest<<CS::Hash as Hash>::DigestSize> {
CS::tuple_hash(
b"SignPolicyCommand-v1",
[
author.as_bytes(),
self.name.as_bytes(),
self.parent_id.as_bytes(),
self.data,
],
)
}
}
custom_id! {
pub struct RoleId;
}
pub fn role_id<CS: CipherSuite>(cmd_id: CmdId, name: &str, policy_id: PolicyId) -> RoleId {
RoleId::new::<CS>(
b"RoleId-v1",
[cmd_id.as_bytes(), name.as_bytes(), policy_id.as_bytes()],
)
}
custom_id! {
pub struct LabelId;
}
pub fn label_id<CS: CipherSuite>(cmd_id: CmdId, name: &str, policy_id: PolicyId) -> LabelId {
LabelId::new::<CS>(
b"LabelId-v1",
[cmd_id.as_bytes(), name.as_bytes(), policy_id.as_bytes()],
)
}
#[cfg(test)]
mod tests {
use spideroak_crypto::{ed25519::Ed25519, rust};
use super::*;
use crate::{default::DhKemP256HkdfSha256, test_util::TestCs};
type CS = TestCs<
rust::Aes256Gcm,
rust::Sha256,
rust::HkdfSha512,
DhKemP256HkdfSha256,
rust::HmacSha512,
Ed25519,
>;
#[test]
fn test_label_id() {
let tests = [
(
CmdId::default(),
"foo",
PolicyId::default(),
"C1PupQYTjr2ouZ3DohnRFEaHR4yoTnMkarbBK4TGhJoi",
),
(
CmdId::default(),
"bar",
PolicyId::default(),
"Eq71P2UhRVMt7R1s1ZB6m1kSuuzwBZwAd21BEv3gmtBC",
),
(
CmdId::from_bytes([b'A'; 32]),
"bar",
PolicyId::default(),
"B4XqE83yLS1i8AiyMxGKo2wtrwvqrhUers5ou3eRfH8z",
),
(
CmdId::from_bytes([b'A'; 32]),
"baz",
PolicyId::from_bytes([b'B'; 32]),
"ACnKJXFwd9e2tSnakXgP8SMiYHBSQLUetWgRjHjyQo8y",
),
];
for (i, (cmd_id, name, policy_id, want)) in tests.iter().enumerate() {
let got = label_id::<CS>(*cmd_id, name, *policy_id);
let want = LabelId::decode(*want).unwrap();
assert_eq!(got, want, "#{i}");
}
}
#[test]
fn test_role_id() {
let tests = [
(
CmdId::default(),
"foo",
PolicyId::default(),
"BoukxZv6twB39TdXkzMafUxsT1uvpmMJbr6nsKLBg7VT",
),
(
CmdId::default(),
"bar",
PolicyId::default(),
"CEEjmy5R6Q7RXBqFtt1nrh597Ytr7bCc2aEWJfixEp9K",
),
(
CmdId::from_bytes([b'A'; 32]),
"bar",
PolicyId::default(),
"9NEW3iaJim8iipkeBCJPJ3v75pEH92iLtrqo8sddkqER",
),
(
CmdId::from_bytes([b'A'; 32]),
"baz",
PolicyId::from_bytes([b'B'; 32]),
"4sVA51vurQexYL8NFxGYnhj7RTf51udZg7Qd1dhsgBnx",
),
];
for (i, (cmd_id, name, policy_id, want)) in tests.iter().enumerate() {
let got = role_id::<CS>(*cmd_id, name, *policy_id);
let want = RoleId::decode(*want).unwrap();
assert_eq!(got, want, "#{i}");
}
}
}