Source publiée
Télécharger la source ZIP
CH-J Tools
Parcourez les répertoires et les fichiers pour une version spécifique de l'application.
CH-J / Server Manager
1704 fichiers
0.1.2-beta
Ajouté: · Construit:
1
//! Bounded packet codec for RFC 1991/2440 and draft-koch-librepgp-06.2
//! Cryptographic primitives and algorithm-specific parameters are provided by rPGP.3
use crate::core::error::{CoreError, CoreResult};4
use base64::{engine::general_purpose::STANDARD, Engine};5
use 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
};14
use zeroize::Zeroizing;16
pub const MAX_BYTES: usize = 4 * 1024 * 1024;17
pub fn invalid() -> CoreError {18
CoreError::new(19
"OPENPGP_FORMAT_INVALID",20
"Invalid or unsupported historical/LibrePGP key material.",21
)22
}23
pub fn crypto<T, E>(result: Result<T, E>) -> CoreResult<T> {24
result.map_err(|_| invalid())25
}26
#[derive(Clone)]27
pub struct Packet {28
pub tag: u8,29
pub body: Zeroizing<Vec<u8>>,30
}31
impl Packet {32
pub fn new(tag: u8, body: Vec<u8>) -> Self {33
Self {34
tag,35
body: Zeroizing::new(body),36
}37
}38
}39
pub struct Reader<'a> {40
pub bytes: &'a [u8],41
pub pos: usize,42
}43
impl<'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 == 080
|| value.first().is_none_or(|b| *b == 0)81
|| (value.len() - 1) * 8 + (8 - value[0].leading_zeros() as usize) != bits82
{83
return Err(invalid());84
}85
Ok(value)86
}87
}88
pub fn checksum(bytes: &[u8]) -> [u8; 2] {89
bytes90
.iter()91
.fold(0u16, |a, b| a.wrapping_add(*b as u16))92
.to_be_bytes()93
}94
fn 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]106
}107
pub fn armor(kind: &str, data: &[u8]) -> String {108
let b64 = STANDARD.encode(data);109
let lines = b64110
.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
)119
}120
pub 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 = first131
.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_BYTES171
|| expected172
.as_ref()173
.is_some_and(|v| v.as_slice() != crc24(&data))174
{175
return Err(invalid());176
}177
Ok(data)178
}179
pub 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
})187
}188
pub 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)214
}215
pub 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(())230
}231
pub fn encode(packets: &[Packet], version: u8) -> CoreResult<Zeroizing<Vec<u8>>> {232
let capacity = packets233
.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)259
}260
pub 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())269
}271
/// V4 is only an internal parameter container for rPGP's low-level primitive272
/// traits. It is never serialized as a compatibility-profile certificate.273
pub struct Parameters {274
pub public: PublicKey,275
pub secret: Option<SecretKey>,276
}277
impl 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.public321
.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
}358
}SHA-256
306b8d45975f845db3692181bc6b62b145cde33a2e3058a9515f084cc40fec67CH-J Proprietary Software License 1.14 · La source est fournie sous CH-J Proprietary Software License 1.14. Sa disponibilité ne modifie pas les termes de la licence ni n'accorde de droits supplémentaires.
Archive SHA-256
ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f