CH-J ToolsZarządzanie serwerem przez SSH
Menu
Opublikowane źródło

CH-J Tools

Przeglądaj katalogi i pliki dla konkretnego wydania aplikacji.

Pobierz źródło ZIP
CH-J / Server Manager 1704 pliki
0.1.2-beta Dodano: · Built:
certificate.rs Dodano: Data niezanotowana 11,9 KB · 335 linie
Kod Pobierz plik PDF
1use super::{key::Key, signature::Signature, wire::*};
2use crate::core::error::CoreResult;
3use pgp::types::KeyDetails;
4use pgp::types::Timestamp;
5use serde_json::{json, Value};
7pub struct Certificate {
8 pub packets: Vec<Packet>,
9 pub primary: Key,
11pub fn certificate_data(key: &Key, uid: &[u8]) -> CoreResult<Vec<u8>> {
12 let mut data = key.prefix()?;
13 if key.version == 5 {
14 data.push(0xb4);
15 data.extend((uid.len() as u32).to_be_bytes());
16 }
17 data.extend(uid);
18 Ok(data)
20pub fn binding_data(primary: &Key, sub: &Key) -> CoreResult<Vec<u8>> {
21 let mut data = primary.prefix()?;
22 data.extend(sub.prefix()?);
23 Ok(data)
25pub fn allowed(key: &Key, primary: bool) -> &'static str {
26 match key.algorithm {
27 1 => {
28 if primary {
29 "CSAE"
30 } else {
31 "SAE"
32 }
33 }
34 17 => {
35 if primary {
36 "CS"
37 } else {
38 "S"
39 }
40 }
41 3 | 19 | 22 => {
42 if primary {
43 "CSA"
44 } else {
45 "SA"
46 }
47 }
48 2 | 18 => "E",
49 _ => "",
50 }
52fn flags(sig: &Signature) -> Option<u8> {
53 sig.field(27).and_then(|b| b.first()).copied()
55fn key_meta(
56 key: &Key,
57 primary: bool,
58 binding: Option<&Signature>,
59 revoked: bool,
60 parent: bool,
61 verified: bool,
62) -> CoreResult<Value> {
63 let now = Timestamp::now().as_secs();
64 let bits = if key.version <= 3 {
65 None
66 } else {
67 binding.and_then(flags)
68 };
69 let declared = bits
70 .map(crate::core::openpgp::capabilities_from_bits)
71 .unwrap_or_default();
72 let derived = if bits.is_none() {
73 allowed(key, primary).replace('A', "")
74 } else {
75 String::new()
76 };
77 let requested = if bits.is_some() { &declared } else { &derived };
78 let expires = if key.version <= 3 {
79 (key.days > 0).then_some(key.created as u64 + key.days as u64 * 86400)
80 } else {
81 binding
82 .and_then(|s| s.duration(9))
83 .filter(|d| *d > 0)
84 .map(|d| key.created as u64 + d as u64)
85 };
86 let expired =
87 expires.is_some_and(|d| d <= now as u64) || binding.is_some_and(|s| !s.current(now));
88 let usable = verified
89 && parent
90 && !revoked
91 && !expired
92 && key.created <= now.saturating_add(300)
93 && binding.is_some_and(|s| s.current(now));
94 let effective: String = if usable {
95 requested
96 .chars()
97 .filter(|c| allowed(key, primary).contains(*c))
98 .collect()
99 } else {
100 String::new()
101 };
102 let mut details = json!({"fingerprint":hex::encode_upper(key.fingerprint()?),"version":key.version,"keyId":hex::encode_upper(key.key_id()?),"algorithm":match key.algorithm{1|2|3=>"RSA",17=>"DSA",18=>"ECDH",19=>"ECDSA",22=>"EdDSALegacy",_=>"Unsupported"},"declaredCapabilities":declared,"derivedCapabilities":derived,"effectiveCapabilities":effective,"capabilitySource":if bits.is_some(){"declared"}else{"derived"},"keyFlagBits":bits,"revoked":revoked,"expired":expired,"bindingValid":verified,"usable":usable,"keyCreatedAt":key.created,"expiresAt":expires});
103 for (name, value) in
104 crate::core::openpgp_algorithms::details(key.parameters()?.public.public_params())
105 .as_object()
106 .unwrap()
107 {
108 details[name] = value.clone();
109 }
110 Ok(details)
112impl Certificate {
113 pub fn parse(bytes: &[u8]) -> CoreResult<Self> {
114 let packets = packets(bytes)?;
115 let first = packets.first().ok_or_else(invalid)?;
116 if ![5, 6].contains(&first.tag) {
117 return Err(invalid());
118 }
119 let primary = Key::parse(first)?;
120 let mut keys = 0;
121 for (i, p) in packets.iter().enumerate() {
122 match p.tag {
123 5 | 6 => {
124 if i != 0 {
125 return Err(invalid());
126 }
127 }
128 7 | 14 => {
129 if (first.tag == 6 && p.tag == 7)
130 || primary.version != 5
131 || Key::parse(p)?.version != 5
132 {
133 return Err(invalid());
134 }
135 keys += 1;
136 if keys > 64 {
137 return Err(invalid());
138 }
139 }
140 2 => {
141 Signature::parse(&p.body)?;
142 }
143 13 => {
144 if p.body.len() > 1024 {
145 return Err(invalid());
146 }
147 }
148 17 if primary.version == 5 => {}
149 _ => return Err(invalid()),
150 }
151 }
152 Ok(Self { packets, primary })
153 }
154 pub fn public_packets(&self) -> CoreResult<Vec<Packet>> {
155 self.packets
156 .iter()
157 .map(|p| {
158 if [5, 7].contains(&p.tag) {
159 let key = Key::parse(p)?;
160 Ok(Packet::new(if p.tag == 5 { 6 } else { 14 }, key.public))
161 } else {
162 Ok(p.clone())
163 }
164 })
165 .collect()
166 }
167 pub fn public_armor(&self) -> CoreResult<String> {
168 Ok(armor(
169 "PUBLIC KEY BLOCK",
170 &encode(&self.public_packets()?, self.primary.version)?,
171 ))
172 }
173 pub fn private_armor(&self) -> CoreResult<String> {
174 if self.packets[0].tag != 5 {
175 return Err(invalid());
176 }
177 Ok(armor(
178 "PRIVATE KEY BLOCK",
179 &encode(&self.packets, self.primary.version)?,
180 ))
181 }
182 fn group(&self, index: usize) -> impl Iterator<Item = (usize, &Packet)> {
183 self.packets
184 .iter()
185 .enumerate()
186 .skip(index + 1)
187 .take_while(|(_, p)| p.tag == 2)
188 }
189 pub fn metadata(&self) -> CoreResult<Value> {
190 let params = self.primary.parameters()?;
191 let now = Timestamp::now().as_secs();
192 let mut candidates: Vec<(usize, Signature)> = Vec::new();
193 let mut user_ids = Vec::new();
194 for (index, p) in self.packets.iter().enumerate() {
195 if p.tag == 13 {
196 let data = certificate_data(&self.primary, &p.body)?;
197 let revoked_at = self
198 .group(index)
199 .filter_map(|(_, p)| Signature::parse(&p.body).ok())
200 .filter(|s| s.typ == 0x30 && s.verify(&params, &data).is_ok())
201 .map(|s| s.created)
202 .max();
203 for (i, p) in self.group(index) {
204 let sig = Signature::parse(&p.body)?;
205 if (0x10..=0x13).contains(&sig.typ)
206 && (sig.version == self.primary.version
207 || (sig.version <= 3 && self.primary.version <= 3))
208 && sig.created <= now.saturating_add(300)
209 && revoked_at.is_none_or(|t| sig.created > t)
210 && sig.verify(&params, &data).is_ok()
211 {
212 candidates.push((i, sig));
213 }
214 }
215 user_ids.push(String::from_utf8_lossy(&p.body).to_string());
216 }
217 }
218 if self.primary.version == 5 {
219 for (i, p) in self.group(0) {
220 let sig = Signature::parse(&p.body)?;
221 if sig.typ == 0x1f
222 && sig.version == 5
223 && sig.created <= now.saturating_add(300)
224 && sig.verify(&params, &self.primary.prefix()?).is_ok()
225 {
226 candidates.push((i, sig));
227 }
228 }
229 }
230 let binding = candidates
231 .iter()
232 .max_by_key(|(i, s)| (s.created, s.typ == 0x1f, *i))
233 .map(|(_, s)| s);
234 let revoked = self
235 .packets
236 .iter()
237 .filter(|p| p.tag == 2)
238 .filter_map(|p| Signature::parse(&p.body).ok())
239 .any(|s| {
240 s.typ == 0x20
241 && (s.version == self.primary.version
242 || (s.version <= 3 && self.primary.version <= 3))
243 && s.verify(&params, &self.primary.prefix().unwrap_or_default())
244 .is_ok()
245 });
246 let mut result = key_meta(
247 &self.primary,
248 true,
249 binding,
250 revoked,
251 true,
252 binding.is_some(),
253 )?;
254 let parent = result["usable"] == true
255 && result["effectiveCapabilities"]
256 .as_str()
257 .unwrap_or("")
258 .contains('C');
259 let mut subkeys = Vec::new();
260 for (index, p) in self
261 .packets
262 .iter()
263 .enumerate()
264 .filter(|(_, p)| [7, 14].contains(&p.tag))
265 {
266 let key = Key::parse(p)?;
267 let data = binding_data(&self.primary, &key)?;
268 let bindings: Vec<_> = self
269 .group(index)
270 .filter_map(|(i, p)| Signature::parse(&p.body).ok().map(|s| (i, s)))
271 .filter(|(_, s)| {
272 s.typ == 0x18
273 && s.version == 5
274 && s.created <= now.saturating_add(300)
275 && s.verify(&params, &data).is_ok()
276 })
277 .collect();
278 let binding = bindings
279 .iter()
280 .max_by_key(|(i, s)| (s.created, *i))
281 .map(|(_, s)| s);
282 let revoked = self
283 .group(index)
284 .filter_map(|(_, p)| Signature::parse(&p.body).ok())
285 .any(|s| s.typ == 0x28 && s.version == 5 && s.verify(&params, &data).is_ok());
286 let verified = binding.is_some_and(|s| {
287 let signing = flags(s)
288 .map(|b| b & 2 != 0)
289 .unwrap_or_else(|| allowed(&key, false).contains('S'));
290 !signing
291 || s.field(32)
292 .and_then(|bytes| Signature::parse(bytes).ok())
293 .is_some_and(|back| {
294 back.version == 5
295 && back.typ == 0x19
296 && back.current(now)
297 && key
298 .parameters()
299 .is_ok_and(|p| back.verify(&p, &data).is_ok())
300 })
301 });
302 let mut sub = key_meta(&key, false, binding, revoked, parent, verified)?;
303 let caps = sub["effectiveCapabilities"].as_str().unwrap_or("");
304 sub["roles"] = json!([('S', "sign"), ('A', "authenticate"), ('E', "encrypt")]
305 .iter()
306 .filter_map(|(c, role)| caps.contains(*c).then_some(*role))
307 .collect::<Vec<_>>());
308 subkeys.push(sub);
309 }
310 let mut aggregate = String::new();
311 for cap in "CSAE".chars() {
312 if std::iter::once(&result).chain(subkeys.iter()).any(|m| {
313 m["effectiveCapabilities"]
314 .as_str()
315 .unwrap_or("")
316 .contains(cap)
317 }) {
318 aggregate.push(cap)
319 }
320 }
321 result["userIds"] = json!(user_ids);
322 result["subkeys"] = json!(subkeys);
323 result["certificateCapabilities"] = json!(aggregate);
324 result["profile"] = json!(match self.primary.version {
325 2 => "PGP 2.x / RFC 1991",
326 3 => "Historical PGP / RFC 1991 and RFC 2440",
327 _ => "LibrePGP / draft-koch-librepgp-06",
328 });
329 result["keyserverSupported"] = json!(false);
330 result["editSupported"] = json!(self.primary.version == 5);
331 result["revokeSupported"] = json!(true);
332 result["signSupported"] = json!(aggregate.contains('S'));
333 Ok(result)
334 }
SHA-256665a949e32f528b4ce02398ccf3a961ce9088c755824729ecbb1abee805360cc

CH-J Proprietary Software License 1.14 · Źródło jest dostarczane pod CH-J Proprietary Software License 1.14. Jego dostępność nie zmienia warunków licencji ani nie przyznaje dodatkowych praw.

SHA-256ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f
Pliki