Publicerad källa
Ladda ner källa ZIP
CH-J Tools
Bläddra i kataloger och filer för en specifik programutgåva.
CH-J / Server Manager
1704 filer
0.1.2-beta
Tillagd: · Byggd:
1
//! Local operations on compatibility certificates. Vault ownership/CAS stays in the caller.2
use super::{3
certificate::{self, Certificate},4
key::Key,5
librepgp, protect,6
signature::{self, Signature},7
wire::*,8
};9
use crate::core::error::CoreResult;10
use pgp::types::Timestamp;11
use serde_json::{json, Value};12
use zeroize::Zeroizing;14
fn 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 != 517
|| entry["fingerprint"] != hex::encode_upper(cert.primary.fingerprint()?)18
|| cert.public_armor()? != entry["public"]19
{20
return Err(invalid());21
}22
Ok(cert)23
}24
fn 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)30
}31
fn 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)44
}45
fn 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"] == fingerprint51
&& m["effectiveCapabilities"]52
.as_str()53
.unwrap_or("")54
.contains(cap)55
}))56
}57
pub 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 = cert61
.packets62
.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
¶ms,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())97
}98
pub 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.algorithm117
|| !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())133
}134
pub 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
¶ms,159
)?;160
cert.packets.insert(1, sig);161
updated(entry, &cert)162
}163
fn 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 += 1167
}168
end169
}170
fn 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)189
}190
pub 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"] == true206
|| 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
¶ms,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 = cert240
.packets241
.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()) == fp246
})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(¶ms, &data).is_ok())257
.max_by_key(|s| s.created)258
.ok_or_else(invalid)?;259
renewal(&cert.primary, ¶ms, &old, payload, &data, key.created)?260
} else {261
librepgp::signature(262
&cert.primary,263
¶ms,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(¶ms, &data).is_ok()284
})285
.max_by_key(|s| s.created)286
{287
additions.push((288
end,289
renewal(290
&cert.primary,291
¶ms,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(¶ms, &data).is_ok())307
.max_by_key(|s| s.created)308
{309
additions.push((310
end,311
renewal(312
&cert.primary,313
¶ms,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)334
}SHA-256
9f8fc9f892f5a940820c9e279fdced5c14d0d16f351a5a17d20d383891d3fe49CH-J Proprietary Software License 1.14 · Källan finns under CH-J Proprietary Software License 1.14. Dess tillgänglighet ändrar inte licensvillkoren eller ger ytterligare rättigheter.
Arkiv SHA-256
ac8fe790f4ba60fc098f8c985fc2656cfcc6cdc21fb34b30e308a2b54e62258f