1use std::fmt;
2
3use iroh_db_core::AuthorId;
4use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
5
6use crate::SecurityError;
7
8#[derive(Clone, Zeroize, ZeroizeOnDrop)]
10pub struct DomainKey([u8; 32]);
11
12impl DomainKey {
13 pub fn generate() -> Result<Self, SecurityError> {
15 let mut bytes = [0_u8; 32];
16 getrandom::fill(&mut bytes).map_err(|_| SecurityError::RandomnessUnavailable)?;
17 Ok(Self(bytes))
18 }
19
20 pub const fn from_bytes(bytes: [u8; 32]) -> Self {
22 Self(bytes)
23 }
24
25 pub(crate) const fn as_bytes(&self) -> &[u8; 32] {
26 &self.0
27 }
28
29 pub fn derive_subkey(
31 &self,
32 salt: &[u8],
33 context: &[u8],
34 ) -> Result<Zeroizing<[u8; 32]>, SecurityError> {
35 use hkdf::Hkdf;
36 use sha2::Sha256;
37
38 let hkdf = Hkdf::<Sha256>::new(Some(salt), &self.0);
39 let mut output = Zeroizing::new([0_u8; 32]);
40 hkdf.expand(context, &mut *output)
41 .map_err(|_| SecurityError::KeyDerivationFailed)?;
42 Ok(output)
43 }
44}
45
46impl fmt::Debug for DomainKey {
47 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
48 formatter.write_str("DomainKey([redacted])")
49 }
50}
51
52#[derive(Clone)]
54pub struct IrohSigner {
55 secret_key: iroh_base::SecretKey,
56}
57
58impl IrohSigner {
59 pub fn generate() -> Self {
61 Self {
62 secret_key: iroh_base::SecretKey::generate(),
63 }
64 }
65
66 pub fn from_secret_bytes(bytes: [u8; 32]) -> Self {
68 Self {
69 secret_key: iroh_base::SecretKey::from_bytes(&bytes),
70 }
71 }
72
73 pub const fn new(secret_key: iroh_base::SecretKey) -> Self {
75 Self { secret_key }
76 }
77
78 pub fn author(&self) -> AuthorId {
80 AuthorId::from_bytes(*self.secret_key.public().as_bytes())
81 }
82
83 pub fn sign(&self, canonical_commit_bytes: &[u8]) -> [u8; 64] {
85 self.secret_key
86 .sign(&commit_signature_message(canonical_commit_bytes))
87 .to_bytes()
88 }
89
90 pub fn sign_snapshot(&self, canonical_snapshot_bytes: &[u8]) -> [u8; 64] {
92 self.secret_key
93 .sign(&snapshot_signature_message(canonical_snapshot_bytes))
94 .to_bytes()
95 }
96
97 pub fn sign_backup_catalog(&self, canonical_catalog_bytes: &[u8]) -> [u8; 64] {
99 self.secret_key
100 .sign(&backup_signature_message(canonical_catalog_bytes))
101 .to_bytes()
102 }
103
104 pub fn sign_gossip(&self, canonical_hint_bytes: &[u8]) -> [u8; 64] {
106 self.secret_key
107 .sign(&gossip_signature_message(canonical_hint_bytes))
108 .to_bytes()
109 }
110
111 pub fn sign_capability(&self, canonical_authority_bytes: &[u8]) -> [u8; 64] {
113 self.secret_key
114 .sign(&capability_signature_message(canonical_authority_bytes))
115 .to_bytes()
116 }
117
118 pub fn sign_domain_descriptor(&self, canonical_descriptor_bytes: &[u8]) -> [u8; 64] {
120 self.secret_key
121 .sign(&domain_descriptor_signature_message(
122 canonical_descriptor_bytes,
123 ))
124 .to_bytes()
125 }
126
127 pub fn sign_invitation(&self, canonical_invitation_bytes: &[u8]) -> [u8; 64] {
129 self.secret_key
130 .sign(&invitation_signature_message(canonical_invitation_bytes))
131 .to_bytes()
132 }
133
134 pub fn sign_authority_checkpoint(&self, canonical_bytes: &[u8]) -> [u8; 64] {
136 self.secret_key
137 .sign(&authority_checkpoint_signature_message(canonical_bytes))
138 .to_bytes()
139 }
140
141 pub fn sign_control(&self, canonical_control_bytes: &[u8]) -> [u8; 64] {
143 self.secret_key
144 .sign(&control_signature_message(canonical_control_bytes))
145 .to_bytes()
146 }
147
148 pub(crate) fn secret_bytes(&self) -> [u8; 32] {
149 self.secret_key.to_bytes()
150 }
151}
152
153impl fmt::Debug for IrohSigner {
154 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
155 formatter.write_str("IrohSigner([redacted])")
156 }
157}
158
159pub(crate) fn commit_signature_message(canonical_commit_bytes: &[u8]) -> Vec<u8> {
160 let mut message = Vec::with_capacity(27 + canonical_commit_bytes.len());
161 message.extend_from_slice(b"iroh-db/commit-signature/v1");
162 message.extend_from_slice(canonical_commit_bytes);
163 message
164}
165
166pub(crate) fn snapshot_signature_message(canonical_snapshot_bytes: &[u8]) -> Vec<u8> {
167 let mut message = Vec::with_capacity(29 + canonical_snapshot_bytes.len());
168 message.extend_from_slice(b"iroh-db/snapshot-signature/v1");
169 message.extend_from_slice(canonical_snapshot_bytes);
170 message
171}
172
173pub(crate) fn backup_signature_message(canonical_catalog_bytes: &[u8]) -> Vec<u8> {
174 let mut message = Vec::with_capacity(27 + canonical_catalog_bytes.len());
175 message.extend_from_slice(b"iroh-db/backup-signature/v1");
176 message.extend_from_slice(canonical_catalog_bytes);
177 message
178}
179
180pub(crate) fn gossip_signature_message(canonical_hint_bytes: &[u8]) -> Vec<u8> {
181 let mut message = Vec::with_capacity(27 + canonical_hint_bytes.len());
182 message.extend_from_slice(b"iroh-db/gossip-signature/v1");
183 message.extend_from_slice(canonical_hint_bytes);
184 message
185}
186
187pub(crate) fn capability_signature_message(canonical_authority_bytes: &[u8]) -> Vec<u8> {
188 let mut message = Vec::with_capacity(31 + canonical_authority_bytes.len());
189 message.extend_from_slice(b"iroh-db/capability-signature/v1");
190 message.extend_from_slice(canonical_authority_bytes);
191 message
192}
193
194pub(crate) fn domain_descriptor_signature_message(canonical_bytes: &[u8]) -> Vec<u8> {
195 let mut message = Vec::with_capacity(38 + canonical_bytes.len());
196 message.extend_from_slice(b"iroh-db/domain-descriptor-signature/v1");
197 message.extend_from_slice(canonical_bytes);
198 message
199}
200
201pub(crate) fn invitation_signature_message(canonical_bytes: &[u8]) -> Vec<u8> {
202 let mut message = Vec::with_capacity(31 + canonical_bytes.len());
203 message.extend_from_slice(b"iroh-db/invitation-signature/v1");
204 message.extend_from_slice(canonical_bytes);
205 message
206}
207
208pub(crate) fn authority_checkpoint_signature_message(canonical_bytes: &[u8]) -> Vec<u8> {
209 let mut message = Vec::with_capacity(41 + canonical_bytes.len());
210 message.extend_from_slice(b"iroh-db/authority-checkpoint-signature/v1");
211 message.extend_from_slice(canonical_bytes);
212 message
213}
214
215pub(crate) fn control_signature_message(canonical_bytes: &[u8]) -> Vec<u8> {
216 let mut message = Vec::with_capacity(28 + canonical_bytes.len());
217 message.extend_from_slice(b"iroh-db/control-signature/v1");
218 message.extend_from_slice(canonical_bytes);
219 message
220}