prikk_object/
signature.rs1use prikk_error::{PrikkError, Result};
4
5use crate::{CanonicalEncode, CanonicalWriter, ObjectId, ObjectType};
6
7pub const SIGNATURE_DOMAIN: &[u8] = b"prikk.sig.v1";
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[repr(u16)]
13pub enum SignatureAlgorithm {
14 Ed25519 = 1,
16}
17
18impl SignatureAlgorithm {
19 #[must_use]
21 pub const fn code(self) -> u16 {
22 self as u16
23 }
24
25 pub fn from_code(code: u16) -> Result<Self> {
27 match code {
28 1 => Ok(Self::Ed25519),
29 other => Err(PrikkError::InvalidSignature(format!(
30 "unknown signature algorithm code: {other}"
31 ))),
32 }
33 }
34}
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
38#[repr(u16)]
39pub enum SignerRole {
40 Author = 1,
42 Maintainer = 2,
44 Ci = 3,
46 Audit = 4,
48}
49
50impl SignerRole {
51 #[must_use]
53 pub const fn code(self) -> u16 {
54 self as u16
55 }
56
57 pub fn from_code(code: u16) -> Result<Self> {
59 match code {
60 1 => Ok(Self::Author),
61 2 => Ok(Self::Maintainer),
62 3 => Ok(Self::Ci),
63 4 => Ok(Self::Audit),
64 other => Err(PrikkError::InvalidSignature(format!(
65 "unknown signer role code: {other}"
66 ))),
67 }
68 }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq)]
73pub struct Signature {
74 pub algorithm: SignatureAlgorithm,
76 pub key_id: String,
78 pub signature_bytes: Vec<u8>,
80 pub created_at: u64,
82 pub signer_role: SignerRole,
84}
85
86impl Signature {
87 #[must_use]
89 pub fn signed_bytes(
90 algorithm: SignatureAlgorithm,
91 object_type: ObjectType,
92 object_id: ObjectId,
93 signer_role: SignerRole,
94 key_id: &str,
95 ) -> Vec<u8> {
96 let mut out = Vec::with_capacity(SIGNATURE_DOMAIN.len() + 2 + 32 + 2 + 2 + key_id.len());
97 out.extend_from_slice(SIGNATURE_DOMAIN);
98 out.extend_from_slice(&algorithm.code().to_be_bytes());
99 out.extend_from_slice(&object_type.code().to_be_bytes());
100 out.extend_from_slice(object_id.as_bytes());
101 out.extend_from_slice(&signer_role.code().to_be_bytes());
102 out.extend_from_slice(&(key_id.len() as u16).to_be_bytes());
103 out.extend_from_slice(key_id.as_bytes());
104 out
105 }
106
107 pub fn validate(&self) -> Result<()> {
109 if self.key_id.is_empty() {
110 return Err(PrikkError::InvalidSignature(
111 "signature key_id must not be empty".to_string(),
112 ));
113 }
114 if self.signature_bytes.is_empty() {
115 return Err(PrikkError::InvalidSignature(
116 "signature bytes must not be empty".to_string(),
117 ));
118 }
119 Ok(())
120 }
121}
122
123impl CanonicalEncode for Signature {
124 fn encode_canonical(&self, writer: &mut CanonicalWriter) -> Result<()> {
125 writer.field_u32(1, self.algorithm.code() as u32)?;
126 writer.field_string(2, &self.key_id)?;
127 writer.field_bytes(3, &self.signature_bytes)?;
128 writer.field_u64(4, self.created_at)?;
129 writer.field_u32(5, self.signer_role.code() as u32)?;
130 Ok(())
131 }
132}