CH-J Toolsمدیریت سرور از طریق SSH
منو
منبع منتشرشده

CH-J Tools

پوشه‌ها و فایل‌های یک نسخهٔ مشخص از برنامه را مرور کنید.

دانلود ZIP منبع
CH-J / Server Manager 1704 فایل
0.1.2-beta افزوده‌شده: · ساخته‌شده:
operations.rs افزوده‌شده: تاریخ ثبت نشده است ۱۲٫۲ KB · 334 خط
کد دانلود فایل
1//! Local operations on compatibility certificates. Vault ownership/CAS stays in the caller.
2use super::{
3 certificate::{self, Certificate},
4 key::Key,
5 librepgp, protect,
6 signature::{self, Signature},
7 wire::*,
8};
9use crate::core::error::CoreResult;
10use pgp::types::Timestamp;
11use serde_json::{json, Value};
12use zeroize::Zeroizing;
14fn private(entry: &Value) -> CoreResult<Certificate> {
15 let cert = Certificate::parse(entry["private"].as_str().ok_or_else(invalid)?.as_bytes())?;
16 if cert.packets[0].tag != 5
17 || entry["fingerprint"] != hex::encode_upper(cert.primary.fingerprint()?)
18 || cert.public_armor()? != entry["public"]
19 {
20 return Err(invalid());
21 }
22 Ok(cert)
24fn unlocked(key: &Key, password: &str) -> CoreResult<Parameters> {
25 if password.chars().count() > 1024 || password.contains('\0') {
26 return Err(invalid());
27 }
28 let plain = protect::open(key, password)?;
29 key.parameters()?.with_secret(key.algorithm, &plain)
31fn updated(entry: &Value, cert: &Certificate) -> CoreResult<Value> {
32 let details = cert.metadata()?;
33 if entry["fingerprint"] != details["fingerprint"] || details["bindingValid"] != true {
34 return Err(invalid());
35 }
36 let mut next = entry.clone();
37 next["public"] = json!(cert.public_armor()?);
38 next["private"] = json!(cert.private_armor()?);
39 next["revoked"] = details["revoked"].clone();
40 next.as_object_mut()
41 .ok_or_else(invalid)?
42 .remove("keyserver");
43 Ok(next)
45fn effective(details: &Value, key: &Key, cap: char) -> CoreResult<bool> {
46 let fingerprint = hex::encode_upper(key.fingerprint()?);
47 Ok(std::iter::once(details)
48 .chain(details["subkeys"].as_array().into_iter().flatten())
49 .any(|m| {
50 m["fingerprint"] == fingerprint
51 && m["effectiveCapabilities"]
52 .as_str()
53 .unwrap_or("")
54 .contains(cap)
55 }))
57pub fn sign(entry: &Value, path: &std::path::Path, password: &str) -> CoreResult<String> {
58 let cert = private(entry)?;
59 let details = cert.metadata()?;
60 let keys = cert
61 .packets
62 .iter()
63 .filter(|p| p.tag == 7)
64 .chain(cert.packets.iter().take(1));
65 for packet in keys {
66 let key = Key::parse(packet)?;
67 if !effective(&details, &key, 'S')? {
68 continue;
69 }
70 let params = unlocked(&key, password)?;
71 let bytes = Zeroizing::new(crate::core::keychain::bounded_file(path, 64 * 1024 * 1024)?);
72 let now = Timestamp::now().as_secs();
73 let (hashed, unhashed) = if key.version == 5 {
74 (
75 librepgp::hashed(&key, now)?,
76 signature::subpacket(16, &key.key_id()?)?,
77 )
78 } else {
79 (Vec::new(), Vec::new())
80 };
81 let sig = signature::create(
82 key.version,
83 0,
84 key.algorithm,
85 now,
86 &key.key_id()?,
87 hashed,
88 unhashed,
89 &bytes,
90 &params,
91 )?;
92 let armored = armor("SIGNATURE", &encode(&[sig], key.version)?);
93 verify(entry, path, armored.as_bytes())?;
94 return Ok(armored);
95 }
96 Err(invalid())
98pub fn verify(entry: &Value, path: &std::path::Path, bytes: &[u8]) -> CoreResult<Value> {
99 if bytes.len() > 1024 * 1024 {
100 return Err(invalid());
101 }
102 let packets = packets(bytes)?;
103 if packets.len() != 1 || packets[0].tag != 2 {
104 return Err(invalid());
105 }
106 let sig = Signature::parse(&packets[0].body)?;
107 if sig.typ != 0 || !sig.current(Timestamp::now().as_secs()) {
108 return Err(invalid());
109 }
110 let cert = Certificate::parse(entry["public"].as_str().ok_or_else(invalid)?.as_bytes())?;
111 let details = cert.metadata()?;
112 let data = Zeroizing::new(crate::core::keychain::bounded_file(path, 64 * 1024 * 1024)?);
113 for packet in cert.packets.iter().filter(|p| [6, 14].contains(&p.tag)) {
114 let key = Key::parse(packet)?;
115 if (sig.version == 5) != (key.version == 5)
116 || sig.algorithm != key.algorithm
117 || !effective(&details, &key, 'S')?
118 {
119 continue;
120 }
121 if sig.field(33).is_some_and(|fp| {
122 fp.first() != Some(&key.version)
123 || key.fingerprint().is_err()
124 || &fp[1..] != key.fingerprint().unwrap_or_default()
125 }) {
126 continue;
127 }
128 if sig.verify(&key.parameters()?, &data).is_ok() {
129 return Ok(json!({"verified":true,"fingerprint":entry["fingerprint"]}));
130 }
131 }
132 Err(invalid())
134pub fn revoke(entry: &Value, password: &str) -> CoreResult<Value> {
135 let mut cert = private(entry)?;
136 let key = &cert.primary;
137 let params = unlocked(key, password)?;
138 let now = Timestamp::now().as_secs();
139 let hashed = if key.version == 5 {
140 librepgp::hashed(key, now)?
141 } else {
142 Vec::new()
143 };
144 let unhashed = if key.version == 5 {
145 signature::subpacket(16, &key.key_id()?)?
146 } else {
147 Vec::new()
148 };
149 let sig = signature::create(
150 key.version,
151 0x20,
152 key.algorithm,
153 now,
154 &key.key_id()?,
155 hashed,
156 unhashed,
157 &key.prefix()?,
158 &params,
159 )?;
160 cert.packets.insert(1, sig);
161 updated(entry, &cert)
163fn group_end(cert: &Certificate, index: usize) -> usize {
164 let mut end = index + 1;
165 while end < cert.packets.len() && cert.packets[end].tag == 2 {
166 end += 1
167 }
168 end
170fn renewal(
171 primary: &Key,
172 params: &Parameters,
173 old: &Signature,
174 payload: &Value,
175 data: &[u8],
176 created: u32,
177) -> CoreResult<Packet> {
178 let now = Timestamp::now().as_secs().max(old.created);
179 let mut hashed = librepgp::hashed(primary, now)?;
180 for (typ, bytes) in signature::subpackets(&old.hashed)? {
181 if ![2, 3, 9, 33].contains(&(typ & 127)) {
182 hashed.extend(signature::subpacket(typ, bytes)?);
183 }
184 }
185 if let Some(exp) = librepgp::expiry(created, payload)? {
186 hashed.extend(exp)
187 }
188 librepgp::signature(primary, params, old.typ, hashed, data)
190pub fn edit(
191 entry: &Value,
192 payload: &Value,
193 progress: &dyn Fn(&str) -> CoreResult<()>,
194) -> CoreResult<Value> {
195 let mut cert = private(entry)?;
196 let details = cert.metadata()?;
197 if cert.primary.version != 5 || details["revoked"] == true || details["bindingValid"] != true {
198 return Err(invalid());
199 }
200 let password = payload["passphrase"].as_str().unwrap_or("");
201 progress("unlock")?;
202 let params = unlocked(&cert.primary, password)?;
203 match payload["operation"].as_str().unwrap_or("") {
204 "addSubkey" => {
205 if details["expired"] == true
206 || details["subkeys"].as_array().is_some_and(|s| s.len() >= 64)
207 {
208 return Err(invalid());
209 }
210 let new_password = payload["subkeyPassphrase"].as_str().unwrap_or(password);
211 if new_password.chars().count() > 1024 || new_password.contains('\0') {
212 return Err(invalid());
213 }
214 cert.packets.extend(librepgp::subkey(
215 &cert.primary,
216 &params,
217 &payload["subkey"],
218 new_password,
219 progress,
220 )?);
221 }
222 "revokeSubkey" | "setExpiration" => {
223 let expiration = payload["operation"] == "setExpiration";
224 if expiration && payload.get("expiresAt").is_none() {
225 return Err(invalid());
226 }
227 let fp = payload["fingerprint"].as_str().filter(|s| !s.is_empty());
228 if !expiration && (payload["confirmed"] != true || fp.is_none()) {
229 return Err(invalid());
230 }
231 if let Some(fp) = fp.filter(|fp| *fp != details["fingerprint"].as_str().unwrap_or("")) {
232 let meta = details["subkeys"]
233 .as_array()
234 .and_then(|subs| subs.iter().find(|s| s["fingerprint"] == fp))
235 .ok_or_else(invalid)?;
236 if meta["revoked"] == true {
237 return Err(invalid());
238 }
239 let index = cert
240 .packets
241 .iter()
242 .position(|p| {
243 [7, 14].contains(&p.tag)
244 && Key::parse(p).is_ok_and(|k| {
245 hex::encode_upper(k.fingerprint().unwrap_or_default()) == fp
246 })
247 })
248 .ok_or_else(invalid)?;
249 let key = Key::parse(&cert.packets[index])?;
250 let data = certificate::binding_data(&cert.primary, &key)?;
251 let end = group_end(&cert, index);
252 let sig = if expiration {
253 let old = cert.packets[index + 1..end]
254 .iter()
255 .filter_map(|p| Signature::parse(&p.body).ok())
256 .filter(|s| s.typ == 0x18 && s.verify(&params, &data).is_ok())
257 .max_by_key(|s| s.created)
258 .ok_or_else(invalid)?;
259 renewal(&cert.primary, &params, &old, payload, &data, key.created)?
260 } else {
261 librepgp::signature(
262 &cert.primary,
263 &params,
264 0x28,
265 librepgp::hashed(&cert.primary, Timestamp::now().as_secs())?,
266 &data,
267 )?
268 };
269 cert.packets.insert(end, sig);
270 } else {
271 if !expiration {
272 return Err(invalid());
273 }
274 // Renew only identities already certified by this primary; never certify an injected UID.
275 let mut additions = Vec::new();
276 for (index, p) in cert.packets.iter().enumerate().filter(|(_, p)| p.tag == 13) {
277 let data = certificate::certificate_data(&cert.primary, &p.body)?;
278 let end = group_end(&cert, index);
279 if let Some(old) = cert.packets[index + 1..end]
280 .iter()
281 .filter_map(|p| Signature::parse(&p.body).ok())
282 .filter(|s| {
283 (0x10..=0x13).contains(&s.typ) && s.verify(&params, &data).is_ok()
284 })
285 .max_by_key(|s| s.created)
286 {
287 additions.push((
288 end,
289 renewal(
290 &cert.primary,
291 &params,
292 &old,
293 payload,
294 &data,
295 cert.primary.created,
296 )?,
297 ));
298 }
299 }
300 // Direct-key metadata must also be renewed when it takes precedence over a UID signature.
301 let end = group_end(&cert, 0);
302 let data = cert.primary.prefix()?;
303 if let Some(old) = cert.packets[1..end]
304 .iter()
305 .filter_map(|p| Signature::parse(&p.body).ok())
306 .filter(|s| s.typ == 0x1f && s.verify(&params, &data).is_ok())
307 .max_by_key(|s| s.created)
308 {
309 additions.push((
310 end,
311 renewal(
312 &cert.primary,
313 &params,
314 &old,
315 payload,
316 &data,
317 cert.primary.created,
318 )?,
319 ));
320 }
321 if additions.is_empty() {
322 return Err(invalid());
323 }
324 additions.sort_by_key(|(index, _)| *index);
325 for (index, sig) in additions.into_iter().rev() {
326 cert.packets.insert(index, sig);
327 }
328 }
329 }
330 _ => return Err(invalid()),
331 }
332 progress("verify")?;
333 updated(entry, &cert)
SHA-2569f8fc9f892f5a940820c9e279fdced5c14d0d16f351a5a17d20d383891d3fe49

CH-J Proprietary Software License 1.14 · کد منبع تحت CH-J Proprietary Software License 1.14 ارائه می‌شود. در دسترس بودن آن شرایط مجوز را تغییر نمی‌دهد و حق اضافه‌ای اعطا نمی‌کند.

SHA-256 بایگانیac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f
فایل‌ها