CH-J Toolsadministrasi server melalui SSH
Menu
Sumber yang diterbitkan

CH-J Tools

Jelajahi direktori dan file untuk rilis aplikasi tertentu.

Unduh ZIP sumber
CH-J / Server Manager 1704 file
0.1.2-beta Ditambahkan: · Dibuat:
wire.rs Ditambahkan: Tanggal tidak tercatat 11,0 KB · 358 baris
Kode Unduh file
1//! Bounded packet codec for RFC 1991/2440 and draft-koch-librepgp-06.
2//! Cryptographic primitives and algorithm-specific parameters are provided by rPGP.
3use crate::core::error::{CoreError, CoreResult};
4use base64::{engine::general_purpose::STANDARD, Engine};
5use pgp::{
6 crypto::{hash::HashAlgorithm, public_key::PublicKeyAlgorithm},
7 packet::{PubKeyInner, PublicKey, SecretKey},
8 ser::Serialize,
9 types::{
10 KeyDetails, KeyVersion, Mpi, Password, PlainSecretParams, PublicParams, SecretParams,
11 SignatureBytes, SigningKey, Timestamp, VerifyingKey,
12 },
13};
14use zeroize::Zeroizing;
16pub const MAX_BYTES: usize = 4 * 1024 * 1024;
17pub fn invalid() -> CoreError {
18 CoreError::new(
19 "OPENPGP_FORMAT_INVALID",
20 "Invalid or unsupported historical/LibrePGP key material.",
21 )
23pub fn crypto<T, E>(result: Result<T, E>) -> CoreResult<T> {
24 result.map_err(|_| invalid())
26#[derive(Clone)]
27pub struct Packet {
28 pub tag: u8,
29 pub body: Zeroizing<Vec<u8>>,
31impl Packet {
32 pub fn new(tag: u8, body: Vec<u8>) -> Self {
33 Self {
34 tag,
35 body: Zeroizing::new(body),
36 }
37 }
39pub struct Reader<'a> {
40 pub bytes: &'a [u8],
41 pub pos: usize,
43impl<'a> Reader<'a> {
44 pub fn new(bytes: &'a [u8]) -> Self {
45 Self { bytes, pos: 0 }
46 }
47 pub fn take(&mut self, len: usize) -> CoreResult<&'a [u8]> {
48 let end = self.pos.checked_add(len).ok_or_else(invalid)?;
49 let data = self.bytes.get(self.pos..end).ok_or_else(invalid)?;
50 self.pos = end;
51 Ok(data)
52 }
53 pub fn u8(&mut self) -> CoreResult<u8> {
54 Ok(self.take(1)?[0])
55 }
56 pub fn u16(&mut self) -> CoreResult<u16> {
57 Ok(u16::from_be_bytes(
58 self.take(2)?.try_into().map_err(|_| invalid())?,
59 ))
60 }
61 pub fn u32(&mut self) -> CoreResult<u32> {
62 Ok(u32::from_be_bytes(
63 self.take(4)?.try_into().map_err(|_| invalid())?,
64 ))
65 }
66 pub fn rest(&self) -> &'a [u8] {
67 &self.bytes[self.pos..]
68 }
69 pub fn done(&self) -> CoreResult<()> {
70 if self.pos == self.bytes.len() {
71 Ok(())
72 } else {
73 Err(invalid())
74 }
75 }
76 pub fn mpi(&mut self) -> CoreResult<&'a [u8]> {
77 let bits = self.u16()? as usize;
78 let value = self.take(bits.div_ceil(8))?;
79 if bits == 0
80 || value.first().is_none_or(|b| *b == 0)
81 || (value.len() - 1) * 8 + (8 - value[0].leading_zeros() as usize) != bits
82 {
83 return Err(invalid());
84 }
85 Ok(value)
86 }
88pub fn checksum(bytes: &[u8]) -> [u8; 2] {
89 bytes
90 .iter()
91 .fold(0u16, |a, b| a.wrapping_add(*b as u16))
92 .to_be_bytes()
94fn crc24(bytes: &[u8]) -> [u8; 3] {
95 let mut value = 0xb704ceu32;
96 for byte in bytes {
97 value ^= (*byte as u32) << 16;
98 for _ in 0..8 {
99 value <<= 1;
100 if value & 0x1000000 != 0 {
101 value ^= 0x1864cfb;
102 }
103 }
104 }
105 [(value >> 16) as u8, (value >> 8) as u8, value as u8]
107pub fn armor(kind: &str, data: &[u8]) -> String {
108 let b64 = STANDARD.encode(data);
109 let lines = b64
110 .as_bytes()
111 .chunks(64)
112 .map(|s| std::str::from_utf8(s).unwrap_or(""))
113 .collect::<Vec<_>>()
114 .join("\n");
115 format!(
116 "-----BEGIN PGP {kind}-----\n\n{lines}\n={}\n-----END PGP {kind}-----\n",
117 STANDARD.encode(crc24(data))
118 )
120pub fn decode(bytes: &[u8]) -> CoreResult<Zeroizing<Vec<u8>>> {
121 if bytes.is_empty() || bytes.len() > MAX_BYTES {
122 return Err(invalid());
123 }
124 if bytes[0] & 0x80 != 0 {
125 return Ok(Zeroizing::new(bytes.to_vec()));
126 }
127 let text = std::str::from_utf8(bytes).map_err(|_| invalid())?;
128 let mut lines = text.trim().lines();
129 let first = lines.next().ok_or_else(invalid)?.trim_end_matches('\r');
130 let kind = first
131 .strip_prefix("-----BEGIN PGP ")
132 .and_then(|v| v.strip_suffix("-----"))
133 .filter(|k| ["PUBLIC KEY BLOCK", "PRIVATE KEY BLOCK", "SIGNATURE"].contains(k))
134 .ok_or_else(invalid)?;
135 let end = format!("-----END PGP {kind}-----");
136 let mut headers = true;
137 let mut encoded = Zeroizing::new(String::new());
138 let mut expected = None;
139 let mut finished = false;
140 for line in lines.by_ref() {
141 let line = line.trim();
142 if headers {
143 if line.is_empty() {
144 headers = false;
145 } else if !line.contains(':') {
146 return Err(invalid());
147 }
148 continue;
149 }
150 if line == end {
151 finished = true;
152 break;
153 }
154 if let Some(crc) = line.strip_prefix('=') {
155 if expected.is_some() {
156 return Err(invalid());
157 }
158 expected = Some(STANDARD.decode(crc).map_err(|_| invalid())?);
159 continue;
160 }
161 if expected.is_some() {
162 return Err(invalid());
163 }
164 encoded.push_str(line);
165 }
166 if !finished || lines.any(|line| !line.trim().is_empty()) {
167 return Err(invalid());
168 }
169 let data = Zeroizing::new(STANDARD.decode(encoded.as_bytes()).map_err(|_| invalid())?);
170 if data.len() > MAX_BYTES
171 || expected
172 .as_ref()
173 .is_some_and(|v| v.as_slice() != crc24(&data))
174 {
175 return Err(invalid());
176 }
177 Ok(data)
179pub fn packet_len(reader: &mut Reader) -> CoreResult<usize> {
180 let n = reader.u8()?;
181 Ok(match n {
182 0..=191 => n as usize,
183 192..=223 => ((n as usize - 192) << 8) + reader.u8()? as usize + 192,
184 255 => reader.u32()? as usize,
185 _ => return Err(invalid()),
186 })
188pub fn packets(bytes: &[u8]) -> CoreResult<Vec<Packet>> {
189 let data = decode(bytes)?;
190 let mut r = Reader::new(&data);
191 let mut result = Vec::new();
192 while !r.rest().is_empty() {
193 if result.len() >= 256 {
194 return Err(invalid());
195 }
196 let head = r.u8()?;
197 if head & 128 == 0 {
198 return Err(invalid());
199 }
200 let (tag, len) = if head & 64 != 0 {
201 (head & 63, packet_len(&mut r)?)
202 } else {
203 let len = match head & 3 {
204 0 => r.u8()? as usize,
205 1 => r.u16()? as usize,
206 2 => r.u32()? as usize,
207 _ => return Err(invalid()),
208 };
209 ((head >> 2) & 15, len)
210 };
211 result.push(Packet::new(tag, r.take(len)?.to_vec()));
212 }
213 Ok(result)
215pub fn write_len(out: &mut Vec<u8>, len: usize) -> CoreResult<()> {
216 if len > MAX_BYTES {
217 return Err(invalid());
218 }
219 if len < 192 {
220 out.push(len as u8)
221 } else if len <= 8383 {
222 let n = len - 192;
223 out.push((n / 256 + 192) as u8);
224 out.push((n % 256) as u8)
225 } else {
226 out.push(255);
227 out.extend((len as u32).to_be_bytes())
228 }
229 Ok(())
231pub fn encode(packets: &[Packet], version: u8) -> CoreResult<Zeroizing<Vec<u8>>> {
232 let capacity = packets
233 .iter()
234 .try_fold(0usize, |sum, p| sum.checked_add(p.body.len() + 6))
235 .filter(|n| *n <= MAX_BYTES)
236 .ok_or_else(invalid)?;
237 let mut out = Zeroizing::new(Vec::with_capacity(capacity));
238 for p in packets {
239 if version <= 3 {
240 if p.tag > 15 || p.body.len() > u16::MAX as usize {
241 return Err(invalid());
242 }
243 if p.tag == 13 && p.body.len() <= 255 {
244 out.extend([0x80 | (p.tag << 2), p.body.len() as u8]);
245 } else {
246 out.push(0x81 | (p.tag << 2));
247 out.extend((p.body.len() as u16).to_be_bytes());
248 }
249 } else {
250 out.push(0xc0 | p.tag);
251 write_len(&mut out, p.body.len())?;
252 }
253 out.extend(p.body.as_slice());
254 if out.len() > MAX_BYTES {
255 return Err(invalid());
256 }
257 }
258 Ok(out)
260pub fn hash(algorithm: u8, parts: &[&[u8]]) -> CoreResult<Vec<u8>> {
261 if ![1, 2, 8, 9, 10, 11].contains(&algorithm) {
262 return Err(invalid());
263 }
264 let mut h = crypto(HashAlgorithm::from(algorithm).new_hasher())?;
265 for part in parts {
266 h.update(part);
267 }
268 Ok(h.finalize().to_vec())
271/// V4 is only an internal parameter container for rPGP's low-level primitive
272/// traits. It is never serialized as a compatibility-profile certificate.
273pub struct Parameters {
274 pub public: PublicKey,
275 pub secret: Option<SecretKey>,
277impl Parameters {
278 pub fn public(algorithm: u8, created: u32, material: &[u8]) -> CoreResult<Self> {
279 if ![1, 2, 3, 17, 18, 19, 22].contains(&algorithm) {
280 return Err(invalid());
281 }
282 let params = crypto(PublicParams::try_from_reader(
283 PublicKeyAlgorithm::from(algorithm),
284 Some(material.len()),
285 std::io::Cursor::new(material),
286 ))?;
287 if crypto(params.to_bytes())? != material {
288 return Err(invalid());
289 }
290 let inner = crypto(PubKeyInner::new(
291 KeyVersion::V4,
292 PublicKeyAlgorithm::from(algorithm),
293 Timestamp::from_secs(created),
294 None,
295 params,
296 ))?;
297 Ok(Self {
298 public: crypto(PublicKey::from_inner(inner))?,
299 secret: None,
300 })
301 }
302 pub fn with_secret(mut self, algorithm: u8, material: &[u8]) -> CoreResult<Self> {
303 let plain = crypto(PlainSecretParams::try_from_reader_no_checksum(
304 std::io::Cursor::new(material),
305 KeyVersion::V4,
306 PublicKeyAlgorithm::from(algorithm),
307 self.public.public_params(),
308 ))?;
309 self.secret = Some(crypto(SecretKey::new(
310 self.public.clone(),
311 SecretParams::Plain(plain),
312 ))?);
313 Ok(self)
314 }
315 pub fn sign(&self, algorithm: u8, digest: &[u8]) -> CoreResult<Vec<u8>> {
316 let key = self.secret.as_ref().ok_or_else(invalid)?;
317 let signature =
318 crypto(key.sign(&Password::empty(), HashAlgorithm::from(algorithm), digest))?;
319 crypto(
320 self.public
321 .verify(HashAlgorithm::from(algorithm), digest, &signature),
322 )?;
323 let SignatureBytes::Mpis(mpis) = signature else {
324 return Err(invalid());
325 };
326 let mut bytes = Vec::new();
327 for mpi in mpis {
328 crypto(mpi.to_writer(&mut bytes))?;
329 }
330 Ok(bytes)
331 }
332 pub fn verify(
333 &self,
334 hash_algorithm: u8,
335 digest: &[u8],
336 algorithm: u8,
337 signature: &[u8],
338 ) -> CoreResult<()> {
339 let count = match algorithm {
340 1 | 3 => 1,
341 17 | 19 | 22 => 2,
342 _ => return Err(invalid()),
343 };
344 let mut reader = std::io::Cursor::new(signature);
345 let mut mpis = Vec::new();
346 for _ in 0..count {
347 mpis.push(crypto(Mpi::try_from_reader(&mut reader))?);
348 }
349 if reader.position() as usize != signature.len() {
350 return Err(invalid());
351 }
352 crypto(self.public.verify(
353 HashAlgorithm::from(hash_algorithm),
354 digest,
355 &SignatureBytes::Mpis(mpis),
356 ))
357 }
SHA-256306b8d45975f845db3692181bc6b62b145cde33a2e3058a9515f084cc40fec67

CH-J Proprietary Software License 1.14 · Kode sumber disediakan berdasarkan CH-J Proprietary Software License 1.14. Ketersediaannya tidak mengubah ketentuan lisensi atau memberikan hak tambahan.

SHA-256 arsipac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f
File